Browse Source

- Fix #412 - "placed" function now called on ItemScript placement in homes. The placed spawn will be the target field (after player).
- Fixed some additional lacking group member checks and bad returns inside mutex locks
- Fix #432 - set languages on sign/conversation, we can now set language in spawn_signs, language setting only garbles description, not title of spawn_signs
* StartDialogConversation has a 9th parameter (after key2) for language (int8), 10th parameter is can_close (defaults to 1)
* StartConversation has an 8th parameter (after key2) for language (int8), 9th prameter is can_close (defaults to 1)
* spawn_signs table now supports the language parameter, db update:
alter table spawn_signs add column language tinyint(3) unsigned default 0;
- Fixed AoM skill display for general skills, languages now display too, but we need to discover how to omit skill values
- /spawn details now includes sign language on the third page if the spawn is a sign.

Emagi 1 year ago
parent
commit
d3819ec135

+ 5 - 2
EQ2/source/WorldServer/Bots/Bot.cpp

@@ -415,10 +415,13 @@ Spell* Bot::GetHoTWardSpell() {
 	{
 		group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
 		deque<GroupMemberInfo*>* members = group->GetMembers();
-		if(!members)
-			return 0;
+
 		for (int8 i = 0; i < members->size(); i++) {
 			Entity* member = members->at(i)->member;
+			
+			if(!member)
+				continue;
+			
 			int8 percent = 0;
 			if (member->GetHP() > 0)
 				percent = (int8)(((float)member->GetHP() / member->GetTotalHP()) * 100);

+ 2 - 3
EQ2/source/WorldServer/Bots/BotCommands.cpp

@@ -114,11 +114,10 @@ void Commands::Command_Bot(Client* client, Seperator* sep) {
 				group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
 				deque<GroupMemberInfo*>* members = group->GetMembers(); 
 				
-				if(!members)
-					return;
-				
 				for (int8 i = 0; i < members->size(); i++) {
 					GroupMemberInfo* gmi2 = members->at(i);
+					if(!gmi2 || !gmi2->member)
+						continue;
 					if (gmi2->member->IsBot() && ((Bot*)gmi2->member)->GetOwner() == client->GetPlayer()) {
 						((Bot*)gmi2->member)->SetMainTank(target);
 						client->Message(CHANNEL_COMMAND_TEXT, "Setting main tank for %s to %s", gmi2->member->GetName(), target->GetName());

+ 1 - 2
EQ2/source/WorldServer/Combat.cpp

@@ -1247,8 +1247,7 @@ void Entity::HandleDeathExperienceDebt(Spawn* killer)
 			{
 				group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
 				deque<GroupMemberInfo*>* members = group->GetMembers();
-				if(!members)
-					return;
+
 				int32 size = members->size();
 				float xpDebtPerMember = ruleDebt/(float)size;
 				deque<GroupMemberInfo*>::iterator itr;

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

@@ -4709,6 +4709,10 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 				details3 += "Speed:	" + to_string(spawn->GetSpeed()) + "\n";
 				details3 += "BaseSpeed:	" + to_string(spawn->GetBaseSpeed()) + "\n";
 
+				if(spawn->IsSign()) {
+					details3 += "Sign Language: " + to_string(((Sign*)spawn)->GetLanguage()) + "\n";
+				}
+				
 				if(spawn->IsEntity())
 				{
 					Entity* ent = (Entity*)spawn;

+ 19 - 29
EQ2/source/WorldServer/LuaFunctions.cpp

@@ -1019,6 +1019,12 @@ int EQ2Emu_lua_StartDialogConversation(lua_State* state) {
 	string mp3 = lua_interface->GetStringValue(state, 6);
 	int32 key1 = lua_interface->GetInt32Value(state, 7);
 	int32 key2 = lua_interface->GetInt32Value(state, 8);
+	int8 language = lua_interface->GetInt8Value(state, 9);
+	
+	int numargs = lua_interface->GetNumberOfArgs(state);
+	int8 can_close = 1;
+	if(numargs > 9)
+		can_close = lua_interface->GetInt32Value(state, 10);
 
 	lua_interface->ResetFunctionStack(state);
 	if (conversation && text.length() > 0 && (spawn || item) && player && player->IsPlayer()) {
@@ -1030,15 +1036,15 @@ int EQ2Emu_lua_StartDialogConversation(lua_State* state) {
 					type++;
 
 				if (mp3.length() > 0)
-					client->DisplayConversation((Entity*)spawn, type, conversation, const_cast<char*>(text.c_str()), mp3.c_str(), key1, key2);
+					client->DisplayConversation((Entity*)spawn, type, conversation, const_cast<char*>(text.c_str()), mp3.c_str(), key1, key2, language, can_close);
 				else
-					client->DisplayConversation((Entity*)spawn, type, conversation, const_cast<char*>(text.c_str()));
+					client->DisplayConversation((Entity*)spawn, type, conversation, const_cast<char*>(text.c_str()), nullptr, 0, 0, language, can_close);
 			}
 			else {
 				if (mp3.length() > 0)
-					client->DisplayConversation(item, conversation, const_cast<char*>(text.c_str()), type, mp3.c_str(), key1, key2);
+					client->DisplayConversation(item, conversation, const_cast<char*>(text.c_str()), type, mp3.c_str(), key1, key2, language, can_close);
 				else
-					client->DisplayConversation(item, conversation, const_cast<char*>(text.c_str()), type);
+					client->DisplayConversation(item, conversation, const_cast<char*>(text.c_str()), type, nullptr, 0, 0, language, can_close);
 			}
 		}
 	}
@@ -1047,29 +1053,6 @@ int EQ2Emu_lua_StartDialogConversation(lua_State* state) {
 	return 0;
 }
 
-/*int EQ2Emu_lua_StartItemConversation(lua_State* state){
-	if(!lua_interface)
-		return 0;
-	vector<ConversationOption>* conversation = lua_interface->GetConversation(state);
-	Item* item = lua_interface->GetItem(state, 2);
-	Spawn* player = lua_interface->GetSpawn(state, 3);
-	string text = lua_interface->GetStringValue(state, 4);
-	string mp3 = lua_interface->GetStringValue(state, 5);
-	int32 key1 = lua_interface->GetInt32Value(state, 6);
-	int32 key2 = lua_interface->GetInt32Value(state, 7);
-	if(conversation && text.length() > 0 && item && player && player->IsPlayer()){
-		Client* client = player->GetZone()->GetClientBySpawn(player);
-		if(client){
-			if(mp3.length() > 0)
-				client->DisplayConversation(item, conversation, (char*)text.c_str(), mp3.c_str(), key1, key2);
-			else
-				client->DisplayConversation(item, conversation, (char*)text.c_str());
-		}
-		safe_delete(conversation);
-	}
-	return 0;
-}*/
-
 int EQ2Emu_lua_StartConversation(lua_State* state) {
 	if (!lua_interface)
 		return 0;
@@ -1080,13 +1063,20 @@ int EQ2Emu_lua_StartConversation(lua_State* state) {
 	string mp3 = lua_interface->GetStringValue(state, 5);
 	int32 key1 = lua_interface->GetInt32Value(state, 6);
 	int32 key2 = lua_interface->GetInt32Value(state, 7);
+	int8 language = lua_interface->GetInt32Value(state, 8);
+	
+	int numargs = lua_interface->GetNumberOfArgs(state);
+	int8 can_close = 1;
+	if(numargs > 8)
+		can_close = lua_interface->GetInt32Value(state, 9);
+	
 	lua_interface->ResetFunctionStack(state);
 	if (conversation && conversation->size() > 0 && text.length() > 0 && source && player && player->IsPlayer()) {
 		Client* client = source->GetZone()->GetClientBySpawn(player);
 		if (mp3.length() > 0)
-			client->DisplayConversation(source, 1, conversation, text.c_str(), mp3.c_str(), key1, key2);
+			client->DisplayConversation(source, 1, conversation, text.c_str(), mp3.c_str(), key1, key2, language, can_close);
 		else
-			client->DisplayConversation(source, 1, conversation, text.c_str());
+			client->DisplayConversation(source, 1, conversation, text.c_str(), nullptr, 0, 0, language, can_close);
 		safe_delete(conversation);
 		lua_interface->SetConversationValue(state, NULL);
 	}

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

@@ -6100,6 +6100,10 @@ void Player::AddAAEntry(int16 template_id, int8 tab_id, int32 aa_id, int16 order
 	
 }
 void Player::AddLanguage(int32 id, const char *name, bool save_needed){
+	Skill* skill = master_skill_list.GetSkillByName(name);
+	if(skill && !GetSkills()->HasSkill(skill->skill_id)) {
+		AddSkill(skill->skill_id, 1, skill->max_val, true);
+	}
 	// Check to see if the player already has the language
 	if (HasLanguage(id))
 		return;

+ 2 - 0
EQ2/source/WorldServer/Sign.cpp

@@ -48,6 +48,7 @@ Sign::Sign(){
 	include_location = false;
 	include_heading = false;
 	zone_id = 0;
+	language = 0;
 }
 
 Sign::~Sign(){
@@ -150,6 +151,7 @@ Sign* Sign::Copy(){
 	new_spawn->SetOmittedByDBFlag(IsOmittedByDBFlag());
 	new_spawn->SetLootTier(GetLootTier());
 	new_spawn->SetLootDropType(GetLootDropType());
+	new_spawn->SetLanguage(GetLanguage());
 	return new_spawn;
 }
 

+ 4 - 1
EQ2/source/WorldServer/Sign.h

@@ -66,7 +66,9 @@ public:
 	bool	GetIncludeLocation();
 	void	SetIncludeHeading(bool val);
 	bool	GetIncludeHeading();
-
+	void	SetLanguage(int8 in_language) { language = in_language; }
+	int8	GetLanguage() { return language; }
+	
 private:
 	string	description;
 	string	title;
@@ -83,6 +85,7 @@ private:
 	float	sign_distance;
 	bool	include_location;
 	bool	include_heading;
+	int8	language;
 };
 
 #endif

+ 24 - 8
EQ2/source/WorldServer/Skills.cpp

@@ -370,15 +370,31 @@ EQ2Packet* PlayerSkillList::GetSkillPacket(int16 version){
 				if (version <= 546 && skill->skill_type >= SKILL_TYPE_GENERAL) { //covert it to DOF types
 					packet->setArrayDataByName("type", skill->skill_type-2, i);					
 				}
-				else
+				else if(version >= 60085 && skill->skill_type >= 12) {
+					packet->setArrayDataByName("type", skill->skill_type-1, i);
+				}
+				else {
 					packet->setArrayDataByName("type", skill->skill_type, i);
-				packet->setArrayDataByName("current_val", skill->current_val, i);
-				packet->setArrayDataByName("base_val", skill->current_val, i);// skill->
-				packet->setArrayDataByName("skill_delta", 0, i);// skill_with_bonuses- skill->current_val
-				packet->setArrayDataByName("skill_delta2", skill_max_with_bonuses - skill->max_val, i);// skill_max_with_bonuses - skill->max_val, i);
-				packet->setArrayDataByName("max_val", skill->max_val, i);
-				packet->setArrayDataByName("display_minval", skill->display, i);
-				packet->setArrayDataByName("display_maxval", skill->display, i);
+				}
+				
+				int16 current_val = skill->current_val;
+				
+				if(skill->skill_type == SKILL_TYPE_LANGUAGE) { // 13 is language in the DB?? 14 is the skill type though
+					packet->setArrayDataByName("language_unknown", skill->skill_id, i);
+					packet->setArrayDataByName("display_maxval", 1, i);
+					packet->setArrayDataByName("max_val", 1, i);
+				}
+				else {
+					packet->setArrayDataByName("max_val", skill->max_val, i);
+					packet->setArrayDataByName("display_minval", skill->display, i);
+					packet->setArrayDataByName("display_maxval", skill->display, i);
+					packet->setArrayDataByName("skill_delta", 0, i);// skill_with_bonuses- skill->current_val
+					packet->setArrayDataByName("skill_delta2", skill_max_with_bonuses - skill->max_val, i);// skill_max_with_bonuses - skill->max_val, i);
+				}
+				
+				packet->setArrayDataByName("current_val", current_val, i);
+				packet->setArrayDataByName("base_val", current_val, i);
+				
 				i++;
 			}
 		}

+ 4 - 2
EQ2/source/WorldServer/Spawn.cpp

@@ -403,7 +403,6 @@ void Spawn::InitializeFooterPacketData(Player* player, PacketStruct* footer) {
 			footer->setDataByName("widget_z", widget->GetWidgetZ());
 		}
 		footer->setDataByName("widget_id", widget->GetWidgetID());
-		footer->setDataByName("unknown3c", 6);
 	}
 	else if (IsSign()){
 		Sign* sign = (Sign*)this;
@@ -411,13 +410,16 @@ void Spawn::InitializeFooterPacketData(Player* player, PacketStruct* footer) {
 		footer->setDataByName("widget_x", sign->GetWidgetX());
 		footer->setDataByName("widget_y", sign->GetWidgetY());
 		footer->setDataByName("widget_z", sign->GetWidgetZ());
-		footer->setDataByName("unknown2b", 6);
 		if (sign->GetSignTitle())
 			footer->setMediumStringByName("title", sign->GetSignTitle());
 		if (sign->GetSignDescription())
 			footer->setMediumStringByName("description", sign->GetSignDescription());
 		footer->setDataByName("sign_distance", sign->GetSignDistance());
 		footer->setDataByName("show", 1);
+		// in live we see that the language is set when the player does not have it, otherwise its left as 00's.
+		if(!player->HasLanguage(sign->GetLanguage())) {
+			footer->setDataByName("language", sign->GetLanguage());
+		}
 	}
 
 	if ( IsPlayer())

+ 2 - 4
EQ2/source/WorldServer/SpellProcess.cpp

@@ -2386,8 +2386,7 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell)
 						{
 							group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
 							deque<GroupMemberInfo*>* members = group->GetMembers();
-							if(!members)
-								return;
+							
 							// iterate through players group members
 							for (itr = members->begin(); itr != members->end(); itr++)
 							{
@@ -2436,8 +2435,7 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell)
 						{
 							group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
 							deque<GroupMemberInfo*>* members = group->GetMembers();
-							if(!members)
-								return;
+							
 							Entity* group_member = 0;
 							for (itr = members->begin(); itr != members->end(); itr++) {
 								group_member = (*itr)->member;

+ 9 - 4
EQ2/source/WorldServer/WorldDatabase.cpp

@@ -1216,7 +1216,7 @@ void WorldDatabase::LoadSigns(ZoneServer* zone){
 	Sign* sign = 0;
 	int32 id = 0;
 	int32 total = 0;
-	MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT ss.spawn_id, s.name, s.model_type, s.size, s.show_command_icon, ss.widget_id, ss.widget_x, ss.widget_y, ss.widget_z, s.command_primary, s.command_secondary, s.collision_radius, ss.icon, ss.type, ss.title, ss.description, ss.sign_distance, ss.zone_id, ss.zone_x, ss.zone_y, ss.zone_z, ss.zone_heading, ss.include_heading, ss.include_location, s.transport_id, s.size_offset, s.display_hand_icon, s.visual_state, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, s.loot_tier, s.loot_drop_type\n"
+	MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT ss.spawn_id, s.name, s.model_type, s.size, s.show_command_icon, ss.widget_id, ss.widget_x, ss.widget_y, ss.widget_z, s.command_primary, s.command_secondary, s.collision_radius, ss.icon, ss.type, ss.title, ss.description, ss.sign_distance, ss.zone_id, ss.zone_x, ss.zone_y, ss.zone_z, ss.zone_heading, ss.include_heading, ss.include_location, s.transport_id, s.size_offset, s.display_hand_icon, s.visual_state, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, s.loot_tier, s.loot_drop_type, ss.language\n"
 												  "FROM spawn s\n"
 												  "INNER JOIN spawn_signs ss\n"
 												  "ON s.id = ss.spawn_id\n"
@@ -1295,6 +1295,8 @@ void WorldDatabase::LoadSigns(ZoneServer* zone){
 		
 		sign->SetLootDropType(atoul(row[35]));
 
+		sign->SetLanguage(atoul(row[36]));
+		
 		zone->AddSign(id, sign);
 		total++;
 
@@ -3425,14 +3427,14 @@ bool WorldDatabase::SaveSpawnInfo(Spawn* spawn){
 		}
 		else if(spawn->IsSign()){
 			Sign* sign = (Sign*)spawn;
-			query.RunQuery2(Q_UPDATE, "update spawn_signs, spawn set name='%s', race=%i, model_type=%i, show_name=%i, attackable=%i, show_level=%i, show_command_icon=%i, display_hand_icon=%i, size=%i, hp=%u, power=%u, collision_radius=%i, command_primary=%u, command_secondary=%u, visual_state=%i, faction_id=%u, suffix ='%s', prefix='%s', last_name='%s', type='%s', zone_id = %u, widget_id = %u, title='%s', widget_x = %f, widget_y = %f, widget_z = %f, icon = %u, description='%s', sign_distance = %f, zone_x = %f, zone_y = %f, zone_z = %f, zone_heading = %f, include_heading = %u, include_location = %u, merchant_min_level = %u, merchant_max_level = %u where spawn_signs.spawn_id = spawn.id and spawn.id = %u",
+			query.RunQuery2(Q_UPDATE, "update spawn_signs, spawn set name='%s', race=%i, model_type=%i, show_name=%i, attackable=%i, show_level=%i, show_command_icon=%i, display_hand_icon=%i, size=%i, hp=%u, power=%u, collision_radius=%i, command_primary=%u, command_secondary=%u, visual_state=%i, faction_id=%u, suffix ='%s', prefix='%s', last_name='%s', type='%s', zone_id = %u, widget_id = %u, title='%s', widget_x = %f, widget_y = %f, widget_z = %f, icon = %u, description='%s', sign_distance = %f, zone_x = %f, zone_y = %f, zone_z = %f, zone_heading = %f, include_heading = %u, include_location = %u, merchant_min_level = %u, merchant_max_level = %u, language = %u where spawn_signs.spawn_id = spawn.id and spawn.id = %u",
 				name.c_str(), spawn->GetRace(), spawn->GetModelType(), spawn->appearance.display_name, spawn->appearance.attackable, spawn->appearance.show_level, spawn->appearance.show_command_icon, spawn->appearance.display_hand_icon, spawn->GetSize(),
 				spawn->GetTotalHP(), spawn->GetTotalPower(), spawn->GetCollisionRadius(), spawn->GetPrimaryCommandListID(), spawn->GetSecondaryCommandListID(), spawn->GetVisualState(), spawn->GetFactionID(),
 				suffix.c_str(), prefix.c_str(), last_name.c_str(), sign->GetSignType(), sign->GetSignZoneID(), 
 				sign->GetWidgetID(), sign->GetSignTitle(), sign->GetWidgetX(), sign->GetWidgetY(), sign->GetWidgetZ(), 
 				sign->GetIconValue(), sign->GetSignDescription(), sign->GetSignDistance(), sign->GetSignZoneX(), 
 				sign->GetSignZoneY(), sign->GetSignZoneZ(), sign->GetSignZoneHeading(), sign->GetIncludeHeading(), 
-				sign->GetIncludeLocation(), spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel(), spawn->GetDatabaseID());
+				sign->GetIncludeLocation(), spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel(), sign->GetLanguage(), spawn->GetDatabaseID());
 		}
 	}
 	if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
@@ -6391,7 +6393,7 @@ bool WorldDatabase::LoadSign(ZoneServer* zone, int32 spawn_id) {
 	Sign* sign = 0;
 	int32 id = 0;
 	DatabaseResult result;
-	database_new.Select(&result, "SELECT ss.spawn_id, s.name, s.model_type, s.size, s.show_command_icon, ss.widget_id, ss.widget_x, ss.widget_y, ss.widget_z, s.command_primary, s.command_secondary, s.collision_radius, ss.icon, ss.type, ss.title, ss.description, ss.sign_distance, ss.zone_id, ss.zone_x, ss.zone_y, ss.zone_z, ss.zone_heading, ss.include_heading, ss.include_location, s.transport_id, s.size_offset, s.display_hand_icon, s.visual_state, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, s.loot_tier, s.loot_drop_type\n"
+	database_new.Select(&result, "SELECT ss.spawn_id, s.name, s.model_type, s.size, s.show_command_icon, ss.widget_id, ss.widget_x, ss.widget_y, ss.widget_z, s.command_primary, s.command_secondary, s.collision_radius, ss.icon, ss.type, ss.title, ss.description, ss.sign_distance, ss.zone_id, ss.zone_x, ss.zone_y, ss.zone_z, ss.zone_heading, ss.include_heading, ss.include_location, s.transport_id, s.size_offset, s.display_hand_icon, s.visual_state, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, s.loot_tier, s.loot_drop_type, ss.language\n"
 								 "FROM spawn s\n"
 								 "INNER JOIN spawn_signs ss\n"
 								 "ON ss.spawn_id = s.id\n"
@@ -6452,6 +6454,9 @@ bool WorldDatabase::LoadSign(ZoneServer* zone, int32 spawn_id) {
 		sign->SetLootTier(result.GetInt32(32));
 
 		sign->SetLootDropType(result.GetInt32(33));
+		
+		sign->SetLanguage(result.GetInt8(34));
+		
 		zone->AddSign(id, sign);
 
 

+ 37 - 20
EQ2/source/WorldServer/client.cpp

@@ -1303,7 +1303,12 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
 			int32 response_index = packet->getType_int32_ByName("response");
 			if (GetCurrentZone()) {
 				MConversation.readlock();
-				Spawn* spawn = conversation_spawns[conversation_id];
+				int32 spawn_id = conversation_spawns[conversation_id];
+				Spawn* spawn = nullptr;
+				if(spawn_id) {
+					spawn = GetCurrentZone()->GetSpawnByID(spawn_id);
+				}
+				
 				Item* item = conversation_items[conversation_id];
 				MConversation.releasereadlock();
 				if (conversation_map.count(conversation_id) > 0 && conversation_map[conversation_id].count(response_index) > 0) {
@@ -2549,6 +2554,8 @@ bool Client::HandleLootItem(Spawn* entity, Item* item) {
 						if(members) {
 							for (int8 i = 0; i < members->size(); i++) {
 								Entity* member = members->at(i)->member;
+								if(!member)
+									continue;
 
 								if ((member->GetZone() != this->GetPlayer()->GetZone()))
 									continue;
@@ -5194,11 +5201,12 @@ void Client::CastGroupOrSelf(Entity* source, uint32 spellID, uint32 spellTier, f
 			{
 				group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
 				deque<GroupMemberInfo*>* members = group->GetMembers();
-				if(!members)
-					return;
+
 				for (int8 i = 0; i < members->size(); i++) {
 					Entity* member = members->at(i)->member;
-
+					if(!member)
+						continue;
+					
 					if (!member->Alive() || (member->GetZone() != source->GetZone()))
 						continue;
 					// if we have a radius provided then check if the group member is outside the radius or not
@@ -6323,12 +6331,13 @@ void Client::GiveQuestReward(Quest* quest) {
 	RemovePlayerQuest(quest->GetQuestID(), true, false);	
 }
 
-void Client::DisplayConversation(int32 conversation_id, int32 spawn_id, vector<ConversationOption>* conversations, const char* text, const char* mp3, int32 key1, int32 key2) {
+void Client::DisplayConversation(int32 conversation_id, int32 spawn_id, vector<ConversationOption>* conversations, const char* text, const char* mp3, int32 key1, int32 key2, int8 language, int8 can_close) {
 	PacketStruct* packet = configReader.getStruct("WS_DialogOpen", GetVersion());
 	if (packet) {
 		packet->setDataByName("conversation_id", conversation_id);
 		packet->setDataByName("text", text);
-		packet->setDataByName("unknown2", 1);
+		packet->setDataByName("language", language); // default 0
+		packet->setDataByName("can_close", can_close); // default 1
 		conversation_map[conversation_id].clear();
 		if (conversations) {
 			packet->setArrayLengthByName("num_responses", conversations->size());
@@ -6350,7 +6359,7 @@ void Client::DisplayConversation(int32 conversation_id, int32 spawn_id, vector<C
 
 }
 
-void Client::DisplayConversation(Item* item, vector<ConversationOption>* conversations, const char* text, int8 type, const char* mp3, int32 key1, int32 key2) {
+void Client::DisplayConversation(Item* item, vector<ConversationOption>* conversations, const char* text, int8 type, const char* mp3, int32 key1, int32 key2, int8 language, int8 can_close) {
 	if (!item || !text || !conversations || conversations->size() == 0) {
 		return;
 	}
@@ -6363,13 +6372,13 @@ void Client::DisplayConversation(Item* item, vector<ConversationOption>* convers
 	conversation_items[conversation_id] = item;
 	MConversation.releasewritelock();
 	if (type == 4)
-		DisplayConversation(conversation_id, player->GetIDWithPlayerSpawn(player), conversations, text, mp3, key1, key2);
+		DisplayConversation(conversation_id, player->GetIDWithPlayerSpawn(player), conversations, text, mp3, key1, key2, language, can_close);
 	else
-		DisplayConversation(conversation_id, 0xFFFFFFFF, conversations, text, mp3, key1, key2);
+		DisplayConversation(conversation_id, 0xFFFFFFFF, conversations, text, mp3, key1, key2, language, can_close);
 
 }
 
-void Client::DisplayConversation(Spawn* src, int8 type, vector<ConversationOption>* conversations, const char* text, const char* mp3, int32 key1, int32 key2) {
+void Client::DisplayConversation(Spawn* src, int8 type, vector<ConversationOption>* conversations, const char* text, const char* mp3, int32 key1, int32 key2, int8 language, int8 can_close) {
 	if (!src || !(type == 1 || type == 2 || type == 3) || !text /*|| !conversations || conversations->size() == 0*/) {
 		return;
 	}
@@ -6379,18 +6388,18 @@ void Client::DisplayConversation(Spawn* src, int8 type, vector<ConversationOptio
 		conversation_id = next_conversation_id;
 	}
 	MConversation.writelock();
-	conversation_spawns[conversation_id] = src;
+	conversation_spawns[conversation_id] = src->GetID();
 	MConversation.releasewritelock();
 
 	/* Spawns can start two different types of conversations.
 	 * Type 1: The chat type with bubbles.
 	 * Type 2: The dialog type with the blue box. */
 	if (type == 1)
-		DisplayConversation(conversation_id, player->GetIDWithPlayerSpawn(src), conversations, text, mp3, key1, key2);
+		DisplayConversation(conversation_id, player->GetIDWithPlayerSpawn(src), conversations, text, mp3, key1, key2, language, can_close);
 	else if (type == 2)
-		DisplayConversation(conversation_id, 0xFFFFFFFF, conversations, text, mp3, key1, key2);
+		DisplayConversation(conversation_id, 0xFFFFFFFF, conversations, text, mp3, key1, key2, language, can_close);
 	else //if (type == 3)
-		DisplayConversation(conversation_id, player->GetIDWithPlayerSpawn(player), conversations, text, mp3, key1, key2);
+		DisplayConversation(conversation_id, player->GetIDWithPlayerSpawn(player), conversations, text, mp3, key1, key2, language, can_close);
 
 }
 
@@ -6409,7 +6418,7 @@ void Client::CloseDialog(int32 conversation_id) {
 		conversation_items.erase(itr);
 	}
 	
-	std::map<int32, Spawn*>::iterator itr2 = conversation_spawns.find(conversation_id);
+	std::map<int32, int32>::iterator itr2 = conversation_spawns.find(conversation_id);
 
 	while((itr2 = conversation_spawns.find(conversation_id)) != conversation_spawns.end())
 	{
@@ -6423,9 +6432,9 @@ int32 Client::GetConversationID(Spawn* spawn, Item* item) {
 	int32 conversation_id = 0;
 	MConversation.readlock();
 	if (spawn) {
-		map<int32, Spawn*>::iterator itr;
+		map<int32, int32>::iterator itr;
 		for (itr = conversation_spawns.begin(); itr != conversation_spawns.end(); itr++) {
-			if (itr->second == spawn) {
+			if (itr->second == spawn->GetID()) {
 				conversation_id = itr->first;
 				break;
 			}
@@ -10461,9 +10470,17 @@ bool Client::PopulateHouseSpawnFinalize()
 				query.RunQuery2(Q_INSERT, "insert into spawn_instance_data set spawn_id = %u, spawn_location_id = %u, pickup_item_id = %u, pickup_unique_item_id = %u", tmp->GetDatabaseID(), tmp->GetSpawnLocationID(), tmp->GetPickupItemID(), uniqueID);
 			}
 
-			database.DeleteItem(GetCharacterID(), uniqueItem, 0);
-			GetPlayer()->item_list.RemoveItem(uniqueItem, true);
-			QueuePacket(GetPlayer()->SendInventoryUpdate(GetVersion()));
+			if(lua_interface->RunItemScript(uniqueItem->GetItemScript(), "placed", uniqueItem, GetPlayer(), tmp))
+			{
+				uniqueItem = GetPlayer()->item_list.GetItemFromUniqueID(uniqueID);
+			}
+			
+			if(uniqueItem) {
+				database.DeleteItem(GetCharacterID(), uniqueItem, 0);
+				GetPlayer()->item_list.RemoveItem(uniqueItem, true);
+				QueuePacket(GetPlayer()->SendInventoryUpdate(GetVersion()));
+			}
+			
 			SetPlacementUniqueItemID(0);
 		}
 		return true;

+ 4 - 4
EQ2/source/WorldServer/client.h

@@ -285,9 +285,9 @@ public:
 	void	AcceptQuestReward(Quest* quest, int32 item_id);
 	Quest*	GetPendingQuestAcceptance(int32 item_id);
 	Quest*	GetActiveQuest(int32 quest_id);
-	void	DisplayConversation(int32 conversation_id, int32 spawn_id, vector<ConversationOption>* conversations, const char* text, const char* mp3, int32 key1, int32 key2);
-	void	DisplayConversation(Item* item, vector<ConversationOption>* conversations, const char* text, int8 type, const char* mp3 = 0, int32 key1 = 0, int32 key2 = 0);
-	void	DisplayConversation(Spawn* src, int8 type, vector<ConversationOption>* conversations, const char* text, const char* mp3 = 0, int32 key1 = 0, int32 key2 = 0);
+	void	DisplayConversation(int32 conversation_id, int32 spawn_id, vector<ConversationOption>* conversations, const char* text, const char* mp3, int32 key1, int32 key2, int8 language = 0, int8 can_close = 1);
+	void	DisplayConversation(Item* item, vector<ConversationOption>* conversations, const char* text, int8 type, const char* mp3 = 0, int32 key1 = 0, int32 key2 = 0, int8 language = 0, int8 can_close = 1);
+	void	DisplayConversation(Spawn* src, int8 type, vector<ConversationOption>* conversations, const char* text, const char* mp3 = 0, int32 key1 = 0, int32 key2 = 0, int8 language = 0, int8 can_close = 1);
 	void	CloseDialog(int32 conversation_id);
 	int32	GetConversationID(Spawn* spawn, Item* item);
 	void	CombineSpawns(float radius, Spawn* spawn);
@@ -568,7 +568,7 @@ private:
 	Spawn*	combine_spawn;
 	int8	num_active_failures;
 	int32	next_conversation_id;
-	map<int32, Spawn*> conversation_spawns;
+	map<int32, int32> conversation_spawns;
 	map<int32, Item*> conversation_items;
 	Mutex MConversation;
 	map<int32, map<int8, string> > conversation_map;