mirror of
https://github.com/UOX3DevTeam/UOX3
synced 2026-08-13 12:27:04 -04:00
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
529 lines
19 KiB
C++
529 lines
19 KiB
C++
#include "uox3.h"
|
|
#include "wholist.h"
|
|
#include "network.h"
|
|
#include "CPacketSend.h"
|
|
#include "Dictionary.h"
|
|
#include "jail.h"
|
|
#include "commands.h"
|
|
|
|
|
|
CWhoList *WhoList;
|
|
CWhoList *OffList;
|
|
|
|
//o------------------------------------------------------------------------------------------------o
|
|
//| Function - CWhoList::ResetUpdateFlag()
|
|
//| Date - 12th February, 2000
|
|
//o------------------------------------------------------------------------------------------------o
|
|
//| Purpose - Disables the WhoList update flag
|
|
//o------------------------------------------------------------------------------------------------o
|
|
void CWhoList::ResetUpdateFlag( void )
|
|
{
|
|
needsUpdating = false;
|
|
}
|
|
|
|
//o------------------------------------------------------------------------------------------------o
|
|
//| Class - CWhoList::CWhoList()
|
|
//| Date - 12th February, 2000
|
|
//o------------------------------------------------------------------------------------------------o
|
|
//| Purpose - Initializes the variables of the wholist on creation
|
|
//o------------------------------------------------------------------------------------------------o
|
|
CWhoList::CWhoList( bool trulyOnline )
|
|
{
|
|
FlagUpdate(); // first use will force it to update
|
|
gmCount = 0; // no GMs currently using it
|
|
whoMenuData.resize( 0 );
|
|
online = trulyOnline;
|
|
}
|
|
|
|
CWhoList::~CWhoList()
|
|
// PRE: WhoList class is active
|
|
// POST: Memory cleared/destroyed
|
|
// DATE: 12th February, 2000
|
|
{
|
|
ResetUpdateFlag();
|
|
gmCount = 0;
|
|
Delete();
|
|
}
|
|
//o------------------------------------------------------------------------------------------------o
|
|
//| Function - CWhoList::ResetUpdateFlag()
|
|
//| Date - 12th February, 2000
|
|
//o------------------------------------------------------------------------------------------------o
|
|
//| Purpose - Flags the WhoList as needing to be updated
|
|
//o------------------------------------------------------------------------------------------------o
|
|
void CWhoList::FlagUpdate( void )
|
|
{
|
|
needsUpdating = true;
|
|
}
|
|
|
|
//o------------------------------------------------------------------------------------------------o
|
|
//| Function - CWhoList::SendSocket()
|
|
//| Date - 12th February, 2000
|
|
//o------------------------------------------------------------------------------------------------o
|
|
//| Purpose - Sends the WhoList to a player's socket
|
|
//o------------------------------------------------------------------------------------------------o
|
|
void CWhoList::SendSocket( CSocket *toSendTo )
|
|
{
|
|
GMEnter();
|
|
if( needsUpdating ) //&& gmCount <= 1 ) // If we need to update, do it now while we still can
|
|
{
|
|
Update();
|
|
}
|
|
|
|
if( online )
|
|
{
|
|
SendVecsAsGump( toSendTo, one, two, 4, INVALIDSERIAL );
|
|
}
|
|
else
|
|
{
|
|
SendVecsAsGump( toSendTo, one, two, 11, INVALIDSERIAL );
|
|
}
|
|
}
|
|
|
|
//o------------------------------------------------------------------------------------------------o
|
|
//| Function - CWhoList::GMEnter()
|
|
//| Date - 12th February, 2000
|
|
//o------------------------------------------------------------------------------------------------o
|
|
//| Purpose - Increments the GM counter
|
|
//o------------------------------------------------------------------------------------------------o
|
|
void CWhoList::GMEnter( void )
|
|
{
|
|
++gmCount;
|
|
}
|
|
|
|
//o------------------------------------------------------------------------------------------------o
|
|
//| Function - CWhoList::GMLeave()
|
|
//| Date - 12th February, 2000
|
|
//o------------------------------------------------------------------------------------------------o
|
|
//| Purpose - Decrements the GM counter
|
|
//o------------------------------------------------------------------------------------------------o
|
|
void CWhoList::GMLeave( void )
|
|
{
|
|
if( gmCount != 0 )
|
|
{
|
|
--gmCount;
|
|
}
|
|
}
|
|
|
|
//o------------------------------------------------------------------------------------------------o
|
|
//| Function - CWhoList::Delete()
|
|
//| Date - 12th February, 2000
|
|
//o------------------------------------------------------------------------------------------------o
|
|
//| Purpose - Deletes all the entries in the WhoList
|
|
//o------------------------------------------------------------------------------------------------o
|
|
void CWhoList::Delete( void )
|
|
{
|
|
one.resize( 0 );
|
|
two.resize( 0 );
|
|
whoMenuData.resize( 0 );
|
|
}
|
|
|
|
//o------------------------------------------------------------------------------------------------o
|
|
//| Function - CWhoList::AddSerial()
|
|
//| Date - 23rd February, 2000
|
|
//o------------------------------------------------------------------------------------------------o
|
|
//| Purpose - Adds an entry into the whoMenuData array
|
|
//o------------------------------------------------------------------------------------------------o
|
|
void CWhoList::AddSerial( SERIAL toAdd )
|
|
{
|
|
whoMenuData.push_back( toAdd );
|
|
}
|
|
|
|
//o------------------------------------------------------------------------------------------------o
|
|
//| Function - CWhoList::ButtonSelect()
|
|
//| Date - 23rd February, 2000
|
|
//o------------------------------------------------------------------------------------------------o
|
|
//| Purpose - Does action based upon button selected
|
|
//o------------------------------------------------------------------------------------------------o
|
|
void CWhoList::ButtonSelect( CSocket *toSendTo, UI16 buttonPressed, UI08 type )
|
|
{
|
|
CChar *sourceChar = toSendTo->CurrcharObj();
|
|
|
|
if( buttonPressed < 200 )
|
|
{
|
|
if( buttonPressed == 0 )
|
|
return;
|
|
|
|
buttonPressed -= 7;
|
|
toSendTo->AddId( buttonPressed );
|
|
Command( toSendTo, type, buttonPressed );
|
|
return;
|
|
}
|
|
|
|
GMLeave();
|
|
SERIAL ser = toSendTo->AddId();
|
|
if( ser == INVALIDSERIAL )
|
|
{
|
|
toSendTo->SysMessage( 1387 ); // Selected character not found.
|
|
return;
|
|
}
|
|
|
|
SERIAL charSerial = whoMenuData[ser];
|
|
|
|
CChar *targetChar = CalcCharObjFromSer( charSerial ); // find selected char ...
|
|
if( !ValidateObject( targetChar ))
|
|
{
|
|
toSendTo->SysMessage( 1387 ); // Selected character not found.
|
|
return;
|
|
}
|
|
CSocket *trgSock = nullptr;
|
|
switch( buttonPressed )
|
|
{
|
|
case 200: // gochar
|
|
if( targetChar->GetCommandLevel() > sourceChar->GetCommandLevel() )
|
|
{
|
|
toSendTo->SysMessage( 1388 ); // You cannot go to such powerful beings.
|
|
return;
|
|
}
|
|
if( sourceChar->WorldNumber() != targetChar->WorldNumber() )
|
|
{
|
|
sourceChar->SetLocation( targetChar );
|
|
SendMapChange( targetChar->WorldNumber(), toSendTo );
|
|
}
|
|
else
|
|
{
|
|
sourceChar->SetLocation( targetChar );
|
|
}
|
|
break;
|
|
case 201: // xtele
|
|
if( targetChar->GetCommandLevel() > sourceChar->GetCommandLevel() )
|
|
{
|
|
toSendTo->SysMessage( 1389 ); // Such powerful beings don't come on your whim!
|
|
return;
|
|
}
|
|
if( !targetChar->IsNpc() && targetChar->WorldNumber() != sourceChar->WorldNumber() )
|
|
{
|
|
// Bring player's followers too
|
|
auto targetFollowers = targetChar->GetFollowerList();
|
|
for( const auto &targetFollower : targetFollowers->collection() )
|
|
{
|
|
if( ValidateObject( targetFollower ))
|
|
{
|
|
if( !targetFollower->GetMounted() && targetFollower->GetOwnerObj() == targetChar )
|
|
{
|
|
if( targetFollower->GetNpcWander() == WT_FOLLOW && ObjInOldRange( targetChar, targetFollower, DIST_BUILDRANGE )) // Be more lenient, bring followers within range
|
|
{
|
|
targetFollower->SetLocation( sourceChar->GetX(), sourceChar->GetY(), sourceChar->GetZ(), sourceChar->WorldNumber(), sourceChar->GetInstanceId() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Then bring the player too
|
|
targetChar->SetLocation( sourceChar );
|
|
trgSock = targetChar->GetSocket();
|
|
SendMapChange( sourceChar->WorldNumber(), trgSock );
|
|
}
|
|
else
|
|
{
|
|
targetChar->SetLocation( sourceChar );
|
|
}
|
|
break;
|
|
case 202: // jail char
|
|
if( sourceChar == targetChar )
|
|
{
|
|
toSendTo->SysMessage( 1390 ); // You cannot jail yourself!
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if( targetChar->GetCommandLevel() > sourceChar->GetCommandLevel() )
|
|
{
|
|
toSendTo->SysMessage( 1391 ); // You cannot jail someone superior to you.
|
|
return;
|
|
}
|
|
if( targetChar->IsJailed() )
|
|
{
|
|
toSendTo->SysMessage( 1070 ); // That player is already in jail!
|
|
}
|
|
|
|
else if( !JailSys->JailPlayer( targetChar, 0xFFFFFFFF ))
|
|
{
|
|
toSendTo->SysMessage( 1072 ); // All jails are currently full!
|
|
}
|
|
break;
|
|
}
|
|
case 203: // release
|
|
if( targetChar->GetCommandLevel() > sourceChar->GetCommandLevel() )
|
|
{
|
|
toSendTo->SysMessage( 1392 ); // You release someone more powerful than you.
|
|
return;
|
|
}
|
|
if( !targetChar->IsJailed() )
|
|
{
|
|
toSendTo->SysMessage( 1064 ); // That player is not in jail!
|
|
}
|
|
else
|
|
{
|
|
JailSys->ReleasePlayer( targetChar );
|
|
toSendTo->SysMessage( 1065, targetChar->GetName().c_str() ); // Player %s released.
|
|
}
|
|
break;
|
|
case 204: // kick player
|
|
if( targetChar == sourceChar )
|
|
{
|
|
toSendTo->SysMessage( 1393 ); // You cannot kick yourself.
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if( !IsOnline(( *targetChar ))) // local var overloads it
|
|
{
|
|
toSendTo->SysMessage( 1394 ); // You can't kick an offline player.
|
|
break;
|
|
}
|
|
if( targetChar->GetCommandLevel() > sourceChar->GetCommandLevel() )
|
|
{
|
|
toSendTo->SysMessage( 1395 ); // You cannot kick someone more powerful than you.
|
|
return;
|
|
}
|
|
toSendTo->SysMessage( 1396 ); // Kicking player.
|
|
Network->Disconnect( targetChar->GetSocket() );
|
|
break;
|
|
}
|
|
case 205:
|
|
// make it prettier later!
|
|
if( targetChar->GetCommandLevel() > sourceChar->GetCommandLevel() )
|
|
{
|
|
toSendTo->SysMessage( 1397 ); // You cannot spy on your betters.
|
|
return;
|
|
}
|
|
toSendTo->SysMessage( "X: %i Y: %i Z: %i World: %u", targetChar->GetX(), targetChar->GetY(), targetChar->GetZ(), targetChar->WorldNumber() );
|
|
break;
|
|
case 206: // remote cstats
|
|
if( targetChar->GetCommandLevel() > sourceChar->GetCommandLevel() )
|
|
{
|
|
toSendTo->SysMessage( 1398 ); // You have not the right to see the intimate details of your betters!
|
|
return;
|
|
}
|
|
toSendTo->SetDWord( 7, targetChar->GetSerial() );
|
|
// Trigger scripted command
|
|
Commands->Command( toSendTo, targetChar, "cstats", true );
|
|
break;
|
|
case 207: // remote tweak
|
|
if( targetChar->GetCommandLevel() > sourceChar->GetCommandLevel() )
|
|
{
|
|
toSendTo->SysMessage( 1399 ); // You certainly cannot adjust those that are your betters!
|
|
return;
|
|
}
|
|
toSendTo->SetDWord( 7, targetChar->GetSerial() );
|
|
// Trigger scripted command
|
|
Commands->Command( toSendTo, targetChar, "tweak", true );
|
|
break;
|
|
default:
|
|
Console.Error( " Fallout of switch statement without default. wholist.cpp, ButtonSelect()" );
|
|
break;
|
|
}
|
|
}
|
|
|
|
//o------------------------------------------------------------------------------------------------o
|
|
//| Function - CWhoList::Command()
|
|
//o------------------------------------------------------------------------------------------------o
|
|
//| Purpose - Builds the WhoList gump and sends it to the player
|
|
//o------------------------------------------------------------------------------------------------o
|
|
void CWhoList::Command( CSocket *toSendTo, UI08 type, UI16 buttonPressed )
|
|
{
|
|
SERIAL serial = whoMenuData[buttonPressed];
|
|
CChar *targetChar = CalcCharObjFromSer( serial ); // find selected char ...
|
|
|
|
if( !ValidateObject( targetChar ))
|
|
{
|
|
toSendTo->SysMessage( 1387 ); // Selected character not found.
|
|
return;
|
|
}
|
|
|
|
CPSendGumpMenu toSend;
|
|
toSend.UserId( INVALIDSERIAL );
|
|
toSend.GumpId( type );
|
|
|
|
UnicodeTypes sLang = toSendTo->Language();
|
|
UI16 lColour = cwmWorldState->ServerData()->LeftTextColour();
|
|
UI16 tColour = cwmWorldState->ServerData()->TitleColour();
|
|
UI16 butRight = cwmWorldState->ServerData()->ButtonRight();
|
|
//--static pages
|
|
toSend.addCommand( "page 0" );
|
|
toSend.addCommand( oldstrutil::format( "resizepic 0 0 %u 260 340", cwmWorldState->ServerData()->BackgroundPic() ));
|
|
toSend.addCommand( oldstrutil::format( "button 30 300 %u %i 1 0 1", cwmWorldState->ServerData()->ButtonCancel(), cwmWorldState->ServerData()->ButtonCancel() + 1 )); // OKAY
|
|
toSend.addCommand( oldstrutil::format( "text 30 40 %u 0", tColour )); // text <Spaces from Left> <Space from top> <Length, Color?> <# in order>
|
|
toSend.addCommand( oldstrutil::format( "text 30 60 %u 1", tColour) );
|
|
toSend.addCommand( "page 1" );
|
|
|
|
toSend.addCommand( oldstrutil::format( "text 50 90 %u 2", lColour) ); // goto text
|
|
toSend.addCommand( oldstrutil::format( "text 50 110 %u 3", lColour) ); // gettext
|
|
toSend.addCommand( oldstrutil::format( "text 50 130 %u 4", lColour )); // Jail text
|
|
toSend.addCommand( oldstrutil::format( "text 50 150 %u 5", lColour )); // Release text
|
|
toSend.addCommand( oldstrutil::format( "text 50 170 %u 6", lColour )); // Kick user text
|
|
toSend.addCommand( oldstrutil::format( "text 50 190 %u 7", lColour ));
|
|
toSend.addCommand( oldstrutil::format( "text 50 210 %u 8", lColour ));
|
|
toSend.addCommand( oldstrutil::format( "text 50 230 %u 9", lColour) );
|
|
toSend.addCommand( oldstrutil::format( "text 50 270 %u 10", lColour ));
|
|
toSend.addCommand( oldstrutil::format( "button 20 90 %u %i 1 0 200", butRight, butRight + 1 )); // goto button
|
|
toSend.addCommand( oldstrutil::format( "button 20 110 %u %i 1 0 201", butRight, butRight + 1 )); // get button
|
|
toSend.addCommand( oldstrutil::format( "button 20 130 %u %i 1 0 202", butRight, butRight + 1 )); // Jail button
|
|
toSend.addCommand( oldstrutil::format( "button 20 150 %u %i 1 0 203", butRight, butRight + 1 )); // Release button
|
|
toSend.addCommand( oldstrutil::format( "button 20 170 %u %i 1 0 204", butRight, butRight + 1 )); // kick button
|
|
toSend.addCommand( oldstrutil::format( "button 20 190 %u %i 1 0 205", butRight, butRight + 1 )); // Where button
|
|
toSend.addCommand( oldstrutil::format( "button 20 210 %u %i 1 0 206", butRight, butRight + 1 )); // Cstats button
|
|
toSend.addCommand( oldstrutil::format( "button 20 230 %u %i 1 0 207", butRight, butRight + 1 )); // Tweak button
|
|
|
|
toSend.addText(oldstrutil::format( "User %u selected (account %u)", buttonPressed, targetChar->GetAccount().wAccountIndex));
|
|
toSend.addText( oldstrutil::format( "Name: %s", targetChar->GetName().c_str() ));
|
|
toSend.addText( Dictionary->GetEntry( 1400, sLang ));
|
|
toSend.addText( Dictionary->GetEntry( 1401, sLang ));
|
|
toSend.addText( Dictionary->GetEntry( 1402, sLang ));
|
|
toSend.addText( Dictionary->GetEntry( 1403, sLang ));
|
|
toSend.addText( Dictionary->GetEntry( 1404, sLang ));
|
|
toSend.addText( Dictionary->GetEntry( 1405, sLang ));
|
|
toSend.addText( Dictionary->GetEntry( 1406, sLang ));
|
|
toSend.addText( Dictionary->GetEntry( 1407, sLang ));
|
|
toSend.addText( oldstrutil::format( "Serial#[%x %x %x %x]", targetChar->GetSerial( 1 ), targetChar->GetSerial( 2 ), targetChar->GetSerial( 3 ), targetChar->GetSerial( 4 )));
|
|
|
|
toSend.Finalize();
|
|
toSendTo->Send( &toSend );
|
|
}
|
|
|
|
void CWhoList::ZeroWho( void )
|
|
{
|
|
FlagUpdate();
|
|
gmCount = 0;
|
|
}
|
|
|
|
//o------------------------------------------------------------------------------------------------o
|
|
//| Function - CWhoList::Update()
|
|
//| Date - 12th February, 2000
|
|
//o------------------------------------------------------------------------------------------------o
|
|
//| Purpose - If multiple GMs using, doesn't actually update (deferred update)
|
|
//| Otherwise updates the arrays, and resets the update flag
|
|
//|
|
|
//| Changes - 1.1 June 29th, 2000
|
|
//| Written to use SendVecsAsGump, as well as making it flexible enough to show offline players
|
|
//o------------------------------------------------------------------------------------------------o
|
|
void CWhoList::Update( void )
|
|
{
|
|
/*if( gmCount > 1 )
|
|
{
|
|
return;
|
|
}*/
|
|
|
|
Delete();
|
|
constexpr UI16 maxsize = 512;
|
|
|
|
size_t i = 0;
|
|
const UI32 numPerPage = 13;
|
|
UI32 pagenum = 1, position = 40, linenum = 1, buttonnum = 7;
|
|
|
|
size_t k = cwmWorldState->GetPlayersOnline();
|
|
SI32 realCount = 1;
|
|
|
|
//--static pages
|
|
one.push_back( "page 0" );
|
|
one.push_back( oldstrutil::format( maxsize, "resizepic 0 0 %u 320 340", cwmWorldState->ServerData()->BackgroundPic() ));
|
|
one.push_back( oldstrutil::format( maxsize,"button 250 15 %u %i 1 0 1", cwmWorldState->ServerData()->ButtonCancel(), cwmWorldState->ServerData()->ButtonCancel() + 1 ));
|
|
one.push_back( oldstrutil::format( maxsize, "text 45 15 %u 0", cwmWorldState->ServerData()->TitleColour() ));
|
|
one.push_back( oldstrutil::format( maxsize, "page %u", pagenum ));
|
|
two.push_back( "Wholist" );
|
|
|
|
//--Start User List
|
|
if( online )
|
|
{
|
|
for( i = 0; i < k; ++i )
|
|
{
|
|
CSocket *tSock = Network->GetSockPtr( static_cast<UI32>( i ));
|
|
CChar *tChar = tSock->CurrcharObj();
|
|
if( !ValidateObject( tChar ))
|
|
continue;
|
|
|
|
if( realCount > 0 && !( realCount % numPerPage ))
|
|
{
|
|
position = 40;
|
|
++pagenum;
|
|
|
|
one.push_back( oldstrutil::format( maxsize, "page %u", pagenum ));
|
|
}
|
|
|
|
one.push_back( oldstrutil::format( maxsize, "text 50 %u %u %u", position, cwmWorldState->ServerData()->LeftTextColour(), linenum ));
|
|
one.push_back( oldstrutil::format( maxsize,"button 20 %u %u %i %u 0 %u", position, cwmWorldState->ServerData()->ButtonRight(), cwmWorldState->ServerData()->ButtonRight() + 1, pagenum, buttonnum ));
|
|
two.push_back( oldstrutil::format( maxsize, " %s", tChar->GetName().c_str() ));
|
|
AddSerial( tChar->GetSerial() );
|
|
|
|
position += 20;
|
|
++linenum;
|
|
++buttonnum;
|
|
++realCount;
|
|
}
|
|
|
|
two[0] = oldstrutil::format( maxsize, "Wholist [%i online]", realCount - 1 );
|
|
|
|
pagenum = 1;
|
|
k = realCount;
|
|
for( i = 0; i < k; i += numPerPage )
|
|
{
|
|
one.push_back( oldstrutil::format( maxsize, "page %u", pagenum ));
|
|
if( i >= numPerPage )
|
|
{
|
|
// back button
|
|
one.push_back( oldstrutil::format( maxsize, "button 50 300 %u %i 0 %i", cwmWorldState->ServerData()->ButtonLeft(), cwmWorldState->ServerData()->ButtonLeft() + 1, pagenum - 1 ));
|
|
}
|
|
if(( k > numPerPage ) && (( i + numPerPage ) < k ))
|
|
{
|
|
one.push_back( oldstrutil::format( maxsize, "button 260 300 %u %i 0 %i", cwmWorldState->ServerData()->ButtonRight(), cwmWorldState->ServerData()->ButtonRight() + 1, pagenum + 1 ));
|
|
}
|
|
++pagenum;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MAPUSERNAMEID_ITERATOR I;
|
|
CChar *ourChar = nullptr;
|
|
for( I = Accounts->Begin(); I != Accounts->End(); ++I )
|
|
{
|
|
CAccountBlock_st& actbSearch = I->second;
|
|
for( i = 0; i < 7; ++i )
|
|
{
|
|
ourChar = actbSearch.lpCharacters[i];
|
|
if( ValidateObject( ourChar ))
|
|
{
|
|
if( !IsOnline(( *ourChar )))
|
|
{
|
|
if( realCount > 0 && !( realCount % numPerPage ))
|
|
{
|
|
position = 40;
|
|
++pagenum;
|
|
|
|
one.push_back( oldstrutil::format( maxsize,"page %u", pagenum ));
|
|
}
|
|
|
|
one.push_back( oldstrutil::format( maxsize, "text 50 %u %u %u", position, cwmWorldState->ServerData()->LeftTextColour(), linenum ));
|
|
one.push_back( oldstrutil::format( maxsize,"button 20 %u %u %i %u 0 %u", position, cwmWorldState->ServerData()->ButtonRight(), cwmWorldState->ServerData()->ButtonRight() + 1, pagenum, buttonnum ));
|
|
two.push_back( oldstrutil::format( maxsize," %s (%s)", ourChar->GetName().c_str(), ourChar->GetLastOn().c_str() ));
|
|
AddSerial( ourChar->GetSerial() );
|
|
|
|
position += 20;
|
|
++linenum;
|
|
++buttonnum;
|
|
++realCount;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
two[0] = oldstrutil::format( maxsize, "Wholist [%i offline]", realCount - 1 );
|
|
|
|
pagenum = 1;
|
|
k = realCount;
|
|
for( i = 0; i < k; i += numPerPage )
|
|
{
|
|
one.push_back( oldstrutil::format( maxsize, "page %u", pagenum ));
|
|
if( i >= numPerPage )
|
|
{
|
|
//back button
|
|
one.push_back( oldstrutil::format( maxsize, "button 50 300 %u %i 0 %i", cwmWorldState->ServerData()->ButtonLeft(), cwmWorldState->ServerData()->ButtonLeft() + 1, pagenum - 1 ));
|
|
}
|
|
if(( k > numPerPage ) && (( i + numPerPage ) < k ))
|
|
{
|
|
one.push_back( oldstrutil::format( maxsize, "button 260 300 %u %i 0 %i", cwmWorldState->ServerData()->ButtonRight(), cwmWorldState->ServerData()->ButtonRight() + 1, pagenum + 1 ));
|
|
}
|
|
++pagenum;
|
|
}
|
|
}
|
|
ResetUpdateFlag();
|
|
}
|