uox3/source/cmdtable.cpp
Xoduz a48d9ee299 UOX3 0.99.6-RC6
Removed arbitrary and hidden restrictions on refreshing list of online players shown with 'wholist command
Fixed an issue where player ghosts that get teleported would not see themselves get updated to new location
Fixed a server crash from clients disconnecting while running server in debug mode
Improved how container updates are sent to players; now only sends to clients that have actually opened the container, and who are still within range
Added more information about UO data files being loaded during server startup
Enhanced the GM 'add menu to allow clicking directly on images of items/npcs to add them, and adjusted size of menus to accommodate this
Fixed an issue with fleeing NPCs that could cause them to attempt to flee across time and space, regardless of distance to target/attacker in combat
Added NPC AI that runs away from players if they get too close (even outside combat). Applied to hind, rabbits, squirrels, ferrets and various birds by default:
	AI_ANIMAL_SCARED (aitype 12)
Fixed issues with NPCs fleeing forever, or getting stuck in flee/don't flee loop, by introducing max fleeing distance (50 tiles) and cooldown (30 sec) on fleeing
Updated onNameRequest JS Event to include third parameter with origin of name request, so script responses can be tailored appropriately:
	onNameRequest( myObj, requestedBy, requestSource )
		Potential values for requestSource:
			0 - Speech/System Messages
			1 - Guild Menus
			2 - Stat Window (self)
			3 - Stat Window (other)
			4 - Tooltip
			5 - Paperdoll Journal
			6 - Paperdoll
			7 - Single Click / All-Names
			8 - System
			9 - Secure Trade Window
Added Adaptive Performance System (APS) that dynamically adjust how often NPC AI/movement is checked based on overall shard performance. If performance drops below defined threshold, UOX3 gradually slows down checks for NPC AI/movement to prioritize player movement/speech/command responsiveness. If performance climbs back up above threshold, slowdowns are gradually removed. The following UOX.INI options have been added under [system] category to support this system:
	APSPERFTHRESHOLD=50 	// Performance threshold (measured in simulation cycles/sec) below which the APS kicks in
	APSINTERVAL=100 		// How often (in milliseconds) the APS checks performance and makes adjustments (if needed) to balance out shard performance
	APSDELAYSTEP=50 		// How much the delay timer is modified by (in milliseconds) each time APS makes adjustments
	APSDELAYMAXCAP=2000 	// Max amount of of delay APS can introduce for NPC AI/movement handling when attempting to restore shard performance
Added new JS Methods for Region objects:
	.GetOrePrefs( oreType )	// Get ore preference data for ore type found in town region. Returned as an array containing the following data:
		orePrefData -> [
			oreName,	// name of ore
			color,		// color of ore
			minskill,	// min skill to mine ore
			ingotName,	// name of ingot created from ore
			makemenu,	// makemenu entry for crafting something from ingot
			oreChance,	// default global chance of finding this ore type
			scriptID 	// script attached to mined ore
		],
		orePrefChance 	// Chance of finding this ore type in given town region
	.GetOreChance() 	// Get base chance of finding any ore in town region
