Browse Source

Fix #517 - AoM client can now remove/add skills and it reflects client updates correctly. DoF has issues removing Issue #525 made. Pet Options UI Window for HP/power and other options now work.
Fix #521 - Spells can now be examined correctly, loot now pops up for previously failing with items like master spells.
- Capped rain/wind to 0.0 - 1.0, going beyond 1.0 crashed DoF client
- Removed stats from clients that do not support them (DoF and AoM vice versa)
- Encounter list no longer splits exp when pet is part of encounter, only bots/players count in the split.
- Rule R_Player, AutoSkillUpBaseSkills added, 0 by default. Setting to 1 automatically sets current skill = max skill for armor,shield,class,weapon skills

Emagi 11 months ago
parent
commit
eec9f5b9f8

+ 10 - 0
EQ2/source/WorldServer/Commands/Commands.cpp

@@ -10567,6 +10567,16 @@ void Commands::Command_Test(Client* client, EQ2_16BitString* command_parms) {
 				safe_delete(packet);
 			}
 		}
+		else if (atoi(sep->arg[0]) == 30) {
+			PacketStruct* packet = configReader.getStruct("WS_UpdateSkillBook", client->GetVersion());
+			if (packet) {
+				packet->setDataByName("unknown", World::newValue);
+				EQ2Packet* outapp = packet->serialize();
+				DumpPacket(outapp);
+				client->QueuePacket(outapp);
+				safe_delete(packet);
+			}
+		}
 	}
 	else {
 			PacketStruct* packet2 = configReader.getStruct("WS_ExamineSpellInfo", client->GetVersion());

+ 4 - 2
EQ2/source/WorldServer/Entity.h

@@ -863,8 +863,10 @@ struct InfoStruct{
 	void	set_strikethrough(float value) { std::lock_guard<std::mutex> lk(classMutex); strikethrough_ = value; }
 	void	set_accuracy(float value) { std::lock_guard<std::mutex> lk(classMutex); accuracy_ = value; }
 	void	set_offensivespeed(float value) { std::lock_guard<std::mutex> lk(classMutex); offensivespeed_ = value; }
-	void	set_rain(float value) { std::lock_guard<std::mutex> lk(classMutex); rain_ = value; }
-	void	set_wind(float value) { std::lock_guard<std::mutex> lk(classMutex); wind_ = value; }
+	
+	// crash client if float values above 1.0 are sent
+	void	set_rain(float value) { std::lock_guard<std::mutex> lk(classMutex); if(value > 1.0f) value = 1.0f; else if(value < 0.0f) value = 0.0f; rain_ = value; }
+	void	set_wind(float value) { std::lock_guard<std::mutex> lk(classMutex); if(value > 1.0f) value = 1.0f; else if(value < 0.0f) value = 0.0f; wind_ = value; }
 
 	void	add_block_chance(float value) { std::lock_guard<std::mutex> lk(classMutex); if(block_chance_ + value < 0.0f) block_chance_ = 0.0f; else block_chance_ += value; }
 	void	add_uncontested_parry(float value) { std::lock_guard<std::mutex> lk(classMutex); if(uncontested_parry_ + value < 0.0f) uncontested_parry_ = 0.0f; else uncontested_parry_ += value; }

+ 19 - 13
EQ2/source/WorldServer/Items/Items.cpp

@@ -1873,18 +1873,8 @@ void Item::serialize(PacketStruct* packet, bool show_name, Player* player, int16
 		for (int32 i = 0; i < item_stats.size(); i++) {
 			ItemStat* stat = item_stats[i];
 			tmp_subtype = world.TranslateSlotSubTypeToClient(client, stat->stat_type, stat->stat_subtype);
-			if (tmp_subtype == 255 ){
-				
-				dropstat += 1;
-				//packet->setSubstructArrayLengthByName("header_info", "stat_count", item_stats.size()-dropstat);
-			}
-			else {
-				packet->setArrayDataByName("stat_type", stat->stat_type, i-dropstat);
-				packet->setArrayDataByName("stat_subtype", tmp_subtype, i-dropstat);
-			}
-			if (stat->stat_name.length() > 0)
-				packet->setArrayDataByName("stat_name", stat->stat_name.c_str(), i-dropstat);
-			/* SF client */
+			int16 stat_type = stat->stat_type;
+			
 			float statValue = stat->value;
 			if(player)
 			{
@@ -1895,7 +1885,20 @@ void Item::serialize(PacketStruct* packet, bool show_name, Player* player, int16
 					float tmpValue = (float)statValue;
 					statValue = (sint32)(float)(tmpValue / (1.0f + ((float)diff * rule_manager.GetGlobalRule(R_Player, MentorItemDecayRate)->GetFloat())));
 				}
+			}	
+			
+			if (tmp_subtype == 255 ){
+				
+				dropstat += 1;
+				//packet->setSubstructArrayLengthByName("header_info", "stat_count", item_stats.size()-dropstat);
+			}
+			else {
+				packet->setArrayDataByName("stat_type", stat_type, i-dropstat);
+				packet->setArrayDataByName("stat_subtype", tmp_subtype, i-dropstat);
 			}
+			if (stat->stat_name.length() > 0)
+				packet->setArrayDataByName("stat_name", stat->stat_name.c_str(), i-dropstat);
+			/* SF client */
 
 			if ((client->GetVersion() >= 63119) || client->GetVersion() == 61331) {
 				if (stat->stat_type == 6){
@@ -2361,7 +2364,6 @@ void Item::serialize(PacketStruct* packet, bool show_name, Player* player, int16
 						else {							
 							spell->SetPacketInformation(packet);
 						}
-
 						//packet->setDataByName("unknown26", 0);
 					}
 				}
@@ -2554,6 +2556,10 @@ PacketStruct* Item::PrepareItem(int16 version, bool merchant_item, bool loot_ite
 	PacketStruct* packet = 0;
 	if(loot_item && version > 546)
 		packet = configReader.getStruct("WS_LootItemGeneric", version);
+	else if(loot_item && version <= 546) {
+		packet = configReader.getStruct("WS_ItemGeneric", version);
+		packet->AddFlag("loot");
+	}
 	else{
 		int8 tmpType = generic_info.item_type;
 		if (version <= 283 && generic_info.item_type > ITEM_TYPE_RECIPE)

+ 43 - 9
EQ2/source/WorldServer/NPC_AI.cpp

@@ -507,11 +507,19 @@ bool Brain::HasRecovered() {
 void Brain::AddToEncounter(Entity* entity) {
 	// If player pet then set the entity to the pets owner
 	if (entity->IsPet() && entity->GetOwner() && !entity->IsBot()) {
-		m_encounter.push_back(entity->GetID());
+		MEncounter.writelock(__FUNCTION__, __LINE__);
+		bool success = AddToEncounter(entity->GetID());
+		MEncounter.releasewritelock(__FUNCTION__, __LINE__);
+		if(!success)
+			return;
 		entity = entity->GetOwner();
 	}
 	else if(entity->HasPet() && entity->GetPet()) {
-		m_encounter.push_back(entity->GetPet()->GetID());
+		MEncounter.writelock(__FUNCTION__, __LINE__);
+		bool success = AddToEncounter(entity->GetPet()->GetID());
+		MEncounter.releasewritelock(__FUNCTION__, __LINE__);
+		if(!success)
+			return;
 	}
 
 	// If player or bot then get the group
@@ -537,8 +545,8 @@ void Brain::AddToEncounter(Entity* entity) {
 			for (itr = members->begin(); itr != members->end(); itr++) {
 				if ((*itr)->member)
 				{
-					m_encounter.push_back((*itr)->member->GetID());
-					if((*itr)->client) {
+					bool success = AddToEncounter((*itr)->member->GetID());
+					if((*itr)->client && success) {
 						m_encounter_playerlist.insert(make_pair((*itr)->client->GetPlayer()->GetCharacterID(), (*itr)->client->GetPlayer()->GetID()));
 					}
 				}
@@ -549,8 +557,8 @@ void Brain::AddToEncounter(Entity* entity) {
 		world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
 	}
 	else {
-		m_encounter.push_back(entity->GetID());
-		if (entity->IsPlayer())
+		bool success = AddToEncounter(entity->GetID());
+		if (success && entity->IsPlayer())
 		{
 			Player* plyr = (Player*)entity;
 			m_encounter_playerlist.insert(make_pair(plyr->GetCharacterID(), entity->GetID()));
@@ -638,9 +646,11 @@ bool Brain::IsPlayerInEncounter(int32 char_id) {
 	return ret;
 }
 
-bool Brain::IsEntityInEncounter(int32 id) {
+bool Brain::IsEntityInEncounter(int32 id, bool skip_read_lock) {
 	bool ret = false;
-	MEncounter.readlock(__FUNCTION__, __LINE__);
+	if(!skip_read_lock) {
+		MEncounter.readlock(__FUNCTION__, __LINE__);
+	}
 	vector<int32>::iterator itr;
 	for (itr = m_encounter.begin(); itr != m_encounter.end(); itr++) {
 		if ((*itr) == id) {
@@ -649,10 +659,34 @@ bool Brain::IsEntityInEncounter(int32 id) {
 			break;
 		}
 	}
-	MEncounter.releasereadlock(__FUNCTION__, __LINE__);
+	if(!skip_read_lock) {
+		MEncounter.releasereadlock(__FUNCTION__, __LINE__);
+	}
 	return ret;
 }
 
+int32 Brain::CountPlayerBotInEncounter() {
+	int32 count = 0;
+	vector<int32>::iterator itr;
+	MEncounter.readlock(__FUNCTION__, __LINE__);	
+	for (itr = m_encounter.begin(); itr != m_encounter.end(); itr++) {
+		Entity* ent = (Entity*)GetBody()->GetZone()->GetSpawnByID((*itr));
+		if (ent && (ent->IsPlayer() || ent->IsBot())) {
+			count++;
+		}
+	}
+	MEncounter.releasereadlock(__FUNCTION__, __LINE__);	
+	return count;
+}
+
+bool Brain::AddToEncounter(int32 id) {
+	if(!IsEntityInEncounter(id, true)) {
+		m_encounter.push_back(id);
+		return true;
+	}
+	return false;
+}
+
 void Brain::ClearEncounter() {
 	MEncounter.writelock(__FUNCTION__, __LINE__);
 	m_encounter.clear();

+ 3 - 1
EQ2/source/WorldServer/NPC_AI.h

@@ -117,7 +117,9 @@ public:
 	/// <returns>True if the encounter list contains a player</returns>
 	bool PlayerInEncounter() { return m_playerInEncounter; }
 	bool IsPlayerInEncounter(int32 char_id);
-	bool IsEntityInEncounter(int32 id);
+	bool IsEntityInEncounter(int32 id, bool skip_read_lock = false);
+	int32 CountPlayerBotInEncounter();
+	bool AddToEncounter(int32 id);
 	/* Helper functions*/
 
 	/// <summary>Gets the NPC this brain controls</summary>

+ 9 - 0
EQ2/source/WorldServer/Player.cpp

@@ -459,6 +459,13 @@ PacketStruct* PlayerInfo::serialize2(int16 version){
 		else {
 			packet->setDataByName("pet_name", "No Pet");
 		}
+		
+		packet->setDataByName("pet_health_pct", info_struct->get_pet_health_pct());
+		packet->setDataByName("pet_power_pct", info_struct->get_pet_power_pct());
+		
+		packet->setDataByName("pet_movement", info_struct->get_pet_movement());
+		packet->setDataByName("pet_behavior", info_struct->get_pet_behavior());
+		
 		packet->setDataByName("status_points", info_struct->get_status_points());
 		if(bind_zone_id > 0){
 			string bind_name = database.GetZoneName(bind_zone_id);
@@ -2434,6 +2441,8 @@ void Player::RemovePlayerSkill(int32 skill_id, bool save) {
 	Skill* skill = skill_list.GetSkill(skill_id);
 	if (skill)
 		RemoveSkillFromDB(skill, save);
+	
+	safe_delete(skill);
 }
 
 void Player::RemoveSkillFromDB(Skill* skill, bool save) {

+ 1 - 1
EQ2/source/WorldServer/Rules/Rules.cpp

@@ -217,9 +217,9 @@ void RuleManager::Init()
 	RULE_INIT(R_Player, HeirloomItemShareExpiration, "172800.0"); // 2 days ('48 hours') in seconds
 	RULE_INIT(R_Player, SwimmingSkillMinSpeed, "20"); 
 	RULE_INIT(R_Player, SwimmingSkillMaxSpeed, "200");
-	
 	RULE_INIT(R_Player, SwimmingSkillMinBreathLength, "30");
 	RULE_INIT(R_Player, SwimmingSkillMaxBreathLength, "1000");
+	RULE_INIT(R_Player, AutoSkillUpBaseSkills, "0"); // when set to 1 we auto skill to max value on levelling up for armor,shield,class,weapon skills
 	
 	/* PVP */
 	RULE_INIT(R_PVP, AllowPVP, "0");

+ 1 - 0
EQ2/source/WorldServer/Rules/Rules.h

@@ -78,6 +78,7 @@ enum RuleType {
 	SwimmingSkillMaxSpeed,
 	SwimmingSkillMinBreathLength,
 	SwimmingSkillMaxBreathLength,
+	AutoSkillUpBaseSkills,
 
 	/* PVP */
 	AllowPVP,

+ 28 - 17
EQ2/source/WorldServer/Skills.cpp

@@ -19,9 +19,11 @@
 */
 #include "Skills.h"
 #include "Spawn.h"
+#include "LuaInterface.h"
 #include "../common/Log.h"
 
 extern ConfigReader configReader;
+extern LuaInterface* lua_interface;
 
 MasterSkillList::MasterSkillList(){
 }
@@ -143,12 +145,22 @@ PlayerSkillList::~PlayerSkillList(){
 }
 
 void PlayerSkillList::AddSkill(Skill* new_skill){
+	Skill* tmpSkill = nullptr;
+	if(skills.count(new_skill->skill_id)) {
+		tmpSkill = skills[new_skill->skill_id];
+	}
 	skills[new_skill->skill_id] = new_skill;
+	if(tmpSkill) {
+		lua_interface->SetLuaUserDataStale(tmpSkill);
+		safe_delete(tmpSkill);
+	}
 }
 
 void PlayerSkillList::RemoveSkill(Skill* skill) {
-	if (skill)
+	if (skill) {
+		lua_interface->SetLuaUserDataStale(skill);
 		skills.erase(skill->skill_id);
+	}
 }
 
 map<int32, Skill*>* PlayerSkillList::GetAllSkills(){
@@ -327,7 +339,6 @@ int16 PlayerSkillList::CalculateSkillMaxValue(int32 skill_id, int16 max_val) {
 EQ2Packet* PlayerSkillList::GetSkillPacket(int16 version){
 	PacketStruct* packet = configReader.getStruct("WS_UpdateSkillBook", version);
 	if(packet){
-		if(packet_count < skills.size()){
 			int16 size = 0;
 			if (version > 546) {
 				size = 21 * skills.size() + 8;
@@ -338,24 +349,26 @@ EQ2Packet* PlayerSkillList::GetSkillPacket(int16 version){
 			else if (version <= 546) {
 				size = 21 * skills.size() + 7;
 			}
-			if(!orig_packet){				
-				xor_packet = new uchar[size];
-				orig_packet = new uchar[size];
-				memset(xor_packet, 0, size);
-				memset(orig_packet, 0, size);
-				orig_packet_size = size;
-			}
-			else{
-				uchar* tmp = new uchar[size];
+			
+			if (skills.size() > packet_count) {
+			uchar* tmp = 0;
+			if (orig_packet) {
+				tmp = new uchar[size];
 				memset(tmp, 0, size);
 				memcpy(tmp, orig_packet, orig_packet_size);
 				safe_delete_array(orig_packet);
-				orig_packet = tmp;
 				safe_delete_array(xor_packet);
-				xor_packet = new uchar[size];
+				orig_packet = tmp;
+			}
+			else {
+				orig_packet = new uchar[size];
+				memset(orig_packet, 0, size);
 			}
-			packet_count = skills.size();
+			xor_packet = new uchar[size];
+			memset(xor_packet, 0, size);
 		}
+		packet_count = skills.size();
+		orig_packet_size = size;
 		packet->setArrayLengthByName("skill_count", skills.size());
 		map<int32, Skill*>::iterator itr;
 		Skill* skill = 0;
@@ -363,7 +376,6 @@ EQ2Packet* PlayerSkillList::GetSkillPacket(int16 version){
 		for(itr = skills.begin(); itr != skills.end(); itr++){
 			skill = itr->second;
 			if(skill){
-
 				int16 skill_max_with_bonuses = CalculateSkillMaxValue(skill->skill_id, skill->max_val);
 				int16 skill_with_bonuses = int(CalculateSkillValue(skill->skill_id, skill->current_val));
 				packet->setArrayDataByName("skill_id", skill->skill_id, i);
@@ -394,9 +406,8 @@ EQ2Packet* PlayerSkillList::GetSkillPacket(int16 version){
 				
 				packet->setArrayDataByName("current_val", current_val, i);
 				packet->setArrayDataByName("base_val", current_val, i);
-				
-				i++;
 			}
+			i++;
 		}
 		int8 offset = 1;
 		if (version <= 283)

+ 0 - 1
EQ2/source/WorldServer/Skills.h

@@ -97,7 +97,6 @@ public:
 	EQ2_16BitString name;
 	EQ2_16BitString description;
 	bool			save_needed;
-
 	int			CheckDisarmSkill(int16 targetLevel, int8 chest_difficulty=0);
 };
 

+ 1 - 1
EQ2/source/WorldServer/World.cpp

@@ -2251,7 +2251,7 @@ int8 World::TranslateSlotSubTypeToClient(Client* client, int8 stat_type, sint16
 			if(client->GetVersion() <= 546) {
 				if(sub_type == (ITEM_STAT_VS_POISON-200)) // poison
 					new_subtype = 9;
-				if(sub_type == (ITEM_STAT_VS_DISEASE-200)) // disease
+				else if(sub_type == (ITEM_STAT_VS_DISEASE-200)) // disease
 					new_subtype = 8;
 				else if(sub_type == (ITEM_STAT_VS_COLD-200)) // cold
 					new_subtype = 4;

+ 13 - 7
EQ2/source/WorldServer/client.cpp

@@ -4764,15 +4764,21 @@ void Client::ChangeLevel(int16 old_level, int16 new_level) {
 
 	player_skills->SetSkillCapsByType(SKILL_TYPE_ARMOR, new_skill_cap);
 	player_skills->SetSkillCapsByType(SKILL_TYPE_SHIELD, new_skill_cap);
-	//SKILL_TYPE_ARMOR/SKILL_TYPE_SHIELD always has the same current / max values
-	player_skills->SetSkillValuesByType(SKILL_TYPE_ARMOR, new_skill_cap, false);
-	player_skills->SetSkillValuesByType(SKILL_TYPE_SHIELD, new_skill_cap, false);
-
+	
+	if(rule_manager.GetGlobalRule(R_Player, AutoSkillUpBaseSkills)->GetBool()) {
+		//SKILL_TYPE_ARMOR/SKILL_TYPE_SHIELD always has the same current / max values
+		player_skills->SetSkillValuesByType(SKILL_TYPE_ARMOR, new_skill_cap, false);
+		player_skills->SetSkillValuesByType(SKILL_TYPE_SHIELD, new_skill_cap, false);
+	}
+	
 	player_skills->SetSkillCapsByType(SKILL_TYPE_CLASS, new_skill_cap);
 	player_skills->SetSkillCapsByType(SKILL_TYPE_WEAPON, new_skill_cap);
-	//SKILL_TYPE_CLASS/SKILL_TYPE_WEAPON always has the same current/max values
-	player_skills->SetSkillValuesByType(SKILL_TYPE_CLASS, new_skill_cap, false);
-	player_skills->SetSkillValuesByType(SKILL_TYPE_WEAPON, new_skill_cap, false);
+	
+	if(rule_manager.GetGlobalRule(R_Player, AutoSkillUpBaseSkills)->GetBool()) {
+		//SKILL_TYPE_CLASS/SKILL_TYPE_WEAPON always has the same current/max values
+		player_skills->SetSkillValuesByType(SKILL_TYPE_CLASS, new_skill_cap, false);
+		player_skills->SetSkillValuesByType(SKILL_TYPE_WEAPON, new_skill_cap, false);
+	}
 	
 	player_skills->SetSkillCapsByType(SKILL_TYPE_COMBAT, new_skill_cap);
 	player_skills->SetSkillCapsByType(SKILL_TYPE_GENERAL, new_skill_cap);

+ 5 - 1
EQ2/source/WorldServer/zoneserver.cpp

@@ -4685,6 +4685,7 @@ void ZoneServer::KillSpawn(bool spawnListLocked, Spawn* dead, Spawn* killer, boo
 	PacketStruct* packet = 0;
 	Client* client = 0;
 	vector<int32>* encounter = 0;
+	int32 encounter_player_bot_count = 1;
 	bool killer_in_encounter = false;
 	int8 loot_state = dead->GetLockedNoLoot();
 		
@@ -4744,6 +4745,9 @@ void ZoneServer::KillSpawn(bool spawnListLocked, Spawn* dead, Spawn* killer, boo
 		}
 		else if (dead->IsNPC()) {
 			encounter = ((NPC*)dead)->Brain()->GetEncounter();
+			encounter_player_bot_count = ((NPC*)dead)->Brain()->CountPlayerBotInEncounter();
+			if(encounter_player_bot_count < 1)
+				encounter_player_bot_count = 1;
 		}
 
 	}
@@ -4816,7 +4820,7 @@ void ZoneServer::KillSpawn(bool spawnListLocked, Spawn* dead, Spawn* killer, boo
 					if (spawn != dead && ((Player*)spawn)->GetArrowColor(dead->GetLevel()) >= ARROW_COLOR_GREEN) {
 						//SendCalculatedXP((Player*)spawn, dead);
 
-						float xp = ((Player*)spawn)->CalculateXP(dead) / size;
+						float xp = ((Player*)spawn)->CalculateXP(dead) / encounter_player_bot_count;
 						if (xp > 0) {
 							((Player*)spawn)->AddXP((int32)xp);
 						}

+ 2 - 1
EQ2/source/common/PacketStruct.cpp

@@ -2578,7 +2578,8 @@ void PacketStruct::setItem(DataStruct* ds, Item* item, Player* player, int32 ind
 	PacketStruct* packet = item->PrepareItem(client_version, false, loot_item);
 	if (packet) {
 		int16 item_version = GetItemPacketType(packet->GetVersion());
-		item->serialize(packet, true, player, item_version);
+		// newer clients can handle the item structure without the loot_item flag set to true, older clients like DoF need a smaller subpacket of item
+		item->serialize(packet, true, player, item_version, 0, (packet->GetVersion() <= 546) ? loot_item : false);
 
 		string* generic_string_data = packet->serializeString();
 		int32 size = generic_string_data->length(); // had to set to 81

+ 10 - 0
server/ItemStructs.xml

@@ -1319,6 +1319,10 @@
 <Data ElementName="name" Type="EQ2_8Bit_String" Size="1" />
 <Data ElementName="description" Type="EQ2_16Bit_String" Size="1" />
 </Struct>
+<Struct Name="Substruct_SkillItemFooter" ClientVersion="546" >
+<Data ElementName="name" Type="EQ2_8Bit_String" Size="1" />
+<Data ElementName="description" Type="EQ2_16Bit_String" Size="1" />
+</Struct>
 <Struct Name="Substruct_ItemFooter" ClientVersion="547" >
 <Data ElementName="num_effects" Type="int8" IfVariableNotSet="header_info_header_unknown_0_0,header_unknown_0" />
 <Data ElementName="effect_array" Type="Array" ArraySizeVariable="num_effects">
@@ -4456,6 +4460,12 @@
 <Data ElementName="display_until_cancelled" Type="int8" Size="1" />
 <Data ElementName="footer" Substruct="Substruct_ItemFooter" Size="1" />
 </Struct>
+<Struct Name="WS_ItemSkill" ClientVersion="546" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqExamineInfoCmd">
+<Data ElementName="header" Substruct="Substruct_ItemDescription" Size="1" />
+<Data ElementName="spell_info" Substruct="WS_SpellInfo" Size="1" />
+<Data ElementName="scribed" Type="int8" Size="1" />
+<Data ElementName="footer" Substruct="Substruct_SkillItemFooter" Size="1" />
+</Struct>
 <Struct Name="WS_ItemSkill" ClientVersion="547" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqExamineInfoCmd">
 <Data ElementName="header" Substruct="Substruct_ItemDescription" Size="1" />
 <Data ElementName="spell_info" Substruct="WS_SpellInfo" Size="1" />

+ 7 - 5
server/WorldStructs.xml

@@ -1993,9 +1993,12 @@ to zero and treated like placeholders." />
 <Data ElementName="leader_index" Type="int32" Size="1" /> <!-- 4697 -->
 <Data ElementName="pet_id" Type="int32" Size="1" /> <!-- 4701 -->
 <Data ElementName="pet_name" Type="char" Size="32" /> <!-- 4705 -->
-<Data ElementName="pet_hp" Type="float" Size="1" /> <!-- 4737 -->
-<Data ElementName="pet_power" Type="float" Size="1" /><!-- 4741 -->
-<Data ElementName="unknown6" Type="int8" Size="12" /><!-- 4745 -->
+<Data ElementName="unknown6" Type="int8" Size="9" /><!-- 4745 -->
+<Data ElementName="pet_health_pct" Type="float" Size="1" /> <!-- 4737 -->
+<Data ElementName="pet_power_pct" Type="float" Size="1" /><!-- 4741 -->
+<Data ElementName="unknown185" Type="int8" Size="1" />
+<Data ElementName="pet_movement" Type="int8" Size="1" />
+<Data ElementName="pet_behavior" Type="int8" Size="1" />
 <Data ElementName="rain" Type="float" Size="1" /><!-- 4757 -->
 <Data ElementName="rain2" Type="float" Size="1" /><!-- 4761 -->
 <Data ElementName="status_points" Type="int32" Size="1" /> <!-- 4765 -->
@@ -6666,7 +6669,6 @@ to zero and treated like placeholders." />
 <Data ElementName="spell_visual" Type="int16" />
 <Data ElementName="cast_time" Type="float" />
 <Data ElementName="spell_level" Type="int8" />
-<Data ElementName="spell_tier" Type="int8" />
 </Struct>
 <Struct Name="WS_HearCastSpell" ClientVersion="547" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqHearSpellCastCmd">
 <Data ElementName="spawn_id" Type="int32" />
@@ -7157,7 +7159,7 @@ to zero and treated like placeholders." />
 	<Data ElementName="display" Type="int8" />
 	<Data ElementName="loot_type" Type="int32" /> <!-- 0=selected member, 1=regular, 2=lotto -->
 	<Data ElementName="lotto_timeout" Type="int32" />
-	<Data ElementName="spawn_id" Type="int32" Size="5" />
+	<Data ElementName="spawn_id" Type="int32"/>
 </Struct>
 <Struct Name="WS_UpdateLoot" ClientVersion="547" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqUpdateLootCmd" >
 <Data ElementName="loot_count" Type="int16" />