mirror of
https://github.com/UOX3DevTeam/UOX3
synced 2026-08-13 12:27:04 -04:00
1496 lines
56 KiB
C++
1496 lines
56 KiB
C++
#include "uox3.h"
|
|
#include "regions.h"
|
|
#include "townregion.h"
|
|
#include "cServerDefinitions.h"
|
|
#include "ssection.h"
|
|
#include "scriptc.h"
|
|
#include <filesystem>
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <fstream>
|
|
|
|
using namespace std::string_literals ;
|
|
|
|
CMulHandler *Map = nullptr;
|
|
|
|
//const UI16 LANDDATA_SIZE = 0x4000; //(512 * 32)
|
|
|
|
/*
|
|
** 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
|
|
*/
|
|
|
|
|
|
|
|
//===============================================================================================
|
|
// CMulHandler
|
|
//===============================================================================================
|
|
//===============================================================================================
|
|
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Function - void LoadMapsDFN( void )
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Purpose - Loads maps.dfn to see which map files to actually load into memory later
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
auto CMulHandler::loadMapsDFN(const std::string &uodir) ->std::map<int,mapdfndata_st> {
|
|
auto entrycount = FileLookup->CountOfEntries(maps_def);
|
|
std::map<int,mapdfndata_st> results ;
|
|
auto uopath = std::filesystem::path(uodir) ;
|
|
for( auto i = 0;i<entrycount;i++) {
|
|
auto toFind = FileLookup->FindEntry( "MAP "s + std::to_string(i), maps_def );
|
|
if (toFind) {
|
|
auto entry = mapdfndata_st() ;
|
|
for (const auto &sec :toFind->collection()){
|
|
auto UTAG = oldstrutil::upper(sec->tag);
|
|
auto data = oldstrutil::trim(oldstrutil::removeTrailing(sec->data));
|
|
switch (UTAG.data()[0]){
|
|
case 'M':
|
|
if (UTAG == "MAP"){
|
|
entry.mapfile = (uopath / std::filesystem::path(data)) ;
|
|
}
|
|
else if (UTAG == "MAPUOPWRAP"){
|
|
entry.mapuop = (uopath / std::filesystem::path(data));
|
|
}
|
|
else if (UTAG == "MAPDIFF"){
|
|
entry.mapdiff = (uopath / std::filesystem::path(data));
|
|
}
|
|
else if (UTAG == "MAPDIFFLIST"){
|
|
entry.mapdiffl = (uopath / std::filesystem::path(data));
|
|
}
|
|
break;
|
|
case 'S':
|
|
if (UTAG == "STATICS"){
|
|
entry.stamul = (uopath / std::filesystem::path(data));
|
|
}
|
|
else if (UTAG == "STAIDX"){
|
|
entry.staidx = (uopath / std::filesystem::path(data));
|
|
}
|
|
else if (UTAG == "STATICSDIFF"){
|
|
entry.stadiff = (uopath / std::filesystem::path(data));
|
|
}
|
|
else if (UTAG == "STATICSDIFFLIST"){
|
|
entry.stadiffl = (uopath / std::filesystem::path(data));
|
|
}
|
|
else if (UTAG == "STATICSDIFFINDEX"){
|
|
entry.stadiffi = (uopath / std::filesystem::path(data));
|
|
}
|
|
break;
|
|
case 'X':
|
|
if (UTAG == "X"){
|
|
entry.width = std::stoi(data);
|
|
}
|
|
break;
|
|
|
|
case 'Y':
|
|
if (UTAG == "Y"){
|
|
entry.height = std::stoi(data);
|
|
}
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
results.insert_or_assign(i, entry);
|
|
}
|
|
}
|
|
return results ;
|
|
}
|
|
|
|
|
|
//=======================================================================
|
|
auto CMulHandler::loadDFNOverrides() ->void {
|
|
std::uint16_t entryNum;
|
|
for (auto &mapScp : FileLookup->ScriptListings[maps_def]){
|
|
if( mapScp ){
|
|
for (const auto &[entryName, toScan]: mapScp->collection()){
|
|
if( toScan ){
|
|
|
|
entryNum = oldstrutil::value<std::uint16_t>(oldstrutil::extractSection(entryName," ", 1, 1 ));
|
|
auto titlePart = oldstrutil::upper( oldstrutil::extractSection( entryName, " ", 0, 0 ));
|
|
// have we got an entry starting with TILE ?
|
|
if( titlePart == "TILE" && entryNum ) {
|
|
if( (entryNum != INVALIDID) && (entryNum < tile_info.sizeArt())) {
|
|
auto tile = &tile_info.art(entryNum);
|
|
if(tile) {
|
|
for (const auto &sec : toScan->collection()){
|
|
auto UTag = oldstrutil::upper( sec->tag );
|
|
auto data = oldstrutil::trim( oldstrutil::removeTrailing( sec->data, "//" ));
|
|
// CTile properties
|
|
if( UTag == "WEIGHT" ){
|
|
tile->Weight( static_cast<UI08>(std::stoul(data, nullptr, 0)) );
|
|
}
|
|
else if( UTag == "HEIGHT" ){
|
|
tile->Height( static_cast<SI08>(std::stoi(data, nullptr, 0))) ;
|
|
}
|
|
else if( UTag == "LAYER" ){
|
|
tile->Layer( static_cast<SI08>(std::stoi(data, nullptr, 0))) ;
|
|
}
|
|
else if( UTag == "HUE" ){
|
|
tile->Hue( static_cast<UI08>(std::stoul(data, nullptr, 0)) );
|
|
}
|
|
else if( UTag == "QUANTITY" ){
|
|
tile->Quantity( static_cast<UI08>(std::stoul(data, nullptr, 0)) );
|
|
}
|
|
else if( UTag == "ANIMATION" ){
|
|
tile->Animation( static_cast<UI16>(std::stoul(data, nullptr, 0)) );
|
|
}
|
|
else if( UTag == "NAME" ) {
|
|
tile->Name( data );
|
|
}
|
|
// BaseTile Flag 1
|
|
else if( UTag == "ATFLOORLEVEL" ){
|
|
tile->SetFlag( TF_FLOORLEVEL, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "HOLDABLE" ){
|
|
tile->SetFlag( TF_HOLDABLE, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "SIGNGUILDBANNER" ){
|
|
tile->SetFlag( TF_TRANSPARENT, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "WEBDIRTBLOOD" ){
|
|
tile->SetFlag( TF_TRANSLUCENT, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "WALLVERTTILE" ){
|
|
tile->SetFlag( TF_WALL, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "DAMAGING" ){
|
|
tile->SetFlag( TF_DAMAGING, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "BLOCKING" ){
|
|
tile->SetFlag( TF_BLOCKING, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "LIQUIDWET" ){
|
|
tile->SetFlag( TF_WET, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
|
|
// BaseTile Flag 2
|
|
else if( UTag == "UNKNOWNFLAG1" ){
|
|
tile->SetFlag( TF_UNKNOWN1, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "STANDABLE" ){
|
|
tile->SetFlag( TF_SURFACE, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "CLIMBABLE" ){
|
|
tile->SetFlag( TF_CLIMBABLE, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "STACKABLE" ){
|
|
tile->SetFlag( TF_STACKABLE, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "WINDOWARCHDOOR" ){
|
|
tile->SetFlag( TF_WINDOW, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "CANNOTSHOOTTHRU" ){
|
|
tile->SetFlag( TF_NOSHOOT, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "DISPLAYASA" ){
|
|
tile->SetFlag( TF_DISPLAYA, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "DISPLAYASAN" ){
|
|
tile->SetFlag( TF_DISPLAYAN, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
|
|
// BaseTile Flag 3
|
|
else if( UTag == "DESCRIPTIONTILE" ){
|
|
tile->SetFlag( TF_DESCRIPTION, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "FADEWITHTRANS" ){
|
|
tile->SetFlag( TF_FOLIAGE, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "PARTIALHUE" ){
|
|
tile->SetFlag( TF_PARTIALHUE, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "UNKNOWNFLAG2" ){
|
|
tile->SetFlag( TF_UNKNOWN2, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "MAP" ){
|
|
tile->SetFlag( TF_MAP, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "CONTAINER" ){
|
|
tile->SetFlag( TF_CONTAINER, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "EQUIPABLE" ){
|
|
tile->SetFlag( TF_WEARABLE, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "LIGHTSOURCE" ){
|
|
tile->SetFlag( TF_LIGHT, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
|
|
// BaseTile Flag 4
|
|
else if( UTag == "ANIMATED" ){
|
|
tile->SetFlag( TF_ANIMATED, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "NODIAGONAL" ){
|
|
tile->SetFlag( TF_NODIAGONAL, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "UNKNOWNFLAG3" ){
|
|
tile->SetFlag( TF_UNKNOWN3, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "WHOLEBODYITEM" ){
|
|
tile->SetFlag( TF_ARMOR, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "WALLROOFWEAP" ){
|
|
tile->SetFlag( TF_ROOF, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "DOOR" ){
|
|
tile->SetFlag( TF_DOOR, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "CLIMBABLEBIT1" ){
|
|
tile->SetFlag( TF_STAIRBACK, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
else if( UTag == "CLIMBABLEBIT2" ){
|
|
tile->SetFlag( TF_STAIRRIGHT, (std::stoi(data, nullptr, 0) != 0) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if( titlePart == "LAND" && entryNum ) { // let's not deal with this just yet
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//===================================================================================
|
|
auto CMulHandler::loadTileData(const std::string& uodir) ->void {
|
|
std::string lName = uodir + "tiledata.mul"s;
|
|
Console << "\t" << lName << "\t";
|
|
|
|
if (!tile_info.load(lName)) {
|
|
Console.PrintFailed();
|
|
Shutdown( FATAL_UOX3_TILEDATA_NOT_FOUND );
|
|
|
|
}
|
|
Console.PrintDone();
|
|
}
|
|
//===================================================================================
|
|
auto CMulHandler::loadMultis( const std::string& uodir ) ->void {
|
|
// now main memory multiItems
|
|
Console << "Loading Multis.... ";
|
|
// Odd we do no check?
|
|
if (!multi_data.load(std::filesystem::path(uodir),&tile_info)) {
|
|
Console.PrintFailed();
|
|
Shutdown( FATAL_UOX3_MULTI_DATA_NOT_FOUND );
|
|
}
|
|
Console.PrintDone();
|
|
}
|
|
//================================================================================
|
|
auto CMulHandler::load() ->void {
|
|
auto uodir = cwmWorldState->ServerData()->Directory( CSDDP_DATA );
|
|
auto mapinfo = loadMapsDFN(uodir) ;
|
|
Console.PrintSectionBegin();
|
|
Console << "Loading uodata (for maps, only mapfile shown)..." << myendl << "(If they don't open, fix your paths in uox.ini or filenames in maps.dfn)" << myendl;
|
|
loadTileData(uodir);
|
|
loadDFNOverrides() ;
|
|
loadMapAndStatics(mapinfo) ;
|
|
if (uoworlds.empty()){
|
|
Console.error( " Fatal Error: No maps found" );
|
|
Console.error( " Check the settings for DATADIRECTORY in uox.ini" );
|
|
Shutdown( FATAL_UOX3_MAP_NOT_FOUND );
|
|
}
|
|
loadMultis(uodir);
|
|
FileLookup->Dispose(maps_def);
|
|
Console.PrintSectionBegin();
|
|
}
|
|
//===================================================================================
|
|
auto CMulHandler::loadMapAndStatics(const std::map<int,mapdfndata_st> &info) ->void{
|
|
for (const auto &[mapnum,dfndata]:info) {
|
|
uoworlds.insert_or_assign(mapnum, ultimamap(mapnum,dfndata.width,dfndata.height,&tile_info)) ;
|
|
auto rvalue = false ;
|
|
|
|
if (std::filesystem::exists(dfndata.mapuop)) {
|
|
Console << "\t" << dfndata.mapuop.string() << "(/" << dfndata.mapuop.filename().string() << ")\t\t";
|
|
rvalue = uoworlds[mapnum].loadUOPTerrainFile(dfndata.mapuop.string());
|
|
}
|
|
else {
|
|
Console << "\t" << dfndata.mapfile.string() << "(/" << dfndata.mapfile.filename().string() << ")\t\t";
|
|
rvalue = uoworlds[mapnum].loadMulTerrainFile(dfndata.mapfile.string());
|
|
if (rvalue) {
|
|
uoworlds[mapnum].applyTerrainDiff(dfndata.mapdiffl.string(), dfndata.mapdiff.string());
|
|
}
|
|
}
|
|
if (rvalue){
|
|
rvalue = uoworlds[mapnum].loadArt(dfndata.stamul.string(), dfndata.staidx.string()) ;
|
|
if (rvalue) {
|
|
uoworlds[mapnum].applyDiff(dfndata.stadiffl.string(), dfndata.stadiffi.string(), dfndata.stadiff.string());
|
|
}
|
|
else {
|
|
// this is intersting, we found the terrain, but not the art. Regardless, lets delete it
|
|
auto iter = uoworlds.find(mapnum) ;
|
|
if (iter != uoworlds.end()){
|
|
uoworlds.erase(iter);
|
|
}
|
|
}
|
|
}
|
|
if (!rvalue) {
|
|
Console.PrintFailed();
|
|
}
|
|
else {
|
|
Console.PrintDone();
|
|
}
|
|
}
|
|
}
|
|
//================================================================================================
|
|
auto CMulHandler::artAt(std::int16_t x, std::int16_t y, std::uint8_t world) ->std::vector<tile_t>& {
|
|
static std::vector<tile_t> empty;
|
|
try {
|
|
return uoworlds.at(world).art(x,y);
|
|
}
|
|
catch(...) {
|
|
return empty;
|
|
}
|
|
}
|
|
//================================================================================================
|
|
auto CMulHandler::artAt(std::int16_t x, std::int16_t y, std::uint8_t world) const -> const std::vector<tile_t>& {
|
|
static std::vector<tile_t> empty;
|
|
try {
|
|
return uoworlds.at(world).art(x,y);
|
|
}
|
|
catch(...) {
|
|
return empty;
|
|
}
|
|
}
|
|
//=============================================================================================
|
|
auto CMulHandler::sizeOfMap(std::uint8_t worldNumber) const ->std::pair<int,int> {
|
|
return uoworlds.at(worldNumber).size() ;
|
|
}
|
|
auto CMulHandler::diffCountForMap(std::uint8_t worldNumber) const ->std::pair<int,int> {
|
|
return std::make_pair(uoworlds.at(worldNumber).diffArt(),uoworlds.at(worldNumber).diffTerrain()) ;
|
|
}
|
|
|
|
//=============================================================================================
|
|
auto CMulHandler::MultiHeight(CItem *i, std::int16_t x, std::int16_t y, std::int8_t oldZ, std::int8_t maxZ, bool checkHeight) ->std::int8_t{
|
|
|
|
UI16 multiID = static_cast<UI16>(i->GetID() - 0x4000);
|
|
|
|
SI08 mHeight = ILLEGAL_Z;
|
|
SI08 tmpTop = ILLEGAL_Z;
|
|
|
|
if(multiExists(multiID)){
|
|
|
|
const SI16 baseX = i->GetX();
|
|
const SI16 baseY = i->GetY();
|
|
const SI08 baseZ = i->GetZ();
|
|
for( auto &multi : SeekMulti( multiID ).items ) {
|
|
if(( checkHeight || multi.flag ) && ( baseX + multi.offsetx ) == x && ( baseY + multi.offsety ) == y ) {
|
|
if( checkHeight ) {
|
|
// Returns height of highest point of multi
|
|
tmpTop = static_cast<SI08>(baseZ + multi.altitude);
|
|
if( ( tmpTop <= oldZ + maxZ ) && tmpTop > oldZ && tmpTop > mHeight ){
|
|
mHeight = tmpTop;
|
|
}
|
|
}
|
|
else {
|
|
tmpTop = static_cast<SI08>( baseZ + multi.altitude );
|
|
if( std::abs( tmpTop - oldZ ) <= maxZ ) {
|
|
mHeight = tmpTop + multi.info->ClimbHeight();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return mHeight;
|
|
}
|
|
|
|
//=============================================================================================
|
|
auto CMulHandler::MultiTile(CItem *i, std::int16_t x, std::int16_t y, std::int8_t oldz, bool checkVisible) ->std::uint16_t {
|
|
auto rvalue = std::uint16_t() ;
|
|
if( !i->CanBeObjType( OT_MULTI ) )
|
|
return 0;
|
|
UI16 multiID = static_cast<UI16>(i->GetID() - 0x4000);
|
|
SI32 length = 0;
|
|
|
|
if( !multiExists(multiID)) {
|
|
Console << "CMulHandler::MultiTile->Bad length in multi file. Avoiding stall." << myendl;
|
|
auto map1 = Map->SeekMap( i->GetX(), i->GetY(), i->WorldNumber() );
|
|
if( map1.CheckFlag( TF_WET ) ) // is it water?
|
|
i->SetID( 0x4001 );
|
|
else
|
|
i->SetID( 0x4064 );
|
|
length = 0;
|
|
}
|
|
else {
|
|
// Loop through each item that makes up the multi
|
|
// If any of those items intersect with area were are checking, return the ID of that tile
|
|
for( auto &multi : SeekMulti(multiID ).items) {
|
|
if(!checkVisible) {
|
|
if( ( i->GetX() + multi.offsetx == x ) && ( i->GetY() + multi.offsety == y ) ) {
|
|
rvalue = multi.tileid;
|
|
break;
|
|
}
|
|
}
|
|
else if(multi.flag > 0 && ( std::abs( i->GetZ() + multi.altitude - oldz ) <= 1) ) {
|
|
if( ( i->GetX() + multi.offsetx == x ) && ( i->GetY() + multi.offsety == y ) ) {
|
|
rvalue = multi.tileid;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return rvalue;
|
|
}
|
|
|
|
//=============================================================================================
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Purpose - Checks if statics at/above given coordinates blocks movement, etc
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
auto CMulHandler::DoesStaticBlock(std::int16_t x, std::int16_t y, std::int8_t z, std::uint8_t worldNumber, bool checkWater) ->bool {
|
|
auto &artwork = Map->artAt( x, y, worldNumber );
|
|
auto rvalue = false ;
|
|
for (auto &tile:artwork){
|
|
auto elev = static_cast<SI08>(tile.altitude + tile.artInfo->ClimbHeight());
|
|
if( checkWater ) {
|
|
if( elev >= z && tile.altitude <= z && ( tile.CheckFlag( TF_BLOCKING ) || tile.CheckFlag( TF_WET ) )){
|
|
rvalue = true;
|
|
break;
|
|
}
|
|
}
|
|
else{
|
|
if( elev >= z && tile.altitude <= z && ( tile.CheckFlag( TF_BLOCKING ) && !tile.CheckFlag( TF_WET ) )){
|
|
rvalue = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return rvalue;
|
|
}
|
|
//=============================================================================================
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Purpose - Checks if there are any dynamic tiles at given coordinates that block movement
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
auto CMulHandler::DoesDynamicBlock(std::int16_t x, std::int16_t y, std::int8_t z, std::uint8_t worldNumber, std::uint16_t realm, bool checkWater, bool waterWalk, bool checkOnlyMultis, bool checkOnlyNonMultis) ->bool {
|
|
auto rvalue = false ;
|
|
// get the tile id of any dynamic tiles at this spot
|
|
auto dtItem = DynTile( x, y, z, worldNumber, realm, checkOnlyMultis, checkOnlyNonMultis );
|
|
if( ValidateObject( dtItem )) {
|
|
rvalue = true ;
|
|
// Don't allow placing houses on top of immovable, visible dynamic items
|
|
if( dtItem->GetMovable() != 2 || dtItem->GetVisible() != 0 ){
|
|
// Ignore items that are permanently invisible or visible to GMs only
|
|
rvalue = false ;
|
|
if( dtItem->GetVisible() != 1 && dtItem->GetVisible() != 3 ) {
|
|
const UI16 dt = dtItem->GetID();
|
|
if( IsValidTile( dt ) && dt != 0 ) {
|
|
auto tile = SeekTile( dt );
|
|
if( waterWalk ) {
|
|
if( !tile.CheckFlag( TF_WET ) ){
|
|
rvalue = true ;
|
|
}
|
|
}
|
|
else {
|
|
if( tile.CheckFlag( TF_ROOF ) || tile.CheckFlag( TF_BLOCKING ) || (checkWater && tile.CheckFlag( TF_WET ) ) /*|| !tile.CheckFlag( TF_SURFACE ) */ ){
|
|
rvalue = true ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return rvalue;
|
|
}
|
|
//=============================================================================================
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Purpose - Returns which dynamic tile is present at (x,y) or -1 if no tile exists
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
auto CMulHandler::DynTile( std::int16_t x, std::int16_t y, std::int8_t z, std::uint8_t worldNumber, std::uint16_t realm, bool checkOnlyMultis, bool checkOnlyNonMultis ) ->CItem *{
|
|
auto rvalue = static_cast<CItem*>(nullptr) ;
|
|
for( auto &CellResponse:MapRegion->PopulateList( x, y, worldNumber ) ) {
|
|
if( CellResponse ){
|
|
auto regItems = CellResponse->GetItemList();
|
|
for (const auto &Item : regItems->collection()){
|
|
if( ValidateObject( Item ) && ( Item->GetInstanceID() == realm) ){
|
|
if( !checkOnlyNonMultis ){
|
|
if( Item->GetID( 1 ) >= 0x40 && ( Item->GetObjType() == OT_MULTI || Item->CanBeObjType( OT_MULTI ) ) ){
|
|
auto deetee = MultiTile( Item, x, y, z, false );
|
|
if( deetee > 0 ){
|
|
rvalue = Item;
|
|
break;
|
|
}
|
|
}
|
|
else if( Item->GetMulti() != INVALIDSERIAL || ValidateObject( calcMultiFromSer( Item->GetOwner() ) ) ){
|
|
if( (Item->GetX() == x) && (Item->GetY() == y )){
|
|
rvalue = Item ;
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
else if( !checkOnlyMultis && (Item->GetX() == x) && (Item->GetY() == y) ){
|
|
rvalue = Item ;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (rvalue) {
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
return rvalue;
|
|
}
|
|
//=============================================================================================
|
|
//=============================================================================================
|
|
//| Purpose - Checks if the map tile at the given coordinates block movement
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
auto CMulHandler::DoesMapBlock(std::int16_t x, std::int16_t y, std::int8_t z, std::uint8_t worldNumber, bool checkWater, bool waterWalk, bool checkMultiPlacement, bool checkForRoad) ->bool {
|
|
auto rvalue = false ;
|
|
if( checkWater || waterWalk ) {
|
|
const auto &map = SeekMap( x, y, worldNumber );
|
|
if(( checkMultiPlacement && map.altitude == z ) || ( !checkMultiPlacement && map.altitude >= z && map.altitude - z < 16 )) {
|
|
if( z == ILLEGAL_Z ) {
|
|
rvalue = true ;
|
|
}
|
|
else if( waterWalk ) {
|
|
if( !map.CheckFlag(TF_WET)) {
|
|
rvalue = true ;
|
|
}
|
|
}
|
|
else {
|
|
if( map.CheckFlag( TF_WET ) || map.CheckFlag( TF_BLOCKING )){
|
|
rvalue = true ;
|
|
}
|
|
}
|
|
// Check that player is not attempting to place a multi on a road
|
|
if (!rvalue){
|
|
if( checkWater && checkMultiPlacement && checkForRoad ) {
|
|
if (map.terrainInfo->isRoadID()) {
|
|
rvalue = true ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return rvalue ;
|
|
}
|
|
//=============================================================================================
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Purpose - Checks to see whether any statics at given coordinates has a specific flag
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
auto CMulHandler::CheckStaticFlag(std::int16_t x, std::int16_t y, std::int8_t z, std::uint8_t worldNumber, TileFlags toCheck, bool checkSpawnSurface) ->bool{
|
|
auto rvalue = checkSpawnSurface ;
|
|
for( const auto &tile:Map->artAt( x, y, worldNumber ) ) {
|
|
const SI08 elev = static_cast<SI08>( tile.altitude );
|
|
const SI08 tileHeight = static_cast<SI08>( tile.artInfo->ClimbHeight() );
|
|
if( checkSpawnSurface ) {
|
|
// Special case used when checking for spawn surfaces
|
|
if(( z >= elev && z <= ( elev + tileHeight )) && !tile.CheckFlag( toCheck ) ){
|
|
rvalue = false ;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
// Generic check exposed to JS
|
|
if(( z >= elev && z <= ( elev + tileHeight )) && tile.CheckFlag( toCheck ) ){
|
|
rvalue = true; // Found static with specified flag
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return rvalue;
|
|
}
|
|
|
|
//=============================================================================================
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Purpose - Checks to see whether any dynamics at given coordinates has a specific flag
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
auto CMulHandler::CheckDynamicFlag(std::int16_t x, std::int16_t y, std::int8_t z, std::uint8_t worldNumber, std::uint16_t realm, TileFlags toCheck) ->bool {
|
|
|
|
for( auto &CellResponse: MapRegion->PopulateList( x, y, worldNumber )) {
|
|
if( CellResponse ) {
|
|
|
|
auto regItems = CellResponse->GetItemList();
|
|
for (const auto &Item : regItems->collection()){
|
|
if( ValidateObject( Item ) && (Item->GetInstanceID() == realm )){
|
|
|
|
if( (Item->GetID( 1 ) >= 0x40) && ( (Item->GetObjType() == OT_MULTI) || (Item->CanBeObjType( OT_MULTI )) ) ){
|
|
// Found a multi
|
|
// Look for a multi item at specific location
|
|
auto multiID = static_cast<UI16>( Item->GetID() - 0x4000 );
|
|
for( auto &multiItem : SeekMulti( multiID ).items ){
|
|
if( multiItem.flag > 0 && ( abs( Item->GetZ() + multiItem.altitude - z ) <= 1 ) ){
|
|
if( ( Item->GetX() + multiItem.offsetx == x ) && ( Item->GetY() + multiItem.offsety == y ) ){
|
|
if( SeekTile( multiItem.tileid ).CheckFlag( toCheck ) ){
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// Item is not a multi
|
|
if( (Item->GetX() == x) && (Item->GetY() == y) && (Item->GetZ() == z) ){
|
|
|
|
auto itemZ = Item->GetZ();
|
|
const SI08 tileHeight = static_cast<SI08>( TileHeight( Item->GetID() ) );
|
|
if(( itemZ == z && itemZ + tileHeight > z ) || ( itemZ < z && itemZ + tileHeight >= z )){
|
|
if( SeekTile( Item->GetID() ).CheckFlag( toCheck ) ){
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//=============================================================================================
|
|
//| Purpose - Checks to see whether given tile ID has a specified flag
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
auto CMulHandler::CheckTileFlag(std::uint16_t itemID, TileFlags flagToCheck) ->bool {
|
|
if( !SeekTile( itemID ).CheckFlag( flagToCheck )){
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
//=============================================================================================
|
|
//| Purpose - Top of statics at/above given coordinates
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
auto CMulHandler::StaticTop(std::int16_t x, std::int16_t y, std::int8_t z, std::uint8_t worldNumber, std::int8_t maxZ) ->std::int8_t {
|
|
auto top = ILLEGAL_Z;
|
|
|
|
const auto &artwork = Map->artAt(x, y, worldNumber );
|
|
for (const auto &tile:artwork){
|
|
auto tempTop = static_cast<std::int8_t>(tile.altitude + tile.artInfo->ClimbHeight());
|
|
if( ( tempTop <= z + maxZ ) && ( tempTop > top ) )
|
|
top = tempTop;
|
|
}
|
|
return top;
|
|
}
|
|
|
|
//=============================================================================================
|
|
//| Purpose - This was fixed to actually return the *elevation* of dynamic items at/above given coordinates
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
auto CMulHandler::DynamicElevation(std::int16_t x, std::int16_t y, std::int8_t z, std::uint8_t worldNumber, std::int8_t maxZ, std::uint16_t realm) ->std::int8_t {
|
|
auto illz = ILLEGAL_Z;
|
|
|
|
auto MapArea = MapRegion->GetMapRegion( MapRegion->GetGridX( x ), MapRegion->GetGridY( y ), worldNumber );
|
|
if( MapArea ) {
|
|
auto regItems = MapArea->GetItemList();
|
|
for (const auto tempItem : regItems->collection()){
|
|
if( ValidateObject( tempItem ) || tempItem->GetInstanceID() != realm ){
|
|
if( tempItem->GetID( 1 ) >= 0x40 && tempItem->CanBeObjType( OT_MULTI )){
|
|
illz = MultiHeight( tempItem, x, y, z, maxZ );
|
|
}
|
|
else if( tempItem->GetX() == x && tempItem->GetY() == y ){
|
|
SI08 ztemp = (SI08)(tempItem->GetZ() + TileHeight( tempItem->GetID() ));
|
|
if( ( ztemp <= z + maxZ ) && ztemp > z ){
|
|
illz = ztemp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return illz;
|
|
}
|
|
//=============================================================================================
|
|
//| Purpose - Returns the map elevation at given coordinates, we'll assume since its land
|
|
//| the height is inherently 0
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
auto CMulHandler::MapElevation(std::int16_t x, std::int16_t y, std::uint8_t worldNumber) ->std::int8_t {
|
|
const auto &map = SeekMap( x, y, worldNumber );
|
|
// make sure nothing can move into black areas
|
|
if (map.isVoid()){
|
|
return ILLEGAL_Z;
|
|
}
|
|
return map.altitude;
|
|
}
|
|
|
|
//=============================================================================================
|
|
//| Purpose - Height of a given tile (If climbable we return 1/2 its height).
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
auto CMulHandler::TileHeight(std::uint16_t tilenum) ->std::int8_t {
|
|
return SeekTile(tilenum).ClimbHeight() ;
|
|
}
|
|
|
|
//=============================================================================================
|
|
//| Purpose - Returns new height of player who walked to X/Y but from OLDZ
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
auto CMulHandler::Height(std::int16_t x, std::int16_t y, std::int8_t z, std::uint8_t worldNumber, std::uint16_t realm) ->std::int8_t {
|
|
// let's check in this order.. dynamic, static, then the map
|
|
auto dynz = DynamicElevation( x, y, x, worldNumber, MAX_Z_STEP, realm );
|
|
if( ILLEGAL_Z != dynz )
|
|
return dynz;
|
|
|
|
auto staticz = StaticTop( x, y, x, worldNumber, MAX_Z_STEP );
|
|
if( ILLEGAL_Z != staticz )
|
|
return staticz;
|
|
|
|
return MapElevation( x, y, worldNumber );
|
|
}
|
|
|
|
//=============================================================================================
|
|
//| Purpose - Returns whether give coordinates are inside a building by checking if there is
|
|
//| a multi or static above them
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
auto CMulHandler::inBuilding( std::int16_t x, std::int16_t y, std::int8_t z, std::uint8_t worldNumber, std::uint16_t realm) ->bool {
|
|
auto dynz = Map->DynamicElevation( x, y, z, worldNumber, static_cast<std::int8_t>(127), realm );
|
|
if( dynz > ( z + 10)) {
|
|
return true;
|
|
}
|
|
else {
|
|
const SI08 staticz = Map->StaticTop( x, y, z, worldNumber, static_cast<std::int8_t>(127));
|
|
if( staticz > ( z + 10)){
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//=============================================================================================
|
|
//| Purpose - Finds the corners of a multi object
|
|
//o--------------------------------------------------------------------------
|
|
auto CMulHandler::MultiArea(CMultiObj *i, std::int16_t &x1, std::int16_t &y1, std::int16_t &x2, std::int16_t &y2) ->void {
|
|
if(ValidateObject(i)){
|
|
const SI16 xAdd = i->GetX();
|
|
const SI16 yAdd = i->GetY();
|
|
|
|
const UI16 multiNum = static_cast<UI16>(i->GetID() - 0x4000);
|
|
if (multiExists(multiNum)) {
|
|
auto structure = SeekMulti(multiNum);
|
|
x1 = static_cast<SI16>(structure.min_x + xAdd);
|
|
x2 = static_cast<SI16>(structure.max_x + xAdd);
|
|
y1 = static_cast<SI16>(structure.min_y + yAdd);
|
|
y2 = static_cast<SI16>(structure.max_y + yAdd);
|
|
}
|
|
}
|
|
}
|
|
//=============================================================================
|
|
auto CMulHandler::multiExists(std::uint16_t multinum) const ->bool{
|
|
return multi_data.exists(multinum);
|
|
}
|
|
//=============================================================================================
|
|
//| Purpose - Checks if multinum/id can be found in multi data.
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
auto CMulHandler::SeekMulti( std::uint16_t multinum ) const -> const collection_item &{
|
|
return multi_data.multi(multinum);
|
|
}
|
|
//=============================================================================================
|
|
//| Purpose - Checks if a given tile number falls within the range of tiles loaded in memory
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
auto CMulHandler::IsValidTile(std::uint16_t tileNum) const ->bool{
|
|
bool retVal = true;
|
|
if( (tileNum == INVALIDID) || (tileNum >= tile_info.sizeArt())) {
|
|
retVal = false;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
//=============================================================================================
|
|
//| Purpose - Fetches data about a specific tile from memory. Non-High Seas version
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
auto CMulHandler::SeekTile(std::uint16_t tileNum) ->CTile&{
|
|
//7.0.8.2 tiledata and earlier
|
|
if( !IsValidTile( tileNum ) ) {
|
|
Console.warning( oldstrutil::format("Invalid tile access, the offending tile number is %u", tileNum) );
|
|
static CTile emptyTile;
|
|
return emptyTile;
|
|
}
|
|
else{
|
|
return tile_info.art(tileNum);
|
|
}
|
|
}
|
|
//=============================================================================================
|
|
//| Purpose - Fetches data about a specific tile from memory. Non-High Seas version
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
auto CMulHandler::SeekTile(std::uint16_t tileNum) const ->const CTile&{
|
|
//7.0.8.2 tiledata and earlier
|
|
if( !IsValidTile( tileNum ) ) {
|
|
Console.warning( oldstrutil::format("Invalid tile access, the offending tile number is %u", tileNum) );
|
|
static CTile emptyTile;
|
|
return emptyTile;
|
|
}
|
|
else{
|
|
return tile_info.art(tileNum);
|
|
}
|
|
}
|
|
//=============================================================================================
|
|
//| Purpose - Fetches data about a specific land tile from memory. Non-High Seas version
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
auto CMulHandler::SeekLand(std::uint16_t landNum) ->CLand&{
|
|
if( landNum == INVALIDID || landNum >=tile_info.sizeTerrain()){
|
|
Console.warning( oldstrutil::format("Invalid land access, the offending land number is %u", landNum) );
|
|
static CLand emptyTile;
|
|
return emptyTile;
|
|
}
|
|
else {
|
|
return tile_info.terrain(landNum);
|
|
}
|
|
}
|
|
//=============================================================================================
|
|
//| Purpose - Fetches data about a specific land tile from memory. Non-High Seas version
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
auto CMulHandler::SeekLand(std::uint16_t landNum) const ->const CLand&{
|
|
if( landNum == INVALIDID || landNum >=tile_info.sizeTerrain()){
|
|
Console.warning( oldstrutil::format("Invalid land access, the offending land number is %u", landNum) );
|
|
static CLand emptyTile;
|
|
return emptyTile;
|
|
}
|
|
else {
|
|
return tile_info.terrain(landNum);
|
|
}
|
|
}
|
|
//=============================================================================================
|
|
//| Purpose - Fetches map data for a specific map/world
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
auto CMulHandler::SeekMap(std::int16_t x, std::int16_t y, std::uint8_t worldNumber) ->tile_t& {
|
|
static tile_t badtile ;
|
|
try {
|
|
return uoworlds.at(worldNumber).terrain(x, y);
|
|
}
|
|
catch (...) {
|
|
std::cerr <<"Bad tile access attempted: "<<x<<","<<y<< static_cast<int>(worldNumber) << std::endl ;
|
|
return badtile ;
|
|
}
|
|
}
|
|
//=============================================================================================
|
|
auto CMulHandler::SeekMap(std::int16_t x, std::int16_t y, std::uint8_t worldNumber) const -> const tile_t& {
|
|
static tile_t badtile ;
|
|
try {
|
|
return uoworlds.at(worldNumber).terrain(x, y);
|
|
}
|
|
catch (...) {
|
|
std::cerr <<"Bad tile access attempted: "<<x<<","<<y<< static_cast<int>(worldNumber) << std::endl ;
|
|
return badtile ;
|
|
}
|
|
}
|
|
//=============================================================================================
|
|
//| Purpose - Checks if location at given coordinates is considered valid for spawning objects
|
|
//| Also used to verify teleport location for NPCs teleporting back to bounding box
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
auto CMulHandler::ValidSpawnLocation(std::int16_t x, std::int16_t y, std::int8_t z, std::uint8_t worldNumber, std::uint16_t realm, bool checkWater) ->bool{
|
|
auto rvalue = false ;
|
|
if( InsideValidWorld( x, y, worldNumber ) ){
|
|
// get the tile id of any dynamic tiles at this spot
|
|
if( !DoesDynamicBlock( x, y, z, worldNumber, realm, checkWater, !checkWater, false, false ) ){
|
|
// if there's a static block here in our way, return false
|
|
if( !DoesStaticBlock( x, y, z, worldNumber, checkWater ) ){
|
|
// if the static isn't a surface return false
|
|
if( CheckStaticFlag( x, y, z, worldNumber, ( checkWater ? TF_SURFACE : TF_WET ), true ) ){
|
|
if( !DoesMapBlock( x, y, z, worldNumber, checkWater, !checkWater, false, false ) ){
|
|
rvalue = true ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return rvalue;
|
|
}
|
|
//=============================================================================================
|
|
//| Purpose - Checks whether given location is valid for house/boat placement
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
auto CMulHandler::ValidMultiLocation(std::int16_t x, std::int16_t y, std::int8_t z, std::uint8_t worldNumber, std::uint16_t realm, bool checkWater, bool checkOnlyOtherMultis, bool checkOnlyNonMultis, bool checkForRoads) ->std::uint8_t {
|
|
|
|
if( !InsideValidWorld( x, y, worldNumber ) )
|
|
return 0;
|
|
|
|
const SI08 elev = Height( x, y, z, worldNumber, realm );
|
|
if( ILLEGAL_Z == elev )
|
|
return 0;
|
|
|
|
// We don't want the house to be halfway embedded in a hill... or hanging off one for that matter.
|
|
if( z != ILLEGAL_Z ) {
|
|
if( elev - z > MAX_Z_STEP ) {
|
|
return 0;
|
|
}
|
|
else if( z - elev > MAX_Z_FALL ) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// get the tile id of any dynamic tiles at this spot
|
|
if( !checkOnlyNonMultis && DoesDynamicBlock( x, y, elev, worldNumber, realm, checkWater, false, checkOnlyOtherMultis, checkOnlyNonMultis ) ) {
|
|
return 2;
|
|
}
|
|
|
|
// if there's a static block here in our way, return false
|
|
if( !checkOnlyOtherMultis && DoesStaticBlock( x, y, elev, worldNumber, checkWater ) ) {
|
|
return 2;
|
|
}
|
|
|
|
if( DoesMapBlock( x, y, elev, worldNumber, checkWater, false, true, checkForRoads ) ) {
|
|
return 0;
|
|
}
|
|
|
|
// Check if house placement is allowed in region
|
|
CTownRegion *calcReg = calcRegionFromXY( x, y, worldNumber, realm );
|
|
if(( !calcReg->CanPlaceHouse() && checkWater) || calcReg->IsDungeon() || ( calcReg->IsGuarded() && checkWater ))
|
|
return 3;
|
|
|
|
// Else, all good!
|
|
return 1;
|
|
}
|
|
//=============================================================================================
|
|
//| Purpose - Returns true if the server has that map in memory
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
auto CMulHandler::MapExists(std::uint8_t worldNumber ) const ->bool {
|
|
auto iter = uoworlds.find(worldNumber);
|
|
return iter != uoworlds.end() ;
|
|
}
|
|
//=============================================================================================
|
|
//| Purpose - Checks if a given location is within the bounds of the specified world
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
auto CMulHandler::InsideValidWorld(std::int16_t x, std::int16_t y, std::uint8_t worldNumber) const ->bool {
|
|
auto rvalue = false ;
|
|
try {
|
|
auto [width,height] = uoworlds.at(worldNumber).size();
|
|
if ((x>=0 && x<width) && (y>=0 && y<height)){
|
|
rvalue = true ;
|
|
}
|
|
}
|
|
catch(...) {
|
|
// Do nothing, all rady set to false
|
|
}
|
|
return rvalue ;
|
|
}
|
|
|
|
//=============================================================================================
|
|
//| Purpose - Returns count of how many maps have been loaded
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
auto CMulHandler::MapCount() const ->std::uint8_t {
|
|
// This ASSUMES the map numbers are consequtive. You dont have say map 0,1,3 <<<< missing 2
|
|
return static_cast<std::uint8_t>(uoworlds.size());
|
|
}
|
|
|
|
//===============================================================================================
|
|
// tileinfo
|
|
//===============================================================================================
|
|
//===============================================================================================
|
|
|
|
//===============================================================================================
|
|
tileinfo::tileinfo(const std::string &filename):isHS_format(false){
|
|
if (!filename.empty()){
|
|
this->load(filename) ;
|
|
}
|
|
}
|
|
//===============================================================================================
|
|
auto tileinfo::load(const std::string &filename) ->bool {
|
|
auto rvalue = false ;
|
|
art_data.clear() ;
|
|
terrain_data.clear() ;
|
|
isHS_format =false ;
|
|
auto path = std::filesystem::path(filename) ;
|
|
if (std::filesystem::exists(path)) {
|
|
if (std::filesystem::file_size(path)>= hssize) {
|
|
isHS_format = true ;;
|
|
}
|
|
auto input = std::ifstream(filename,std::ios::binary) ;
|
|
if (input.is_open()){
|
|
rvalue = true ;
|
|
processTerrain(input) ;
|
|
processArt(input) ;
|
|
}
|
|
}
|
|
return rvalue ;
|
|
}
|
|
//===============================================================================================
|
|
auto tileinfo::terrain(std::uint16_t tileid) const -> const CLand& {
|
|
return terrain_data[tileid] ;
|
|
}
|
|
//===============================================================================================
|
|
auto tileinfo::terrain(std::uint16_t tileid) -> CLand& {
|
|
return terrain_data[tileid] ;
|
|
}
|
|
//===============================================================================================
|
|
auto tileinfo::art(std::uint16_t tileid) const -> const CTile& {
|
|
return art_data[tileid] ;
|
|
}
|
|
//===============================================================================================
|
|
auto tileinfo::art(std::uint16_t tileid) -> CTile& {
|
|
return art_data[tileid] ;
|
|
}
|
|
//===============================================================================================
|
|
auto tileinfo::sizeTerrain() const -> size_t {
|
|
return terrain_data.size();
|
|
}
|
|
//===============================================================================================
|
|
auto tileinfo::sizeArt() const -> size_t {
|
|
return art_data.size() ;
|
|
}
|
|
//===============================================================================================
|
|
auto tileinfo::collectionTerrain() const -> const std::vector<CLand>&{
|
|
return terrain_data;
|
|
}
|
|
//===============================================================================================
|
|
auto tileinfo::collectionTerrain() ->std::vector<CLand> &{
|
|
return terrain_data;
|
|
}
|
|
|
|
//===============================================================================================
|
|
auto tileinfo::collectionArt() const -> const std::vector<CTile>& {
|
|
return art_data;
|
|
}
|
|
//===============================================================================================
|
|
auto tileinfo::collectionArt() ->std::vector<CTile>& {
|
|
return art_data;
|
|
}
|
|
|
|
//===============================================================================================
|
|
auto tileinfo::processTerrain(std::ifstream &input) ->void {
|
|
terrain_data.reserve(0x4000);
|
|
std::uint32_t value32 = 0 ;
|
|
std::uint64_t value64 = 0;
|
|
std::array<char,20> string_buffer ;
|
|
string_buffer.fill(0);
|
|
for (auto i=0 ; i< 0x4000;i++){
|
|
// We have to get rid of the header on blocks of information
|
|
// HS has the firt entry messed up
|
|
if (isHS_format) {
|
|
if ( (((i & 0x1F)==0) && (i>0)) || (i == 1)){
|
|
input.read(reinterpret_cast<char*>(&value32),4); // read off the group header
|
|
}
|
|
|
|
}
|
|
else {
|
|
if ( (i & 0x1f) == 0) {
|
|
input.read(reinterpret_cast<char*>(&value32),4); // read off the group header
|
|
}
|
|
}
|
|
// now create the info_t we will use
|
|
auto info = CLand();
|
|
// read off the flags, 32 bit on pre-HS, 64 bit after
|
|
if (isHS_format) {
|
|
input.read(reinterpret_cast<char*>(&value64),8);
|
|
info.flags = std::bitset<64>(value64);
|
|
|
|
}
|
|
|
|
else{
|
|
input.read(reinterpret_cast<char*>(&value32),4);
|
|
info.flags = std::bitset<64>(value32);
|
|
}
|
|
// only other thing for terrain is the texture
|
|
// and name
|
|
input.read(reinterpret_cast<char*>(&info.textureID),2);
|
|
input.read(string_buffer.data(),20);
|
|
if (input.gcount()==20){
|
|
// We need to find the "0", if any
|
|
auto iter = std::find(string_buffer.begin(),string_buffer.end(),0);
|
|
info.name = std::string(string_buffer.begin(),iter);
|
|
terrain_data.push_back(std::move(info));
|
|
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
//===============================================================================================
|
|
auto tileinfo::processArt(std::ifstream &input) ->void {
|
|
art_data.reserve(0xFFFF);
|
|
std::uint32_t value32 = 0 ;
|
|
std::uint64_t value64 = 0;
|
|
std::array<char,20> string_buffer ;
|
|
string_buffer.fill(0);
|
|
if ( !(input.eof() || input.bad())){
|
|
auto loopcount = 0 ;
|
|
do {
|
|
if ( (loopcount & 0x1f) == 0) {
|
|
input.read(reinterpret_cast<char*>(&value32),4); // read off the group header
|
|
}
|
|
auto info = CTile();
|
|
if (isHS_format) {
|
|
input.read(reinterpret_cast<char*>(&value64),8);
|
|
info.flags = std::bitset<64>(value64);
|
|
|
|
|
|
}
|
|
else {
|
|
input.read(reinterpret_cast<char*>(&value32),4);
|
|
info.flags = std::bitset<64>(value32);
|
|
|
|
}
|
|
input.read(reinterpret_cast<char*>(&info.weight),1);
|
|
input.read(reinterpret_cast<char*>(&info.layer),1);
|
|
//misc data
|
|
input.read(reinterpret_cast<char*>(&info.unknown1),2);
|
|
//Second unkown
|
|
input.read(reinterpret_cast<char*>(&info.unknown2),1);
|
|
// Quantity
|
|
input.read(reinterpret_cast<char*>(&info.quantity),1);
|
|
// Animation
|
|
input.read(reinterpret_cast<char*>(&info.animation),2);
|
|
|
|
//Third a byte
|
|
input.read(reinterpret_cast<char*>(&info.unknown3),1);
|
|
|
|
// Hue/Value
|
|
input.read(reinterpret_cast<char*>(&info.hue),1);
|
|
//stacking offset/value = 16 bits, but UOX3 doesn't know that, so two eight bit unkowns
|
|
input.read(reinterpret_cast<char*>(&info.unknown4),1);
|
|
input.read(reinterpret_cast<char*>(&info.unknown5),1);
|
|
// Height
|
|
input.read(reinterpret_cast<char*>(&info.height),1);
|
|
// and name
|
|
input.read(string_buffer.data(),20);
|
|
if ((input.gcount()==20) && input.good()){
|
|
// We need to find the "0", if any
|
|
auto iter = std::find(string_buffer.begin(),string_buffer.end(),0);
|
|
info.name = std::string(string_buffer.begin(),iter);
|
|
art_data.push_back(std::move(info));
|
|
}
|
|
loopcount++;
|
|
|
|
}while( (!input.eof()) && input.good());
|
|
|
|
}
|
|
|
|
}
|
|
//===============================================================================================
|
|
auto tileinfo::totalMemory() const ->size_t {
|
|
auto flag = isHS_format?8:4 ;
|
|
auto terrain = flag + 20 + 2 ;
|
|
auto art = flag + 20 + 23 ;
|
|
return static_cast<size_t>((terrain*terrain_data.size()) + (art * art_data.size()) );
|
|
}
|
|
//====================================================================================================
|
|
// terrainblock
|
|
//====================================================================================================
|
|
//=========================================================
|
|
terrainblock::terrainblock(std::uint8_t *data,const tileinfo *info){
|
|
if (data != nullptr) {
|
|
load(data,info) ;
|
|
}
|
|
}
|
|
//=========================================================
|
|
auto terrainblock::load(std::uint8_t *data,const tileinfo *info) ->void {
|
|
if (data !=nullptr){
|
|
data +=4 ;
|
|
for (auto y= 0 ; y < 8 ;++y){
|
|
for (auto x= 0 ; x < 8 ;++x){
|
|
auto tileid = std::uint16_t(0) ;
|
|
// we copy here, because of potential alignment issues
|
|
std::copy(data,data+2,reinterpret_cast<std::uint8_t*>(&(tileid)));
|
|
_tiles[x][y].tileid = tileid ;
|
|
if (info){
|
|
_tiles[x][y].terrainInfo = &info->terrain(tileid);
|
|
}
|
|
_tiles[x][y].altitude = *(reinterpret_cast<std::int8_t*>(data+2));
|
|
data += 3 ; // advance data three bytes ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//=========================================================
|
|
auto terrainblock::tile(int x, int y) ->tile_t& {
|
|
return _tiles.at(x).at(y) ;
|
|
}
|
|
//=========================================================
|
|
auto terrainblock::tile(int x, int y) const ->const tile_t& {
|
|
return _tiles.at(x).at(y) ;
|
|
}
|
|
|
|
//================================================================================
|
|
// artblock
|
|
//================================================================================
|
|
|
|
//=========================================================
|
|
artblock::artblock(int length,std::uint8_t *data,const tileinfo *info){
|
|
if (data != nullptr) {
|
|
load(length,data,info) ;
|
|
}
|
|
}
|
|
|
|
//=========================================================
|
|
auto artblock::tile(int x, int y) ->std::vector<tile_t>& {
|
|
return _tiles.at(x).at(y) ;
|
|
}
|
|
//=========================================================
|
|
auto artblock::tile(int x, int y) const ->const std::vector<tile_t>& {
|
|
return _tiles.at(x).at(y) ;
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
auto artblock::clear() ->void {
|
|
for (auto &row : _tiles){
|
|
for (auto &tile: row){
|
|
tile.clear();
|
|
}
|
|
}
|
|
}
|
|
//==========================================================
|
|
auto artblock::load(int length, std::istream &input,const tileinfo *info) ->void{
|
|
this->clear() ;
|
|
auto count = length/7 ;
|
|
auto x = std::uint8_t(0);
|
|
auto y = std::uint8_t(0) ;
|
|
auto alt = std::int8_t(0) ;
|
|
for (auto i=0; i<count ;++i){
|
|
auto arttile = tile_t() ;
|
|
arttile.type = tiletype_t::art ;
|
|
input.read(reinterpret_cast<char*>(&arttile.tileid),2) ;
|
|
input.read(reinterpret_cast<char*>(&x),1);
|
|
input.read(reinterpret_cast<char*>(&y),1);
|
|
input.read(reinterpret_cast<char*>(&alt),1);
|
|
arttile.altitude = alt ;
|
|
input.read(reinterpret_cast<char*>(&arttile.static_hue),2);
|
|
if (info){
|
|
arttile.artInfo = &info->art(arttile.tileid);
|
|
}
|
|
tile(x,y).push_back(std::move(arttile));
|
|
|
|
}
|
|
}
|
|
//==========================================================
|
|
auto artblock::load(int length,std::uint8_t *data,const tileinfo *info) ->void{
|
|
this->clear() ;
|
|
|
|
auto count = length/7 ;
|
|
auto x = std::uint8_t(0);
|
|
auto y = std::uint8_t(0) ;
|
|
for (auto i=0; i<count ;++i){
|
|
auto arttile = tile_t() ;
|
|
arttile.type = tiletype_t::art ;
|
|
std::copy(data,data+2,reinterpret_cast<char*>(&arttile.tileid)) ;
|
|
x = *(data+2) ;
|
|
y = *(data+3);
|
|
arttile.altitude = static_cast<int>(*(reinterpret_cast<std::int8_t*>(data+4)));
|
|
std::copy(data+5,data+7,reinterpret_cast<char*>(&arttile.static_hue)) ;
|
|
if (info){
|
|
arttile.artInfo = &info->art(arttile.tileid);
|
|
}
|
|
|
|
tile(x,y).push_back(std::move(arttile));
|
|
data +=7 ;
|
|
}
|
|
}
|
|
|
|
//================================================================================
|
|
// ultimamap
|
|
//================================================================================
|
|
//===============================================================================
|
|
ultimamap::ultimamap() :_diffcount(0),_diffterrain(0),_width(0),_height(0),tile_info(nullptr){
|
|
}
|
|
//=========================================================
|
|
ultimamap::ultimamap(int mapnum,int width,int height,const tileinfo *info):ultimamap(){
|
|
_width = width ;
|
|
_height= height ;
|
|
tile_info = info ;
|
|
_mapnum = mapnum ;
|
|
auto size = _mapsizes.at(mapnum) ;
|
|
if (_width == 0){
|
|
_width = size.first ;
|
|
}
|
|
if (_height == 0){
|
|
_height = size.second ;
|
|
}
|
|
_terrain.resize( (_width/8) * (_height/8) );
|
|
_art.resize(_terrain.size()) ;
|
|
isUOP = true ;
|
|
}
|
|
//=========================================================
|
|
auto ultimamap::width() const ->int {
|
|
return _width ;
|
|
}
|
|
//=========================================================
|
|
auto ultimamap::height() const ->int {
|
|
return _height ;
|
|
}
|
|
//=========================================================
|
|
auto ultimamap::setSize(int width, int height) ->void {
|
|
_width = width ;
|
|
_height = height ;
|
|
_terrain.resize( (_width/8) * (_height/8) );
|
|
_art.resize(_terrain.size()) ;
|
|
}
|
|
//=========================================================
|
|
auto ultimamap::size() const ->std::pair<int,int> {
|
|
return std::make_pair(static_cast<int>(_width),static_cast<int>(_height));
|
|
}
|
|
|
|
//=========================================================
|
|
auto ultimamap::diffArt() const ->int {
|
|
return _diffcount ;
|
|
}
|
|
//=========================================================
|
|
auto ultimamap::diffTerrain() const ->int {
|
|
return _diffterrain ;
|
|
}
|
|
//===========================================================
|
|
auto ultimamap::loadUOPTerrainFile(const std::string &filename) ->bool {
|
|
auto hash = this->format("build/map%ilegacymul/%s", _mapnum,"%.8u.dat");
|
|
//auto filename = this->format("map%iLegacyMUL.uop",_mapnum);
|
|
isUOP = loadUOP(filename, 0x300, hash);
|
|
return isUOP ;
|
|
}
|
|
//===========================================================
|
|
auto ultimamap::loadMulTerrainFile(const std::string &filename) ->bool {
|
|
auto rvalue = false ;
|
|
auto mul = std::ifstream(filename,std::ios::binary) ;
|
|
if (mul.is_open()){
|
|
rvalue = true;
|
|
isUOP = false ;
|
|
auto data = std::vector<std::uint8_t>(196,0);
|
|
auto blocknum = 0 ;
|
|
while (mul.good() && !mul.eof()) {
|
|
mul.read(reinterpret_cast<char*>(data.data()),196);
|
|
if (mul.gcount() == 196) {
|
|
loadTerrainBlock(blocknum, data.data());
|
|
}
|
|
blocknum++;
|
|
}
|
|
}
|
|
return rvalue ;
|
|
}
|
|
//=========================================================
|
|
auto ultimamap::loadArt(const std::string &mulfile, const std::string &idxfile)->bool {
|
|
auto idx = std::ifstream(idxfile,std::ios::binary);
|
|
auto rvalue = idx.is_open();
|
|
if (rvalue){
|
|
auto mul = std::ifstream(mulfile,std::ios::binary);
|
|
rvalue = mul.is_open();
|
|
if (rvalue) {
|
|
auto offset = std::uint32_t(0) ;
|
|
auto length = std::int32_t(0) ;
|
|
auto extra = std::uint32_t(0) ;
|
|
auto block = 0 ;
|
|
while (idx.good()&& !idx.eof()){
|
|
idx.read(reinterpret_cast<char*>(&offset),4);
|
|
idx.read(reinterpret_cast<char*>(&length),4);
|
|
idx.read(reinterpret_cast<char*>(&extra),4);
|
|
if ((length >0) && (idx.gcount()==4)) {
|
|
mul.seekg(offset,std::ios::beg);
|
|
if (block < _art.size()){
|
|
_art[block].load(length, mul,tile_info);
|
|
}
|
|
}
|
|
++block ;
|
|
}
|
|
}
|
|
}
|
|
return rvalue ;
|
|
}
|
|
//=========================================================
|
|
auto ultimamap::applyDiff(const std::string &difflpath,const std::string &diffipath, const std::string &diffpath) ->int {
|
|
_diffcount = 0 ;
|
|
auto diffblock = std::ifstream(difflpath,std::ios::binary) ;
|
|
if (diffblock.is_open()){
|
|
auto diffidx = std::ifstream(diffipath,std::ios::binary) ;
|
|
if (diffidx.is_open()){
|
|
auto diff = std::ifstream(diffpath,std::ios::binary) ;
|
|
if (diff.is_open()){
|
|
auto blocknum = std::uint32_t(0) ;
|
|
auto offset = std::uint32_t(0);
|
|
auto length = std::int32_t(0) ;
|
|
auto extra = std::uint32_t(0);
|
|
while (diffblock.good() && !diffblock.eof()){
|
|
diffblock.read(reinterpret_cast<char*>(&blocknum),4);
|
|
diffidx.read(reinterpret_cast<char*>(&offset),4);
|
|
diffidx.read(reinterpret_cast<char*>(&length),4);
|
|
diffidx.read(reinterpret_cast<char*>(&extra),4);
|
|
if ((diffblock.gcount()==4) && (diffidx.gcount()==4)){
|
|
if (blocknum< _art.size()){
|
|
if (length >0){
|
|
diff.seekg(offset,std::ios::beg);
|
|
_art[blocknum].load(length, diff,tile_info);
|
|
}
|
|
else {
|
|
_art[blocknum].clear();
|
|
}
|
|
}
|
|
++_diffcount;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
return _diffcount ;
|
|
}
|
|
//=========================================================
|
|
auto ultimamap::applyTerrainDiff(const std::string &difflpath,const std::string &diffpath) ->int {
|
|
auto diffblock = std::ifstream(difflpath,std::ios::binary) ;
|
|
_diffterrain = 0 ;
|
|
if (diffblock.is_open()) {
|
|
auto diff = std::ifstream(diffpath,std::ios::binary) ;
|
|
if (diff.is_open()) {
|
|
auto blocknum = std::uint32_t(0) ;
|
|
auto data = std::vector<std::uint8_t>(196,0) ;
|
|
while (diffblock.good() && !diffblock.eof()){
|
|
diffblock.read(reinterpret_cast<char*>(&blocknum),4) ;
|
|
if (diffblock.gcount()==4) {
|
|
diff.read(reinterpret_cast<char*>(data.data()),196);
|
|
if (diff.gcount()==196){
|
|
_terrain[blocknum].load(data.data(),tile_info);
|
|
_diffterrain++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return _diffterrain;
|
|
|
|
}
|
|
//=========================================================
|
|
auto ultimamap::calcBlock(int x,int y) const ->int {
|
|
// How many blocks is x across?
|
|
return ((x/8)*(_height/8)) + (y/8) ;
|
|
}
|
|
//=========================================================
|
|
auto ultimamap::calcXYOffset(int block) const ->std::pair<int,int> {
|
|
auto x = (block / (_height/8)) * 8 ;
|
|
auto y = (block % (_height/8)) * 8;
|
|
return std::make_pair(x, y);
|
|
}
|
|
//=========================================================
|
|
auto ultimamap::blockAndIndexFor(int x, int y) const ->std::tuple<int, int, int>{
|
|
auto block = calcBlock(x, y);
|
|
auto offset = calcXYOffset(block);
|
|
return std::make_tuple(block,x-offset.first,y-offset.second);
|
|
}
|
|
//=========================================================
|
|
auto ultimamap::processEntry(std::size_t entry, std::size_t index, std::vector<std::uint8_t> &data) ->bool {
|
|
auto count = data.size()/196 ;
|
|
auto block = (static_cast<int>(index) * 0xC4000)/196 ;
|
|
for (auto i=0 ;i<count;++i){
|
|
auto ptr = data.data()+(i*196);
|
|
if (block < _terrain.size()){
|
|
loadTerrainBlock(block, ptr);
|
|
}
|
|
++block ;
|
|
}
|
|
return true ;
|
|
}
|
|
//=========================================================
|
|
auto ultimamap::loadTerrainBlock(int blocknum,std::uint8_t *data)->void {
|
|
_terrain[blocknum].load(data,tile_info);
|
|
}
|
|
|
|
//=========================================================
|
|
auto ultimamap::terrain(int x, int y) const ->const tile_t& {
|
|
auto [blocknum,xoffset,yoffset] = blockAndIndexFor(x, y);
|
|
return _terrain[blocknum].tile(xoffset, yoffset);
|
|
}
|
|
//=========================================================
|
|
auto ultimamap::terrain(int x, int y) ->tile_t& {
|
|
auto [blocknum,xoffset,yoffset] = blockAndIndexFor(x, y);
|
|
return _terrain[blocknum].tile(xoffset, yoffset);
|
|
|
|
}
|
|
//=========================================================
|
|
auto ultimamap::art(int x, int y) const ->const std::vector<tile_t>& {
|
|
auto [blocknum,xoffset,yoffset] = blockAndIndexFor(x, y);
|
|
return _art[blocknum].tile(xoffset, yoffset);
|
|
|
|
}
|
|
//=========================================================
|
|
auto ultimamap::art(int x, int y) ->std::vector<tile_t>& {
|
|
auto [blocknum,xoffset,yoffset] = blockAndIndexFor(x, y);
|
|
return _art[blocknum].tile(xoffset, yoffset);
|
|
}
|