uox3/source/fileio.cpp
Xoduz e54dec97d8 Fishing revamp
Extended Character JS Method DoAction() to support two additional parameters - frameCount and playBackwards - which define the length of the animation (defaults to 7) and whether the animation should be played backwards (defaults to false). Note that this only works for old-style animations, and thus is not available for Gargoyle characters. Updated method syntax:
	.DoAction( actionID ) // Old animation packet
	.DoAction( actionID, null, frameCount) // Old animation packet with frameCount
	.DoAction( actionID, null, frameCount, frameDelay ) // Old animation packet with frameCount, frameDelay
	.DoAction( actionID, null, frameCount, frameDelay, playBackwards) // Old animation packet with frameCount, frameDelay, playBackwards
	.DoAction( actionID, subActionID ) // New animation packet
Extended fishing JS script to include:
	Fishing special fishing nets, big fish, messages in a bottle/SOS messages and associated shipwreck treasures, with chances of attracting sea serpents, deep sea serpents, water elementals and krakens while fishing.
	Enhanced detection of fishing in shallow vs deep water, with deep water now being defined as any water tile that's 16 or more tiles away from the nearest land area.
	Updated timer system, LoS checks,
Implemented SOS messages and shipwreck locations, with areas valid for potential shipwreck locations being defined in a new section of regions.dfn named [SOSAREAS], which follows the same syntax as the INSTALOG section in the same file. Once a message in a bottle is opened, a SOS message is generated with coordinates for a random shipwreck location, which players can sail to and fish near in order to find a sunken treasure (and other items).
Implemented shipwreck treasures, which come in 3 levels of quality (with an optional 4th quality level available), with increasing amount of gold, gems, scrolls, reagents and magic items (coming in next commit!) per quality level of treasure
Implemented special fishing nets, which can be used to fish up a variety of sea creatures (sea serpents, deep sea serpents, water elementals, kraken), which have a high chance of having SOS bottles on their corpses when defeated
Updated fishinglist.dfn, itemlist.dfn and lootlist.dfn to support new fishing updates
Updated movable state of various items that can be fished up (pillows, some pieces of furniture) to ensure players can move them!
Renamed ORERESPAWNAREA ini setting to RESOURCEAREASIZE (it's also used for ore, and logs) and reduced it from 10 to 8 to divide each world into 8x8 sized resource areas
Fixed a bug where the new fish resource type was not getting regenerated or saved properly
Added INSTANCEID support to INSTALOG section of regions.dfn, and added default WORLD and INSTANCEID entries to all areas
Fixed an issue with Runebooks (js/item/runebook.js) where the coordinates of a rune location would not be displayed properly
Separated map coordinate function from Runebook script to its own script (js/server/data/map_coordinates.js), which other scripts can call upon to get map coords using TriggerEvent() JS function
Ported sextant implementation from code to JS (5033=js/item/sextant.js), and removed the hard-coded version of this.
Updated Item DFN entry for sextants to include script=5033 tag
Fixed a bug in handling of the LOOT tag for NPCs where server would crash if a number was specified behind the item/itemlist to add as loot
The DFN tags PACKITEM and LOOT are now handled similarly when used for NPCs and Items respectively. PACKITEM can be used for adding items with specific IDs, or "list objects" from itemlists.dfn, while LOOT can be used to add items from lootlists.dfn. One big difference between these is that if the optional amount parameter is specified for either tag when used with itemlists/lootlists (PACKITEM=listObject,amount vs LOOT=lootlist,amount), PACKITEM will add the specified amount of the specified/randomly chosen item, while LOOT will add X amount of random items.
Updated implementation of ITEMLISTs and LOOTLISTs to support an optional weighting system, which can be used to adjust the probability of some items being chosen from these lists over other items. In the below example, specialfishingnet has a weight of 10, while the blank entry has a weight of 90, making it 9 times more likely for the blank entry to be chosen than the specialfishingnet! Note that higher weighted items should be sorted first for this to be accurate:
	[LOOTLIST shipwreck_treasure_special]
	{
	90|blank
	10|specialfishingnet
	}
Using bladed weapons on stacks of fish will now cut the entire stack at once, instead of one fish at a time
Updated DFN entries for various sea creatures (dfndata/npc/seacreatures.dfn) to adjust color of NPCs, loot and NPC AI
Extended functionality of JS Function DoMovingEffect() to allow a set of coordinates as source location, without having to specify a character as either the source or target of the effect
Fixed an issue with sea creatures being unable to pathfind in shallow water due to underwater land tiles being treated as blocking
Moved fishing skill out of code and into js for more user friendly control
Added js/item/magicfish.js for the magic fish food
2021-09-20 18:47:54 +08:00

905 lines
29 KiB
C++

#include "uox3.h"
#include "fileio.h"
#include "cServerDefinitions.h"
#include "ssection.h"
#include "cSpawnRegion.h"
#include "scriptc.h"
#include "townregion.h"
#include <stdexcept>
#include <vector>
#include <map>
#include <fstream>
#include <cstring>
#include <filesystem>
#include <cmath>
#include <algorithm>
#include <sstream>
#include "UOPInterface.hpp"
#include <algorithm>
#if PLATFORM != WINDOWS
# include <fcntl.h> // open
# include <sys/mman.h> // mmap, mmunmap
#endif
//o-----------------------------------------------------------------------------------------------o
//| Function - UOXFile::UOXFile( const char* const fileName, const char * const )
//| : memPtr( 0 ), fileSize( 0 ), bIndex( 0 ), usingUOP( false )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Support read-only binary mode.
//o-----------------------------------------------------------------------------------------------o
UOXFile::UOXFile( const char* const fileName, const char * const )
: memPtr( 0 ), fileSize( 0 ), bIndex( 0 ), usingUOP( false )
{
auto strfilename = std::string( fileName );
auto filepath = std::filesystem::path( strfilename );
if( filepath.extension() == ".uop" )
{
// UOP File, get the map #
auto nameonly = filepath.filename();
auto mapnum = std::stoi( nameonly.string().substr( 3, 1 ));
try {
auto rvalue = UOP::loadUOP( strfilename, mapnum );
this->memPtr = std::get<1>( rvalue );
if( this->memPtr != nullptr )
{
this->fileSize = std::get<0>( rvalue );
usingUOP = true;
}
}
catch (...) {
if( this->memPtr != nullptr )
{
delete[] memPtr;
memPtr=nullptr;
}
this->memPtr= nullptr;
this->fileSize = 0;
}
return;
}
#if PLATFORM == WINDOWS
HANDLE hFile = ::CreateFileA(
fileName,
GENERIC_READ,
FILE_SHARE_READ,
nullptr,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
nullptr
);
if( hFile == INVALID_HANDLE_VALUE )
return;
// Store the size of the file, it's used to construct
// the end iterator
fileSize = ::GetFileSize( hFile, nullptr );
HANDLE hMap = ::CreateFileMapping( hFile, nullptr, PAGE_READONLY, 0, 0, nullptr );
if( hMap == nullptr )
{
::CloseHandle( hFile );
return;
}
memPtr = (char*)::MapViewOfFile( hMap, FILE_MAP_READ, 0, 0, 0 );
// We hold both the file handle and the memory pointer.
// We can close the hMap handle now because Windows holds internally
// a reference to it since there is a view mapped.
::CloseHandle( hMap );
// It seems like we can close the file handle as well (because
// a reference is hold by the filemap object).
::CloseHandle( hFile );
#else
// postfix version
// open the file
SI32 fd = open(fileName,
#ifdef O_NOCTTY
O_NOCTTY | // if stdin was closed then opening a file
// would cause the file to become the controlling
// terminal if the filename refers to a tty. Setting
// O_NOCTTY inhibits this.
#endif
O_RDONLY);
if( fd == -1 )
return;
// call fstat to find get information about the file just
// opened (size and file type)
struct stat stat_buf;
if(( fstat( fd, &stat_buf ) != 0 ) || !S_ISREG( stat_buf.st_mode ))
{ // if fstat returns an error or if the file isn't a
// regular file we give up.
close( fd );
return;
}
fileSize = stat_buf.st_size;
// perform the actual mapping
memPtr = (char*)mmap( 0, stat_buf.st_size, PROT_READ, MAP_SHARED, fd, 0 );
// it is safe to close() here. POSIX requires that the OS keeps a
// second handle to the file while the file is mmapped.
close( fd );
#endif
}
UOXFile::~UOXFile()
{
if( usingUOP )
{
delete[] memPtr;
memPtr = nullptr;
}
if( memPtr )
{
#if PLATFORM == WINDOWS
UnmapViewOfFile( memPtr );
#else
munmap(memPtr, fileSize);
#endif
}
}
void UOXFile::seek( size_t offset, UI08 whence )
{
if( whence == SEEK_SET ) // seek from beginnng
bIndex = offset;
else if ( whence == SEEK_CUR ) // seek from current
bIndex += offset;
else if ( whence == SEEK_END ) // seek from end
bIndex = fileSize + offset;
}
SI32 UOXFile::getch( void )
{
return *(memPtr + bIndex++);
}
void UOXFile::getUChar( UI08 *buff, UI32 number )
{
memcpy( buff, memPtr+bIndex, number);
bIndex += number;
}
void UOXFile::getChar( SI08 *buff, UI32 number )
{
memcpy( buff, memPtr+bIndex, number);
bIndex += number;
}
void UOXFile::getUShort( UI16 *buff, UI32 number )
{
number *= sizeof(UI16);
memcpy( buff, memPtr+bIndex, number);
bIndex += number;
}
void UOXFile::getShort( SI16 *buff, UI32 number )
{
number *= sizeof(SI16);
memcpy( buff, memPtr+bIndex, number);
bIndex += number;
}
void UOXFile::getULong( UI32 *buff, UI32 number )
{
number *= sizeof(UI32);
memcpy( buff, memPtr+bIndex, number);
bIndex += number;
}
void UOXFile::getLong( SI32 *buff, UI32 number )
{
number *= sizeof(SI32);
memcpy( buff, memPtr+bIndex, number);
bIndex += number;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void LoadCustomTitle( void )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Loads players titles (Karma, Fame, Murder, ect)
//o-----------------------------------------------------------------------------------------------o
void LoadCustomTitle( void )
{
size_t titlecount = 0;
std::string tag;
std::string data;
ScriptSection *CustomTitle = FileLookup->FindEntry( "SKILL", titles_def );
if( CustomTitle == nullptr )
{
return;
}
for( tag = CustomTitle->First(); !CustomTitle->AtEnd() && titlecount < ALLSKILLS; tag = CustomTitle->Next() )
{
data = CustomTitle->GrabData();
cwmWorldState->title[titlecount].skill = data;
++titlecount;
}
CustomTitle = FileLookup->FindEntry( "PROWESS", titles_def );
if( CustomTitle == nullptr )
{
return;
}
for( tag = CustomTitle->First(); !CustomTitle->AtEnd(); tag = CustomTitle->Next() )
{
data = CustomTitle->GrabData();
cwmWorldState->prowessTitles.push_back( TitlePair_st( static_cast<SI16>(std::stoi(tag, nullptr, 0)), data ) );
}
CustomTitle = FileLookup->FindEntry( "FAME", titles_def );
if( CustomTitle == nullptr )
{
return;
}
titlecount = 0;
for( tag = CustomTitle->First(); !CustomTitle->AtEnd() && titlecount < ALLSKILLS; tag = CustomTitle->Next() )
{
data = CustomTitle->GrabData();
cwmWorldState->title[titlecount].fame = data;
++titlecount;
}
// Murder tags now scriptable in SECTION MURDER - Titles.dfn
CustomTitle = FileLookup->FindEntry( "MURDERER", titles_def );
if( CustomTitle == nullptr )
{
return;
}
for( tag = CustomTitle->First(); !CustomTitle->AtEnd(); tag = CustomTitle->Next() )
{
data = CustomTitle->GrabData();
cwmWorldState->murdererTags.push_back( TitlePair_st( static_cast<SI16>(std::stoi(tag, nullptr, 0)), data ) );
}
FileLookup->Dispose( titles_def );
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void LoadSkills( void )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Load skills from definition files
//o-----------------------------------------------------------------------------------------------o
void LoadSkills( void )
{
std::string skEntry;
std::string tag, data, UTag;
UI08 i = 0;
for( Script *creatScp = FileLookup->FirstScript( skills_def ); !FileLookup->FinishedScripts( skills_def ); creatScp = FileLookup->NextScript( skills_def ) )
{
if( creatScp == nullptr )
continue;
for( ScriptSection *SkillList = creatScp->FirstEntry(); SkillList != nullptr; SkillList = creatScp->NextEntry() )
{
if( SkillList == nullptr )
continue;
skEntry = creatScp->EntryName();
auto ssecs = strutil::sections( skEntry, " " );
if( strutil::trim( strutil::removeTrailing( ssecs[0], "// ")) == "SKILL" )
{
if( ssecs.size() > 1 )
{
i = static_cast<UI08>(std::stoul(strutil::trim( strutil::removeTrailing( ssecs[1], "//" ))));
if( i <= INTELLECT )
{
cwmWorldState->skill[i].ResetDefaults();
for( tag = SkillList->First(); !SkillList->AtEnd(); tag = SkillList->Next() )
{
UTag = strutil::upper( tag );
data = SkillList->GrabData();
data = strutil::trim( strutil::removeTrailing( data, "//" ));
if( UTag == "STR" )
{
cwmWorldState->skill[i].strength = static_cast<UI16>(std::stoul(data, nullptr, 0));
}
else if( UTag == "DEX" )
{
cwmWorldState->skill[i].dexterity = static_cast<UI16>(std::stoul(data, nullptr, 0));
}
else if( UTag == "INT" )
{
cwmWorldState->skill[i].intelligence = static_cast<UI16>(std::stoul(data, nullptr, 0));
}
else if( UTag == "SKILLPOINT" )
{
advance_st tempAdvance;
data = strutil::simplify( data );
auto csecs = strutil::sections( data, "," );
tempAdvance.base = static_cast<UI16>(std::stoul(strutil::trim( strutil::removeTrailing( csecs[0], "//" )), nullptr, 0));
tempAdvance.success = static_cast<UI16>(std::stoul(strutil::trim( strutil::removeTrailing( csecs[1], "//" )), nullptr, 0));
tempAdvance.failure = static_cast<UI16>(std::stoul(strutil::trim( strutil::removeTrailing( csecs[2], "//" )), nullptr, 0));
if( csecs.size() == 4 )
{
tempAdvance.amtToGain = static_cast<UI16>(std::stoul(strutil::trim( strutil::removeTrailing( csecs[3], "//" )), nullptr, 0));
}
cwmWorldState->skill[i].advancement.push_back( tempAdvance );
}
else if( UTag == "SKILLDELAY" )
{
cwmWorldState->skill[i].skillDelay = static_cast<SI32>( std::stoi( data, nullptr, 0 ));
}
else if( UTag == "MADEWORD" )
{
cwmWorldState->skill[i].madeword = data;
}
else if( UTag == "NAME" )
{
cwmWorldState->skill[i].name = data;
}
else
{
Console.warning( strutil::format("Unknown tag in skills.dfn: %s", data.c_str() ));
}
}
}
}
}
}
}
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void LoadSpawnRegions( void )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Loads spawning regions from definition files
//o-----------------------------------------------------------------------------------------------o
void LoadSpawnRegions( void )
{
cwmWorldState->spawnRegions.clear();
UI16 i = 0;
for( Script *spnScp = FileLookup->FirstScript( spawn_def ); !FileLookup->FinishedScripts( spawn_def ); spnScp = FileLookup->NextScript( spawn_def ) )
{
if( spnScp == nullptr )
continue;
for( ScriptSection *toScan = spnScp->FirstEntry(); toScan != nullptr; toScan = spnScp->NextEntry() )
{
if( toScan == nullptr )
continue;
std::string sectionName = spnScp->EntryName();
auto ssecs = strutil::sections( sectionName, " " );
if( "REGIONSPAWN" == ssecs[0] ) // Is it a region spawn entry?
{
i = static_cast<UI16>(std::stoul(strutil::trim( strutil::removeTrailing( ssecs[1], "//" )), nullptr, 0));
if( cwmWorldState->spawnRegions.find( i ) == cwmWorldState->spawnRegions.end() )
{
cwmWorldState->spawnRegions[i] = new CSpawnRegion( i );
cwmWorldState->spawnRegions[i]->Load( toScan );
}
else
{
Console.warning( strutil::format("spawn.dfn has a duplicate REGIONSPAWN entry, Entry Number: %u", i) );
}
}
}
}
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void LoadRegions( void )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Load regions from regions.dfn and townregions from regions.wsc
//o-----------------------------------------------------------------------------------------------o
void LoadRegions( void )
{
cwmWorldState->townRegions.clear();
std::string regionsFile = cwmWorldState->ServerData()->Directory( CSDDP_SHARED ) + "regions.wsc";
bool performLoad = false;
Script *ourRegions = nullptr;
if( FileExists( regionsFile ) )
{
performLoad = true;
ourRegions = new Script( regionsFile, NUM_DEFS, false );
}
UI16 i = 0;
std::string regEntry;
for( Script *regScp = FileLookup->FirstScript( regions_def ); !FileLookup->FinishedScripts( regions_def ); regScp = FileLookup->NextScript( regions_def ) )
{
if( regScp == nullptr )
continue;
for( ScriptSection *toScan = regScp->FirstEntry(); toScan != nullptr; toScan = regScp->NextEntry() )
{
if( toScan == nullptr )
continue;
regEntry = regScp->EntryName();
auto ssecs = strutil::sections( regEntry, " " );
if( strutil::trim( strutil::removeTrailing( ssecs[0], "//" )) == "REGION" )
{
i = static_cast<UI16>(std::stoul(strutil::trim( strutil::removeTrailing( ssecs[1], "//" )), nullptr, 0));
if( cwmWorldState->townRegions.find( i ) == cwmWorldState->townRegions.end() )
{
cwmWorldState->townRegions[i] = new CTownRegion( i );
cwmWorldState->townRegions[i]->InitFromScript( toScan );
if( performLoad )
cwmWorldState->townRegions[i]->Load( ourRegions );
}
else
{
Console.warning( strutil::format("regions.dfn has a duplicate REGION entry, Entry Number: %u", i) );
}
}
}
}
if( regEntry == "" )
{
// No regions found? :O Shut down UOX3, or we'll run into trouble later.
Console.PrintFailed();
Shutdown( FATAL_UOX3_ALLOC_MAPREGIONS );
}
if( performLoad )
{
delete ourRegions;
ourRegions = nullptr;
}
// Load Instant Logout regions from [INSTALOG] section of regions.dfn
// Note that all the tags below are required to setup valid locations
ScriptSection *InstaLog = FileLookup->FindEntry( "INSTALOG", regions_def );
if( InstaLog == nullptr )
return;
LogoutLocationEntry toAdd;
std::string data, UTag;
for( std::string tag = InstaLog->First(); !InstaLog->AtEnd(); tag = InstaLog->Next() )
{
UTag = strutil::upper( tag );
data = InstaLog->GrabData();
data = strutil::trim( strutil::removeTrailing( data, "//" ));
if( UTag == "X1" )
{
toAdd.x1 = static_cast<SI16>(std::stoi(data, nullptr, 0));
}
else if( UTag == "Y1" )
{
toAdd.y1 = static_cast<SI16>(std::stoi(data, nullptr, 0));
}
else if( UTag == "X2" )
{
toAdd.x2 = static_cast<SI16>(std::stoi(data, nullptr, 0));
}
else if( UTag == "Y2" )
{
toAdd.y2 = static_cast<SI16>(std::stoi(data, nullptr, 0));
}
else if( UTag == "WORLD" )
{
toAdd.worldNum = static_cast<SI08>(std::stoi(data, nullptr, 0));
}
else if( UTag == "INSTANCEID" )
{
toAdd.instanceID = static_cast<UI16>(std::stoul(data, nullptr, 0));
cwmWorldState->logoutLocs.push_back( toAdd );
}
}
// Load areas valid for SOS coordinates from [SOSAREAS] section of regions.dfn
// Note that all the tags below are required to setup valid locations
ScriptSection *sosAreas = FileLookup->FindEntry( "SOSAREAS", regions_def );
if( sosAreas == nullptr )
return;
SOSLocationEntry toAddSOS;
for( std::string tag = sosAreas->First(); !sosAreas->AtEnd(); tag = sosAreas->Next() )
{
UTag = strutil::upper( tag );
data = sosAreas->GrabData();
data = strutil::trim( strutil::removeTrailing( data, "//" ));
if( UTag == "X1" )
{
toAddSOS.x1 = static_cast<SI16>(std::stoi(data, nullptr, 0));
}
else if( UTag == "Y1" )
{
toAddSOS.y1 = static_cast<SI16>(std::stoi(data, nullptr, 0));
}
else if( UTag == "X2" )
{
toAddSOS.x2 = static_cast<SI16>(std::stoi(data, nullptr, 0));
}
else if( UTag == "Y2" )
{
toAddSOS.y2 = static_cast<SI16>(std::stoi(data, nullptr, 0));
}
else if( UTag == "WORLD" )
{
toAddSOS.worldNum = static_cast<SI08>(std::stoi(data, nullptr, 0));
}
else if( UTag == "INSTANCEID" )
{
toAddSOS.instanceID = static_cast<UI16>(std::stoul(data, nullptr, 0));
cwmWorldState->sosLocs.push_back( toAddSOS );
}
}
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void LoadTeleportLocations( void )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Load teleport locations from definition files
//o-----------------------------------------------------------------------------------------------o
void LoadTeleportLocations( void )
{
std::string filename = cwmWorldState->ServerData()->Directory( CSDDP_SCRIPTS ) + "teleport.scp";
cwmWorldState->teleLocs.resize( 0 );
if( !FileExists( filename ) )
{
Console << myendl;
Console.error(strutil::format(" Failed to open teleport data script %s", filename.c_str()) );
Console.error(strutil::format( " Teleport Data not found") );
cwmWorldState->SetKeepRun( false );
cwmWorldState->SetError( true );
return;
}
Script *teleportData = new Script( filename, NUM_DEFS, false );
if( teleportData != nullptr )
{
cwmWorldState->teleLocs.reserve( teleportData->NumEntries() );
UI16 tempX, tempY;
SI08 tempZ;
ScriptSection *teleportSect = nullptr;
std::string tag, data, temp;
for( teleportSect = teleportData->FirstEntry(); teleportSect != nullptr; teleportSect = teleportData->NextEntry() )
{
if( teleportSect != nullptr )
{
for( tag = teleportSect->First(); !teleportSect->AtEnd(); tag = teleportSect->Next() )
{
CTeleLocationEntry toAdd;
if( strutil::upper( tag ) == "ENTRY" )
{
tempX = 0;
tempY = 0;
tempZ = ILLEGAL_Z;
data = strutil::simplify( teleportSect->GrabData() );
auto csecs = strutil::sections( data, "," );
SI32 sectCount = static_cast<SI32>(csecs.size());
if( sectCount >= 6 )
{
tempX = static_cast<UI16>(std::stoul(strutil::trim( strutil::removeTrailing( csecs[0], "//" )), nullptr, 0) );
tempY = static_cast<UI16>(std::stoul(strutil::trim( strutil::removeTrailing( csecs[1], "//" )), nullptr, 0) );
temp = strutil::upper(strutil::trim( strutil::removeTrailing( csecs[2], "//" )));
if( temp != "ALL" && temp != "A" )
{
tempZ = static_cast<UI16>(std::stoul(temp, nullptr, 0) );
}
toAdd.SourceLocation( tempX, tempY, tempZ );
tempX = static_cast<UI16>(std::stoul(strutil::trim( strutil::removeTrailing( csecs[3], "//" )), nullptr, 0) );
tempY = static_cast<UI16>(std::stoul(strutil::trim( strutil::removeTrailing( csecs[4], "//" )), nullptr, 0) );
tempZ = static_cast<SI08>(std::stoi(strutil::trim( strutil::removeTrailing( csecs[5], "//" )), nullptr, 0) );
toAdd.TargetLocation( tempX, tempY, tempZ );
if( sectCount >= 7 )
{
toAdd.SourceWorld( static_cast<UI16>(std::stoul(strutil::trim( strutil::removeTrailing( csecs[6], "//" )), nullptr, 0) ));
if( sectCount >= 8 )
{
toAdd.TargetWorld( static_cast<UI16>(std::stoul(strutil::trim( strutil::removeTrailing( csecs[7], "//" )), nullptr, 0) ) );
}
}
cwmWorldState->teleLocs.push_back( toAdd );
}
else
{
Console.error( "Insufficient parameters for teleport entry" );
}
}
}
}
}
std::sort(cwmWorldState->teleLocs.begin(), cwmWorldState->teleLocs.end(), [] ( CTeleLocationEntry &one, CTeleLocationEntry &two)
{
return static_cast<UI32>(one.SourceLocation().x) < static_cast<UI32>(two.SourceLocation().x);
});
delete teleportData;
}
}
//o-----------------------------------------------------------------------------------------------o
//| Function - LoadCreatures()
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Loads creatures from creature definition files
//o-----------------------------------------------------------------------------------------------o
void LoadCreatures( void )
{
std::string cEntry;
std::string tag, data, UTag;
UI16 i = 0;
for( Script *creatScp = FileLookup->FirstScript( creatures_def ); !FileLookup->FinishedScripts( creatures_def ); creatScp = FileLookup->NextScript( creatures_def ) )
{
if( creatScp == nullptr )
continue;
for( ScriptSection *creatureData = creatScp->FirstEntry(); creatureData != nullptr; creatureData = creatScp->NextEntry() )
{
if( creatureData == nullptr )
continue;
cEntry = creatScp->EntryName();
auto ssecs = strutil::sections( cEntry," " );
if( ssecs[0] == "CREATURE" )
{
i = static_cast<UI16>(std::stoul(strutil::trim( strutil::removeTrailing( ssecs[1], "//" )), nullptr, 0) );
cwmWorldState->creatures[i].CreatureID( i );
for( tag = creatureData->First(); !creatureData->AtEnd(); tag = creatureData->Next() )
{
if( tag.empty() )
{
continue;
}
data = creatureData->GrabData();
data = strutil::trim( strutil::removeTrailing( data, "//" ));
UTag = strutil::upper( tag );
switch( (UTag.data()[0]) )
{
case 'A':
if( UTag == "ANTIBLINK" )
{
cwmWorldState->creatures[i].AntiBlink( true );
}
else if( UTag == "ANIMAL" )
{
cwmWorldState->creatures[i].IsAnimal( true );
}
break;
case 'B':
if( UTag == "BASESOUND" )
{
break;
}
break;
case 'F':
if( UTag == "FLIES" )
{
cwmWorldState->creatures[i].CanFly( true );
}
break;
case 'H':
if( UTag == "HUMAN" )
{
cwmWorldState->creatures[i].IsHuman( true );
}
break;
case 'I':
if( UTag == "ICON" )
{
cwmWorldState->creatures[i].Icon( static_cast<UI16>(std::stoul(data, nullptr, 0) ) );
}
break;
case 'M':
if( UTag == "MOVEMENT" )
{
if( strutil::upper( data ) == "WATER" )
{
cwmWorldState->creatures[i].IsWater( true );
}
else if( strutil::upper( data ) == "BOTH" )
{
cwmWorldState->creatures[i].IsAmphibian( true );
}
else
{
cwmWorldState->creatures[i].IsWater( false );
cwmWorldState->creatures[i].IsAmphibian( false );
}
}
else if( UTag == "MOUNTID" )
{
cwmWorldState->creatures[i].MountID( static_cast<UI16>(std::stoul(data, nullptr, 0) ) );
}
break;
case 'S':
if( UTag == "SOUNDFLAG" )
{
break;
}
else if( UTag == "SOUND_IDLE" )
{
cwmWorldState->creatures[i].SetSound( SND_IDLE, static_cast<UI16>(std::stoul(data, nullptr, 0) ) );
}
else if( UTag == "SOUND_STARTATTACK" )
{
cwmWorldState->creatures[i].SetSound( SND_STARTATTACK,static_cast<UI16>(std::stoul(data, nullptr, 0) ) );
}
else if( UTag == "SOUND_ATTACK" )
{
cwmWorldState->creatures[i].SetSound( SND_ATTACK, static_cast<UI16>(std::stoul(data, nullptr, 0) ) );
}
else if( UTag == "SOUND_DEFEND" )
{
cwmWorldState->creatures[i].SetSound( SND_DEFEND, static_cast<UI16>(std::stoul(data, nullptr, 0) ) );
}
else if( UTag == "SOUND_DIE" )
{
cwmWorldState->creatures[i].SetSound( SND_DIE, static_cast<UI16>(std::stoul(data, nullptr, 0) ) );
}
break;
default:
break;
}
}
}
}
}
FileLookup->Dispose( creatures_def );
}
void ReadWorldTagData( std::ifstream &inStream, std::string &tag, std::string &data )
{
char temp[4097];
tag = "o---o";
data = "o---o";
while( !inStream.eof() && !inStream.fail() )
{
inStream.getline(temp, 4096);
temp[inStream.gcount()] = 0;
auto sLine = std::string( temp );
auto cloc = sLine.find( "//" );
if( cloc != std::string::npos )
{
sLine = sLine.substr( 0, cloc );
}
cloc = sLine.find_first_not_of( " \t\v\f\0" );
if( cloc != std::string::npos )
{
if( cloc != std::string::npos )
{
auto temp2 = sLine.find_last_not_of( " \t\v\f\0" );
sLine = sLine.substr( cloc, ( temp2 - cloc ) + 1 );
}
}
if( !sLine.empty() )
{
if( sLine != "o---o" )
{
auto loc = sLine.find( "=" );
if( loc!= std::string::npos )
{
tag = sLine.substr( 0, loc );
auto temp = tag.find_first_not_of(" \t\v\f\0");
if( temp != std::string::npos )
{
auto temp2 = tag.find_last_not_of( " \t\v\f\0" );
tag = tag.substr( temp, ( temp2 - temp ) + 1 );
}
if(( loc+1 ) < sLine.size() )
{
data = sLine.substr( loc + 1 );
auto temp = data.find_first_not_of( " \t\v\f\0" );
if( temp != std::string::npos )
{
auto temp2 = data.find_last_not_of( " \t\v\f\0" );
data = data.substr( temp, ( temp2 - temp ) + 1 );
}
}
else
{
data = "";
}
break;
}
}
else
break;
}
}
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void LoadPlaces( void )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Load locations from location definition files
//o-----------------------------------------------------------------------------------------------o
void LoadPlaces( void )
{
cwmWorldState->goPlaces.clear();
std::string data, UTag, entryName;
GoPlaces_st *toAdd = nullptr;
for( Script *locScp = FileLookup->FirstScript( location_def ); !FileLookup->FinishedScripts( location_def ); locScp = FileLookup->NextScript( location_def ) )
{
if( locScp == nullptr )
{
continue;
}
for( ScriptSection *toScan = locScp->FirstEntry(); toScan != nullptr; toScan = locScp->NextEntry() )
{
if( toScan == nullptr )
{
continue;
}
entryName = locScp->EntryName();
auto ssecs = strutil::sections( entryName, " " );
size_t entryNum = static_cast<UI32>(std::stoul(strutil::trim( strutil::removeTrailing( ssecs[1], "//" )), nullptr, 0));
if( (strutil::upper(strutil::trim( strutil::removeTrailing( ssecs[0], "//" ))) == "LOCATION") && entryNum )
{
if( cwmWorldState->goPlaces.find( static_cast<UI16>(entryNum) ) != cwmWorldState->goPlaces.end() )
{
Console.warning( strutil::format("Doubled up entry in Location.dfn (%u)", entryNum) );
}
toAdd = &cwmWorldState->goPlaces[static_cast<UI16>(entryNum)];
if( toAdd != nullptr )
{
for( std::string tag = toScan->First(); !toScan->AtEnd(); tag = toScan->Next() )
{
data = toScan->GrabData();
data = strutil::trim( strutil::removeTrailing( data, "//" ));
UTag = strutil::upper( tag );
if( UTag == "X" )
{
toAdd->x = static_cast<SI16>(std::stoi(data, nullptr, 0));
}
else if( UTag == "Y" )
{
toAdd->y = static_cast<SI16>(std::stoi(data, nullptr, 0));
}
else if( UTag == "Z" )
{
toAdd->z = static_cast<SI08>(std::stoi(data, nullptr, 0));
}
else if( UTag == "WORLD" )
{
toAdd->worldNum = static_cast<UI16>(std::stoul(data, nullptr, 0));
}
else if( UTag == "INSTANCEID" )
{
toAdd->instanceID = static_cast<UI16>(std::stoul(data, nullptr, 0));
}
else if( UTag == "LOCATION" )
{
auto csecs = strutil::sections( data, "," );
size_t sectionCount = csecs.size() + 1;
if( sectionCount >= 3 )
{
toAdd->x = static_cast<SI16>(std::stoi(strutil::trim( strutil::removeTrailing( csecs[0], "//" )), nullptr, 0));
toAdd->y = static_cast<SI16>(std::stoi(strutil::trim( strutil::removeTrailing( csecs[1], "//" )), nullptr, 0));
toAdd->z = static_cast<SI08>(std::stoi(strutil::trim( strutil::removeTrailing( csecs[2], "//" )), nullptr, 0));
toAdd->worldNum = static_cast<UI16>(std::stoul(strutil::trim( strutil::removeTrailing( csecs[3], "//" )), nullptr, 0));
}
if( sectionCount == 4 )
{
toAdd->instanceID = static_cast<UI16>(std::stoul(strutil::trim( strutil::removeTrailing( csecs[4], "//" )), nullptr, 0));
}
}
}
}
}
}
}
FileLookup->Dispose( location_def );
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool FileExists( const std::string& filepath )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Check if specified file/filepath exists
//o-----------------------------------------------------------------------------------------------o
bool FileExists( const std::string& filepath )
{
auto temp = std::filesystem::path( filepath );
return std::filesystem::exists(temp);
}