mirror of
https://github.com/UOX3DevTeam/UOX3
synced 2026-08-13 12:27:04 -04:00
Fixed an issue with ItemID skill where using the skill on a crafted item would not output a result if creator of item was unknown Command logs now also include any parameters used with a command Fixed an issue where messages used with CustomTarget JS function would not get truncated properly if longer than the allowed maximum length Fixed some instances of potential buffer overrun and/or out-of-bounds memory access Fixed some potential null pointer dereferences Fixed a couple instances of missing breaks in switch cases Added [[maybe_unused]] attributes in places where return values are intentionally unused Added [[fallthrough]] attributes in places where switch case fallthroughs are intentional Misc minor code cleanup
7944 lines
249 KiB
C++
7944 lines
249 KiB
C++
#include "uox3.h"
|
||
#include "CPacketSend.h"
|
||
#include "speech.h"
|
||
#include "combat.h"
|
||
#include "cMagic.h"
|
||
#include "power.h"
|
||
#include "msgboard.h"
|
||
#include "mapstuff.h"
|
||
#include "PartySystem.h"
|
||
#include "cGuild.h"
|
||
|
||
// Unknown bytes
|
||
// 5->8
|
||
// 18->27
|
||
// 30->36
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPCharLocBody()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet that confirms client login
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x1B (Login Confirm)
|
||
//| Size: 37 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[4] player id
|
||
//| BYTE[4] unknown1
|
||
//| BYTE[2] bodyType
|
||
//| BYTE[2] xLoc
|
||
//| BYTE[2] yLoc
|
||
//| BYTE[2] zLoc
|
||
//| BYTE direction
|
||
//| BYTE[2] unknown2
|
||
//| BYTE[4] unknown3 (usually has FF somewhere in it)
|
||
//| BYTE[4] unknown4
|
||
//| BYTE flag byte
|
||
//| BYTE highlight color
|
||
//| BYTE[7] unknown5
|
||
//|
|
||
//| Newer, more up to date version of the packet. When did it change?
|
||
//| BYTE cmd
|
||
//| BYTE[4] player id
|
||
//| BYTE[4] unknown1
|
||
//| BYTE[2] bodyType
|
||
//| BYTE[2] xLoc
|
||
//| BYTE[2] yLoc
|
||
//| BYTE Unknown (0x0)
|
||
//| BYTE zLoc
|
||
//| BYTE direction
|
||
//| BYTE[4] unknown2 (0x0, Described as X in Jerrith's old docs)
|
||
//| BYTE[4] unknown3 (0x0, Described as Y in Jerrith's old docs)
|
||
//| BYTE unknown4 (0x0)
|
||
//| BYTE[2] Map Width/Server boundary width (minus eight according to POL)
|
||
//| BYTE[2] Map Height/Server boundary height
|
||
//| BYTE[2] unknown5 (0x0)
|
||
//| BYTE[4] unknown6 (0x0)
|
||
//|
|
||
//| Note: Only send once after login. It’s mandatory to send it once.
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPCharLocBody::Log( std::ofstream &outStream, bool fullHeader )
|
||
{
|
||
if( fullHeader )
|
||
outStream << "[SEND]Packet : CPCharLocBody 0x1B --> Length: " << pStream.GetSize() << TimeStamp() << std::endl;
|
||
outStream << "PlayerID : " << std::hex << "0x" << pStream.GetULong( 1 ) << std::endl;
|
||
outStream << "Unknown1 : " << std::dec << pStream.GetULong( 5 ) << std::endl;
|
||
outStream << "BodyType : " << std::hex << "0x" << pStream.GetUShort( 9 ) << std::endl;
|
||
outStream << "X Loc : " << std::dec << pStream.GetUShort( 11 ) << std::endl;
|
||
outStream << "Y Loc : " << pStream.GetUShort( 13 ) << std::endl;
|
||
outStream << "Z Loc : " << (UI16)pStream.GetByte( 16 ) << std::endl;
|
||
outStream << "Direction : " << (UI16)pStream.GetByte( 17 ) << std::endl;
|
||
outStream << "Unknown2 : " << pStream.GetUShort( 18 ) << std::endl;
|
||
outStream << "Unknown3 : " << pStream.GetULong( 20 ) << std::endl;
|
||
outStream << "Unknown4 : " << pStream.GetULong( 24 ) << std::endl;
|
||
outStream << "Map Width : " << pStream.GetUShort( 27 ) << std::endl;
|
||
outStream << "Map Height : " << pStream.GetUShort( 29 ) << std::endl;
|
||
outStream << "Flag Byte : " << (UI16)pStream.GetByte( 31 ) << std::endl;
|
||
outStream << "Highlight Colour : " << (UI16)pStream.GetByte( 32 ) << std::endl;
|
||
outStream << " Raw dump :" << std::endl;
|
||
CPUOXBuffer::Log( outStream, false );
|
||
}
|
||
|
||
void CPCharLocBody::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 37 );
|
||
pStream.WriteByte( 0, 0x1B );
|
||
|
||
for( UI08 k = 5; k < 9; ++k )
|
||
pStream.WriteByte( k, 0x00 );
|
||
for( UI08 j = 33; j < 37; ++j )
|
||
pStream.WriteByte( j, 0x00 );
|
||
|
||
HighlightColour( 0 );
|
||
}
|
||
CPCharLocBody::CPCharLocBody()
|
||
{
|
||
InternalReset();
|
||
}
|
||
|
||
CPCharLocBody::CPCharLocBody( CChar &toCopy )
|
||
{
|
||
InternalReset();
|
||
CopyData( toCopy );
|
||
}
|
||
|
||
void CPCharLocBody::CopyData( CChar &toCopy )
|
||
{
|
||
pStream.WriteLong( 1, toCopy.GetSerial() );
|
||
pStream.WriteShort( 9, toCopy.GetID() );
|
||
pStream.WriteShort( 11, toCopy.GetX() );
|
||
pStream.WriteShort( 13, toCopy.GetY() );
|
||
pStream.WriteShort( 15, toCopy.GetZ() );
|
||
pStream.WriteByte( 17, toCopy.GetDir() );
|
||
pStream.WriteByte( 18, 0x0 );
|
||
pStream.WriteShort( 19, 0xFFFF );
|
||
pStream.WriteShort( 21, 0xFFFF );
|
||
pStream.WriteLong( 23, 0x0000 );
|
||
MapData_st& mMap = Map->GetMapData( toCopy.WorldNumber() );
|
||
UI16 mapWidth = mMap.xBlock;
|
||
UI16 mapHeight = mMap.yBlock;
|
||
pStream.WriteShort( 27, mapWidth );
|
||
pStream.WriteShort( 29, mapHeight );
|
||
}
|
||
|
||
CPCharLocBody &CPCharLocBody::operator=( CChar &toCopy )
|
||
{
|
||
CopyData( toCopy );
|
||
return (*this);
|
||
}
|
||
|
||
void CPCharLocBody::Flag( UI08 toPut )
|
||
{
|
||
pStream.WriteByte( 31, toPut );
|
||
}
|
||
void CPCharLocBody::HighlightColour( UI08 color )
|
||
{
|
||
pStream.WriteByte( 32, color );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPacketSpeech()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet containing speech
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x1C (Send Speech)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] blockSize
|
||
//| BYTE[4] itemID (FF FF FF FF = system)
|
||
//| BYTE[2] model (item hex # - FF FF = system)
|
||
//| BYTE Type
|
||
//| 0x00 - Normal
|
||
//| 0x01 - Broadcast/System
|
||
//| 0x02 - Emote
|
||
//| 0x06 - System/Lower Corner
|
||
//| 0x07 - Message/Corner With Name
|
||
//| 0x08 - Whisper
|
||
//| 0x09 - Yell
|
||
//| 0x0A - Spell
|
||
//| 0x0D - Guild Chat
|
||
//| 0x0E - Alliance Chat
|
||
//| 0x0F - Command Prompts
|
||
//| BYTE[2] Text Color
|
||
//| BYTE[2] Font
|
||
//| BYTE[30] Name
|
||
//| BYTE[?] Null-Terminated Message (? = blockSize - 44)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPacketSpeech::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 44 );
|
||
pStream.WriteByte( 0, 0x1C );
|
||
}
|
||
CPacketSpeech::CPacketSpeech( CSpeechEntry &toCopy )
|
||
{
|
||
InternalReset();
|
||
CopyData( toCopy );
|
||
}
|
||
|
||
CPacketSpeech::CPacketSpeech( CPITalkRequest &toCopy )
|
||
{
|
||
InternalReset();
|
||
CopyData( toCopy );
|
||
}
|
||
|
||
void CPacketSpeech::CopyData( CPITalkRequest &toCopy )
|
||
{
|
||
Colour( toCopy.TextColour() );
|
||
Font( (FontType)toCopy.Font() );
|
||
Type( toCopy.Type() );
|
||
Speech( toCopy.Text() );
|
||
}
|
||
|
||
void CPacketSpeech::CopyData( CSpeechEntry &toCopy )
|
||
{
|
||
Speech( toCopy.Speech() );
|
||
SpeakerSerial( toCopy.Speaker() );
|
||
Colour( toCopy.Colour() );
|
||
Font( toCopy.Font() );
|
||
Language( toCopy.Language() );
|
||
Unicode( toCopy.Unicode() );
|
||
Type( toCopy.Type() );
|
||
switch( toCopy.SpkrType() )
|
||
{
|
||
case SPK_UNKNOWN:
|
||
case SPK_SYSTEM:
|
||
SpeakerName( "System" );
|
||
SpeakerModel( INVALIDID );
|
||
if( toCopy.Colour() == 0 )
|
||
Colour( 0x0040 );
|
||
break;
|
||
case SPK_CHARACTER:
|
||
CChar *ourChar;
|
||
ourChar = calcCharObjFromSer( toCopy.Speaker() );
|
||
if( ValidateObject( ourChar ) )
|
||
{
|
||
SpeakerName( ourChar->GetName() );
|
||
SpeakerModel( ourChar->GetID() );
|
||
}
|
||
else
|
||
SpeakerModel( INVALIDID );
|
||
break;
|
||
case SPK_ITEM:
|
||
CItem *ourItem;
|
||
ourItem = calcItemObjFromSer( toCopy.Speaker() );
|
||
if( ValidateObject( ourItem ) )
|
||
{
|
||
SpeakerName( ourItem->GetName() );
|
||
SpeakerModel( ourItem->GetID() );
|
||
}
|
||
else
|
||
SpeakerModel( INVALIDID );
|
||
break;
|
||
case SPK_NULL:
|
||
|
||
break;
|
||
}
|
||
if( toCopy.SpeakerName().length() != 0 )
|
||
SpeakerName( toCopy.SpeakerName() );
|
||
}
|
||
|
||
void CPacketSpeech::GhostIt( UI08 method )
|
||
{
|
||
// Method ignored currently
|
||
// Designed with the idea that you can garble text in more than one way
|
||
// eg 0 == ghost, 1 == racial, 2 == magical, etc etc
|
||
for( UI16 j = 44; j < pStream.GetSize() - 1; ++j )
|
||
{
|
||
if( pStream.GetByte( j ) != 32 )
|
||
pStream.WriteByte( j, ( RandomNum( 0, 1 ) == 0 ? 'O' : 'o' ) );
|
||
}
|
||
}
|
||
|
||
CPacketSpeech &CPacketSpeech::operator=( CSpeechEntry &toCopy )
|
||
{
|
||
CopyData( toCopy );
|
||
return (*this);
|
||
}
|
||
|
||
CPacketSpeech::CPacketSpeech() : isUnicode( false )
|
||
{
|
||
pStream.ReserveSize( 44 );
|
||
pStream.WriteByte( 0, 0x1C );
|
||
pStream.WriteShort( 1, 44 );
|
||
pStream.WriteLong( 3, 0xFFFFFFFF );
|
||
pStream.WriteShort( 7, 0xFFFF );
|
||
pStream.WriteString( 14, "System", 6 );
|
||
pStream.WriteByte( 20, 0 );
|
||
}
|
||
|
||
void CPacketSpeech::SpeakerName( const std::string& toPut )
|
||
{
|
||
size_t len = toPut.length();
|
||
if( len >= 30 )
|
||
{
|
||
pStream.WriteString( 14, toPut, 29 );
|
||
pStream.WriteByte( 43, 0 );
|
||
}
|
||
else
|
||
pStream.WriteString( 14, toPut, toPut.length() );
|
||
}
|
||
void CPacketSpeech::SpeakerSerial( SERIAL toPut )
|
||
{
|
||
pStream.WriteLong( 3, toPut );
|
||
}
|
||
void CPacketSpeech::SpeakerModel( UI16 toPut )
|
||
{
|
||
pStream.WriteShort( 7, toPut );
|
||
}
|
||
void CPacketSpeech::Colour( COLOUR toPut )
|
||
{
|
||
pStream.WriteShort( 10, toPut );
|
||
}
|
||
void CPacketSpeech::Font( FontType toPut )
|
||
{
|
||
pStream.WriteShort( 12, toPut );
|
||
}
|
||
#if defined( _MSC_VER )
|
||
#pragma note( "Function Warning: CPacketSpeech::Language(), does nothing" )
|
||
#endif
|
||
void CPacketSpeech::Language( UnicodeTypes toPut )
|
||
{
|
||
|
||
}
|
||
void CPacketSpeech::Unicode( bool toPut )
|
||
{
|
||
isUnicode = toPut;
|
||
}
|
||
void CPacketSpeech::Type( SpeechType toPut )
|
||
{
|
||
pStream.WriteByte( 9, static_cast<UI08>(toPut) );
|
||
}
|
||
void CPacketSpeech::Speech( const std::string& toPut )
|
||
{
|
||
size_t len = toPut.length();
|
||
const size_t newLen = 44 + len + 1;
|
||
|
||
pStream.ReserveSize( newLen );
|
||
pStream.WriteString( 44, toPut, toPut.length() );
|
||
pStream.WriteShort( 1, static_cast<UI16>(newLen) );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPWalkDeny()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet with movement rejection details
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x21 (Character Move Reject)
|
||
//| Size: 8 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE sequence #
|
||
//| BYTE[2] xLoc
|
||
//| BYTE[2] yLoc
|
||
//| BYTE direction
|
||
//| BYTE zLoc
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPWalkDeny::CPWalkDeny()
|
||
{
|
||
pStream.ReserveSize( 8 );
|
||
pStream.WriteByte( 0, 0x21 );
|
||
}
|
||
void CPWalkDeny::SequenceNumber( char newValue )
|
||
{
|
||
pStream.WriteByte( 1, newValue );
|
||
}
|
||
void CPWalkDeny::X( SI16 newValue )
|
||
{
|
||
pStream.WriteShort( 2, newValue );
|
||
}
|
||
void CPWalkDeny::Y( SI16 newValue )
|
||
{
|
||
pStream.WriteShort( 4, newValue );
|
||
}
|
||
void CPWalkDeny::Z( SI08 newValue )
|
||
{
|
||
pStream.WriteByte( 7, newValue );
|
||
}
|
||
void CPWalkDeny::Direction( char newValue )
|
||
{
|
||
pStream.WriteByte( 6, newValue );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPWalkOK()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet with movement acknowledgement
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x22 (Character Move ACK/ Resync Request)
|
||
//| Size: 3 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE sequence (matches sent sequence)
|
||
//| BYTE notoriety (updates the notoriety of the char)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPWalkOK::CPWalkOK()
|
||
{
|
||
pStream.ReserveSize( 3 );
|
||
pStream.WriteByte( 0, 0x22 );
|
||
}
|
||
void CPWalkOK::SequenceNumber( char newValue )
|
||
{
|
||
pStream.WriteByte( 1, newValue );
|
||
}
|
||
void CPWalkOK::FlagColour( UI08 newValue )
|
||
{
|
||
pStream.WriteByte( 2, newValue );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPExtMove()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet containing updated player details
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x77 (Update Player)
|
||
//| Size: 17 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[4] player id
|
||
//| BYTE[2] model
|
||
//| BYTE[2] xLoc
|
||
//| BYTE[2] yLoc
|
||
//| BYTE zLoc
|
||
//| BYTE direction
|
||
//| BYTE[2] hue/skin color
|
||
//| BYTE flag (bit field)
|
||
//| BYTE highlight color
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPExtMove::CPExtMove()
|
||
{
|
||
pStream.ReserveSize( 17 );
|
||
pStream.WriteByte( 0, 0x77 );
|
||
}
|
||
|
||
CPExtMove::CPExtMove( CChar &toCopy )
|
||
{
|
||
pStream.ReserveSize( 17 );
|
||
pStream.WriteByte( 0, 0x77 );
|
||
CopyData( toCopy );
|
||
SetFlags( toCopy );
|
||
}
|
||
|
||
CPExtMove &CPExtMove::operator=( CChar &toCopy )
|
||
{
|
||
CopyData( toCopy );
|
||
return (*this);
|
||
}
|
||
|
||
void CPExtMove::FlagColour( UI08 newValue )
|
||
{
|
||
pStream.WriteByte( 16, newValue );
|
||
}
|
||
|
||
void CPExtMove::SetFlags( CChar &toCopy )
|
||
{
|
||
std::bitset< 8 > flag( 0 );
|
||
|
||
if( cwmWorldState->ServerData()->ClientSupport7000() || cwmWorldState->ServerData()->ClientSupport7090() ||
|
||
cwmWorldState->ServerData()->ClientSupport70160() || cwmWorldState->ServerData()->ClientSupport70240() ||
|
||
cwmWorldState->ServerData()->ClientSupport70300() || cwmWorldState->ServerData()->ClientSupport70331() ||
|
||
cwmWorldState->ServerData()->ClientSupport704565() || cwmWorldState->ServerData()->ClientSupport70610() )
|
||
{
|
||
// Clients 7.0.0.0 and later
|
||
const UI08 BIT_FROZEN = 0; // 0x01, frozen/paralyzed
|
||
const UI08 BIT_FEMALE = 1; // 0x02, female flag
|
||
const UI08 BIT_FLYING = 2; // 0x04, flying (post 7.0.0.0)
|
||
//const UI08 BIT_GOLDEN = 4; // 0x08, yellow healthbar
|
||
//const UI08 BIT_IGNOREMOBILES = 5; // 0x10, ignore other mobiles?
|
||
|
||
flag.set( BIT_FROZEN, toCopy.IsFrozen() );
|
||
flag.set( BIT_FEMALE, ( toCopy.GetID() == 0x0191 || toCopy.GetID() == 0x025E ) || toCopy.GetID() == 0x029B );
|
||
flag.set( BIT_FLYING, ( toCopy.IsFlying() ));
|
||
}
|
||
else
|
||
{
|
||
// Clients earlier than 7.0.0.0
|
||
const UI08 BIT_INVUL = 0; // 0x01
|
||
const UI08 BIT_DEAD = 1; // 0x02
|
||
const UI08 BIT_POISON = 2; // 0x04, poison
|
||
//const UI08 BIT_GOLDEN = 4; // 0x08, yellow healthbar
|
||
//const UI08 BIT_IGNOREMOBILES = 5; // 0x10, ignore other mobiles?
|
||
|
||
flag.set( BIT_INVUL, toCopy.IsInvulnerable() );
|
||
flag.set( BIT_DEAD, toCopy.IsDead() );
|
||
flag.set( BIT_POISON, ( toCopy.GetPoisoned() != 0 ) );
|
||
}
|
||
|
||
const UI08 BIT_ATWAR = 6; // 0x40
|
||
const UI08 BIT_DEAD = 7; // 0x80, dead or hidden
|
||
flag.set( BIT_ATWAR, toCopy.IsAtWar() );
|
||
flag.set( BIT_DEAD, ( toCopy.IsDead() || toCopy.GetVisible() != VT_VISIBLE ) );
|
||
|
||
pStream.WriteByte( 15, static_cast<UI08>( flag.to_ulong() ) );
|
||
}
|
||
|
||
void CPExtMove::CopyData( CChar &toCopy )
|
||
{
|
||
pStream.WriteLong( 1, toCopy.GetSerial() );
|
||
pStream.WriteShort( 5, toCopy.GetID() );
|
||
pStream.WriteShort( 7, toCopy.GetX() );
|
||
pStream.WriteShort( 9, toCopy.GetY() );
|
||
pStream.WriteByte( 11, toCopy.GetZ() );
|
||
|
||
UI08 dir = toCopy.GetDir();
|
||
if( toCopy.GetRunning() )
|
||
dir |= 0x80;
|
||
pStream.WriteByte( 12, dir );
|
||
pStream.WriteShort( 13, toCopy.GetSkin() );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPAttackOK()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet informing client if it's ok or not to attack target
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xAA (OK / Not OK To Attack)
|
||
//| Size: 5 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[4] CharID being attacked
|
||
//| ID is set to 00 00 00 00 when attack is refused.
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPAttackOK::CopyData( CChar &toCopy )
|
||
{
|
||
Serial( toCopy.GetSerial() );
|
||
}
|
||
CPAttackOK::CPAttackOK()
|
||
{
|
||
pStream.ReserveSize( 5 );
|
||
pStream.WriteByte( 0, 0xAA );
|
||
}
|
||
CPAttackOK::CPAttackOK( CChar &toCopy )
|
||
{
|
||
pStream.ReserveSize( 5 );
|
||
pStream.WriteByte( 0, 0xAA );
|
||
CopyData( toCopy );
|
||
}
|
||
void CPAttackOK::Serial( SERIAL newSerial )
|
||
{
|
||
pStream.WriteLong( 1, newSerial );
|
||
}
|
||
CPAttackOK &CPAttackOK::operator=( CChar &toCopy )
|
||
{
|
||
CopyData( toCopy );
|
||
return (*this);
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPRemoveItem()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet informing client of the removal of an object
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x1D (Delete object)
|
||
//| Size: 5 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[4] item/char id
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPRemoveItem::CopyData( CBaseObject &toCopy )
|
||
{
|
||
Serial( toCopy.GetSerial() );
|
||
}
|
||
CPRemoveItem::CPRemoveItem()
|
||
{
|
||
pStream.ReserveSize( 5 );
|
||
pStream.WriteByte( 0, 0x1D );
|
||
}
|
||
CPRemoveItem::CPRemoveItem( CBaseObject &toCopy )
|
||
{
|
||
pStream.ReserveSize( 5 );
|
||
pStream.WriteByte( 0, 0x1D );
|
||
CopyData( toCopy );
|
||
}
|
||
void CPRemoveItem::Serial( SERIAL newSerial )
|
||
{
|
||
pStream.WriteLong( 1, newSerial );
|
||
}
|
||
CPRemoveItem &CPRemoveItem::operator=( CBaseObject &toCopy )
|
||
{
|
||
CopyData( toCopy );
|
||
return (*this);
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPWorldChange()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet telling client to update season visuals of world
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xBC (Seasonal Information)
|
||
//| Size: 3 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE id1
|
||
//| BYTE id2
|
||
//|
|
||
//| Note: Server message
|
||
//| Note: if id2 = 1, then this is a season change. Although, this could also just
|
||
//| be a flag to play a sound FX on season change or not...
|
||
//| Note: if season change, then id1 = (0=spring, 1=summer, 2=fall, 3=winter, 4 = desolation)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPWorldChange::CPWorldChange()
|
||
{
|
||
pStream.ReserveSize( 3 );
|
||
pStream.WriteByte( 0, 0xBC );
|
||
}
|
||
CPWorldChange::CPWorldChange( WorldType newSeason, UI08 newCursor )
|
||
{
|
||
pStream.ReserveSize( 3 );
|
||
pStream.WriteByte( 0, 0xBC );
|
||
Season( newSeason );
|
||
Cursor( newCursor );
|
||
}
|
||
void CPWorldChange::Season( WorldType newSeason )
|
||
{
|
||
pStream.WriteByte( 1, (UI08)newSeason );
|
||
}
|
||
void CPWorldChange::Cursor( UI08 newCursor )
|
||
{
|
||
pStream.WriteByte( 2, newCursor );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPLightLevel()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet updating client with current overall light level
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x4F (Overall Light Level)
|
||
//| Size: 2 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE level
|
||
//| 0x00 - day
|
||
//| 0x09 - OSI night
|
||
//| 0x1F - Black
|
||
//| Max normal val = 0x1F
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPLightLevel::CPLightLevel()
|
||
{
|
||
pStream.ReserveSize( 2 );
|
||
pStream.WriteByte( 0, 0x4F );
|
||
}
|
||
CPLightLevel::CPLightLevel( LIGHTLEVEL level )
|
||
{
|
||
pStream.ReserveSize( 2 );
|
||
pStream.WriteByte( 0, 0x4F );
|
||
Level( level );
|
||
}
|
||
|
||
void CPLightLevel::Level( LIGHTLEVEL level )
|
||
{
|
||
pStream.WriteByte( 1, level );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPUpdIndSkill()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet updating client with skill values and lock states
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x3A (Send Skills)
|
||
//| Size: 13 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE Command
|
||
//| BYTE[2] Length
|
||
//| BYTE Type
|
||
//| Repeat next for each skill
|
||
//| BYTE[2] Skill ID Number
|
||
//| BYTE[2] Skill Value * 10
|
||
//| BYTE[2] Unmodified Value * 10
|
||
//| BYTE Skill Lock
|
||
//| If (Type==2 || Type==0xDF)
|
||
//| BYTE[2] Skill Cap
|
||
//| BYTE[2] Null Terminated(0000) (ONLY IF TYPE == 0x00)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPUpdIndSkill::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 13 );
|
||
pStream.WriteByte( 0, 0x3A );
|
||
pStream.WriteShort( 1, 0x000D ); // Length of message
|
||
pStream.WriteByte( 3, 0xDF); // delta, capped
|
||
}
|
||
void CPUpdIndSkill::CopyData( CChar& i, UI08 sNum )
|
||
{
|
||
SkillNum( sNum );
|
||
Skill( i.GetSkill( sNum ) );
|
||
BaseSkill( i.GetBaseSkill( sNum ) );
|
||
Lock( (SkillLock)i.GetSkillLock( sNum ) );
|
||
Cap( (UI16)cwmWorldState->ServerData()->ServerSkillCapStatus() );
|
||
}
|
||
CPUpdIndSkill::CPUpdIndSkill()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPUpdIndSkill::CPUpdIndSkill( CChar& i, UI08 sNum )
|
||
{
|
||
InternalReset();
|
||
CopyData( i, sNum );
|
||
}
|
||
void CPUpdIndSkill::Character( CChar& i, UI08 sNum )
|
||
{
|
||
CopyData( i, sNum );
|
||
}
|
||
void CPUpdIndSkill::SkillNum( UI08 sNum )
|
||
{
|
||
pStream.WriteShort( 4, sNum );
|
||
}
|
||
void CPUpdIndSkill::Skill( SI16 skillval )
|
||
{
|
||
pStream.WriteShort( 6, skillval );
|
||
}
|
||
void CPUpdIndSkill::BaseSkill( SI16 skillval )
|
||
{
|
||
pStream.WriteShort( 8, skillval );
|
||
}
|
||
void CPUpdIndSkill::Lock( SkillLock lockVal )
|
||
{
|
||
pStream.WriteByte( 10, lockVal );
|
||
}
|
||
void CPUpdIndSkill::Cap( SI16 capVal )
|
||
{
|
||
pStream.WriteShort( 11, capVal );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPBuyItem()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet with list of items bought from vendor
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x3B (Buy Item(s))
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] blockSize
|
||
//| BYTE[4] vendorID
|
||
//| BYTE flag
|
||
//| 0x00 - no items following
|
||
//| 0x02 - items following
|
||
//| For each item
|
||
//| BYTE (0x1A)
|
||
//| BYTE[4] itemID (from 3C packet)
|
||
//| BYTE[2] # bought
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPBuyItem::CopyData( CBaseObject &i )
|
||
{
|
||
Serial( i.GetSerial() );
|
||
}
|
||
CPBuyItem::CPBuyItem()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPBuyItem::CPBuyItem( CBaseObject &i )
|
||
{
|
||
InternalReset();
|
||
CopyData( i );
|
||
}
|
||
CPBuyItem &CPBuyItem::operator=( CBaseObject &toCopy )
|
||
{
|
||
CopyData( toCopy );
|
||
return (*this);
|
||
}
|
||
void CPBuyItem::Serial( SERIAL toSet )
|
||
{
|
||
pStream.WriteLong( 3, toSet );
|
||
}
|
||
|
||
void CPBuyItem::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 8 );
|
||
pStream.WriteByte( 0, 0x3B );
|
||
pStream.WriteShort( 1, 0x0008 );
|
||
pStream.WriteByte( 7, 0x00 );
|
||
}
|
||
|
||
//const UI32 loopbackIP = (127<<24) + 1;
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPRelay()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet relaying details about game server to client
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x8C (Connect to Game Server)
|
||
//| Size: 11 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[4] gameServer IP
|
||
//| BYTE[2] gameServer port
|
||
//| BYTE[4] new key
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPRelay::CPRelay()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPRelay::CPRelay( UI32 newIP )
|
||
{
|
||
InternalReset();
|
||
ServerIP( newIP );
|
||
}
|
||
CPRelay::CPRelay( UI32 newIP, UI16 newPort )
|
||
{
|
||
InternalReset();
|
||
ServerIP( newIP );
|
||
Port( newPort );
|
||
}
|
||
void CPRelay::ServerIP( UI32 newIP )
|
||
{
|
||
pStream.WriteLong( 1, newIP );
|
||
}
|
||
void CPRelay::Port( UI16 newPort )
|
||
{
|
||
pStream.WriteShort( 5, newPort );
|
||
}
|
||
void CPRelay::SeedIP( UI32 newIP )
|
||
{
|
||
pStream.WriteLong( 7, newIP );
|
||
}
|
||
void CPRelay::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 11 );
|
||
pStream.WriteByte( 0, 0x8C );
|
||
SeedIP( 0xFFFFFFFF );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPWornItem()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to equip a single item for player and update paperdoll
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x2E (Worn Item)
|
||
//| Size: 15 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[4] item serial
|
||
//| BYTE[2] model (item hex #)
|
||
//| BYTE (0x00)
|
||
//| BYTE layer
|
||
//| BYTE[4] playerID
|
||
//| BYTE[2] color/hue
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPWornItem::CPWornItem()
|
||
{
|
||
pStream.ReserveSize( 15 );
|
||
pStream.WriteByte( 0, 0x2E );
|
||
pStream.WriteByte( 7, 0x00 );
|
||
}
|
||
void CPWornItem::ItemSerial( SERIAL itemSer )
|
||
{
|
||
pStream.WriteLong( 1, itemSer );
|
||
}
|
||
void CPWornItem::Model( SI16 newModel )
|
||
{
|
||
pStream.WriteShort( 5, newModel );
|
||
}
|
||
void CPWornItem::Layer( UI08 layer )
|
||
{
|
||
pStream.WriteByte( 8, layer );
|
||
}
|
||
void CPWornItem::CharSerial( SERIAL chSer )
|
||
{
|
||
pStream.WriteLong( 9, chSer );
|
||
}
|
||
void CPWornItem::Colour( SI16 newColour )
|
||
{
|
||
pStream.WriteShort( 13, newColour );
|
||
}
|
||
|
||
CPWornItem::CPWornItem( CItem &toCopy )
|
||
{
|
||
pStream.ReserveSize( 15 );
|
||
pStream.WriteByte( 0, 0x2E );
|
||
pStream.WriteByte( 7, 0x00 );
|
||
CopyData( toCopy );
|
||
}
|
||
|
||
void CPWornItem::CopyData( CItem &toCopy )
|
||
{
|
||
ItemSerial( toCopy.GetSerial() );
|
||
Colour( toCopy.GetColour() );
|
||
Model( toCopy.GetID() );
|
||
Layer( toCopy.GetLayer() );
|
||
CharSerial( toCopy.GetContSerial() );
|
||
}
|
||
CPWornItem &CPWornItem::operator=( CItem &toCopy )
|
||
{
|
||
CopyData( toCopy );
|
||
return (*this);
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPCharacterAnimation()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to play specific animation for character
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x6E (Character Animation)
|
||
//| Size: 14 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[4] item/char ID
|
||
//| BYTE[2] movement model
|
||
//| 0x00 = walk
|
||
//| 0x01 = walk faster
|
||
//| 0x02 = run
|
||
//| 0x03 = run (faster?)
|
||
//| 0x04 = nothing
|
||
//| 0x05 = shift shoulders
|
||
//| 0x06 = hands on hips
|
||
//| 0x07 = attack stance (short)
|
||
//| 0x08 = attack stance (longer)
|
||
//| 0x09 = swing (attack with knife)
|
||
//| 0x0a = stab (underhanded)
|
||
//| 0x0b = swing (attack overhand with sword)
|
||
//| 0x0c = swing (attack with sword over and side)
|
||
//| 0x0d = swing (attack with sword side)
|
||
//| 0x0e = stab with point of sword
|
||
//| 0x0f = ready stance
|
||
//| 0x10 = magic (butter churn!)
|
||
//| 0x11 = hands over head (balerina)
|
||
//| 0x12 = bow shot
|
||
//| 0x13 = crossbow
|
||
//| 0x14 = get hit
|
||
//| 0x15 = fall down and die (backwards)
|
||
//| 0x16 = fall down and die (forwards)
|
||
//| 0x17 = ride horse (long)
|
||
//| 0x18 = ride horse (medium)
|
||
//| 0x19 = ride horse (short)
|
||
//| 0x1a = swing sword from horse
|
||
//| 0x1b = normal bow shot on horse
|
||
//| 0x1c = crossbow shot
|
||
//| 0x1d = block #2 on horse with shield
|
||
//| 0x1e = block on ground with shield
|
||
//| 0x1f = swing, and get hit in middle
|
||
//| 0x20 = bow (deep)
|
||
//| 0x21 = salute
|
||
//| 0x22 = scratch head
|
||
//| 0x23 = 1 foot forward for 2 secs
|
||
//| 0x24 = same
|
||
//| BYTE[1] unknown1 (0x00)
|
||
//| BYTE[1] Frame Count
|
||
//| BYTE[2] repeat (1 = once / 2 = twice / 0 = repeat forever)
|
||
//| BYTE forward/backwards(0x00=forward, 0x01=backwards)
|
||
//| BYTE repeat Flag (0 - Don't repeat / 1 repeat)
|
||
//| BYTE frame Delay (0x00 - fastest / 0xFF - Too slow to watch)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPCharacterAnimation::CopyData( CChar &toCopy )
|
||
{
|
||
Serial( toCopy.GetSerial() );
|
||
//Direction( toCopy.GetDir() );
|
||
}
|
||
CPCharacterAnimation::CPCharacterAnimation()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPCharacterAnimation::CPCharacterAnimation( CChar &toCopy )
|
||
{
|
||
InternalReset();
|
||
CopyData( toCopy );
|
||
}
|
||
void CPCharacterAnimation::Serial( SERIAL toSet )
|
||
{
|
||
pStream.WriteLong( 1, toSet );
|
||
}
|
||
void CPCharacterAnimation::Action( UI16 model )
|
||
{
|
||
pStream.WriteShort( 5, model );
|
||
}
|
||
void CPCharacterAnimation::Direction( UI08 dir )
|
||
{
|
||
pStream.WriteByte( 8, dir );
|
||
}
|
||
void CPCharacterAnimation::Repeat( SI16 repeatValue )
|
||
{
|
||
pStream.WriteShort( 9, repeatValue );
|
||
}
|
||
void CPCharacterAnimation::DoBackwards( bool newValue )
|
||
{
|
||
pStream.WriteByte( 11, (UI08)((newValue?1:0)) );
|
||
}
|
||
void CPCharacterAnimation::RepeatFlag( bool newValue )
|
||
{
|
||
pStream.WriteByte( 12, (UI08)((newValue?1:0)) );
|
||
}
|
||
void CPCharacterAnimation::FrameDelay( UI08 delay )
|
||
{
|
||
pStream.WriteByte( 13, delay );
|
||
}
|
||
CPCharacterAnimation &CPCharacterAnimation::operator=( CChar &toCopy )
|
||
{
|
||
CopyData( toCopy );
|
||
return (*this);
|
||
}
|
||
void CPCharacterAnimation::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 14 );
|
||
pStream.WriteByte( 0, 0x6E );
|
||
pStream.WriteByte( 7, 0x00 );
|
||
pStream.WriteByte( 8, 0x07 ); // Animation Length, default to 7
|
||
Repeat( 1 );
|
||
DoBackwards( false );
|
||
RepeatFlag( false );
|
||
FrameDelay( 0 );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPNewCharacterAnimation()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to play specific animation for character in clients 7.0.0.0+
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xE2 (New Character Animation)
|
||
//| Size: 10 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[4] item/char serial
|
||
//| BYTE[2] action type
|
||
//| BYTE[2] sub action
|
||
//| BYTE sub sub action/variation
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPNewCharacterAnimation::CopyData( CChar &toCopy )
|
||
{
|
||
Serial( toCopy.GetSerial() );
|
||
}
|
||
CPNewCharacterAnimation::CPNewCharacterAnimation()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPNewCharacterAnimation::CPNewCharacterAnimation( CChar &toCopy )
|
||
{
|
||
InternalReset();
|
||
CopyData( toCopy );
|
||
}
|
||
void CPNewCharacterAnimation::Serial( SERIAL toSet )
|
||
{
|
||
pStream.WriteLong( 1, toSet );
|
||
}
|
||
void CPNewCharacterAnimation::Action( UI16 action )
|
||
{
|
||
pStream.WriteShort( 5, action );
|
||
}
|
||
void CPNewCharacterAnimation::SubAction( UI16 subAction )
|
||
{
|
||
pStream.WriteShort( 7, subAction );
|
||
}
|
||
void CPNewCharacterAnimation::SubSubAction( UI08 subSubAction )
|
||
{
|
||
pStream.WriteByte( 9, subSubAction );
|
||
}
|
||
CPNewCharacterAnimation &CPNewCharacterAnimation::operator=( CChar &toCopy )
|
||
{
|
||
CopyData( toCopy );
|
||
return (*this);
|
||
}
|
||
void CPNewCharacterAnimation::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 10 );
|
||
pStream.WriteByte( 0, 0xE2 );
|
||
pStream.WriteByte( 7, 0 );
|
||
pStream.WriteByte( 9, 0 );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPDrawGamePlayer()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to update player's looks, flags and location
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x20 (Draw Game Player/Mobile Update/Teleport)
|
||
//| Size: 19 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[4] creature id
|
||
//| BYTE[2] bodyType
|
||
//| BYTE unknown1 (0)
|
||
//| BYTE[2] skin color / hue
|
||
//| BYTE flag byte
|
||
//| BYTE[2] xLoc
|
||
//| BYTE[2] yLoc
|
||
//| BYTE[2] unknown2 (0)
|
||
//| BYTE direction
|
||
//| BYTE zLoc
|
||
//|
|
||
//| Note: Only used with the character being played by the client.
|
||
//| Ancient client version Flags:
|
||
//| 0x01 = Unknown
|
||
//| 0x02 = CanAlterPaperdoll
|
||
//| 0x04 = Posioned
|
||
//| 0x08 = Yellow HealthBar
|
||
//| 0x10 = Unknown
|
||
//| 0x20 = Unknown
|
||
//| 0x40 = War Mode
|
||
//| 0x80 = Hidden
|
||
//| Pre 7.0.0.0 Flags:
|
||
//| 0x01 = Invulnerable
|
||
//| 0x02 = Dead
|
||
//| 0x04 = Posioned
|
||
//| 0x08 = Yellow HealthBar
|
||
//| 0x10 = Ignore Mobiles
|
||
//| 0x40 = War Mode
|
||
//| 0x80 = Hidden )
|
||
//| Post 7.0.0.0 Flags:
|
||
//| 0x01 = Frozen
|
||
//| 0x02 = Female
|
||
//| 0x04 = Flying
|
||
//| 0x08 = Yellow HealthBar
|
||
//| 0x10 = Ignore Mobiles
|
||
//| 0x20 = Movable if normally not
|
||
//| 0x40 = War Mode
|
||
//| 0x80 = Hidden )
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPDrawGamePlayer::CPDrawGamePlayer( CChar &toCopy )
|
||
{
|
||
InternalReset();
|
||
CopyData( toCopy );
|
||
}
|
||
|
||
void CPDrawGamePlayer::CopyData( CChar &toCopy )
|
||
{
|
||
pStream.WriteLong( 1, toCopy.GetSerial() );
|
||
pStream.WriteShort( 5, toCopy.GetID() );
|
||
pStream.WriteShort( 8, toCopy.GetColour() );
|
||
pStream.WriteShort( 11, toCopy.GetX() );
|
||
pStream.WriteShort( 13, toCopy.GetY() );
|
||
pStream.WriteByte( 17, toCopy.GetDir() );
|
||
pStream.WriteByte( 18, toCopy.GetZ() );
|
||
|
||
std::bitset< 8 > flag( 0 );
|
||
|
||
if( cwmWorldState->ServerData()->ClientSupport7000() || cwmWorldState->ServerData()->ClientSupport7090() ||
|
||
cwmWorldState->ServerData()->ClientSupport70160() || cwmWorldState->ServerData()->ClientSupport70240() ||
|
||
cwmWorldState->ServerData()->ClientSupport70300() || cwmWorldState->ServerData()->ClientSupport70331() ||
|
||
cwmWorldState->ServerData()->ClientSupport704565() || cwmWorldState->ServerData()->ClientSupport70610() )
|
||
{
|
||
// Clients 7.0.0.0 and later
|
||
const UI08 BIT_FROZEN = 0; // 0x01, frozen/paralyzed
|
||
const UI08 BIT_FEMALE = 1; // 0x02, should be female flag
|
||
const UI08 BIT_FLYING = 2; // 0x04, flying (post 7.0.0.0)
|
||
//const UI08 BIT_GOLDEN = 4; // 0x08, yellow healthbar
|
||
//const UI08 BIT_IGNOREMOBILES = 5; // 0x10, ignore other mobiles?
|
||
|
||
flag.set( BIT_FROZEN, toCopy.IsFrozen() );
|
||
flag.set( BIT_FEMALE, ( toCopy.GetID() == 0x0191 || toCopy.GetID() == 0x025E ) );
|
||
flag.set( BIT_FLYING, toCopy.IsFlying() );
|
||
}
|
||
else
|
||
{
|
||
// Clients below 7.0.0.0
|
||
const UI08 BIT_INVUL = 0; // 0x01
|
||
const UI08 BIT_DEAD = 1; // 0x02
|
||
const UI08 BIT_POISON = 2; // 0x04, poison
|
||
//const UI08 BIT_GOLDEN = 4; // 0x08, yellow healthbar
|
||
//const UI08 BIT_IGNOREMOBILES = 5; // 0x10, ignore other mobiles?
|
||
|
||
flag.set( BIT_INVUL, toCopy.IsInvulnerable() );
|
||
flag.set( BIT_DEAD, toCopy.IsDead() );
|
||
flag.set( BIT_POISON, ( toCopy.GetPoisoned() != 0 ) );
|
||
}
|
||
|
||
const UI08 BIT_ATWAR = 6; // 0x40
|
||
const UI08 BIT_INVIS = 7; // 0x80
|
||
|
||
flag.set( BIT_ATWAR, toCopy.IsAtWar() );
|
||
flag.set( BIT_INVIS, (toCopy.GetVisible() != VT_VISIBLE) || toCopy.IsDead() );
|
||
|
||
pStream.WriteByte( 10, static_cast< UI08 >(flag.to_ulong()) );
|
||
}
|
||
void CPDrawGamePlayer::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 19 );
|
||
pStream.WriteByte( 0, 0x20 );
|
||
pStream.WriteByte( 7, 0x00 );
|
||
pStream.WriteShort( 15, 0x0000 );
|
||
}
|
||
CPDrawGamePlayer::CPDrawGamePlayer()
|
||
{
|
||
InternalReset();
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPPersonalLightLevel()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to update players personal light level
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x4E (Personal Light Level)
|
||
//| Size: 6 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[4] creature id
|
||
//| BYTE level
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPPersonalLightLevel::CopyData( CChar &toCopy )
|
||
{
|
||
Serial( toCopy.GetSerial() );
|
||
}
|
||
void CPPersonalLightLevel::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 6 );
|
||
pStream.WriteByte( 0, 0x4E );
|
||
}
|
||
CPPersonalLightLevel::CPPersonalLightLevel()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPPersonalLightLevel::CPPersonalLightLevel( CChar &toCopy )
|
||
{
|
||
InternalReset();
|
||
CopyData( toCopy );
|
||
}
|
||
void CPPersonalLightLevel::Serial( SERIAL toSet )
|
||
{
|
||
pStream.WriteLong( 1, toSet );
|
||
}
|
||
void CPPersonalLightLevel::Level( LIGHTLEVEL lightLevel )
|
||
{
|
||
pStream.WriteByte( 5, lightLevel );
|
||
}
|
||
|
||
CPPersonalLightLevel &CPPersonalLightLevel::operator=( CChar &toCopy )
|
||
{
|
||
CopyData( toCopy );
|
||
return (*this);
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPPlaySoundEffect()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to play sounds effect in player's client
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x54 (Play Sound Effect)
|
||
//| Size: 12 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE mode (0x00=quiet, repeating, 0x01=single normally played sound effect)
|
||
//| BYTE[2] SoundModel
|
||
//| BYTE[2] unknown3 (speed/volume modifier? Line of sight stuff?)
|
||
//| BYTE[2] xLoc
|
||
//| BYTE[2] yLoc
|
||
//| BYTE[2] zLoc / Or not z at all? Could be related to sequences when sent after packet 0x7B
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPPlaySoundEffect::CPPlaySoundEffect()
|
||
{
|
||
InternalReset();
|
||
}
|
||
|
||
void CPPlaySoundEffect::Mode( UI08 mode )
|
||
{
|
||
pStream.WriteByte( 1, mode );
|
||
}
|
||
void CPPlaySoundEffect::Model( UI16 newModel )
|
||
{
|
||
pStream.WriteShort( 2, newModel );
|
||
}
|
||
void CPPlaySoundEffect::X( SI16 xLoc )
|
||
{
|
||
pStream.WriteShort( 6, xLoc );
|
||
}
|
||
void CPPlaySoundEffect::Y( SI16 yLoc )
|
||
{
|
||
pStream.WriteShort( 8, yLoc );
|
||
}
|
||
void CPPlaySoundEffect::Z( SI16 zLoc )
|
||
{
|
||
pStream.WriteShort( 10, zLoc );
|
||
}
|
||
void CPPlaySoundEffect::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 12 );
|
||
pStream.WriteByte( 0, 0x54 );
|
||
pStream.WriteByte( 1, 1 );
|
||
pStream.WriteShort( 4, 0x00 );
|
||
}
|
||
CPPlaySoundEffect &CPPlaySoundEffect::operator=( CBaseObject &toCopy )
|
||
{
|
||
CopyData( toCopy );
|
||
return (*this);
|
||
}
|
||
|
||
void CPPlaySoundEffect::CopyData( CBaseObject &toCopy )
|
||
{
|
||
X( toCopy.GetX() );
|
||
Y( toCopy.GetY() );
|
||
Z( toCopy.GetZ() );
|
||
}
|
||
|
||
CPPlaySoundEffect::CPPlaySoundEffect( CBaseObject &toCopy )
|
||
{
|
||
InternalReset();
|
||
CopyData( toCopy );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPPaperdoll()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to open player's paperdoll
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x88 (Open Paperdoll)
|
||
//| Size: 66 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[4] charid
|
||
//| BYTE[60] text
|
||
//| BYTE flag byte
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPPaperdoll::CopyData( CChar &toCopy )
|
||
{
|
||
Serial( toCopy.GetSerial() );
|
||
}
|
||
void CPPaperdoll::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 66 );
|
||
pStream.WriteByte( 0, 0x88 );
|
||
}
|
||
CPPaperdoll::CPPaperdoll()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPPaperdoll::CPPaperdoll( CChar &toCopy )
|
||
{
|
||
InternalReset();
|
||
CopyData( toCopy );
|
||
}
|
||
void CPPaperdoll::Serial( SERIAL tSerial )
|
||
{
|
||
pStream.WriteLong( 1, tSerial );
|
||
}
|
||
void CPPaperdoll::FlagByte( UI08 fVal )
|
||
{
|
||
pStream.WriteByte( 65, fVal );
|
||
}
|
||
void CPPaperdoll::Text( const std::string& toPut )
|
||
{
|
||
if( toPut.length() > 60 )
|
||
{
|
||
pStream.WriteString( 5, toPut, 59 );
|
||
pStream.WriteByte( 64, 0x00 );
|
||
}
|
||
else
|
||
pStream.WriteString( 5, toPut, toPut.size() );
|
||
}
|
||
CPPaperdoll &CPPaperdoll::operator=( CChar &toCopy )
|
||
{
|
||
CopyData( toCopy );
|
||
return (*this);
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPWeather()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to update weather in player's client
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x65 (Set Weather)
|
||
//| Size: 4 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE type (0x00 – “It starts to rain”, 0x01 – “A fierce storm approaches.”, 0x02 – “It begins to snow”, 0x03 - “A storm is brewing.”, 0xFF – None (turns off sound effects), 0xFE (no effect?? Set temperature?)
|
||
//| BYTE num (number of weather effects on screen)
|
||
//| BYTE temperature
|
||
//|
|
||
//| Note: Temperature has no effect at present.
|
||
//| Note: Maximum number of weather effects on screen is 70.
|
||
//| Note: If it is raining, you can add snow by setting the num to the num of rain
|
||
//| currently going, plus the number of snow you want.
|
||
//| Note: Weather messages are only displayed when weather starts.
|
||
//| Note: Weather will end automatically after 6 minutes without any weather change packets.
|
||
//| Note: You can totally end weather (to display a new message) by teleporting.
|
||
//| I think it’s either the 0x78 or 0x20 messages that reset it, though I
|
||
//| haven’t checked to be sure (other possibilities, 0x4F or 0x4E)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPWeather::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 4 );
|
||
pStream.WriteByte( 0, 0x65 );
|
||
pStream.WriteByte( 1, 0xFF );
|
||
pStream.WriteByte( 2, 0x40 );
|
||
pStream.WriteByte( 3, 0x10 );
|
||
}
|
||
CPWeather::CPWeather()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPWeather::CPWeather( UI08 nType )
|
||
{
|
||
InternalReset();
|
||
Type( nType );
|
||
}
|
||
CPWeather::CPWeather( UI08 nType, UI08 nParts )
|
||
{
|
||
InternalReset();
|
||
Type( nType );
|
||
Particles( nParts );
|
||
}
|
||
CPWeather::CPWeather( UI08 nType, UI08 nParts, UI08 nTemp )
|
||
{
|
||
InternalReset();
|
||
Type( nType );
|
||
Particles( nParts );
|
||
Temperature( nTemp );
|
||
}
|
||
void CPWeather::Type( UI08 nType )
|
||
{
|
||
pStream.WriteByte( 1, nType );
|
||
}
|
||
void CPWeather::Particles( UI08 nParts )
|
||
{
|
||
if( nParts > 70 )
|
||
nParts = 70;
|
||
if( nParts < 0 )
|
||
nParts = 0;
|
||
|
||
pStream.WriteByte( 2, nParts );
|
||
}
|
||
void CPWeather::Temperature( UI08 nTemp )
|
||
{
|
||
pStream.WriteByte( 3, nTemp );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPGraphicalEffect()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to play a visual effect in player's client
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x70 (Graphical Effect)
|
||
//| Size: 28 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE direction type
|
||
//| 00 = go from source to dest
|
||
//| 01 = lightning strike at source
|
||
//| 02 = stay at current x,y,z
|
||
//| 03 = stay with current source character id
|
||
//| BYTE[4] character id
|
||
//| BYTE[4] target id
|
||
//| BYTE[2] model of the first frame of the effect
|
||
//| BYTE[2] xLoc
|
||
//| BYTE[2] yLoc
|
||
//| BYTE zLoc
|
||
//| BYTE[2] xLoc of target
|
||
//| BYTE[2] yLoc of target
|
||
//| BYTE zLoc of target
|
||
//| BYTE speed of the animation
|
||
//| BYTE duration (0=really long, 1= shortest)
|
||
//| BYTE[2] unknown2 (0 works)
|
||
//| BYTE adjust direction during animation (1=no)
|
||
//| BYTE explode on impact
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPGraphicalEffect::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 28 );
|
||
pStream.WriteByte( 0, 0x70 );
|
||
pStream.WriteShort( 24, 0x0000 );
|
||
}
|
||
CPGraphicalEffect::CPGraphicalEffect( UI08 effectType )
|
||
{
|
||
InternalReset();
|
||
Effect( effectType );
|
||
}
|
||
void CPGraphicalEffect::Effect( UI08 effectType )
|
||
{
|
||
pStream.WriteByte( 1, effectType );
|
||
}
|
||
void CPGraphicalEffect::SourceSerial( CBaseObject &toSet )
|
||
{
|
||
SourceSerial( toSet.GetSerial() );
|
||
}
|
||
void CPGraphicalEffect::SourceSerial( SERIAL toSet )
|
||
{
|
||
pStream.WriteLong( 2, toSet );
|
||
}
|
||
void CPGraphicalEffect::TargetSerial( CBaseObject &toSet )
|
||
{
|
||
TargetSerial( toSet.GetSerial() );
|
||
}
|
||
void CPGraphicalEffect::TargetSerial( SERIAL toSet )
|
||
{
|
||
pStream.WriteLong( 6, toSet );
|
||
}
|
||
void CPGraphicalEffect::Model( SI16 nModel )
|
||
{
|
||
pStream.WriteShort( 10, nModel );
|
||
}
|
||
void CPGraphicalEffect::X( SI16 nX )
|
||
{
|
||
pStream.WriteShort( 12, nX );
|
||
}
|
||
void CPGraphicalEffect::Y( SI16 nY )
|
||
{
|
||
pStream.WriteShort( 14, nY );
|
||
}
|
||
void CPGraphicalEffect::Z( SI08 nZ )
|
||
{
|
||
pStream.WriteByte( 16, nZ );
|
||
}
|
||
void CPGraphicalEffect::XTrg( SI16 nX )
|
||
{
|
||
pStream.WriteShort( 17, nX );
|
||
}
|
||
void CPGraphicalEffect::YTrg( SI16 nY )
|
||
{
|
||
pStream.WriteShort( 19, nY );
|
||
}
|
||
void CPGraphicalEffect::ZTrg( SI08 nZ )
|
||
{
|
||
pStream.WriteByte( 21, nZ );
|
||
}
|
||
void CPGraphicalEffect::Speed( UI08 nSpeed )
|
||
{
|
||
pStream.WriteByte( 22, nSpeed );
|
||
}
|
||
void CPGraphicalEffect::Duration( UI08 nDuration )
|
||
{
|
||
pStream.WriteByte( 23, nDuration );
|
||
}
|
||
void CPGraphicalEffect::AdjustDir( bool nValue )
|
||
{
|
||
pStream.WriteByte( 26, (UI08)((nValue?0:1)) );
|
||
}
|
||
void CPGraphicalEffect::ExplodeOnImpact( bool nValue )
|
||
{
|
||
pStream.WriteByte( 27, (UI08)((nValue?1:0)) );
|
||
}
|
||
|
||
CPGraphicalEffect::CPGraphicalEffect( UI08 effectType, CBaseObject &src, CBaseObject &trg )
|
||
{
|
||
InternalReset();
|
||
Effect( effectType );
|
||
SourceSerial( src );
|
||
TargetSerial( trg );
|
||
}
|
||
CPGraphicalEffect::CPGraphicalEffect( UI08 effectType, CBaseObject &src )
|
||
{
|
||
InternalReset();
|
||
Effect( effectType );
|
||
SourceSerial( src );
|
||
}
|
||
|
||
void CPGraphicalEffect::SourceLocation( CBaseObject& toSet )
|
||
{
|
||
SourceLocation( toSet.GetX(), toSet.GetY(), toSet.GetZ() );
|
||
}
|
||
void CPGraphicalEffect::TargetLocation( CBaseObject& toSet )
|
||
{
|
||
TargetLocation( toSet.GetX(), toSet.GetY(), toSet.GetZ() );
|
||
}
|
||
|
||
void CPGraphicalEffect::SourceLocation( SI16 x, SI16 y, SI08 z )
|
||
{
|
||
X( x );
|
||
Y( y );
|
||
Z( z );
|
||
}
|
||
void CPGraphicalEffect::TargetLocation( SI16 x, SI16 y, SI08 z )
|
||
{
|
||
XTrg( x );
|
||
YTrg( y );
|
||
ZTrg( z );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPUpdateStat()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet(s) to update player's health, mana and stamina
|
||
//| Also used to update the health of items/multis with damageable flag set
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xA1 (Update Current Health)
|
||
//| Packet: 0xA2 (Update Current Mana)
|
||
//| Packet: 0xA3 (Update Current Stamina)
|
||
//| Size: 9 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[4] playerID
|
||
//| BYTE[2] maxHealth/maxMana/maxStamina
|
||
//| BYTE[2] currentHealth/currentMana/currentStamina
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPUpdateStat::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 9 );
|
||
pStream.WriteByte( 0, 0xA1 );
|
||
}
|
||
CPUpdateStat::CPUpdateStat( CBaseObject &toUpdate, UI08 statNum )
|
||
{
|
||
InternalReset();
|
||
Serial( toUpdate.GetSerial() );
|
||
auto maxHP = 0;
|
||
auto maxStam = 0;
|
||
auto maxMana = 0;
|
||
if( toUpdate.CanBeObjType( OT_CHAR ))
|
||
{
|
||
// For characters,
|
||
auto objChar = calcCharObjFromSer(toUpdate.GetSerial());
|
||
maxHP = objChar->GetMaxHP();
|
||
maxStam = objChar->GetMaxStam();
|
||
maxMana = objChar->GetMaxMana();
|
||
}
|
||
else
|
||
{
|
||
// For items and multis, only health is relevant, as not only does
|
||
// client not display anything relevant for stamina/mana for items,
|
||
// but items also have no mana/stamina values to send!
|
||
if( toUpdate.CanBeObjType(OT_MULTI) )
|
||
{
|
||
auto objMulti = calcMultiFromSer(toUpdate.GetSerial());
|
||
maxHP = objMulti->GetMaxHP();
|
||
}
|
||
else
|
||
{
|
||
auto objItem = calcItemObjFromSer(toUpdate.GetSerial());
|
||
maxHP = objItem->GetMaxHP();
|
||
}
|
||
}
|
||
|
||
switch( statNum )
|
||
{
|
||
case 0: MaxVal( maxHP );
|
||
CurVal( toUpdate.GetHP() );
|
||
break;
|
||
case 2: MaxVal( maxStam );
|
||
CurVal( toUpdate.GetStamina() );
|
||
break;
|
||
case 1: MaxVal( maxMana );
|
||
CurVal( toUpdate.GetMana() );
|
||
break;
|
||
}
|
||
|
||
pStream.WriteByte( 0, (pStream.GetByte( 0 ) + statNum) );
|
||
}
|
||
void CPUpdateStat::Serial( SERIAL toSet )
|
||
{
|
||
pStream.WriteLong( 1, toSet );
|
||
}
|
||
void CPUpdateStat::MaxVal( SI16 maxVal )
|
||
{
|
||
pStream.WriteShort( 5, maxVal );
|
||
}
|
||
void CPUpdateStat::CurVal( SI16 curVal )
|
||
{
|
||
pStream.WriteShort( 7, curVal );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPDeathAction()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to have client display player's death action
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xAF (Display Death Action)
|
||
//| Size: 13 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[4] player id
|
||
//| BYTE[4] corpse id
|
||
//| BYTE[4] unknown (all 0)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPDeathAction::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 13 );
|
||
pStream.WriteByte( 0, 0xAF );
|
||
pStream.WriteByte( 9, 0x00 );
|
||
pStream.WriteByte( 10, 0x00 );
|
||
pStream.WriteByte( 11, 0x00 );
|
||
}
|
||
CPDeathAction::CPDeathAction( CChar &dying, CItem &corpse )
|
||
{
|
||
InternalReset();
|
||
Player( dying.GetSerial() );
|
||
Corpse( corpse.GetSerial() );
|
||
}
|
||
CPDeathAction::CPDeathAction()
|
||
{
|
||
InternalReset();
|
||
}
|
||
void CPDeathAction::Player( SERIAL toSet )
|
||
{
|
||
pStream.WriteLong( 1, toSet );
|
||
}
|
||
void CPDeathAction::Corpse( SERIAL toSet )
|
||
{
|
||
pStream.WriteLong( 5, toSet );
|
||
}
|
||
void CPDeathAction::FallDirection( UI08 toFall )
|
||
{
|
||
pStream.WriteByte( 12, toFall );
|
||
}
|
||
CPDeathAction &CPDeathAction::operator=( CChar &dying )
|
||
{
|
||
Player( dying.GetSerial() );
|
||
return (*this);
|
||
}
|
||
CPDeathAction &CPDeathAction::operator=( CItem &corpse )
|
||
{
|
||
Corpse( corpse.GetSerial() );
|
||
return (*this);
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPPlayMusic()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to have client play specified music
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x6D (Play Midi Music)
|
||
//| Size: 3 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] musicID
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPPlayMusic::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 3 );
|
||
pStream.WriteByte( 0, 0x6D );
|
||
}
|
||
CPPlayMusic::CPPlayMusic( SI16 musicID )
|
||
{
|
||
InternalReset();
|
||
MusicID( musicID );
|
||
}
|
||
CPPlayMusic::CPPlayMusic()
|
||
{
|
||
InternalReset();
|
||
}
|
||
void CPPlayMusic::MusicID( SI16 musicID )
|
||
{
|
||
pStream.WriteShort( 1, musicID );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPDrawContainer()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to display a particular container gump
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x24 (Draw Container)
|
||
//| Size: 3 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[4] item id
|
||
//| BYTE[2] model-Gump
|
||
//| 0x003c = backpack
|
||
//| BYTE[2] container type // added in UO:HS clients, 0x00 for vendors, 0x7D
|
||
//| for spellbooks and containers
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPDrawContainer::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 7 );
|
||
pStream.WriteByte( 0, 0x24 );
|
||
}
|
||
CPDrawContainer::CPDrawContainer()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPDrawContainer::CPDrawContainer( CItem &toCopy )
|
||
{
|
||
InternalReset();
|
||
CopyData( toCopy );
|
||
}
|
||
void CPDrawContainer::Model( UI16 newModel )
|
||
{
|
||
pStream.WriteShort( 5, newModel ); //-1?
|
||
}
|
||
void CPDrawContainer::ContType( UI16 contType )
|
||
{
|
||
pStream.ReserveSize( 9 );
|
||
pStream.WriteShort( 7, contType );
|
||
}
|
||
CPDrawContainer &CPDrawContainer::operator=( CItem &toCopy )
|
||
{
|
||
CopyData( toCopy );
|
||
return (*this);
|
||
}
|
||
void CPDrawContainer::CopyData( CItem &toCopy )
|
||
{
|
||
Serial( toCopy.GetSerial() );
|
||
}
|
||
void CPDrawContainer::Serial( SERIAL toSet )
|
||
{
|
||
pStream.WriteLong( 1, toSet );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPOpenGump()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to open a dialog box/item list menu
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x7C (Open Dialog Box/Item List)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] blockSize
|
||
//| BYTE[4] dialogID (echo'd back to the server in 7d)
|
||
//| BYTE[2] menuid (echo'd back to server in 7d)
|
||
//| BYTE length of question
|
||
//| BYTE[length of question] question text
|
||
//| BYTE # of responses
|
||
//| Then for each response:
|
||
//| BYTE[2] model id # of shown item (if grey menu -- then always 0x00 as msb)
|
||
//| BYTE[2] color of shown item
|
||
//| BYTE response text length
|
||
//| BYTE[response text length] response text
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPOpenGump::Log( std::ofstream &outStream, bool fullHeader )
|
||
{
|
||
if( fullHeader )
|
||
outStream << "[SEND]Packet : CPOpenGump 0x7C --> Length: " << pStream.GetSize() << TimeStamp() << std::endl;
|
||
outStream << "DialogID : " << std::hex << pStream.GetULong( 3 ) << std::endl;
|
||
outStream << "MenuID : " << pStream.GetUShort( 7 ) << std::endl;
|
||
outStream << "Question Length : " << std::dec << (SI16)pStream.GetByte( 9 ) << std::endl;
|
||
outStream << "Question : ";
|
||
|
||
for( UI32 i = 0; i < pStream.GetByte( 9 ); ++i )
|
||
outStream << pStream.GetByte( 10+static_cast<size_t>(i) );
|
||
outStream << std::endl;
|
||
|
||
outStream << "# Responses : " << pStream.GetByte( responseBaseOffset ) << std::endl;
|
||
size_t offsetCount = responseBaseOffset + 1;
|
||
for( UI32 j = 0; j < pStream.GetByte( responseBaseOffset ); ++j )
|
||
{
|
||
outStream << " Response " << j << std::endl;
|
||
outStream << " Model : " << std::hex << pStream.GetUShort( offsetCount + 0 ) << std::endl;
|
||
outStream << " Colour : " << pStream.GetUShort( offsetCount + 2 ) << std::endl;
|
||
outStream << " Length : " << std::dec << pStream.GetByte( offsetCount + 4 ) << std::endl;
|
||
outStream << " Text : ";
|
||
|
||
for( UI32 k = 0; k < pStream.GetByte( offsetCount + 4 ); ++k )
|
||
outStream << pStream.GetByte( offsetCount + 5 + k );
|
||
outStream << std::endl;
|
||
offsetCount += ( 5 + static_cast<size_t>(pStream.GetByte( static_cast<size_t>(offsetCount) + 4 )) );
|
||
}
|
||
outStream << " Raw dump :" << std::endl;
|
||
CPUOXBuffer::Log( outStream, false );
|
||
}
|
||
void CPOpenGump::Question( std::string toAdd )
|
||
{
|
||
pStream.ReserveSize( 10 + toAdd.length() + 2 ); // 10 for start of string, length of string + nullptr, plus spot for # responses
|
||
pStream.WriteString( 10, toAdd, toAdd.length() );
|
||
#if defined( UOX_DEBUG_MODE )
|
||
if( toAdd.length() >= 255 )
|
||
Console.error( strutil::format("CPOpenGump::Question toAdd.length() is too long (%i)", toAdd.length()) );
|
||
#endif
|
||
pStream.WriteByte( 9, static_cast< UI08 >(toAdd.length() + 1) );
|
||
responseBaseOffset = (pStream.GetSize() - 1);
|
||
responseOffset = responseBaseOffset + 1;
|
||
}
|
||
void CPOpenGump::AddResponse( UI16 modelNum, UI16 colour, std::string responseText )
|
||
{
|
||
pStream.WriteByte( responseBaseOffset, pStream.GetByte( responseBaseOffset ) + 1 ); // increment number of responses
|
||
#if defined( UOX_DEBUG_MODE )
|
||
if( responseText.length() >= 255 )
|
||
Console.error( strutil::format("CPOpenGump::AddResponse responseText is too long (%i)", responseText.length()) );
|
||
#endif
|
||
UI16 toAdd = static_cast< UI16 >(5 + responseText.length());
|
||
pStream.ReserveSize( pStream.GetSize() + toAdd );
|
||
pStream.WriteShort( (responseOffset + 0), modelNum );
|
||
pStream.WriteShort( (responseOffset + 2), colour );
|
||
pStream.WriteByte( (responseOffset + 4), static_cast< UI08 >(responseText.length()) );
|
||
pStream.WriteString( (responseOffset + 5), responseText, responseText.length() );
|
||
responseOffset += toAdd;
|
||
}
|
||
void CPOpenGump::Finalize( void )
|
||
{
|
||
pStream.WriteShort( 1, static_cast< UI16 >(pStream.GetSize()) );
|
||
}
|
||
void CPOpenGump::InternalReset( void )
|
||
{
|
||
responseOffset = 0xFFFFFFFF;
|
||
responseBaseOffset = 0xFFFFFFFF;
|
||
pStream.ReserveSize( 9 );
|
||
pStream.WriteByte( 0, 0x7C );
|
||
pStream.WriteShort( 1, 0x0000 );
|
||
pStream.WriteByte( 3, 0x01 );
|
||
pStream.WriteByte( 4, 0x02 );
|
||
pStream.WriteByte( 5, 0x03 );
|
||
pStream.WriteByte( 6, 0x04 );
|
||
pStream.WriteShort( 7, 0x0064 );
|
||
}
|
||
CPOpenGump::CPOpenGump()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPOpenGump::CPOpenGump( CChar &toCopy )
|
||
{
|
||
InternalReset();
|
||
CopyData( toCopy );
|
||
}
|
||
void CPOpenGump::Length( SI32 totalLines )
|
||
{
|
||
pStream.WriteShort( 1, totalLines );
|
||
}
|
||
void CPOpenGump::GumpIndex( SI32 index )
|
||
{
|
||
pStream.WriteShort( 7, index );
|
||
}
|
||
CPOpenGump &CPOpenGump::operator=( CChar &toCopy )
|
||
{
|
||
CopyData( toCopy );
|
||
return (*this);
|
||
}
|
||
void CPOpenGump::CopyData( CChar &toCopy )
|
||
{
|
||
Serial( toCopy.GetSerial() );
|
||
}
|
||
void CPOpenGump::Serial( SERIAL toSet )
|
||
{
|
||
pStream.WriteLong( 3, toSet );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPTargetCursor()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to activate targeting cursor in client
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x6C (Targeting Cursor Commands)
|
||
//| Size: 19 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE type
|
||
//| 0x00 = Select Object
|
||
//| 0x01 = Select X, Y, Z
|
||
//| BYTE[4] cursorID
|
||
//| BYTE Cursor Type
|
||
//| Always 0 now
|
||
//| The following are always sent but are only valid if sent by client
|
||
//| BYTE[4] Clicked On ID
|
||
//| BYTE[2] click xLoc
|
||
//| BYTE[2] click yLoc
|
||
//| BYTE unknown (0x00)
|
||
//| BYTE click zLoc
|
||
//| BYTE[2] model # (if a static tile, 0 if a map/landscape tile)
|
||
//| Note: the model # shouldn’t be trusted.
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPTargetCursor::CPTargetCursor()
|
||
{
|
||
pStream.ReserveSize( 19 );
|
||
pStream.WriteByte( 0, 0x6C );
|
||
CursorType( 0 );
|
||
}
|
||
void CPTargetCursor::Type( UI08 nType )
|
||
{
|
||
pStream.WriteByte( 1, nType );
|
||
}
|
||
void CPTargetCursor::ID( SERIAL toSet )
|
||
{
|
||
pStream.WriteLong( 2, toSet );
|
||
}
|
||
void CPTargetCursor::CursorType( UI08 nType )
|
||
{
|
||
pStream.WriteByte( 6, nType );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPStatWindow()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to update player's stat window
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x11 (Stat Window Info )
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] packet length
|
||
//| BYTE[4] player id
|
||
//| BYTE[30] playerName
|
||
//| BYTE[2] currentHitpoints
|
||
//| BYTE[2] maxHitpoints
|
||
//| BYTE name change flag (0x1 = allowed, 0 = not allowed)
|
||
//| BYTE Status Flag/Supported Features
|
||
//| Status Flag
|
||
//| 0x00: no more data following (end of packet here).
|
||
//| 0x01: T2A Extended Info
|
||
//| 0x03: UOR Extended Info
|
||
//| 0x04: AOS Extended Info (4.0+)
|
||
//| 0x05: UOML Extended Info (5.0+)
|
||
//| 0x06: UOKR Extended Info (UOKR+)
|
||
//| BYTE sex + race
|
||
//| 0: Male Human
|
||
//| 1: Female Human
|
||
//| 2: Male Elf
|
||
//| 3: Female Elf
|
||
//| BYTE[2] str
|
||
//| BYTE[2] dex
|
||
//| BYTE[2] int
|
||
//| BYTE[2] currentStamina
|
||
//| BYTE[2] maxStamina
|
||
//| BYTE[2] currentMana
|
||
//| BYTE[2] maxMana
|
||
//| BYTE[4] gold
|
||
//| BYTE[2] armor class
|
||
//| BYTE[2] weight
|
||
//| If (flag 5 or higher - ML attributes 5.0+)
|
||
//| BYTE[2] Max Weight
|
||
//| BYTE Race (see notes)
|
||
//| UOML+ Race Flag
|
||
//| 1: Human
|
||
//| 2: Elf
|
||
//| 3: Gargoyle
|
||
//| If (flag == 3) (UO:R attributes)
|
||
//| BYTE[2] statcap
|
||
//| BYTE pets current
|
||
//| BYTE pets max
|
||
//| If (flag == 4 - AoS attributes 4.0+)
|
||
//| BYTE[2] fireresist
|
||
//| BYTE[2] coldresist
|
||
//| BYTE[2] poisonresist
|
||
//| BYTE[2] energyresist
|
||
//| BYTE[2] luck
|
||
//| BYTE[2] damage max
|
||
//| BYTE[2] damage min
|
||
//| BYTE[4] Tithing points (Paladin Books)
|
||
//| If (flag 6 or higher - KR attributes)
|
||
//| BYTE[2] Hit Chance Increase
|
||
//| BYTE[2] Swing Speed Increase
|
||
//| BYTE[2] Damage Chance Increase
|
||
//| BYTE[2] Lower Reagent Cost
|
||
//| BYTE[2] Hit Points Regeneration
|
||
//| BYTE[2] Stamina Regeneration
|
||
//| BYTE[2] Mana Regeneration
|
||
//| BYTE[2] Reflect Physical Damage
|
||
//| BYTE[2] Enhance Potions
|
||
//| BYTE[2] Defense Chance Increase
|
||
//| BYTE[2] Spell Damage Increase
|
||
//| BYTE[2] Faster Cast Recovery
|
||
//| BYTE[2] Faster Casting
|
||
//| BYTE[2] Lower Mana Cost
|
||
//| BYTE[2] Strength Increase
|
||
//| BYTE[2] Dexterity Increase
|
||
//| BYTE[2] Intelligence Increase
|
||
//| BYTE[2] Hit Points Increase
|
||
//| BYTE[2] Stamina Increase
|
||
//| BYTE[2] Mana Increase
|
||
//| BYTE[2] Maximum Hit Points Increase
|
||
//| BYTE[2] Maximum Stamina Increase
|
||
//| BYTE[2] Maximum Mana Increase
|
||
//|
|
||
//| Note: For characters other than the player, currentHitpoints and maxHitpoints
|
||
//| are not the actual values. MaxHitpoints is a fixed value, and currentHitpoints
|
||
//| works like a percentage.
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPStatWindow::SetCharacter( CChar &toCopy, CSocket &target )
|
||
{
|
||
CChar *mChar = target.CurrcharObj();
|
||
if( toCopy.GetSerial() == mChar->GetSerial() )
|
||
{
|
||
if( target.ReceivedVersion() )
|
||
{
|
||
/*if( target.ClientVersionMajor() >= 6 )
|
||
{
|
||
// Extended stats not implemented yet
|
||
extended3 = true;
|
||
extended4 = true;
|
||
extended5 = true;
|
||
extended6 = true;
|
||
pStream.ReserveSize( 121 );
|
||
pStream.WriteByte( 2, 121 );
|
||
Flag( 6 );
|
||
}*/
|
||
if( target.ClientVersionMajor() >= 5 )
|
||
{
|
||
extended3 = true;
|
||
extended4 = true;
|
||
extended5 = true;
|
||
pStream.ReserveSize( 91 );
|
||
pStream.WriteByte( 2, 91 );
|
||
Flag( 5 );
|
||
}
|
||
else if( target.ClientVersionMajor() >= 4 )
|
||
{
|
||
extended3 = true;
|
||
extended4 = true;
|
||
pStream.ReserveSize( 88 );
|
||
pStream.WriteByte( 2, 88 );
|
||
Flag( 4 );
|
||
}
|
||
else if( target.ClientVersionMajor() >= 3 )
|
||
{
|
||
extended3 = true;
|
||
pStream.ReserveSize( 70 );
|
||
pStream.WriteByte( 2, 70 );
|
||
Flag( 3 );
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// We haven't received any client details yet.. let's use default server settings
|
||
|
||
/*if( cwmWorldState->ServerData()->GetClientFeature( CF_BIT_HS ) )
|
||
{
|
||
// Extended stats not implemented yet
|
||
extended3 = true;
|
||
extended4 = true;
|
||
extended5 = true;
|
||
extended6 = true;
|
||
pStream.ReserveSize( 121 );
|
||
pStream.WriteByte( 2, 121 );
|
||
Flag( 6 );
|
||
}*/
|
||
if( cwmWorldState->ServerData()->GetClientFeature( CF_BIT_ML ) )
|
||
{
|
||
extended3 = true;
|
||
extended4 = true;
|
||
extended5 = true;
|
||
pStream.ReserveSize( 91 );
|
||
pStream.WriteByte( 2, 91 );
|
||
Flag( 5 );
|
||
}
|
||
else if( cwmWorldState->ServerData()->GetClientFeature( CF_BIT_AOS ) )
|
||
{
|
||
extended3 = true;
|
||
extended4 = true;
|
||
pStream.ReserveSize( 88 );
|
||
pStream.WriteByte( 2, 88 );
|
||
Flag( 4 );
|
||
}
|
||
else if( cwmWorldState->ServerData()->GetClientFeature( CF_BIT_UOR ) )
|
||
{
|
||
extended3 = true;
|
||
pStream.ReserveSize( 70 );
|
||
pStream.WriteByte( 2, 70 );
|
||
Flag( 3 );
|
||
}
|
||
else
|
||
{
|
||
Flag( 1 );
|
||
}
|
||
}
|
||
}
|
||
|
||
// If toCopy is an NPC or another player, only show basic stats
|
||
if( toCopy.GetSerial() != mChar->GetSerial() )
|
||
{
|
||
pStream.ReserveSize( 43 );
|
||
pStream.WriteByte( 2, 43 );
|
||
Flag( 0 );
|
||
Serial( toCopy.GetSerial() );
|
||
Name( toCopy.GetName() );
|
||
SI16 currentHP = toCopy.GetHP();
|
||
UI16 maxHP = toCopy.GetMaxHP();
|
||
SI16 percentHP = 0;
|
||
if( currentHP > 0 )
|
||
{
|
||
percentHP = static_cast<SI16>( ceil(100 * ( static_cast<float>( currentHP ) / static_cast<float>( maxHP ) )) );
|
||
}
|
||
CurrentHP( percentHP );
|
||
MaxHP( 100 );
|
||
NameChange( false );
|
||
}
|
||
else
|
||
{
|
||
// Send player their own full stats
|
||
Serial( toCopy.GetSerial() );
|
||
Name( toCopy.GetName() );
|
||
|
||
bool isDead = toCopy.IsDead();
|
||
if( isDead )
|
||
{
|
||
CurrentHP( 0 );
|
||
MaxHP( 0 );
|
||
}
|
||
else
|
||
{
|
||
CurrentHP( toCopy.GetHP() );
|
||
MaxHP( toCopy.GetMaxHP() );
|
||
}
|
||
NameChange( false );
|
||
if( toCopy.GetID() == 0x0190 || toCopy.GetOrgID() == 0x0190 ) // Male huamn
|
||
Sex( 0 );
|
||
else if( toCopy.GetID() == 0x0191 || toCopy.GetOrgID() == 0x0191 ) // Female human
|
||
Sex( 1 );
|
||
else if( toCopy.GetID() == 0x025D || toCopy.GetOrgID() == 0x025D ) // Male elf
|
||
Sex( 2 );
|
||
else if( toCopy.GetID() == 0x025E || toCopy.GetOrgID() == 0x025E ) // Female elf
|
||
Sex( 3 );
|
||
if( isDead )
|
||
{
|
||
Strength( 0 );
|
||
Dexterity( 0 );
|
||
Intelligence( 0 );
|
||
Stamina( 0 );
|
||
MaxStamina( 0 );
|
||
Mana( 0 );
|
||
MaxMana( 0 );
|
||
}
|
||
else
|
||
{
|
||
Strength( toCopy.GetStrength() );
|
||
Dexterity( toCopy.GetDexterity() );
|
||
Intelligence( toCopy.GetIntelligence() );
|
||
Stamina( toCopy.GetStamina() );
|
||
MaxStamina( toCopy.GetMaxStam() );
|
||
Mana( toCopy.GetMana() );
|
||
MaxMana( toCopy.GetMaxMana() );
|
||
}
|
||
Weight( static_cast<UI16>(toCopy.GetWeight() / 100) );
|
||
if( extended5 )
|
||
{
|
||
MaxWeight( toCopy.GetStrength() * cwmWorldState->ServerData()->WeightPerStr() + 40 );
|
||
UI16 bodyID = toCopy.GetID();
|
||
switch( bodyID )
|
||
{
|
||
case 0x0190:
|
||
case 0x0191:
|
||
case 0x0192:
|
||
case 0x0193:
|
||
Race( 0x01 ); break; //human
|
||
case 0x025D:
|
||
case 0x025E:
|
||
case 0x025F:
|
||
case 0x0260:
|
||
Race( 0x02 ); break; //elf
|
||
case 0x029A:
|
||
case 0x029B:
|
||
case 0x02B6:
|
||
case 0x02B7:
|
||
Race( 0x03 ); break; //gargoyle
|
||
default:
|
||
Race( 0x01 ); break;
|
||
}
|
||
}
|
||
if( extended3 )
|
||
{
|
||
StatCap( cwmWorldState->ServerData()->ServerStatCapStatus() );
|
||
CurrentPets( static_cast<UI08>(toCopy.GetPetList()->Num()) );
|
||
MaxPets( 0xFF );
|
||
}
|
||
if( extended4 )
|
||
{
|
||
FireResist( Combat->calcDef( &toCopy, 0, false, HEAT ) );
|
||
ColdResist( Combat->calcDef( &toCopy, 0, false, COLD ) );
|
||
PoisonResist( Combat->calcDef( &toCopy, 0, false, POISON ) );
|
||
EnergyResist( Combat->calcDef( &toCopy, 0, false, LIGHTNING ) );
|
||
Luck( 0 );
|
||
DamageMin( Combat->calcLowDamage( &toCopy ) );
|
||
DamageMax( Combat->calcHighDamage( &toCopy ) );
|
||
TithingPoints( 0 );
|
||
}
|
||
}
|
||
}
|
||
void CPStatWindow::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 43 );
|
||
pStream.WriteByte( 0, 0x11 );
|
||
pStream.WriteByte( 1, 0x00 );
|
||
pStream.WriteByte( 2, 43 );
|
||
extended3 = false;
|
||
extended4 = false;
|
||
extended5 = false;
|
||
extended6 = false;
|
||
byteOffset = 0;
|
||
Flag( 0 );
|
||
}
|
||
CPStatWindow::CPStatWindow()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPStatWindow::CPStatWindow( CBaseObject &toCopy, CSocket &target )
|
||
{
|
||
InternalReset();
|
||
if( toCopy.CanBeObjType(OT_CHAR) )
|
||
{
|
||
auto charObj = calcCharObjFromSer(toCopy.GetSerial());
|
||
SetCharacter( *charObj, target );
|
||
}
|
||
else
|
||
{
|
||
// Item! Let's send the compact version
|
||
auto itemObj = calcItemObjFromSer( toCopy.GetSerial() );
|
||
pStream.ReserveSize(43);
|
||
pStream.WriteByte(2, 43);
|
||
Serial(itemObj->GetSerial());
|
||
Name(itemObj->GetName());
|
||
SI16 currentHP = itemObj->GetHP();
|
||
UI16 maxHP = itemObj->GetMaxHP();
|
||
SI16 percentHP = 0;
|
||
if( currentHP > 0 )
|
||
{
|
||
percentHP = static_cast<SI16>( ceil(100 * ( static_cast<float>( currentHP ) / static_cast<float>( maxHP ) )) );
|
||
}
|
||
CurrentHP( percentHP );
|
||
MaxHP(100);
|
||
NameChange(false);
|
||
Flag(0);
|
||
}
|
||
}
|
||
void CPStatWindow::Serial( SERIAL toSet )
|
||
{
|
||
pStream.WriteLong( 3, toSet );
|
||
}
|
||
void CPStatWindow::Name( const std::string& nName )
|
||
{
|
||
if( nName.length() >= 30 )
|
||
{
|
||
pStream.WriteString( 7, nName, 29 );
|
||
pStream.WriteByte( 36, 0x00 );
|
||
}
|
||
else
|
||
pStream.WriteString( 7, nName, nName.size() );
|
||
}
|
||
void CPStatWindow::CurrentHP( SI16 nValue )
|
||
{
|
||
pStream.WriteShort( 37, nValue );
|
||
}
|
||
void CPStatWindow::MaxHP( SI16 nValue )
|
||
{
|
||
pStream.WriteShort( 39, nValue );
|
||
}
|
||
void CPStatWindow::NameChange( bool nValue )
|
||
{
|
||
pStream.WriteByte( 41, (UI08)((nValue?0xFF:0)) );
|
||
}
|
||
void CPStatWindow::Flag( UI08 nValue )
|
||
{
|
||
pStream.WriteByte( 42, nValue );
|
||
}
|
||
void CPStatWindow::Sex( UI08 nValue )
|
||
{
|
||
pStream.WriteByte( 43, nValue );
|
||
}
|
||
void CPStatWindow::Strength( SI16 nValue )
|
||
{
|
||
pStream.WriteShort( 44, nValue );
|
||
}
|
||
void CPStatWindow::Dexterity( SI16 nValue )
|
||
{
|
||
pStream.WriteShort( 46, nValue );
|
||
}
|
||
void CPStatWindow::Intelligence( SI16 nValue )
|
||
{
|
||
pStream.WriteShort( 48, nValue );
|
||
}
|
||
void CPStatWindow::Stamina( SI16 nValue )
|
||
{
|
||
pStream.WriteShort( 50, nValue );
|
||
}
|
||
void CPStatWindow::MaxStamina( SI16 nValue )
|
||
{
|
||
pStream.WriteShort( 52, nValue );
|
||
}
|
||
void CPStatWindow::Mana( SI16 nValue )
|
||
{
|
||
pStream.WriteShort( 54, nValue );
|
||
}
|
||
void CPStatWindow::MaxMana( SI16 nValue )
|
||
{
|
||
pStream.WriteShort( 56, nValue );
|
||
}
|
||
void CPStatWindow::Gold( UI32 gValue )
|
||
{
|
||
pStream.WriteLong( 58, gValue );
|
||
}
|
||
void CPStatWindow::AC( UI16 nValue )
|
||
{
|
||
pStream.WriteShort( 62, nValue );
|
||
}
|
||
void CPStatWindow::Weight( UI16 nValue )
|
||
{
|
||
pStream.WriteShort( 64, nValue );
|
||
byteOffset = 66;
|
||
}
|
||
//extended5
|
||
void CPStatWindow::MaxWeight( UI16 value )
|
||
{
|
||
pStream.WriteShort( byteOffset, value );
|
||
byteOffset += 2;
|
||
}
|
||
void CPStatWindow::Race( UI08 value )
|
||
{
|
||
pStream.WriteByte( byteOffset, value );
|
||
byteOffset += 1;
|
||
}
|
||
//extended5 end
|
||
//extended3 start
|
||
void CPStatWindow::StatCap( UI16 value )
|
||
{
|
||
pStream.WriteShort( byteOffset, value );
|
||
byteOffset += 2;
|
||
}
|
||
void CPStatWindow::CurrentPets( UI08 value )
|
||
{
|
||
pStream.WriteByte( byteOffset, value );
|
||
byteOffset += 1;
|
||
}
|
||
void CPStatWindow::MaxPets( UI08 value )
|
||
{
|
||
pStream.WriteByte( byteOffset, value );
|
||
byteOffset += 1;
|
||
}
|
||
//extended3 end
|
||
//extended4 start
|
||
void CPStatWindow::FireResist( UI16 value )
|
||
{
|
||
pStream.WriteShort( byteOffset, value );
|
||
byteOffset += 2;
|
||
}
|
||
void CPStatWindow::ColdResist( UI16 value )
|
||
{
|
||
pStream.WriteShort( byteOffset, value );
|
||
byteOffset += 2;
|
||
}
|
||
void CPStatWindow::PoisonResist( UI16 value )
|
||
{
|
||
pStream.WriteShort( byteOffset, value );
|
||
byteOffset += 2;
|
||
}
|
||
void CPStatWindow::EnergyResist( UI16 value )
|
||
{
|
||
pStream.WriteShort( byteOffset, value );
|
||
byteOffset += 2;
|
||
}
|
||
void CPStatWindow::Luck( UI16 value )
|
||
{
|
||
pStream.WriteShort( byteOffset, value );
|
||
byteOffset += 2;
|
||
}
|
||
void CPStatWindow::DamageMin( UI16 value )
|
||
{
|
||
pStream.WriteShort( byteOffset, value );
|
||
byteOffset += 2;
|
||
}
|
||
void CPStatWindow::DamageMax( UI16 value )
|
||
{
|
||
pStream.WriteShort( byteOffset, value );
|
||
byteOffset += 2;
|
||
}
|
||
void CPStatWindow::TithingPoints( UI32 value )
|
||
{
|
||
pStream.WriteLong( byteOffset, value );
|
||
byteOffset += 4;
|
||
}
|
||
//extended4 end
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPIdleWarning()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to show idle warning or login rejection message
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x53 (Idle Warning/Reject Character Logon)
|
||
//| Size: 2 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE value
|
||
//| 0x07 idle
|
||
//| 0x05 another character is online
|
||
//| "Another character from this account is currently online in this world.
|
||
//| You must either log in as that character or wait for it to time out.”
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPIdleWarning::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 2 );
|
||
pStream.WriteByte( 0, 0x53 );
|
||
}
|
||
CPIdleWarning::CPIdleWarning()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPIdleWarning::CPIdleWarning( UI08 errorNum )
|
||
{
|
||
InternalReset();
|
||
Error( errorNum );
|
||
}
|
||
void CPIdleWarning::Error( UI08 errorNum )
|
||
{
|
||
pStream.WriteByte( 1, errorNum );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPTime()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to update time in client
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x5B (Time)
|
||
//| Size: 4 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE hour
|
||
//| BYTE minute
|
||
//| BYTE second
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPTime::CPTime()
|
||
{
|
||
InternalReset();
|
||
}
|
||
void CPTime::Hour( UI08 hour )
|
||
{
|
||
pStream.WriteByte( 1, hour );
|
||
}
|
||
void CPTime::Minute( UI08 minute )
|
||
{
|
||
pStream.WriteByte( 2, minute );
|
||
}
|
||
void CPTime::Second( UI08 second )
|
||
{
|
||
pStream.WriteByte( 3, second );
|
||
}
|
||
void CPTime::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 4 );
|
||
pStream.WriteByte( 0, 0x5B );
|
||
}
|
||
CPTime::CPTime( UI08 hour, UI08 minute, UI08 second )
|
||
{
|
||
InternalReset();
|
||
Hour( hour );
|
||
Minute( minute );
|
||
Second( second );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPLoginComplete()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to verify login completion
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x55 (Login Complete, Start Game)
|
||
//| Size: 1 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPLoginComplete::CPLoginComplete()
|
||
{
|
||
pStream.ReserveSize( 1 );
|
||
pStream.WriteByte( 0, 0x55 );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPTextEmoteColour()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to set text/emote color
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x69 (Change Text/Emote Color)
|
||
//| Size: 5 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] blockSize
|
||
//| BYTE[2] unknown1
|
||
//| The client sends two of these independent of the color chosen. It sends two of
|
||
//| them in quick succession as part of the "same" packet. The unknown1 is
|
||
//| 0x00 0x01 in the first and 0x00 0x02 in the second.
|
||
//| Note, this message has been removed. It is no longer used.
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPTextEmoteColour::CPTextEmoteColour()
|
||
{
|
||
BlockSize( 5 );
|
||
}
|
||
void CPTextEmoteColour::BlockSize( SI16 newValue )
|
||
{
|
||
pStream.ReserveSize( newValue );
|
||
pStream.WriteByte( 0, 0x69 );
|
||
pStream.WriteShort( 1, newValue );
|
||
}
|
||
void CPTextEmoteColour::Unknown( SI16 newValue )
|
||
{
|
||
pStream.WriteShort( 3, newValue );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPWarMode()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to respond to request for war mode/set war mode status
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x72 (Request War Mode Change/Send War Mode status)
|
||
//| Size: 5 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE flag
|
||
//| 0x00 - Normal
|
||
//| 0x01 - Fighting
|
||
//| BYTE[3] unknown1 (always 00 32 00 in testing)
|
||
//|
|
||
//| Server also replies with 0x77 packet to update player after changing war mode
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPWarMode::CPWarMode()
|
||
{
|
||
InternalReset();
|
||
}
|
||
|
||
void CPWarMode::Flag( UI08 nFlag )
|
||
{
|
||
pStream.WriteByte( 1, nFlag );
|
||
}
|
||
void CPWarMode::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 5 );
|
||
pStream.WriteByte( 0, 0x72 );
|
||
pStream.WriteByte( 2, 0x00 );
|
||
pStream.WriteByte( 3, 0x32 );
|
||
pStream.WriteByte( 4, 0x00 );
|
||
}
|
||
CPWarMode::CPWarMode( UI08 nFlag )
|
||
{
|
||
InternalReset();
|
||
Flag( nFlag );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPPauseResume()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to pause/resume client
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x33 (Pause/Resume Client)
|
||
//| Size: 2 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE pause/resume (0=pause, 1=resume)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPPauseResume::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 2 );
|
||
pStream.WriteByte( 0, 0x33 );
|
||
}
|
||
CPPauseResume::CPPauseResume()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPPauseResume::CPPauseResume( UI08 mode )
|
||
{
|
||
InternalReset();
|
||
Mode( mode );
|
||
}
|
||
void CPPauseResume::Mode( UI08 mode )
|
||
{
|
||
pStream.WriteByte( 1, mode );
|
||
}
|
||
|
||
bool CPPauseResume::ClientCanReceive( CSocket *mSock )
|
||
{
|
||
if( mSock == nullptr )
|
||
return false;
|
||
switch( mSock->ClientType() )
|
||
{
|
||
case CV_DEFAULT:
|
||
case CV_T2A:
|
||
case CV_UO3D:
|
||
case CV_ML:
|
||
case CV_KR2D:
|
||
case CV_KR3D:
|
||
case CV_SA2D:
|
||
case CV_SA3D:
|
||
case CV_HS2D:
|
||
case CV_HS3D:
|
||
if( mSock->ClientVersionMajor() >= 4 )
|
||
return false;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPWebLaunch()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to open a specific URL in player's default browser
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xA5 (Open Web Browser)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] blockSize
|
||
//| BYTE[blockSize-3] null terminated full web address
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPWebLaunch::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 4 );
|
||
pStream.WriteByte( 0, 0xA5 );
|
||
}
|
||
CPWebLaunch::CPWebLaunch()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPWebLaunch::CPWebLaunch( const std::string& txt )
|
||
{
|
||
InternalReset();
|
||
Text( txt );
|
||
}
|
||
void CPWebLaunch::Text( const std::string& txt )
|
||
{
|
||
const SI16 tLen = (SI16)txt.length() + 4;
|
||
SetSize( tLen );
|
||
pStream.WriteString( 3, txt, txt.size() );
|
||
}
|
||
|
||
void CPWebLaunch::SetSize( SI16 newSize )
|
||
{
|
||
pStream.ReserveSize( newSize );
|
||
pStream.WriteShort( 1, newSize );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPTrackingArrow()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to show quest tracking arrow in client
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xBA (Quest Arrow)
|
||
//| Size: 6 or 10 bytes (HSA expansion)
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE active (1=on, 0=off)
|
||
//| BYTE[2] xLoc
|
||
//| BYTE[2] yLoc
|
||
//| if HSA client
|
||
//| BYTE[4] serial
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPTrackingArrow::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 6 );
|
||
pStream.WriteByte( 0, 0xBA );
|
||
}
|
||
CPTrackingArrow::CPTrackingArrow()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPTrackingArrow::CPTrackingArrow( SI16 x, SI16 y )
|
||
{
|
||
InternalReset();
|
||
Location( x, y );
|
||
}
|
||
void CPTrackingArrow::Location( SI16 x, SI16 y )
|
||
{
|
||
pStream.WriteShort( 2, x );
|
||
pStream.WriteShort( 4, y );
|
||
}
|
||
CPTrackingArrow &CPTrackingArrow::operator=( CBaseObject &toCopy )
|
||
{
|
||
Location( toCopy.GetX(), toCopy.GetY() );
|
||
return (*this);
|
||
}
|
||
void CPTrackingArrow::Active( UI08 value )
|
||
{
|
||
pStream.WriteByte( 1, value );
|
||
}
|
||
void CPTrackingArrow::AddSerial( SERIAL targetSerial )
|
||
{
|
||
pStream.ReserveSize( 10 );
|
||
pStream.WriteLong( 6, targetSerial );
|
||
}
|
||
CPTrackingArrow::CPTrackingArrow( CBaseObject &toCopy )
|
||
{
|
||
InternalReset();
|
||
Location( toCopy.GetX(), toCopy.GetY() );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPBounce()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to reject client request to move item
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x27 (Reject Request to Move Items)
|
||
//| Size: 2 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE unknown1 (0x00)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPBounce::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 2 );
|
||
pStream.WriteByte( 0, 0x27 );
|
||
}
|
||
CPBounce::CPBounce()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPBounce::CPBounce( UI08 mode )
|
||
{
|
||
InternalReset();
|
||
Mode( mode );
|
||
}
|
||
void CPBounce::Mode( UI08 mode )
|
||
{
|
||
pStream.WriteByte( 1, mode );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPDyeVat()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to resond to client request to show dye window
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x95 (Dye Window)
|
||
//| Size: 2 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[4] itemID of dye target
|
||
//| BYTE[2] ignored on send, model on return
|
||
//| BYTE[2] model on send, color on return (default on server send is 0x0FAB)
|
||
//|
|
||
//| NOTE: This packet is sent by both the server and client
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPDyeVat::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 9 );
|
||
pStream.WriteByte( 0, 0x95 );
|
||
}
|
||
void CPDyeVat::CopyData( CBaseObject &target )
|
||
{
|
||
Serial( target.GetSerial() );
|
||
Model( target.GetID() );
|
||
}
|
||
CPDyeVat::CPDyeVat()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPDyeVat::CPDyeVat( CBaseObject &target )
|
||
{
|
||
InternalReset();
|
||
CopyData( target );
|
||
}
|
||
void CPDyeVat::Serial( SERIAL toSet )
|
||
{
|
||
pStream.WriteLong( 1, toSet );
|
||
}
|
||
void CPDyeVat::Model( SI16 toSet )
|
||
{
|
||
pStream.WriteShort( 7, toSet );
|
||
}
|
||
CPDyeVat &CPDyeVat::operator=( CBaseObject &target )
|
||
{
|
||
CopyData( target );
|
||
return (*this);
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPMultiPlacementView()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to bring up house/boat placement preview
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x99 (Bring Up House/Boat Placement View)
|
||
//| Size: 26 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE request (0x01 from server, 0x00 from client)
|
||
//| BYTE[4] ID of deed
|
||
//| BYTE[12] unknown (all 0)
|
||
//| BYTE[2] multi model (item model - 0x4000)
|
||
//| BYTE[6] unknown (all 0)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPMultiPlacementView::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 26 );
|
||
pStream.WriteByte( 0, 0x99 );
|
||
for( UI08 i = 6; i < 18; ++i )
|
||
pStream.WriteByte( i, 0 );
|
||
for( UI08 k = 20; k < 26; ++k )
|
||
pStream.WriteByte( k, 0 );
|
||
}
|
||
void CPMultiPlacementView::CopyData( CItem &target )
|
||
{
|
||
DeedSerial( target.GetSerial() );
|
||
}
|
||
CPMultiPlacementView::CPMultiPlacementView()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPMultiPlacementView::CPMultiPlacementView( CItem &target )
|
||
{
|
||
InternalReset();
|
||
CopyData( target );
|
||
}
|
||
void CPMultiPlacementView::RequestType( UI08 rType )
|
||
{
|
||
pStream.WriteByte( 1, rType );
|
||
}
|
||
void CPMultiPlacementView::DeedSerial( SERIAL toSet )
|
||
{
|
||
pStream.WriteLong( 2, toSet );
|
||
}
|
||
void CPMultiPlacementView::MultiModel( SI16 toSet )
|
||
{
|
||
pStream.WriteShort( 18, toSet );
|
||
}
|
||
void CPMultiPlacementView::SetHue( UI16 hueValue )
|
||
{
|
||
pStream.ReserveSize( 30 );
|
||
pStream.WriteLong( 26, hueValue );
|
||
}
|
||
CPMultiPlacementView &CPMultiPlacementView::operator=( CItem &target )
|
||
{
|
||
CopyData( target );
|
||
return (*this);
|
||
}
|
||
|
||
CPMultiPlacementView::CPMultiPlacementView( SERIAL toSet )
|
||
{
|
||
InternalReset();
|
||
DeedSerial( toSet );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPEnableClientFeatures()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to enable client features
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xB9 (Enable locked client features)
|
||
//| Size: 3 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| Original Legacy Client Version
|
||
//| BYTE cmd
|
||
//| BYTE[2] Feature Bitflag
|
||
//| From Legacy Client 6.0.14.2+
|
||
//| BYTE cmd
|
||
//| BYTE[4] Feature Bitflag
|
||
//|
|
||
//| Bitflags:
|
||
//| 0x00 None
|
||
//| 0x01 Enable T2A features: chat-button, regions
|
||
//| 0x02 Enable UOR features
|
||
//| 0x04 Enable TD features
|
||
//| 0x08 Enable LBR features: skills, maps, mp3s, LBR monsters in 2D client
|
||
//| 0x10 Enable AOS update (Necromancer/Paladin skills, malas map/AOS monsters if AOS installation present)
|
||
//| 0x20 Enable Sixth Character Slot
|
||
//| 0x40 Enable SE features: Ninja/Samurai, spells, skills, map, housing tiles
|
||
//| 0x80 Enable ML features: elven race, spells, skills, housing tiles
|
||
//| 0x100 Enable the Eight Age splash screen
|
||
//| 0x200 Enable the Ninth Age splash screen and crystal/shadow housing tiles
|
||
//| 0x400 Enable the Tenth Age
|
||
//| 0x800 Enable increased housing and bank-storage
|
||
//| 0x1000 Enable 7th character slot
|
||
//| 0x2000 Enable KR faces
|
||
//| 0x4000 Enable Trial Account
|
||
//| 0x8000 Live (non-trial) Account. Since client 4.0 this bit has to be set, otherwise bits 3..14 are ignored.
|
||
//| 0x10000 Enable SA features: gargoyle race, spells, skills, housing tiles
|
||
//| 0x20000 Enable HS features
|
||
//| 0x40000 Enable Gothing housing tiles
|
||
//| 0x80000 Enable Rustic housing tiles
|
||
//|
|
||
//| Example
|
||
//| 0 neither T2A NOR LBR, equal to not sending it at all,
|
||
//| 1 is T2A, chatbutton,
|
||
//| 2 is LBR without chatbutton,
|
||
//| 3 is LBR with chatbutton…
|
||
//| 8013 LBR + chatbutton + AOS enabled
|
||
//|
|
||
//| Note1: this message is send immediately after login.
|
||
//| Note2: on OSI servers this controls features OSI enables/disables via “upgrade codes.”
|
||
//| Note3: a 3 doesn’t seem to “hurt” older (NON LBR) clients.
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPEnableClientFeatures::CPEnableClientFeatures( CSocket *mSock )
|
||
{
|
||
if( mSock->ClientType() <= CV_KR3D )
|
||
{
|
||
//Clients 6.0.14.1 and lower
|
||
pStream.ReserveSize( 3 );
|
||
pStream.WriteByte( 0, 0xB9 );
|
||
pStream.WriteShort( 1, cwmWorldState->ServerData()->GetClientFeatures() );
|
||
}
|
||
if( mSock->ClientType() >= CV_SA2D )
|
||
{
|
||
//Clients 6.0.14.2 and higher
|
||
pStream.ReserveSize( 5 );
|
||
pStream.WriteByte( 0, 0xB9 );
|
||
pStream.WriteLong( 1, cwmWorldState->ServerData()->GetClientFeatures() );
|
||
}
|
||
}
|
||
|
||
void CPEnableClientFeatures::Log( std::ofstream &outStream, bool fullHeader )
|
||
{
|
||
if( fullHeader )
|
||
outStream << "[SEND]Packet : CPEnableClientFeatures 0xB9 --> Length: 3" << TimeStamp() << std::endl;
|
||
|
||
UI32 lastByte = pStream.GetUShort( 1 );
|
||
outStream << "Flags : " << std::hex << (UI32)lastByte << std::dec << std::endl;
|
||
if( (lastByte&0x01) == 0x01 )
|
||
outStream << " : Enabled T2A features (including chat)" << std::endl;
|
||
if( (lastByte&0x02) == 0x02 )
|
||
outStream << " : Enabled UO:R support" << std::endl;
|
||
if( (lastByte&0x04) == 0x04 )
|
||
outStream << " : Enabled UO:TD support" << std::endl;
|
||
if( (lastByte&0x08) == 0x08 )
|
||
outStream << " : Enabled LBR support" << std::endl;
|
||
if( (lastByte&0x10) == 0x10 )
|
||
outStream << " : Enabled AOS support" << std::endl;
|
||
if( (lastByte&0x20) == 0x20 )
|
||
outStream << " : Enabled Sixth Character Slot?" << std::endl;
|
||
if( (lastByte&0x40) == 0x40 )
|
||
outStream << " : Enabled Samurai Empire?" << std::endl;
|
||
if( (lastByte&0x80) == 0x80 )
|
||
outStream << " : Enabled Elves and ML" << std::endl;
|
||
if( (lastByte&0x100) == 0x100 )
|
||
outStream << " : Enabled Eight Age splash-screen" << std::endl;
|
||
if( (lastByte&0x200) == 0x200 )
|
||
outStream << " : Enabled Ninth Age splash-screen" << std::endl;
|
||
if( (lastByte&0x400) == 0x400 )
|
||
outStream << " : Enabled Tenth Age splash-screen" << std::endl;
|
||
if( (lastByte&0x800) == 0x800 )
|
||
outStream << " : Enabled increased housing and bank-storage" << std::endl;
|
||
if( (lastByte&0x1000) == 0x1000 )
|
||
outStream << " : Enabled 7th character slot" << std::endl;
|
||
if( (lastByte&0x2000) == 0x2000 )
|
||
outStream << " : Enabled KR faces" << std::endl;
|
||
if( (lastByte&0x4000) == 0x4000 )
|
||
outStream << " : Enabled Trial Account" << std::endl;
|
||
if( (lastByte&0x8000) == 0x8000 )
|
||
outStream << " : Enabled Live (non-trial) account" << std::endl;
|
||
if( (lastByte&0x10000) == 0x10000 )
|
||
outStream << " : Enable SA features" << std::endl;
|
||
if( (lastByte&0x20000) == 0x20000 )
|
||
outStream << " : Enabled HS features" << std::endl;
|
||
if( (lastByte&0x40000) == 0x40000 )
|
||
outStream << " : Enabled Gothing housing tiles" << std::endl;
|
||
if( (lastByte&0x80000) == 0x80000 )
|
||
outStream << " : Enabled Rustic housing tiles" << std::endl;
|
||
if( ( lastByte & 0x100000 ) == 0x100000 )
|
||
outStream << " : Enabled Jungle housing tiles" << std::endl;
|
||
if( ( lastByte & 0x200000 ) == 0x200000 )
|
||
outStream << " : Enabled Shadowguard housing tiles" << std::endl;
|
||
if( ( lastByte & 0x400000 ) == 0x400000 )
|
||
outStream << " : Enabled ToL features" << std::endl;
|
||
if( ( lastByte & 0x800000 ) == 0x800000 )
|
||
outStream << " : Enabled Endless Journey account" << std::endl;
|
||
|
||
outStream << " Raw dump :" << std::endl;
|
||
CPUOXBuffer::Log( outStream, false );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPNegotiateAssistantFeatures()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet with feature negotiation request for assistant tools
|
||
//| like Razor and AssistUO
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xF0 (Negotiate Assistant Features)
|
||
//| Size: 12 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] length
|
||
//| BYTE 0xFE
|
||
//| BYTE[8] Feature bits from enum
|
||
//|
|
||
//| At the same time as this is sent in pcmanage.cpp, a timer is started if
|
||
//| KICKONASSISTANTSILENCE is enabled in uox.ini. If no valid response is received
|
||
//| within 30 seconds, player is disconnected from the server.
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPNegotiateAssistantFeatures::CPNegotiateAssistantFeatures( CSocket *mSock )
|
||
{
|
||
pStream.ReserveSize( 12 );
|
||
pStream.WriteByte( 0, 0xF0 );
|
||
pStream.WriteShort( 1, 0x000C );
|
||
pStream.WriteByte( 3, 0xFE );
|
||
pStream.WriteLong( 4, cwmWorldState->ServerData()->GetDisabledAssistantFeatures() >> 32 );
|
||
pStream.WriteLong( 8, cwmWorldState->ServerData()->GetDisabledAssistantFeatures() & 0xFFFFFFFF );
|
||
}
|
||
|
||
void CPNegotiateAssistantFeatures::Log( std::ofstream &outStream, bool fullHeader )
|
||
{
|
||
if( fullHeader )
|
||
outStream << "[SEND]Packet : CPNegotiateAssistantFeatures 0xF0 --> Length: " << pStream.GetSize() << TimeStamp() << std::endl;
|
||
outStream << " Raw dump :" << std::endl;
|
||
CPUOXBuffer::Log( outStream, false );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPAddItemToCont()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to add/show item to container
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x25 (Add Item to Container)
|
||
//| Size: 20/21 bytes
|
||
//|
|
||
//| Packet Build (Before 6.0.1.7, 2D client)
|
||
//| BYTE cmd
|
||
//| BYTE[4] serial if item to add
|
||
//| BYTE[2] model ID
|
||
//| BYTE model ID offset (0)
|
||
//| BYTE[2] # of items in stack
|
||
//| BYTE[2] xLoc in container
|
||
//| BYTE[2] yLoc in container
|
||
//| BYTE[4] serial of container
|
||
//| BYTE[2] color
|
||
//|
|
||
//| Packet Build (After 6.0.1.7, UOKR+ and 2D client)
|
||
//| BYTE cmd
|
||
//| BYTE[4] Item Serial
|
||
//| BYTE[2] Item ID (Graphic)
|
||
//| BYTE Item ID Offset (wtf?)
|
||
//| BYTE[2] Stack Amount
|
||
//| BYTE[2] X
|
||
//| BYTE[2] Y
|
||
//| BYTE Slot Index (see notes)
|
||
//| BYTE[4] Container Serial
|
||
//| BYTE[2] Color
|
||
//|
|
||
//| NOTE: The backpack grid index exists since 6.0.1.7 2D and 2.45.5.6 KR.
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPAddItemToCont::InternalReset( void )
|
||
{
|
||
uokrFlag = false;
|
||
pStream.ReserveSize( 20 );
|
||
pStream.WriteByte( 0, 0x25 );
|
||
pStream.WriteByte( 7, 0 );
|
||
}
|
||
void CPAddItemToCont::CopyData( CItem &toCopy )
|
||
{
|
||
Serial( toCopy.GetSerial() );
|
||
Model( toCopy.GetID() );
|
||
NumItems( toCopy.GetAmount() );
|
||
X( toCopy.GetX() );
|
||
Y( toCopy.GetY() );
|
||
if( uokrFlag )
|
||
GridLocation( toCopy.GetGridLocation() );
|
||
Container( toCopy.GetContSerial() );
|
||
Colour( toCopy.GetColour() );
|
||
}
|
||
CPAddItemToCont::CPAddItemToCont()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPAddItemToCont::CPAddItemToCont( CItem &toAdd )
|
||
{
|
||
InternalReset();
|
||
CopyData( toAdd );
|
||
}
|
||
void CPAddItemToCont::UOKRFlag( bool newVal )
|
||
{
|
||
uokrFlag = newVal;
|
||
pStream.ReserveSize( (uokrFlag ? 21 : 20) );
|
||
}
|
||
void CPAddItemToCont::Serial( SERIAL toSet )
|
||
{
|
||
pStream.WriteLong( 1, toSet );
|
||
}
|
||
void CPAddItemToCont::Model( SI16 toSet )
|
||
{
|
||
pStream.WriteShort( 5, toSet );
|
||
}
|
||
void CPAddItemToCont::NumItems( SI16 toSet )
|
||
{
|
||
pStream.WriteShort( 8, toSet );
|
||
}
|
||
void CPAddItemToCont::X( SI16 x )
|
||
{
|
||
pStream.WriteShort( 10, x );
|
||
}
|
||
void CPAddItemToCont::Y( SI16 y )
|
||
{
|
||
pStream.WriteShort( 12, y );
|
||
}
|
||
void CPAddItemToCont::GridLocation( SI08 gridLoc )
|
||
{
|
||
pStream.WriteByte( 14, gridLoc );
|
||
}
|
||
void CPAddItemToCont::Container( SERIAL toAdd )
|
||
{
|
||
pStream.WriteLong( (uokrFlag ? 15 : 14), toAdd );
|
||
}
|
||
void CPAddItemToCont::Colour( SI16 toSet )
|
||
{
|
||
pStream.WriteShort( (uokrFlag ? 19 : 18 ), toSet );
|
||
}
|
||
void CPAddItemToCont::Object( CItem &toAdd )
|
||
{
|
||
CopyData( toAdd );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPKickPlayer()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to kick player from server
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x26 (Kick Player)
|
||
//| Size: 5 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[4] serial of GM who issued kick?
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPKickPlayer::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 5 );
|
||
pStream.WriteByte( 0, 0x26 );
|
||
}
|
||
void CPKickPlayer::CopyData( CChar &toCopy )
|
||
{
|
||
Serial( toCopy.GetSerial() );
|
||
}
|
||
CPKickPlayer::CPKickPlayer()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPKickPlayer::CPKickPlayer( CChar &toCopy )
|
||
{
|
||
InternalReset();
|
||
CopyData( toCopy );
|
||
}
|
||
void CPKickPlayer::Serial( SERIAL toSet )
|
||
{
|
||
pStream.WriteLong( 1, toSet );
|
||
}
|
||
CPKickPlayer &CPKickPlayer::operator=( CChar &toCopy )
|
||
{
|
||
CopyData( toCopy );
|
||
return (*this);
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPResurrectMenu()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to respond to resurrection menu choice
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x2C (Resurrection Menu)
|
||
//| Size: 2 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE action (2=ghost, 1=resurrect, 0=from server)
|
||
//|
|
||
//| Note: Resurrection menu has been removed from UO.
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPResurrectMenu::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 2 );
|
||
pStream.WriteByte( 0, 0x2C );
|
||
}
|
||
CPResurrectMenu::CPResurrectMenu()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPResurrectMenu::CPResurrectMenu( UI08 action )
|
||
{
|
||
InternalReset();
|
||
Action( action );
|
||
}
|
||
void CPResurrectMenu::Action( UI08 action )
|
||
{
|
||
pStream.WriteByte( 1, action );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPFightOccurring()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet update client about combat taking place nearby
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x2F (Fight Occurring)
|
||
//| Size: 10 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE unknown1 (0)
|
||
//| BYTE[4] ID of attacker
|
||
//| BYTE[4] ID of attacked
|
||
//|
|
||
//| This packet is sent when there is a fight going on somewhere on screen.
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPFightOccurring::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 10 );
|
||
pStream.WriteByte( 0, 0x2F );
|
||
pStream.WriteByte( 1, 0 );
|
||
}
|
||
// Sent when fight occuring somewhere on the screen
|
||
CPFightOccurring::CPFightOccurring()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPFightOccurring::CPFightOccurring( CChar &attacker, CChar &defender )
|
||
{
|
||
InternalReset();
|
||
Attacker( attacker );
|
||
Defender( defender );
|
||
}
|
||
void CPFightOccurring::Attacker( SERIAL toSet )
|
||
{
|
||
pStream.WriteLong( 2, toSet );
|
||
}
|
||
void CPFightOccurring::Attacker( CChar &attacker )
|
||
{
|
||
Attacker( attacker.GetSerial() );
|
||
}
|
||
void CPFightOccurring::Defender( SERIAL toSet )
|
||
{
|
||
pStream.WriteLong( 6, toSet );
|
||
}
|
||
void CPFightOccurring::Defender( CChar &defender )
|
||
{
|
||
Defender( defender.GetSerial() );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPSkillsValues()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet update client with skill values
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x3A (Send Skills)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] Length
|
||
//| BYTE Type
|
||
//| 0x00 - Full List of skills,
|
||
//| 0xFF - Single skill update,
|
||
//| 0x02 - Full List of skills with skill cap for each skill
|
||
//| 0xDF - Single skill update with skill cap for skill
|
||
//| Repeat for each skill:
|
||
//| BYTE[2] Skill ID number
|
||
//| BYTE[2] Skill Value * 10
|
||
//| BYTE[2] Unmodified Value * 10
|
||
//| BYTE Skill Lock
|
||
//| If( Type == 2 || Type == 0xDF )
|
||
//| BYTE[2] Skill Cap
|
||
//| Byte[2] Null terminated(0000) - Only if Type == 0x00
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPSkillsValues::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 1 );
|
||
pStream.WriteByte( 0, 0x3A );
|
||
}
|
||
void CPSkillsValues::CopyData( CChar &toCopy )
|
||
{
|
||
NumSkills( ALLSKILLS );
|
||
SetCharacter( toCopy );
|
||
}
|
||
|
||
void CPSkillsValues::SetCharacter( CChar &toCopy )
|
||
{
|
||
for( SI08 i = 0; i < NumSkills(); ++i )
|
||
SkillEntry( i, toCopy.GetSkill( i ), toCopy.GetBaseSkill( i ), toCopy.GetSkillLock( i ) );
|
||
}
|
||
|
||
void CPSkillsValues::NumSkills( UI08 numSkills )
|
||
{
|
||
// multiply numSkills with amount of bytes needed per skill
|
||
// plus the additional bytes needed for packet details
|
||
BlockSize( 6 + ( numSkills * 9 ));
|
||
}
|
||
UI08 CPSkillsValues::NumSkills( void )
|
||
{
|
||
SI32 size = pStream.GetShort( 1 );
|
||
size -= 6;
|
||
size /= 9;
|
||
return (UI08)size;
|
||
}
|
||
|
||
void CPSkillsValues::BlockSize( SI16 newValue )
|
||
{
|
||
pStream.ReserveSize( newValue );
|
||
pStream.WriteShort( 1, newValue ); //packet size, variable based on amount of skills
|
||
pStream.WriteByte( 3, 0x02 ); // full list, capped
|
||
pStream.WriteByte( static_cast<size_t>(newValue)-1, 0x00 ); // finish off with a double nullptr
|
||
pStream.WriteByte( static_cast<size_t>(newValue)-2, 0x00 );
|
||
}
|
||
CPSkillsValues::CPSkillsValues()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPSkillsValues::CPSkillsValues( CChar &toCopy )
|
||
{
|
||
InternalReset();
|
||
CopyData( toCopy );
|
||
}
|
||
|
||
void CPSkillsValues::SkillEntry( SI16 skillID, SI16 skillVal, SI16 baseSkillVal, SkillLock skillLock )
|
||
{
|
||
SI32 offset = ( skillID * 9 ) + 4;
|
||
pStream.WriteShort( offset, skillID + 1 );
|
||
pStream.WriteShort( static_cast<size_t>(offset) + 2, skillVal );
|
||
pStream.WriteShort( static_cast<size_t>(offset) + 4, baseSkillVal );
|
||
pStream.WriteByte( static_cast<size_t>(offset) + 6, skillLock );
|
||
pStream.WriteShort( static_cast<size_t>(offset) + 7, (UI16)cwmWorldState->ServerData()->ServerSkillCapStatus() );
|
||
}
|
||
CPSkillsValues &CPSkillsValues::operator=( CChar &toCopy )
|
||
{
|
||
CopyData( toCopy );
|
||
return (*this);
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPMapMessage()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet informing client to display a map
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x90 (Map Message/Details/Course)
|
||
//| Size: 19 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[4] key used/map serial
|
||
//| BYTE[2] gump art id (0x139D), corner image
|
||
//| BYTE[2] upper left x location of map
|
||
//| BYTE[2] upper left y location of map
|
||
//| BYTE[2] lower right x location of map
|
||
//| BYTE[2] lower right y location of map
|
||
//| BYTE[2] gump width in pixels
|
||
//| BYTE[2] gump height in pixels
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPMapMessage::CPMapMessage()
|
||
{
|
||
pStream.ReserveSize( 19 );
|
||
pStream.WriteByte( 0, 0x90 );
|
||
GumpArt( 0x139D );
|
||
}
|
||
void CPMapMessage::UpperLeft( SI16 x, SI16 y )
|
||
{
|
||
pStream.WriteShort( 7, x );
|
||
pStream.WriteShort( 9, y );
|
||
}
|
||
void CPMapMessage::LowerRight( SI16 x, SI16 y )
|
||
{
|
||
pStream.WriteShort( 11, x );
|
||
pStream.WriteShort( 13, y );
|
||
}
|
||
void CPMapMessage::Dimensions( SI16 width, SI16 height )
|
||
{
|
||
pStream.WriteShort( 15, width );
|
||
pStream.WriteShort( 17, height );
|
||
}
|
||
void CPMapMessage::GumpArt( SI16 newArt )
|
||
{
|
||
pStream.WriteShort( 5, newArt );
|
||
}
|
||
void CPMapMessage::KeyUsed( UI32 key )
|
||
{
|
||
pStream.WriteLong( 1, key );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPBookTitlePage()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet updating title page of a book
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x93 (Book Header (old))
|
||
//| Size: 99 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[4] book serial
|
||
//| BYTE write flag
|
||
//| 0x00 - non-writable
|
||
//| 0x01 - writable
|
||
//| BYTE new flag/unknown (0x1)
|
||
//| BYTE[2] # of pages
|
||
//| BYTE[60] title
|
||
//| BYTE[30] author
|
||
//|
|
||
//| NOTE: Client sends a 0x93 message on book close. Update packet for the server
|
||
//| to handle changes. Write Flag through Page Count are all 0's on client response
|
||
//| NOTE: Replaced with 0xD4 in newer clients?
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPBookTitlePage::CPBookTitlePage()
|
||
{
|
||
pStream.ReserveSize( 99 );
|
||
pStream.WriteByte( 0, 0x93 );
|
||
}
|
||
void CPBookTitlePage::Serial( SERIAL toSet )
|
||
{
|
||
pStream.WriteLong( 1, toSet );
|
||
}
|
||
void CPBookTitlePage::WriteFlag( UI08 flag )
|
||
{
|
||
pStream.WriteByte( 5, flag );
|
||
}
|
||
void CPBookTitlePage::NewFlag( UI08 flag )
|
||
{
|
||
pStream.WriteByte( 6, flag );
|
||
}
|
||
void CPBookTitlePage::Pages( SI16 pages )
|
||
{
|
||
pStream.WriteShort( 7, pages );
|
||
}
|
||
void CPBookTitlePage::Title( const std::string& txt )
|
||
{
|
||
if( txt.length() >= 60 )
|
||
{
|
||
pStream.WriteString( 9, txt, 59 );
|
||
pStream.WriteByte( 68, 0x00 );
|
||
}
|
||
else
|
||
pStream.WriteString( 9, txt, txt.size() );
|
||
}
|
||
void CPBookTitlePage::Author( const std::string& txt )
|
||
{
|
||
if( txt.length() >= 30 )
|
||
{
|
||
pStream.WriteString( 69, txt, 29 );
|
||
pStream.WriteByte( 98, 0x00 );
|
||
}
|
||
else
|
||
pStream.WriteString( 69, txt, txt.size() );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPUltimaMessenger()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet updating title page of a book
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xBB (Ultima Messenger)
|
||
//| Size: 9 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[4] id1 (source serial)
|
||
//| BYTE[4] id2 (target serial)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPUltimaMessenger::CPUltimaMessenger()
|
||
{
|
||
pStream.ReserveSize( 9 );
|
||
pStream.WriteByte( 0, 0xBB );
|
||
}
|
||
void CPUltimaMessenger::ID1( SERIAL toSet )
|
||
{
|
||
pStream.WriteLong( 1, toSet );
|
||
}
|
||
void CPUltimaMessenger::ID2( SERIAL toSet )
|
||
{
|
||
pStream.WriteLong( 5, toSet );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPGumpTextEntry()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to display text entry dialog in client
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xAB (Gump Text Entry Dialog)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] blockSize
|
||
//| BYTE[4] id
|
||
//| BYTE parentID
|
||
//| BYTE buttonID
|
||
//| BYTE[2] textlen
|
||
//| BYTE[?] text (text appearing at top of gump)
|
||
//| BYTE cancel (0=disable, 1=enable - if enabled, client is able to cancel query)
|
||
//| BYTE style (0=disable, 1=normal, 2=numerical)
|
||
//| BYTE[4] format (if style 1, max text len, if style2, max numeric value)
|
||
//| BYTE[2] text2 length
|
||
//| BYTE[?] text2 (label above text entry box)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPGumpTextEntry::InternalReset( void )
|
||
{
|
||
BlockSize( 3 );
|
||
pStream.WriteByte( 0, 0xAB );
|
||
}
|
||
CPGumpTextEntry::CPGumpTextEntry()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPGumpTextEntry::CPGumpTextEntry( const std::string& text )
|
||
{
|
||
InternalReset();
|
||
Text1( text );
|
||
}
|
||
CPGumpTextEntry::CPGumpTextEntry( const std::string& text1, const std::string& text2 )
|
||
{
|
||
InternalReset();
|
||
Text1( text1 );
|
||
Text2( text2 );
|
||
}
|
||
void CPGumpTextEntry::Serial( SERIAL id )
|
||
{
|
||
pStream.WriteLong( 3, id );
|
||
}
|
||
void CPGumpTextEntry::ParentID( UI08 newVal )
|
||
{
|
||
pStream.WriteByte( 7, newVal );
|
||
}
|
||
void CPGumpTextEntry::ButtonID( UI08 newVal )
|
||
{
|
||
pStream.WriteByte( 8, newVal );
|
||
}
|
||
void CPGumpTextEntry::Cancel( UI08 newVal )
|
||
{
|
||
SI16 t1Len = Text1Len();
|
||
pStream.WriteByte( static_cast<size_t>(t1Len) + 11, newVal );
|
||
}
|
||
void CPGumpTextEntry::Style( UI08 newVal )
|
||
{
|
||
SI16 t1Len = Text1Len();
|
||
pStream.WriteByte( static_cast<size_t>(t1Len) + 12, newVal );
|
||
}
|
||
void CPGumpTextEntry::Format( SERIAL id )
|
||
{
|
||
SI16 t1Len = Text1Len();
|
||
pStream.WriteLong( static_cast<size_t>(t1Len) + 13, id );
|
||
}
|
||
void CPGumpTextEntry::Text1( const std::string& txt )
|
||
{
|
||
size_t sLen = txt.length();
|
||
BlockSize( static_cast<SI16>(sLen + 20) ); // 11 + 1 + 8
|
||
Text1Len( static_cast<SI16>(sLen + 1) );
|
||
pStream.WriteString( 11, txt, sLen );
|
||
}
|
||
void CPGumpTextEntry::Text2( const std::string& txt )
|
||
{
|
||
size_t sLen = txt.length();
|
||
SI16 currentSize = CurrentSize();
|
||
BlockSize( static_cast< UI16 >(currentSize + sLen + 1) );
|
||
Text2Len( static_cast< SI16 >(sLen + 1) );
|
||
pStream.WriteString( currentSize, txt, sLen );
|
||
}
|
||
void CPGumpTextEntry::BlockSize( SI16 newVal )
|
||
{
|
||
pStream.ReserveSize( newVal );
|
||
pStream.WriteByte( 0, 0xAB );
|
||
pStream.WriteShort( 1, newVal );
|
||
}
|
||
|
||
SI16 CPGumpTextEntry::CurrentSize( void )
|
||
{
|
||
return pStream.GetShort( 1 );
|
||
}
|
||
|
||
SI16 CPGumpTextEntry::Text1Len( void )
|
||
{
|
||
return pStream.GetShort( 9 );
|
||
}
|
||
|
||
void CPGumpTextEntry::Text1Len( SI16 newVal )
|
||
{
|
||
pStream.WriteShort( 9, newVal );
|
||
}
|
||
|
||
SI16 CPGumpTextEntry::Text2Len( void )
|
||
{
|
||
SI16 t1Len = Text1Len();
|
||
return pStream.GetShort( static_cast<size_t>(t1Len) + 17 );
|
||
}
|
||
|
||
void CPGumpTextEntry::Text2Len( SI16 newVal )
|
||
{
|
||
SI16 t1Len = Text1Len();
|
||
pStream.WriteShort( static_cast<size_t>(t1Len) + 17, newVal );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPGodModeToggle()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to enable/disable god mode in god client
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x2B (Enable/Disable God Mode for God Client)
|
||
//| Size: 2 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE Cmd
|
||
//| BYTE 0/1 For Disable/Enable God Mode in client.
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPGodModeToggle::CPGodModeToggle()
|
||
{
|
||
InternalReset();
|
||
}
|
||
void CPGodModeToggle::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 2 );
|
||
pStream.WriteByte( 0, 0x2B );
|
||
}
|
||
|
||
void CPGodModeToggle::CopyData( CSocket *s )
|
||
{
|
||
pStream.WriteByte( 1, s->GetByte( 1 ) );
|
||
}
|
||
|
||
CPGodModeToggle::CPGodModeToggle( CSocket *s )
|
||
{
|
||
InternalReset();
|
||
CopyData( s );
|
||
}
|
||
|
||
CPGodModeToggle& CPGodModeToggle::operator=( CSocket *s )
|
||
{
|
||
CopyData( s );
|
||
return (*this);
|
||
}
|
||
|
||
void CPGodModeToggle::ToggleStatus( bool toSet )
|
||
{
|
||
if( toSet )
|
||
pStream.WriteByte( 1, 1 );
|
||
else
|
||
pStream.WriteByte( 1, 0 );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPLoginDeny()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to deny client's login request
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x82 (Login Denied)
|
||
//| Size: 2 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE reason
|
||
//|
|
||
//| Login reasons handled by client:
|
||
//| 0x00 - Incorrect name/password.
|
||
//| 0x01 - Someone is already using this account.
|
||
//| 0x02 - Your account has been blocked.
|
||
//| 0x03 - Your account credentials are invalid.
|
||
//| 0x04 - Communication problem.
|
||
//| 0x05 - The IGR concurrency limit has been met.
|
||
//| 0x06 - The IGR time limit has been met.
|
||
//| 0x07 - General IGR authentication failure.
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPLoginDeny::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 2 );
|
||
pStream.WriteByte( 0, 0x82 );
|
||
}
|
||
CPLoginDeny::CPLoginDeny()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPLoginDeny::CPLoginDeny( LoginDenyReason reason )
|
||
{
|
||
InternalReset();
|
||
DenyReason( reason );
|
||
}
|
||
void CPLoginDeny::DenyReason( LoginDenyReason reason )
|
||
{
|
||
pStream.WriteByte( 1, reason );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPKREncryptionRequest()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet with encryption request for KR3D client
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xE3 (KR Encryption Request)
|
||
//| Size: 2 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE[1] 0xE3
|
||
//| BYTE[2] Length
|
||
//| BYTE[4] Key 1 Length
|
||
//| BYTE[Key 1 Length] Key 1
|
||
//| BYTE[4] Key 2 Length
|
||
//| BYTE[Key 2 Length] Key 2
|
||
//| BYTE[4] Key 3 Length
|
||
//| BYTE[Key 3 Length] Key 3
|
||
//| BYTE[4] Unknown
|
||
//| BYTE[4] Key 4 Length
|
||
//| BYTE[Key 4 Length] Key 4
|
||
//|
|
||
//| Notes
|
||
//| Unverified encryption method of keys is AES in CFB Mode
|
||
//| As of 08/02/2009, this is the current encryption known for KR clients up to date:
|
||
//| 0xe3,
|
||
//| 0x4d00,
|
||
//| 0x03000000,
|
||
//| 0x02, 0x01, 0x03,
|
||
//| 0x13000000,
|
||
//| 0x02, 0x11, 0x00, 0xfc, 0x2f, 0xe3, 0x81, 0x93, 0xcb, 0xaf, 0x98, 0xdd, 0x83, 0x13, 0xd2, 0x9e, 0xea, 0xe4, 0x13,
|
||
//| 0x10000000,
|
||
//| 0x78, 0x13, 0xb7, 0x7b, 0xce, 0xA8, 0xd7, 0xbc, 0x52, 0xde, 0x38, 0x30, 0xea, 0xe9, 0x1e, 0xa3,
|
||
//| 0x20000000,
|
||
//| 0x10000000,
|
||
//| 0x5a, 0xce, 0x3e, 0xe3, 0x97, 0x92, 0xe4, 0x8a, 0xf1, 0x9a, 0xd3, 0x04, 0x41, 0x03, 0xcb, 0x53
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPKREncryptionRequest::CPKREncryptionRequest( CSocket* s)
|
||
{
|
||
pStream.ReserveSize( 77 );
|
||
pStream.WriteByte( 0, 0xE3 );
|
||
|
||
// Size
|
||
pStream.WriteByte( 1, 0x00 );
|
||
pStream.WriteByte( 2, 0x4D );
|
||
|
||
pStream.WriteByte( 3, 0x00 );
|
||
pStream.WriteByte( 4, 0x00 );
|
||
pStream.WriteByte( 5, 0x00 );
|
||
pStream.WriteByte( 6, 0x03 );
|
||
pStream.WriteByte( 7, 0x02 );
|
||
pStream.WriteByte( 8, 0x01 );
|
||
pStream.WriteByte( 9, 0x03 );
|
||
pStream.WriteByte( 10, 0x00 );
|
||
pStream.WriteByte( 11, 0x00 );
|
||
pStream.WriteByte( 12, 0x00 );
|
||
pStream.WriteByte( 13, 0x13 );
|
||
pStream.WriteByte( 14, 0x02 );
|
||
pStream.WriteByte( 15, 0x11 );
|
||
|
||
pStream.WriteByte( 16, 0x00 );
|
||
pStream.WriteByte( 17, 0xFC );
|
||
pStream.WriteByte( 18, 0x2F );
|
||
pStream.WriteByte( 19, 0xE3 );
|
||
pStream.WriteByte( 20, 0x81 );
|
||
pStream.WriteByte( 21, 0x93 );
|
||
pStream.WriteByte( 22, 0xCB );
|
||
pStream.WriteByte( 23, 0xAF );
|
||
pStream.WriteByte( 24, 0x98 );
|
||
pStream.WriteByte( 25, 0xDD );
|
||
pStream.WriteByte( 26, 0x83 );
|
||
pStream.WriteByte( 27, 0x13 );
|
||
pStream.WriteByte( 28, 0xD2 );
|
||
pStream.WriteByte( 29, 0x9E );
|
||
pStream.WriteByte( 30, 0xEA );
|
||
pStream.WriteByte( 31, 0xE4 );
|
||
|
||
pStream.WriteByte( 32, 0x13 );
|
||
pStream.WriteByte( 33, 0x00 );
|
||
pStream.WriteByte( 34, 0x00 );
|
||
pStream.WriteByte( 35, 0x00 );
|
||
pStream.WriteByte( 36, 0x10 );
|
||
pStream.WriteByte( 37, 0x78 );
|
||
pStream.WriteByte( 38, 0x13 );
|
||
pStream.WriteByte( 39, 0xB7 );
|
||
pStream.WriteByte( 40, 0x7B );
|
||
pStream.WriteByte( 41, 0xCE );
|
||
pStream.WriteByte( 42, 0xA8 );
|
||
pStream.WriteByte( 43, 0xD7 );
|
||
pStream.WriteByte( 44, 0xBC );
|
||
pStream.WriteByte( 45, 0x52 );
|
||
pStream.WriteByte( 46, 0xDE );
|
||
pStream.WriteByte( 47, 0x38 );
|
||
|
||
pStream.WriteByte( 48, 0x30 );
|
||
pStream.WriteByte( 49, 0xEA );
|
||
pStream.WriteByte( 50, 0xE9 );
|
||
pStream.WriteByte( 51, 0x1E );
|
||
pStream.WriteByte( 52, 0xA3 );
|
||
pStream.WriteByte( 53, 0x00 );
|
||
pStream.WriteByte( 54, 0x00 );
|
||
pStream.WriteByte( 55, 0x00 );
|
||
pStream.WriteByte( 56, 0x20 );
|
||
pStream.WriteByte( 57, 0x00 );
|
||
pStream.WriteByte( 58, 0x00 );
|
||
pStream.WriteByte( 59, 0x00 );
|
||
pStream.WriteByte( 60, 0x10 );
|
||
pStream.WriteByte( 61, 0x5A );
|
||
pStream.WriteByte( 62, 0xCE );
|
||
pStream.WriteByte( 63, 0x3E );
|
||
|
||
pStream.WriteByte( 64, 0xE3 );
|
||
pStream.WriteByte( 65, 0x97 );
|
||
pStream.WriteByte( 66, 0x92 );
|
||
pStream.WriteByte( 67, 0xE4 );
|
||
pStream.WriteByte( 68, 0x8A );
|
||
pStream.WriteByte( 69, 0xF1 );
|
||
pStream.WriteByte( 70, 0x9A );
|
||
pStream.WriteByte( 71, 0xD3 );
|
||
pStream.WriteByte( 72, 0x04 );
|
||
pStream.WriteByte( 73, 0x41 );
|
||
pStream.WriteByte( 74, 0x03 );
|
||
pStream.WriteByte( 75, 0xCB );
|
||
pStream.WriteByte( 76, 0x53 );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPKrriosClientSpecial()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet for krrios client special packet, used by ClassicUO
|
||
//| for things like updating party/guild member locations in WorldMap
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xF0 (KrriosClientSpecial)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| Byte cmd
|
||
//| Byte[2] length
|
||
//| Byte type
|
||
//| 0x00 // krrios special packet accepted, enable WorldMap updates
|
||
//| 0x01 // custom party info
|
||
//| Loop for each party member online
|
||
//| BYTE[4] serial
|
||
//| BYTE[2] x
|
||
//| BYTE[2] y
|
||
//| BYTE world
|
||
//| BYTE[4] 0x0000
|
||
//| 0x02 // guild track info
|
||
//| BYTE locations // bool, whether to update locations on map
|
||
//| Loop for each party/guild member online
|
||
//| BYTE[4] serial
|
||
//| BYTE[2] x
|
||
//| BYTE[2] y
|
||
//| BYTE world
|
||
//| BYTE healthpoint percentage of max hp
|
||
//| BYTE[5] 0x00000
|
||
//| 0x03 // runebook contents
|
||
//| 0x04 // guardline data
|
||
//| 0xf0 // ???
|
||
//| 0xfe // Razor ack sent
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPKrriosClientSpecial::CPKrriosClientSpecial( CSocket * mSock, CChar * mChar, UI08 type, bool locations )
|
||
{
|
||
pStream.ReserveSize( 2 );
|
||
pStream.WriteByte( 0, 0xF0 );
|
||
UI08 byteOffset = 3;
|
||
|
||
switch( type )
|
||
{
|
||
case 0x00: // Party
|
||
{
|
||
pStream.WriteByte( byteOffset, 0x01 );
|
||
byteOffset += 1;
|
||
Party * myParty = PartyFactory::getSingleton().Get( mSock->CurrcharObj() );
|
||
if( myParty != nullptr )
|
||
{
|
||
std::vector< PartyEntry * > *mList = myParty->MemberList();
|
||
if( mList != nullptr )
|
||
{
|
||
for( size_t j = 0; j < mList->size(); ++j )
|
||
{
|
||
PartyEntry *mEntry = ( *mList )[j];
|
||
CChar * partyMember = mEntry->Member();
|
||
|
||
if( partyMember->GetSerial() == mSock->CurrcharObj()->GetSerial() )
|
||
continue;
|
||
|
||
if( objInRange( mSock->CurrcharObj(), partyMember, DIST_INRANGE ) && partyMember->GetVisible() == 0 )
|
||
continue;
|
||
|
||
pStream.WriteLong( byteOffset, partyMember->GetSerial() );
|
||
byteOffset += 4;
|
||
pStream.WriteShort( byteOffset, partyMember->GetX() );
|
||
byteOffset += 2;
|
||
pStream.WriteShort( byteOffset, partyMember->GetY() );
|
||
byteOffset += 2;
|
||
pStream.WriteByte( byteOffset, partyMember->WorldNumber() );
|
||
byteOffset += 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Finalize packet and ship it!
|
||
pStream.WriteLong( byteOffset, 0x0000 );
|
||
byteOffset += 4;
|
||
pStream.WriteShort( 1, byteOffset ); // length
|
||
pStream.ReserveSize( byteOffset );
|
||
break;
|
||
}
|
||
case 0x01: // Guild
|
||
{
|
||
pStream.WriteByte( byteOffset, 0x02 );
|
||
byteOffset += 2;
|
||
CGuild * mGuild = GuildSys->Guild( mChar->GetGuildNumber() );
|
||
if( mGuild != nullptr )
|
||
{
|
||
size_t numRecruits = mGuild->NumRecruits();
|
||
size_t numMembers = mGuild->NumMembers();
|
||
|
||
// First, look up the recruits to see who's online
|
||
for( auto i = 0; i < numRecruits; i++ )
|
||
{
|
||
SERIAL recruitSerial = mGuild->RecruitNumber( i );
|
||
CChar * guildRecruit = calcCharObjFromSer( recruitSerial );
|
||
if( guildRecruit != nullptr && guildRecruit->GetSocket() != nullptr )
|
||
{
|
||
if( guildRecruit->GetSerial() == mChar->GetSerial() )
|
||
continue;
|
||
|
||
if( locations && objInRange( mChar, guildRecruit, DIST_SAMESCREEN ) && guildRecruit->GetVisible() == 0 )
|
||
continue;
|
||
|
||
// Guild recruit is online, and it's not the player!
|
||
pStream.WriteLong( byteOffset, guildRecruit->GetSerial() );
|
||
byteOffset += 4;
|
||
if( locations )
|
||
{
|
||
pStream.WriteShort( byteOffset, guildRecruit->GetX() );
|
||
byteOffset += 2;
|
||
pStream.WriteShort( byteOffset, guildRecruit->GetY() );
|
||
byteOffset += 2;
|
||
pStream.WriteByte( byteOffset, guildRecruit->WorldNumber() );
|
||
byteOffset += 1;
|
||
SI16 currentHP = guildRecruit->GetHP();
|
||
UI16 maxHP = guildRecruit->GetMaxHP();
|
||
UI08 percentHP = static_cast<UI08>( 100 * ( currentHP / maxHP ));
|
||
|
||
pStream.WriteByte( byteOffset, percentHP );
|
||
byteOffset += 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Then, look up the guild members to see who's online
|
||
for( auto i = 0; i < numMembers; i++ )
|
||
{
|
||
SERIAL memberSerial = mGuild->MemberNumber( i );
|
||
CChar * guildMember = calcCharObjFromSer( memberSerial );
|
||
if( guildMember != nullptr && guildMember->GetSocket() != nullptr )
|
||
{
|
||
if( guildMember->GetSerial() == mChar->GetSerial() )
|
||
continue;
|
||
|
||
if( locations && objInRange( mChar, guildMember, DIST_SAMESCREEN ) && guildMember->GetVisible() == 0 )
|
||
continue;
|
||
|
||
// Guild member is online, and it's not the player!
|
||
pStream.WriteLong( byteOffset, guildMember->GetSerial() );
|
||
byteOffset += 4;
|
||
if( locations )
|
||
{
|
||
pStream.WriteShort( byteOffset, guildMember->GetX() );
|
||
byteOffset += 2;
|
||
pStream.WriteShort( byteOffset, guildMember->GetY() );
|
||
byteOffset += 2;
|
||
pStream.WriteByte( byteOffset, guildMember->WorldNumber() );
|
||
byteOffset += 1;
|
||
|
||
SI16 currentHP = guildMember->GetHP();
|
||
UI16 maxHP = guildMember->GetMaxHP();
|
||
UI08 percentHP = static_cast<UI08>( 100 * ( currentHP / maxHP ));
|
||
|
||
pStream.WriteByte( byteOffset, percentHP );
|
||
byteOffset += 1;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Finalize packet and ship it!
|
||
pStream.WriteByte( 4, locations );
|
||
pStream.WriteLong( byteOffset, 0x0000 );
|
||
byteOffset += 4;
|
||
pStream.WriteByte( byteOffset, 0x00 );
|
||
byteOffset += 1;
|
||
pStream.WriteShort( 1, byteOffset ); // length
|
||
pStream.ReserveSize( byteOffset );
|
||
break;
|
||
}
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
void CPKrriosClientSpecial::Log( std::ofstream &outStream, bool fullHeader )
|
||
{
|
||
if( fullHeader )
|
||
outStream << "[SEND]Packet : CPKrriosClientSpecial 0xF0 --> Length: " << pStream.GetSize() << TimeStamp() << std::endl;
|
||
outStream << " Raw dump :" << std::endl;
|
||
CPUOXBuffer::Log( outStream, false );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPMapChange()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to change huge of mouse cursor for Felucca and Trammel
|
||
//| facets, and/or to change the map in the client
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xBF (General Information Packet)
|
||
//| Subcommand: 0x8 (Set Cursor Hue / Set MAP )
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd (0xBF)
|
||
//| BYTE[2] Length
|
||
//| BYTE[2] Subcommand (0x8)
|
||
//| Subcommand details
|
||
//| BYTE hue
|
||
//| 0x00 = Felucca, unhued / BRITANNIA map.
|
||
//| 0x01 = Trammel, hued gold / BRITANNIA map
|
||
//| 0x02 = Ilshenar
|
||
//| 0x03 = Malas
|
||
//| 0x04 = Tokuno
|
||
//| 0x05 = TerMur)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPMapChange::Log( std::ofstream &outStream, bool fullHeader )
|
||
{
|
||
if( fullHeader )
|
||
outStream << "[SEND]Packet : CPMapChange 0xBF Subcommand 8 --> Length: " << pStream.GetSize() << TimeStamp() << std::endl;
|
||
outStream << "Hue : " << (SI16)pStream.GetByte( 5 ) << std::endl;
|
||
switch( pStream.GetByte( 5 ) )
|
||
{
|
||
case 0: outStream << " (Felucca)" << std::endl; break;
|
||
case 1: outStream << " (Trammel)" << std::endl; break;
|
||
case 2: outStream << " (Ilshenar)" << std::endl; break;
|
||
case 3: outStream << " (Malas)" << std::endl; break;
|
||
default: outStream << " (Unknown)" << std::endl; break;
|
||
}
|
||
outStream << " Raw dump :" << std::endl;
|
||
CPUOXBuffer::Log( outStream, false );
|
||
}
|
||
void CPMapChange::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 6 );
|
||
pStream.WriteByte( 0, 0xBF );
|
||
pStream.WriteShort( 1, 0x0006 );
|
||
pStream.WriteShort( 3, 0x0008 ); // set client hue
|
||
}
|
||
CPMapChange::CPMapChange()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPMapChange::CPMapChange( UI08 newMap )
|
||
{
|
||
InternalReset();
|
||
SetMap( newMap );
|
||
}
|
||
CPMapChange::CPMapChange( CBaseObject *moving )
|
||
{
|
||
InternalReset();
|
||
if( ValidateObject( moving ) )
|
||
SetMap( moving->WorldNumber() );
|
||
}
|
||
|
||
void CPMapChange::SetMap( UI08 newMap )
|
||
{
|
||
pStream.WriteByte( 5, newMap );
|
||
}
|
||
CPMapChange& CPMapChange::operator=( CBaseObject& moving )
|
||
{
|
||
SetMap( moving.WorldNumber() );
|
||
return (*this);
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPCloseGump( UI32 gumpID, UI32 buttonID )
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to close a generic/custom gump with a specified ID,
|
||
//| while also allowing to specify the button ID response to send after
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xBF (General Information Packet)
|
||
//| Subcommand: 0x4 (Close Generic Gump)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd (0xBF)
|
||
//| BYTE[2] Length
|
||
//| BYTE[2] Subcommand (0x4)
|
||
//| Subcommand details
|
||
//| BYTE[4] dialogID // which gump to destroy (second ID in 0xB0 packet)
|
||
//| BYTE[4] buttonId // response buttonID for packet 0xB1
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPCloseGump::CPCloseGump( UI32 gumpID, UI32 buttonID )
|
||
{
|
||
_gumpID = gumpID;
|
||
_buttonID = buttonID;
|
||
InternalReset();
|
||
}
|
||
void CPCloseGump::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 13 );
|
||
pStream.WriteByte( 0, 0xBF ); // Command: Packet 0xBF - General Information Packet
|
||
pStream.WriteShort( 1, 13 ); // Packet length
|
||
pStream.WriteShort( 3, 0x04 ); // Subcommand 0x04 - Close Generic Gump
|
||
pStream.WriteLong( 5, _gumpID ); // gumpID - which gump to destroy
|
||
pStream.WriteLong( 9, _buttonID ); // buttonID - response buttonID for packet 0xB1
|
||
}
|
||
void CPCloseGump::Log( std::ofstream &outStream, bool fullHeader )
|
||
{
|
||
if( fullHeader )
|
||
outStream << "[SEND]Packet : CPCloseGump 0xBF Subcommand 4 --> Length: " << pStream.GetSize() << TimeStamp() << std::endl;
|
||
outStream << "gumpID : " << (UI32)pStream.GetLong( 5 ) << std::endl;
|
||
outStream << "buttonID : " << (UI32)pStream.GetLong( 9 ) << std::endl;
|
||
outStream << " Raw dump :" << std::endl;
|
||
CPUOXBuffer::Log( outStream, false );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPItemsInContainer()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to send list of items in container to client
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x3C (Items in Container)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] blockSize
|
||
//| BYTE[2] # of Item segments
|
||
//| Item Segments:
|
||
//| BYTE[4] itemID
|
||
//| BYTE[2] model
|
||
//| BYTE unknown1 (0x00)
|
||
//| BYTE[2] # of items in stack
|
||
//| BYTE[2] xLoc
|
||
//| BYTE[2] yLoc
|
||
//| BYTE[4] Container ItemID
|
||
//| BYTE[2] color
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPItemsInContainer::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 5 );
|
||
pStream.WriteByte( 0, 0x3C );
|
||
isVendor = false;
|
||
isPlayerVendor = false;
|
||
vendorSerial = INVALIDSERIAL;
|
||
isCorpse = false;
|
||
uokrFlag = false;
|
||
}
|
||
CPItemsInContainer::CPItemsInContainer()
|
||
{
|
||
InternalReset();
|
||
}
|
||
|
||
CPItemsInContainer::CPItemsInContainer( CSocket *mSock, CItem *container, UI08 contType, bool isPVendor )
|
||
{
|
||
if( ValidateObject( container ) )
|
||
{
|
||
InternalReset();
|
||
if( mSock->ClientVerShort() >= CVS_6017 )
|
||
UOKRFlag( true );
|
||
Type( contType );
|
||
PlayerVendor( isPVendor );
|
||
if( isVendor )
|
||
VendorSerial( container->GetSerial() );
|
||
|
||
CopyData( mSock, (*container) );
|
||
}
|
||
}
|
||
|
||
void CPItemsInContainer::Type( UI08 contType )
|
||
{
|
||
if( contType == 0x01 ) // Corpse
|
||
isCorpse = true;
|
||
else if( contType == 0x02 ) // Vendor
|
||
isVendor = true;
|
||
}
|
||
|
||
void CPItemsInContainer::UOKRFlag( bool value )
|
||
{
|
||
uokrFlag = value;
|
||
}
|
||
void CPItemsInContainer::PlayerVendor( bool value )
|
||
{
|
||
isPlayerVendor = value;
|
||
}
|
||
|
||
void CPItemsInContainer::VendorSerial( SERIAL toSet )
|
||
{
|
||
vendorSerial = toSet;
|
||
}
|
||
|
||
UI16 CPItemsInContainer::NumberOfItems( void ) const
|
||
{
|
||
return pStream.GetUShort( 3 );
|
||
}
|
||
|
||
void CPItemsInContainer::NumberOfItems( UI16 numItems )
|
||
{
|
||
UI16 packetSize = (UI16)((numItems * (uokrFlag ? 20 : 19)) + 5);
|
||
pStream.ReserveSize( packetSize );
|
||
pStream.WriteShort( 1, packetSize );
|
||
pStream.WriteShort( 3, numItems );
|
||
}
|
||
void CPItemsInContainer::AddItem( CItem *toAdd, UI16 itemNum, CSocket *mSock )
|
||
{
|
||
pStream.ReserveSize( pStream.GetSize() + (uokrFlag ? 20 : 19) );
|
||
UI16 baseOffset = (UI16)(5 + itemNum * (uokrFlag ? 20 : 19));
|
||
pStream.WriteLong( baseOffset + 0, toAdd->GetSerial() );
|
||
pStream.WriteShort( static_cast<size_t>(baseOffset) + 4, toAdd->GetID() );
|
||
pStream.WriteShort( static_cast<size_t>(baseOffset) + 7, toAdd->GetAmount() );
|
||
pStream.WriteShort( static_cast<size_t>(baseOffset) + 9, toAdd->GetX() );
|
||
pStream.WriteShort( static_cast<size_t>(baseOffset) + 11, toAdd->GetY() );
|
||
if( uokrFlag )
|
||
{
|
||
pStream.WriteByte( static_cast<size_t>(baseOffset) + 13, toAdd->GetGridLocation() );
|
||
baseOffset+=1;
|
||
}
|
||
|
||
if( isVendor )
|
||
pStream.WriteLong( static_cast<size_t>(baseOffset) + 13, vendorSerial );
|
||
else
|
||
pStream.WriteLong( static_cast<size_t>(baseOffset) + 13, toAdd->GetContSerial() );
|
||
|
||
pStream.WriteShort( static_cast<size_t>(baseOffset) + 17, toAdd->GetColour() );
|
||
|
||
toAdd->SetDecayTime( 0 );
|
||
|
||
CPToolTip pSend( toAdd->GetSerial(), !isVendor, isPlayerVendor );
|
||
mSock->Send( &pSend );
|
||
}
|
||
|
||
void CPItemsInContainer::Add( UI16 itemNum, SERIAL toAdd, SERIAL cont, UI08 amount )
|
||
{
|
||
UI16 baseOffset = (UI16)((itemNum * (uokrFlag ? 20 : 19)) + 5);
|
||
|
||
pStream.WriteLong( baseOffset + 0, toAdd );
|
||
pStream.WriteShort( static_cast<size_t>(baseOffset) + 7, amount );
|
||
pStream.WriteLong( static_cast<size_t>(baseOffset) + (uokrFlag ? 14 : 13), cont );
|
||
}
|
||
|
||
void CPItemsInContainer::CopyData( CSocket *mSock, CItem& toCopy )
|
||
{
|
||
UI16 itemCount = 0;
|
||
bool itemIsCorpse = toCopy.isCorpse();
|
||
|
||
GenericList< CItem * > *tcCont = toCopy.GetContainsList();
|
||
for( CItem *ctr = tcCont->First(); !tcCont->Finished(); ctr = tcCont->Next() )
|
||
{
|
||
if( ValidateObject( ctr ) && ( !isCorpse || !itemIsCorpse || ( itemIsCorpse && ctr->GetLayer() ) ) )
|
||
{
|
||
if( !ctr->isFree() )
|
||
{
|
||
if( ctr->GetVisible() != 3 || mSock->CurrcharObj()->IsGM() ) // don't show GM hidden objects to non-GM players.
|
||
{
|
||
AddItem( ctr, itemCount, mSock );
|
||
++itemCount;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
NumberOfItems( itemCount );
|
||
}
|
||
|
||
void CPItemsInContainer::Log( std::ofstream &outStream, bool fullHeader )
|
||
{
|
||
size_t numItems = pStream.GetUShort( 3 );
|
||
if( fullHeader )
|
||
outStream << "[SEND]Packet : CPItemsInContainer 0x3c --> Length: " << pStream.GetSize() << TimeStamp() << std::endl;
|
||
outStream << "Block size : " << pStream.GetUShort( 1 ) << std::endl;
|
||
outStream << "Number of Items: " << std::dec << numItems << std::endl;
|
||
size_t baseOffset = 5;
|
||
for( size_t x = 0; x < numItems; ++x )
|
||
{
|
||
outStream << " ITEM " << x << " ID: " << "0x" << std::hex << pStream.GetULong( baseOffset ) << std::endl;
|
||
outStream << " Model : " << "0x" << pStream.GetUShort( baseOffset+=4 ) << std::endl;
|
||
outStream << " Amount : " << std::dec << pStream.GetUShort( baseOffset+=3 ) << std::endl;
|
||
outStream << " XY : " << pStream.GetUShort( baseOffset+=2 ) ;
|
||
outStream << "," <<
|
||
pStream.GetUShort( baseOffset+=2 ) << std::endl;
|
||
outStream << " Container : " << "0x" << std::hex << pStream.GetULong( baseOffset+=2 ) << std::endl;
|
||
outStream << " Color : " << "0x" << pStream.GetUShort( baseOffset += 4 ) << std::endl;
|
||
baseOffset += 2;
|
||
}
|
||
outStream << " Raw dump :" << std::endl;
|
||
|
||
CPUOXBuffer::Log( outStream, false );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPOpenBuyWindow()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to open vendor buy window
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x74 (Open Buy Window)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] blockSize
|
||
//| BYTE[4] (vendorID | 0x40000000)
|
||
//| BYTE # of items
|
||
//| # of items worth of item segments
|
||
//| BYTE[4] price
|
||
//| BYTE length of text description
|
||
//| BYTE[text length] item description
|
||
//|
|
||
//| NOTE: This packet is always preceded by a describe contents packet (0x3c)
|
||
//| with the container id as the (vendorID | 0x40000000) and then an open
|
||
//| container packet (0x24?) with the vendorID only and a model number of
|
||
//| 0x0030 (probably the model # for the buy screen)
|
||
//| NOTE: The client displays items in the buy window from top left to bottom
|
||
//| right. This means we need to sort the items logically before sending packets.
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPOpenBuyWindow::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 8 ); // start big, and work back down
|
||
pStream.WriteByte( 0, 0x74 );
|
||
}
|
||
CPOpenBuyWindow::CPOpenBuyWindow()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPOpenBuyWindow::CPOpenBuyWindow( CItem *container, CChar *vendorID, CPItemsInContainer& iic, CSocket *mSock )
|
||
{
|
||
if( ValidateObject( container ) )
|
||
{
|
||
InternalReset();
|
||
pStream.WriteLong( 3, container->GetSerial() );
|
||
CopyData( (*container), vendorID, iic, mSock );
|
||
}
|
||
}
|
||
|
||
UI08 CPOpenBuyWindow::NumberOfItems( void ) const
|
||
{
|
||
return pStream.GetByte( 7 );
|
||
}
|
||
|
||
void CPOpenBuyWindow::NumberOfItems( UI08 numItems )
|
||
{
|
||
// set the number of items
|
||
pStream.WriteByte( 7, numItems );
|
||
}
|
||
|
||
UI32 calcGoodValue( CTownRegion *tReg, CItem *i, UI32 value, bool isSelling );
|
||
UI32 calcValue( CItem *i, UI32 value );
|
||
void CPOpenBuyWindow::AddItem( CItem *toAdd, CTownRegion *tReg, UI16 &baseOffset )
|
||
{
|
||
UI32 value = toAdd->GetBuyValue();
|
||
if( cwmWorldState->ServerData()->RankSystemStatus() )
|
||
value = calcValue( toAdd, value );
|
||
if( cwmWorldState->ServerData()->TradeSystemStatus() )
|
||
value = calcGoodValue( tReg, toAdd, value, false );
|
||
|
||
std::string itemname;
|
||
itemname.reserve( MAX_NAME );
|
||
UI08 sLen = 0;
|
||
std::string temp = toAdd->GetName();
|
||
temp = strutil::simplify(temp);
|
||
if( temp.substr( 0, 1 ) == "#" )
|
||
{
|
||
itemname = strutil::number( 1020000 + toAdd->GetID() );
|
||
sLen = static_cast<UI08>(itemname.size() + 1);
|
||
}
|
||
else
|
||
{
|
||
sLen = static_cast<UI08>(getTileName( (*toAdd), itemname )); // Item name length, don't strip the nullptr (3D client doesn't like it)
|
||
}
|
||
|
||
pStream.ReserveSize( static_cast<size_t>(baseOffset) + 5 + static_cast<size_t>(sLen) );
|
||
pStream.WriteLong( baseOffset, value );
|
||
pStream.WriteByte( baseOffset += 4, sLen );
|
||
pStream.WriteString( baseOffset += 1, itemname, sLen );
|
||
baseOffset += sLen;
|
||
}
|
||
|
||
void CPOpenBuyWindow::CopyData( CItem& toCopy, CChar *vendorID, CPItemsInContainer& iic, CSocket *mSock )
|
||
{
|
||
UI08 itemCount = 0;
|
||
UI16 length = 8;
|
||
CTownRegion *tReg = nullptr;
|
||
if( cwmWorldState->ServerData()->TradeSystemStatus() && ValidateObject( vendorID ) )
|
||
tReg = calcRegionFromXY( vendorID->GetX(), vendorID->GetY(), vendorID->WorldNumber(), vendorID->GetInstanceID() );
|
||
|
||
SI16 baseY = 0, baseX = 0;
|
||
switch( toCopy.GetLayer() )
|
||
{
|
||
case IL_BUYCONTAINER: // buy layer
|
||
break;
|
||
case IL_BOUGHTCONTAINER: // bought layer
|
||
baseY = 100;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
GenericList< CItem * > *tcCont = toCopy.GetContainsList();
|
||
for( CItem *ctr = tcCont->First(); !tcCont->Finished(); ctr = tcCont->Next() )
|
||
{
|
||
if( ValidateObject( ctr ) )
|
||
{
|
||
if( !ctr->isFree() )
|
||
{
|
||
ctr->WalkXY( ++baseX, baseY );
|
||
if( baseX == 200 )
|
||
{
|
||
baseX = 0;
|
||
++baseY;
|
||
}
|
||
|
||
iic.AddItem( ctr, itemCount, mSock );
|
||
AddItem( ctr, tReg, length );
|
||
++itemCount;
|
||
}
|
||
}
|
||
}
|
||
iic.NumberOfItems( itemCount );
|
||
|
||
NumberOfItems( itemCount );
|
||
pStream.ReserveSize( length );
|
||
pStream.WriteShort( 1, length );
|
||
}
|
||
|
||
void CPOpenBuyWindow::Log( std::ofstream &outStream, bool fullHeader )
|
||
{
|
||
if( fullHeader )
|
||
outStream << "[SEND]Packet : CPOpenBuyWindow 0x74 --> Length: " << pStream.GetSize() << TimeStamp() << std::endl;
|
||
outStream << "Block size : " << pStream.GetUShort( 1 ) << std::endl;
|
||
outStream << "Vendor ID : " << std::hex << pStream.GetULong( 3 ) << std::endl;
|
||
outStream << "Number of Items: " << std::dec << (SI16)pStream.GetByte( 7 ) << std::endl;
|
||
SI32 baseOffset = 8;
|
||
for( UI32 x = 0; x < pStream.GetByte( 7 ); ++x )
|
||
{
|
||
outStream << " ITEM " << x << std::endl;
|
||
outStream << " Price: " << pStream.GetULong( baseOffset ) << std::endl;
|
||
baseOffset += 4;
|
||
outStream << " Len : " << (SI16)pStream.GetByte( baseOffset ) << std::endl;
|
||
outStream << " Name : ";
|
||
for( UI08 y = 0; y < pStream.GetByte( baseOffset ); ++y )
|
||
outStream << pStream.GetByte( static_cast<size_t>(baseOffset) + 1 + y );
|
||
baseOffset += pStream.GetByte( baseOffset ) + 1;
|
||
outStream << std::endl;
|
||
}
|
||
|
||
outStream << " Raw dump :" << std::endl;
|
||
|
||
CPUOXBuffer::Log( outStream, false );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPCharAndStartLoc()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet with character list / starting locations
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xA9 (Character List / Starting Locations)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] length
|
||
//| BYTE # of characters (5, 6 or 7 slots)
|
||
//| Following repeated for each character slot
|
||
//| BYTE[30] character name
|
||
//| BYTE[30] character password
|
||
//| BYTE number of starting locations
|
||
//| Repeat the following for as many locations as you have
|
||
//| if client version >= 7.0.13.0
|
||
//| BYTE locationIndex (0-based)
|
||
//| BYTE[32] city name(general name)
|
||
//| BYTE[32] area of city or town
|
||
//| BYTE[4] City X Coordinate
|
||
//| BYTE[4] City Y Coordinate
|
||
//| BYTE[4] City Z Coordinate
|
||
//| BYTE[4] CIty Map ( Probably Map ID same as in mul files have to make sure )
|
||
//| BYTE[4] Cliloc Description
|
||
//| BYTE[4] Always 0
|
||
//| if client version < 7.0.13.0
|
||
//| BYTE locationIndex (0-based)
|
||
//| BYTE[31] town (general name)
|
||
//| BYTE[31] exact name
|
||
//| BYTE[4] Flags
|
||
//| 0x01 = unknown
|
||
//| 0x02 = send config/req logout (IGR?)
|
||
//| 0x04 = single character (siege) (alternative seen, Limit Characters)
|
||
//| 0x08 = enable npcpopup/context menus
|
||
//| 0x10 = unknown, (alternative seen, single character)
|
||
//| 0x20 = enable common AOS features (tooltip thing/fight system book, but not AOS monsters/map/skills)
|
||
//| 0x40 = Sixth Character Slot?
|
||
//| 8x80 = Samurai Empire?
|
||
//| 0x100 = Elven race
|
||
//| 0x200 = KR support flag1
|
||
//| 0x400 = Send UO3D client type (client will send 0xE1 packet)
|
||
//| 0x800 = unknown
|
||
//| 0x1000 = 7th character slot, 2D client
|
||
//| 0x2000 = unknown (SA?)
|
||
//| 0x4000 = new movement packets 0xF0 -> 0xF2
|
||
//| 0x8000 = unlock new felucca areas (faction areas)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPCharAndStartLoc::Log( std::ofstream &outStream, bool fullHeader )
|
||
{
|
||
if( fullHeader )
|
||
outStream << "[SEND]Packet : CPCharAndStartLoc 0xA9 --> Length: " << pStream.GetSize() << TimeStamp() << std::endl;
|
||
outStream << "# Chars : " << (SI16)pStream.GetByte( 3 ) << std::endl;
|
||
outStream << "Characters --" << std::endl;
|
||
|
||
UI32 startLocOffset, realChars;
|
||
if( pStream.GetByte( 3 ) > 6 )
|
||
{
|
||
startLocOffset = 424;
|
||
realChars = 7;
|
||
}
|
||
else if( pStream.GetByte( 3 ) > 5 )
|
||
{
|
||
startLocOffset = 364;
|
||
realChars = 6;
|
||
}
|
||
else
|
||
{
|
||
startLocOffset = 304;
|
||
realChars = 5;
|
||
}
|
||
|
||
for( UI08 i = 0; i < realChars; ++i )
|
||
{
|
||
UI32 baseOffset = 4 + i * 60;
|
||
outStream << " Character " << (UI16)i << ":" << std::endl;
|
||
outStream << " Name: ";
|
||
for( UI08 j = 0; j < 30; ++j )
|
||
{
|
||
if( pStream.GetByte( static_cast<size_t>(baseOffset)+j ) != 0 )
|
||
outStream << (SI08)pStream.GetByte( static_cast<size_t>(baseOffset)+j );
|
||
else
|
||
break;
|
||
}
|
||
outStream << std::endl << " Pass: ";
|
||
for( UI08 k = 0; k < 30; ++k )
|
||
{
|
||
if( pStream.GetByte( static_cast<size_t>(baseOffset)+k+30 ) != 0 )
|
||
outStream << (SI08)pStream.GetByte( static_cast<size_t>(baseOffset)+k+30 );
|
||
else
|
||
break;
|
||
}
|
||
outStream << std::endl;
|
||
}
|
||
|
||
outStream << "# Starts : " << (SI16)pStream.GetByte( startLocOffset ) << std::endl;
|
||
outStream << "Starting locations --" << std::endl;
|
||
for( UI08 l = 0; l < pStream.GetByte( startLocOffset ); ++l )
|
||
{
|
||
UI32 baseOffset = startLocOffset + 1 + l * 63;
|
||
outStream << " Start " << (SI16)l << std::endl;
|
||
outStream << " Index : " << (SI16)pStream.GetByte( baseOffset ) << std::endl;
|
||
outStream << " General Name: ";
|
||
++baseOffset;
|
||
for( UI08 m = 0; m < 31; ++m )
|
||
{
|
||
if( pStream.GetByte( static_cast<size_t>(baseOffset)+m ) != 0 )
|
||
outStream << pStream.GetByte( static_cast<size_t>(baseOffset)+m );
|
||
else
|
||
break;
|
||
}
|
||
outStream << std::endl << " Exact Name : ";
|
||
baseOffset += 31;
|
||
for( UI08 n = 0; n < 31; ++n )
|
||
{
|
||
if( pStream.GetByte( static_cast<size_t>(baseOffset)+n ) != 0 )
|
||
outStream << pStream.GetByte( static_cast<size_t>(baseOffset)+n );
|
||
else
|
||
break;
|
||
}
|
||
outStream << std::endl;
|
||
}
|
||
UI16 lastByte = pStream.GetUShort( pStream.GetSize() - 2 );
|
||
outStream << "Flags : " << std::hex << (UI32)lastByte << std::dec << std::endl;
|
||
if( (lastByte&0x02) == 0x02 )
|
||
outStream << " : Send config/request logout" << std::endl;
|
||
if( (lastByte&0x04) == 0x04 )
|
||
outStream << " : Single character" << std::endl;
|
||
if( (lastByte&0x08) == 0x08 )
|
||
outStream << " : Enable NPC Popup menus" << std::endl;
|
||
if( (lastByte&0x10) == 0x10 )
|
||
outStream << " : Unknown" << std::endl;
|
||
if( (lastByte&0x20) == 0x20 )
|
||
outStream << " : Enable Common AoS features" << std::endl;
|
||
if( (lastByte&0x40) == 0x40 )
|
||
outStream << " : Enable Sixth Character Slot?" << std::endl;
|
||
if( (lastByte&0x80) == 0x80 )
|
||
outStream << " : Enable Samurai Empire?" << std::endl;
|
||
if( (lastByte&0x100) == 0x100 )
|
||
outStream << " : Enable Elves and ML?" << std::endl;
|
||
if( (lastByte&0x200) == 0x200 )
|
||
outStream << " : Enable KR support flag1" << std::endl;
|
||
if( (lastByte&0x400) == 0x400 )
|
||
outStream << " : Send UO3D client type" << std::endl;
|
||
if( (lastByte&0x800) == 0x800 )
|
||
outStream << " : Unknown" << std::endl;
|
||
if( (lastByte&0x1000) == 0x1000 )
|
||
outStream << " : Enable 7th character slot" << std::endl;
|
||
if( (lastByte&0x2000) == 0x2000 )
|
||
outStream << " : Unknown" << std::endl;
|
||
if( (lastByte&0x4000) == 0x4000 )
|
||
outStream << " : Enable new movement packets" << std::endl;
|
||
if( (lastByte&0x8000) == 0x8000 )
|
||
outStream << " : Enable new Felucca faction areas" << std::endl;
|
||
outStream << " Raw dump :" << std::endl;
|
||
CPUOXBuffer::Log( outStream, false );
|
||
}
|
||
|
||
void CPCharAndStartLoc::InternalReset( void )
|
||
{
|
||
packetSize = 0;
|
||
numCharacters = 0;
|
||
pStream.ReserveSize( 4 );
|
||
pStream.WriteByte( 0, 0xA9 );
|
||
}
|
||
|
||
CPCharAndStartLoc::CPCharAndStartLoc()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPCharAndStartLoc::CPCharAndStartLoc( CAccountBlock& actbBlock, UI08 numCharacters, UI08 numLocations, CSocket *mSock )
|
||
{
|
||
InternalReset();
|
||
|
||
UI08 noLoopBytes = 9;
|
||
if( mSock->ClientType() == CV_SA3D || mSock->ClientType() == CV_HS3D )
|
||
noLoopBytes = 11;
|
||
|
||
UI08 charSlots = 5;
|
||
if( cwmWorldState->ServerData()->GetServerFeature( SF_BIT_SIXCHARS ) &&
|
||
cwmWorldState->ServerData()->GetClientFeature( CF_BIT_SIXCHARS ) &&
|
||
mSock->ClientVersionMajor() >= 4 )
|
||
charSlots = 6;
|
||
if( cwmWorldState->ServerData()->GetServerFeature( SF_BIT_SEVENCHARS ) &&
|
||
cwmWorldState->ServerData()->GetClientFeature( CF_BIT_SEVENCHARS ) &&
|
||
mSock->ClientVersionMajor() >= 7 )
|
||
charSlots = 7;
|
||
|
||
if( mSock->ClientType() >= CV_HS2D && mSock->ClientVersionSub() >= 13 )
|
||
{
|
||
packetSize = (UI16)( noLoopBytes + ( charSlots * 60 ) + ( numLocations * 89 ));
|
||
pStream.ReserveSize( packetSize );
|
||
pStream.WriteShort( 1, packetSize );
|
||
pStream.WriteLong( packetSize - 4, static_cast<UI32>(cwmWorldState->ServerData()->GetServerFeatures() ));
|
||
}
|
||
else
|
||
{
|
||
packetSize = (UI16)( noLoopBytes + ( charSlots * 60 ) + ( numLocations * 63 ));
|
||
pStream.ReserveSize( packetSize );
|
||
pStream.WriteShort( 1, packetSize );
|
||
pStream.WriteLong( packetSize - 4, static_cast<UI32>(cwmWorldState->ServerData()->GetServerFeatures()) );
|
||
}
|
||
|
||
pStream.WriteByte( 3, charSlots );
|
||
|
||
if( mSock->ClientType() == CV_SA3D || mSock->ClientType() == CV_HS3D ) //maybe 7.0.13+ too?
|
||
pStream.WriteShort( packetSize - 2, 0x00 ); // last used character slot?
|
||
|
||
CopyData( actbBlock );
|
||
NumberOfLocations( numLocations, mSock );
|
||
}
|
||
|
||
void CPCharAndStartLoc::CopyData( CAccountBlock& toCopy )
|
||
{
|
||
UI16 baseOffset = 0;
|
||
for( UI08 i = 0; i < pStream.GetByte( 3 ); ++i )
|
||
{
|
||
baseOffset = (UI16)(4 + ( i * 60 ));
|
||
if( toCopy.lpCharacters[i] != nullptr )
|
||
AddCharacter( toCopy.lpCharacters[i], i );
|
||
else
|
||
{
|
||
pStream.WriteString( baseOffset, "", 60 );
|
||
}
|
||
}
|
||
}
|
||
|
||
void CPCharAndStartLoc::AddCharacter( CChar *toAdd, UI08 charOffset )
|
||
{
|
||
UI16 baseOffset = 0;
|
||
baseOffset = (UI16)(4 + ( charOffset * 60 ));
|
||
if( !ValidateObject( toAdd ) )
|
||
{
|
||
pStream.WriteString( baseOffset, "", 60 );
|
||
return;
|
||
}
|
||
pStream.WriteString( baseOffset, toAdd->GetName(), 60 );
|
||
}
|
||
|
||
void CPCharAndStartLoc::NumberOfLocations( UI08 numLocations, CSocket *mSock )
|
||
{
|
||
UI16 byteOffset = 0;
|
||
byteOffset = (UI16)( 4 + ( pStream.GetByte( 3 ) * 60 ));
|
||
pStream.WriteByte( byteOffset, numLocations );
|
||
}
|
||
|
||
void CPCharAndStartLoc::AddStartLocation( LPSTARTLOCATION sLoc, UI08 locOffset )
|
||
{
|
||
if( sLoc == nullptr )
|
||
return;
|
||
|
||
UI16 baseOffset = 0;
|
||
baseOffset = (UI16)( 5 + ( pStream.GetByte( 3 ) * 60 ));
|
||
baseOffset += ( locOffset * 63 );
|
||
|
||
pStream.WriteByte( baseOffset, locOffset ); // StartLocation #
|
||
pStream.WriteString( static_cast<size_t>(baseOffset)+1, sLoc->oldTown, 31 );
|
||
pStream.WriteString( static_cast<size_t>(baseOffset)+33, sLoc->oldDescription, 31 );
|
||
}
|
||
|
||
void CPCharAndStartLoc::NewAddStartLocation( LPSTARTLOCATION sLoc, UI08 locOffset )
|
||
{
|
||
if( sLoc == nullptr )
|
||
return;
|
||
|
||
UI16 baseOffset = 0;
|
||
baseOffset = (UI16)( 5 + ( pStream.GetByte( 3 ) * 60 ));
|
||
baseOffset += ( locOffset * 89 );
|
||
|
||
pStream.WriteByte( baseOffset, locOffset ); // StartLocation #
|
||
pStream.WriteString( static_cast<size_t>(baseOffset)+1, sLoc->newTown, 32 );
|
||
pStream.WriteString( static_cast<size_t>(baseOffset)+33, sLoc->newDescription, 32 );
|
||
pStream.WriteLong( static_cast<size_t>(baseOffset)+65, sLoc->x );
|
||
pStream.WriteLong( static_cast<size_t>(baseOffset)+69, sLoc->y );
|
||
pStream.WriteLong( static_cast<size_t>(baseOffset)+73, sLoc->z );
|
||
pStream.WriteLong( static_cast<size_t>(baseOffset)+77, sLoc->worldNum );
|
||
pStream.WriteLong( static_cast<size_t>(baseOffset)+81, sLoc->clilocDesc );
|
||
pStream.WriteLong( static_cast<size_t>(baseOffset)+85, 0x00 );
|
||
}
|
||
|
||
CPCharAndStartLoc& CPCharAndStartLoc::operator=( CAccountBlock& actbBlock )
|
||
{
|
||
CopyData(actbBlock);
|
||
return (*this);
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPKAccept()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to acknowledge login of custom (Krrios') client
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xF0 (Custom client packet)
|
||
//| Size: 5
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] len
|
||
//| BYTE subcmd
|
||
//| BYTE[len - 4] data
|
||
//|
|
||
//| Subcommand 0: Acknowledge login (1 byte (for 5 total))
|
||
//| BYTE ack
|
||
//| 0 = Rejected, unauthorized
|
||
//| 1 = Accepted, GM priviledges
|
||
//| 2 = Accepted, player priviledges
|
||
//|
|
||
//| Note: GM priviledges unlock movement rate configurations
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPKAccept::CPKAccept( UI08 Response )
|
||
{
|
||
pStream.ReserveSize( 5 );
|
||
pStream.WriteByte( 0, 0xF0 );
|
||
pStream.WriteShort( 1, 0x0005 );
|
||
pStream.WriteByte( 3, 0x00 );
|
||
pStream.WriteByte( 4, Response );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPUpdScroll()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to display tips/notice window with message on login
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xA6 (Tips/Notice window)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] blockSize
|
||
//| BYTE flag
|
||
//| 0x00 - tips window
|
||
//| 0x01 - notice window
|
||
//| 0x02 - update window
|
||
//| BYTE[2] unknown1
|
||
//| BYTE[2] tip #
|
||
//| BYTE[2] msgSize
|
||
//| BYTE[msgSize] message (ascii, not null terminated)
|
||
//| OR BYTE[?] message (? = blockSize - 10)
|
||
//|
|
||
//| *Null terminated I think (Gimli)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPUpdScroll::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 10 ); // 10, not 11
|
||
pStream.WriteByte( 0, 0xA6 );
|
||
pStream.WriteByte( 1, 0x01 );
|
||
pStream.WriteByte( 2, 0x02 );
|
||
pStream.WriteByte( 3, 0x02 );
|
||
pStream.WriteByte( 4, 0x00 );
|
||
pStream.WriteByte( 5, 0x00 );
|
||
pStream.WriteByte( 6, 0x00 );
|
||
pStream.WriteByte( 7, 0x00 );
|
||
pStream.WriteByte( 8, 0x01 );
|
||
pStream.WriteByte( 9, 0x02 );
|
||
memset( tipData, 0, 2048 );
|
||
}
|
||
CPUpdScroll::CPUpdScroll()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPUpdScroll::CPUpdScroll( UI08 tType )
|
||
{
|
||
InternalReset();
|
||
TipType( tType );
|
||
}
|
||
CPUpdScroll::CPUpdScroll( UI08 tType, UI08 tNum )
|
||
{
|
||
InternalReset();
|
||
TipType( tType );
|
||
TipNumber( tNum );
|
||
}
|
||
|
||
void CPUpdScroll::AddString( const char *toAdd )
|
||
{
|
||
strcat( tipData, toAdd );
|
||
}
|
||
void CPUpdScroll::TipType( UI08 tType )
|
||
{
|
||
pStream.WriteByte( 3, tType );
|
||
}
|
||
void CPUpdScroll::TipNumber( UI08 tipNum )
|
||
{
|
||
pStream.WriteByte( 7, tipNum );
|
||
}
|
||
void CPUpdScroll::Finalize( void )
|
||
{
|
||
size_t y = strlen( tipData ) + 10;
|
||
SetLength( static_cast< UI16 >(y) );
|
||
pStream.WriteString( 10, tipData, y-10 );
|
||
}
|
||
|
||
void CPUpdScroll::SetLength( UI16 len )
|
||
{
|
||
pStream.ReserveSize( len );
|
||
pStream.WriteShort( 1, len );
|
||
pStream.WriteShort( 8, (len - 10) );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPGraphicalEffect2()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to display graphical effect in client
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xC0 (Graphical Effect 2)
|
||
//| Size: 36 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE type
|
||
//| BYTE[4] sourceSerial
|
||
//| BYTE[4] targetSerial
|
||
//| BYTE[2] itemID
|
||
//| BYTE[2] xSource
|
||
//| BYTES[2] ySource
|
||
//| BYTE zSource
|
||
//| BYTE[2] xTarget
|
||
//| BYTE[2] yTarget
|
||
//| BYTE zTarget
|
||
//| BYTE speed
|
||
//| BYTE duration
|
||
//| BYTE[2] unk // On OSI, flamestrikes are 0x0100
|
||
//| BYTE fixedDirection
|
||
//| BYTE explodes
|
||
//| BYTE[4] hue
|
||
//| BYTE[4] renderMode
|
||
//| Rendermode details:
|
||
//| One Darken
|
||
//| Two Lighten
|
||
//| Three Transparent to dark colors to emphasize the bright colors
|
||
//| Four Semi-transparent (high transparency)
|
||
//| Five Translucent (near the present)
|
||
//| Six "Negapoji" (???) reversal
|
||
//| Seven Invert background "Negapoji Keta"(???)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPGraphicalEffect2::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 36 );
|
||
pStream.WriteByte( 0, 0xC0 );
|
||
pStream.WriteShort( 24, 0x0000 );
|
||
}
|
||
CPGraphicalEffect2::CPGraphicalEffect2( UI08 effectType ) : CPGraphicalEffect( effectType )
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPGraphicalEffect2::CPGraphicalEffect2( UI08 effectType, CBaseObject &src, CBaseObject &trg ) : CPGraphicalEffect( effectType, src, trg )
|
||
{
|
||
InternalReset();
|
||
Effect( effectType );
|
||
SourceSerial( src );
|
||
TargetSerial( trg );
|
||
}
|
||
CPGraphicalEffect2::CPGraphicalEffect2( UI08 effectType, CBaseObject &src ) : CPGraphicalEffect( effectType, src )
|
||
{
|
||
InternalReset();
|
||
Effect( effectType );
|
||
SourceSerial( src );
|
||
}
|
||
void CPGraphicalEffect2::Hue( UI32 hue )
|
||
{
|
||
pStream.WriteLong( 28, hue );
|
||
}
|
||
void CPGraphicalEffect2::RenderMode( UI32 mode )
|
||
{
|
||
pStream.WriteLong( 32, mode );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPMapRelated()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet responding to client requests for map actions
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x56 (Map Related Actions)
|
||
//| Size: 11 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[4] map object serial
|
||
//| BYTE map action
|
||
//| 1 = add map point,
|
||
//| 2 = add new pin with pin number. (insertion. other pins after the number are pushed back.)
|
||
//| 3 = change pin
|
||
//| 4 = remove pin
|
||
//| 5 = remove all pins on the map
|
||
//| 6 = toggle the 'editable' state of the map.
|
||
//| 7 = return msg from the server to the request 6 of the client.
|
||
//| BYTE plotting state (1=on, 0=off, valid only if command 7)
|
||
//| BYTE[2] x location (relative to upper left corner of the map, in pixels, for points)
|
||
//| BYTE[2] y location (relative to upper left corner of the map, in pixels, for points)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPMapRelated::CPMapRelated()
|
||
{
|
||
pStream.ReserveSize( 11 );
|
||
pStream.WriteByte( 0, 0x56 );
|
||
}
|
||
void CPMapRelated::PlotState( UI08 pState )
|
||
{
|
||
pStream.WriteByte( 6, pState );
|
||
}
|
||
void CPMapRelated::Location( SI16 x, SI16 y )
|
||
{
|
||
pStream.WriteShort( 7, x );
|
||
pStream.WriteShort( 9, y );
|
||
}
|
||
void CPMapRelated::Command( UI08 cmd )
|
||
{
|
||
pStream.WriteByte( 5, cmd );
|
||
}
|
||
void CPMapRelated::ID( SERIAL key )
|
||
{
|
||
pStream.WriteLong( 1, key );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPDrawObject()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet with instructions for client to draw an object
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x78 (Draw object)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] blockSize
|
||
//| BYTE[4] item/character serial
|
||
//| BYTE[2] model (item hex #)
|
||
//| if (itemID & 0x80000000)
|
||
//| BYTE[2] amount/Corpse Model Num
|
||
//| BYTE[2] xLoc (only 15 lsb)
|
||
//| BYTE[2] yLoc
|
||
//| if (xLoc & 0x8000)
|
||
//| BYTE direction
|
||
//| BYTE zLoc
|
||
//| BYTE direction
|
||
//| BYTE[2] dye/skin color
|
||
//| BYTE flag
|
||
//| BYTE notoriety (2's complement signed)
|
||
//| if (BYTE[4] == 0x00 0x00 0x00 0x00)
|
||
//| DONE
|
||
//| else loop this until above if statement is satisified
|
||
//| BYTE[4] item serial
|
||
//| BYTE[2] model (item hex # - only 15 lsb)
|
||
//| BYTE layer
|
||
//| if (model & 0x8000)
|
||
//| BYTE[2] hue
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPDrawObject::CPDrawObject()
|
||
{
|
||
InternalReset();
|
||
}
|
||
|
||
CPDrawObject::CPDrawObject( CChar &mChar )
|
||
{
|
||
InternalReset();
|
||
CopyData( mChar );
|
||
}
|
||
|
||
void CPDrawObject::InternalReset( void )
|
||
{
|
||
SetLength( 19 );
|
||
pStream.WriteByte( 0, 0x78 );
|
||
}
|
||
|
||
void CPDrawObject::SetLength( UI16 len )
|
||
{
|
||
pStream.ReserveSize( len );
|
||
pStream.WriteShort( 1, len );
|
||
curLen = len;
|
||
}
|
||
|
||
void CPDrawObject::Finalize( void )
|
||
{
|
||
UI16 cPos = curLen;
|
||
SetLength( curLen + 4 );
|
||
pStream.WriteLong( cPos, static_cast<UI32>(0) );
|
||
}
|
||
|
||
void CPDrawObject::AddItem( CItem *toAdd, bool alwaysAddItemHue )
|
||
{
|
||
UI16 cPos = curLen;
|
||
|
||
pStream.WriteLong( cPos, toAdd->GetSerial() );
|
||
pStream.WriteShort( cPos+=4, toAdd->GetID() );
|
||
pStream.WriteByte( cPos+=2, toAdd->GetLayer() );
|
||
|
||
if( alwaysAddItemHue )
|
||
{
|
||
// Always send color to clients 7.0.33.1 and above
|
||
pStream.WriteShort( cPos+=1, toAdd->GetColour() );
|
||
SetLength( curLen + 9 );
|
||
}
|
||
else
|
||
{
|
||
// Send color to clients below 7.0.33.1 only if different than default
|
||
bool bColour = ( toAdd->GetColour() != 0 );
|
||
if( bColour )
|
||
{
|
||
SetLength( curLen + 9 );
|
||
pStream.WriteByte( cPos-2, pStream.GetByte( cPos-2 ) | 0x80 );
|
||
pStream.WriteShort( ++cPos, toAdd->GetColour() );
|
||
}
|
||
else
|
||
{
|
||
SetLength( curLen + 7 );
|
||
}
|
||
}
|
||
}
|
||
|
||
void CPDrawObject::SetRepFlag( UI08 value )
|
||
{
|
||
pStream.WriteByte( 18, value );
|
||
}
|
||
|
||
void CPDrawObject::CopyData( CChar& mChar )
|
||
{
|
||
pStream.WriteLong( 3, mChar.GetSerial() );
|
||
pStream.WriteShort( 7, mChar.GetID() );
|
||
pStream.WriteShort( 9, mChar.GetX() );
|
||
pStream.WriteShort( 11, mChar.GetY() );
|
||
pStream.WriteByte( 13, mChar.GetZ() );
|
||
pStream.WriteByte( 14, mChar.GetDir() );
|
||
pStream.WriteShort( 15, mChar.GetSkin() );
|
||
|
||
// 0 0x01 - Frozen/Invulnerable
|
||
// 1 0x02 - Female/Dead
|
||
// 2 0x04 . Flying/Poisoned
|
||
// 3 0x08 - Yellow Healthbar
|
||
// 4 0x10 - Ignore Mobiles
|
||
// 5 0x20 - ???
|
||
// 6 0x40 - War Mode
|
||
// 7 0x80 - Hidden
|
||
|
||
std::bitset< 8 > flag( 0 );
|
||
|
||
if( cwmWorldState->ServerData()->ClientSupport7000() || cwmWorldState->ServerData()->ClientSupport7090() ||
|
||
cwmWorldState->ServerData()->ClientSupport70160() || cwmWorldState->ServerData()->ClientSupport70240() ||
|
||
cwmWorldState->ServerData()->ClientSupport70300() || cwmWorldState->ServerData()->ClientSupport70331() ||
|
||
cwmWorldState->ServerData()->ClientSupport704565() || cwmWorldState->ServerData()->ClientSupport70610() )
|
||
{
|
||
// Clients 7.0.0.0 and later
|
||
const UI08 BIT_FROZEN = 0; // 0x01, frozen/paralyzed
|
||
const UI08 BIT_FEMALE = 1; // 0x02, female
|
||
const UI08 BIT_FLYING = 2; // 0x04, flying (post 7.0.0.0)
|
||
//const UI08 BIT_GOLDEN = 4; // 0x08, yellow healthbar
|
||
//const UI08 BIT_IGNOREMOBILES = 5; // 0x10, ignore other mobiles?
|
||
|
||
flag.set( BIT_FROZEN, mChar.IsFrozen() );
|
||
flag.set( BIT_FEMALE, ( mChar.GetID() == 0x0191 || mChar.GetID() == 0x025E ) );
|
||
flag.set( BIT_FLYING, mChar.IsFlying() );
|
||
}
|
||
else
|
||
{
|
||
// Clients below 7.0.0.0
|
||
const UI08 BIT_INVUL = 0; // 0x01, invulnerable
|
||
const UI08 BIT_DEAD = 1; // 0x02, dead
|
||
const UI08 BIT_POISON = 2; // 0x04, poison
|
||
//const UI08 BIT_GOLDEN = 4; // 0x08, yellow healthbar
|
||
//const UI08 BIT_IGNOREMOBILES = 5; // 0x10, ignore other mobiles?
|
||
|
||
flag.set( BIT_INVUL, mChar.IsInvulnerable() );
|
||
flag.set( BIT_DEAD, mChar.IsDead() );
|
||
flag.set( BIT_POISON, ( mChar.GetPoisoned() != 0 ) );
|
||
}
|
||
|
||
const UI08 BIT_ATWAR = 6; // 0x40
|
||
const UI08 BIT_OTHER = 7; // 0x80
|
||
flag.set( BIT_ATWAR, mChar.IsAtWar() );
|
||
flag.set( BIT_OTHER, ( ( !mChar.IsNpc() && !isOnline( mChar ) ) || ( mChar.GetVisible() != VT_VISIBLE ) || ( mChar.IsDead() && !mChar.IsAtWar() ) ) );
|
||
|
||
pStream.WriteByte( 17, static_cast<UI08>( flag.to_ulong() ) );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPCorpseClothing()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet with items to equip on corpse
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x89 (Corpse Clothing)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] blockSize
|
||
//| BYTE[4] corpse serial
|
||
//| Then, for each item to equip on corpse, loop this:
|
||
//| BYTE itemLayer
|
||
//| BYTE[4] itemID
|
||
//| BYTE terminator (0x00)
|
||
//|
|
||
//| Followed by a 0x3C message with the contents.
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPCorpseClothing::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 7 );
|
||
pStream.WriteByte( 0, 0x89 );
|
||
}
|
||
CPCorpseClothing::CPCorpseClothing()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPCorpseClothing::CPCorpseClothing( CItem *corpse )
|
||
{
|
||
InternalReset();
|
||
if( ValidateObject( corpse ) )
|
||
CopyData( (*corpse) );
|
||
}
|
||
|
||
void CPCorpseClothing::NumberOfItems( UI16 numItems )
|
||
{
|
||
// knowing the number of items, set the packet size
|
||
UI16 packetSize = (UI16)((numItems * 5) + 8); // 7 for lead in, 1 for lead out
|
||
pStream.ReserveSize( packetSize );
|
||
pStream.WriteShort( 1, packetSize );
|
||
pStream.WriteByte( packetSize - 1, 0x00 );
|
||
}
|
||
void CPCorpseClothing::AddItem( CItem *toAdd, UI16 itemNum )
|
||
{
|
||
UI16 baseOffset = (UI16)(7 + itemNum * 5);
|
||
pStream.WriteByte( baseOffset, toAdd->GetLayer() );
|
||
pStream.WriteLong( static_cast<size_t>(baseOffset) + 1, toAdd->GetSerial() );
|
||
}
|
||
CPCorpseClothing& CPCorpseClothing::operator=( CItem& corpse )
|
||
{
|
||
CopyData( corpse );
|
||
return (*this);
|
||
}
|
||
|
||
void CPCorpseClothing::CopyData( CItem& toCopy )
|
||
{
|
||
pStream.WriteLong( 3, toCopy.GetSerial() );
|
||
UI16 itemCount = 0;
|
||
GenericList< CItem * > *tcCont = toCopy.GetContainsList();
|
||
for( CItem *ctr = tcCont->First(); !tcCont->Finished(); ctr = tcCont->Next() )
|
||
{
|
||
if( ValidateObject( ctr ) )
|
||
{
|
||
if( !ctr->isFree() && ctr->GetLayer() )
|
||
{
|
||
pStream.ReserveSize( pStream.GetSize() + 5 );
|
||
AddItem( ctr, itemCount );
|
||
++itemCount;
|
||
}
|
||
}
|
||
}
|
||
NumberOfItems( itemCount );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPNewObjectInfo()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet with object information (new version)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xF3 (New Object Information)
|
||
//| Size: 24 bytes SA, 26 bytes HS
|
||
//|
|
||
//| Packet Build
|
||
//| Byte cmd
|
||
//| Byte[2] 0x1 // always 0x1 on OSI
|
||
//| Byte DataType // 0x00 = Item , 0x01 = Character, 0x02 = Multi
|
||
//| Byte[4] Serial
|
||
//| Byte[2] Object ID // for multi its same value as the multi has in multi.mul
|
||
//| Byte Offset/Facing // 0x00 if Multi
|
||
//| Byte[2] Amount // 0x1 if Multi
|
||
//| Byte[2] Amount // 0x1 if Multi , no idea why Amount is sent 2 times
|
||
//| Byte[2] X
|
||
//| Byte[2] Y
|
||
//| Byte Z
|
||
//| Byte Layer // 0x00 if Multi / Light Level (TileData.Quality or 0 for Mobiles)
|
||
//| Byte[2] Color // 0x00 if Multi
|
||
//| Byte Flag // 0x20 = Movable if normally not , 0x80 = Hidden , 0x00 if Multi
|
||
//| IF ITEM
|
||
//| Byte[2] Access (for items only, 0x01 = Player Item, 0x00 = World Item)
|
||
//|
|
||
//| Notes
|
||
//| Replaces 0x1A packet for clients 7.0.0.0+
|
||
//| Required to to display items with IDs over 0x3FFF (old 0x1A packet only works with items up to that ID)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPNewObjectInfo::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 24 );
|
||
pStream.WriteByte( 0, 0xF3 );
|
||
pStream.WriteShort( 1, 0x1 );
|
||
}
|
||
void CPNewObjectInfo::CopyData( CItem& mItem, CChar& mChar )
|
||
{
|
||
if( mItem.CanBeObjType( OT_MULTI ) )
|
||
CopyMultiData( static_cast<CMultiObj&>(mItem), mChar );
|
||
else if( mItem.CanBeObjType( OT_ITEM ) )
|
||
CopyItemData( mItem, mChar );
|
||
}
|
||
|
||
void CPNewObjectInfo::CopyItemData( CItem &mItem, CChar &mChar )
|
||
{
|
||
bool isInvisible = (mItem.GetVisible() != VT_VISIBLE);
|
||
bool isMovable = (mItem.GetMovable() == 1 || mChar.AllMove() || ( mItem.IsLockedDown() && &mChar == mItem.GetOwnerObj() ));
|
||
|
||
if( mItem.isDamageable() )
|
||
pStream.WriteByte( 3, 0x03 ); // Damageable Item DataType
|
||
else
|
||
pStream.WriteByte( 3, 0x00 ); // Item DataType
|
||
|
||
pStream.WriteLong( 4, mItem.GetSerial() ); //Serial
|
||
|
||
// if player is a gm, this item is shown like a candle (so that he can move it),
|
||
// ....if not, the item is a normal invisible light source!
|
||
if( mChar.IsGM() && mItem.GetID() == 0x1647 )
|
||
pStream.WriteShort( 8, 0x0A0F );
|
||
else
|
||
pStream.WriteShort( 8, mItem.GetID() );
|
||
|
||
pStream.WriteByte( 10, 0x00 ); //Offset/Facing?
|
||
|
||
//Amount
|
||
pStream.WriteShort( 11, mItem.GetAmount() );
|
||
pStream.WriteShort( 13, mItem.GetAmount() );
|
||
|
||
//Location
|
||
pStream.WriteShort( 15, mItem.GetX() );
|
||
pStream.WriteShort( 17, mItem.GetY() );
|
||
pStream.WriteByte( 19, mItem.GetZ() );
|
||
|
||
//Direction/Light Level
|
||
pStream.WriteByte( 20, mItem.GetDir() );
|
||
|
||
if( mChar.IsGM() && mItem.GetID() == 0x1647 ) //Lightsource
|
||
pStream.WriteShort( 21, 0x00C6 );
|
||
else
|
||
pStream.WriteShort( 21, mItem.GetColour() );
|
||
|
||
//Flags
|
||
if( isInvisible )
|
||
pStream.WriteByte( 23, (pStream.GetByte( 23 ) | 0x80) );
|
||
if( isMovable )
|
||
pStream.WriteByte( 23, (pStream.GetByte( 23 ) | 0x20) );
|
||
|
||
if( mChar.GetSocket()->ClientType() >= CV_HS2D )
|
||
{
|
||
pStream.ReserveSize( 26 );
|
||
pStream.WriteShort( 24, 0x00 ); // HS requires 2 extra bytes
|
||
}
|
||
}
|
||
|
||
void CPNewObjectInfo::CopyMultiData( CMultiObj& mMulti, CChar &mChar )
|
||
{
|
||
pStream.WriteByte( 3, 0x02 ); //DataType
|
||
pStream.WriteLong( 4, mMulti.GetSerial() ); //Serial
|
||
|
||
SI32 itemID = mMulti.GetID();
|
||
itemID &= 0x3FFF;
|
||
if( mChar.ViewHouseAsIcon() )
|
||
pStream.WriteShort( 8, 0x14F0 );
|
||
else
|
||
pStream.WriteShort( 8, itemID ); //Client wants real ID from multi
|
||
|
||
pStream.WriteByte( 10, 0 ); //Offset/Facing? 0 or 0x00 - does it matter?
|
||
|
||
//Amount
|
||
pStream.WriteShort( 11, 1 ); //1 or 0x1 - does it matter? can multis have amounts higher than 1?
|
||
pStream.WriteShort( 13, 1 ); //1 or 0x1 - does it matter? can multis have amounts higher than 1?
|
||
|
||
//Location
|
||
pStream.WriteShort( 15, mMulti.GetX() );
|
||
pStream.WriteShort( 17, mMulti.GetY() );
|
||
pStream.WriteByte( 19, mMulti.GetZ() );
|
||
|
||
//Light Level
|
||
pStream.WriteByte( 20, 0x00 );
|
||
|
||
pStream.WriteShort( 21, mMulti.GetColour() ); //Central mast on classic boats can have a color, apparently!
|
||
|
||
//Flags
|
||
pStream.WriteByte( 23, 0x00 );
|
||
|
||
if( mChar.GetSocket()->ClientType() >= CV_HS2D ) //required for boats to work properly?
|
||
{
|
||
pStream.ReserveSize( 26 );
|
||
pStream.WriteShort( 24, 0x00 ); // HS requires 2 extra bytes
|
||
}
|
||
}
|
||
|
||
CPNewObjectInfo::CPNewObjectInfo()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPNewObjectInfo::CPNewObjectInfo( CItem& mItem, CChar& mChar )
|
||
{
|
||
InternalReset();
|
||
CopyData( mItem, mChar );
|
||
}
|
||
|
||
void CPNewObjectInfo::Objects( CItem& mItem, CChar& mChar )
|
||
{
|
||
CopyData( mItem, mChar );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPObjectInfo()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet with object information (old version)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x1A (Object Information)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] blockSize
|
||
//| BYTE[4] itemID
|
||
//| BYTE[2] model #
|
||
//| if (itemID & 0x80000000)
|
||
//| BYTE[2] item count (or model # for corpses)
|
||
//| if (model & 0x8000)
|
||
//| BYTE Incr Counter (increment model by this #)
|
||
//| BYTE[2] xLoc (only use lowest significant 15 bits)
|
||
//| BYTE[2] yLoc
|
||
//| if (xLoc & 0x8000)
|
||
//| BYTE direction
|
||
//| BYTE zLoc
|
||
//| if (yLoc & 0x8000)
|
||
//| BYTE[2] dye
|
||
//| if (yLoc & 0x4000)
|
||
//| BYTE flag byte
|
||
//|
|
||
//| Flag:
|
||
//| 0x00 - None
|
||
//| 0x02 - Female
|
||
//| 0x04 - Poisoned
|
||
//| 0x08 - YellowHits // healthbar gets yellow
|
||
//| 0x10 - FactionShip // unsure why client needs to know
|
||
//| 0x20 - Movable if normally not
|
||
//| 0x40 - War mode
|
||
//| 0x80 - Hidden
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPObjectInfo::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 16 );
|
||
pStream.WriteByte( 0, 0x1A );
|
||
pStream.WriteShort( 1, 16 );
|
||
}
|
||
void CPObjectInfo::CopyData( CItem& mItem, CChar& mChar )
|
||
{
|
||
pStream.WriteLong( 3, mItem.GetSerial() );
|
||
|
||
if( mItem.CanBeObjType( OT_MULTI ) )
|
||
CopyMultiData( static_cast<CMultiObj&>(mItem), mChar );
|
||
else if( mItem.CanBeObjType( OT_ITEM ) )
|
||
CopyItemData( mItem, mChar );
|
||
}
|
||
|
||
void CPObjectInfo::CopyItemData( CItem &mItem, CChar &mChar )
|
||
{
|
||
if( mItem.isPileable() || mItem.isCorpse() )
|
||
{
|
||
pStream.ReserveSize( 18 );
|
||
pStream.WriteByte( 2, 18 );
|
||
pStream.WriteByte( 3, (pStream.GetByte( 3 ) | 0x80) ); // Enable piles
|
||
}
|
||
|
||
bool isInvisible = (mItem.GetVisible() != VT_VISIBLE);
|
||
bool isMovable = (mItem.GetMovable() == 1 || mChar.AllMove() || ( mItem.IsLockedDown() && &mChar == mItem.GetOwnerObj() ));
|
||
// if player is a gm, this item
|
||
// is shown like a candle (so that he can move it),
|
||
// ....if not, the item is a normal
|
||
// invisible light source!
|
||
if( mChar.IsGM() && mItem.GetID() == 0x1647 )
|
||
pStream.WriteShort( 7, 0x0A0F );
|
||
else
|
||
pStream.WriteShort( 7, mItem.GetID() );
|
||
|
||
UI08 byteNum = 7;
|
||
if( mItem.isPileable() || mItem.isCorpse() )
|
||
pStream.WriteShort( byteNum+=2, mItem.GetAmount() );
|
||
|
||
pStream.WriteShort( byteNum+=2, mItem.GetX() );
|
||
if( isInvisible || isMovable )
|
||
{
|
||
pStream.WriteShort( byteNum+=2, (mItem.GetY() | 0xC000) );
|
||
pStream.ReserveSize( pStream.GetSize()+1 );
|
||
pStream.WriteByte( 2, pStream.GetByte( 2 ) + 1 );
|
||
}
|
||
else
|
||
pStream.WriteShort( byteNum+=2, (mItem.GetY() | 0x8000) );
|
||
if( mItem.GetDir() )
|
||
{
|
||
pStream.ReserveSize( pStream.GetSize()+1 );
|
||
pStream.WriteByte( byteNum-2, (pStream.GetByte( byteNum-2 ) | 0x80) ); // Enable direction
|
||
pStream.WriteByte( byteNum+=2, mItem.GetDir() );
|
||
pStream.WriteByte( ++byteNum, mItem.GetZ() );
|
||
pStream.WriteByte( 2, pStream.GetByte( 2 ) + 1 );
|
||
}
|
||
else
|
||
pStream.WriteByte( byteNum+=2, mItem.GetZ() );
|
||
|
||
if( mChar.IsGM() && mItem.GetID() == 0x1647 )
|
||
pStream.WriteShort( ++byteNum, 0x00C6 );
|
||
else
|
||
pStream.WriteShort( ++byteNum, mItem.GetColour() );
|
||
|
||
if( isInvisible || isMovable )
|
||
{
|
||
pStream.WriteByte( byteNum+=2, 0x00 );
|
||
if( isInvisible )
|
||
pStream.WriteByte( byteNum, (pStream.GetByte( byteNum ) | 0x80) );
|
||
|
||
if( isMovable )
|
||
pStream.WriteByte( byteNum, (pStream.GetByte( byteNum ) | 0x20) );
|
||
}
|
||
}
|
||
|
||
void CPObjectInfo::CopyMultiData( CMultiObj& mMulti, CChar &mChar )
|
||
{
|
||
bool isInvisible = (mMulti.GetVisible() != VT_VISIBLE);
|
||
bool isMovable = (mChar.AllMove());
|
||
|
||
if( mChar.ViewHouseAsIcon() )
|
||
pStream.WriteShort( 7, 0x14F0 );
|
||
else
|
||
pStream.WriteShort( 7, mMulti.GetID() );
|
||
|
||
pStream.WriteShort( 9, mMulti.GetX() );
|
||
if( isInvisible || isMovable )
|
||
{
|
||
pStream.WriteShort( 11, (mMulti.GetY() | 0xC000) );
|
||
pStream.ReserveSize( pStream.GetSize()+1 );
|
||
pStream.WriteByte( 2, pStream.GetByte( 2 ) + 1 );
|
||
}
|
||
else
|
||
pStream.WriteShort( 11, (mMulti.GetY() | 0x8000) );
|
||
UI08 byteNum = 12;
|
||
if( mMulti.GetDir() )
|
||
{
|
||
pStream.ReserveSize( pStream.GetSize()+1 );
|
||
pStream.WriteByte( 2, pStream.GetByte( 2 ) + 1 );
|
||
pStream.WriteByte( 9, (pStream.GetByte( 9 ) | 0x80) ); // Enable direction
|
||
pStream.WriteByte( ++byteNum, mMulti.GetDir() );
|
||
}
|
||
pStream.WriteByte( ++byteNum, mMulti.GetZ() );
|
||
|
||
pStream.WriteShort( ++byteNum, mMulti.GetColour() );
|
||
if( isInvisible || isMovable )
|
||
{
|
||
pStream.WriteByte( ++byteNum, 0x00 );
|
||
if( isInvisible )
|
||
pStream.WriteByte( byteNum, (pStream.GetByte( byteNum ) | 0x80) );
|
||
|
||
if( isMovable )
|
||
pStream.WriteByte( byteNum, (pStream.GetByte( byteNum ) | 0x20) );
|
||
}
|
||
}
|
||
|
||
CPObjectInfo::CPObjectInfo()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPObjectInfo::CPObjectInfo( CItem& mItem, CChar& mChar )
|
||
{
|
||
InternalReset();
|
||
CopyData( mItem, mChar );
|
||
}
|
||
|
||
void CPObjectInfo::Objects( CItem& mItem, CChar& mChar )
|
||
{
|
||
CopyData( mItem, mChar );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPUnicodeSpeech()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet with unicode speech message
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xAE (Unicode Speech message)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] blockSize
|
||
//| BYTE[4] ID
|
||
//| BYTE[2] Model
|
||
//| BYTE Type
|
||
//| BYTE[2] Color
|
||
//| BYTE[2] Font
|
||
//| BYTE[4] Language
|
||
//| BYTE[30] Name
|
||
//| BYTE[?][2] Msg – Null Terminated (blockSize - 48)
|
||
//|
|
||
//| The various types of text is as follows:
|
||
//| 0x00 - Normal
|
||
//| 0x01 - Broadcast/System
|
||
//| 0x02 - Emote
|
||
//| 0x06 - System/Lower Corner
|
||
//| 0x07 - Message/Corner With Name
|
||
//| 0x08 - Whisper
|
||
//| 0x09 - Yell
|
||
//| 0x0A - Spell
|
||
//| 0x0D - Guild Chat
|
||
//| 0x0E - Alliance Chat
|
||
//| 0x0F - Command Prompts
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPUnicodeSpeech::Object( CBaseObject &talking )
|
||
{
|
||
CopyData( talking );
|
||
}
|
||
void CPUnicodeSpeech::Object( CPITalkRequestAscii &tSaid )
|
||
{
|
||
CopyData( tSaid );
|
||
}
|
||
void CPUnicodeSpeech::Object( CPITalkRequestUnicode &tSaid )
|
||
{
|
||
CopyData( tSaid );
|
||
}
|
||
void CPUnicodeSpeech::Type( UI08 value )
|
||
{
|
||
pStream.WriteByte( 9, ( value & 0x0F ) );
|
||
}
|
||
void CPUnicodeSpeech::Colour( COLOUR value )
|
||
{
|
||
pStream.WriteShort( 10, value );
|
||
}
|
||
void CPUnicodeSpeech::Font( UI16 value )
|
||
{
|
||
pStream.WriteShort( 12, value );
|
||
}
|
||
void CPUnicodeSpeech::Language( char *value )
|
||
{
|
||
pStream.WriteString( 14, value, 4 );
|
||
}
|
||
void CPUnicodeSpeech::Language( const char *value )
|
||
{
|
||
pStream.WriteString( 14, value, 4 );
|
||
}
|
||
void CPUnicodeSpeech::Lanaguge( const std::string& value )
|
||
{
|
||
pStream.WriteString( 14, value.c_str(), 4 );
|
||
}
|
||
void CPUnicodeSpeech::Name( std::string value )
|
||
{
|
||
pStream.WriteString( 18, value, 30 );
|
||
}
|
||
void CPUnicodeSpeech::Message( const char *value )
|
||
{
|
||
size_t length = strlen( value );
|
||
SetLength( static_cast< UI16 >(48 + (2 * length) + 2) );
|
||
for( size_t i = 0; i < length; ++i )
|
||
pStream.WriteByte( 48 + i * 2, value[i] );
|
||
}
|
||
void CPUnicodeSpeech::SetLength( UI16 value )
|
||
{
|
||
pStream.ReserveSize( value );
|
||
pStream.WriteShort( 1, value );
|
||
}
|
||
|
||
void CPUnicodeSpeech::InternalReset( void )
|
||
{
|
||
SetLength( 48 );
|
||
pStream.WriteByte( 0, 0xAE );
|
||
Language( "ENU" );
|
||
}
|
||
|
||
CPUnicodeSpeech::CPUnicodeSpeech()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPUnicodeSpeech::CPUnicodeSpeech( CBaseObject &toCopy )
|
||
{
|
||
InternalReset();
|
||
CopyData( toCopy );
|
||
}
|
||
CPUnicodeSpeech::CPUnicodeSpeech( CPITalkRequestAscii &toCopy )
|
||
{
|
||
InternalReset();
|
||
CopyData( toCopy );
|
||
}
|
||
CPUnicodeSpeech::CPUnicodeSpeech( CPITalkRequestUnicode &toCopy )
|
||
{
|
||
InternalReset();
|
||
CopyData( toCopy );
|
||
}
|
||
CPUnicodeSpeech &CPUnicodeSpeech::operator=( CBaseObject &toCopy )
|
||
{
|
||
CopyData( toCopy );
|
||
return (*this);
|
||
}
|
||
void CPUnicodeSpeech::CopyData( CBaseObject &toCopy )
|
||
{
|
||
Serial( toCopy.GetSerial() );
|
||
ID( toCopy.GetID() );
|
||
Name( toCopy.GetName() );
|
||
}
|
||
void CPUnicodeSpeech::CopyData( CPITalkRequestAscii &talking )
|
||
{
|
||
Colour( talking.TextColour() );
|
||
Font( talking.Font() );
|
||
Type( talking.Type() );
|
||
Language( "ENU" );
|
||
Message( talking.Text() );
|
||
}
|
||
void CPUnicodeSpeech::CopyData( CPITalkRequestUnicode &talking )
|
||
{
|
||
Colour( talking.TextColour() );
|
||
Font( talking.Font() );
|
||
Type( talking.Type() );
|
||
Language( talking.Language() );
|
||
|
||
UI16 length = talking.Length();
|
||
char *uniTxt = talking.UnicodeText();
|
||
|
||
SetLength( 48 + (2 * length) );
|
||
pStream.WriteArray( 48, (UI08 *)uniTxt, (2 * static_cast<size_t>(length)) );
|
||
}
|
||
void CPUnicodeSpeech::Serial( SERIAL toSet )
|
||
{
|
||
pStream.WriteLong( 3, toSet );
|
||
}
|
||
void CPUnicodeSpeech::ID( UI16 toSet )
|
||
{
|
||
pStream.WriteShort( 7, toSet );
|
||
}
|
||
|
||
void CPUnicodeSpeech::GhostIt( UI08 method )
|
||
{
|
||
// Method ignored currently
|
||
// Designed with the idea that you can garble text in more than one way
|
||
// eg 0 == ghost, 1 == racial, 2 == magical, etc etc
|
||
for( UI16 j = 49; j < pStream.GetSize() - 1; j += 2 )
|
||
{
|
||
if( pStream.GetByte( j ) != 32 )
|
||
pStream.WriteByte( j, ( RandomNum( 0, 1 ) == 0 ? 'O' : 'o' ) );
|
||
}
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPGameServerList()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet with gameserver list to display in client during login
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xA8 (Game Server List)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] blockSize
|
||
//| BYTE System Info Flag
|
||
//| 0xCC - Don't send
|
||
//| 0x5D - ???
|
||
//| 0x64 - Send Video card
|
||
//| ?? -
|
||
//| BYTE[2] # of servers
|
||
//| Then each server --
|
||
//| BYTE[2] serverIndex (0-based)
|
||
//| BYTE[32] serverName
|
||
//| BYTE percentFull
|
||
//| BYTE timezone
|
||
//| BYTE[4] pingIP
|
||
//|
|
||
//| Note: System Info Flags: 0xCC - Do not send video card info. 0x64 - Send Video card.
|
||
//| Note: Server IP has to be sent in reverse order. For example, 192.168.0.1 is sent as 0100A8C0.
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPGameServerList::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 6 );
|
||
pStream.WriteByte( 0, 0xA8 );
|
||
pStream.WriteByte( 2, 6 );
|
||
pStream.WriteByte( 3, 0xFF );
|
||
}
|
||
CPGameServerList::CPGameServerList()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPGameServerList::CPGameServerList( UI16 numServers )
|
||
{
|
||
InternalReset();
|
||
NumberOfServers( numServers );
|
||
}
|
||
void CPGameServerList::NumberOfServers( UI16 numItems )
|
||
{
|
||
UI32 tlen = 6 + ( numItems * 40 );
|
||
pStream.ReserveSize( tlen );
|
||
pStream.WriteShort( 1, static_cast< UI16 >(tlen) );
|
||
pStream.WriteShort( 4, numItems );
|
||
}
|
||
void CPGameServerList::AddServer( UI16 servNum, physicalServer *data )
|
||
{
|
||
UI32 baseOffset = 6 + servNum * 40;
|
||
pStream.WriteShort( baseOffset, servNum + 1 );
|
||
pStream.WriteString( static_cast<size_t>(baseOffset) + 2, data->getName(), data->getName().length() );
|
||
UI32 ip = inet_addr( data->getIP().c_str() );
|
||
pStream.WriteLong( static_cast<size_t>(baseOffset) + 36, ip );
|
||
}
|
||
void CPGameServerList::addEntry( const std::string & name, UI32 addressBig )
|
||
{
|
||
pStream.WriteShort( 1, 46 );
|
||
pStream.WriteShort( 4, 1 );
|
||
pStream.WriteShort( 6, 0 );
|
||
UI32 baseOffset = 6;
|
||
pStream.WriteString( static_cast<size_t>(baseOffset) + 2, name.c_str(), name.size() );
|
||
pStream.WriteLong( static_cast<size_t>(baseOffset) + 36, addressBig );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPSecureTrading()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet with server response to secure trading request
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x6F (Secure Trading)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] blockSize
|
||
//| BYTE action
|
||
//| 0x00: Start
|
||
//| 0x01: Cancel
|
||
//| 0x02: Update
|
||
//| BYTE[4] id1
|
||
//| BYTE[4] id2
|
||
//| BYTE[4] id3
|
||
//| BYTE nameFollowing (0 or 1)
|
||
//| If (nameFollowing = 1)
|
||
//| BYTE[?] charName
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPSecureTrading::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 8 );
|
||
pStream.WriteByte( 0, 0x6F );
|
||
pStream.WriteByte( 2, 8 );
|
||
}
|
||
void CPSecureTrading::CopyData( CBaseObject& mItem, SERIAL mItem2, SERIAL mItem3 )
|
||
{
|
||
pStream.ReserveSize( 16 );
|
||
pStream.WriteByte( 2, 16 );
|
||
pStream.WriteLong( 4, mItem.GetSerial() );
|
||
pStream.WriteLong( 8, mItem2 );
|
||
pStream.WriteLong( 12, mItem3 );
|
||
}
|
||
CPSecureTrading::CPSecureTrading()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPSecureTrading::CPSecureTrading( CBaseObject& mItem )
|
||
{
|
||
InternalReset();
|
||
pStream.WriteLong( 4, mItem.GetSerial() );
|
||
}
|
||
CPSecureTrading::CPSecureTrading( CBaseObject& mItem, SERIAL mItem2, SERIAL mItem3 )
|
||
{
|
||
InternalReset();
|
||
CopyData( mItem, mItem2, mItem3 );
|
||
}
|
||
|
||
void CPSecureTrading::Action( UI08 value )
|
||
{
|
||
pStream.WriteByte( 3, value );
|
||
}
|
||
void CPSecureTrading::Name( const std::string& nameFollowing )
|
||
{
|
||
pStream.ReserveSize( 47 );
|
||
pStream.WriteByte( 2, 47 );
|
||
pStream.WriteByte( 16, 1 );
|
||
if( nameFollowing.length() >= 30 )
|
||
pStream.WriteString( 17, nameFollowing, 30 );
|
||
else
|
||
pStream.WriteString( 17, nameFollowing, nameFollowing.length() );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPAllNames3D()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet with server response to all names request
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x98 (All-names “3D”)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] blocksize
|
||
//| BYTE[4] ID
|
||
//| If (server-reply)
|
||
//| BYTE[30] name (0 terminated)
|
||
//|
|
||
//| NOTE: Only 3D clients (used to) send this packet. Now both 2D and 3D do!
|
||
//| Server and client packet.
|
||
//|
|
||
//| Client asks for name of object with ID x.
|
||
//| Server has to reply with ID + name
|
||
//| Client automatically knows names of items.
|
||
//| Hence it only asks only for NPC/Player names nearby, but shows bars of items plus NPC’s.
|
||
//|
|
||
//| Client request has 7 bytes, server-reply 37
|
||
//| Triggered by Crtl + Shift.
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPAllNames3D::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 37 );
|
||
pStream.WriteByte( 0, 0x98 );
|
||
pStream.WriteByte( 2, 37 );
|
||
}
|
||
void CPAllNames3D::CopyData( CBaseObject& obj )
|
||
{
|
||
pStream.WriteLong( 3, obj.GetSerial() );
|
||
pStream.WriteString( 7, obj.GetName(), obj.GetName().length() );
|
||
}
|
||
CPAllNames3D::CPAllNames3D()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPAllNames3D::CPAllNames3D( CBaseObject& obj )
|
||
{
|
||
InternalReset();
|
||
CopyData( obj );
|
||
}
|
||
void CPAllNames3D::Object( CBaseObject& obj )
|
||
{
|
||
CopyData( obj );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPBookPage()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet with server response to request for book pages
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x66 (Books - Page)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] blockSize
|
||
//| BYTE[4] bookID
|
||
//| BYTE[2] # of pages in this packet
|
||
//| For each page:
|
||
//| BYTE[2] page #
|
||
//| BYTE[2] # of lines on page
|
||
//| Repeated for each line:
|
||
//| BYTE[var] null terminated line
|
||
//| Note:
|
||
//| server side: # of pages equals value given in 0x93/0xd4
|
||
//| EACH page # given. If empty: # lines: 0 + terminator (=3 0’s)
|
||
//| client side: # of pages always 1. if 2 pages changed, client generates 2 packets.
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPBookPage::IncLength( UI08 amount )
|
||
{
|
||
bookLength += amount;
|
||
pStream.ReserveSize( bookLength );
|
||
}
|
||
void CPBookPage::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 9 );
|
||
bookLength = 9;
|
||
pageCount = 0;
|
||
pStream.WriteByte( 0, 0x66 );
|
||
}
|
||
void CPBookPage::CopyData( CItem& obj )
|
||
{
|
||
Serial( obj.GetSerial() );
|
||
}
|
||
CPBookPage::CPBookPage()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPBookPage::CPBookPage( CItem& obj )
|
||
{
|
||
InternalReset();
|
||
CopyData( obj );
|
||
}
|
||
void CPBookPage::Object( CItem& obj )
|
||
{
|
||
CopyData( obj );
|
||
}
|
||
void CPBookPage::NewPage( SI16 pNum )
|
||
{
|
||
++pageCount; // 1 based counter
|
||
UI16 baseOffset = bookLength;
|
||
IncLength( 4 );
|
||
if( pNum == -1 )
|
||
pStream.WriteShort( baseOffset, pageCount );
|
||
else
|
||
pStream.WriteShort( baseOffset, pNum );
|
||
pStream.WriteByte( static_cast<size_t>(baseOffset) + 3, 8 ); // 8 lines per page
|
||
}
|
||
void CPBookPage::AddLine( const std::string& line )
|
||
{
|
||
UI16 baseOffset = bookLength;
|
||
size_t strLen = line.length() + 1;
|
||
IncLength( static_cast< UI08 >(strLen) );
|
||
pStream.WriteString( baseOffset, line, line.length() );
|
||
}
|
||
void CPBookPage::NewPage( SI16 pNum, const STRINGLIST *lines )
|
||
{
|
||
++pageCount; // 1 based counter
|
||
UI16 baseOffset = bookLength;
|
||
IncLength( 4 );
|
||
if( pNum == -1 )
|
||
pStream.WriteShort( baseOffset, pageCount );
|
||
else
|
||
pStream.WriteShort( baseOffset, pNum );
|
||
pStream.WriteByte( static_cast<size_t>(baseOffset + 3), static_cast<UI08>(lines->size()) ); // 8 lines per page
|
||
|
||
for( STRINGLIST_CITERATOR lIter = lines->begin(); lIter != lines->end(); ++lIter )
|
||
{
|
||
AddLine( (*lIter) );
|
||
}
|
||
}
|
||
void CPBookPage::Finalize( void )
|
||
{
|
||
pStream.WriteShort( 1, bookLength );
|
||
pStream.WriteShort( 7, pageCount );
|
||
}
|
||
void CPBookPage::Serial( SERIAL value )
|
||
{
|
||
pStream.WriteLong( 3, value );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPSendGumpMenu()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet with gump menu details
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xB0 (Send Gump Menu Dialog)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] blockSize
|
||
//| BYTE[4] id
|
||
//| BYTE[4] gumpid
|
||
//| BYTE[4] x
|
||
//| BYTE[4] y
|
||
//| BYTE[2] command section length
|
||
//| BYTE[?] commands (zero terminated)
|
||
//| BYTE[2] numTextLines
|
||
//| BYTE[2] text length (in unicode (2 byte) characters.)
|
||
//| BYTE[?] text (in unicode)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPSendGumpMenu::CPSendGumpMenu()
|
||
{
|
||
pStream.ReserveSize( 21 );
|
||
pStream.WriteByte( 0, 0xB0 ); // command byte
|
||
pStream.WriteByte( 14, 0x6E ); // default x
|
||
pStream.WriteByte( 18, 0x46 ); // default y
|
||
}
|
||
void CPSendGumpMenu::UserID( SERIAL value )
|
||
{
|
||
pStream.WriteLong( 3, value );
|
||
}
|
||
void CPSendGumpMenu::GumpID( SERIAL value )
|
||
{
|
||
pStream.WriteLong( 7, value );
|
||
}
|
||
void CPSendGumpMenu::X( UI32 value )
|
||
{
|
||
pStream.WriteLong( 11, value );
|
||
}
|
||
void CPSendGumpMenu::Y( UI32 value )
|
||
{
|
||
pStream.WriteLong( 15, value );
|
||
}
|
||
void CPSendGumpMenu::addCommand( const std::string& msg )
|
||
{
|
||
if( msg.empty() ){
|
||
throw new std::runtime_error( "Blank command field added!" );
|
||
}
|
||
auto temp = msg ;
|
||
if (temp.size() > 1024){
|
||
temp = temp.substr(0,1024) ;
|
||
}
|
||
|
||
#if defined( UOX_DEBUG_MODE )
|
||
Console << temp << myendl;
|
||
#endif
|
||
commands.push_back( temp );
|
||
}
|
||
|
||
void CPSendGumpMenu::addText( const std::string& msg )
|
||
{
|
||
if( msg.empty() || msg.size() == 0 )
|
||
{
|
||
//throw new std::runtime_error( "Blank text field added!" );
|
||
Console.error( "Blank text field added!" );
|
||
return;
|
||
}
|
||
auto temp = msg ;
|
||
if (temp.size() > 512){
|
||
temp = temp.substr(0,512) ;
|
||
}
|
||
#if defined( UOX_DEBUG_MODE )
|
||
Console << msg << myendl;
|
||
#endif
|
||
|
||
text.push_back( msg );
|
||
}
|
||
|
||
void CPSendGumpMenu::Finalize( void )
|
||
{
|
||
UI32 length = 21;
|
||
UI16 increment = 0;
|
||
UI16 lineLen = 0;
|
||
|
||
std::string cmdString;
|
||
|
||
for( STRINGLIST_CITERATOR cIter = commands.begin(); cIter != commands.end(); ++cIter )
|
||
{
|
||
lineLen = static_cast<UI16>((*cIter).length());
|
||
if( lineLen == 0 )
|
||
break;
|
||
increment = static_cast<UI16>(lineLen + 4);
|
||
if( (length + increment) >= 0xFFFF )
|
||
{
|
||
Console.warning( "SendGump Packet (0xB0) attempted to send a packet that exceeds 65355 bytes!" );
|
||
break;
|
||
}
|
||
|
||
pStream.ReserveSize( static_cast<size_t>(length) + static_cast<size_t>(increment) );
|
||
cmdString = "{ " + (*cIter) + " }";
|
||
pStream.WriteString( length, cmdString, increment );
|
||
length += increment;
|
||
}
|
||
|
||
if( length > 65536 )
|
||
throw std::runtime_error( "Packet 0xB0 is far too large" );
|
||
|
||
pStream.WriteShort( 19, static_cast<UI16>(length-20) );
|
||
|
||
UI32 tlOff = length + 1;
|
||
length += 3;
|
||
UI32 tlines = 0;
|
||
|
||
pStream.ReserveSize( length ); // match the 3 byte increase
|
||
|
||
for( STRINGLIST_CITERATOR tIter = text.begin(); tIter != text.end(); ++tIter )
|
||
{
|
||
lineLen = static_cast<UI16>((*tIter).length());
|
||
if( lineLen == 0 )
|
||
break;
|
||
// Unfortunately, unicode strings are... different
|
||
// so we can't use PackString
|
||
increment = lineLen * 2 + 2;
|
||
if( (length + increment) >= 0xFFFF )
|
||
{
|
||
Console.warning( "SendGump Packet (0xB0) attempted to send a packet that exceeds 65355 bytes!" );
|
||
break;
|
||
}
|
||
|
||
pStream.ReserveSize( static_cast<size_t>(length) + static_cast<size_t>(increment) );
|
||
pStream.WriteShort( length, lineLen );
|
||
for( UI16 i = 0; i < lineLen; ++i )
|
||
pStream.WriteByte( static_cast<size_t>(length) + 3 + static_cast<size_t>(i)*2, (*tIter)[i] );
|
||
length += increment;
|
||
++tlines;
|
||
}
|
||
|
||
if( length > 65536 )
|
||
throw std::runtime_error( "Packet 0xB0 is far too large" );
|
||
|
||
pStream.WriteShort( 1, static_cast< UI16 >(length) );
|
||
pStream.WriteShort( tlOff, static_cast< UI16 >(tlines) );
|
||
}
|
||
|
||
void CPSendGumpMenu::Log( std::ofstream &outStream, bool fullHeader )
|
||
{
|
||
if( fullHeader )
|
||
outStream << "[SEND]Packet : CPSendGumpMenu 0xB0 --> Length: " << pStream.GetSize() << TimeStamp() << std::endl;
|
||
outStream << "ID : " << std::hex << pStream.GetULong( 3 ) << std::endl;
|
||
outStream << "GumpID : " << std::hex << pStream.GetULong( 7 ) << std::endl;
|
||
outStream << "X : " << std::hex << pStream.GetUShort( 11 ) << std::endl;
|
||
outStream << "Y : " << std::hex << pStream.GetUShort( 15 ) << std::endl;
|
||
outStream << "Command Sec Len : " << std::dec << pStream.GetUShort( 19 ) << std::endl;
|
||
outStream << "Commands : " << std::endl;
|
||
for( size_t x = 0; x < commands.size(); ++x )
|
||
outStream << " " << commands[x] << std::endl;
|
||
outStream << "Text : " << std::endl;
|
||
for( size_t y = 0; y < text.size(); ++y )
|
||
outStream << " " << text[y] << std::endl;
|
||
|
||
outStream << " Raw dump :" << std::endl;
|
||
CPUOXBuffer::Log( outStream, false );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPNewSpellBook()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to send spellbook content
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xBF (General Information Packet)
|
||
//| Subcommand: 0x1B (New Spellbook)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd (0xBF)
|
||
//| BYTE[2] Length
|
||
//| BYTE[2] Subcommand (0x1B)
|
||
//| Subcommand details
|
||
//| BYTE[2] unknown, always 1
|
||
//| BYTE[4] Spellbook serial
|
||
//| BYTE[2] Item Id
|
||
//| BYTE[2] scroll offset
|
||
//| 1 = regular
|
||
//| 101 = necro
|
||
//| 201 = paladin
|
||
//| 401 = bushido
|
||
//| 501 = ninjitsu
|
||
//| 601 = spellweaving
|
||
//| BYTE[8] spellbook content
|
||
//| first bit of first byte = spell #1
|
||
//| second bit of first byte = spell #2
|
||
//| first bit of second byte = spell #8, etc
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPNewSpellBook::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 23 );
|
||
pStream.WriteByte( 0, 0xBF ); //Main packet
|
||
pStream.WriteShort( 1, 23 );
|
||
pStream.WriteShort( 3, 0x1B ); //Subcommand
|
||
pStream.WriteShort( 5, 0x01 );
|
||
pStream.WriteByte( 11, 0x0E ); // Graphic part I?
|
||
pStream.WriteByte( 12, 0xFA ); // Graphic part II?
|
||
pStream.WriteShort( 13, 1 );// Offset
|
||
}
|
||
void CPNewSpellBook::CopyData( CItem& obj )
|
||
{
|
||
pStream.WriteLong( 7, obj.GetSerial() );
|
||
for( UI08 i = 0 ; i < 64 ; ++i )
|
||
{
|
||
SI32 y = (i % 8);
|
||
SI32 x = 15 + (SI32)(i / 8);
|
||
if( Magic->HasSpell( &obj, i ) )
|
||
pStream.WriteByte( x, (pStream.GetByte( x ) | static_cast<UI08>(power( 2, y ))) );
|
||
}
|
||
}
|
||
CPNewSpellBook::CPNewSpellBook()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPNewSpellBook::CPNewSpellBook( CItem& obj )
|
||
{
|
||
InternalReset();
|
||
CopyData( obj );
|
||
}
|
||
|
||
bool CPNewSpellBook::ClientCanReceive( CSocket *mSock )
|
||
{
|
||
if( mSock == nullptr )
|
||
return false;
|
||
switch( mSock->ClientType() )
|
||
{
|
||
case CV_DEFAULT:
|
||
case CV_T2A:
|
||
case CV_UO3D:
|
||
if( mSock->ClientVersionMajor() < 4 )
|
||
return false;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPDisplayDamage()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to show damage numbers above head of characters
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xBF (General Information Packet)
|
||
//| Subcommand: 0x22 (Damage)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd (0xBF)
|
||
//| BYTE[2] Length
|
||
//| BYTE[2] Subcommand (0x22)
|
||
//| Subcommand details
|
||
//| BYTE[2] unknown, always 1
|
||
//| BYTE[4] Serial
|
||
//| BYTE Damage // how much damage was done ?
|
||
//|
|
||
//| Note: displays damage above the npc/player’s head.
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPDisplayDamage::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 11 );
|
||
pStream.WriteByte( 0, 0xBF );
|
||
pStream.WriteByte( 1, 0x00 );
|
||
pStream.WriteByte( 2, 0x0B );
|
||
pStream.WriteByte( 3, 0x00 );
|
||
pStream.WriteByte( 4, 0x22 );
|
||
pStream.WriteByte( 5, 0x01 );
|
||
}
|
||
void CPDisplayDamage::CopyData( CChar& ourTarg, UI16 ourDamage )
|
||
{
|
||
pStream.WriteLong( 6, ourTarg.GetSerial() );
|
||
pStream.WriteByte( 10, (ourDamage>>8) + (ourDamage%256) );
|
||
}
|
||
CPDisplayDamage::CPDisplayDamage()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPDisplayDamage::CPDisplayDamage( CChar& ourTarg, UI16 ourDamage )
|
||
{
|
||
InternalReset();
|
||
CopyData( ourTarg, ourDamage );
|
||
}
|
||
bool CPDisplayDamage::ClientCanReceive( CSocket *mSock )
|
||
{
|
||
if( mSock == nullptr )
|
||
return false;
|
||
switch( mSock->ClientType() )
|
||
{
|
||
case CV_DEFAULT:
|
||
case CV_T2A:
|
||
case CV_UO3D:
|
||
if( mSock->ClientVersionMajor() < 4 )
|
||
return false;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPQueryToolTip()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet with response to client request for item tooltip
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xBF (General Information Packet)
|
||
//| Subcommand: 0x10 (Display Equipment Info)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd (0xBF)
|
||
//| BYTE[2] Length
|
||
//| BYTE[2] Subcommand (0x10)
|
||
//| Subcommand details
|
||
//| BYTE[4] Serial
|
||
//| BYTE[4] Info Number
|
||
//| BYTE[4] 0xFFFFFFFD (If Owner != null )
|
||
//| BYTE[2] Owner Name Length (If Owner != null )
|
||
//| BYTE[30] Owner Name (If Owner != null )
|
||
//| BYTE[4] 0xFFFFFFFC (If equipment unidentified )
|
||
//| loop Attribute
|
||
//| BYTE[4] Number
|
||
//| BYTE[2] Charges
|
||
//| endloop Attribute
|
||
//| BYTE[4] 0xFFFFFFFF
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPQueryToolTip::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 13 );
|
||
pStream.WriteByte( 0, 0xBF );
|
||
pStream.WriteByte( 1, 0x00 );
|
||
pStream.WriteByte( 2, 0x0D );
|
||
pStream.WriteByte( 3, 0x00 );
|
||
pStream.WriteByte( 4, 0x10 );
|
||
pStream.WriteByte( 9, 0x78 );
|
||
pStream.WriteByte( 10, 0xA1 );
|
||
pStream.WriteByte( 11, 0xBA );
|
||
pStream.WriteByte( 12, 0x2B );
|
||
}
|
||
void CPQueryToolTip::CopyData( CBaseObject& mObj )
|
||
{
|
||
pStream.WriteLong( 5, mObj.GetSerial() );
|
||
}
|
||
CPQueryToolTip::CPQueryToolTip()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPQueryToolTip::CPQueryToolTip( CBaseObject& mObj )
|
||
{
|
||
InternalReset();
|
||
CopyData( mObj );
|
||
}
|
||
|
||
bool CPQueryToolTip::ClientCanReceive( CSocket *mSock )
|
||
{
|
||
if( mSock == nullptr )
|
||
return false;
|
||
switch( mSock->ClientType() )
|
||
{
|
||
case CV_DEFAULT:
|
||
case CV_T2A:
|
||
case CV_UO3D:
|
||
if( mSock->ClientVersionMajor() < 4 )
|
||
return false;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPToolTip()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet with response to AoS client request for item tooltip
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xD6 (AOS tooltip/Object property list)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] length
|
||
//| BYTE[2] unknown1, always 0x001
|
||
//| BYTE[4] serial of item/creature
|
||
//| BYTE unknown2, always 0
|
||
//| BYTE unknown3, always 0
|
||
//| BYTE[4] list ID (see notes)
|
||
//| Loop
|
||
//| BYTE[4] cliloc#
|
||
//| if cliloc# is 0, break loop
|
||
//| BYTE[2] text length
|
||
//| BYTE[text length] little endian Unicode text, not 0 terminated
|
||
//| End Loop
|
||
//| BYTE[4] 00000000 - Sent as end of packet/loop
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPToolTip::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 15 );
|
||
pStream.WriteByte( 0, 0xD6 );
|
||
pStream.WriteShort( 3, 0x0001 );
|
||
pStream.WriteShort( 9, 0x0000 );
|
||
pStream.WriteLong( 11, 0x00000000 );
|
||
}
|
||
|
||
void CPToolTip::FinalizeData( toolTipEntry tempEntry, size_t &totalStringLen )
|
||
{
|
||
tempEntry.stringLen = (tempEntry.ourText.size()*2);
|
||
totalStringLen += (tempEntry.stringLen+6);
|
||
ourEntries.push_back( tempEntry );
|
||
}
|
||
|
||
void CPToolTip::CopyItemData( CItem& cItem, size_t &totalStringLen, bool addAmount, bool playerVendor )
|
||
{
|
||
toolTipEntry tempEntry = {};
|
||
if( cItem.GetType() == IT_HOUSESIGN )
|
||
tempEntry.ourText = " \tA House Sign\t ";
|
||
else if( cItem.GetName()[0] == '#' )
|
||
{
|
||
std::string temp;
|
||
getTileName( cItem, temp );
|
||
if( cItem.GetAmount() > 1 && addAmount ) {
|
||
tempEntry.ourText = strutil::format( " \t%s : %i\t ", temp.c_str(), cItem.GetAmount() );
|
||
}
|
||
else{
|
||
tempEntry.ourText = strutil::format(" \t%s\t ",temp.c_str() );
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if( cItem.GetAmount() > 1 && !cItem.isCorpse() && addAmount && cItem.GetType() != IT_SPAWNCONT && cItem.GetType() != IT_LOCKEDSPAWNCONT && cItem.GetType() != IT_UNLOCKABLESPAWNCONT ){
|
||
tempEntry.ourText = strutil::format( " \t%s : %i\t ", cItem.GetName().c_str(), cItem.GetAmount() );
|
||
}
|
||
else{
|
||
tempEntry.ourText = strutil::format(" \t%s\t ",cItem.GetName().c_str() );
|
||
}
|
||
}
|
||
tempEntry.stringNum = 1050045;
|
||
FinalizeData( tempEntry, totalStringLen );
|
||
|
||
if( cItem.IsLockedDown() )
|
||
{
|
||
if( ValidateObject( cItem.GetMultiObj() ) && cItem.GetMultiObj()->IsSecureContainer( &cItem ) )
|
||
{
|
||
// Locked down and secure
|
||
tempEntry.stringNum = 501644;
|
||
FinalizeData( tempEntry, totalStringLen );
|
||
}
|
||
else
|
||
{
|
||
// Locked down
|
||
tempEntry.stringNum = 501643;
|
||
FinalizeData( tempEntry, totalStringLen );
|
||
}
|
||
}
|
||
if( cItem.GetType() == IT_CONTAINER || cItem.GetType() == IT_LOCKEDCONTAINER )
|
||
{
|
||
tempEntry.stringNum = 1050044;
|
||
//tempEntry.ourText = strutil::format( "%u\t%i",cItem.GetContainsList()->Num(), (cItem.GetWeight()/100) );
|
||
tempEntry.ourText = strutil::format( "%u\t%i", GetTotalItemCount( &cItem ), (cItem.GetWeight()/100) );
|
||
FinalizeData( tempEntry, totalStringLen );
|
||
if( ( cItem.GetWeightMax() / 100 ) >= 1 )
|
||
{
|
||
//Uncomment and replace 0 with maxitem value if we ever implement it
|
||
tempEntry.stringNum = 1072226;
|
||
tempEntry.ourText = strutil::format( "%u\t%i", cItem.GetMaxItems(), ( cItem.GetWeightMax() / 100 ) );
|
||
//tempEntry.stringNum = 1060658;
|
||
//tempEntry.ourText = strutil::format( "Capacity\t%i Stones", ( cItem.GetWeightMax() / 100 ) );
|
||
FinalizeData( tempEntry, totalStringLen );
|
||
}
|
||
}
|
||
else if( cItem.GetType() == IT_HOUSESIGN )
|
||
{
|
||
tempEntry.stringNum = 1061112;
|
||
tempEntry.ourText = cItem.GetName();
|
||
FinalizeData( tempEntry, totalStringLen );
|
||
|
||
if( cItem.GetOwnerObj() != nullptr )
|
||
{
|
||
tempEntry.stringNum = 1061113;
|
||
tempEntry.ourText = cItem.GetOwnerObj()->GetName();
|
||
FinalizeData( tempEntry, totalStringLen );
|
||
}
|
||
}
|
||
else if( cItem.GetType() == IT_MAGICWAND && cItem.GetTempVar( CITV_MOREZ ) )
|
||
{
|
||
tempEntry.stringNum = 1060584;
|
||
tempEntry.ourText = strutil::number( cItem.GetTempVar( CITV_MOREZ ) );
|
||
FinalizeData( tempEntry, totalStringLen );
|
||
}
|
||
else if( cItem.GetType() == IT_RECALLRUNE )
|
||
{
|
||
// Add a tooltip that says something about where the recall rune is marked
|
||
switch( cItem.GetTempVar( CITV_MORE ) )
|
||
{
|
||
case 1: // Trammel
|
||
tempEntry.stringNum = 1114778;
|
||
tempEntry.ourText = "(Trammel)";
|
||
break;
|
||
case 2: // Ilshenar
|
||
tempEntry.stringNum = 1114778;
|
||
tempEntry.ourText = "(Ilshenar)";
|
||
break;
|
||
case 3: // Malas
|
||
tempEntry.stringNum = 1114778;
|
||
tempEntry.ourText = "(Malas)";
|
||
break;
|
||
case 4: // Tokuno
|
||
tempEntry.stringNum = 1114778;
|
||
tempEntry.ourText = "(Tokuno Islands)";
|
||
break;
|
||
case 5: // TerMur
|
||
tempEntry.stringNum = 1114778;
|
||
tempEntry.ourText = "(Ter Mur)";
|
||
break;
|
||
default: // Felucca
|
||
tempEntry.stringNum = 1114778;
|
||
tempEntry.ourText = "(Felucca)";
|
||
break;
|
||
}
|
||
FinalizeData( tempEntry, totalStringLen );
|
||
}
|
||
else if( ( cItem.GetWeight() / 100 ) >= 1 && cItem.GetType() != IT_SPAWNCONT && cItem.GetType() != IT_LOCKEDSPAWNCONT && cItem.GetType() != IT_UNLOCKABLESPAWNCONT )
|
||
{
|
||
if( ( cItem.GetWeight() / 100 ) == 1 )
|
||
tempEntry.stringNum = 1072788;
|
||
else
|
||
tempEntry.stringNum = 1072789;
|
||
|
||
if( cItem.GetType() == IT_ITEMSPAWNER || cItem.GetType() == IT_NPCSPAWNER )
|
||
tempEntry.ourText = strutil::number( ( cItem.GetWeight() / 100 ) );
|
||
else
|
||
tempEntry.ourText = strutil::number( ( cItem.GetWeight() / 100 ) * cItem.GetAmount() );
|
||
FinalizeData( tempEntry, totalStringLen );
|
||
}
|
||
if( !cwmWorldState->ServerData()->BasicTooltipsOnly() )
|
||
{
|
||
if( cItem.GetLayer() != IL_NONE )
|
||
{
|
||
if( cItem.GetHiDamage() > 0 )
|
||
{
|
||
tempEntry.stringNum = 1060403;
|
||
tempEntry.ourText = strutil::number( 100 );
|
||
FinalizeData( tempEntry, totalStringLen );
|
||
}
|
||
|
||
if( cItem.GetHiDamage() > 0 )
|
||
{
|
||
tempEntry.stringNum = 1061168;
|
||
tempEntry.ourText = strutil::format( "%i\t%i", cItem.GetLoDamage(), cItem.GetHiDamage() );
|
||
FinalizeData( tempEntry, totalStringLen );
|
||
}
|
||
|
||
if( cItem.GetSpeed() > 0 )
|
||
{
|
||
tempEntry.stringNum = 1061167;
|
||
tempEntry.ourText = strutil::number( cItem.GetSpeed() );
|
||
FinalizeData( tempEntry, totalStringLen );
|
||
}
|
||
|
||
if( cItem.GetHiDamage() > 0 )
|
||
{
|
||
if( cItem.GetLayer() == IL_RIGHTHAND )
|
||
tempEntry.stringNum = 1061824;
|
||
else
|
||
tempEntry.stringNum = 1061171;
|
||
FinalizeData( tempEntry, totalStringLen );
|
||
}
|
||
|
||
if( Combat->getCombatSkill( &cItem ) != WRESTLING )
|
||
{
|
||
switch( Combat->getCombatSkill( &cItem ) )
|
||
{
|
||
case SWORDSMANSHIP:
|
||
tempEntry.stringNum = 1061172;
|
||
break;
|
||
case MACEFIGHTING:
|
||
tempEntry.stringNum = 1061173;
|
||
break;
|
||
case FENCING:
|
||
tempEntry.stringNum = 1061174;
|
||
break;
|
||
case ARCHERY:
|
||
tempEntry.stringNum = 1061175;
|
||
break;
|
||
}
|
||
FinalizeData( tempEntry, totalStringLen );
|
||
}
|
||
|
||
if( cItem.GetResist( PHYSICAL ) > 0 )
|
||
{
|
||
tempEntry.stringNum = 1060448;
|
||
tempEntry.ourText = strutil::number( cItem.GetResist( PHYSICAL ) );
|
||
FinalizeData( tempEntry, totalStringLen );
|
||
}
|
||
|
||
if( cItem.GetMaxHP() > 1 )
|
||
{
|
||
tempEntry.stringNum = 1060639;
|
||
tempEntry.ourText = strutil::format( "%i\t%i", cItem.GetHP(), cItem.GetMaxHP() );
|
||
FinalizeData( tempEntry, totalStringLen );
|
||
}
|
||
|
||
if( cItem.GetStrength2() > 0 )
|
||
{
|
||
tempEntry.stringNum = 1060485;
|
||
tempEntry.ourText = strutil::number( cItem.GetStrength2() );
|
||
FinalizeData( tempEntry, totalStringLen );
|
||
}
|
||
if( cItem.GetDexterity2() > 0 )
|
||
{
|
||
tempEntry.stringNum = 1060409;
|
||
tempEntry.ourText = strutil::number( cItem.GetDexterity2() );
|
||
FinalizeData( tempEntry, totalStringLen );
|
||
}
|
||
if( cItem.GetIntelligence2() > 0 )
|
||
{
|
||
tempEntry.stringNum = 1060432;
|
||
tempEntry.ourText = strutil::number( cItem.GetIntelligence2() );
|
||
FinalizeData( tempEntry, totalStringLen );
|
||
}
|
||
|
||
if( cItem.GetStrength() > 1 )
|
||
{
|
||
tempEntry.stringNum = 1061170;
|
||
tempEntry.ourText = strutil::number( cItem.GetStrength() );
|
||
FinalizeData( tempEntry, totalStringLen );
|
||
}
|
||
}
|
||
}
|
||
|
||
if( playerVendor )
|
||
{
|
||
if( cItem.GetBuyValue() > 0 )
|
||
{
|
||
// First the price
|
||
tempEntry.stringNum = 1043304;
|
||
tempEntry.ourText = strutil::number( cItem.GetBuyValue() );
|
||
FinalizeData( tempEntry, totalStringLen );
|
||
// Then the description
|
||
tempEntry.stringNum = 1043305;
|
||
tempEntry.ourText = cItem.GetDesc();
|
||
FinalizeData( tempEntry, totalStringLen );
|
||
}
|
||
else
|
||
{
|
||
// Item is not for sale
|
||
tempEntry.stringNum = 1043307;
|
||
FinalizeData( tempEntry, totalStringLen );
|
||
// The description
|
||
tempEntry.stringNum = 1043305;
|
||
tempEntry.ourText = cItem.GetDesc();
|
||
FinalizeData( tempEntry, totalStringLen );
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
void CPToolTip::CopyCharData( CChar& mChar, size_t &totalStringLen )
|
||
{
|
||
toolTipEntry tempEntry = {};
|
||
tempEntry.stringNum = 1050045;
|
||
tempEntry.ourText = strutil::format( " \t%s\t ",mChar.GetName().c_str() );
|
||
FinalizeData( tempEntry, totalStringLen );
|
||
}
|
||
|
||
void CPToolTip::CopyData( SERIAL objSer, bool addAmount, bool playerVendor )
|
||
{
|
||
size_t totalStringLen = 0; //total string length
|
||
|
||
if( objSer < BASEITEMSERIAL )
|
||
{
|
||
CChar *mChar = calcCharObjFromSer( objSer );
|
||
if( mChar != nullptr )
|
||
CopyCharData( (*mChar), totalStringLen );
|
||
}
|
||
else
|
||
{
|
||
CItem *cItem = calcItemObjFromSer( objSer );
|
||
if( cItem != nullptr )
|
||
CopyItemData( (*cItem), totalStringLen, addAmount, playerVendor );
|
||
}
|
||
|
||
size_t packetLen = 14 + totalStringLen + 5;
|
||
//size_t packetLen = 15 + totalStringLen + 5;
|
||
pStream.ReserveSize( packetLen );
|
||
pStream.WriteShort( 1, static_cast<SI32>(packetLen) );
|
||
pStream.WriteLong( 5, objSer );
|
||
|
||
size_t modifier = 14;
|
||
//size_t modifier = 15;
|
||
//loop through all lines
|
||
for( size_t i = 0; i < ourEntries.size(); ++i )
|
||
{
|
||
size_t stringLen = ourEntries[i].stringLen;
|
||
pStream.WriteLong( ++modifier, ourEntries[i].stringNum );
|
||
modifier += 4;
|
||
pStream.WriteShort( modifier, static_cast<SI32>(stringLen) );
|
||
modifier += 1;
|
||
|
||
//convert to uni character
|
||
for( size_t j = 0; j < stringLen; j += 2 )
|
||
{
|
||
pStream.WriteByte( ++modifier, ourEntries[i].ourText[j/2] );
|
||
pStream.WriteByte( ++modifier, 0x00 );
|
||
}
|
||
}
|
||
|
||
pStream.WriteLong( packetLen-4, 0x00000000 );
|
||
}
|
||
|
||
CPToolTip::CPToolTip()
|
||
{
|
||
InternalReset();
|
||
}
|
||
CPToolTip::CPToolTip( SERIAL objSer, bool addAmount, bool playerVendor )
|
||
{
|
||
InternalReset();
|
||
CopyData( objSer, addAmount, playerVendor );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPSellList()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet with vendor's shop sell list
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x9E (Sell List)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] blockSize
|
||
//| BYTE[4] shopkeeper serial
|
||
//| BYTE[2] numItems
|
||
//| For each item, a structure containing:
|
||
//| BYTE[4] item serial
|
||
//| BYTE[2] itemModel
|
||
//| BYTE[2] itemHue/Color
|
||
//| BYTE[2] itemAmount
|
||
//| BYTE[2] value
|
||
//| BYTE[2] nameLength
|
||
//| BYTE[?] name
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPSellList::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 9 );
|
||
pStream.WriteByte( 0, 0x9E );
|
||
numItems = 0;
|
||
}
|
||
void CPSellList::CopyData( CChar& mChar, CChar& vendorID )
|
||
{
|
||
CItem *buyPack = vendorID.GetItemAtLayer( IL_BUYCONTAINER );
|
||
CItem *ourPack = mChar.GetPackItem();
|
||
|
||
numItems = 0;
|
||
size_t packetLen = 9;
|
||
|
||
if( ValidateObject( buyPack ) && ValidateObject( ourPack ) )
|
||
{
|
||
CTownRegion *tReg = nullptr;
|
||
if( cwmWorldState->ServerData()->TradeSystemStatus() )
|
||
tReg = calcRegionFromXY( vendorID.GetX(), vendorID.GetY(), vendorID.WorldNumber(), vendorID.GetInstanceID() );
|
||
GenericList< CItem * > *spCont = buyPack->GetContainsList();
|
||
for( CItem *spItem = spCont->First(); !spCont->Finished(); spItem = spCont->Next() )
|
||
{
|
||
if( ValidateObject( spItem ) )
|
||
AddContainer( tReg, spItem, ourPack, packetLen );
|
||
}
|
||
}
|
||
|
||
pStream.WriteShort( 1, (UI16)packetLen );
|
||
pStream.WriteLong( 3, vendorID.GetSerial() );
|
||
pStream.WriteShort( 7, numItems );
|
||
}
|
||
|
||
void CPSellList::AddContainer( CTownRegion *tReg, CItem *spItem, CItem *ourPack, size_t &packetLen )
|
||
{
|
||
GenericList< CItem * > *opCont = ourPack->GetContainsList();
|
||
for( CItem *opItem = opCont->First(); !opCont->Finished(); opItem = opCont->Next() )
|
||
{
|
||
if( ValidateObject( opItem ) )
|
||
{
|
||
if( opItem->GetType() == IT_CONTAINER )
|
||
AddContainer( tReg, spItem, opItem, packetLen );
|
||
else if( opItem->GetID() == spItem->GetID() && opItem->GetType() == spItem->GetType() &&
|
||
( spItem->GetName() == opItem->GetName() || !cwmWorldState->ServerData()->SellByNameStatus() ) )
|
||
{
|
||
AddItem( tReg, spItem, opItem, packetLen );
|
||
++numItems;
|
||
}
|
||
if( numItems >= 60 )
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
void CPSellList::AddItem( CTownRegion *tReg, CItem *spItem, CItem *opItem, size_t &packetLen )
|
||
{
|
||
std::string itemname;
|
||
size_t stringLen = getTileName( (*opItem), itemname );
|
||
size_t newLen = (packetLen + 14 + stringLen);
|
||
pStream.ReserveSize( newLen );
|
||
pStream.WriteLong( packetLen, opItem->GetSerial() );
|
||
pStream.WriteShort( packetLen+4, opItem->GetID() );
|
||
pStream.WriteShort( packetLen+6, opItem->GetColour() );
|
||
pStream.WriteShort( packetLen+8, opItem->GetAmount() );
|
||
UI32 value = calcValue( opItem, spItem->GetSellValue() );
|
||
if( cwmWorldState->ServerData()->TradeSystemStatus() )
|
||
value = calcGoodValue( tReg, spItem, value, true );
|
||
pStream.WriteShort( packetLen+10, value );
|
||
pStream.WriteShort( packetLen+12, static_cast<SI32>(stringLen) );
|
||
pStream.WriteString( packetLen+14, itemname, stringLen );
|
||
packetLen = newLen;
|
||
}
|
||
|
||
CPSellList::CPSellList()
|
||
{
|
||
InternalReset();
|
||
}
|
||
|
||
bool CPSellList::CanSellItems( CChar &mChar, CChar &vendor )
|
||
{
|
||
CopyData( mChar, vendor );
|
||
return (numItems != 0);
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPOpenMessageBoard()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to display bulletin board
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x71 (Bulletin Board Message)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] len
|
||
//| BYTE subcmd
|
||
//| BYTE[ len - 4 ] submessage
|
||
//| Submessage 0 – Display Bulletin Board
|
||
//| BYTE[4] Board serial
|
||
//| BYTE[22] board name (default is “bulletin board”, the rest nulls)
|
||
//| BYTE[4] unknown/ID?
|
||
//| BYTE[4] zero (0)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPOpenMessageBoard::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 38 );
|
||
pStream.WriteByte( 0, 0x71 );
|
||
pStream.WriteShort( 1, 38 );
|
||
pStream.WriteByte( 3, 0x00 ); //was 38
|
||
// pStream.WriteString( 8, "Bulletin Board", 22 ); //was 15
|
||
}
|
||
|
||
void CPOpenMessageBoard::CopyData( CSocket *mSock )
|
||
{
|
||
CItem *msgBoard = calcItemObjFromSer( mSock->GetDWord( 1 ) );
|
||
|
||
if( ValidateObject( msgBoard ) )
|
||
{
|
||
pStream.WriteLong( 4, msgBoard->GetSerial() );
|
||
// If the name the item (Bulletin Board) has been defined, display it
|
||
// instead of the default "Bulletin Board" title.
|
||
std::string msgBoardName = msgBoard->GetName();
|
||
if( msgBoardName != "#" )
|
||
{
|
||
if( msgBoardName.length() >= 29 )
|
||
{
|
||
pStream.WriteString( 8, msgBoardName, 29 );
|
||
}
|
||
else
|
||
{
|
||
pStream.WriteString( 8, msgBoardName, msgBoardName.length() );
|
||
}
|
||
}
|
||
else
|
||
{
|
||
pStream.WriteString( 8, "Bulletin Board", 29 ); //was 15
|
||
}
|
||
pStream.WriteByte( 37, 0x00 );
|
||
/* pStream.WriteLong( 4, msgBoard->GetSerial() );
|
||
// If the name the item (Bulletin Board) has been defined, display it
|
||
// instead of the default "Bulletin Board" title.
|
||
if( msgBoard->GetName() != "#" )
|
||
pStream.WriteString( 8, msgBoard->GetName(), 22 ); //was 21
|
||
else
|
||
pStream.WriteString( 8, "Bulletin Board", 22 ); //was 15
|
||
pStream.WriteLong( 30, 0x402000FF );
|
||
pStream.WriteLong( 34, 0x00 );*/
|
||
}
|
||
}
|
||
|
||
CPOpenMessageBoard::CPOpenMessageBoard( CSocket *mSock )
|
||
{
|
||
InternalReset();
|
||
CopyData( mSock );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPOpenMsgBoardPost()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to open a bulletin board msg and/or msg summary
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x71 (Bulletin Board Message)
|
||
//|
|
||
//| Subcommand: 0x1 (Message Summary)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd (0x71)
|
||
//| BYTE[2] len
|
||
//| BYTE subcmd (0x1)
|
||
//| Subcommand details
|
||
//| BYTE[4] BoardID
|
||
//| BYTE[4] MessageID
|
||
//| BYTE[4] ParentID (0 if top level)
|
||
//| BYTE posterLen
|
||
//| BYTE[posterLen] poster (null terminated string)
|
||
//| BYTE subjectLen
|
||
//| BYTE[subjectLen] subject (null terminated string)
|
||
//| BYTE timeLen
|
||
//| BYTE[timeLen] time (null terminated string with time of posting) (“Day 1 @ 11:28”)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Subcommand: 0x2 (Message Summary)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd (0x71)
|
||
//| BYTE[2] len
|
||
//| BYTE subcmd (0x2)
|
||
//| Subcommand details
|
||
//| BYTE[4] BoardID
|
||
//| BYTE[4] MessageID
|
||
//| BYTE posterLen
|
||
//| BYTE[posterLen] poster (null terminated string)
|
||
//| BYTE subjectLen
|
||
//| BYTE[subjectLen] subject (null terminated string)
|
||
//| BYTE timeLen
|
||
//| BYTE[timeLen] time (null terminated string with time of posting) (“Day 1 @ 11:28”)
|
||
//| BYTE[5] Unknown (01 90 03 F7 00)
|
||
//| BYTE numlines
|
||
//| For each line:
|
||
//| BYTE linelen
|
||
//| BYTE[linelen] body (null terminated)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPOpenMsgBoardPost::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 4 );
|
||
pStream.WriteByte( 0, 0x71 );
|
||
if( bFullPost )
|
||
pStream.WriteByte( 3, 2 );
|
||
else
|
||
pStream.WriteByte( 3, 1 );
|
||
}
|
||
|
||
void CPOpenMsgBoardPost::CopyData( CSocket *mSock, const msgBoardPost_st& mbPost )
|
||
{
|
||
size_t totSize = 19 + static_cast<size_t>(mbPost.DateLen) + static_cast<size_t>(mbPost.PosterLen) + static_cast<size_t>(mbPost.SubjectLen);
|
||
std::vector< std::string >::const_iterator pIter;
|
||
if( !bFullPost ) //index
|
||
{
|
||
pStream.ReserveSize( totSize );
|
||
pStream.WriteShort( 1, static_cast<UI16>(totSize) ); //packetSize
|
||
pStream.WriteLong( 4, mSock->GetDWord( 4 ) ); // board serial
|
||
pStream.WriteLong( 8, (mbPost.Serial + BASEITEMSERIAL) ); //message serial
|
||
SERIAL pSerial = mbPost.ParentSerial; // thread serial
|
||
if( pSerial )
|
||
pSerial += BASEITEMSERIAL;
|
||
else
|
||
pSerial += 0x80000000;
|
||
|
||
pStream.WriteLong( 12, pSerial );
|
||
size_t byteOffset = 16;
|
||
|
||
pStream.WriteByte( byteOffset, mbPost.PosterLen );
|
||
pStream.WriteString( ++byteOffset, (char *)mbPost.Poster, mbPost.PosterLen );
|
||
byteOffset += mbPost.PosterLen;
|
||
pStream.WriteByte( byteOffset-1, 0x00 );
|
||
|
||
pStream.WriteByte( byteOffset, mbPost.SubjectLen );
|
||
pStream.WriteString( ++byteOffset, (char *)mbPost.Subject, mbPost.SubjectLen );
|
||
byteOffset += mbPost.SubjectLen;
|
||
pStream.WriteByte( byteOffset-1, 0x00 );
|
||
|
||
pStream.WriteByte( byteOffset, mbPost.DateLen );
|
||
pStream.WriteString( ++byteOffset, (char *)mbPost.Date, mbPost.DateLen );
|
||
byteOffset += mbPost.DateLen;
|
||
pStream.WriteByte( byteOffset-1, 0x00 );
|
||
}
|
||
else if( bFullPost ) //full post
|
||
{
|
||
for( pIter = mbPost.msgBoardLine.begin(); pIter != mbPost.msgBoardLine.end(); ++pIter )
|
||
totSize += (*pIter).size()+5;
|
||
totSize += 1;
|
||
pStream.ReserveSize( totSize );
|
||
pStream.WriteShort( 1, static_cast<UI16>(totSize) ); //packet size
|
||
pStream.WriteLong( 4, mSock->GetDWord( 4 ) ); //board serial
|
||
pStream.WriteLong( 8, (mbPost.Serial + BASEITEMSERIAL) ); //message serial
|
||
size_t offset = 12;
|
||
|
||
CChar *mChar = mSock->CurrcharObj();
|
||
TAGMAPOBJECT modboards = mChar->GetTag( "modboards" );
|
||
if( mChar->IsGM() && modboards.m_IntValue == 1 )
|
||
{
|
||
// If character is a GM, and has used the MSGMOD ON command, send their username instead of poster's,
|
||
// to allow removing messages from the message board
|
||
pStream.WriteByte( offset, static_cast<UI08>(mChar->GetName().length() + 1));
|
||
pStream.WriteString( ++offset, mChar->GetName(), mChar->GetName().length() + 1 );
|
||
offset += mChar->GetName().length() + 1;
|
||
}
|
||
else
|
||
{
|
||
pStream.WriteByte( offset, mbPost.PosterLen + 1 );
|
||
pStream.WriteString( ++offset, (char *)mbPost.Poster, mbPost.PosterLen + 1 );
|
||
offset += mbPost.PosterLen + 1;
|
||
}
|
||
|
||
pStream.WriteByte( offset, mbPost.SubjectLen );
|
||
pStream.WriteString( ++offset, (char *)mbPost.Subject, mbPost.SubjectLen );
|
||
offset += mbPost.SubjectLen;
|
||
|
||
pStream.WriteByte( offset, mbPost.DateLen );
|
||
pStream.WriteString( ++offset, (char *)mbPost.Date, mbPost.DateLen );
|
||
offset += mbPost.DateLen;
|
||
|
||
pStream.WriteShort( offset, 0x0190 ); //postedbody
|
||
pStream.WriteShort( offset+=2, 0x03F7 ); //postedhue
|
||
pStream.WriteByte( offset+=2, 0x00 ); // postedequip-length?
|
||
|
||
pStream.WriteByte( ++offset, mbPost.Lines );
|
||
|
||
for( pIter = mbPost.msgBoardLine.begin(); pIter != mbPost.msgBoardLine.end(); ++pIter )
|
||
{
|
||
pStream.WriteByte( ++offset, static_cast<UI08>((*pIter).size() + 2) );
|
||
pStream.WriteString( ++offset, (*pIter), (*pIter).size() );
|
||
offset += (*pIter).size();
|
||
pStream.WriteByte( offset, 0x00 );
|
||
pStream.WriteByte( ++offset, 0x00 );
|
||
}
|
||
}
|
||
}
|
||
|
||
CPOpenMsgBoardPost::CPOpenMsgBoardPost( CSocket *mSock, const msgBoardPost_st& mbPost, bool fullPost )
|
||
{
|
||
bFullPost = fullPost;
|
||
InternalReset();
|
||
CopyData( mSock, mbPost );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPSendMsgBoardPosts()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to display index of bulletin board messages
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x3C (Bulletin Board Index/Container Content)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] packet length
|
||
//| BYTE[2] number of Items to add
|
||
//| loop items:
|
||
//| BYTE[4] item serial
|
||
//| BYTE[2] item ID (objtype)
|
||
//| BYTE unknown (0x00)
|
||
//| BYTE[2] item amount (stack)
|
||
//| BYTE[2] xLoc
|
||
//| BYTE[2] yLoc
|
||
//| BYTE Backpack grid index
|
||
//| BYTE[4] Container serial
|
||
//| BYTE[2] item color
|
||
//| endloop
|
||
//|
|
||
//| NOTE: Backpack grid index is only sent for client versions above 6.0.1.7 (2D)
|
||
//| and 2.45.5.6 (KR)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPSendMsgBoardPosts::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 5 );
|
||
pStream.WriteByte( 0, 0x3C );
|
||
pStream.WriteShort( 1, 5 );
|
||
}
|
||
|
||
void CPSendMsgBoardPosts::CopyData( CSocket *mSock, SERIAL mSerial, UI08 pToggle, SERIAL oSerial )
|
||
{
|
||
size_t byteOffset = pStream.GetSize();
|
||
if( mSock->ClientVerShort() >= CVS_6017 )
|
||
pStream.ReserveSize( byteOffset+20 );
|
||
else
|
||
pStream.ReserveSize( byteOffset+19 );
|
||
|
||
pStream.WriteLong( byteOffset, (mSerial + BASEITEMSERIAL) );
|
||
pStream.WriteShort( byteOffset+4, 0x0EB0 ); // Item ID
|
||
pStream.WriteByte( byteOffset+6, 0 ); // itemID offset
|
||
pStream.WriteShort( byteOffset+7, 0x0001 ); // item amount
|
||
pStream.WriteShort( byteOffset+9, 0x00 ); // xLoc
|
||
pStream.WriteShort( byteOffset+11, 0x00 ); // yLoc
|
||
if( mSock->ClientVerShort() >= CVS_6017 )
|
||
{
|
||
pStream.WriteByte( byteOffset+13, 0 ); // grid location
|
||
pStream.WriteLong( byteOffset+14, oSerial ); // container serial
|
||
pStream.WriteShort( byteOffset+18, 0x00 ); // item color
|
||
}
|
||
else
|
||
{
|
||
pStream.WriteLong( byteOffset+13, oSerial ); // container serial
|
||
pStream.WriteShort( byteOffset+17, 0x00 ); // item color
|
||
}
|
||
|
||
++postCount;
|
||
}
|
||
|
||
void CPSendMsgBoardPosts::Finalize( void )
|
||
{
|
||
pStream.WriteShort( 1, static_cast<SI32>(pStream.GetSize()) );
|
||
pStream.WriteShort( 3, postCount );
|
||
}
|
||
|
||
CPSendMsgBoardPosts::CPSendMsgBoardPosts()
|
||
{
|
||
postCount = 0;
|
||
InternalReset();
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPExtendedStats()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet with extended stats
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xBF (General Information Packet)
|
||
//| Subcommand: 0x19 (Extended Stats)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd (0xBF)
|
||
//| BYTE[2] Length
|
||
//| BYTE[2] Subcommand (0x19)
|
||
//| Subcommand details
|
||
//| BYTE subsubcommand (0x2 for 2D client, 0x5 for KR client)
|
||
//| BYTE[4] serial
|
||
//| BYTE unknown // always 0 ?
|
||
//| BYTE lockBits
|
||
//| Bits: XXSS DDII
|
||
//| s=strength, d=dex, i=int
|
||
//| 0 = up, 1 = down, 2 = locked, FF = update mobile status animation (KR only)
|
||
//| if subsubcommand is 0x5
|
||
//| If Lock flags = 0xFF //Update mobile status animation
|
||
//| BYTE[1] Status // Unveryfied if lock flags == FF the locks will be handled here
|
||
//| BYTE[1] unknown (0x00)
|
||
//| BYTE[1] Animation
|
||
//| BYTE[1] unknown (0x00)
|
||
//| BYTE[1] Frame
|
||
//| else
|
||
//| BYTE[1] unknown (0x00)
|
||
//| BYTE[4] unknown (0x00000000)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPExtendedStats::CPExtendedStats()
|
||
{
|
||
InternalReset();
|
||
}
|
||
|
||
CPExtendedStats::CPExtendedStats( CChar& mChar )
|
||
{
|
||
InternalReset();
|
||
CopyData( mChar );
|
||
}
|
||
|
||
void CPExtendedStats::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 12 );
|
||
pStream.WriteByte( 0, 0xBF );
|
||
pStream.WriteShort( 1, 12 );
|
||
pStream.WriteShort( 3, 0x19 );
|
||
pStream.WriteByte( 5, 2 );
|
||
}
|
||
|
||
void CPExtendedStats::CopyData( CChar& mChar )
|
||
{
|
||
pStream.WriteLong( 6, mChar.GetSerial() );
|
||
|
||
const UI08 strength = static_cast<UI08>((mChar.GetSkillLock( STRENGTH )&0x3) << 4);
|
||
const UI08 dexterity = static_cast<UI08>((mChar.GetSkillLock( DEXTERITY )&0x3) << 2);
|
||
const UI08 intelligence = static_cast<UI08>(mChar.GetSkillLock( INTELLECT )&0x3);
|
||
|
||
pStream.WriteByte( 11, (strength | dexterity | intelligence) );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPHealthBarStatus()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to update health bar status of character
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x16 (UO3D Mobile New Health Bar Status)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] Packet Size
|
||
//| BYTE[4] Serial
|
||
//| BYTE[2] Extended (1 if status bar is green or yellow, else 0)
|
||
//| BYTE[2] Status Color (0x01 = Green, 0x02 = Yellow, sends only if extended == 1)
|
||
//| BYTE Flag (0x00 = Remove Status Color, 0x01 = Enable Status Color, sends only if extended == 1)
|
||
//| If mobile is poisoned, flag value > 0x00 - poison level.
|
||
//| Since 4.0.7.0/7.0.7.0, sends to both 2d and 3d but works only in 3d. Server
|
||
//| sends it as response for 0x34 Mobile Status Query.
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Packet: 0x17 (Mobile Health Bar Status Update)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] Packet Size
|
||
//| BYTE[4] Mobile Serial
|
||
//| BYTE[2] 0x01
|
||
//| BYTE[2] Status Color (0x01 = Green, 0x02 = Yellow, 0x03 = Red)
|
||
//| BYTE Flag (0x00 = Remove Status Color, 0x01 = Enable Status Color)
|
||
//| If mobile is poisoned, flag value > 0x00 - poison level
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPHealthBarStatus::CPHealthBarStatus()
|
||
{
|
||
InternalReset();
|
||
}
|
||
|
||
CPHealthBarStatus::CPHealthBarStatus( CChar &mChar, CSocket &tSock )
|
||
{
|
||
InternalReset();
|
||
SetHBStatusData( mChar, tSock );
|
||
//CopyData( mChar );
|
||
}
|
||
|
||
void CPHealthBarStatus::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 9 );
|
||
}
|
||
|
||
void CPHealthBarStatus::SetHBStatusData( CChar &mChar, CSocket &tSock )
|
||
{
|
||
if( tSock.ClientType() == CV_UO3D || tSock.ClientType() == CV_KR3D || tSock.ClientType() == CV_SA3D || tSock.ClientType() == CV_HS3D )
|
||
{
|
||
pStream.WriteByte( 0, 0x16 );
|
||
pStream.WriteLong( 3, mChar.GetSerial() );
|
||
CChar *sockChar = tSock.CurrcharObj();
|
||
if(( mChar.GetGuildNumber() == sockChar->GetGuildNumber() ) || mChar.IsInvulnerable() )
|
||
{
|
||
pStream.ReserveSize( 12 );
|
||
pStream.WriteShort( 1, 12 );
|
||
pStream.WriteShort( 7, 0x01 );
|
||
if( mChar.GetGuildNumber() == sockChar->GetGuildNumber() )
|
||
pStream.WriteShort( 9, 1 );
|
||
else if( mChar.IsInvulnerable() )
|
||
pStream.WriteShort( 9, 2 );
|
||
else
|
||
pStream.WriteShort( 9, 0 );
|
||
|
||
if( mChar.GetPoisoned() > 0 )
|
||
pStream.WriteByte( 11, mChar.GetPoisoned() );
|
||
else
|
||
pStream.WriteByte( 11, 0 );
|
||
}
|
||
else
|
||
pStream.WriteShort( 7, 0x0 ); //Packet ends
|
||
}
|
||
else
|
||
{
|
||
pStream.ReserveSize( 12 );
|
||
pStream.WriteByte( 0, 0x17 );
|
||
pStream.WriteShort( 1, 12 );
|
||
pStream.WriteLong( 3, mChar.GetSerial() );
|
||
pStream.WriteShort( 7, 0x01 );
|
||
CChar *sockChar = tSock.CurrcharObj();
|
||
if( mChar.GetGuildNumber() == sockChar->GetGuildNumber() )
|
||
pStream.WriteShort( 9, 1 );
|
||
else if( mChar.IsInvulnerable() )
|
||
pStream.WriteShort( 9, 2 );
|
||
else if( mChar.GetNPCAiType() == AI_EVIL || mChar.IsMurderer() )
|
||
pStream.WriteShort( 9, 3 );
|
||
else
|
||
pStream.WriteShort( 9, 0 );
|
||
if( mChar.GetPoisoned() > 0 )
|
||
pStream.WriteByte( 11, mChar.GetPoisoned() );
|
||
else
|
||
pStream.WriteByte( 11, 0 );
|
||
}
|
||
}
|
||
|
||
void CPHealthBarStatus::CopyData( CChar& mChar )
|
||
{
|
||
pStream.WriteLong( 6, mChar.GetSerial() );
|
||
|
||
const UI08 strength = static_cast<UI08>((mChar.GetSkillLock( STRENGTH )&0x3) << 4);
|
||
const UI08 dexterity = static_cast<UI08>((mChar.GetSkillLock( DEXTERITY )&0x3) << 2);
|
||
const UI08 intelligence = static_cast<UI08>(mChar.GetSkillLock( INTELLECT )&0x3);
|
||
|
||
pStream.WriteByte( 11, (strength | dexterity | intelligence) );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPEnableMapDiffs()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet enabling map diffs in client
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xBF (General Information Packet)
|
||
//| Subcommand: 0x18 (Enable Map-Diff files)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd (0xBF)
|
||
//| BYTE[2] Length
|
||
//| BYTE[2] Subcommand (0x18)
|
||
//| Subcommand details
|
||
//| BYTE[4] Number of maps
|
||
//| For each map
|
||
//| BYTE[4] Number of map patches in this map (these two lines are in reverse order!)
|
||
//| BYTE[4] Number of static patches in this map
|
||
//|
|
||
//| Note: Only client versions lower than v7.0.8.2 will load diff files
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPEnableMapDiffs::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 6 );
|
||
pStream.WriteByte( 0, 0xBF );
|
||
pStream.WriteShort( 1, 0x0006 );
|
||
pStream.WriteShort( 3, 0x0018 );
|
||
}
|
||
CPEnableMapDiffs::CPEnableMapDiffs()
|
||
{
|
||
InternalReset();
|
||
CopyData();
|
||
}
|
||
|
||
void CPEnableMapDiffs::CopyData( void )
|
||
{
|
||
UI08 mapCount = Map->MapCount();
|
||
size_t pSize = ((static_cast<size_t>(mapCount)+1)*8)+9;
|
||
|
||
pStream.ReserveSize( pSize );
|
||
pStream.WriteShort( 1, static_cast<SI32>(pSize) );
|
||
pStream.WriteLong( 5, mapCount+1 );
|
||
|
||
for( UI08 i = 0; i < mapCount; ++i )
|
||
{
|
||
MapData_st &mMap = Map->GetMapData( i );
|
||
pStream.WriteLong( 9+(static_cast<size_t>(i)*8), static_cast<UI32>(mMap.staticsDiffIndex.size()) );
|
||
pStream.WriteLong( 13+(static_cast<size_t>(i)*8), static_cast<UI32>(mMap.mapDiffList.size()) );
|
||
}
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPNewBookHeader()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to respond to client request to change book title
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xD4 (New Book Header)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] length
|
||
//| BYTE[4] book ID
|
||
//| BYTE flag1
|
||
//| BYTE flag2
|
||
//| BYTE[2] number of pages
|
||
//| BYTE[2] length of author string (0 terminator included)
|
||
//| BYTE[?] author (ASCII, 0 terminated)
|
||
//| BYTE[2] length of title string (0 terminator included)
|
||
//| BYTE[?] title string
|
||
//|
|
||
//| Note1:
|
||
//| server and client packet.
|
||
//| server side: opening book writeable: flag1+flag2 both 1
|
||
//| (opening readonly book: flag1+flag2 0, unverified though)
|
||
//| client side: flag1+flag2 both 0, number of pages 0.
|
||
//|
|
||
//| Note2:
|
||
//| Opening books goes like this:
|
||
//| open book ->
|
||
//| server sends 0xd4(title + author)
|
||
//| server sends 0x66 with all book data "beyond" title + author
|
||
//| if title + author changed: client side 0xd4 send
|
||
//| if other book pages changed: 0x66 client side send.
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPNewBookHeader::CPNewBookHeader()
|
||
{
|
||
InternalReset();
|
||
}
|
||
|
||
void CPNewBookHeader::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 12 );
|
||
pStream.WriteByte( 0, 0xD4 );
|
||
pStream.WriteShort( 1, 12 );
|
||
}
|
||
|
||
void CPNewBookHeader::Serial( UI32 bookSer )
|
||
{
|
||
pStream.WriteLong( 3, bookSer );
|
||
}
|
||
|
||
void CPNewBookHeader::Flag1( UI08 toSet )
|
||
{
|
||
pStream.WriteByte( 7, toSet );
|
||
}
|
||
|
||
void CPNewBookHeader::Flag2( UI08 toSet )
|
||
{
|
||
pStream.WriteByte( 8, toSet );
|
||
}
|
||
|
||
void CPNewBookHeader::Pages( UI16 numPages )
|
||
{
|
||
pStream.WriteShort( 9, numPages );
|
||
}
|
||
|
||
void CPNewBookHeader::Author( const std::string& newAuthor )
|
||
{
|
||
author = newAuthor;
|
||
}
|
||
|
||
void CPNewBookHeader::Title( const std::string& newTitle )
|
||
{
|
||
title = newTitle;
|
||
}
|
||
|
||
void CPNewBookHeader::Finalize( void )
|
||
{
|
||
const UI16 authorLen = static_cast<UI16>(author.size());
|
||
const UI16 titleLen = static_cast<UI16>(title.size());
|
||
const UI16 totalLen = static_cast<UI16>(15+authorLen+titleLen+2);
|
||
size_t offset = 11;
|
||
|
||
pStream.ReserveSize( totalLen );
|
||
pStream.WriteShort( 1, totalLen );
|
||
|
||
pStream.WriteShort( offset, titleLen+1 );
|
||
pStream.WriteString( offset+=2, title, titleLen );
|
||
pStream.WriteByte( offset+=titleLen, 0x00 );
|
||
|
||
pStream.WriteShort( offset+=1, authorLen+1 );
|
||
pStream.WriteString( offset+=2, author, authorLen );
|
||
pStream.WriteByte( offset+=authorLen, 0x00 );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPPopupMenu()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to display context menu
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xBF (General Information Packet)
|
||
//| Subcommand: 0x14 (Display Popup menu, Old version)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd (0xBF)
|
||
//| BYTE[2] Length
|
||
//| BYTE[2] Subcommand (0x14)
|
||
//| Subcommand details
|
||
//| BYTE unknown (0x00)
|
||
//| BYTE subsubcommand
|
||
//| 0x01 for 2D < 7.0.0.0
|
||
//| 0x02 for KR, SA3D and 2D > 7.0.0.0
|
||
//| BYTE[4] Serial
|
||
//| BYTE Number of entries in the popup
|
||
//| if subsubcommand is 0x01:
|
||
//| Loop for each entry:
|
||
//| BYTE[2] Entry Tag (this will be returned by the client on selection)
|
||
//| BYTE[2] Cliloc ID (file number for intloc#.language e.g intloc6.enu and the index into that)
|
||
//| BYTE[2] Flags 0x00=enabled, 0x01=disabled, 0x02=arrow, 0x20 = color
|
||
//| If (Flags &0x20)
|
||
//| BYTE[2] color
|
||
//| rgb 1555 color (ex, 0 = transparent, 0x8000 = solid black,
|
||
//| 0x1F = blue, 0x3E0 = green, 0x7C00 = red)
|
||
//| if subsubcommand is 0x02:
|
||
//| Loop for each entry:
|
||
//| BYTE[4] Cliloc ID
|
||
//| BYTE[2] Index of entry (entry tag)?
|
||
//| BYTE[2] Flags 0x00=enabled, 0x01=disabled, 0x04 = highlighted, 0x20 = color
|
||
//| If (flags &0x20 and 2D client)
|
||
//| BYTE[2] color
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPPopupMenu::CPPopupMenu( void )
|
||
{
|
||
InternalReset();
|
||
}
|
||
|
||
CPPopupMenu::CPPopupMenu( CChar& toCopy )
|
||
{
|
||
InternalReset();
|
||
CopyData( toCopy );
|
||
}
|
||
|
||
void CPPopupMenu::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 5 );
|
||
pStream.WriteByte( 0, 0xBF );
|
||
pStream.WriteShort( 1, 5 );
|
||
pStream.WriteShort( 3, 0x14 );
|
||
pStream.WriteShort( 5, 0x0001 );
|
||
}
|
||
|
||
void CPPopupMenu::CopyData( CChar& toCopy )
|
||
{
|
||
UI16 packetLen = (12 + (4 * 8));
|
||
pStream.ReserveSize( packetLen );
|
||
pStream.WriteShort( 1, packetLen );
|
||
|
||
pStream.WriteLong( 7, toCopy.GetSerial() );
|
||
pStream.WriteByte( 11, 4 );
|
||
size_t offset = 12;
|
||
|
||
pStream.WriteShort( offset, 0x000A ); // Open Paperdoll
|
||
pStream.WriteShort( offset+=2, 6123 );
|
||
if( cwmWorldState->creatures[toCopy.GetID()].IsHuman() )
|
||
{
|
||
pStream.WriteShort( offset+=2, 0x0020 );
|
||
pStream.WriteShort( offset+=2, 0x03E0 );
|
||
}
|
||
else
|
||
{
|
||
pStream.WriteShort( offset+=2, 0x0021 );
|
||
pStream.WriteShort( offset+=2, 0xFFFF );
|
||
}
|
||
|
||
pStream.WriteShort( offset+=2, 0x000B ); // Open Backpack
|
||
pStream.WriteShort( offset+=2, 6145 );
|
||
if( ( cwmWorldState->creatures[toCopy.GetID()].IsHuman() || toCopy.GetID() == 0x0123 || toCopy.GetID() == 0x0124 ) && ValidateObject( toCopy.GetPackItem() ) )
|
||
{
|
||
pStream.WriteShort( offset+=2, 0x0020 );
|
||
pStream.WriteShort( offset+=2, 0x03E0 );
|
||
}
|
||
else
|
||
{
|
||
pStream.WriteShort( offset+=2, 0x0021 );
|
||
pStream.WriteShort( offset+=2, 0xFFFF );
|
||
}
|
||
|
||
pStream.WriteShort( offset+=2, 0x000C ); // Shopkeep
|
||
pStream.WriteShort( offset+=2, 6103 );
|
||
if( toCopy.IsShop() )
|
||
{
|
||
pStream.WriteShort( offset+=2, 0x0020 );
|
||
pStream.WriteShort( offset+=2, 0x03E0 );
|
||
}
|
||
else
|
||
{
|
||
pStream.WriteShort( offset+=2, 0x0021 );
|
||
pStream.WriteShort( offset+=2, 0xFFFF );
|
||
}
|
||
|
||
pStream.WriteShort( offset+=2, 0x000D );
|
||
pStream.WriteShort( offset+=2, 6104 );
|
||
if( toCopy.IsShop() )
|
||
{
|
||
pStream.WriteShort( offset+=2, 0x0020 );
|
||
pStream.WriteShort( offset+=2, 0x03E0 );
|
||
}
|
||
else
|
||
{
|
||
pStream.WriteShort( offset+=2, 0x0021 );
|
||
pStream.WriteShort( offset+=2, 0xFFFF );
|
||
}
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPClilocMessage()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet with cliloc message
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xC1 (Cliloc Message)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] length
|
||
//| BYTE[4] serial (0xffff for system message)
|
||
//| BYTE[2] body/id (0xff for system message)
|
||
//| BYTE type (6 - lower left, 7 on player)
|
||
//| Message Type
|
||
//| 0x00 = Regular, 0x01 = System, 0x02 = Emote, 0x06 = Label,
|
||
//| 0x07 = Focus, 0x08 = Whisper, 0x09 = Yell, 0x0A = Spell,
|
||
//| 0x0D = Guild, 0x0E = Alliance, 0x0F = GM Request, 0x10 = GM Response,
|
||
//| 0x20 = Special, 0xC0 = Encoded
|
||
//| BYTE[2] hue
|
||
//| BYTE[2] font
|
||
//| BYTE[4] Message number
|
||
//| BYTE[30] - speaker's name
|
||
//| BYTE[?*2] - arguments // _little-endian_ unicode string, tabs ('\t') seperate the arguments
|
||
//| Argument example:
|
||
//| take number 1042762:
|
||
//| "Only ~1_AMOUNT~ gold could be deposited. A check for ~2_CHECK_AMOUNT~ gold was returned to you."
|
||
//| the arguments string may have "100 thousand\t25 hundred", which in turn would modify the string:
|
||
//| "Only 100 thousand gold could be deposited. A check for 25 hundred gold was returned to you."
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPClilocMessage::CPClilocMessage( void )
|
||
{
|
||
InternalReset();
|
||
Serial( INVALIDSERIAL );
|
||
Body( INVALIDID );
|
||
Name( "System" );
|
||
}
|
||
|
||
CPClilocMessage::CPClilocMessage( CBaseObject& toCopy )
|
||
{
|
||
InternalReset();
|
||
CopyData( toCopy );
|
||
}
|
||
|
||
void CPClilocMessage::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 48 );
|
||
pStream.WriteByte( 0, 0xC1 );
|
||
pStream.WriteShort( 1, 48 );
|
||
}
|
||
|
||
void CPClilocMessage::CopyData( CBaseObject& toCopy )
|
||
{
|
||
Serial( toCopy.GetSerial() );
|
||
Body( toCopy.GetID() );
|
||
Name( toCopy.GetName() );
|
||
}
|
||
|
||
void CPClilocMessage::Serial( SERIAL toSet )
|
||
{
|
||
pStream.WriteLong( 3, toSet );
|
||
}
|
||
|
||
void CPClilocMessage::Body( UI16 toSet )
|
||
{
|
||
pStream.WriteShort( 7, toSet );
|
||
}
|
||
|
||
void CPClilocMessage::Type( UI08 toSet )
|
||
{
|
||
pStream.WriteByte( 9, toSet );
|
||
}
|
||
|
||
void CPClilocMessage::Hue( UI16 hueColor )
|
||
{
|
||
pStream.WriteShort( 10, hueColor );
|
||
}
|
||
|
||
void CPClilocMessage::Font( UI16 fontType )
|
||
{
|
||
pStream.WriteShort( 12, fontType );
|
||
}
|
||
|
||
void CPClilocMessage::Message( UI32 messageNum )
|
||
{
|
||
pStream.WriteLong( 14, messageNum );
|
||
}
|
||
|
||
void CPClilocMessage::Name( const std::string& name )
|
||
{
|
||
if( name.size() > 29 )
|
||
{
|
||
pStream.WriteString( 18, name, 29 );
|
||
pStream.WriteByte( 47, 0x00 );
|
||
}
|
||
else
|
||
pStream.WriteString( 18, name, 30 );
|
||
}
|
||
|
||
void CPClilocMessage::ArgumentString( const std::string& arguments )
|
||
{
|
||
const size_t stringLen = arguments.size();
|
||
const UI16 packetLen = static_cast<UI16>(pStream.GetShort( 1 ) + (stringLen * 2) + 2);
|
||
pStream.ReserveSize( packetLen );
|
||
pStream.WriteShort( 1, packetLen );
|
||
|
||
for( size_t i = 0; i < stringLen; ++i )
|
||
pStream.WriteByte( 48 + i * 2, arguments[i] );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPPartyMemberList()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet with response to client request to add someone to party
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xBF (General Information Packet)
|
||
//| Subcommand: 0x06 (Party Commands)
|
||
//| Subsubcommand: 0x01 (Party Add Member)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd (0xBF)
|
||
//| BYTE[2] Length
|
||
//| BYTE[2] Subcommand (0x06)
|
||
//| Subcommand details
|
||
//| BYTE subsubcommand (0x01)
|
||
//| BYTE number of Members (total number of members in the party)
|
||
//| Then, loop for each member in numMembers:
|
||
//| BYTE[4] serial
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPPartyMemberList::CPPartyMemberList( void )
|
||
{
|
||
InternalReset();
|
||
}
|
||
|
||
void CPPartyMemberList::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 7 );
|
||
pStream.WriteByte( 0, 0xBF ); // packet ID
|
||
pStream.WriteShort( 1, 7 ); // packet length
|
||
pStream.WriteShort( 3, 6 ); // party command
|
||
pStream.WriteByte( 5, 1 ); // subcommand
|
||
pStream.WriteByte( 6, 0 ); // member Count
|
||
}
|
||
|
||
void CPPartyMemberList::AddMember( CChar *member )
|
||
{
|
||
size_t curPos = pStream.GetSize();
|
||
pStream.ReserveSize( curPos + 4 );
|
||
pStream.WriteLong( curPos, member->GetSerial() );
|
||
pStream.WriteByte( 6, pStream.GetByte( 6 ) + 1 );
|
||
pStream.WriteShort( 1, static_cast<SI32>(curPos) + 4 );
|
||
}
|
||
|
||
void CPPartyMemberList::Log( std::ofstream &outStream, bool fullHeader )
|
||
{
|
||
if( fullHeader )
|
||
outStream << "[SEND]Packet : CPPartyMemberList 0xBF Subcommand Party Command --> Length: " << pStream.GetSize() << TimeStamp() << std::endl;
|
||
outStream << "Packet ID : " << std::hex << (UI16)pStream.GetByte( 0 ) << std::dec << std::endl;
|
||
outStream << "Subcommand : " << pStream.GetShort( 3 ) << std::endl;
|
||
outStream << "Party Sub : " << (UI16)pStream.GetByte( 5 ) << std::endl;
|
||
outStream << "Member Count : " << (UI16)pStream.GetByte( 6 ) << std::endl;
|
||
for( SI32 i = 0; i < pStream.GetByte( 6 ); ++i )
|
||
outStream << " Member #" << i << " : " << std::hex << "0x" << pStream.GetLong( 7 + 4 * static_cast<size_t>(i) ) << std::dec << std::endl;
|
||
outStream << " Raw dump :" << std::endl;
|
||
CPUOXBuffer::Log( outStream, false );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPPartyInvitation()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet with invitation for player to join a party
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xBF (General Information Packet)
|
||
//| Subcommand: 0x06 (Party Commands)
|
||
//| Subsubcommand: 0x07 (Party Invitation)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd (0xBF)
|
||
//| BYTE[2] Length
|
||
//| BYTE[2] Subcommand (0x06)
|
||
//| Subcommand details
|
||
//| BYTE subsubcommand (0x07)
|
||
//| BYTE[4] serial (party leader)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPPartyInvitation::CPPartyInvitation( void )
|
||
{
|
||
InternalReset();
|
||
}
|
||
|
||
void CPPartyInvitation::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 7 );
|
||
pStream.WriteByte( 0, 0xBF ); // packet ID
|
||
pStream.WriteShort( 1, 10 ); // packet length
|
||
pStream.WriteShort( 3, 6 ); // party command
|
||
pStream.WriteByte( 5, 7 ); // subcommand
|
||
}
|
||
|
||
void CPPartyInvitation::Leader( CChar *leader )
|
||
{
|
||
pStream.WriteLong( 6, leader->GetSerial() );
|
||
}
|
||
|
||
void CPPartyInvitation::Log( std::ofstream &outStream, bool fullHeader )
|
||
{
|
||
if( fullHeader )
|
||
outStream << "[SEND]Packet : CPPartyInvitation 0xBF Subcommand Party Command --> Length: " << pStream.GetSize() << TimeStamp() << std::endl;
|
||
outStream << "Packet ID : " << std::hex << (UI16)pStream.GetByte( 0 ) << std::dec << std::endl;
|
||
outStream << "Subcommand : " << pStream.GetShort( 3 ) << std::endl;
|
||
outStream << "Party Sub : " << (UI16)pStream.GetByte( 5 ) << std::endl;
|
||
outStream << "Leader Serial : 0x" << std::hex << pStream.GetLong( 6 ) << std::dec << std::endl;
|
||
outStream << " Raw dump :" << std::endl;
|
||
CPUOXBuffer::Log( outStream, false );
|
||
}
|
||
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPPartyMemberRemove()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to remove member from party
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xBF (General Information Packet)
|
||
//| Subcommand: 0x06 (Party Commands)
|
||
//| Subsubcommand: 0x02 (Remove Party Member)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd (0xBF)
|
||
//| BYTE[2] Length
|
||
//| BYTE[2] Subcommand (0x06)
|
||
//| Subcommand details
|
||
//| BYTE subsubcommand (0x02)
|
||
//| BYTE membersInNewParty
|
||
//| BYTE[4] idOfRemovedPlayer
|
||
//| For each member
|
||
//| BYTE[4] memberID
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPPartyMemberRemove::CPPartyMemberRemove( CChar *removed )
|
||
{
|
||
InternalReset();
|
||
pStream.WriteLong( 7, removed->GetSerial() );
|
||
}
|
||
|
||
void CPPartyMemberRemove::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 11 );
|
||
pStream.WriteByte( 0, 0xBF ); // packet ID
|
||
pStream.WriteShort( 1, 11 ); // packet length
|
||
pStream.WriteShort( 3, 6 ); // party command
|
||
pStream.WriteByte( 5, 2 ); // subcommand
|
||
pStream.WriteByte( 6, 0 ); // member Count
|
||
}
|
||
|
||
void CPPartyMemberRemove::AddMember( CChar *member )
|
||
{
|
||
size_t curPos = pStream.GetSize();
|
||
pStream.ReserveSize( curPos + 4 );
|
||
pStream.WriteLong( curPos, member->GetSerial() );
|
||
pStream.WriteByte( 6, pStream.GetByte( 6 ) + 1 );
|
||
pStream.WriteShort( 1, static_cast<SI32>(curPos) + 4 );
|
||
}
|
||
|
||
void CPPartyMemberRemove::Log( std::ofstream &outStream, bool fullHeader )
|
||
{
|
||
if( fullHeader )
|
||
outStream << "[SEND]Packet : CPPartyMemberRemove 0xBF Subcommand Party Command --> Length: " << pStream.GetSize() << TimeStamp() << std::endl;
|
||
outStream << "Packet ID : " << std::hex << (UI16)pStream.GetByte( 0 ) << std::dec << std::endl;
|
||
outStream << "Subcommand : " << pStream.GetShort( 3 ) << std::endl;
|
||
outStream << "Party Sub : " << (UI16)pStream.GetByte( 5 ) << std::endl;
|
||
outStream << "Member Count : " << (UI16)pStream.GetByte( 6 ) << std::endl;
|
||
outStream << "Member Removed : 0x" << std::hex << (UI16)pStream.GetLong( 7 ) << std::dec << std::endl;
|
||
outStream << "Members : " << std::endl;
|
||
for( SI32 i = 0; i < pStream.GetByte( 6 ); ++i )
|
||
outStream << " Member #" << i << " : " << std::hex << "0x" << pStream.GetLong( 11 + 4 * static_cast<size_t>(i) ) << std::dec << std::endl;
|
||
outStream << " Raw dump :" << std::endl;
|
||
CPUOXBuffer::Log( outStream, false );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPPartyTell()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to send a message to a specific party member
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xBF (General Information Packet)
|
||
//| Subcommand: 0x06 (Party Commands)
|
||
//| Subsubcommand: 0x03 (Tell party member a message)
|
||
//| Size: Variable
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd (0xBF)
|
||
//| BYTE[2] Length
|
||
//| BYTE[2] Subcommand (0x06)
|
||
//| Subcommand details
|
||
//| BYTE subsubcommand (0x03)
|
||
//| BYTE[4] id (of target, from client, of source, from server)
|
||
//| BYTE[n][2] Null terminated Unicode message.
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPPartyTell::CPPartyTell( CPIPartyCommand *removed, CSocket *talker )
|
||
{
|
||
InternalReset();
|
||
CSocket *talkSocket = removed->GetSocket();
|
||
// let's mirror tell all/indiv
|
||
UI08 tellMode = talkSocket->GetByte( 5 );
|
||
pStream.WriteByte( 5, tellMode );
|
||
pStream.WriteLong( 6, talker->CurrcharObj()->GetSerial() );
|
||
|
||
UI16 sizeModifier = 6;
|
||
if( tellMode == 3 )
|
||
sizeModifier = 10;
|
||
|
||
UI16 messageLength = talkSocket->GetWord( 1 ) - sizeModifier;
|
||
|
||
pStream.ReserveSize( 12 + static_cast<size_t>(messageLength) );
|
||
pStream.WriteShort( 1, 12 + messageLength );
|
||
for( size_t i = 0; i < messageLength; ++i )
|
||
pStream.WriteByte( i + 10, talkSocket->GetByte( sizeModifier + i ) );
|
||
pStream.WriteShort( 10 + static_cast<size_t>(messageLength), 0 );
|
||
}
|
||
|
||
void CPPartyTell::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 12 );
|
||
pStream.WriteByte( 0, 0xBF ); // packet ID
|
||
pStream.WriteShort( 1, 12 ); // packet length
|
||
pStream.WriteShort( 3, 6 ); // party command
|
||
pStream.WriteByte( 5, 3 ); // subcommand
|
||
}
|
||
|
||
void CPPartyTell::Log( std::ofstream &outStream, bool fullHeader )
|
||
{
|
||
if( fullHeader )
|
||
outStream << "[SEND]Packet : CPPartyTell 0xBF Subcommand Party Command --> Length: " << pStream.GetSize() << TimeStamp() << std::endl;
|
||
outStream << "Packet ID : " << std::hex << (UI16)pStream.GetByte( 0 ) << std::dec << std::endl;
|
||
outStream << "Subcommand : " << pStream.GetShort( 3 ) << std::endl;
|
||
outStream << "Party Sub : " << (UI16)pStream.GetByte( 5 ) << std::endl;
|
||
outStream << "Talker : 0x" << std::hex << pStream.GetLong( 6 ) << std::dec << std::endl;
|
||
outStream << "Message : ";
|
||
for( size_t j = 10; j < pStream.GetSize(); ++j )
|
||
outStream << (UI08)pStream.GetByte( j );
|
||
outStream << std::endl;
|
||
outStream << " Raw dump :" << std::endl;
|
||
CPUOXBuffer::Log( outStream, false );
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPClientVersion()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet to request client version
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0xBD (Client Version Request)
|
||
//| Size: 3 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//| BYTE[2] length
|
||
//|
|
||
//| Notes
|
||
//| Server version : 0xbd 0x0 0x3 (client replies with client version of this packet)
|
||
//| Clients sends a client version of this packet ONCE at login (without server request.)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
void CPClientVersion::InternalReset( void )
|
||
{
|
||
pStream.ReserveSize( 3 );
|
||
pStream.WriteByte( 0, 0xBD );
|
||
pStream.WriteByte( 2, 0x03 );
|
||
}
|
||
CPClientVersion::CPClientVersion()
|
||
{
|
||
InternalReset();
|
||
}
|
||
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Function - CPDropItemApproved()
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Purpose - Handles outgoing packet
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
//| Notes - Packet: 0x29 (Drop Item Approved)
|
||
//| Size: 1 bytes
|
||
//|
|
||
//| Packet Build
|
||
//| BYTE cmd
|
||
//|
|
||
//| Notes
|
||
//| Older 2D Clients this was for Paperdoll ackknowledgement.
|
||
//| Newest (KR era and 6.0.1.7 and higher) use this also for the regular drop item packet.
|
||
//| > 6.0.1.7 = Client sends Drop On Paperdoll (0x13), Server Responds with This (0x29)
|
||
//| < 6.0.1.7 = Client sends Drop On Paperdoll (0x13) or sends Drop Item (0x08), server responds with this (0x29)
|
||
//o-----------------------------------------------------------------------------------------------o
|
||
CPDropItemApproved::CPDropItemApproved()
|
||
{
|
||
pStream.ReserveSize( 1 );
|
||
pStream.WriteByte( 0, 0x29 );
|
||
}
|