Moved Mining skill plus gravedigging feature from hard code to scripts (js/skill/mining.js) to make it easier to maintain and/or customize, and removed hard coded variants
Gravedigging now relies on the ore resource system behind the scenes to restrict how often graves in a given area can be dug up
Fixed some incorrect references to .worldNumber Character property in misc scripts (should be .worldnumber)
Made some "hard-scripted" system messages in misc scripts use dictionary system instead
Added spawn region for banker NPCs in Serpent's Hold (dfndata/spawn/felucca/spawn_town_serpents_hold.dfn, dfndata/spawn/trammel/spawn_town_serpents_hold.dfn)
Fixed region definition of Ocllo to actually cover the entire town (dfndata/regions/regions.dfn)
Added numerous additional locations accessible with 'goplace # command, for key locations in Ilshenar, Malas, Tokuno Islands and Ter Mur (dfndata/location/location.dfn)
Revised travel-menu portion of GM menu (shortcut: 'travel) to include more travel options based on new locations (dfndata/items/travelmenu.dfn and travelmenu.bulk.dfn)
Fixed an issue where NPCs could attempt to attack targets in other worlds/instances
Added new JS Event that triggers for characters who are about to deal damage in combat. Complimentary to onDamage, which triggers for chars receiving damage:
	onDamageDeal( dmgDealer, dmgReceiver, damageValue, damageType )
Added new JS Event that can trigger in global script upon creation of new player chars. Note that this will trigger in place of onCreateDFN event for player characters:
	onCreatePlayer( pChar )
Added new JS Event that triggers for characters selecting a target with a spell. Complimentary to onSpellTarget, which triggers for targets selected with a spell:
	onSpellTargetSelect( caster, target, spellNum )
Updated onCombatStart and onCombatEnd JS Events to also trigger for the other party in combat
Updated onPickup JS Event to include a third parameter - the potential container item was picked up from. Event now also triggers event in scripts attached to said containers:
	onPickup( iPickedUp, pChar, iCont )
Added tracking of total playtime per individual character, and per account across all characters, and exposed these properties to JS engine:
	.totalPlayTime		// Account property, total playtime across all chars
	.playTime 			// Character property, total playtime on given character only
Added new player-accessible command ('playtime) to spit out the playtime of current character/account as a whole (js/commands/playtime.js)
Implemented first version of Young Player System:
	Replaced the UNUSED9 account flag with YOUNG, to be used by Young/New Player System
	Added new JS Account property that gets/sets whether player account is considered Young:
		.isYoung
	Added new Char timers:
		TIMER_YOUNGHEAL		// Restricts how often Young players are healed by NPC healers
		TIMER_YOUNGMESSAGE	// Restricts how often Young players are warned about dangerous looking monsters in overworld
	Updated GM commands 'get and 'set to get/set .isYoung property of player's account (js/commands/targeting/get.js and set.js)
	Added new UOX.INI setting to enable/disable Young Player System (enabled by default):
		YOUNGPLAYERSYSTEM=1
	If Young Player System is enabled, all newly created player accounts are automatically marked with Young flag
	Added new script to handle various restrictions and functions related to Young characters (js/player/young_player.js):
		Young characters will have [Young] displayed over their head
		Young characters will have their Young status checked and verified on every login + every stat/skill gain, to revoke the Young status if any of the following is true:
			Account has a total playtime of more than 40 hours
			Any character has more than 350 total (base) skill points
			Any character has more than 70 skill points in a single skill
			Any character has more than 150 total stat points
			Any character has more than 80 stat points in a single stat
		Young characters can renounce their Young status manually by saying the words "I renounce my young player status"
		Young characters get two additional items upon creation:
			a new player ticket (can be combined with any other players new player ticket for both players to get a reward)
			a sextant (shows Young players directions to nearest moongate/bank regardless of where they are, as long as outdoors)
		Young characters cannot target other player characters with hostile spells or skills
		Young characters cannot be the target of other player characters' hostile spells or skills
		Young characters can only cast beneficial spells upon themselves or other Young characters
		Young characters can only be the target of beneficial spells from other Young players
		Young players (and their pets/followers) cannot attack or harm any other players (or their pets/followers), nor themselves be attacked or harmed by any other players (or their pets/followers), whether from combat, spells, skills or items
		Young players don't lose any of their items on death, and are teleported to the nearest healer upon death with all their items intact
		Young players receive a warning upon entering dungeons that monsters there will be hostile
	Updated global script (js/server/global.js) to:
		Check/Verify Young status of players upon login
		Attach young player script on login/creation for players on Young accounts
		Give specific items to Young players upon creation
	Added new script (js/item/corpse.js) that is assigned to all freshly created corpses. This script handles restrictions related to Young players interactions with corpses, and the interactions of other players with corpses of Young players
	Updated JS Method .Carve() to return true/false depending on whether carving a corpse was successful
	Updated Healer AI in code to heal nearby injured Young players
	Updated Evil AI and Evil Caster AI in code to avoid selecting Young players as targets in combat outside of dungeons
	Added a new section to UOX.INI - [young player starting locations] - that can be used to define starting locations for Young players. If only one such location is provided, all players start there. Otherwise, it matches the same starting location setup as the regular one - with one entry per town in Britannia.
	Restricted transferring and/or friending of pets (code) and hirelings (hirelings.js) between Young and non-Young players
	Restricted Young players from recalling or gating to Felucca facet
	Young players can instantly logout from anywhere, at any time
2023-07-01 20:02:40 +08:00

1570 lines
57 KiB
C++

//
// This code is an attempt to clean up the messy "if/then/else" routines
// currently in use for GM commands, as well as adding more functionality
// and more potential for functionality.
//
// Current features:
// - Actual table of commands to execute, what perms are required, dialog
// messages for target commands, etc handled by a central system
//
//
#include "uox3.h"
#include "commands.h"
#include "msgboard.h"
#include "townregion.h"
#include "cWeather.hpp"
#include "cServerDefinitions.h"
#include "wholist.h"
#include "cSpawnRegion.h"
#include "PageVector.h"
#include "speech.h"
#include "cHTMLSystem.h"
#include "gump.h"
#include "cEffects.h"
#include "classes.h"
#include "regions.h"
#include "CPacketSend.h"
#include "teffect.h"
#include "magic.h"
#include "ObjectFactory.h"
#include "cRaces.h"
#include "StringUtility.hpp"
#include "Dictionary.h"
void CollectGarbage( void );
void EndMessage( SI32 x );
void HandleGumpCommand( CSocket *s, std::string cmd, std::string data );
void Restock( bool stockAll );
void SysBroadcast( const std::string& txt );
void HandleHowTo( CSocket *sock, SI32 cmdNumber );
//o------------------------------------------------------------------------------------------------o
//| Function - CloseCall()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Closes an open call in the Que
//o------------------------------------------------------------------------------------------------o
void CloseCall( CSocket *s, bool isGM )
{
CChar *mChar = s->CurrcharObj();
if( mChar->GetCallNum() != 0 )
{
UI16 sysMessage = 1285; // Call removed from the GM queue.
PageVector *tmpVector = nullptr;
if( isGM )
{
tmpVector = GMQueue;
}
else
{
tmpVector = CounselorQueue;
++sysMessage;
}
if( tmpVector->GotoPos( tmpVector->FindCallNum( mChar->GetCallNum() )))
{
tmpVector->Remove();
mChar->SetCallNum( 0 );
s->SysMessage( sysMessage );
}
}
else
{
s->SysMessage( 1287 ); // You are currently not on a call.
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - CurrentCall()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Send GM/Counselor to the current call in the queue
//o------------------------------------------------------------------------------------------------o
void CurrentCall( CSocket *s, bool isGM )
{
CChar *mChar = s->CurrcharObj();
if( mChar->GetCallNum() != 0 )
{
PageVector *tmpVector = nullptr;
if( isGM )
{
tmpVector = GMQueue;
}
else
{
tmpVector = CounselorQueue;
}
if( tmpVector->GotoPos( tmpVector->FindCallNum( mChar->GetCallNum() )))
{
CHelpRequest *currentPage = tmpVector->Current();
CChar *i = CalcCharObjFromSer( currentPage->WhoPaging() );
if( ValidateObject( i ))
{
s->SysMessage( 73 ); // Transporting to your current call.
mChar->SetLocation( i );
}
currentPage->IsHandled( true );
}
else
{
s->SysMessage( 75 ); // No such call exists
}
}
else
{
s->SysMessage( 72 ); // You are not currently on a call.
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - NextCall()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Send GM/Counsellor to next call in the que
//o------------------------------------------------------------------------------------------------o
void NextCall( CSocket *s, bool isGM )
{
CChar *mChar = s->CurrcharObj();
if( mChar->GetCallNum() != 0 )
{
CloseCall( s, isGM );
}
if( isGM )
{
if( !GMQueue->AnswerNextCall( s, mChar ))
{
s->SysMessage( 347 ); // The GM queue is currently empty.
}
}
else //Player is a counselor
{
if( !CounselorQueue->AnswerNextCall( s, mChar ))
{
s->SysMessage( 348 ); // The Counselor queue is currently empty.
}
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - FixSpawnFunctor()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Replaces legacy spawner objects that have incorrect item type
//o------------------------------------------------------------------------------------------------o
bool FixSpawnFunctor( CBaseObject *a, [[maybe_unused]] UI32 &b, [[maybe_unused]] void *extraData )
{
bool retVal = true;
if( ValidateObject( a ))
{
CItem *i = static_cast<CItem *>( a );
ItemTypes iType = i->GetType();
if( iType == IT_ITEMSPAWNER || iType == IT_NPCSPAWNER || iType == IT_SPAWNCONT ||
iType == IT_LOCKEDSPAWNCONT || iType == IT_UNLOCKABLESPAWNCONT ||
iType == IT_AREASPAWNER || iType == IT_ESCORTNPCSPAWNER )
{
if( i->GetObjType() != OT_SPAWNER )
{
CSpawnItem *spawnItem = static_cast<CSpawnItem *>(i->Dupe( OT_SPAWNER ));
if( ValidateObject( spawnItem ))
{
spawnItem->SetInterval( 0, static_cast<UI08>( i->GetTempVar( CITV_MOREY )));
spawnItem->SetInterval( 1, static_cast<UI08>( i->GetTempVar( CITV_MOREX )));
spawnItem->SetSpawnSection( i->GetDesc() );
spawnItem->IsSectionAList( i->IsSpawnerList() );
}
i->Delete();
}
}
}
return retVal;
}
void command_fixspawn( void )
{
UI32 b = 0;
ObjectFactory::GetSingleton().IterateOver( OT_ITEM, b, nullptr, &FixSpawnFunctor );
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_AddAccount()
//o------------------------------------------------------------------------------------------------o
//| Purpose - GM command for adding new user accounts while in-game
//o------------------------------------------------------------------------------------------------o
void Command_AddAccount( CSocket *s)
{
VALIDATESOCKET( s );
if( Commands->NumArguments() > 1 )
{
std::string newUsername = Commands->CommandString( 2, 2 );
std::string newPassword = Commands->CommandString( 3, 3 );
UI16 newFlags = 0x0000;
if( Commands->NumArguments() > 2 )
{
newFlags = oldstrutil::value<UI16>( Commands->CommandString( 4, 4 ));
}
if( newUsername == "" || newPassword == "" )
{
s->SysMessage( 9018 ); // Unable to add account, insufficient data provided (syntax: [username] [password] [flags])
return;
}
// ok we need to add the account now. We will rely in the internalaccountscreation system for this
CAccountBlock_st &actbTemp = Accounts->GetAccountByName( newUsername );
if( actbTemp.wAccountIndex == AB_INVALID_ID )
{
Accounts->AddAccount( newUsername, newPassword, "NA", newFlags );
Console << "o Account added ingame: " << newUsername << ":" << newPassword << ":" << newFlags << myendl;
s->SysMessage( 9019, newUsername.c_str(), newPassword.c_str(), newFlags ); // Account Added: %s:%s:%i
}
else
{
Console << "o Account was not added, an account with that username already exists!" << myendl;
s->SysMessage( 9020 ); // Account not added, an account with that username already exists!
}
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_GetLight()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Displays current in-game light level as a system message
//o------------------------------------------------------------------------------------------------o
//| Notes - Needs redoing to support new (weather-based) lighting system
//o------------------------------------------------------------------------------------------------o
void Command_GetLight( CSocket *s )
{
VALIDATESOCKET( s );
CChar *mChar = s->CurrcharObj();
if( !ValidateObject( mChar ))
return;
CTownRegion *tRegion = mChar->GetRegion();
UI16 weatherId = tRegion->GetWeather();
CWeather *sys = Weather->Weather( weatherId );
if( sys != nullptr )
{
const R32 lightMin = sys->LightMin();
const R32 lightMax = sys->LightMax();
if( lightMin < 300 && lightMax < 300 )
{
R32 i = sys->CurrentLight();
if( Races->VisLevel( mChar->GetRace() ) > i )
{
s->SysMessage( 1632, 0 ); // Current light level is %i
}
else
{
s->SysMessage( 1632, static_cast<LIGHTLEVEL>(RoundNumber( i - Races->VisLevel( mChar->GetRace() )))); // Current light level is %i
}
}
else
{
s->SysMessage( 1632, cwmWorldState->ServerData()->WorldLightCurrentLevel() ); // Current light level is %i
}
}
else
{
s->SysMessage( 1632, cwmWorldState->ServerData()->WorldLightCurrentLevel() ); // Current light level is %i
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_SetPost()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Sets bulletin board posting mode for user
//o------------------------------------------------------------------------------------------------o
void Command_SetPost( CSocket *s )
{
VALIDATESOCKET( s );
CChar *mChar = s->CurrcharObj();
if( !ValidateObject( mChar ))
return;
PostTypes type = PT_LOCAL;
std::string upperCommand = oldstrutil::upper( Commands->CommandString( 2, 2 ));
if( upperCommand == "GLOBAL" ) // user's next post appears in ALL bulletin boards
{
type = PT_GLOBAL;
}
else if( upperCommand == "REGIONAL" ) // user's next post appears in all bulletin boards in current region
{
type = PT_REGIONAL;
}
else if( upperCommand == "LOCAL" ) // user's next post appears only locally in the next bulletin board used
{
type = PT_LOCAL;
}
mChar->SetPostType( static_cast<UI08>( type ));
switch ( type )
{
case PT_LOCAL: s->SysMessage( 726 ); break; // Post type set to LOCAL.
case PT_REGIONAL: s->SysMessage( 727 ); break; // Post type set to REGION
case PT_GLOBAL: s->SysMessage( 728 ); break; // Post type set to GLOBAL.
default: s->SysMessage( 725 ); break; // Invalid post type.
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_GetPost()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Returns current bulletin board posting mode for user
//o------------------------------------------------------------------------------------------------o
void Command_GetPost( CSocket *s )
{
VALIDATESOCKET( s );
CChar *mChar = s->CurrcharObj();
if( !ValidateObject( mChar ))
return;
switch( mChar->GetPostType() )
{
case PT_LOCAL: s->SysMessage( 722 ); break; // Currently posting LOCAL messages.
case PT_REGIONAL: s->SysMessage( 723 ); break; // Currently posting REGIONAL messages.
case PT_GLOBAL: s->SysMessage( 724 ); break; // Currently posting GLOBAL messages.
default: s->SysMessage( 725 ); break; // Invalid post type.
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_ShowIds()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Display the serial number of every item on user's screen
//o------------------------------------------------------------------------------------------------o
auto Command_ShowIds( CSocket *s ) -> void
{
VALIDATESOCKET( s );
CChar *mChar = s->CurrcharObj();
CMapRegion *Cell = MapRegion->GetMapRegion( mChar );
if( Cell )
{
auto regChars = Cell->GetCharList();
for( auto &toShow : regChars->collection() )
{
if( ValidateObject( toShow ))
{
if( CharInRange( mChar, toShow ))
{
s->ShowCharName( toShow, true );
}
}
}
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_Tile()
//o------------------------------------------------------------------------------------------------o
//| Purpose - (h) Tiles the item specified over a square area.
//o------------------------------------------------------------------------------------------------o
//| Notes - To find the hexidecimal ID code for an item to tile,
//| either create the item with /add or find it in the
//| world, and get /ISTATS on the object to get its ID code.
//o------------------------------------------------------------------------------------------------o
void Command_Tile( CSocket *s )
{
VALIDATESOCKET( s );
if( Commands->NumArguments() == 0 )
return;
UI16 targId = 0;
if( oldstrutil::upper( Commands->CommandString( 2, 2 )) == "STATIC" )
{
targId = static_cast<UI16>( Commands->Argument( 2 ));
}
else
{
targId = static_cast<UI16>( Commands->Argument( 1 ));
}
// Reset tempint2 on socket
s->TempInt2( 0 );
if( Commands->NumArguments() == 7 || Commands->NumArguments() == 8 )
{
// tile itemId x1 y1 x2 y2 z rndVal
SI32 rndVal = 0;
UI16 rndId = 0;
if( Commands->NumArguments() == 8 )
{
rndVal = static_cast<SI32>( Commands->Argument( 7 ));
}
// tile itemId x1 y1 x2 y2 z
SI16 x1 = static_cast<SI16>( Commands->Argument( 2 ));
SI16 x2 = static_cast<SI16>( Commands->Argument( 3 ));
SI16 y1 = static_cast<SI16>( Commands->Argument( 4 ));
SI16 y2 = static_cast<SI16>( Commands->Argument( 5 ));
SI08 z = static_cast<SI08>( Commands->Argument( 6 ));
for( SI16 x = x1; x <= x2; ++x )
{
for( SI16 y = y1; y <= y2; ++y )
{
rndId = targId + RandomNum( static_cast<UI16>( 0 ), static_cast<UI16>( rndVal ));
CItem *a = Items->CreateItem( nullptr, s->CurrcharObj(), rndId, 1, 0, OT_ITEM );
if( ValidateObject( a )) // crash prevention
{
a->SetLocation( x, y, z );
a->SetDecayable( false );
}
}
}
}
else if( Commands->NumArguments() == 4 )
{
// tile static itemId rndVal
s->ClickX( -1 );
s->ClickY( -1 );
s->TempInt2( static_cast<SI32>( Commands->Argument( 3 )));
s->AddId1( static_cast<UI08>( targId >> 8 ));
s->AddId2( static_cast<UI08>( targId % 256 ));
s->SendTargetCursor( 0, TARGET_TILING, 0, 24 );
}
else if( Commands->NumArguments() == 3 )
{
// tile itemId rndVal
// tile static itemId
s->ClickX( -1 );
s->ClickY( -1 );
if( oldstrutil::upper( Commands->CommandString( 2, 2 )) != "STATIC" )
{
s->TempInt2( static_cast<SI32>( Commands->Argument( 2 )));
}
s->AddId1( static_cast<UI08>( targId >> 8 ));
s->AddId2( static_cast<UI08>( targId % 256 ));
s->SendTargetCursor( 0, TARGET_TILING, 0, 24 );
}
else if( Commands->NumArguments() == 2 )
{
// tile itemId
s->ClickX( -1 );
s->ClickY( -1 );
s->AddId1( static_cast<UI08>( targId >> 8 ));
s->AddId2( static_cast<UI08>( targId % 256 ));
s->SendTargetCursor( 0, TARGET_TILING, 0, 24 );
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_Save()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Saves the current world data into .WSC files
//o------------------------------------------------------------------------------------------------o
void Command_Save( void )
{
if( cwmWorldState->GetWorldSaveProgress() != SS_SAVING )
{
cwmWorldState->SaveNewWorld( true );
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_Dye()
//o------------------------------------------------------------------------------------------------o
//| Purpose - (h h/nothing) Dyes an item a specific color, or brings up
//| a dyeing menu if no color is specified.
//o------------------------------------------------------------------------------------------------o
void Command_Dye( CSocket *s )
{
VALIDATESOCKET( s );
s->DyeAll( 1 );
if( Commands->NumArguments() == 2 )
{
UI16 tNum = static_cast<SI16>( Commands->Argument( 1 ));
s->AddId1( static_cast<UI08>( tNum >> 8 ));
s->AddId2( static_cast<UI08>( tNum % 256 ));
}
else if( Commands->NumArguments() == 3 )
{
s->AddId1( static_cast<UI08>( Commands->Argument( 1 )));
s->AddId2( static_cast<UI08>( Commands->Argument( 2 )));
}
else
{
s->AddId1( 0xFF );
s->AddId2( 0xFF );
}
s->SendTargetCursor( 0, TARGET_DYE, 0, 31 );
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_SetTime()
//o------------------------------------------------------------------------------------------------o
//| Purpose - (d d) Sets the current UO time in hours and minutes.
//o------------------------------------------------------------------------------------------------o
void Command_SetTime( void )
{
if( Commands->NumArguments() == 3 )
{
UI08 newhours = static_cast<UI08>( Commands->Argument( 1 ));
UI08 newminutes = static_cast<UI08>( Commands->Argument( 2 ));
if(( newhours < 25 ) && ( newhours > 0 ) && ( newminutes < 60 ))
{
cwmWorldState->ServerData()->ServerTimeAMPM(( newhours > 12 ));
if( newhours > 12 )
{
cwmWorldState->ServerData()->ServerTimeHours( static_cast<UI08>( newhours - 12 ));
}
else
{
cwmWorldState->ServerData()->ServerTimeHours( newhours );
}
cwmWorldState->ServerData()->ServerTimeMinutes( newminutes );
}
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_Shutdown()
//o------------------------------------------------------------------------------------------------o
//| Purpose - (d) Shuts down the server. Argument is how many minutes
//| until shutdown.
//o------------------------------------------------------------------------------------------------o
void Command_Shutdown( void )
{
if( Commands->NumArguments() == 2 )
{
cwmWorldState->SetEndTime( BuildTimeValue( static_cast<R32>( Commands->Argument( 1 ))));
if( Commands->Argument( 1 ) == 0 )
{
cwmWorldState->SetEndTime( 0 );
SysBroadcast( Dictionary->GetEntry( 36 ));
}
else
{
EndMessage( 0 );
}
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_Tell()
//o------------------------------------------------------------------------------------------------o
//| Purpose - (d text) Sends an anonymous message to the user logged in under the specified slot.
//o------------------------------------------------------------------------------------------------o
void Command_Tell( CSocket *s )
{
VALIDATESOCKET( s );
if( Commands->NumArguments() > 2 )
{
CSocket *i = Network->GetSockPtr( Commands->Argument( 1 ));
std::string txt = Commands->CommandString( 3 );
if( i == nullptr || txt.empty() )
return;
CChar *mChar = i->CurrcharObj();
CChar *tChar = s->CurrcharObj();
std::string temp = mChar->GetNameRequest( tChar, 0 ) + " tells " + tChar->GetNameRequest( mChar, NRS_SPEECH ) + ": " + txt;
if( cwmWorldState->ServerData()->UseUnicodeMessages() )
{
CPUnicodeMessage unicodeMessage;
unicodeMessage.Message( temp );
unicodeMessage.Font( static_cast<FontType>( mChar->GetFontType() ));
if( mChar->GetSayColour() == 0x1700 )
{
unicodeMessage.Colour( 0x5A );
}
else if( mChar->GetSayColour() == 0x0 )
{
unicodeMessage.Colour( 0x5A );
}
else
{
unicodeMessage.Colour( mChar->GetSayColour() );
}
unicodeMessage.Type( TALK );
unicodeMessage.Language( "ENG" );
unicodeMessage.Name( mChar->GetNameRequest( tChar, NRS_SPEECH ));
unicodeMessage.ID( INVALIDID );
unicodeMessage.Serial( mChar->GetSerial() );
s->Send( &unicodeMessage );
}
else
{
CSpeechEntry& toAdd = SpeechSys->Add();
toAdd.Font( static_cast<FontType>( mChar->GetFontType() ));
toAdd.Speech( temp );
toAdd.Speaker( mChar->GetSerial() );
toAdd.SpokenTo( tChar->GetSerial() );
toAdd.Type( TALK );
toAdd.At( cwmWorldState->GetUICurrentTime() );
toAdd.TargType( SPTRG_INDIVIDUAL );
if( mChar->GetSayColour() == 0x1700 )
{
toAdd.Colour( 0x5A );
}
else if( mChar->GetSayColour() == 0x0 )
{
toAdd.Colour( 0x5A );
}
else
{
toAdd.Colour( mChar->GetSayColour() );
}
}
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_GmMenu()
//o------------------------------------------------------------------------------------------------o
//| Purpose - (d) Opens the specified GM Menu from dfndata/menus.dfn
//o------------------------------------------------------------------------------------------------o
void Command_GmMenu( CSocket *s )
{
VALIDATESOCKET( s );
if( Commands->NumArguments() == 2 )
{
CPIHelpRequest toHandle( s, static_cast<UI16>( Commands->Argument( 1 )));
toHandle.Handle();
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_Command()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Executes a trigger scripting command.
//o------------------------------------------------------------------------------------------------o
void Command_Command( CSocket *s )
{
VALIDATESOCKET( s );
if( Commands->NumArguments() > 1 )
{
HandleGumpCommand( s, oldstrutil::upper( Commands->CommandString( 2, 2 )), oldstrutil::upper( Commands->CommandString( 3 )));
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_MemStats()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Display some information about cache and memory usage.
//o------------------------------------------------------------------------------------------------o
void Command_MemStats( CSocket *s )
{
VALIDATESOCKET( s );
size_t charsSize = ObjectFactory::GetSingleton().SizeOfObjects( OT_CHAR );
size_t itemsSize = ObjectFactory::GetSingleton().SizeOfObjects( OT_ITEM );
size_t spellsSize = 69 * sizeof( CSpellInfo );
size_t teffectsSize = sizeof( CTEffect ) * cwmWorldState->tempEffects.Num();
size_t regionsSize = sizeof( CTownRegion ) * cwmWorldState->townRegions.size();
size_t spawnregionsSize = sizeof( CSpawnRegion ) * cwmWorldState->spawnRegions.size();
size_t total = charsSize + itemsSize + spellsSize + regionsSize + spawnregionsSize + teffectsSize;
CGumpDisplay cacheStats( s, 350, 345 );
cacheStats.SetTitle( "UOX Memory Information (sizes in bytes)" );
cacheStats.AddData( "Total Memory Usage: ", static_cast<UI32>( total ));
cacheStats.AddData( " Characters: ", ObjectFactory::GetSingleton().CountOfObjects( OT_CHAR ));
cacheStats.AddData( " Allocated Memory: ", static_cast<UI32>( charsSize ));
cacheStats.AddData( " CChar: ", sizeof( CChar ));
cacheStats.AddData( " Items: ", ObjectFactory::GetSingleton().CountOfObjects( OT_ITEM ));
cacheStats.AddData( " Allocated Memory: ", static_cast<UI32>( itemsSize ));
cacheStats.AddData( " CItem: ", sizeof( CItem ));
cacheStats.AddData( " Spells Size: ", static_cast<UI32>( spellsSize ));
cacheStats.AddData( " TEffects: ", static_cast<UI32>( cwmWorldState->tempEffects.Num() ));
cacheStats.AddData( " Allocated Memory: ", static_cast<UI32>( teffectsSize ));
cacheStats.AddData( " TEffect: ", sizeof( CTEffect ));
cacheStats.AddData( " Regions Size: ", static_cast<UI32>( cwmWorldState->townRegions.size() ));
cacheStats.AddData( " Allocated Memory: ", static_cast<UI32>( regionsSize ));
cacheStats.AddData( " CTownRegion: ", sizeof( CTownRegion ));
cacheStats.AddData( " SpawnRegions ", static_cast<UI32>( cwmWorldState->spawnRegions.size() ));
cacheStats.AddData( " Allocated Memory: ", static_cast<UI32>( spawnregionsSize ));
cacheStats.AddData( " CSpawnRegion: ", sizeof( CSpawnRegion ));
cacheStats.Send( 0, false, INVALIDSERIAL );
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_Restock()
//o------------------------------------------------------------------------------------------------o
//| Purpose - (nothing / s) Forces a manual vendor restock
//o------------------------------------------------------------------------------------------------o
void Command_Restock( CSocket *s )
{
VALIDATESOCKET( s );
if( oldstrutil::upper( Commands->CommandString( 2, 2 )) == "ALL" )
{
Restock( true );
s->SysMessage( 55 ); // Restocking all shops to their maximums
}
else
{
Restock( false );
s->SysMessage( 54 ); // Manual shop restock has occurred.
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_SetShopRestockRate()
//o------------------------------------------------------------------------------------------------o
//| Purpose - (d) Sets the universe's shop restock rate
//o------------------------------------------------------------------------------------------------o
void Command_SetShopRestockRate( CSocket *s )
{
VALIDATESOCKET( s );
if( Commands->NumArguments() == 2 )
{
cwmWorldState->ServerData()->SystemTimer( tSERVER_SHOPSPAWN, static_cast<UI16>( Commands->Argument( 1 )));
s->SysMessage( 56 ); // NPC shop restock rate changed.
}
else
s->SysMessage( 57 ); // Invalid number of parameters.
}
bool RespawnFunctor( CBaseObject *a, [[maybe_unused]] UI32 &b, [[maybe_unused]] void *extraData )
{
bool retVal = true;
if( ValidateObject( a ))
{
CItem *i = static_cast<CItem *>( a );
ItemTypes iType = i->GetType();
if( iType == IT_ITEMSPAWNER || iType == IT_NPCSPAWNER || iType == IT_SPAWNCONT ||
iType == IT_LOCKEDSPAWNCONT || iType == IT_UNLOCKABLESPAWNCONT ||
iType == IT_AREASPAWNER || iType == IT_ESCORTNPCSPAWNER )
{
if( i->GetObjType() == OT_SPAWNER )
{
CSpawnItem *spawnItem = static_cast<CSpawnItem *>( i );
if( !spawnItem->DoRespawn() )
{
spawnItem->SetTempTimer( BuildTimeValue( static_cast<R32>( RandomNum( spawnItem->GetInterval( 0 ) * 60, spawnItem->GetInterval( 1 ) * 60 ))));
}
}
else
{
i->SetType( IT_NOTYPE );
}
}
}
return retVal;
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_Respawn()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Forces a respawn of all spawn regions
//o------------------------------------------------------------------------------------------------o
void Command_Respawn( void )
{
UI32 spawnedItems = 0;
UI32 spawnedNpcs = 0;
std::for_each( cwmWorldState->spawnRegions.begin(), cwmWorldState->spawnRegions.end(), [&spawnedItems, &spawnedNpcs]( std::pair<UI16, CSpawnRegion *> entry )
{
if( entry.second )
{
entry.second->DoRegionSpawn( spawnedItems, spawnedNpcs );
}
});
UI32 b = 0;
ObjectFactory::GetSingleton().IterateOver( OT_ITEM, b, nullptr, &RespawnFunctor );
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_RegSpawn()
//o------------------------------------------------------------------------------------------------o
//| Purpose - (s/d) Forces a region spawn, First argument is region number or "ALL"
//o------------------------------------------------------------------------------------------------o
void Command_RegSpawn( CSocket *s )
{
VALIDATESOCKET( s );
if( Commands->NumArguments() == 2 )
{
UI32 itemsSpawned = 0;
UI32 npcsSpawned = 0;
if( oldstrutil::upper( Commands->CommandString( 2, 2 )) == "ALL" )
{
std::for_each( cwmWorldState->spawnRegions.begin(), cwmWorldState->spawnRegions.end(), [&itemsSpawned, &npcsSpawned]( std::pair<UI16, CSpawnRegion *> entry )
{
if( entry.second )
{
entry.second->DoRegionSpawn( itemsSpawned, npcsSpawned );
}
});
if( itemsSpawned || npcsSpawned )
{
s->SysMessage( 9021, itemsSpawned, npcsSpawned, cwmWorldState->spawnRegions.size() ); // Spawned %u items and %u npcs in %u SpawnRegions
}
else
{
s->SysMessage( 9022, cwmWorldState->spawnRegions.size() ); // Failed to spawn any new items or npcs in %u SpawnRegions
}
}
else
{
UI16 spawnRegNum = static_cast<UI16>( Commands->Argument( 1 ));
if( cwmWorldState->spawnRegions.find( spawnRegNum ) != cwmWorldState->spawnRegions.end() )
{
CSpawnRegion *spawnReg = cwmWorldState->spawnRegions[spawnRegNum];
if( spawnReg != nullptr )
{
spawnReg->DoRegionSpawn( itemsSpawned, npcsSpawned );
if( itemsSpawned || npcsSpawned )
{
s->SysMessage( 9023, spawnReg->GetName().c_str(), spawnRegNum, itemsSpawned, npcsSpawned ); // Region: '%s'(%u) spawned %u items and %u npcs
}
else
{
s->SysMessage( 9024, spawnReg->GetName().c_str(), spawnRegNum ); // Region: '%s'(%u) failed to spawn any new items or npcs
}
return;
}
}
s->SysMessage( 9025, spawnRegNum ); // Invalid SpawnRegion %u
}
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_LoadDefaults()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Loads the hardcoded server defaults for various settings in uox.ini
//o------------------------------------------------------------------------------------------------o
void Command_LoadDefaults( void )
{
cwmWorldState->ServerData()->ResetDefaults();
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_CQ()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Display the Counselor queue when used with no arguments
//| Can trigger other queue-related commands when arguments are provided
//o------------------------------------------------------------------------------------------------o
void Command_CQ( CSocket *s )
{
VALIDATESOCKET( s );
if( Commands->NumArguments() == 2 )
{
std::string upperCommand = oldstrutil::upper( Commands->CommandString( 2, 2 ));
if( upperCommand == "NEXT" ) // Go to next call in Counselor queue
{
NextCall( s, false );
}
else if( upperCommand == "CLEAR" ) // Close and clear current call as resolved
{
CloseCall( s, false );
}
else if( upperCommand == "CURR" ) // Take Counselor to current call they are on
{
CurrentCall( s, false );
}
else if( upperCommand == "TRANSFER" ) // Transfer call from Counselor queue to GM queue
{
CChar *mChar = s->CurrcharObj();
if( mChar->GetCallNum() != 0 )
{
if( CounselorQueue->GotoPos( CounselorQueue->FindCallNum( mChar->GetCallNum() )))
{
CHelpRequest pageToAdd;
CHelpRequest *currentPage = nullptr;
currentPage = CounselorQueue->Current();
pageToAdd.Reason( currentPage->Reason() );
pageToAdd.WhoPaging( currentPage->WhoPaging() );
pageToAdd.IsHandled( false );
pageToAdd.TimeOfPage( time( nullptr ));
GMQueue->Add( &pageToAdd );
s->SysMessage( 74 ); // Call successfully transferred to the GM queue.
CloseCall( s, false );
}
else
{
s->SysMessage( 75 ); // No such call exists
}
}
else
{
s->SysMessage( 72 ); // You are not currently on a call.
}
}
}
else
{
CounselorQueue->SendAsGump( s ); // Show the Counselor queue, not GM queue
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_GQ()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Display the GM queue when used with no arguments
//| Can trigger other queue-related commands when arguments are provided
//o------------------------------------------------------------------------------------------------o
void Command_GQ( CSocket *s )
{
VALIDATESOCKET( s );
if( Commands->NumArguments() == 2 )
{
std::string upperCommand = oldstrutil::upper( Commands->CommandString( 2, 2 ));
if( upperCommand == "NEXT" ) // Go to next call in GM queue
{
NextCall( s, true );
}
else if( upperCommand == "CLEAR" ) // Close and clear current call as resolved
{
CloseCall( s, true );
}
else if( upperCommand == "CURR" ) // Take GM to current call they are on
{
CurrentCall( s, true );
}
}
else
{
GMQueue->SendAsGump( s );
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_MineCheck()
//o------------------------------------------------------------------------------------------------o
//| Purpose - (d) Set the server mine check status to determine where mining will work
//| 0 - allow mining everywhere
//| 1 - mountainsides and cave floors
//| 2 - mining regions only
//o------------------------------------------------------------------------------------------------o
void Command_MineCheck( void )
{
if( Commands->NumArguments() == 2 )
{
cwmWorldState->ServerData()->MineCheck( static_cast<UI08>( Commands->Argument( 1 )));
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_Guards()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Enables (ON) or disables (OFF) town guards globally
//o------------------------------------------------------------------------------------------------o
void Command_Guards( void )
{
if( oldstrutil::upper( Commands->CommandString( 2, 2 )) == "ON" )
{
cwmWorldState->ServerData()->GuardStatus( true );
SysBroadcast( Dictionary->GetEntry( 61 )); // Guards have been reactivated.
}
else if( oldstrutil::upper( Commands->CommandString( 2, 2 )) == "OFF" )
{
cwmWorldState->ServerData()->GuardStatus( false );
SysBroadcast( Dictionary->GetEntry( 62 )); // Warning: Guards have been deactivated globally.
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_Announce()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Enables (ON) or disables (OFF) announcement of world saves
//o------------------------------------------------------------------------------------------------o
void Command_Announce( void )
{
if( oldstrutil::upper( Commands->CommandString( 2, 2 )) == "ON" )
{
cwmWorldState->ServerData()->ServerAnnounceSaves( true );
SysBroadcast( Dictionary->GetEntry( 63 )); // WorldStat Saves will be displayed.
}
else if( oldstrutil::upper( Commands->CommandString( 2, 2 )) == "OFF" )
{
cwmWorldState->ServerData()->ServerAnnounceSaves( false );
SysBroadcast( Dictionary->GetEntry( 64 )); // WorldStat Saves will not be displayed.
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_PDump()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Display some performance information
//o------------------------------------------------------------------------------------------------o
void Command_PDump( CSocket *s )
{
VALIDATESOCKET( s );
UI32 networkTimeCount = cwmWorldState->ServerProfile()->NetworkTimeCount();
UI32 timerTimeCount = cwmWorldState->ServerProfile()->TimerTimeCount();
UI32 autoTimeCount = cwmWorldState->ServerProfile()->AutoTimeCount();
UI32 loopTimeCount = cwmWorldState->ServerProfile()->LoopTimeCount();
s->SysMessage( "Performance Dump:" );
s->SysMessage( "Network code: %fmsec [%i]", static_cast<R32>( static_cast<R32>( cwmWorldState->ServerProfile()->NetworkTime() ) / static_cast<R32>( networkTimeCount )), networkTimeCount );
s->SysMessage( "Timer code: %fmsec [%i]", static_cast<R32>( static_cast<R32>( cwmWorldState->ServerProfile()->TimerTime() ) / static_cast<R32>( timerTimeCount )), timerTimeCount );
s->SysMessage( "Auto code: %fmsec [%i]", static_cast<R32>( static_cast<R32>( cwmWorldState->ServerProfile()->AutoTime() ) / static_cast<R32>( autoTimeCount )), autoTimeCount );
s->SysMessage( "Loop Time: %fmsec [%i]", static_cast<R32>( static_cast<R32>( cwmWorldState->ServerProfile()->LoopTime() ) / static_cast<R32>( loopTimeCount )), loopTimeCount );
s->SysMessage( "Simulation Cycles/Sec: %f", ( 1000.0 * ( 1.0 / static_cast<R32>( static_cast<R32>( cwmWorldState->ServerProfile()->LoopTime() ) / static_cast<R32>( loopTimeCount )))));
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_SpawnKill()
//o------------------------------------------------------------------------------------------------o
//| Purpose - (d) Kills spawns from the specified spawn region in SPAWN.DFN
//o------------------------------------------------------------------------------------------------o
auto Command_SpawnKill( CSocket *s ) -> void
{
VALIDATESOCKET( s );
if( Commands->NumArguments() == 2 )
{
UI16 regNum = static_cast<UI16>( Commands->Argument( 1 ));
if( cwmWorldState->spawnRegions.find( regNum ) != cwmWorldState->spawnRegions.end() )
{
auto spawnReg = cwmWorldState->spawnRegions[regNum];
if( spawnReg )
{
SI32 killed = 0;
s->SysMessage( 349 ); // Killing spawn, this may cause lag...
std::vector<CChar *> spCharsToDelete;
auto spCharList = spawnReg->GetSpawnedCharsList();
for( auto &spChar : spCharList->collection() )
{
if( ValidateObject( spChar ))
{
if( spChar->IsSpawned() )
{
// Store reference to character we want to delete
spCharsToDelete.push_back( spChar );
}
}
}
// Loop through the characters we want to delete from spawn region
std::for_each( spCharsToDelete.begin(), spCharsToDelete.end(), [&killed]( CChar *charToDelete )
{
Effects->Bolteffect( charToDelete );
Effects->PlaySound( charToDelete, 0x0029 );
charToDelete->Delete();
++killed;
});
s->SysMessage( 84 ); // Done.
s->SysMessage( 350, killed, regNum ); // %i of Spawn %i have been killed.
}
}
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - BuildWhoGump()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Build and send a gump with a list of players based on provided criteria
//o------------------------------------------------------------------------------------------------o
void BuildWhoGump( CSocket *s, UI08 commandLevel, std::string title )
{
UI16 j = 0;
CGumpDisplay Who( s, 400, 300 );
Who.SetTitle( title );
{
for( auto &iSock : Network->connClients )
{
CChar *iChar = iSock->CurrcharObj();
if( iChar->GetCommandLevel() >= commandLevel )
{
auto temp = oldstrutil::format( "%i) %s", j, iChar->GetName().c_str() );
Who.AddData( temp, iChar->GetSerial(), 3 );
}
++j;
}
}
Who.Send( 4, false, INVALIDSERIAL );
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_Who()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Displays a list of users currently online
//o------------------------------------------------------------------------------------------------o
void Command_Who( CSocket *s )
{
VALIDATESOCKET( s );
BuildWhoGump( s, 0, "Who's Online" );
}
//o------------------------------------------------------------------------------------------------o
//| Function - void Command_GMs( CSocket *s )
//o------------------------------------------------------------------------------------------------o
//| Purpose - Displays a list of server staff currently online
//o------------------------------------------------------------------------------------------------o
void Command_GMs( CSocket *s )
{
VALIDATESOCKET( s );
BuildWhoGump( s, CL_CNS, Dictionary->GetEntry( 77, s->Language() )); // Current Staff online
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_ReportBug()
//| Date - 9th February, 2000
//o------------------------------------------------------------------------------------------------o
//| Purpose - Writes out a bug reported by player to the bug file
//o------------------------------------------------------------------------------------------------o
void Command_ReportBug( CSocket *s )
{
VALIDATESOCKET( s );
CChar *mChar = s->CurrcharObj();
if( !ValidateObject( mChar ))
return;
std::string logName = cwmWorldState->ServerData()->Directory( CSDDP_LOGS ) + "bugs.log";
std::ofstream logDestination;
logDestination.open( logName.c_str(), std::ios::out | std::ios::app );
if( !logDestination.is_open() )
{
Console.Error( oldstrutil::format( "Unable to open bugs log file %s!", logName.c_str() ));
return;
}
char dateTime[1024];
RealTime( dateTime );
logDestination << "[" << dateTime << "] ";
logDestination << "<" << mChar->GetName() << "> ";
logDestination << "Reports: " << Commands->CommandString( 2 ) << std::endl;
logDestination.close();
s->SysMessage( 87 ); // Thank you for your continuing support, your feedback is important to us
bool x = false;
{
for( auto &iSock : Network->connClients )
{
CChar *iChar = iSock->CurrcharObj();
if( ValidateObject( iChar ))
{
if( iChar->IsGMPageable() )
{
x = true;
iSock->SysMessage( 277, mChar->GetName().c_str(), Commands->CommandString( 2 ).c_str() ); // User %s reported a bug (%s)
}
}
}
}
if( x )
{
s->SysMessage( 88 ); // Available Game Masters have been notified of your bug submission.
}
else
{
s->SysMessage( 89 ); // There was no Game Master available to note your bug report.
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_ForceWho()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Brings up an interactive listing of online users.
//o------------------------------------------------------------------------------------------------o
void Command_ForceWho( CSocket *s )
{
VALIDATESOCKET( s );
WhoList->ZeroWho();
WhoList->SendSocket( s );
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_ValidCmd()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Displays all valid commands for the user's character
//o------------------------------------------------------------------------------------------------o
void Command_ValidCmd( CSocket *s )
{
VALIDATESOCKET( s );
CChar *mChar = s->CurrcharObj();
UI08 targetCommand = mChar->GetCommandLevel();
CGumpDisplay targetCmds( s, 300, 300 );
targetCmds.SetTitle( "Valid Commands" );
for( COMMANDMAP_ITERATOR myCounter = CommandMap.begin(); myCounter != CommandMap.end(); ++myCounter )
{
if( myCounter->second.cmdLevelReq <= targetCommand )
{
targetCmds.AddData( myCounter->first, myCounter->second.cmdLevelReq );
}
}
for( TARGETMAP_ITERATOR targCounter = TargetMap.begin(); targCounter != TargetMap.end(); ++targCounter )
{
if( targCounter->second.cmdLevelReq <= targetCommand )
{
targetCmds.AddData( targCounter->first, targCounter->second.cmdLevelReq );
}
}
for( JSCOMMANDMAP_ITERATOR jsCounter = JSCommandMap.begin(); jsCounter != JSCommandMap.end(); ++jsCounter )
{
if( jsCounter->second.cmdLevelReq <= targetCommand )
{
targetCmds.AddData( jsCounter->first, jsCounter->second.cmdLevelReq );
}
}
targetCmds.Send( 4, false, INVALIDSERIAL );
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_HowTo()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Opens a list of commands, with explanations of how to use each specific command
//o------------------------------------------------------------------------------------------------o
void Command_HowTo( CSocket *s )
{
VALIDATESOCKET( s );
std::string commandStart = oldstrutil::upper( Commands->CommandString( 2 ));
if( commandStart.empty() )
{
CChar *mChar = s->CurrcharObj();
if( !ValidateObject( mChar ))
return;
SI32 iCmd = 2;
SI32 numAdded = 0;
UI08 pagenum = 1;
UI16 position = 40;
UI16 linenum = 1;
CPSendGumpMenu toSend;
toSend.UserId( INVALIDSERIAL );
toSend.GumpId( 13 );
toSend.addCommand( oldstrutil::format( "resizepic 0 0 %u 480 320", cwmWorldState->ServerData()->BackgroundPic() ));
toSend.addCommand( "page 0" );
toSend.addCommand( "text 200 20 0 0" );
toSend.addText( "HOWTO" );
toSend.addCommand( oldstrutil::format( "button 440 20 %u %i 1 0 1", cwmWorldState->ServerData()->ButtonCancel(), cwmWorldState->ServerData()->ButtonCancel() + 1 ));
UI08 currentLevel = mChar->GetCommandLevel();
COMMANDMAP_ITERATOR gAdd = CommandMap.begin();
TARGETMAP_ITERATOR tAdd = TargetMap.begin();
JSCOMMANDMAP_ITERATOR jAdd = JSCommandMap.begin();
toSend.addCommand( "page 1" );
bool justDonePageAdd = false;
while( gAdd != CommandMap.end() )
{
if( numAdded > 0 && !(numAdded%10) && !justDonePageAdd )
{
position = 40;
++pagenum;
toSend.addCommand( oldstrutil::format( "page %u", pagenum ));
justDonePageAdd = true;
}
if( gAdd->second.cmdLevelReq <= currentLevel )
{
justDonePageAdd = false;
toSend.addCommand( oldstrutil::format( "text 50 %u %u %u", position, cwmWorldState->ServerData()->LeftTextColour(), linenum ));
toSend.addCommand( oldstrutil::format("button 20 %u %u %i %u 0 %i", position, cwmWorldState->ServerData()->ButtonRight(), cwmWorldState->ServerData()->ButtonRight() + 1, pagenum, iCmd ));
toSend.addText( gAdd->first );
++numAdded;
++linenum;
position += 20;
}
++iCmd;
++gAdd;
}
while( tAdd != TargetMap.end() )
{
if( numAdded > 0 && !(numAdded%10) && !justDonePageAdd )
{
position = 40;
++pagenum;
toSend.addCommand( oldstrutil::format( "page %u", pagenum ));
justDonePageAdd = true;
}
if( tAdd->second.cmdLevelReq <= currentLevel )
{
justDonePageAdd = false;
toSend.addCommand( oldstrutil::format( "text 50 %u %u %u", position, cwmWorldState->ServerData()->LeftTextColour(), linenum ));
toSend.addCommand( oldstrutil::format( "button 20 %u %u %i %u 0 %i", position, cwmWorldState->ServerData()->ButtonRight(), cwmWorldState->ServerData()->ButtonRight() + 1, pagenum, iCmd ));
toSend.addText( tAdd->first );
++numAdded;
++linenum;
position += 20;
}
++iCmd;
++tAdd;
}
while( jAdd != JSCommandMap.end() )
{
if( numAdded > 0 && !(numAdded%10) && !justDonePageAdd )
{
position = 40;
++pagenum;
toSend.addCommand( oldstrutil::format( "page %u", pagenum ));
justDonePageAdd = true;
}
if( jAdd->second.cmdLevelReq <= currentLevel )
{
justDonePageAdd = false;
toSend.addCommand( oldstrutil::format( "text 50 %u %u %u", position, cwmWorldState->ServerData()->LeftTextColour(), linenum ));
toSend.addCommand( oldstrutil::format( "button 20 %u %u %i %u 0 %i", position, cwmWorldState->ServerData()->ButtonRight(), cwmWorldState->ServerData()->ButtonRight() + 1, pagenum, iCmd ));
toSend.addText( jAdd->first );
++numAdded;
++linenum;
position += 20;
}
++iCmd;
++jAdd;
}
pagenum = 1;
for( SI32 i = 0; i < numAdded; i += 10 )
{
toSend.addCommand( oldstrutil::format( "page %u", pagenum ));
if( i >= 10 )
{
toSend.addCommand( oldstrutil::format( "button 30 290 %u %i 0 %i", cwmWorldState->ServerData()->ButtonLeft(), cwmWorldState->ServerData()->ButtonLeft() + 1, pagenum - 1 )); //back button
}
if(( numAdded > 10 ) && (( i + 10 ) < numAdded ))
{
toSend.addCommand( oldstrutil::format("button 440 290 %u %i 0 %i", cwmWorldState->ServerData()->ButtonRight(), cwmWorldState->ServerData()->ButtonRight() + 1, pagenum + 1 ));
}
++pagenum;
}
toSend.Finalize();
s->Send( &toSend );
}
else
{
SI32 i = 0;
COMMANDMAP_ITERATOR toFind;
for( toFind = CommandMap.begin(); toFind != CommandMap.end(); ++toFind )
{
if( commandStart == toFind->first )
{
break;
}
++i;
}
if( toFind == CommandMap.end() )
{
TARGETMAP_ITERATOR findTarg;
for( findTarg = TargetMap.begin(); findTarg != TargetMap.end(); ++findTarg )
{
if( commandStart == findTarg->first )
{
break;
}
++i;
}
if( findTarg == TargetMap.end() )
{
JSCOMMANDMAP_ITERATOR findJS = JSCommandMap.begin();
for( findJS = JSCommandMap.begin(); findJS != JSCommandMap.end(); ++findJS )
{
if( commandStart == findJS->first )
{
break;
}
++i;
}
if( findJS == JSCommandMap.end() )
{
s->SysMessage( 280 ); // Error finding command
return;
}
}
}
HandleHowTo( s, i );
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_Temp()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Displays the current temperature in user's current townregion
//o------------------------------------------------------------------------------------------------o
void Command_Temp( CSocket *s )
{
VALIDATESOCKET( s );
CChar *mChar = s->CurrcharObj();
if( !ValidateObject( mChar ))
return;
CTownRegion *reg = mChar->GetRegion();
WEATHID toGrab = reg->GetWeather();
if( toGrab != 0xFF )
{
R32 curTemp = Weather->Temp( toGrab );
s->SysMessage( 1751, curTemp ); // It is currently %f degrees
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - Command_Status()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Opens the HTML status information gump
//o------------------------------------------------------------------------------------------------o
void Command_Status( CSocket *s )
{
VALIDATESOCKET( s );
HTMLTemplates->TemplateInfoGump( s );
}
COMMANDMAP CommandMap;
TARGETMAP TargetMap;
JSCOMMANDMAP JSCommandMap;
void CCommands::CommandReset()
{
// TargetMap[Command Name] = TargetMapEntry_st(Required Command Level, Command Type, Target ID, Dictionary Entry);
// A
// B
// C
// D
// E
// F
// G
// H
// I
TargetMap["INFO"] = TargetMapEntry_st( CL_GM, CMD_TARGET, TARGET_INFO, 261);
// J
// K
// L
// M
TargetMap["MAKE"] = TargetMapEntry_st( CL_ADMIN, CMD_TARGETTXT, TARGET_MAKESTATUS, 279);
// N
// O
// P
// Q
// R
// S
TargetMap["SHOWSKILLS"] = TargetMapEntry_st( CL_GM, CMD_TARGETINT, TARGET_SHOWSKILLS, 260);
// T
//TargetMap["TWEAK"] = TargetMapEntry_st( CL_GM, CMD_TARGET, TARGET_TWEAK, 229);
// U
// V
// W
TargetMap["WSTATS"] = TargetMapEntry_st( CL_CNS, CMD_TARGET, TARGET_WSTATS, 183);
// X
// Y
// Z
// CommandMap[Command Name] = CommandMapEntry_st(Required Command Level, Command Type, Command Function);
//A
CommandMap["ADDACCOUNT"] = CommandMapEntry_st( CL_GM, CMD_SOCKFUNC, (CMD_DEFINE)&Command_AddAccount);
CommandMap["ANNOUNCE"] = CommandMapEntry_st( CL_GM, CMD_FUNC, (CMD_DEFINE)&Command_Announce);
//B
//C
CommandMap["CQ"] = CommandMapEntry_st( CL_CNS, CMD_SOCKFUNC, (CMD_DEFINE)&Command_CQ);
CommandMap["COMMAND"] = CommandMapEntry_st( CL_GM, CMD_SOCKFUNC, (CMD_DEFINE)&Command_Command);
//D
CommandMap["DYE"] = CommandMapEntry_st( CL_GM, CMD_SOCKFUNC, (CMD_DEFINE)&Command_Dye);
//E
//F
CommandMap["FORCEWHO"] = CommandMapEntry_st( CL_GM, CMD_SOCKFUNC, (CMD_DEFINE)&Command_ForceWho);
CommandMap["FIXSPAWN"] = CommandMapEntry_st( CL_GM, CMD_FUNC, (CMD_DEFINE)&command_fixspawn);
//G,
CommandMap["GETLIGHT"] = CommandMapEntry_st( CL_GM, CMD_SOCKFUNC, (CMD_DEFINE)&Command_GetLight);
CommandMap["GUARDS"] = CommandMapEntry_st( CL_GM, CMD_FUNC, (CMD_DEFINE)&Command_Guards);
CommandMap["GMS"] = CommandMapEntry_st( CL_CNS, CMD_SOCKFUNC, (CMD_DEFINE)&Command_GMs);
CommandMap["GMMENU"] = CommandMapEntry_st( CL_GM, CMD_SOCKFUNC, (CMD_DEFINE)&Command_GmMenu);
CommandMap["GCOLLECT"] = CommandMapEntry_st( CL_GM, CMD_FUNC, (CMD_DEFINE)&CollectGarbage);
CommandMap["GQ"] = CommandMapEntry_st( CL_GM, CMD_SOCKFUNC, (CMD_DEFINE)&Command_GQ);
//H
CommandMap["HOWTO"] = CommandMapEntry_st( CL_PLAYER, CMD_SOCKFUNC, (CMD_DEFINE)&Command_HowTo );
//I
//J
//K
//L
CommandMap["LOADDEFAULTS"] = CommandMapEntry_st( CL_ADMIN, CMD_FUNC, (CMD_DEFINE)&Command_LoadDefaults);
//M
CommandMap["MEMSTATS"] = CommandMapEntry_st( CL_GM, CMD_SOCKFUNC, (CMD_DEFINE)&Command_MemStats);
CommandMap["MINECHECK"] = CommandMapEntry_st( CL_GM, CMD_FUNC, (CMD_DEFINE)&Command_MineCheck);
//N
//O
//P
CommandMap["PDUMP"] = CommandMapEntry_st( CL_GM, CMD_SOCKFUNC, (CMD_DEFINE)&Command_PDump);
CommandMap["POST"] = CommandMapEntry_st( CL_CNS, CMD_SOCKFUNC, (CMD_DEFINE)&Command_GetPost);
//Q
//R
CommandMap["RESTOCK"] = CommandMapEntry_st( CL_GM, CMD_SOCKFUNC, (CMD_DEFINE)&Command_Restock);
CommandMap["RESPAWN"] = CommandMapEntry_st( CL_GM, CMD_FUNC, (CMD_DEFINE)&Command_Respawn);
CommandMap["REGSPAWN"] = CommandMapEntry_st( CL_GM, CMD_SOCKFUNC, (CMD_DEFINE)&Command_RegSpawn);
CommandMap["REPORTBUG"] = CommandMapEntry_st( CL_PLAYER, CMD_SOCKFUNC, (CMD_DEFINE)&Command_ReportBug);
//S
CommandMap["SETPOST"] = CommandMapEntry_st( CL_CNS, CMD_SOCKFUNC, (CMD_DEFINE)&Command_SetPost);
CommandMap["SPAWNKILL"] = CommandMapEntry_st( CL_GM, CMD_SOCKFUNC, (CMD_DEFINE)&Command_SpawnKill);
CommandMap["SETSHOPRESTOCKRATE"]= CommandMapEntry_st( CL_GM, CMD_SOCKFUNC, (CMD_DEFINE)&Command_SetShopRestockRate);
CommandMap["SETTIME"] = CommandMapEntry_st( CL_GM, CMD_FUNC, (CMD_DEFINE)&Command_SetTime);
CommandMap["SHUTDOWN"] = CommandMapEntry_st( CL_GM, CMD_FUNC, (CMD_DEFINE)&Command_Shutdown);
CommandMap["SAVE"] = CommandMapEntry_st( CL_GM, CMD_FUNC, (CMD_DEFINE)&Command_Save);
CommandMap["STATUS"] = CommandMapEntry_st( CL_GM, CMD_SOCKFUNC, (CMD_DEFINE)&Command_Status);
CommandMap["SHOWIDS"] = CommandMapEntry_st( CL_GM, CMD_SOCKFUNC, (CMD_DEFINE)&Command_ShowIds);
//T
CommandMap["TEMP"] = CommandMapEntry_st( CL_GM, CMD_SOCKFUNC, (CMD_DEFINE)&Command_Temp );
CommandMap["TELL"] = CommandMapEntry_st( CL_GM, CMD_SOCKFUNC, (CMD_DEFINE)&Command_Tell);
CommandMap["TILE"] = CommandMapEntry_st( CL_GM, CMD_SOCKFUNC, (CMD_DEFINE)&Command_Tile);
//U
//V
CommandMap["VALIDCMD"] = CommandMapEntry_st( CL_PLAYER, CMD_SOCKFUNC, (CMD_DEFINE)&Command_ValidCmd );
//W
CommandMap["WHO"] = CommandMapEntry_st( CL_CNS, CMD_SOCKFUNC, (CMD_DEFINE)&Command_Who);
//X
//Y
//Z
}
//o------------------------------------------------------------------------------------------------o
//| Function - CCommands::UnRegister()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Unregisters a command from JS command table
//o------------------------------------------------------------------------------------------------o
void CCommands::UnRegister( const std::string &cmdName, [[maybe_unused]] cScript *toRegister )
{
#if defined( UOX_DEBUG_MODE )
Console.Print( oldstrutil::format( " UnRegistering command %s\n", cmdName.c_str() ));
#endif
std::string upper = cmdName;
upper = oldstrutil::upper( upper );
JSCOMMANDMAP_ITERATOR p = JSCommandMap.find( upper );
if( p != JSCommandMap.end() )
{
JSCommandMap.erase( p );
}
#if defined( UOX_DEBUG_MODE )
else
{
Console.Print( oldstrutil::format( " Command \"%s\" was not found.\n", cmdName.c_str() ));
}
#endif
}
//o------------------------------------------------------------------------------------------------o
//| Function - CCommands::Register()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Resgisters a new command in JS command table
//o------------------------------------------------------------------------------------------------o
void CCommands::Register( const std::string &cmdName, UI16 scriptId, UI08 cmdLevel, bool isEnabled )
{
#if defined( UOX_DEBUG_MODE )
Console.Print( " " );
Console.MoveTo( 15 );
Console.Print( "Registering " );
Console.TurnYellow();
Console.Print( cmdName );
Console.TurnNormal();
Console.MoveTo( 50 );
Console.Print( "@ command level " );
Console.TurnYellow();
Console.Print( oldstrutil::format( "%i\n", cmdLevel ));
Console.TurnNormal();
#endif
std::string upper = cmdName;
upper = oldstrutil::upper( upper );
JSCommandMap[upper] = JSCommandEntry_st( cmdLevel, scriptId, isEnabled );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CCommands::SetCommandStatus()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Enables or disables a command in JS command table
//o------------------------------------------------------------------------------------------------o
void CCommands::SetCommandStatus( const std::string &cmdName, bool isEnabled )
{
std::string upper = cmdName;
upper = oldstrutil::upper( upper );
JSCOMMANDMAP_ITERATOR toFind = JSCommandMap.find( upper );
if( toFind != JSCommandMap.end() )
{
toFind->second.isEnabled = isEnabled;
}
}