diff --git a/changelog.txt b/changelog.txt
index cb9c9a6af..fc815952e 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,5 +1,18 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50)
-------------------------------------------------------
+
+== 04/27/2013 ==
+Pixel Bounds: Verify OP_Ack size & fix crash in BasePacket::build_raw_header_dump due to uninitialised timestamp.
+Derision: Verify minimum size of OP_Packet, OP_Fragment and OP_OutOfOrderAck.
+
+== 04/24/2013 ==
+Bad_Captain: Fixed a couple of merc stat issues.
+Bad_Captain: Removed unneeded bot pet AI.
+Bad_Captain: Fixed a few bot aggro issues. (Uleat)
+
+== 04/20/2013 ==
+JJ: Fixed rare case where heals from buffs could go negative.
+
== 04/12/2013 ==
Derision: Moved entity_list.Clear() prior to destruction of Perl objects in zone shutdown as I was seeing a segfault due to attempts to call EVENT_HATE_LIST as mobs were being destroyed.
diff --git a/common/BasePacket.cpp b/common/BasePacket.cpp
index 2717ee354..82fd13649 100644
--- a/common/BasePacket.cpp
+++ b/common/BasePacket.cpp
@@ -28,6 +28,7 @@ BasePacket::BasePacket(const unsigned char *buf, uint32 len)
this->size=0;
this->_wpos = 0;
this->_rpos = 0;
+ this->timestamp.tv_sec = 0;
if (len>0) {
this->size=len;
pBuffer= new unsigned char[len];
diff --git a/common/EQStream.cpp b/common/EQStream.cpp
index 561c8f4a6..ae5c6953c 100644
--- a/common/EQStream.cpp
+++ b/common/EQStream.cpp
@@ -173,6 +173,11 @@ uint32 processed=0,subpacket_length=0;
break;
case OP_Packet: {
+ if(!p->pBuffer || (p->Size() < 4))
+ {
+ _log(NET__ERROR, _L "Received OP_Packet that was of malformed size" __L);
+ break;
+ }
uint16 seq=ntohs(*(uint16 *)(p->pBuffer));
SeqOrder check=CompareSequence(NextInSeq,seq);
if (check == SeqFuture) {
@@ -218,6 +223,11 @@ uint32 processed=0,subpacket_length=0;
break;
case OP_Fragment: {
+ if(!p->pBuffer || (p->Size() < 4))
+ {
+ _log(NET__ERROR, _L "Received OP_Fragment that was of malformed size" __L);
+ break;
+ }
uint16 seq=ntohs(*(uint16 *)(p->pBuffer));
SeqOrder check=CompareSequence(NextInSeq,seq);
if (check == SeqFuture) {
@@ -284,6 +294,11 @@ uint32 processed=0,subpacket_length=0;
}
break;
case OP_Ack: {
+ if(!p->pBuffer || (p->Size() < 4))
+ {
+ _log(NET__ERROR, _L "Received OP_Ack that was of malformed size" __L);
+ break;
+ }
#ifndef COLLECTOR
uint16 seq=ntohs(*(uint16 *)(p->pBuffer));
AckPackets(seq);
@@ -383,6 +398,11 @@ uint32 processed=0,subpacket_length=0;
}
break;
case OP_OutOfOrderAck: {
+ if(!p->pBuffer || (p->Size() < 4))
+ {
+ _log(NET__ERROR, _L "Received OP_OutOfOrderAck that was of malformed size" __L);
+ break;
+ }
#ifndef COLLECTOR
uint16 seq=ntohs(*(uint16 *)(p->pBuffer));
MOutboundQueue.lock();
diff --git a/utils/EQExtractor2/EQExtractor2/ChangeLog.txt b/utils/EQExtractor2/EQExtractor2/ChangeLog.txt
index be8d241dc..2181b9279 100644
--- a/utils/EQExtractor2/EQExtractor2/ChangeLog.txt
+++ b/utils/EQExtractor2/EQExtractor2/ChangeLog.txt
@@ -1,5 +1,11 @@
EQExtractor2 Changelog. All changes since the 1.0 release.
+==04/25/2013==
+Derision: Added SQL generation support to current Live client decoder.
+
+==04/24/2013==
+Derision: Added decoder to support packet dumps from current Live client. SQL generation NOT SUPPORTED YET (need tweaks to position struct).
+
==03/17/2013==
Derision: Added SQL generation support to current Live client decoder.
diff --git a/utils/EQExtractor2/EQExtractor2/EQApplicationLayer.cs b/utils/EQExtractor2/EQExtractor2/EQApplicationLayer.cs
index b6732ca82..58537e9be 100644
--- a/utils/EQExtractor2/EQExtractor2/EQApplicationLayer.cs
+++ b/utils/EQExtractor2/EQExtractor2/EQApplicationLayer.cs
@@ -104,6 +104,8 @@ namespace EQApplicationLayer
PatchList.Add(new PatchFebruary112013Decoder());
PatchList.Add(new PatchMarch132013Decoder());
+
+ PatchList.Add(new PatchApril152013Decoder());
PatchList.Add(new PatchSoD());
diff --git a/utils/EQExtractor2/EQExtractor2/EQExtractor2.csproj b/utils/EQExtractor2/EQExtractor2/EQExtractor2.csproj
index 6e56a9c58..542700b6c 100644
--- a/utils/EQExtractor2/EQExtractor2/EQExtractor2.csproj
+++ b/utils/EQExtractor2/EQExtractor2/EQExtractor2.csproj
@@ -72,6 +72,7 @@
LogForm.cs
+
@@ -137,6 +138,9 @@
UserOptions.cs
+
+ Always
+
Always
diff --git a/utils/EQExtractor2/EQExtractor2/EQExtractor2Form1.cs b/utils/EQExtractor2/EQExtractor2/EQExtractor2Form1.cs
index 7f28f792a..4fb5096bc 100644
--- a/utils/EQExtractor2/EQExtractor2/EQExtractor2Form1.cs
+++ b/utils/EQExtractor2/EQExtractor2/EQExtractor2Form1.cs
@@ -18,7 +18,7 @@ namespace EQExtractor2
{
public partial class EQExtractor2Form1 : Form
{
- string Version = "EQExtractor2 Version 2.6.2 SVN";
+ string Version = "EQExtractor2 Version 2.6.4 GIT";
static int PacketsSeen = 0;
static long BytesRead = 0;
diff --git a/utils/EQExtractor2/EQExtractor2/PatchApril15-2013.cs b/utils/EQExtractor2/EQExtractor2/PatchApril15-2013.cs
new file mode 100644
index 000000000..9ae389832
--- /dev/null
+++ b/utils/EQExtractor2/EQExtractor2/PatchApril15-2013.cs
@@ -0,0 +1,240 @@
+using System;
+using System.IO;
+using System.Diagnostics;
+using System.Collections.Generic;
+using EQExtractor2.InternalTypes;
+using EQExtractor2.OpCodes;
+using EQPacket;
+using MyUtils;
+
+namespace EQExtractor2.Patches
+{
+ class PatchApril152013Decoder : PatchMarch132013Decoder
+ {
+ public PatchApril152013Decoder()
+ {
+ Version = "EQ Client Build Date April 15 2013.";
+
+ PatchConfFileName = "patch_April15-2013.conf";
+
+ SupportsSQLGeneration = true;
+ }
+
+ override public List GetSpawns()
+ {
+ List ZoneSpawns = new List();
+
+ List SpawnPackets = GetPacketsOfType("OP_ZoneEntry", PacketDirection.ServerToClient);
+
+ foreach (byte[] SpawnPacket in SpawnPackets)
+ {
+ ZoneEntryStruct NewSpawn = new ZoneEntryStruct();
+
+ ByteStream Buffer = new ByteStream(SpawnPacket);
+
+ NewSpawn.SpawnName = Buffer.ReadString(true);
+
+ NewSpawn.SpawnName = Utils.MakeCleanName(NewSpawn.SpawnName);
+
+ NewSpawn.SpawnID = Buffer.ReadUInt32();
+
+ NewSpawn.Level = Buffer.ReadByte();
+
+ float UnkSize = Buffer.ReadSingle();
+
+ NewSpawn.IsNPC = Buffer.ReadByte();
+
+ UInt32 Bitfield = Buffer.ReadUInt32();
+
+ NewSpawn.Gender = (Bitfield & 3);
+
+ Byte OtherData = Buffer.ReadByte();
+
+ Buffer.SkipBytes(8); // Skip 8 unknown bytes
+
+ NewSpawn.DestructableString1 = "";
+ NewSpawn.DestructableString2 = "";
+ NewSpawn.DestructableString3 = "";
+
+ if ((NewSpawn.IsNPC > 0) && ((OtherData & 1) > 0))
+ {
+ // Destructable Objects
+ NewSpawn.DestructableString1 = Buffer.ReadString(false);
+ NewSpawn.DestructableString2 = Buffer.ReadString(false);
+ NewSpawn.DestructableString3 = Buffer.ReadString(false);
+ Buffer.SkipBytes(53);
+ }
+
+ if ((OtherData & 4) > 0)
+ {
+ // Auras
+ Buffer.ReadString(false);
+ Buffer.ReadString(false);
+ Buffer.SkipBytes(54);
+ }
+
+ NewSpawn.PropCount = Buffer.ReadByte();
+
+ if (NewSpawn.PropCount > 0)
+ NewSpawn.BodyType = Buffer.ReadUInt32();
+ else
+ NewSpawn.BodyType = 0;
+
+
+ for (int j = 1; j < NewSpawn.PropCount; ++j)
+ Buffer.SkipBytes(4);
+
+ Buffer.SkipBytes(1); // Skip HP %
+ NewSpawn.HairColor = Buffer.ReadByte();
+ NewSpawn.BeardColor = Buffer.ReadByte();
+ NewSpawn.EyeColor1 = Buffer.ReadByte();
+ NewSpawn.EyeColor2 = Buffer.ReadByte();
+ NewSpawn.HairStyle = Buffer.ReadByte();
+ NewSpawn.Beard = Buffer.ReadByte();
+
+ NewSpawn.DrakkinHeritage = Buffer.ReadUInt32();
+ NewSpawn.DrakkinTattoo = Buffer.ReadUInt32();
+ NewSpawn.DrakkinDetails = Buffer.ReadUInt32();
+
+ NewSpawn.EquipChest2 = Buffer.ReadByte();
+
+ bool UseWorn = (NewSpawn.EquipChest2 == 255);
+
+ Buffer.SkipBytes(2); // 2 Unknown bytes;
+
+ NewSpawn.Helm = Buffer.ReadByte();
+
+ NewSpawn.Size = Buffer.ReadSingle();
+
+ NewSpawn.Face = Buffer.ReadByte();
+
+ NewSpawn.WalkSpeed = Buffer.ReadSingle();
+
+ NewSpawn.RunSpeed = Buffer.ReadSingle();
+
+ NewSpawn.Race = Buffer.ReadUInt32();
+
+ Buffer.SkipBytes(1); // Skip Holding
+
+ NewSpawn.Deity = Buffer.ReadUInt32();
+
+ Buffer.SkipBytes(8); // Skip GuildID and GuildRank
+
+ NewSpawn.Class = Buffer.ReadByte();
+
+ Buffer.SkipBytes(4); // Skip PVP, Standstate, Light, Flymode
+
+ NewSpawn.LastName = Buffer.ReadString(true);
+
+ Buffer.SkipBytes(6);
+
+ NewSpawn.PetOwnerID = Buffer.ReadUInt32();
+
+ Buffer.SkipBytes(25);
+
+ NewSpawn.MeleeTexture1 = 0;
+ NewSpawn.MeleeTexture2 = 0;
+
+ if ((NewSpawn.IsNPC == 0) || NPCType.IsPlayableRace(NewSpawn.Race))
+ {
+ for (int ColourSlot = 0; ColourSlot < 9; ++ColourSlot)
+ NewSpawn.SlotColour[ColourSlot] = Buffer.ReadUInt32();
+
+ for (int i = 0; i < 9; ++i)
+ {
+ NewSpawn.Equipment[i] = Buffer.ReadUInt32();
+
+ UInt32 Equip3 = Buffer.ReadUInt32();
+
+ UInt32 Equip2 = Buffer.ReadUInt32();
+
+ UInt32 Equip1 = Buffer.ReadUInt32();
+
+ UInt32 Equip0 = Buffer.ReadUInt32();
+ }
+
+ if (NewSpawn.Equipment[Constants.MATERIAL_CHEST] > 0)
+ {
+ NewSpawn.EquipChest2 = (byte)NewSpawn.Equipment[Constants.MATERIAL_CHEST];
+
+ }
+
+ NewSpawn.ArmorTintRed = (byte)((NewSpawn.SlotColour[Constants.MATERIAL_CHEST] >> 16) & 0xff);
+
+ NewSpawn.ArmorTintGreen = (byte)((NewSpawn.SlotColour[Constants.MATERIAL_CHEST] >> 8) & 0xff);
+
+ NewSpawn.ArmorTintBlue = (byte)(NewSpawn.SlotColour[Constants.MATERIAL_CHEST] & 0xff);
+
+ if (NewSpawn.Equipment[Constants.MATERIAL_PRIMARY] > 0)
+ NewSpawn.MeleeTexture1 = NewSpawn.Equipment[Constants.MATERIAL_PRIMARY];
+
+ if (NewSpawn.Equipment[Constants.MATERIAL_SECONDARY] > 0)
+ NewSpawn.MeleeTexture2 = NewSpawn.Equipment[Constants.MATERIAL_SECONDARY];
+
+ if (UseWorn)
+ NewSpawn.Helm = (byte)NewSpawn.Equipment[Constants.MATERIAL_HEAD];
+ else
+ NewSpawn.Helm = 0;
+
+ }
+ else
+ {
+ // Non playable race
+
+ Buffer.SkipBytes(20);
+
+ NewSpawn.MeleeTexture1 = Buffer.ReadUInt32();
+ Buffer.SkipBytes(16);
+ NewSpawn.MeleeTexture2 = Buffer.ReadUInt32();
+ Buffer.SkipBytes(16);
+ }
+
+ if (NewSpawn.EquipChest2 == 255)
+ NewSpawn.EquipChest2 = 0;
+
+ if (NewSpawn.Helm == 255)
+ NewSpawn.Helm = 0;
+
+ UInt32 Position1 = Buffer.ReadUInt32();
+
+ UInt32 Position2 = Buffer.ReadUInt32();
+
+ UInt32 Position3 = Buffer.ReadUInt32();
+
+ UInt32 Position4 = Buffer.ReadUInt32();
+
+ UInt32 Position5 = Buffer.ReadUInt32();
+
+ NewSpawn.YPos = Utils.EQ19ToFloat((Int32)((Position1 >> 12) & 0x7FFFF));
+
+ NewSpawn.ZPos = Utils.EQ19ToFloat((Int32)(Position2) & 0x7FFFF);
+
+ NewSpawn.XPos = Utils.EQ19ToFloat((Int32)(Position4 >> 13) & 0x7FFFF);
+
+ NewSpawn.Heading = Utils.EQ19ToFloat((Int32)(Position3 >> 13) & 0xFFF);
+
+ if ((OtherData & 16) > 0)
+ {
+ NewSpawn.Title = Buffer.ReadString(false);
+ }
+
+ if ((OtherData & 32) > 0)
+ {
+ NewSpawn.Suffix = Buffer.ReadString(false);
+ }
+
+ // unknowns
+ Buffer.SkipBytes(8);
+
+ NewSpawn.IsMercenary = Buffer.ReadByte();
+
+ Buffer.SkipBytes(54);
+
+ Debug.Assert(Buffer.GetPosition() == Buffer.Length(), "Length mismatch while parsing zone spawns");
+
+ ZoneSpawns.Add(NewSpawn);
+ }
+ return ZoneSpawns;
+ }
+ }
+}
\ No newline at end of file
diff --git a/utils/EQExtractor2/EQExtractor2/patch_April15-2013.conf b/utils/EQExtractor2/EQExtractor2/patch_April15-2013.conf
new file mode 100644
index 000000000..94d57d345
--- /dev/null
+++ b/utils/EQExtractor2/EQExtractor2/patch_April15-2013.conf
@@ -0,0 +1,653 @@
+# ShowEQ Import Notes:
+# ZERO THE FILE first
+# perl -pi -e 's/0x[0-9a-fA-F]{4}/0x0000/g' opcodes.conf
+# Unknown Mapping:
+# OP_Action2 -> OP_Damage
+# OP_EnvDamage -> OP_Damage ---> might have been a one time mistake
+# Name Differences:
+# OP_CancelInvite -> OP_GroupCancelInvite
+# OP_GMFind -> OP_FindPersonRequest
+# OP_CommonMessage -> OP_ChannelMessage
+
+OP_Unknown=0x0000
+OP_ExploreUnknown=0x0000 used for unknown explorer
+
+# world packets
+# Required to reach Char Select:
+OP_SendLoginInfo=0x5f21
+OP_ApproveWorld=0x6604
+OP_LogServer=0x27a4
+OP_SendCharInfo=0x6b98
+OP_ExpansionInfo=0x0322
+OP_GuildsList=0x3d34
+OP_EnterWorld=0x70c9
+OP_PostEnterWorld=0x29f7
+OP_World_Client_CRC1=0x0786
+OP_World_Client_CRC2=0x77cd
+OP_SendSpellChecksum=0x0000
+OP_SendSkillCapsChecksum=0x0000
+
+# Character Select Related:
+OP_SendMaxCharacters=0x5a84
+OP_SendMembership=0x3603
+OP_SendMembershipDetails=0x3222
+OP_CharacterCreateRequest=0x329a
+OP_CharacterCreate=0x7f24
+OP_DeleteCharacter=0x3078
+OP_RandomNameGenerator=0x2617
+OP_ApproveName=0x657d
+OP_MOTD=0x0db2
+OP_SetChatServer=0x6a13
+OP_SetChatServer2=0x48a1
+OP_ZoneServerInfo=0x4cae
+OP_WorldComplete=0x1db8
+OP_WorldUnknown001=0x5810
+OP_FloatListThing=0x1ada
+
+# Reasons for Disconnect:
+OP_ZoneUnavail=0x2a10
+OP_WorldClientReady=0x0b64
+OP_CharacterStillInZone=0x0000
+OP_WorldChecksumFailure=0x0000
+OP_WorldLoginFailed=0x0000
+OP_WorldLogout=0x0000
+OP_WorldLevelTooHigh=0x0000
+OP_CharInacessable=0x0000
+OP_UserCompInfo=0x0000
+OP_SendExeChecksum=0x0000
+OP_SendBaseDataChecksum=0x0000
+
+# Zone in opcodes
+OP_AckPacket=0x58f7
+OP_ZoneEntry=0x31d8
+OP_ReqNewZone=0x3cb7
+OP_NewZone=0x0bf6
+OP_ZoneSpawns=0x36f6
+OP_PlayerProfile=0x6b5b
+OP_TimeOfDay=0x3377
+OP_LevelUpdate=0x3e3c
+OP_Stamina=0x5813
+OP_RequestClientZoneChange=0x1b24
+OP_ZoneChange=0x5538
+OP_LockoutTimerInfo=0x0000
+OP_ZoneServerReady=0x0000
+OP_ZoneInUnknown=0x0000
+OP_LogoutReply=0x0000
+OP_PreLogoutReply=0x0000
+
+# Required to fully log in
+OP_SpawnAppearance=0x130f
+OP_ChangeSize=0x3619
+OP_TributeUpdate=0x365d
+OP_TributeTimer=0x7ea7
+OP_SendTributes=0x57f5
+OP_SendGuildTributes=0x430c
+OP_TributeInfo=0x3c65
+OP_Weather=0x4ec0
+OP_ReqClientSpawn=0x16ca
+OP_SpawnDoor=0x375d
+OP_GroundSpawn=0x3a2b
+OP_SendZonepoints=0x41b8
+OP_BlockedBuffs=0x52b5
+OP_RemoveBlockedBuffs=0x5884
+OP_ClearBlockedBuffs=0x565c
+OP_WorldObjectsSent=0x37d3
+OP_SendExpZonein=0x7d57
+OP_SendAATable=0x65c4
+OP_RespondAA=0x0b69
+OP_UpdateAA=0x67e0
+OP_SendAAStats=0x702d
+OP_AAExpUpdate=0x4616
+OP_ExpUpdate=0x39a0
+OP_HPUpdate=0x648b
+OP_ManaChange=0x2ed8
+OP_TGB=0x6516
+OP_SpecialMesg=0x0000
+OP_GuildMemberList=0x2bad
+OP_GuildMOTD=0x4e44
+OP_CharInventory=0x3deb
+OP_WearChange=0x1ff6
+OP_ClientUpdate=0x6962
+OP_ClientReady=0x0c66 # 0x422d
+OP_SetServerFilter=0x3b5b
+
+# Guild Opcodes - Disabled until crashes are resolved in RoF
+OP_GetGuildMOTD=0x6ce8 # Was 0x35dc
+OP_GetGuildMOTDReply=0x150b # Was 0x4586
+OP_GuildMemberUpdate=0x665a # Was 0x5643
+OP_GuildInvite=0x3834
+OP_GuildRemove=0x1eb3
+OP_GuildPeace=0x5b6e
+OP_SetGuildMOTD=0x183e
+OP_GuildList=0x0000
+OP_GuildWar=0x7e3d
+OP_GuildLeader=0x0c63
+OP_GuildDelete=0x1e22
+OP_GuildInviteAccept=0x3462
+OP_GuildDemote=0x47ed
+OP_GuildPublicNote=0x6208
+OP_GuildManageBanker=0x2010 # Was 0x0737
+OP_GuildBank=0x3d39 # Was 0x10c3
+OP_SetGuildRank=0x671c
+OP_GuildUpdateURLAndChannel=0x31fc
+OP_GuildStatus=0x7b81
+OP_GuildCreate=0x52c7 # or maybe 0x086e
+OP_GuildMemberLevelUpdate=0x0000 # Unused?
+OP_ZoneGuildList=0x0000 # Unused?
+OP_GetGuildsList=0x0000 # Unused?
+OP_LFGuild=0x0000
+OP_GuildManageRemove=0x0000
+OP_GuildManageAdd=0x0000
+OP_GuildManageStatus=0x0000
+
+# GM/Guide Opcodes
+OP_GMServers=0x4946
+OP_GMBecomeNPC=0x2b92
+OP_GMZoneRequest=0x7a65
+OP_GMZoneRequest2=0x5585
+OP_GMGoto=0x15b4
+OP_GMSearchCorpse=0x594b
+OP_GMHideMe=0x0005
+OP_GMDelCorpse=0x0633
+OP_GMApproval=0x4bbf
+OP_GMToggle=0x5e6f
+OP_GMSummon=0x7b50 # Was 0x684f
+OP_GMEmoteZone=0x735a # Was 0x0655
+OP_GMEmoteWorld=0x2114 # Was 0x1935
+OP_GMFind=0x4e59
+OP_GMKick=0x5c02
+OP_GMKill=0x2728
+OP_GMNameChange=0x7b23 # Was 0x4434
+OP_GMLastName=0x1063 # Was 0x3077
+
+# Misc Opcodes
+OP_InspectRequest=0x5d21
+OP_InspectAnswer=0x5569
+OP_InspectMessageUpdate=0x09cd
+OP_BeginCast=0x557a
+OP_BuffFadeMsg=0x6226
+OP_ConsentResponse=0x0333
+OP_MemorizeSpell=0x7bc9
+OP_SwapSpell=0x3217
+OP_CastSpell=0x3f6d
+OP_Consider=0x3815
+OP_FormattedMessage=0x52c0
+OP_SimpleMessage=0x0e29
+OP_Buff=0x0dd5
+OP_Illusion=0x1182
+OP_MoneyOnCorpse=0x05e8
+OP_RandomReply=0x4b1a
+OP_DenyResponse=0x3918
+OP_SkillUpdate=0x4b94
+OP_GMTrainSkillConfirm=0x0498 # 0x3960
+OP_RandomReq=0x4629
+OP_Death=0x1aa0
+OP_GMTraining=0x698c
+OP_GMEndTraining=0x36dc
+OP_GMTrainSkill=0x580c
+OP_Animation=0x172f
+OP_Begging=0x39a6
+OP_Consent=0x2c4e
+OP_ConsentDeny=0x69bb
+OP_AutoFire=0x086e
+OP_PetCommands=0x17bc
+OP_DeleteSpell=0x709b
+OP_Surname=0x0329
+OP_ClearSurname=0x6182
+OP_FaceChange=0x6b0e
+OP_SenseHeading=0x12cf
+OP_Action=0x7329
+OP_ConsiderCorpse=0x28ed
+OP_HideCorpse=0x0179
+OP_CorpseDrag=0x47d9
+OP_CorpseDrop=0x0df7
+OP_Bug=0x51a4
+OP_Feedback=0x2061
+OP_Report=0x4c00
+OP_Damage=0x4725
+OP_ChannelMessage=0x0dc9
+OP_Assist=0x68d3
+OP_AssistGroup=0x6c10
+OP_MoveCoin=0x37b3
+OP_ZonePlayerToBind=0x36ad
+OP_KeyRing=0x7ac2
+OP_WhoAllRequest=0x7cf3
+OP_WhoAllResponse=0x14cc
+OP_FriendsWho=0x7ee9
+OP_ConfirmDelete=0x13c1
+OP_Logout=0x226f
+OP_Rewind=0x7562
+OP_TargetCommand=0x3bd6
+OP_Hide=0x4517
+OP_Jump=0x1dbf
+OP_Camp=0x6aa7
+OP_Emote=0x6195
+OP_SetRunMode=0x068d
+OP_BankerChange=0x4d5b
+OP_TargetMouse=0x3f43
+OP_MobHealth=0x52dc
+OP_InitialMobHealth=0x0000 # Unused?
+OP_TargetHoTT=0x2baa
+OP_XTargetResponse=0x0be2
+OP_XTargetRequest=0x024c
+OP_XTargetAutoAddHaters=0x3729
+OP_TargetBuffs=0x241c
+OP_BuffCreate=0x6878
+OP_BuffRemoveRequest=0x78ab
+OP_DeleteSpawn=0x5279
+OP_AutoAttack=0x1bf5
+OP_AutoAttack2=0x0ce9
+OP_Consume=0x7711
+OP_MoveItem=0x2bb9
+OP_DeleteItem=0x5c68
+OP_DeleteCharge=0x7660
+OP_ItemPacket=0x154e
+OP_ItemLinkResponse=0x62f9
+OP_ItemLinkClick=0x53c8
+OP_ItemPreview=0x0ee7
+OP_NewSpawn=0x61e9
+OP_Track=0x7f7a
+OP_TrackTarget=0x4190
+OP_TrackUnknown=0x3a5e
+OP_ClickDoor=0x349b
+OP_MoveDoor=0x4920
+OP_RemoveAllDoors=0x6542
+OP_EnvDamage=0x7912
+OP_BoardBoat=0x6a35
+OP_Forage=0x7621
+OP_LeaveBoat=0x7251
+OP_ControlBoat=0x5635
+OP_SafeFallSuccess=0x3358
+OP_RezzComplete=0x570d
+OP_RezzRequest=0x3e8b
+OP_RezzAnswer=0x77c7
+OP_Shielding=0x3f0b
+OP_RequestDuel=0x0a38
+OP_MobRename=0x2638
+OP_AugmentItem=0x4695 # Was 0x37cb
+OP_WeaponEquip1=0x1d1d
+OP_WeaponEquip2=0x6147 # Was 0x6022
+OP_WeaponUnequip2=0x4b3e # Was 0x0110
+OP_ApplyPoison=0x1b5b
+OP_Save=0x6444
+OP_TestBuff=0x2c3c # Was 0x3772
+OP_CustomTitles=0x61ab
+OP_Split=0x3dff
+OP_YellForHelp=0x694a
+OP_LoadSpellSet=0x5d60
+OP_Bandolier=0x1d9d
+OP_PotionBelt=0x5499 # Was 0x4d3b
+OP_DuelResponse=0x3a06
+OP_DuelResponse2=0x7929
+OP_SaveOnZoneReq=0x0b2e
+OP_ReadBook=0x1b02
+OP_Dye=0x0999
+OP_InterruptCast=0x57e1
+OP_AAAction=0x3994
+OP_LeadershipExpToggle=0x2746
+OP_LeadershipExpUpdate=0x0fdc
+OP_PurchaseLeadershipAA=0x0f78
+OP_UpdateLeadershipAA=0x4ef0
+OP_MarkNPC=0x70e1
+OP_ClearNPCMarks=0x643a
+OP_DelegateAbility=0x2aa5
+OP_SetGroupTarget=0x19d0
+OP_Charm=0x5221
+OP_Stun=0x5826
+OP_SendFindableNPCs=0x743b
+OP_FindPersonRequest=0x3560
+OP_FindPersonReply=0x0844
+OP_Sound=0x1b73
+OP_PetBuffWindow=0x56c0
+OP_LevelAppearance=0x6698
+OP_Translocate=0x091a
+OP_Sacrifice=0x5b22
+OP_PopupResponse=0x1475
+OP_OnLevelMessage=0x1cbd
+OP_AugmentInfo=0x6524
+OP_Petition=0x3b76
+OP_SomeItemPacketMaybe=0x0668
+OP_PVPStats=0x3388 # Unsure
+OP_PVPLeaderBoardRequest=0x2852
+OP_PVPLeaderBoardReply=0x65cf
+OP_PVPLeaderBoardDetailsRequest=0x1d11
+OP_PVPLeaderBoardDetailsReply=0x1a8e
+OP_RestState=0x3018
+OP_RespawnWindow=0x7b02
+OP_LDoNButton=0x4438
+OP_SetStartCity=0x2121 # Was 0x2d1b
+OP_VoiceMacroIn=0x6f3d
+OP_VoiceMacroOut=0x0577
+OP_ItemViewUnknown=0x31a8
+OP_VetRewardsAvaliable=0x0687
+OP_VetClaimRequest=0x0407
+OP_VetClaimReply=0x1ef4
+OP_DisciplineUpdate=0x06da # Was 0x2f05
+OP_DisciplineTimer=0x2a91 # Was 0x5e3f
+OP_BecomeCorpse=0x0000 # Unused?
+OP_Action2=0x0000 # Unused?
+OP_MobUpdate=0x6d4b
+OP_NPCMoveUpdate=0x5a39
+OP_CameraEffect=0x5099
+OP_SpellEffect=0x1e99
+OP_RemoveNimbusEffect=0x29af
+OP_AltCurrency=0x797d
+OP_AltCurrencyMerchantRequest=0x5132
+OP_AltCurrencyMerchantReply=0x7ec5
+OP_AltCurrencyPurchase=0x6731
+OP_AltCurrencySell=0x5420
+OP_AltCurrencySellSelection=0x6fed
+OP_AltCurrencyReclaim=0x5eda
+OP_CrystalCountUpdate=0x3b26 # Was 0x3f60
+OP_CrystalCreate=0x39ed # Was 0x5a82
+OP_CrystalReclaim=0x369e # Was 0x7616
+OP_Untargetable=0x6c0b
+OP_IncreaseStats=0x757b
+OP_Weblink=0x4173
+#OP_OpenInventory=0x0000 # Likely does not exist in RoF -U
+OP_OpenContainer=0x0000
+
+OP_DzQuit=0x7579
+OP_DzListTimers=0x00e5
+OP_DzAddPlayer=0x070f
+OP_DzRemovePlayer=0x11eb
+OP_DzSwapPlayer=0x72fe
+OP_DzMakeLeader=0x3904
+OP_DzPlayerList=0x20a4
+OP_DzJoinExpeditionConfirm=0x1c57
+OP_DzJoinExpeditionReply=0x3274
+OP_DzExpeditionInfo=0xe86b
+OP_DzExpeditionList=0x1349
+OP_DzMemberStatus=0x638e
+OP_DzLeaderStatus=0x39cf
+OP_DzExpeditionEndsWarning=0x44eb
+OP_DzMemberList=0x0000
+OP_DzCompass=0x5baa # Was 0x4f09
+OP_DzChooseZone=0x0000 # Maybe 0x29d6
+
+# New Opcodes
+OP_SpawnPositionUpdate=0x0000 # Actually OP_MobUpdate ?
+OP_ManaUpdate=0x4c12
+OP_EnduranceUpdate=0x250e
+OP_MobManaUpdate=0x230a
+OP_MobEnduranceUpdate=0x7012
+
+# Mercenary Opcodes
+OP_MercenaryDataUpdateRequest=0x1d60
+OP_MercenaryDataUpdate=0x0c2d
+OP_MercenaryDataRequest=0x8fa2
+OP_MercenaryDataResponse=0x6a0c
+OP_MercenaryHire=0x05bd
+OP_MercenaryDismiss=0x376f
+OP_MercenaryTimerRequest=0x293d
+OP_MercenaryTimer=0x3f92
+OP_MercenaryUnknown1=0x1c14
+OP_MercenaryCommand=0x26b9
+OP_MercenarySuspendRequest=0x4b19
+OP_MercenarySuspendResponse=0x0830
+OP_MercenaryUnsuspendResponse=0x4405
+
+# Looting
+OP_LootRequest=0x52ba
+OP_EndLootRequest=0x0573
+OP_LootItem=0x2344
+OP_LootComplete=0x71c6
+
+# bazaar trader stuff:
+OP_BazaarSearch=0x6c36
+OP_TraderDelItem=0x0000
+OP_BecomeTrader=0x081e
+OP_TraderShop=0x327c
+OP_Trader=0x2284 # Was 0x6790
+OP_TraderBuy=0x0000
+OP_Barter=0x241f
+OP_ShopItem=0x0000
+OP_BazaarInspect=0x0000
+OP_Bazaar=0x0000
+OP_TraderItemUpdate=0x0000
+
+# pc/npc trading
+OP_TradeRequest=0x164e
+OP_TradeAcceptClick=0x61eb
+OP_TradeRequestAck=0x76c1
+OP_TradeCoins=0x582b
+OP_FinishTrade=0x6bba
+OP_CancelTrade=0x5a9f
+OP_TradeMoneyUpdate=0x2432
+OP_MoneyUpdate=0x52e5
+OP_TradeBusy=0x6482
+
+# Sent after canceling trade or after closing tradeskill object
+OP_FinishWindow=0x5ea3
+OP_FinishWindow2=0x6177
+
+# Sent on Live for what seems to be item existance verification
+# Ex. Before Right Click Effect happens from items
+OP_ItemVerifyRequest=0x49fc
+OP_ItemVerifyReply=0x0061
+
+# merchant stuff
+OP_ShopPlayerSell=0x1961
+OP_ShopRequest=0x393f
+OP_ShopEnd=0x7385
+OP_ShopEndConfirm=0x2ed5
+OP_ShopPlayerBuy=0x0f9d
+OP_ShopDelItem=0x78d2
+
+# tradeskill stuff:
+OP_ClickObject=0x6cb6
+OP_ClickObjectAction=0x5d27
+OP_ClearObject=0x3a64
+OP_RecipeDetails=0x2d9b
+OP_RecipesFavorite=0x2875
+OP_RecipesSearch=0x5013
+OP_RecipeReply=0x4161
+OP_RecipeAutoCombine=0x74ac
+OP_TradeSkillCombine=0x48fd
+
+# Tribute Packets:
+OP_OpenGuildTributeMaster=0x5ba2
+OP_OpenTributeMaster=0x6c0f # Was 0x40f5
+OP_SelectTribute=0x003e
+OP_TributeItem=0x07cd
+OP_TributeMoney=0x2f60 # Was 0x6fed
+OP_TributeToggle=0x420c
+OP_TributePointUpdate=0x5552
+OP_TributeNPC=0x0000
+OP_GuildTributeInfo=0x0000
+OP_OpenTributeReply=0x0000
+OP_GuildTributeStatus=0x0000
+
+# Adventure packets:
+OP_LeaveAdventure=0x234c
+OP_AdventureFinish=0x33a0
+OP_AdventureInfoRequest=0x00af
+OP_AdventureInfo=0x6a8c
+OP_AdventureRequest=0x1c3b
+OP_AdventureDetails=0x4b02
+OP_AdventureData=0x0e46
+OP_AdventureUpdate=0x724c
+OP_AdventureMerchantRequest=0x6d4f # Was 654d
+OP_AdventureMerchantResponse=0x7d2d # Was 7949
+OP_AdventureMerchantPurchase=0x3afc # Was 155a
+OP_AdventureMerchantSell=0x19eb # Was 389c
+OP_AdventurePointsUpdate=0x7df1 # Was 7589
+OP_AdventureStatsRequest=0x0dc1
+OP_AdventureStatsReply=0x7ca8
+OP_AdventureLeaderboardRequest=0x4769
+OP_AdventureLeaderboardReply=0x08fc
+
+# Group Opcodes
+OP_GroupDisband=0x4e45
+OP_GroupInvite=0x1694
+OP_GroupFollow=0x6246
+OP_GroupUpdate=0x31c8
+OP_GroupUpdateB=0x13cd
+OP_GroupCancelInvite=0x0000
+OP_GroupAcknowledge=0x4dbe
+OP_GroupDelete=0x6d6b
+OP_CancelInvite=0x6417
+OP_GroupFollow2=0x26fc
+OP_GroupInvite2=0x6232
+OP_GroupDisbandYou=0x14ca
+OP_GroupDisbandOther=0x0f0b
+OP_GroupLeaderChange=0x1cdb
+OP_GroupRoles=0x75de
+OP_GroupMakeLeader=0x24b9
+OP_DoGroupLeadershipAbility=0x46df
+OP_GroupLeadershipAAUpdate=0x3b89
+
+# LFG/LFP Opcodes
+OP_LFGCommand=0x4719
+OP_LFGGetMatchesRequest=0x78db
+OP_LFGGetMatchesResponse=0x36b4
+OP_LFPGetMatchesRequest=0x5ef8
+OP_LFPGetMatchesResponse=0x1c19
+OP_LFPCommand=0x00f4
+OP_LFGAppearance=0x0000
+OP_LFGResponse=0x0000
+
+# Raid Opcodes
+OP_RaidInvite=0x1419
+OP_RaidUpdate=0x5191
+OP_RaidJoin=0x0000
+
+# Button-push commands
+OP_Taunt=0x1fbe
+OP_CombatAbility=0x11c2
+OP_SenseTraps=0x713b # Was 0x2ee0
+OP_PickPocket=0x5229
+OP_DisarmTraps=0x0000
+OP_Disarm=0x5482
+OP_Sneak=0x209e
+OP_Fishing=0x3520
+OP_InstillDoubt=0x5e78
+OP_FeignDeath=0x0258
+OP_Mend=0x742c
+OP_Bind_Wound=0x5f4d
+OP_LDoNOpen=0x0621
+
+# Task packets
+OP_TaskDescription=0x3c8f
+OP_TaskActivity=0x6140
+OP_CompletedTasks=0x7b7c
+OP_TaskActivityComplete=0x6bb5
+OP_AcceptNewTask=0x5d0e
+OP_CancelTask=0x2f6c
+OP_TaskMemberList=0x16f5 # Was 0x1656
+OP_OpenNewTasksWindow=0x065c # Was 0x11de
+OP_AvaliableTask=0x5ed2 # Was 0x2377
+OP_TaskHistoryRequest=0x2da2
+OP_TaskHistoryReply=0x2cc2
+OP_DeclineAllTasks=0x0000
+
+# Title opcodes
+OP_NewTitlesAvailable=0x7705
+OP_RequestTitles=0x6cde
+OP_SendTitleList=0x12cc
+OP_SetTitle=0x0bda
+OP_SetTitleReply=0x05b3
+
+# mail opcodes
+OP_Command=0x0000
+OP_MailboxHeader=0x0000
+OP_MailHeader=0x0000
+OP_MailBody=0x0000
+OP_NewMail=0x0000
+OP_SentConfirm=0x0000
+
+########### Below this point should not be needed ###########
+
+# This section are all unknown in Titanium
+OP_ForceFindPerson=0x0000
+OP_LocInfo=0x0000
+OP_ReloadUI=0x0000
+OP_ItemName=0x0000
+OP_ItemLinkText=0x0000
+OP_MultiLineMsg=0x0000
+OP_MendHPUpdate=0x0000
+OP_TargetReject=0x0000
+OP_SafePoint=0x0000
+OP_ApproveZone=0x0000
+OP_ZoneComplete=0x0000
+OP_ClientError=0x0000
+OP_DumpName=0x0000
+OP_Heartbeat=0x0000
+OP_CrashDump=0x0000
+OP_LoginComplete=0x0000
+
+# discovered opcodes not yet used:
+OP_PickLockSuccess=0x0000
+OP_PlayMP3=0x0000
+OP_ReclaimCrystals=0x0000
+OP_DynamicWall=0x0000
+OP_OpenDiscordMerchant=0x0000
+OP_DiscordMerchantInventory=0x0000
+OP_GiveMoney=0x0000
+OP_RequestKnowledgeBase=0x0000
+OP_KnowledgeBase=0x0000
+OP_SlashAdventure=0x0000 # /adventure
+OP_BecomePVPPrompt=0x0000
+OP_MoveLogRequest=0x0000 # gone I think
+OP_MoveLogDisregard=0x0000 # gone I think
+
+# named unknowns, to make looking for real unknown easier
+OP_AnnoyingZoneUnknown=0x0000
+OP_Some6ByteHPUpdate=0x0000 seems to happen when you target group members
+OP_QueryResponseThing=0x0000
+
+
+# realityincarnate: these are just here to stop annoying several thousand byte packet dumps
+#OP_LoginUnknown1=0x46d3 # OP_SendSpellChecksum
+#OP_LoginUnknown2=0x040b # OP_SendSkillCapsChecksum
+
+# Petition Opcodes
+OP_PetitionSearch=0x0000 search term for petition
+OP_PetitionSearchResults=0x0000 (list of?) matches from search
+OP_PetitionSearchText=0x0000 text results of search
+
+OP_PetitionUpdate=0x0000
+OP_PetitionCheckout=0x0000
+OP_PetitionCheckIn=0x0000
+OP_PetitionQue=0x0000
+OP_PetitionUnCheckout=0x0000
+OP_PetitionDelete=0x0000
+OP_DeletePetition=0x0000
+OP_PetitionResolve=0x0000
+OP_PDeletePetition=0x0000
+OP_PetitionBug=0x0000
+OP_PetitionRefresh=0x0000
+OP_PetitionCheckout2=0x0000
+OP_PetitionViewPetition=0x0000
+
+# Login opcodes
+OP_SessionReady=0x0000
+OP_Login=0x0000
+OP_ServerListRequest=0x0000
+OP_PlayEverquestRequest=0x0000
+OP_PlayEverquestResponse=0x0000
+OP_ChatMessage=0x0000
+OP_LoginAccepted=0x0000
+OP_ServerListResponse=0x0000
+OP_Poll=0x0000
+OP_EnterChat=0x0000
+OP_PollResponse=0x0000
+
+# raw opcodes
+OP_RAWSessionRequest=0x0000
+OP_RAWSessionResponse=0x0000
+OP_RAWCombined=0x0000
+OP_RAWSessionDisconnect=0x0000
+OP_RAWKeepAlive=0x0000
+OP_RAWSessionStatRequest=0x0000
+OP_RAWSessionStatResponse=0x0000
+OP_RAWPacket=0x0000
+OP_RAWFragment=0x0000
+OP_RAWOutOfOrderAck=0x0000
+OP_RAWAck=0x0000
+OP_RAWAppCombined=0x0000
+OP_RAWOutOfSession=0x0000
+
+# we need to document the differences between these packets to make identifying them easier
+OP_Some3ByteHPUpdate=0x0000 # initial HP update for mobs
+OP_InitialHPUpdate=0x0000
diff --git a/world/client.cpp b/world/client.cpp
index 055126d10..5f47d36eb 100644
--- a/world/client.cpp
+++ b/world/client.cpp
@@ -368,15 +368,560 @@ void Client::SendPostEnterWorld() {
safe_delete(outapp);
}
+bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app) {
+ if (app->size != sizeof(LoginInfo_Struct)) {
+ return false;
+ }
+
+ LoginInfo_Struct *li=(LoginInfo_Struct *)app->pBuffer;
+
+ // Quagmire - max len for name is 18, pass 15
+ char name[19] = {0};
+ char password[16] = {0};
+ strn0cpy(name, (char*)li->login_info,18);
+ strn0cpy(password, (char*)&(li->login_info[strlen(name)+1]), 15);
+
+ if (strlen(password) <= 1) {
+ // TODO: Find out how to tell the client wrong username/password
+ clog(WORLD__CLIENT_ERR,"Login without a password");
+ return false;
+ }
+
+ pZoning=(li->zoning==1);
+
+#ifdef IPBASED_AUTH_HACK
+ struct in_addr tmpip;
+ tmpip.s_addr = ip;
+#endif
+ uint32 id=0;
+ bool minilogin = loginserverlist.MiniLogin();
+ if(minilogin){
+ struct in_addr miniip;
+ miniip.s_addr = ip;
+ id = database.GetMiniLoginAccount(inet_ntoa(miniip));
+ }
+ else if(strncasecmp(name, "LS#", 3) == 0)
+ id=atoi(&name[3]);
+ else
+ id=atoi(name);
+#ifdef IPBASED_AUTH_HACK
+ if ((cle = zoneserver_list.CheckAuth(inet_ntoa(tmpip), password)))
+#else
+ if (loginserverlist.Connected() == false && !pZoning) {
+ clog(WORLD__CLIENT_ERR,"Error: Login server login while not connected to login server.");
+ return false;
+ }
+ if ((minilogin && (cle = client_list.CheckAuth(id,password,ip))) || (cle = client_list.CheckAuth(id, password)))
+#endif
+ {
+ if (cle->AccountID() == 0 || (!minilogin && cle->LSID()==0)) {
+ clog(WORLD__CLIENT_ERR,"ID is 0. Is this server connected to minilogin?");
+ if(!minilogin)
+ clog(WORLD__CLIENT_ERR,"If so you forget the minilogin variable...");
+ else
+ clog(WORLD__CLIENT_ERR,"Could not find a minilogin account, verify ip address logging into minilogin is the same that is in your account table.");
+ return false;
+ }
+
+ cle->SetOnline();
+
+ clog(WORLD__CLIENT,"Logged in. Mode=%s",pZoning ? "(Zoning)" : "(CharSel)");
+
+ if(minilogin){
+ WorldConfig::DisableStats();
+ clog(WORLD__CLIENT,"MiniLogin Account #%d",cle->AccountID());
+ }
+ else {
+ clog(WORLD__CLIENT,"LS Account #%d",cle->LSID());
+ }
+
+ const WorldConfig *Config=WorldConfig::get();
+
+ if(Config->UpdateStats){
+ ServerPacket* pack = new ServerPacket;
+ pack->opcode = ServerOP_LSPlayerJoinWorld;
+ pack->size = sizeof(ServerLSPlayerJoinWorld_Struct);
+ pack->pBuffer = new uchar[pack->size];
+ memset(pack->pBuffer,0,pack->size);
+ ServerLSPlayerJoinWorld_Struct* join =(ServerLSPlayerJoinWorld_Struct*)pack->pBuffer;
+ strcpy(join->key,GetLSKey());
+ join->lsaccount_id = GetLSID();
+ loginserverlist.SendPacket(pack);
+ safe_delete(pack);
+ }
+
+ if (!pZoning)
+ SendGuildList();
+ SendLogServer();
+ SendApproveWorld();
+ SendEnterWorld(cle->name());
+ SendPostEnterWorld();
+ if (!pZoning) {
+ SendExpansionInfo();
+ SendCharInfo();
+ database.LoginIP(cle->AccountID(), long2ip(GetIP()).c_str());
+ }
+
+ }
+ else {
+ // TODO: Find out how to tell the client wrong username/password
+ clog(WORLD__CLIENT_ERR,"Bad/Expired session key '%s'",name);
+ return false;
+ }
+
+ if (!cle)
+ return true;
+
+ cle->SetIP(GetIP());
+ return true;
+}
+
+bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) {
+
+ if (GetAccountID() == 0) {
+ clog(WORLD__CLIENT_ERR,"Name approval request with no logged in account");
+ return false;
+ }
+
+ snprintf(char_name, 64, "%s", (char*)app->pBuffer);
+ uchar race = app->pBuffer[64];
+ uchar clas = app->pBuffer[68];
+
+ clog(WORLD__CLIENT,"Name approval request. Name=%s, race=%s, class=%s",char_name,GetRaceName(race),GetEQClassName(clas));
+
+ EQApplicationPacket *outapp;
+ outapp = new EQApplicationPacket;
+ outapp->SetOpcode(OP_ApproveName);
+ outapp->pBuffer = new uchar[1];
+ outapp->size = 1;
+
+ bool valid;
+ if(!database.CheckNameFilter(char_name)) {
+ valid = false;
+ }
+ else if(char_name[0] < 'A' && char_name[0] > 'Z') {
+ //name must begin with an upper-case letter.
+ valid = false;
+ }
+ else if (database.ReserveName(GetAccountID(), char_name)) {
+ valid = true;
+ }
+ else {
+ valid = false;
+ }
+ outapp->pBuffer[0] = valid? 1 : 0;
+ QueuePacket(outapp);
+ safe_delete(outapp);
+
+ if(!valid) {
+ memset(char_name, 0, sizeof(char_name));
+ }
+
+ return true;
+}
+
+bool Client::HandleGenerateRandomNamePacket(const EQApplicationPacket *app) {
+ // creates up to a 10 char name
+ char vowels[18]="aeiouyaeiouaeioe";
+ char cons[48]="bcdfghjklmnpqrstvwxzybcdgklmnprstvwbcdgkpstrkd";
+ char rndname[17]="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+ char paircons[33]="ngrkndstshthphsktrdrbrgrfrclcr";
+ int rndnum=MakeRandomInt(0, 75),n=1;
+ bool dlc=false;
+ bool vwl=false;
+ bool dbl=false;
+ if (rndnum>63)
+ { // rndnum is 0 - 75 where 64-75 is cons pair, 17-63 is cons, 0-16 is vowel
+ rndnum=(rndnum-61)*2; // name can't start with "ng" "nd" or "rk"
+ rndname[0]=paircons[rndnum];
+ rndname[1]=paircons[rndnum+1];
+ n=2;
+ }
+ else if (rndnum>16)
+ {
+ rndnum-=17;
+ rndname[0]=cons[rndnum];
+ }
+ else
+ {
+ rndname[0]=vowels[rndnum];
+ vwl=true;
+ }
+ int namlen=MakeRandomInt(5, 10);
+ for (int i=n;i46)
+ { // pick a cons pair
+ if (i>namlen-3) // last 2 chars in name?
+ { // name can only end in cons pair "rk" "st" "sh" "th" "ph" "sk" "nd" or "ng"
+ rndnum=MakeRandomInt(0, 7)*2;
+ }
+ else
+ { // pick any from the set
+ rndnum=(rndnum-47)*2;
+ }
+ rndname[i]=paircons[rndnum];
+ rndname[i+1]=paircons[rndnum+1];
+ dlc=true; // flag keeps second letter from being doubled below
+ i+=1;
+ }
+ else
+ { // select a single cons
+ rndname[i]=cons[rndnum];
+ }
+ }
+ else
+ { // select a vowel
+ rndname[i]=vowels[MakeRandomInt(0, 16)];
+ }
+ vwl=!vwl;
+ if (!dbl && !dlc)
+ { // one chance at double letters in name
+ if (!MakeRandomInt(0, i+9)) // chances decrease towards end of name
+ {
+ rndname[i+1]=rndname[i];
+ dbl=true;
+ i+=1;
+ }
+ }
+ }
+
+ rndname[0]=toupper(rndname[0]);
+ NameGeneration_Struct* ngs = (NameGeneration_Struct*)app->pBuffer;
+ memset(ngs->name,0,64);
+ strcpy(ngs->name,rndname);
+
+ QueuePacket(app);
+ return true;
+}
+
+bool Client::HandleCharacterCreateRequestPacket(const EQApplicationPacket *app) {
+ // New OpCode in SoF
+ uint32 allocs = character_create_allocations.size();
+ uint32 combos = character_create_race_class_combos.size();
+ uint32 len = sizeof(RaceClassAllocation) * allocs;
+ len += sizeof(RaceClassCombos) * combos;
+ len += sizeof(uint8);
+ len += sizeof(uint32);
+ len += sizeof(uint32);
+
+ EQApplicationPacket *outapp = new EQApplicationPacket(OP_CharacterCreateRequest, len);
+ unsigned char *ptr = outapp->pBuffer;
+ *((uint8*)ptr) = 0;
+ ptr += sizeof(uint8);
+
+ *((uint32*)ptr) = allocs;
+ ptr += sizeof(uint32);
+
+ for(int i = 0; i < allocs; ++i) {
+ RaceClassAllocation *alc = (RaceClassAllocation*)ptr;
+
+ alc->Index = character_create_allocations[i].Index;
+ for(int j = 0; j < 7; ++j) {
+ alc->BaseStats[j] = character_create_allocations[i].BaseStats[j];
+ alc->DefaultPointAllocation[j] = character_create_allocations[i].DefaultPointAllocation[j];
+ }
+ ptr += sizeof(RaceClassAllocation);
+ }
+
+ *((uint32*)ptr) = combos;
+ ptr += sizeof(uint32);
+ for(int i = 0; i < combos; ++i) {
+ RaceClassCombos *cmb = (RaceClassCombos*)ptr;
+ cmb->ExpansionRequired = character_create_race_class_combos[i].ExpansionRequired;
+ cmb->Race = character_create_race_class_combos[i].Race;
+ cmb->Class = character_create_race_class_combos[i].Class;
+ cmb->Deity = character_create_race_class_combos[i].Deity;
+ cmb->AllocationIndex = character_create_race_class_combos[i].AllocationIndex;
+ cmb->Zone = character_create_race_class_combos[i].Zone;
+ ptr += sizeof(RaceClassCombos);
+ }
+
+ QueuePacket(outapp);
+ safe_delete(outapp);
+ return true;
+}
+
+bool Client::HandleCharacterCreatePacket(const EQApplicationPacket *app) {
+ if (GetAccountID() == 0)
+ {
+ clog(WORLD__CLIENT_ERR,"Account ID not set; unable to create character.");
+ return false;
+ }
+ else if (app->size != sizeof(CharCreate_Struct))
+ {
+ clog(WORLD__CLIENT_ERR,"Wrong size on OP_CharacterCreate. Got: %d, Expected: %d",app->size,sizeof(CharCreate_Struct));
+ DumpPacket(app);
+ // the previous behavior was essentially returning true here
+ // but that seems a bit odd to me.
+ return true;
+ }
+
+ CharCreate_Struct *cc = (CharCreate_Struct*)app->pBuffer;
+ if(OPCharCreate(char_name, cc) == false)
+ {
+ database.DeleteCharacter(char_name);
+ EQApplicationPacket *outapp = new EQApplicationPacket(OP_ApproveName, 1);
+ outapp->pBuffer[0] = 0;
+ QueuePacket(outapp);
+ safe_delete(outapp);
+ }
+ else
+ SendCharInfo();
+
+ return true;
+}
+
+bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
+
+ if (GetAccountID() == 0) {
+ clog(WORLD__CLIENT_ERR,"Enter world with no logged in account");
+ eqs->Close();
+ return true;
+ }
+
+ if(GetAdmin() < 0)
+ {
+ clog(WORLD__CLIENT,"Account banned or suspended.");
+ eqs->Close();
+ return true;
+ }
+
+ if (RuleI(World, MaxClientsPerIP) >= 0) {
+ client_list.GetCLEIP(this->GetIP()); //Check current CLE Entry IPs against incoming connection
+ }
+
+ EnterWorld_Struct *ew=(EnterWorld_Struct *)app->pBuffer;
+ strn0cpy(char_name, ew->name, 64);
+
+ EQApplicationPacket *outapp;
+ uint32 tmpaccid = 0;
+ charid = database.GetCharacterInfo(char_name, &tmpaccid, &zoneID, &instanceID);
+ if (charid == 0 || tmpaccid != GetAccountID()) {
+ clog(WORLD__CLIENT_ERR,"Could not get CharInfo for '%s'",char_name);
+ eqs->Close();
+ return true;
+ }
+
+ // Make sure this account owns this character
+ if (tmpaccid != GetAccountID()) {
+ clog(WORLD__CLIENT_ERR,"This account does not own the character named '%s'",char_name);
+ eqs->Close();
+ return true;
+ }
+
+ if(!pZoning && ew->return_home)
+ {
+ CharacterSelect_Struct* cs = new CharacterSelect_Struct;
+ memset(cs, 0, sizeof(CharacterSelect_Struct));
+ database.GetCharSelectInfo(GetAccountID(), cs);
+ bool home_enabled = false;
+
+ for(int x = 0; x < 10; ++x)
+ {
+ if(strcasecmp(cs->name[x], char_name) == 0)
+ {
+ if(cs->gohome[x] == 1)
+ {
+ home_enabled = true;
+ return true;
+ }
+ }
+ }
+ safe_delete(cs);
+
+ if(home_enabled)
+ {
+ zoneID = database.MoveCharacterToBind(charid,4);
+ }
+ else
+ {
+ clog(WORLD__CLIENT_ERR,"'%s' is trying to go home before they're able...",char_name);
+ database.SetHackerFlag(GetAccountName(), char_name, "MQGoHome: player tried to go home before they were able.");
+ eqs->Close();
+ return true;
+ }
+ }
+
+ if(!pZoning && (RuleB(World, EnableTutorialButton) && (ew->tutorial || StartInTutorial))) {
+ CharacterSelect_Struct* cs = new CharacterSelect_Struct;
+ memset(cs, 0, sizeof(CharacterSelect_Struct));
+ database.GetCharSelectInfo(GetAccountID(), cs);
+ bool tutorial_enabled = false;
+
+ for(int x = 0; x < 10; ++x)
+ {
+ if(strcasecmp(cs->name[x], char_name) == 0)
+ {
+ if(cs->tutorial[x] == 1)
+ {
+ tutorial_enabled = true;
+ return true;
+ }
+ }
+ }
+ safe_delete(cs);
+
+ if(tutorial_enabled)
+ {
+ zoneID = RuleI(World, TutorialZoneID);
+ database.MoveCharacterToZone(charid, database.GetZoneName(zoneID));
+ }
+ else
+ {
+ clog(WORLD__CLIENT_ERR,"'%s' is trying to go to tutorial but are not allowed...",char_name);
+ database.SetHackerFlag(GetAccountName(), char_name, "MQTutorial: player tried to enter the tutorial without having tutorial enabled for this character.");
+ eqs->Close();
+ return true;
+ }
+ }
+
+ if (zoneID == 0 || !database.GetZoneName(zoneID)) {
+ // This is to save people in an invalid zone, once it's removed from the DB
+ database.MoveCharacterToZone(charid, "arena");
+ clog(WORLD__CLIENT_ERR, "Zone not found in database zone_id=%i, moveing char to arena character:%s", zoneID, char_name);
+ }
+
+ if(instanceID > 0)
+ {
+ if(!database.VerifyInstanceAlive(instanceID, GetCharID()))
+ {
+ zoneID = database.MoveCharacterToBind(charid);
+ instanceID = 0;
+ }
+ else
+ {
+ if(!database.VerifyZoneInstance(zoneID, instanceID))
+ {
+ zoneID = database.MoveCharacterToBind(charid);
+ instanceID = 0;
+ }
+ }
+ }
+
+ if(!pZoning) {
+ database.SetGroupID(char_name, 0, charid);
+ database.SetLoginFlags(charid, false, false, 1);
+ }
+ else{
+ uint32 groupid=database.GetGroupID(char_name);
+ if(groupid>0){
+ char* leader=0;
+ char leaderbuf[64]={0};
+ if((leader=database.GetGroupLeaderForLogin(char_name,leaderbuf)) && strlen(leader)>1){
+ EQApplicationPacket* outapp3 = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupJoin_Struct));
+ GroupJoin_Struct* gj=(GroupJoin_Struct*)outapp3->pBuffer;
+ gj->action=8;
+ strcpy(gj->yourname,char_name);
+ strcpy(gj->membername,leader);
+ QueuePacket(outapp3);
+ safe_delete(outapp3);
+ }
+ }
+ }
+
+ outapp = new EQApplicationPacket(OP_MOTD);
+ char tmp[500] = {0};
+ if (database.GetVariable("MOTD", tmp, 500)) {
+ outapp->size = strlen(tmp)+1;
+ outapp->pBuffer = new uchar[outapp->size];
+ memset(outapp->pBuffer,0,outapp->size);
+ strcpy((char*)outapp->pBuffer, tmp);
+
+ } else {
+ // Null Message of the Day. :)
+ outapp->size = 1;
+ outapp->pBuffer = new uchar[outapp->size];
+ outapp->pBuffer[0] = 0;
+ }
+ QueuePacket(outapp);
+ safe_delete(outapp);
+
+ int MailKey = MakeRandomInt(1, INT_MAX);
+
+ database.SetMailKey(charid, GetIP(), MailKey);
+
+ char ConnectionType;
+
+ if(ClientVersionBit & BIT_UnderfootAndLater)
+ ConnectionType = 'U';
+ else if(ClientVersionBit & BIT_SoFAndLater)
+ ConnectionType = 'S';
+ else
+ ConnectionType = 'C';
+
+ EQApplicationPacket *outapp2 = new EQApplicationPacket(OP_SetChatServer);
+ char buffer[112];
+
+ const WorldConfig *Config = WorldConfig::get();
+
+ sprintf(buffer,"%s,%i,%s.%s,%c%08X",
+ Config->ChatHost.c_str(),
+ Config->ChatPort,
+ Config->ShortName.c_str(),
+ this->GetCharName(), ConnectionType, MailKey
+ );
+ outapp2->size=strlen(buffer)+1;
+ outapp2->pBuffer = new uchar[outapp2->size];
+ memcpy(outapp2->pBuffer,buffer,outapp2->size);
+ QueuePacket(outapp2);
+ safe_delete(outapp2);
+
+ outapp2 = new EQApplicationPacket(OP_SetChatServer2);
+
+ if(ClientVersionBit & BIT_TitaniumAndEarlier)
+ ConnectionType = 'M';
+
+ sprintf(buffer,"%s,%i,%s.%s,%c%08X",
+ Config->MailHost.c_str(),
+ Config->MailPort,
+ Config->ShortName.c_str(),
+ this->GetCharName(), ConnectionType, MailKey
+ );
+ outapp2->size=strlen(buffer)+1;
+ outapp2->pBuffer = new uchar[outapp2->size];
+ memcpy(outapp2->pBuffer,buffer,outapp2->size);
+ QueuePacket(outapp2);
+ safe_delete(outapp2);
+
+ EnterWorld();
+
+ return true;
+}
+
+bool Client::HandleDeleteCharacterPacket(const EQApplicationPacket *app) {
+
+ uint32 char_acct_id = database.GetAccountIDByChar((char*)app->pBuffer);
+ if(char_acct_id == GetAccountID())
+ {
+ clog(WORLD__CLIENT,"Delete character: %s",app->pBuffer);
+ database.DeleteCharacter((char *)app->pBuffer);
+ SendCharInfo();
+ }
+
+ return true;
+}
+
+bool Client::HandleZoneChangePacket(const EQApplicationPacket *app) {
+ // HoT sends this to world while zoning and wants it echoed back.
+ if(ClientVersionBit & BIT_RoFAndLater)
+ {
+ QueuePacket(app);
+ }
+ return true;
+}
+
bool Client::HandlePacket(const EQApplicationPacket *app) {
- const WorldConfig *Config=WorldConfig::get();
+
EmuOpcode opcode = app->GetOpcode();
clog(WORLD__CLIENT_TRACE,"Recevied EQApplicationPacket");
_pkt(WORLD__CLIENT_TRACE,app);
- bool ret = true;
-
if (!eqs->CheckState(ESTABLISHED)) {
clog(WORLD__CLIENT,"Client disconnected (net inactive on send)");
return false;
@@ -401,586 +946,75 @@ bool Client::HandlePacket(const EQApplicationPacket *app) {
switch(opcode)
{
- case OP_CrashDump:
- break;
- case OP_SendLoginInfo:
- {
- if (app->size != sizeof(LoginInfo_Struct)) {
- ret = false;
- break;
- }
-
- LoginInfo_Struct *li=(LoginInfo_Struct *)app->pBuffer;
-
- // Quagmire - max len for name is 18, pass 15
- char name[19] = {0};
- char password[16] = {0};
- strn0cpy(name, (char*)li->login_info,18);
- strn0cpy(password, (char*)&(li->login_info[strlen(name)+1]), 15);
-
- if (strlen(password) <= 1) {
- // TODO: Find out how to tell the client wrong username/password
- clog(WORLD__CLIENT_ERR,"Login without a password");
- ret = false;
- break;
- }
-
- pZoning=(li->zoning==1);
-
-#ifdef IPBASED_AUTH_HACK
- struct in_addr tmpip;
- tmpip.s_addr = ip;
-#endif
- uint32 id=0;
- bool minilogin = loginserverlist.MiniLogin();
- if(minilogin){
- struct in_addr miniip;
- miniip.s_addr = ip;
- id = database.GetMiniLoginAccount(inet_ntoa(miniip));
- }
- else if(strncasecmp(name, "LS#", 3) == 0)
- id=atoi(&name[3]);
- else
- id=atoi(name);
-#ifdef IPBASED_AUTH_HACK
- if ((cle = zoneserver_list.CheckAuth(inet_ntoa(tmpip), password)))
-#else
- if (loginserverlist.Connected() == false && !pZoning) {
- clog(WORLD__CLIENT_ERR,"Error: Login server login while not connected to login server.");
- ret = false;
- break;
- }
- if ((minilogin && (cle = client_list.CheckAuth(id,password,ip))) || (cle = client_list.CheckAuth(id, password)))
-#endif
- {
- if (cle->AccountID() == 0 || (!minilogin && cle->LSID()==0)) {
- clog(WORLD__CLIENT_ERR,"ID is 0. Is this server connected to minilogin?");
- if(!minilogin)
- clog(WORLD__CLIENT_ERR,"If so you forget the minilogin variable...");
- else
- clog(WORLD__CLIENT_ERR,"Could not find a minilogin account, verify ip address logging into minilogin is the same that is in your account table.");
- ret = false;
- break;
- }
-
- cle->SetOnline();
-
- clog(WORLD__CLIENT,"Logged in. Mode=%s",pZoning ? "(Zoning)" : "(CharSel)");
-
- if(minilogin){
- WorldConfig::DisableStats();
- clog(WORLD__CLIENT,"MiniLogin Account #%d",cle->AccountID());
- }
- else {
- clog(WORLD__CLIENT,"LS Account #%d",cle->LSID());
- }
- if(Config->UpdateStats){
- ServerPacket* pack = new ServerPacket;
- pack->opcode = ServerOP_LSPlayerJoinWorld;
- pack->size = sizeof(ServerLSPlayerJoinWorld_Struct);
- pack->pBuffer = new uchar[pack->size];
- memset(pack->pBuffer,0,pack->size);
- ServerLSPlayerJoinWorld_Struct* join =(ServerLSPlayerJoinWorld_Struct*)pack->pBuffer;
- strcpy(join->key,GetLSKey());
- join->lsaccount_id = GetLSID();
- loginserverlist.SendPacket(pack);
- safe_delete(pack);
- }
-
- if (!pZoning)
- SendGuildList();
- SendLogServer();
- SendApproveWorld();
- SendEnterWorld(cle->name());
- SendPostEnterWorld();
- if (!pZoning) {
- SendExpansionInfo();
- SendCharInfo();
- database.LoginIP(cle->AccountID(), long2ip(GetIP()).c_str());
- }
-
- }
- else {
- // TODO: Find out how to tell the client wrong username/password
- clog(WORLD__CLIENT_ERR,"Bad/Expired session key '%s'",name);
- ret = false;
- break;
- }
-
- if (!cle)
- break;
- cle->SetIP(GetIP());
- break;
- }
- case OP_ApproveName: //Name approval
- {
- if (GetAccountID() == 0) {
- clog(WORLD__CLIENT_ERR,"Name approval request with no logged in account");
- ret = false;
- break;
- }
- snprintf(char_name, 64, "%s", (char*)app->pBuffer);
- uchar race = app->pBuffer[64];
- uchar clas = app->pBuffer[68];
-
- clog(WORLD__CLIENT,"Name approval request. Name=%s, race=%s, class=%s",char_name,GetRaceName(race),GetEQClassName(clas));
-
- EQApplicationPacket *outapp;
- outapp = new EQApplicationPacket;
- outapp->SetOpcode(OP_ApproveName);
- outapp->pBuffer = new uchar[1];
- outapp->size = 1;
- bool valid;
- if(!database.CheckNameFilter(char_name)) {
- valid = false;
- }
- else if(char_name[0] < 'A' && char_name[0] > 'Z') {
- //name must begin with an upper-case letter.
- valid = false;
- }
- else if (database.ReserveName(GetAccountID(), char_name)) {
- valid = true;
- }
- else {
- valid = false;
- }
- outapp->pBuffer[0] = valid? 1 : 0;
- QueuePacket(outapp);
- safe_delete(outapp);
-
- if(!valid) {
- memset(char_name, 0, sizeof(char_name));
- }
-
- break;
- }
- case OP_RandomNameGenerator:
- {
- // creates up to a 10 char name
- char vowels[18]="aeiouyaeiouaeioe";
- char cons[48]="bcdfghjklmnpqrstvwxzybcdgklmnprstvwbcdgkpstrkd";
- char rndname[17]="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
- char paircons[33]="ngrkndstshthphsktrdrbrgrfrclcr";
- int rndnum=MakeRandomInt(0, 75),n=1;
- bool dlc=false;
- bool vwl=false;
- bool dbl=false;
- if (rndnum>63)
- { // rndnum is 0 - 75 where 64-75 is cons pair, 17-63 is cons, 0-16 is vowel
- rndnum=(rndnum-61)*2; // name can't start with "ng" "nd" or "rk"
- rndname[0]=paircons[rndnum];
- rndname[1]=paircons[rndnum+1];
- n=2;
- }
- else if (rndnum>16)
- {
- rndnum-=17;
- rndname[0]=cons[rndnum];
- }
- else
- {
- rndname[0]=vowels[rndnum];
- vwl=true;
- }
- int namlen=MakeRandomInt(5, 10);
- for (int i=n;i46)
- { // pick a cons pair
- if (i>namlen-3) // last 2 chars in name?
- { // name can only end in cons pair "rk" "st" "sh" "th" "ph" "sk" "nd" or "ng"
- rndnum=MakeRandomInt(0, 7)*2;
- }
- else
- { // pick any from the set
- rndnum=(rndnum-47)*2;
- }
- rndname[i]=paircons[rndnum];
- rndname[i+1]=paircons[rndnum+1];
- dlc=true; // flag keeps second letter from being doubled below
- i+=1;
- }
- else
- { // select a single cons
- rndname[i]=cons[rndnum];
- }
- }
- else
- { // select a vowel
- rndname[i]=vowels[MakeRandomInt(0, 16)];
- }
- vwl=!vwl;
- if (!dbl && !dlc)
- { // one chance at double letters in name
- if (!MakeRandomInt(0, i+9)) // chances decrease towards end of name
- {
- rndname[i+1]=rndname[i];
- dbl=true;
- i+=1;
- }
- }
- }
-
- rndname[0]=toupper(rndname[0]);
- NameGeneration_Struct* ngs = (NameGeneration_Struct*)app->pBuffer;
- memset(ngs->name,0,64);
- strcpy(ngs->name,rndname);
-
-// char names[8][64] = { "How", "About", "You", "Think", "Of", "Your", "Own", "Name" };
-// //Could have parts of the random name in this struct and they compile together
-// NameGeneration_Struct* ngs = (NameGeneration_Struct*)app->pBuffer;
-// strncpy(ngs->name,"Notcreated",64);
-
- QueuePacket(app);
- break;
-
- }
- case OP_CharacterCreateRequest: {
- // New OpCode in SoF
- uint32 allocs = character_create_allocations.size();
- uint32 combos = character_create_race_class_combos.size();
- uint32 len = sizeof(RaceClassAllocation) * allocs;
- len += sizeof(RaceClassCombos) * combos;
- len += sizeof(uint8);
- len += sizeof(uint32);
- len += sizeof(uint32);
-
- EQApplicationPacket *outapp = new EQApplicationPacket(OP_CharacterCreateRequest, len);
- unsigned char *ptr = outapp->pBuffer;
- *((uint8*)ptr) = 0;
- ptr += sizeof(uint8);
-
- *((uint32*)ptr) = allocs;
- ptr += sizeof(uint32);
-
- for(int i = 0; i < allocs; ++i) {
- RaceClassAllocation *alc = (RaceClassAllocation*)ptr;
-
- alc->Index = character_create_allocations[i].Index;
- for(int j = 0; j < 7; ++j) {
- alc->BaseStats[j] = character_create_allocations[i].BaseStats[j];
- alc->DefaultPointAllocation[j] = character_create_allocations[i].DefaultPointAllocation[j];
- }
- ptr += sizeof(RaceClassAllocation);
- }
-
- *((uint32*)ptr) = combos;
- ptr += sizeof(uint32);
- for(int i = 0; i < combos; ++i) {
- RaceClassCombos *cmb = (RaceClassCombos*)ptr;
- cmb->ExpansionRequired = character_create_race_class_combos[i].ExpansionRequired;
- cmb->Race = character_create_race_class_combos[i].Race;
- cmb->Class = character_create_race_class_combos[i].Class;
- cmb->Deity = character_create_race_class_combos[i].Deity;
- cmb->AllocationIndex = character_create_race_class_combos[i].AllocationIndex;
- cmb->Zone = character_create_race_class_combos[i].Zone;
- ptr += sizeof(RaceClassCombos);
- }
-
- QueuePacket(outapp);
- safe_delete(outapp);
- break;
- }
-
- case OP_CharacterCreate: //Char create
- {
- if (GetAccountID() == 0)
- {
- clog(WORLD__CLIENT_ERR,"Account ID not set; unable to create character.");
- ret = false;
- break;
- }
- else if (app->size != sizeof(CharCreate_Struct))
- {
- clog(WORLD__CLIENT_ERR,"Wrong size on OP_CharacterCreate. Got: %d, Expected: %d",app->size,sizeof(CharCreate_Struct));
- DumpPacket(app);
- break;
- }
-
- CharCreate_Struct *cc = (CharCreate_Struct*)app->pBuffer;
- if(OPCharCreate(char_name, cc) == false)
- {
- database.DeleteCharacter(char_name);
- EQApplicationPacket *outapp = new EQApplicationPacket(OP_ApproveName, 1);
- outapp->pBuffer[0] = 0;
- QueuePacket(outapp);
- safe_delete(outapp);
- }
- else
- SendCharInfo();
- break;
- }
- case OP_EnterWorld: // Enter world
- {
- if (GetAccountID() == 0) {
- clog(WORLD__CLIENT_ERR,"Enter world with no logged in account");
- eqs->Close();
- break;
- }
- if(GetAdmin() < 0)
- {
- clog(WORLD__CLIENT,"Account banned or suspended.");
- eqs->Close();
- break;
- }
-
- if (RuleI(World, MaxClientsPerIP) >= 0) {
- client_list.GetCLEIP(this->GetIP()); //Check current CLE Entry IPs against incoming connection
- }
-
- EnterWorld_Struct *ew=(EnterWorld_Struct *)app->pBuffer;
- strn0cpy(char_name, ew->name, 64);
-
- EQApplicationPacket *outapp;
- uint32 tmpaccid = 0;
- charid = database.GetCharacterInfo(char_name, &tmpaccid, &zoneID, &instanceID);
- if (charid == 0 || tmpaccid != GetAccountID()) {
- clog(WORLD__CLIENT_ERR,"Could not get CharInfo for '%s'",char_name);
- eqs->Close();
- break;
- }
-
- // Make sure this account owns this character
- if (tmpaccid != GetAccountID()) {
- clog(WORLD__CLIENT_ERR,"This account does not own the character named '%s'",char_name);
- eqs->Close();
- break;
- }
-
- if(!pZoning && ew->return_home)
- {
- CharacterSelect_Struct* cs = new CharacterSelect_Struct;
- memset(cs, 0, sizeof(CharacterSelect_Struct));
- database.GetCharSelectInfo(GetAccountID(), cs);
- bool home_enabled = false;
-
- for(int x = 0; x < 10; ++x)
- {
- if(strcasecmp(cs->name[x], char_name) == 0)
- {
- if(cs->gohome[x] == 1)
- {
- home_enabled = true;
- break;
- }
- }
- }
- safe_delete(cs);
-
- if(home_enabled)
- {
- zoneID = database.MoveCharacterToBind(charid,4);
- }
- else
- {
- clog(WORLD__CLIENT_ERR,"'%s' is trying to go home before they're able...",char_name);
- database.SetHackerFlag(GetAccountName(), char_name, "MQGoHome: player tried to go home before they were able.");
- eqs->Close();
- break;
- }
- }
-
- if(!pZoning && (RuleB(World, EnableTutorialButton) && (ew->tutorial || StartInTutorial))) {
- CharacterSelect_Struct* cs = new CharacterSelect_Struct;
- memset(cs, 0, sizeof(CharacterSelect_Struct));
- database.GetCharSelectInfo(GetAccountID(), cs);
- bool tutorial_enabled = false;
-
- for(int x = 0; x < 10; ++x)
- {
- if(strcasecmp(cs->name[x], char_name) == 0)
- {
- if(cs->tutorial[x] == 1)
- {
- tutorial_enabled = true;
- break;
- }
- }
- }
- safe_delete(cs);
-
- if(tutorial_enabled)
- {
- zoneID = RuleI(World, TutorialZoneID);
- database.MoveCharacterToZone(charid, database.GetZoneName(zoneID));
- }
- else
- {
- clog(WORLD__CLIENT_ERR,"'%s' is trying to go to tutorial but are not allowed...",char_name);
- database.SetHackerFlag(GetAccountName(), char_name, "MQTutorial: player tried to enter the tutorial without having tutorial enabled for this character.");
- eqs->Close();
- break;
- }
- }
-
- if (zoneID == 0 || !database.GetZoneName(zoneID)) {
- // This is to save people in an invalid zone, once it's removed from the DB
- database.MoveCharacterToZone(charid, "arena");
- clog(WORLD__CLIENT_ERR, "Zone not found in database zone_id=%i, moveing char to arena character:%s", zoneID, char_name);
- }
-
- if(instanceID > 0)
- {
- if(!database.VerifyInstanceAlive(instanceID, GetCharID()))
- {
- zoneID = database.MoveCharacterToBind(charid);
- instanceID = 0;
- }
- else
- {
- if(!database.VerifyZoneInstance(zoneID, instanceID))
- {
- zoneID = database.MoveCharacterToBind(charid);
- instanceID = 0;
- }
- }
- }
-
- if(!pZoning) {
- database.SetGroupID(char_name, 0, charid);
- database.SetLoginFlags(charid, false, false, 1);
- }
- else{
- uint32 groupid=database.GetGroupID(char_name);
- if(groupid>0){
- char* leader=0;
- char leaderbuf[64]={0};
- if((leader=database.GetGroupLeaderForLogin(char_name,leaderbuf)) && strlen(leader)>1){
- EQApplicationPacket* outapp3 = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupJoin_Struct));
- GroupJoin_Struct* gj=(GroupJoin_Struct*)outapp3->pBuffer;
- gj->action=8;
- strcpy(gj->yourname,char_name);
- strcpy(gj->membername,leader);
- QueuePacket(outapp3);
- safe_delete(outapp3);
- }
- }
- }
-
- outapp = new EQApplicationPacket(OP_MOTD);
- char tmp[500] = {0};
- if (database.GetVariable("MOTD", tmp, 500)) {
- outapp->size = strlen(tmp)+1;
- outapp->pBuffer = new uchar[outapp->size];
- memset(outapp->pBuffer,0,outapp->size);
- strcpy((char*)outapp->pBuffer, tmp);
-
- } else {
- // Null Message of the Day. :)
- outapp->size = 1;
- outapp->pBuffer = new uchar[outapp->size];
- outapp->pBuffer[0] = 0;
- }
- QueuePacket(outapp);
- safe_delete(outapp);
-
- int MailKey = MakeRandomInt(1, INT_MAX);
-
- database.SetMailKey(charid, GetIP(), MailKey);
-
- char ConnectionType;
-
- if(ClientVersionBit & BIT_UnderfootAndLater)
- ConnectionType = 'U';
- else if(ClientVersionBit & BIT_SoFAndLater)
- ConnectionType = 'S';
- else
- ConnectionType = 'C';
-
- EQApplicationPacket *outapp2 = new EQApplicationPacket(OP_SetChatServer);
- char buffer[112];
- sprintf(buffer,"%s,%i,%s.%s,%c%08X",
- Config->ChatHost.c_str(),
- Config->ChatPort,
- Config->ShortName.c_str(),
- this->GetCharName(), ConnectionType, MailKey
- );
- outapp2->size=strlen(buffer)+1;
- outapp2->pBuffer = new uchar[outapp2->size];
- memcpy(outapp2->pBuffer,buffer,outapp2->size);
- QueuePacket(outapp2);
- safe_delete(outapp2);
-
- outapp2 = new EQApplicationPacket(OP_SetChatServer2);
-
- if(ClientVersionBit & BIT_TitaniumAndEarlier)
- ConnectionType = 'M';
-
- sprintf(buffer,"%s,%i,%s.%s,%c%08X",
- Config->MailHost.c_str(),
- Config->MailPort,
- Config->ShortName.c_str(),
- this->GetCharName(), ConnectionType, MailKey
- );
- outapp2->size=strlen(buffer)+1;
- outapp2->pBuffer = new uchar[outapp2->size];
- memcpy(outapp2->pBuffer,buffer,outapp2->size);
- QueuePacket(outapp2);
- safe_delete(outapp2);
-
- EnterWorld();
- break;
- }
- case OP_LoginComplete:{
- break;
- }
- case OP_DeleteCharacter: {
- uint32 char_acct_id = database.GetAccountIDByChar((char*)app->pBuffer);
- if(char_acct_id == GetAccountID())
- {
- clog(WORLD__CLIENT,"Delete character: %s",app->pBuffer);
- database.DeleteCharacter((char *)app->pBuffer);
- SendCharInfo();
- }
- break;
- }
- case OP_ApproveWorld:
- {
- break;
- }
- case OP_WorldClientReady:{
- break;
- }
case OP_World_Client_CRC1:
- case OP_World_Client_CRC2: {
+ case OP_World_Client_CRC2:
+ {
// There is no obvious entry in the CC struct to indicate that the 'Start Tutorial button
// is selected when a character is created. I have observed that in this case, OP_EnterWorld is sent
// before OP_World_Client_CRC1. Therefore, if we receive OP_World_Client_CRC1 before OP_EnterWorld,
// then 'Start Tutorial' was not chosen.
StartInTutorial = false;
- break;
+ return true;
}
- case OP_WearChange: { // User has selected a different character
- break;
+ case OP_SendLoginInfo:
+ {
+ return HandleSendLoginInfoPacket(app);
}
- case OP_WorldComplete: {
+ case OP_ApproveName: //Name approval
+ {
+ return HandleNameApprovalPacket(app);
+ }
+ case OP_RandomNameGenerator:
+ {
+ return HandleGenerateRandomNamePacket(app);
+ }
+ case OP_CharacterCreateRequest:
+ {
+ // New OpCode in SoF
+ return HandleCharacterCreateRequestPacket(app);
+ }
+ case OP_CharacterCreate: //Char create
+ {
+ return HandleCharacterCreatePacket(app);
+ }
+ case OP_EnterWorld: // Enter world
+ {
+ return HandleEnterWorldPacket(app);
+ }
+ case OP_DeleteCharacter:
+ {
+ return HandleDeleteCharacterPacket(app);
+ }
+ case OP_WorldComplete:
+ {
eqs->Close();
- break;
+ return true;
+ }
+ case OP_ZoneChange:
+ {
+ // HoT sends this to world while zoning and wants it echoed back.
+ return HandleZoneChangePacket(app);
}
case OP_LoginUnknown1:
case OP_LoginUnknown2:
- break;
-
- case OP_ZoneChange:
- // HoT sends this to world while zoning and wants it echoed back.
- if(ClientVersionBit & BIT_RoFAndLater)
- {
- QueuePacket(app);
- }
- break;
-
-
- default: {
+ case OP_CrashDump:
+ case OP_WearChange:
+ case OP_LoginComplete:
+ case OP_ApproveWorld:
+ case OP_WorldClientReady:
+ {
+ // Essentially we are just 'eating' these packets, indicating
+ // they are handled.
+ return true;
+ }
+ default:
+ {
clog(WORLD__CLIENT_ERR,"Received unknown EQApplicationPacket");
_pkt(WORLD__CLIENT_ERR,app);
- break;
+ return true;
}
}
- return ret;
+ return true;
}
bool Client::Process() {
diff --git a/world/client.h b/world/client.h
index ea8f1a27c..877ec17a0 100644
--- a/world/client.h
+++ b/world/client.h
@@ -97,7 +97,17 @@ private:
bool firstlogin;
bool seencharsel;
bool realfirstlogin;
+
bool HandlePacket(const EQApplicationPacket *app);
+ bool HandleNameApprovalPacket(const EQApplicationPacket *app);
+ bool HandleSendLoginInfoPacket(const EQApplicationPacket *app);
+ bool HandleGenerateRandomNamePacket(const EQApplicationPacket *app);
+ bool HandleCharacterCreateRequestPacket(const EQApplicationPacket *app);
+ bool HandleCharacterCreatePacket(const EQApplicationPacket *app);
+ bool HandleEnterWorldPacket(const EQApplicationPacket *app);
+ bool HandleDeleteCharacterPacket(const EQApplicationPacket *app);
+ bool HandleZoneChangePacket(const EQApplicationPacket *app);
+
EQStreamInterface* const eqs;
};
diff --git a/zone/MobAI.cpp b/zone/MobAI.cpp
index cceedd7f3..2b1a8cc76 100644
--- a/zone/MobAI.cpp
+++ b/zone/MobAI.cpp
@@ -1083,6 +1083,15 @@ void Mob::AI_Process() {
RemoveFromHateList(this);
return;
}
+
+#ifdef BOTS
+ if (IsPet() && GetOwner()->IsBot() && target == GetOwner())
+ {
+ // this blocks all pet attacks against owner..bot pet test (copied above check)
+ RemoveFromHateList(this);
+ return;
+ }
+#endif //BOTS
if(DivineAura())
return;
diff --git a/zone/bot.cpp b/zone/bot.cpp
index 5e9af2925..f7d9e73a0 100644
--- a/zone/bot.cpp
+++ b/zone/bot.cpp
@@ -3122,10 +3122,6 @@ bool Bot::Process()
// Bot AI
AI_Process();
- // Bot Pet AI
- if(HasPet())
- PetAIProcess();
-
return true;
}
@@ -3581,8 +3577,8 @@ void Bot::AI_Process() {
if(!IsEngaged()) {
if(GetFollowID()) {
- if(BotOwner && BotOwner->CastToClient()->AutoAttackEnabled() && BotOwner->GetTarget() &&
- BotOwner->GetTarget()->IsNPC() && BotOwner->GetTarget()->GetHateAmount(BotOwner)) {
+ if(BotOwner && BotOwner->GetTarget() && BotOwner->GetTarget()->IsNPC() && (BotOwner->GetTarget()->GetHateAmount(BotOwner)
+ || BotOwner->CastToClient()->AutoAttackEnabled()) && IsAttackAllowed(BotOwner->GetTarget())) {
AddToHateList(BotOwner->GetTarget(), 1);
if(HasPet())
@@ -3594,11 +3590,12 @@ void Bot::AI_Process() {
if(g) {
for(int counter = 0; counter < g->GroupCount(); counter++) {
if(g->members[counter]) {
- if(g->members[counter]->IsEngaged() && g->members[counter]->GetTarget()) {
- AddToHateList(g->members[counter]->GetTarget(), 1);
+ Mob* tar = g->members[counter]->GetTarget();
+ if(tar && tar->IsNPC() && tar->GetHateAmount(g->members[counter]) && IsAttackAllowed(g->members[counter]->GetTarget())) {
+ AddToHateList(tar, 1);
if(HasPet())
- GetPet()->AddToHateList(g->members[counter]->GetTarget(), 1);
+ GetPet()->AddToHateList(tar, 1);
break;
}
@@ -6370,6 +6367,10 @@ void Bot::Damage(Mob *from, int32 damage, uint16 spell_id, SkillType attack_skil
SendHPUpdate();
+ if(this == from) {
+ return;
+ }
+
// Aggro the bot's group members
if(IsGrouped())
{
@@ -6378,7 +6379,7 @@ void Bot::Damage(Mob *from, int32 damage, uint16 spell_id, SkillType attack_skil
{
for(int i=0; imembers[i] && g->members[i]->IsBot() && from && !g->members[i]->CheckAggro(from))
+ if(g->members[i] && g->members[i]->IsBot() && from && !g->members[i]->CheckAggro(from) && g->members[i]->IsAttackAllowed(from))
{
g->members[i]->AddToHateList(from, 1);
}
@@ -9045,7 +9046,12 @@ bool Bot::IsBotAttackAllowed(Mob* attacker, Mob* target, bool& hasRuleDefined)
if(attacker && target)
{
- if(attacker->IsClient() && target->IsBot() && attacker->CastToClient()->GetPVP() && target->CastToBot()->GetBotOwner()->CastToClient()->GetPVP())
+ if(attacker == target)
+ {
+ hasRuleDefined = true;
+ Result = false;
+ }
+ else if(attacker->IsClient() && target->IsBot() && attacker->CastToClient()->GetPVP() && target->CastToBot()->GetBotOwner()->CastToClient()->GetPVP())
{
hasRuleDefined = true;
Result = true;
diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp
index 64937b92d..5bae0b60b 100644
--- a/zone/botspellsai.cpp
+++ b/zone/botspellsai.cpp
@@ -308,7 +308,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) {
continue;
// no buffs with illusions.. use #bot command to cast illusions
- if(IsEffectInSpell(selectedBotSpell.SpellId, SE_Illusion))
+ if(IsEffectInSpell(selectedBotSpell.SpellId, SE_Illusion) && tar != this)
continue;
//no teleport spells use #bot command to cast teleports
diff --git a/zone/merc.cpp b/zone/merc.cpp
index 98b868795..71cf5ad24 100644
--- a/zone/merc.cpp
+++ b/zone/merc.cpp
@@ -61,6 +61,7 @@ Merc::Merc(const NPCType* d, float x, float y, float z, float heading)
skills[r] = database.GetSkillCap(GetClass(),(SkillType)r,GetLevel());
}
+ GetMercSize();
CalcBonuses();
SetHP(GetMaxHP());
@@ -80,7 +81,6 @@ Merc::~Merc() {
void Merc::CalcBonuses()
{
//_ZP(Merc_CalcBonuses);
- GenerateBaseStats();
memset(&itembonuses, 0, sizeof(StatBonuses));
memset(&aabonuses, 0, sizeof(StatBonuses));
CalcItemBonuses(&itembonuses);
@@ -117,59 +117,7 @@ void Merc::CalcBonuses()
rooted = FindType(SE_Root);
}
-void Merc::GenerateBaseStats() {
-
- // base stats
- uint16 Strength = _baseSTR;
- uint16 Stamina = _baseSTA;
- uint16 Dexterity = _baseDEX;
- uint16 Agility = _baseAGI;
- uint16 Wisdom = _baseWIS;
- uint16 Intelligence = _baseINT;
- uint16 Charisma = _baseCHA;
- uint16 Attack = _baseATK;
- uint16 MagicResist = _baseMR;
- uint16 FireResist = _baseFR;
- uint16 DiseaseResist = _baseDR;
- uint16 PoisonResist = _basePR;
- uint16 ColdResist = _baseCR;
- uint16 CorruptionResist = _baseCorrup;
-
- switch(this->GetClass()) {
- case 1: // Warrior
- Strength += 10;
- Stamina += 20;
- Agility += 10;
- Dexterity += 10;
- Attack += 12;
- break;
- case 2: // Cleric
- Strength += 5;
- Stamina += 5;
- Agility += 10;
- Wisdom += 30;
- Attack += 8;
- break;
- case 4: // Ranger
- Strength += 15;
- Stamina += 10;
- Agility += 10;
- Wisdom += 15;
- Attack += 17;
- break;
- case 9: // Rogue
- Strength += 10;
- Stamina += 20;
- Agility += 10;
- Dexterity += 10;
- Attack += 12;
- break;
- case 12: // Wizard
- Stamina += 20;
- Intelligence += 30;
- Attack += 5;
- break;
- }
+void Merc::GetMercSize() {
float MercSize = GetSize();
@@ -220,20 +168,6 @@ void Merc::GenerateBaseStats() {
break;
}
- this->_baseSTR = Strength;
- this->_baseSTA = Stamina;
- this->_baseDEX = Dexterity;
- this->_baseAGI = Agility;
- this->_baseWIS = Wisdom;
- this->_baseINT = Intelligence;
- this->_baseCHA = Charisma;
- this->_baseATK = Attack;
- this->_baseMR = MagicResist;
- this->_baseFR = FireResist;
- this->_baseDR = DiseaseResist;
- this->_basePR = PoisonResist;
- this->_baseCR = ColdResist;
- this->_baseCorrup = CorruptionResist;
this->size = MercSize;
}
@@ -940,7 +874,7 @@ int16 Merc::CalcCorrup()
}
int16 Merc::CalcATK() {
- ATK = _baseATK + itembonuses.ATK + spellbonuses.ATK + aabonuses.ATK + GroupLeadershipAAOffenseEnhancement() + ((GetSTR() + GetSkill(OFFENSE)) * 9 / 10);
+ ATK = _baseATK + itembonuses.ATK + spellbonuses.ATK + aabonuses.ATK + GroupLeadershipAAOffenseEnhancement();
return(ATK);
}
diff --git a/zone/merc.h b/zone/merc.h
index dadba3fc1..4ed6e6332 100644
--- a/zone/merc.h
+++ b/zone/merc.h
@@ -328,7 +328,7 @@ private:
int GroupLeadershipAAHealthRegeneration();
int GroupLeadershipAAOffenseEnhancement();
- void GenerateBaseStats();
+ void GetMercSize();
void GenerateAppearance();
bool LoadMercSpells();
diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp
index 9b1973438..4c7d4cb98 100644
--- a/zone/spell_effects.cpp
+++ b/zone/spell_effects.cpp
@@ -1178,7 +1178,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
{
Gate();
}
- // solar: shadow step is handled by client already, nothing required
+ // shadow step is handled by client already, nothing required
break;
}
@@ -1189,7 +1189,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
#endif
if (spells[spell_id].base[i] == 1)
BuffFadeByEffect(SE_Blind);
- // solar: handled by client
+ // handled by client
// TODO: blind flag?
break;
}
@@ -1369,7 +1369,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
#ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "Spin: %d", effect_value);
#endif
- // solar: the spinning is handled by the client
+ // the spinning is handled by the client
int max_level = spells[spell_id].max[i];
if(max_level == 0)
max_level = RuleI(Spells, BaseImmunityLevel); // Default max is 55 level limit
@@ -1383,7 +1383,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
}
else
{
- // solar: the spinning is handled by the client
+ // the spinning is handled by the client
// Stun duration is based on the effect_value, not the buff duration(alot don't have buffs)
Stun(effect_value);
if(!IsClient()) {
@@ -2004,7 +2004,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
#ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "Call Pet");
#endif
- // solar: this is cast on self, not on the pet
+ // this is cast on self, not on the pet
if(GetPet() && GetPet()->IsNPC())
{
GetPet()->CastToNPC()->GMMove(GetX(), GetY(), GetZ(), GetHeading());
@@ -2015,7 +2015,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
case SE_StackingCommand_Block:
case SE_StackingCommand_Overwrite:
{
- // solar: these are special effects used by the buff stuff
+ // these are special effects used by the buff stuff
break;
}
@@ -2100,7 +2100,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
#ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "Skill Attack");
#endif
- /*Kayen:
+ /*
Weapon Damage = spells[spell_id].base[i]
Chance to Hit Bonus = spells[spell_id].base2[i]
???? = spells[spell_id].max[i] - MOST of the effects have this value.
@@ -2812,11 +2812,10 @@ int Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level,
return(effect_value);
}
-// solar: generic formula calculations
+// generic formula calculations
int Mob::CalcSpellEffectValue_formula(int formula, int base, int max, int caster_level, uint16 spell_id, int ticsremaining)
{
/*
-neotokyo: i need those formulas checked!!!!
0 = base
1 - 99 = base + level * formulaID
@@ -2845,8 +2844,11 @@ neotokyo: i need those formulas checked!!!!
int result = 0, updownsign = 1, ubase = base;
if(ubase < 0)
ubase = 0 - ubase;
+ int level_diff = caster_level - GetMinLevel(spell_id);
+ if (level_diff < 0)
+ level_diff = 0;
- // solar: this updown thing might look messed up but if you look at the
+ // this updown thing might look messed up but if you look at the
// spells it actually looks like some have a positive base and max where
// the max is actually less than the base, hence they grow downward
/*
@@ -2875,17 +2877,17 @@ snare has both of them negative, yet their range should work the same:
case 70:
result = ubase/100; break;
case 0:
- case 100: // solar: confirmed 2/6/04
+ case 100: // confirmed 2/6/04
result = ubase; break;
- case 101: // solar: confirmed 2/6/04
+ case 101: // confirmed 2/6/04
result = updownsign * (ubase + (caster_level / 2)); break;
- case 102: // solar: confirmed 2/6/04
+ case 102: // confirmed 2/6/04
result = updownsign * (ubase + caster_level); break;
- case 103: // solar: confirmed 2/6/04
+ case 103: // confirmed 2/6/04
result = updownsign * (ubase + (caster_level * 2)); break;
- case 104: // solar: confirmed 2/6/04
+ case 104: // confirmed 2/6/04
result = updownsign * (ubase + (caster_level * 3)); break;
- case 105: // solar: confirmed 2/6/04
+ case 105: // confirmed 2/6/04
result = updownsign * (ubase + (caster_level * 4)); break;
case 107:
@@ -2893,35 +2895,35 @@ snare has both of them negative, yet their range should work the same:
result = updownsign * (ubase + (caster_level / 2)); break;
case 108:
result = updownsign * (ubase + (caster_level / 3)); break;
- case 109: // solar: confirmed 2/6/04
+ case 109: // confirmed 2/6/04
result = updownsign * (ubase + (caster_level / 4)); break;
- case 110: // solar: confirmed 2/6/04
+ case 110: // confirmed 2/6/04
//is there a reason we dont use updownsign here???
result = ubase + (caster_level / 5); break;
case 111:
- result = updownsign * (ubase + 6 * (caster_level - GetMinLevel(spell_id))); break;
+ result = updownsign * (ubase + 6 * level_diff); break;
case 112:
- result = updownsign * (ubase + 8 * (caster_level - GetMinLevel(spell_id))); break;
+ result = updownsign * (ubase + 8 * level_diff); break;
case 113:
- result = updownsign * (ubase + 10 * (caster_level - GetMinLevel(spell_id))); break;
+ result = updownsign * (ubase + 10 * level_diff); break;
case 114:
- result = updownsign * (ubase + 15 * (caster_level - GetMinLevel(spell_id))); break;
+ result = updownsign * (ubase + 15 * level_diff); break;
//these formula were updated according to lucy 10/16/04
- case 115: // solar: this is only in symbol of transal
- result = ubase + 6 * (caster_level - GetMinLevel(spell_id)); break;
- case 116: // solar: this is only in symbol of ryltan
- result = ubase + 8 * (caster_level - GetMinLevel(spell_id)); break;
- case 117: // solar: this is only in symbol of pinzarn
- result = ubase + 12 * (caster_level - GetMinLevel(spell_id)); break;
- case 118: // solar: used in naltron and a few others
- result = ubase + 20 * (caster_level - GetMinLevel(spell_id)); break;
+ case 115: // this is only in symbol of transal
+ result = ubase + 6 * level_diff; break;
+ case 116: // this is only in symbol of ryltan
+ result = ubase + 8 * level_diff; break;
+ case 117: // this is only in symbol of pinzarn
+ result = ubase + 12 * level_diff; break;
+ case 118: // used in naltron and a few others
+ result = ubase + 20 * level_diff; break;
- case 119: // solar: confirmed 2/6/04
+ case 119: // confirmed 2/6/04
result = ubase + (caster_level / 8); break;
- case 121: // solar: corrected 2/6/04
+ case 121: // corrected 2/6/04
result = ubase + (caster_level / 3); break;
case 122:
{
@@ -2933,7 +2935,7 @@ snare has both of them negative, yet their range should work the same:
result = updownsign * (ubase - (12 * ticdif));
break;
}
- case 123: // solar: added 2/6/04
+ case 123: // added 2/6/04
result = MakeRandomInt(ubase, abs(max));
break;
@@ -3304,7 +3306,7 @@ void Mob::DoBuffTic(uint16 spell_id, uint32 ticsremaining, uint8 caster_level, M
}
}
-// solar: removes the buff in the buff slot 'slot'
+// removes the buff in the buff slot 'slot'
void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
{
if(slot < 0 || slot > GetMaxTotalSlots())
@@ -5317,7 +5319,7 @@ int32 Mob::GetAdditionalDamage(Mob *caster, uint32 spell_id, bool use_skill, uin
int32 Mob::ApplySpellEffectiveness(Mob* caster, int16 spell_id, int32 value, bool IsBard) {
- //Kayen 9-17-12: This is likely causing crashes, disabled till can resolve.
+ //9-17-12: This is likely causing crashes, disabled till can resolve.
if (IsBard)
return value;