9
3

2 Ревизии fddd7b37c0 ... 3ea7ffebb6

Автор SHA1 Съобщение Дата
  LethalEncounter 3ea7ffebb6 Merge branch 'master' of http://cutpon.com:3000/devn00b/EQ2EMu преди 4 години
  LethalEncounter ebddaba763 Tutorial instructions for the Far Journey zone преди 4 години
променени са 38 файла, в които са добавени 1561 реда и са изтрити 639 реда
  1. 1 1
      EQ2/source/LoginServer/Login.vcxproj
  2. 1 1
      EQ2/source/WorldServer/Bots/BotCommands.cpp
  3. 2 2
      EQ2/source/WorldServer/Chat/ChatChannel.cpp
  4. 6 6
      EQ2/source/WorldServer/Combat.cpp
  5. 87 90
      EQ2/source/WorldServer/Commands/Commands.cpp
  6. 3 43
      EQ2/source/WorldServer/Commands/Commands.h
  7. 2 2
      EQ2/source/WorldServer/Entity.cpp
  8. 6 6
      EQ2/source/WorldServer/GroundSpawn.cpp
  9. 27 27
      EQ2/source/WorldServer/Guilds/Guild.cpp
  10. 17 3
      EQ2/source/WorldServer/Items/Items.cpp
  11. 1 2
      EQ2/source/WorldServer/Items/Items.h
  12. 504 46
      EQ2/source/WorldServer/LuaFunctions.cpp
  13. 28 0
      EQ2/source/WorldServer/LuaFunctions.h
  14. 80 7
      EQ2/source/WorldServer/LuaInterface.cpp
  15. 12 1
      EQ2/source/WorldServer/LuaInterface.h
  16. 4 4
      EQ2/source/WorldServer/NPC_AI.cpp
  17. 42 34
      EQ2/source/WorldServer/Player.cpp
  18. 74 3
      EQ2/source/WorldServer/Player.h
  19. 1 1
      EQ2/source/WorldServer/PlayerGroups.cpp
  20. 10 4
      EQ2/source/WorldServer/Quests.cpp
  21. 34 12
      EQ2/source/WorldServer/Spawn.cpp
  22. 4 4
      EQ2/source/WorldServer/SpellProcess.cpp
  23. 3 3
      EQ2/source/WorldServer/Tradeskills/Tradeskills.cpp
  24. 3 3
      EQ2/source/WorldServer/Transmute.cpp
  25. 6 41
      EQ2/source/WorldServer/World.cpp
  26. 1 1
      EQ2/source/WorldServer/WorldDatabase.cpp
  27. 434 118
      EQ2/source/WorldServer/client.cpp
  28. 8 4
      EQ2/source/WorldServer/client.h
  29. 42 54
      EQ2/source/WorldServer/zoneserver.cpp
  30. 1 1
      EQ2/source/WorldServer/zoneserver.h
  31. 1 1
      EQ2/source/common/ConfigReader.cpp
  32. 17 9
      EQ2/source/common/PacketStruct.cpp
  33. 2 1
      EQ2/win/EQ2WorldVC10.sln
  34. 3 2
      EQ2/win/VC10Projects/EQ2World.vcxproj
  35. 25 28
      server/Quests/PeatBog/Reinforcements.lua
  36. 50 63
      server/Quests/PeatBog/ambushed.lua
  37. 9 8
      server/SpawnStructs.xml
  38. 10 3
      server/WorldStructs.xml

+ 1 - 1
EQ2/source/LoginServer/Login.vcxproj

@@ -27,7 +27,7 @@
     <IncludePath>$(SolutionDir)..\source\depends\mariadb-10.1.19\include;$(SolutionDir)..\source\depends\zlib\include;$(SolutionDir)..\source\depends\recastnavigation\Detour\Include;$(SolutionDir)..\source\depends\boost_1_72_0\;$(SolutionDir)..\source\depends\glm\;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
     <LibraryPath>$(SolutionDir)..\source\depends\recastnavigation\RecastDemo\Build\vs2019\lib\Debug;$(SolutionDir)..\source\depends\mariadb-10.1.19\lib\64-debug;$(SolutionDir)..\source\depends\zlib\lib;$(SolutionDir)..\source\depends\boost_1_72_0\lib64-msvc-14.2;$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(NETFXKitsDir)Lib\um\x64</LibraryPath>
     <LinkIncremental>false</LinkIncremental>
-    <OutDir>$(SolutionDir)loginserver\</OutDir>
+    <OutDir>E:\EQ2EMu\my_server\</OutDir>
     <IntDir>.\$(ProjectName)__Debug64\</IntDir>
     <TargetName>$(ProjectName)__Debug64</TargetName>
   </PropertyGroup>

+ 1 - 1
EQ2/source/WorldServer/Bots/BotCommands.cpp

@@ -591,7 +591,7 @@ void Commands::Command_Bot_Inv(Client* client, Seperator* sep) {
 				Item* item = bot->GetEquipmentList()->GetItem(i);
 				if (item) {
 					//\\aITEM %u %u:%s\\/a
-					item_list += to_string(i) + ":\t\\aITEM " + to_string(item->details.item_id) + " " + to_string(item->details.unique_id) + ":" + item->name + "\\/a \n";
+					item_list += to_string(i) + ":\t" + item->CreateItemLink(GetVersion(), true) + "\n";
 				}
 			}
 

+ 2 - 2
EQ2/source/WorldServer/Chat/ChatChannel.cpp

@@ -142,7 +142,7 @@ bool ChatChannel::TellChannel(Client *client, const char *message, const char* n
 			packet_struct->setDataByName("from", name2);
 
 		packet_struct->setDataByName("to", to_client->GetPlayer()->GetName());
-		packet_struct->setDataByName("channel", 34);
+		packet_struct->setDataByName("channel", to_client->GetMessageChannelColor(CHANNEL_CUSTOM_CHANNEL));
 		packet_struct->setDataByName("language", 0);
 		packet_struct->setDataByName("message", message);
 		packet_struct->setDataByName("channel_name", name);
@@ -170,7 +170,7 @@ bool ChatChannel::TellChannelClient(Client* to_client, const char* message, cons
 		packet_struct->setDataByName("to_spawn_id", 0xFFFFFFFF);
 		packet_struct->setDataByName("from", name2);
 		packet_struct->setDataByName("to", to_client->GetPlayer()->GetName());
-		packet_struct->setDataByName("channel", 34);
+		packet_struct->setDataByName("channel", to_client->GetMessageChannelColor(CHANNEL_CUSTOM_CHANNEL));
 		packet_struct->setDataByName("language", 0);
 		packet_struct->setDataByName("message", message);
 		packet_struct->setDataByName("channel_name", name);

+ 6 - 6
EQ2/source/WorldServer/Combat.cpp

@@ -156,14 +156,14 @@ bool Entity::AttackAllowed(Entity* target, float distance, bool range_attack) {
 			// Distance is less then min weapon range
 			if(distance < weapon->ranged_info->range_low) {
 				if (client)
-					client->SimpleMessage(CHANNEL_COLOR_COMBAT, "Your target is too close! Move back!");
+					client->SimpleMessage(CHANNEL_GENERAL_COMBAT, "Your target is too close! Move back!");
 				LogWrite(COMBAT__DEBUG, 3, "AttackAllowed", "Failed to attack: range attack, target to close");
 				return false;
 			}
 			// Distance is greater then max weapon range
 			if  (distance > (weapon->ranged_info->range_high + ammo->thrown_info->range)) {
 				if (client)
-					client->SimpleMessage(CHANNEL_COLOR_COMBAT, "Your target is too far away! Move closer!");
+					client->SimpleMessage(CHANNEL_GENERAL_COMBAT, "Your target is too far away! Move closer!");
 				LogWrite(COMBAT__DEBUG, 3, "AttackAllowed", "Failed to attack: range attack, target is to far");
 				return false;
 			}
@@ -414,7 +414,7 @@ bool Entity::SpellAttack(Spawn* victim, float distance, LuaSpell* luaspell, int8
 				string success_message = spell->GetSpellData()->success_message;
 				if(success_message.find("%t") < 0xFFFFFFFF)
 					success_message.replace(success_message.find("%t"), 2, victim->GetName());
-				client->Message(CHANNEL_COLOR_SPELL, success_message.c_str());
+				client->Message(CHANNEL_YOU_CAST, success_message.c_str());
 				//commented out the following line as it was causing a duplicate message EmemJR 5/4/2019
 				//GetZone()->SendDamagePacket(this, victim, DAMAGE_PACKET_TYPE_SPELL_DAMAGE, hit_result, damage_type, 0, spell->GetName()); 
 			}
@@ -423,7 +423,7 @@ bool Entity::SpellAttack(Spawn* victim, float distance, LuaSpell* luaspell, int8
 			string effect_message = spell->GetSpellData()->effect_message;
 			if(effect_message.find("%t") < 0xFFFFFFFF)
 				effect_message.replace(effect_message.find("%t"), 2, victim->GetName());
-			GetZone()->SimpleMessage(CHANNEL_COLOR_SPELL_EFFECT, effect_message.c_str(), victim, 50);
+			GetZone()->SimpleMessage(CHANNEL_SPELLS, effect_message.c_str(), victim, 50);
 		}
 	}
 	else {
@@ -506,13 +506,13 @@ bool Entity::ProcAttack(Spawn* victim, int8 damage_type, int32 low_damage, int32
 			if(client) {
 				if(success_msg.find("%t") < 0xFFFFFFFF)
 					success_msg.replace(success_msg.find("%t"), 2, victim->GetName());
-				client->Message(CHANNEL_COLOR_SPELL, success_msg.c_str());
+				client->Message(CHANNEL_YOU_CAST, success_msg.c_str());
 			}
 		}
 		if (effect_msg.length() > 0) {
 			if(effect_msg.find("%t") < 0xFFFFFFFF)
 				effect_msg.replace(effect_msg.find("%t"), 2, victim->GetName());
-			GetZone()->SimpleMessage(CHANNEL_COLOR_SPELL_EFFECT, effect_msg.c_str(), victim, 50);
+			GetZone()->SimpleMessage(CHANNEL_SPELLS, effect_msg.c_str(), victim, 50);
 		}
 	}
 	else {

+ 87 - 90
EQ2/source/WorldServer/Commands/Commands.cpp

@@ -1469,7 +1469,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 						Recipe* recipe_book = new Recipe(master_recipebook_list.GetRecipeBooks(item->details.item_id));
 						// if valid recipe book and the player doesn't have it
 						if (recipe_book && recipe_book->GetLevel() > client->GetPlayer()->GetTSLevel()) {
-							client->Message(CHANNEL_COLOR_WHITE, "Your tradeskill level is not high enough to scribe this book.");
+							client->Message(CHANNEL_NARRATIVE, "Your tradeskill level is not high enough to scribe this book.");
 							safe_delete(recipe_book);
 						}
 						else if (recipe_book && !(client->GetPlayer()->GetRecipeBookList()->HasRecipeBook(item->details.item_id))) {
@@ -1500,7 +1500,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 									Recipe* recipe = new Recipe(master_recipe_list.GetRecipe((*itr)->GetID()));
 									client->GetPlayer()->GetRecipeList()->AddRecipe(recipe);
 									database.SavePlayerRecipe(client->GetPlayer(), recipe->GetID());
-									client->Message(CHANNEL_COLOR_WHITE, "Recipe: \"%s\" put in recipe book.", recipe->GetName());
+									client->Message(CHANNEL_NARRATIVE, "Recipe: \"%s\" put in recipe book.", recipe->GetName());
 
 									if (packet && client->GetRecipeListSent()) {
 										packet->setArrayDataByName("id", recipe->GetID(), i);
@@ -1531,7 +1531,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 						}
 						else {
 							if (recipe_book)
-								client->Message(CHANNEL_COLOR_WHITE, "You have already learned all you can from this item.");
+								client->Message(CHANNEL_NARRATIVE, "You have already learned all you can from this item.");
 							safe_delete(recipe_book);
 						}
 						
@@ -1709,7 +1709,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 						 }
 		case COMMAND_TELL:{
 			if(sep && sep->arg[0] && sep->argplus[1]){
-				if(!zone_list.HandleGlobalChatMessage(client, sep->arg[0], CHANNEL_TELL, sep->argplus[1]))
+				if(!zone_list.HandleGlobalChatMessage(client, sep->arg[0], CHANNEL_PRIVATE_TELL, sep->argplus[1]))
 					client->Message(CHANNEL_COLOR_RED,"Unable to find client %s",sep->arg[0]);
 			}else
 				client->SimpleMessage(CHANNEL_COLOR_YELLOW,"Usage:  /tell {character_name} {message}");
@@ -1732,7 +1732,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 		case COMMAND_OOC:{
 			//For now ooc will be the global chat channel, eventually when we create more channels we will create a global chat channel
 			if(sep && sep->arg[0][0])
-				zone_list.HandleGlobalChatMessage(client, 0, CHANNEL_OOC, sep->argplus[0]);
+				zone_list.HandleGlobalChatMessage(client, 0, CHANNEL_OUT_OF_CHARACTER, sep->argplus[0]);
 			else
 				client->SimpleMessage(CHANNEL_COLOR_YELLOW,"Usage:  /ooc {message}");
 			break;
@@ -2321,7 +2321,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 					world.GetGroupManager()->GroupMessage(group_id, "%s has left the group.", client->GetPlayer()->GetName());
 				}
 
-				client->SimpleMessage(CHANNEL_COLOR_GROUP, "You have left the group");
+				client->SimpleMessage(CHANNEL_GROUP_CHAT, "You have left the group");
 			}
 
 			break;
@@ -2440,10 +2440,10 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 			if(send_kicked_message)
 				world.GetGroupManager()->GroupMessage(group_id, "%s was kicked from the group.", kicked->GetName());
 			else
-				client->Message(CHANNEL_COLOR_GROUP, "You kicked %s from the group", kicked->GetName());
+				client->Message(CHANNEL_GROUP_CHAT, "You kicked %s from the group", kicked->GetName());
 
 			if (kicked_client)
-				kicked_client->SimpleMessage(CHANNEL_COLOR_GROUP, "You were kicked from the group");
+				kicked_client->SimpleMessage(CHANNEL_GROUP_CHAT, "You were kicked from the group");
 				
 			break;
 		}
@@ -2476,13 +2476,13 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 				int8 result = world.GetGroupManager()->AcceptInvite(client->GetPlayer());
 
 				if (result == 0)
-					client->SimpleMessage(CHANNEL_GROUP, "You have joined the group.");
+					client->SimpleMessage(CHANNEL_GROUP_CHAT, "You have joined the group.");
 				else if (result == 1)
-					client->SimpleMessage(CHANNEL_GROUP, "You do not have a pending invite.");
+					client->SimpleMessage(CHANNEL_GROUP_CHAT, "You do not have a pending invite.");
 				else if (result == 2)
-					client->SimpleMessage(CHANNEL_GROUP, "Unable to join group - could not find leader.");
+					client->SimpleMessage(CHANNEL_GROUP_CHAT, "Unable to join group - could not find leader.");
 				else
-					client->SimpleMessage(CHANNEL_GROUP, "Unable to join group - unknown error.");
+					client->SimpleMessage(CHANNEL_GROUP_CHAT, "Unable to join group - unknown error.");
 			}
 			break;
 		}
@@ -3028,7 +3028,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 			}
 			if(type == 0){
 				if(incombat)
-					client->SimpleMessage(CHANNEL_COLOR_COMBAT, "You stop fighting.");
+					client->SimpleMessage(CHANNEL_GENERAL_COMBAT, "You stop fighting.");
 				player->InCombat(false);
 				player->InCombat(false, true);
 				player->SetRangeAttack(false);
@@ -3039,12 +3039,12 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 					if(incombat && player->GetRangeAttack()){
 						player->SetRangeAttack(false);
 						player->InCombat(false, true);
-						client->SimpleMessage(CHANNEL_COLOR_COMBAT, "You stop fighting.");
+						client->SimpleMessage(CHANNEL_GENERAL_COMBAT, "You stop fighting.");
 					}
 					else{
 						player->SetRangeAttack(true);
 						player->InCombat(true, true);
-						client->SimpleMessage(CHANNEL_COLOR_COMBAT, "You start fighting.");
+						client->SimpleMessage(CHANNEL_GENERAL_COMBAT, "You start fighting.");
 					}
 				}
 				else {
@@ -3052,7 +3052,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 					player->SetRangeAttack(false);
 					player->InCombat(true);
 					if(!incombat)
-						client->SimpleMessage(CHANNEL_COLOR_COMBAT, "You start fighting.");
+						client->SimpleMessage(CHANNEL_GENERAL_COMBAT, "You start fighting.");
 				}
 				/*else
 					client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You cannot attack that!");*/
@@ -3094,6 +3094,8 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 				int8 quantity = atoi(sep->arg[1]);
 				client->BuyItem(item_id, quantity);
 			}
+			else
+				Command_SendMerchantWindow(client, sep);
 			break;
 								  }
 		case COMMAND_MERCHANT_SELL:{
@@ -3195,9 +3197,33 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 				client->AcceptQuestReward(quest, item_id);
 				break;
 			}
-
+			bool collectedItems = false;
 			if (collection = player->GetPendingCollectionReward())
 				client->AcceptCollectionRewards(collection, selectable_item_id);
+			else if (client->GetPlayer()->HasPendingItemRewards()) {
+				vector<Item*> items = client->GetPlayer()->GetPendingItemRewards();
+				if (items.size() > 0) {
+					collectedItems = true;
+					for (int i = 0; i < items.size(); i++) {
+						client->GetPlayer()->AddItem(new Item(items[i]));
+					}
+					client->GetPlayer()->ClearPendingItemRewards();
+				}
+				map<int32, Item*> selectable_item = client->GetPlayer()->GetPendingSelectableItemReward(item_id);
+				if (selectable_item.size() > 0) {
+					collectedItems = true;
+					map<int32, Item*>::iterator itr;
+					for (itr = selectable_item.begin(); itr != selectable_item.end(); itr++) {
+						client->GetPlayer()->AddItem(new Item(itr->second));
+						client->GetPlayer()->ClearPendingSelectableItemRewards(itr->first);
+					}
+				}
+			}
+			if (collectedItems) {
+				EQ2Packet* outapp = client->GetPlayer()->SendInventoryUpdate(client->GetVersion());
+				if (outapp)
+					client->QueuePacket(outapp);
+			}
 			else
 				LogWrite(COMMAND__ERROR, 0, "Command", "Error in COMMAND_ACCEPT_REWARD. No pending quest or collection reward was found (unknown=%u).", unknown);
 			break;
@@ -4806,7 +4832,7 @@ void Commands::Command_Follow(Client* client, Seperator* sep)
 		client->GetPlayer()->changed = true;
 	}
 	else
-		client->Message(CHANNEL_COLOR_WHITE, "You must first select a group member to follow.");
+		client->Message(CHANNEL_NARRATIVE, "You must first select a group member to follow.");
 }
 
 /* 
@@ -4952,7 +4978,7 @@ void Commands::Command_Guild(Client* client, Seperator* sep)
 					if (target->IsPlayer())
 						guild->InvitePlayer(client, target->GetName());
 					else
-						client->Message(CHANNEL_COLOR_WHITE, "%s is not a player.", target->GetName());
+						client->Message(CHANNEL_NARRATIVE, "%s is not a player.", target->GetName());
 				}
 			}
 		}
@@ -4973,7 +4999,7 @@ void Commands::Command_Guild(Client* client, Seperator* sep)
 				Client* client_inviter = pgi->invited_by->GetZone()->GetClientBySpawn(pgi->invited_by);
 
 				if (client_inviter)
-					client_inviter->Message(CHANNEL_COLOR_WHITE, "%s has declined your invitation to join %s.", client->GetPlayer()->GetName(), pgi->guild->GetName());
+					client_inviter->Message(CHANNEL_NARRATIVE, "%s has declined your invitation to join %s.", client->GetPlayer()->GetName(), pgi->guild->GetName());
 			}
 			client->SetPendingGuildInvite(0);
 		}
@@ -4984,7 +5010,7 @@ void Commands::Command_Guild(Client* client, Seperator* sep)
 			if (!guild_list.GetGuild(guild_name))
 				world.CreateGuild(guild_name, client, client->GetPlayer()->GetGroupMemberInfo() ? client->GetPlayer()->GetGroupMemberInfo()->group_id : 0);
 			else
-				client->SimpleMessage(CHANNEL_COLOR_WHITE, "A guild with that name already exists.");
+				client->SimpleMessage(CHANNEL_NARRATIVE, "A guild with that name already exists.");
 		}
 		else if (strncmp(command, "search", length) == 0)
 			client->ShowGuildSearchWindow();
@@ -5082,7 +5108,7 @@ void Commands::Command_GuildSay(Client* client, Seperator* sep)
 			guild->HandleGuildSay(client, sep->argplus[0]);
 	}
 	else
-		client->SimpleMessage(CHANNEL_COLOR_WHITE, "You are not a member of a guild");
+		client->SimpleMessage(CHANNEL_NARRATIVE, "You are not a member of a guild");
 }
 
 /* 
@@ -5100,7 +5126,7 @@ void Commands::Command_OfficerSay(Client* client, Seperator* sep)
 			guild->HandleOfficerSay(client, sep->argplus[0]);
 	}
 	else
-		client->SimpleMessage(CHANNEL_COLOR_WHITE, "You are not a member of a guild");
+		client->SimpleMessage(CHANNEL_NARRATIVE, "You are not a member of a guild");
 }
 
 /* 
@@ -5745,12 +5771,12 @@ void Commands::Command_Languages(Client* client, Seperator* sep)
 	list<Language*>* languages = client->GetPlayer()->GetPlayerLanguages()->GetAllLanguages();
 	list<Language*>::iterator itr;
 	Language* language;
-	client->Message(CHANNEL_COLOR_WHITE, "You know the following languages:");
+	client->Message(CHANNEL_NARRATIVE, "You know the following languages:");
 
 	for(itr = languages->begin(); itr != languages->end(); itr++)
 	{
 		language = *itr;
-		client->Message(CHANNEL_COLOR_WHITE, "%s", language->GetName());
+		client->Message(CHANNEL_NARRATIVE, "%s", language->GetName());
 	}
 }
 
@@ -5776,7 +5802,7 @@ void Commands::Command_SetLanguage(Client* client, Seperator* sep)
 			{
 				database.SaveCharacterCurrentLang(0, client->GetCharacterID(), client);
 				client->SendLanguagesUpdate(0);
-				client->Message(CHANNEL_COLOR_WHITE, "You are now speaking %s", value);
+				client->Message(CHANNEL_NARRATIVE, "You are now speaking %s", value);
 			}
 			else
 			{
@@ -5785,10 +5811,10 @@ void Commands::Command_SetLanguage(Client* client, Seperator* sep)
 					Language* language = player->GetPlayerLanguages()->GetLanguageByName(value);
 					database.SaveCharacterCurrentLang(language->GetID(), client->GetCharacterID(), client);
 					client->SendLanguagesUpdate(language->GetID());
-					client->Message(CHANNEL_COLOR_WHITE, "You are now speaking %s", language->GetName());
+					client->Message(CHANNEL_NARRATIVE, "You are now speaking %s", language->GetName());
 				}
 				else
-					client->Message(CHANNEL_COLOR_WHITE, "You do not know how to speak %s", value);
+					client->Message(CHANNEL_NARRATIVE, "You do not know how to speak %s", value);
 			}
 		}
 		else
@@ -5801,12 +5827,12 @@ void Commands::Command_SetLanguage(Client* client, Seperator* sep)
 				Language* language = player->GetPlayerLanguages()->GetLanguage(id);
 				database.SaveCharacterCurrentLang(id, client->GetCharacterID(), client);
 				client->SendLanguagesUpdate(id);
-				client->Message(CHANNEL_COLOR_WHITE, "You are now speaking %s", language->GetName());
+				client->Message(CHANNEL_NARRATIVE, "You are now speaking %s", language->GetName());
 			}
 			else
 			{
 				Language* language = master_languages_list.GetLanguage(id);
-				client->Message(CHANNEL_COLOR_WHITE, "You do not know how to speak %s", language->GetName());
+				client->Message(CHANNEL_NARRATIVE, "You do not know how to speak %s", language->GetName());
 			}
 		}
 	}
@@ -5818,10 +5844,10 @@ void Commands::Command_SetLanguage(Client* client, Seperator* sep)
 		if(id > 0)
 		{
 			Language* language = player->GetPlayerLanguages()->GetLanguage(id);
-			client->Message(CHANNEL_COLOR_WHITE, "You are currently speaking %s ", language->GetName());
+			client->Message(CHANNEL_NARRATIVE, "You are currently speaking %s ", language->GetName());
 		}
 		else
-			client->Message(CHANNEL_COLOR_WHITE, "You are currently speaking Common");
+			client->Message(CHANNEL_NARRATIVE, "You are currently speaking Common");
 	}
 }
 
@@ -7624,11 +7650,11 @@ void Commands::Command_TitleList(Client* client)
 	Title* title;
 	int16 i = 0;
 
-	client->Message(CHANNEL_COLOR_WHITE, "Listing available titles:");
+	client->Message(CHANNEL_NARRATIVE, "Listing available titles:");
 	for(itr = titles->begin(); itr != titles->end(); itr++)
 	{
 		title = *itr;
-		client->Message(CHANNEL_COLOR_WHITE, "%i: type=[%s] title=[%s]", i, title->GetPrefix() ? "Prefix":"Suffix", title->GetName());
+		client->Message(CHANNEL_NARRATIVE, "%i: type=[%s] title=[%s]", i, title->GetPrefix() ? "Prefix":"Suffix", title->GetName());
 		i++;
 	}
 
@@ -7727,9 +7753,9 @@ void Commands::Command_Toggle_AutoConsume(Client* client, Seperator* sep)
 			player->SetCharSheetChanged(true);
 			client->QueuePacket(player->GetEquipmentList()->serialize(client->GetVersion(), player));
 			if (flag == 1)
-				client->Message(CHANNEL_COLOR_WHITE, "You decide to eat immediately whenever you become hungry.");
+				client->Message(CHANNEL_NARRATIVE, "You decide to eat immediately whenever you become hungry.");
 			else
-				client->Message(CHANNEL_COLOR_WHITE, "You decide to ignore the hunger.");
+				client->Message(CHANNEL_NARRATIVE, "You decide to ignore the hunger.");
 		}
 		else
 		{
@@ -7737,9 +7763,9 @@ void Commands::Command_Toggle_AutoConsume(Client* client, Seperator* sep)
 			player->SetCharSheetChanged(true);
 			client->QueuePacket(player->GetEquipmentList()->serialize(client->GetVersion(), player));
 			if (flag == 1)
-				client->Message(CHANNEL_COLOR_WHITE, "You decide to drink immediately whenever you become thirsty.");
+				client->Message(CHANNEL_NARRATIVE, "You decide to drink immediately whenever you become thirsty.");
 			else
-				client->Message(CHANNEL_COLOR_WHITE, "You decide to ignore the thirst.");
+				client->Message(CHANNEL_NARRATIVE, "You decide to ignore the thirst.");
 		}
 	}
 }
@@ -8323,17 +8349,17 @@ void Commands::Command_JoinChannel(Client * client, Seperator *sep) {
 	}
 
 	if (chat.IsInChannel(client, channel_name)) {
-		client->Message(CHANNEL_COLOR_WHITE, "You are already in '%s'.", channel_name);
+		client->Message(CHANNEL_NARRATIVE, "You are already in '%s'.", channel_name);
 		return;
 	}
 
 	if (chat.HasPassword(channel_name)) {
 		if (password == NULL) {
-			client->Message(CHANNEL_COLOR_WHITE, "Unable to join '%s': That channel is password protected.", channel_name);
+			client->Message(CHANNEL_NARRATIVE, "Unable to join '%s': That channel is password protected.", channel_name);
 			return;
 		}
 		if (!chat.PasswordMatches(channel_name, password)) {
-			client->Message(CHANNEL_COLOR_WHITE, "Unable to join '%s': The password is not correc.t", channel_name);
+			client->Message(CHANNEL_NARRATIVE, "Unable to join '%s': The password is not correc.t", channel_name);
 			return;
 		}
 	}
@@ -8425,9 +8451,8 @@ void Commands::Command_Test(Client* client, EQ2_16BitString* command_parms) {
 			}
 		}
 		else if (atoi(sep->arg[0]) == 5) {
-			client->GetPlayer()->GetTarget();
-			int16 offset = atoi(sep->arg[2]);
-			int32 value1 = atol(sep->arg[4]);
+			int16 offset = atoi(sep->arg[0]);
+			int32 value1 = atol(sep->arg[1]);
 			EQ2Packet* outapp = client->GetPlayer()->GetPlayerInfo()->serialize(client->GetVersion(), offset, value1);
 			client->QueuePacket(outapp);
 		}
@@ -8513,67 +8538,39 @@ void Commands::Command_Test(Client* client, EQ2_16BitString* command_parms) {
 					value2 = atol(sep->arg[7]);
 				}
 				sprintf(spawn->appearance.name, "Offset %i to %i", offset, offset2);
+				spawn->AddPrimaryEntityCommand("attack", 10000, "attack","", 0, 0);
 				EQ2Packet* ret = spawn->spawn_serialize(client->GetPlayer(), client->GetVersion(), offset, value1, offset2, offset3, offset4, value2);
 				DumpPacket(ret);
 				client->QueuePacket(ret);
 			}
 		}
 		else if (atoi(sep->arg[0]) == 9) {
-			Item* item = master_item_list.GetItem(70007);
-			EQ2Packet* app = item->serialize(client->GetVersion(), true, client->GetPlayer());
-			if (sep->IsSet(2)) {
-				int8 offset = atoi(sep->arg[1]);
-				uchar* ptr2 = app->pBuffer;
-				ptr2 += offset;
-				if (sep->IsNumber(2)) {
-					int32 value1 = atol(sep->arg[2]);
-					if (value1 > 0xFFFF)
-						memcpy(ptr2, (uchar*)&value1, 4);
-					else if (value1 > 0xFF)
-						memcpy(ptr2, (uchar*)&value1, 2);
-					else
-						memcpy(ptr2, (uchar*)&value1, 1);
-				}
-				else {
-					int8 len = strlen(sep->arg[2]);
-					memcpy(ptr2, (uchar*)&len, 1);
-					ptr2 += 1;
-					memcpy(ptr2, sep->arg[2], len);
+			Spawn* spawn = client->GetPlayer()->GetTarget();
+			if (spawn) {	
+				if (sep->IsSet(3)) {
+					int32 amount = (int32)atoi(sep->arg[3]);
+					spawn->SetActivityStatus(amount);
 				}
-			}
-			if (sep->IsSet(4)) {
-				int8 offset = atoi(sep->arg[3]);
-				uchar* ptr2 = app->pBuffer;
-				ptr2 += offset;
-				if (sep->IsNumber(4)) {
-					int32 value1 = atol(sep->arg[4]);
-					if (value1 > 0xFFFF)
-						memcpy(ptr2, (uchar*)&value1, 4);
-					else if (value1 > 0xFF)
-						memcpy(ptr2, (uchar*)&value1, 2);
-					else
-						memcpy(ptr2, (uchar*)&value1, 1);
+				if (sep->IsSet(2)) {
+					int8 amount = (int8)atoi(sep->arg[2]);
+					spawn->SetLockedNoLoot(amount);
 				}
-				else {
-					int8 len = strlen(sep->arg[4]);
-					memcpy(ptr2, (uchar*)&len, 1);
-					ptr2 += 1;
-					memcpy(ptr2, sep->arg[4], len);
+				if (sep->IsSet(1)) {
+					sint8 amount = (sint8)atoi(sep->arg[1]);
+					spawn->AddIconValue(amount);
 				}
 			}
-			DumpPacket(app);
-			client->QueuePacket(app);
 		}
 		else if (atoi(sep->arg[0]) == 10) {
 			PacketStruct* packet2 = configReader.getStruct("WS_QuestJournalUpdate", client->GetVersion());
 			if (packet2) {
 				packet2->setArrayLengthByName("num_quests", 1);
 				packet2->setArrayDataByName("active", 1);
-				packet2->setArrayDataByName("name", "Archetype Selection");
+				packet2->setArrayDataByName("name", "Tasks aboard the Far Journey");
 				packet2->setArrayDataByName("quest_type", "Hallmark");
 				packet2->setArrayDataByName("quest_zone", "Hallmark");
 				packet2->setArrayDataByName("journal_updated", 1);
-				packet2->setArrayDataByName("quest_id", 5);
+				packet2->setArrayDataByName("quest_id", 524);
 				packet2->setArrayDataByName("day", 19);
 				packet2->setArrayDataByName("month", 6);
 				packet2->setArrayDataByName("year", 20);
@@ -8581,7 +8578,7 @@ void Commands::Command_Test(Client* client, EQ2_16BitString* command_parms) {
 				packet2->setArrayDataByName("encounter_level", 4);
 				packet2->setArrayDataByName("difficulty", 3);
 				packet2->setArrayDataByName("visible", 1);
-				packet2->setDataByName("visible_quest_id", 5);
+				packet2->setDataByName("visible_quest_id", 524);
 				packet2->setDataByName("player_crc", 2900677088);
 				packet2->setDataByName("player_name", "LethalEncounter");
 				EQ2Packet* app = packet2->serialize();
@@ -9457,7 +9454,7 @@ void Commands::Command_LeaveChannel(Client *client, Seperator *sep) {
 	channel_name = sep->arg[0];
 
 	if (!chat.IsInChannel(client, channel_name))
-		client->Message(CHANNEL_COLOR_WHITE, "Unable to leave '%s': You are not in the channel.", channel_name);
+		client->Message(CHANNEL_NARRATIVE, "Unable to leave '%s': You are not in the channel.", channel_name);
 	else if (!chat.LeaveChannel(client, channel_name))
 		client->SimpleMessage(CHANNEL_COLOR_YELLOW, "There was an internal error preventing you from leaving that channel MUAHAHA");
 }
@@ -9795,9 +9792,9 @@ void Commands::Command_ConsumeFood(Client* client, Seperator* sep) {
 			if(item->GetItemScript() && lua_interface){
 				lua_interface->RunItemScript(item->GetItemScript(), "cast", item, client->GetPlayer());
 				if (slot == 22)
-					client->Message(CHANNEL_COLOR_WHITE, "You eat a %s.", item->name.c_str());
+					client->Message(CHANNEL_NARRATIVE, "You eat a %s.", item->name.c_str());
 				else
-					client->Message(CHANNEL_COLOR_WHITE, "You drink a %s.", item->name.c_str());
+					client->Message(CHANNEL_NARRATIVE, "You drink a %s.", item->name.c_str());
 			}
 		}
 

+ 3 - 43
EQ2/source/WorldServer/Commands/Commands.h

@@ -37,52 +37,12 @@ extern map<int16,OpcodeManager*>EQOpcodeManager;
 #define CHANNEL_COLOR_RED				3
 #define CHANNEL_COLOR_CHAT_RELATIONSHIP	4
 #define CHANNEL_COLOR_YELLOW			5
-#define CHANNEL_COLOR_REVIVE			12
-#define CHANNEL_COLOR_MAIL				12
-#define CHANNEL_COLOR_WHITE				12
-#define CHANNEL_COLOR_GROUP				15
-#define CHANNEL_COLOR_GUILD				18
-#define CHANNEL_COLOR_GUILD_MOTD		20
-#define CHANNEL_COLOR_GUILD_EVENT		22
-#define CHANNEL_COLOR_EXP				36
-#define CHANNEL_COLOR_SKILL				39
-#define CHANNEL_COLOR_FACTION			40
-#define CHANNEL_COLOR_SPELL_EFFECT		42
-#define CHANNEL_COLOR_SPELL				47
-#define CHANNEL_COLOR_SPELL_INTERRUPT	52
-#define CHANNEL_COLOR_SPELL_FADE		51
-#define CHANNEL_COLOR_COMBAT			60
-#define CHANNEL_COLOR_LOOT				81
 #define CHANNEL_COLOR_NEW_LOOT			84
 #define CHANNEL_COLOR_NEWEST_LOOT		89
-#define CHANNEL_COLOR_MERCHANT			88
-#define CHANNEL_COLOR_GROUP_INVITE		89
-#define CHANNEL_COLOR_GUILD_MSGS		94
-#define CHANNEL_COLOR_HARVEST			96
 
 #define UPDATE_COLOR_WHITE				254  // For UpdateText
 
-#define CHANNEL_SAY			8
-#define CHANNEL_SHOUT		9
-#define CHANNEL_EMOTE		10
-#define CHANNEL_GROUP		15
-#define CHANNEL_RAID		16
-#define CHANNEL_GUILD		18
-#define CHANNEL_OFFICER		19
-#define CHANNEL_SAYTARGET	25 // you say to xxx
-#define CHANNEL_TELL		28 // you tell xxx
-#define CHANNEL_OOC			32
-//#define CHANNEL_AUCTION		30
-
-#define CLASSIC_CLIENT_CHANNEL_GROUP		14
-#define CLASSIC_CLIENT_CHANNEL_RAID			15
-#define CLASSIC_CLIENT_CHANNEL_GUILD		17
-#define CLASSIC_CLIENT_CHANNEL_SAYTARGET	21 // you say to xxx
-#define CLASSIC_CLIENT_CHANNEL_TELL			22 // you tell xxx
-#define CLASSIC_CLIENT_CHANNEL_OOC			25
-#define CLASSIC_CLIENT_CHANNEL_AUCTION		26
-#define CLASSIC_CLIENT_CHANNEL_BROADCAST	61
-
+#define CHANNEL_ALL_TEXT					0
 #define CHANNEL_GAME_TEXT					1
 #define CHANNEL_DEFAULT						2
 #define CHANNEL_ERROR						3
@@ -94,7 +54,7 @@ extern map<int16,OpcodeManager*>EQOpcodeManager;
 #define CHANNEL_SHOUT						9
 #define CHANNEL_EMOTE						10
 #define CHANNEL_YELL						11
-#define CHANNEL_NARRATIVE					12
+#define CHANNEL_NARRATIVE					12 //white
 #define CHANNEL_NONPLAYER_SAY				13
 #define CHANNEL_GROUP_CHAT					14
 #define CHANNEL_GROUP_SAY					15 // Use this for group chat
@@ -116,7 +76,7 @@ extern map<int16,OpcodeManager*>EQOpcodeManager;
 #define CHANNEL_CHAT_CHANNEL_TEXT			31
 #define CHANNEL_OUT_OF_CHARACTER			32
 #define CHANNEL_AUCTION						33
-// 34 is nothing, message with 34 as type will not show on client
+#define CHANNEL_CUSTOM_CHANNEL				34 // 34 is nothing, message with 34 as type will not show on client
 #define CHANNEL_CHARACTER_TEXT				35
 #define CHANNEL_REWARD						36
 #define CHANNEL_DEATH						37

+ 2 - 2
EQ2/source/WorldServer/Entity.cpp

@@ -1317,12 +1317,12 @@ int32 Entity::CheckWards(Entity* attacker, int32 damage, int8 damage_type) {
 			if (this->IsPlayer())
 			{
 				Client* client = GetZone()->GetClientBySpawn(this);
-				client->Message(CHANNEL_COLOR_COMBAT, "%s intercepted some of the damage intended for you!", spell->caster->GetName());
+				client->Message(CHANNEL_COMBAT, "%s intercepted some of the damage intended for you!", spell->caster->GetName());
 			}
 			if (spell->caster && spell->caster->IsPlayer())
 			{
 				Client* client = GetZone()->GetClientBySpawn(spell->caster);
-				client->Message(CHANNEL_COLOR_COMBAT, "YOU intercept some of the damage intended for %s!", this->GetName());
+				client->Message(CHANNEL_COMBAT, "YOU intercept some of the damage intended for %s!", this->GetName());
 			}
 
 			if (attacker && spell->caster)

+ 6 - 6
EQ2/source/WorldServer/GroundSpawn.cpp

@@ -381,7 +381,7 @@ void GroundSpawn::ProcessHarvest(Client* client) {
 						item->details.count = reward_total;
 
 						// chat box update for normal item (todo: verify output text)
-						client->Message(CHANNEL_COLOR_HARVEST, "You %s %i \\aITEM %u %u:%s\\/a from the %s.", GetHarvestMessageName(true).c_str(), item->details.count, item->details.item_id, item->details.unique_id, item->name.c_str(), GetName());
+						client->Message(CHANNEL_HARVESTING, "You %s %i %s from the %s.", GetHarvestMessageName(true).c_str(), item->details.count, item->CreateItemLink(GetVersion(), true).c_str(), GetName());
 						// add Normal item to player inventory
 						client->AddItem(item);
 						//Check if the player has a harvesting quest for this
@@ -410,12 +410,12 @@ void GroundSpawn::ProcessHarvest(Client* client) {
 
 								// send Rare harvest message to client
 								sprintf(tmp, "\\#FFFF6ERare item found!\12%s: \\#C8FFFF%i %s", GetHarvestMessageName().c_str(), item_rare->details.count, item_rare->name.c_str());
-								client->Message(CHANNEL_COLOR_HARVEST, "You have found a rare item!");
+								client->Message(CHANNEL_HARVESTING, "You have found a rare item!");
 								client->SendPopupMessage(11, tmp, "ui_harvested_rare", 2.25, 0xFF, 0xFF, 0xFF);
 								client->GetPlayer()->UpdatePlayerStatistic(STAT_PLAYER_RARES_HARVESTED, item_rare->details.count);
 
 								// chat box update for rare item (todo: verify output text)
-								client->Message(CHANNEL_COLOR_HARVEST, "You %s %i \\aITEM %u %u:%s\\/a from the %s.", GetHarvestMessageName(true).c_str(), item_rare->details.count, item_rare->details.item_id, item_rare->details.unique_id, item_rare->name.c_str(), GetName());
+								client->Message(CHANNEL_HARVESTING, "You %s %i %s from the %s.", GetHarvestMessageName(true).c_str(), item_rare->details.count, item->CreateItemLink(GetVersion(), true).c_str(), GetName());
 								// add Rare item to player inventory
 								client->AddItem(item_rare);
 								//Check if the player has a harvesting quest for this
@@ -428,7 +428,7 @@ void GroundSpawn::ProcessHarvest(Client* client) {
 
 							// send Rare harvest message to client
 							sprintf(tmp, "\\#FFFF6ERare item found!\12%s: \\#C8FFFF%i %s", GetHarvestMessageName().c_str(), item->details.count, item->name.c_str());
-							client->Message(CHANNEL_COLOR_HARVEST, "You have found a rare item!");
+							client->Message(CHANNEL_HARVESTING, "You have found a rare item!");
 							client->SendPopupMessage(11, tmp, "ui_harvested_rare", 2.25, 0xFF, 0xFF, 0xFF);
 							client->GetPlayer()->UpdatePlayerStatistic(STAT_PLAYER_RARES_HARVESTED, item->details.count);
 						}
@@ -452,13 +452,13 @@ void GroundSpawn::ProcessHarvest(Client* client) {
 				else {
 					// if no item harvested
 					LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "No item_harvested");
-					client->Message(CHANNEL_COLOR_HARVEST, "You failed to %s anything from %s.", GetHarvestMessageName(true, true).c_str(), GetName());
+					client->Message(CHANNEL_HARVESTING, "You failed to %s anything from %s.", GetHarvestMessageName(true, true).c_str(), GetName());
 				}
 			}
 			else {
 				// if no harvest type
 				LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "No harvest_type");
-				client->Message(CHANNEL_COLOR_HARVEST, "You failed to %s anything from %s.", GetHarvestMessageName(true, true).c_str(), GetName());
+				client->Message(CHANNEL_HARVESTING, "You failed to %s anything from %s.", GetHarvestMessageName(true, true).c_str(), GetName());
 			}
 		}
 	} // cycle through num_attempts_per_harvest

+ 27 - 27
EQ2/source/WorldServer/Guilds/Guild.cpp

@@ -689,9 +689,9 @@ bool Guild::AddNewGuildMember(Client *client, const char *invited_by, int8 rank)
 		player->SetSubTitle(subtitle.c_str());
 
 		if (invited_by)
-			client->SimpleMessage(CHANNEL_COLOR_WHITE, "You accept the invite into the guild.");
+			client->SimpleMessage(CHANNEL_NARRATIVE, "You accept the invite into the guild.");
 		else {
-			client->SimpleMessage(CHANNEL_COLOR_WHITE, "You have formed the guild.");
+			client->SimpleMessage(CHANNEL_NARRATIVE, "You have formed the guild.");
 			LogWrite(GUILD__DEBUG, 0, "Guilds", "New Guild formed: %s", GetName());
 		}
 
@@ -911,7 +911,7 @@ bool Guild::InvitePlayer(Client *client, const char *name, bool send_packet) {
 	assert(name);
 
 	if (!(client_invite = zone_list.GetClientByCharName(string(name)))) {
-		client->Message(CHANNEL_COLOR_WHITE, "%s couldn't be found.", name);
+		client->Message(CHANNEL_NARRATIVE, "%s couldn't be found.", name);
 		LogWrite(GUILD__WARNING, 0, "Guilds", "Attempted to invite %s to guild %s: Player Not Found", name, GetName());
 		return false;
 	}
@@ -919,13 +919,13 @@ bool Guild::InvitePlayer(Client *client, const char *name, bool send_packet) {
 	player_invite = client_invite->GetPlayer();
 
 	if (player_invite->GetGuild()) {
-		client->Message(CHANNEL_COLOR_WHITE, "%s is already in a guild.", player_invite->GetName());
+		client->Message(CHANNEL_NARRATIVE, "%s is already in a guild.", player_invite->GetName());
 		LogWrite(GUILD__WARNING, 0, "Guilds", "Attempted to invite %s to guild %s: Already in a guild", player_invite->GetName(), GetName());
 		return false;
 	}
 
 	if (client_invite->GetPendingGuildInvite()->guild) {
-		client->Message(CHANNEL_COLOR_WHITE, "%s is already considering joining a guild.", player_invite->GetName());
+		client->Message(CHANNEL_NARRATIVE, "%s is already considering joining a guild.", player_invite->GetName());
 		LogWrite(GUILD__WARNING, 0, "Guilds", "Attempted to invite %s to guild %s: Pending Invite elsewhere", player_invite->GetName(), GetName());
 		return false;
 	}
@@ -939,7 +939,7 @@ bool Guild::InvitePlayer(Client *client, const char *name, bool send_packet) {
 	packet->setMediumStringByName("guild_name", GetName());
 	client_invite->QueuePacket(packet->serialize());
 
-	client->Message(CHANNEL_COLOR_WHITE, "You have invited %s to join %s.", player_invite->GetName(), GetName());
+	client->Message(CHANNEL_NARRATIVE, "You have invited %s to join %s.", player_invite->GetName(), GetName());
 	LogWrite(GUILD__DEBUG, 0, "Guilds", "%s invited %s to guild %s", client->GetPlayer()->GetName(), player_invite->GetName(), GetName());
 
 	client_invite->SetPendingGuildInvite(this, client->GetPlayer());
@@ -972,7 +972,7 @@ bool Guild::AddPointsToAll(Client *client, float points, const char *comment, bo
 		AddPointHistory(gm, Timer::GetUnixTimeStamp(), client->GetPlayer()->GetName(), points, comment);
 		if ((client_to = zone_list.GetClientByCharID(gm->character_id)))
 		{
-			client_to->Message(CHANNEL_COLOR_GUILD_MSGS, "%s increased your guild member points by %.1f.", client->GetPlayer()->GetName(), points);
+			client_to->Message(CHANNEL_GUILD_CHAT, "%s increased your guild member points by %.1f.", client->GetPlayer()->GetName(), points);
 			LogWrite(GUILD__DEBUG, 0, "Guilds", "Guild: %s", GetName());
 			LogWrite(GUILD__DEBUG, 0, "Guilds", "\tAwarded By: %s +%.1f pts to Player: %s", client->GetPlayer()->GetName(), points, gm->name);
 		}
@@ -982,7 +982,7 @@ bool Guild::AddPointsToAll(Client *client, float points, const char *comment, bo
 	}
 	mMembers.releasereadlock(__FUNCTION__, __LINE__);
 
-	client->Message(CHANNEL_COLOR_GUILD_MSGS, "Points modified for %u guild members.", character_ids.size());
+	client->Message(CHANNEL_GUILD_CHAT, "Points modified for %u guild members.", character_ids.size());
 	if (send_packet) {
 		LogWrite(GUILD__DEBUG, 0, "Guilds", "%s modified points for %u members. Reason: %s", client->GetPlayer()->GetName(), character_ids.size(), points, comment);
 		SendGuildModification(points, &character_ids);
@@ -1020,7 +1020,7 @@ bool Guild::AddPointsToAllOnline(Client *client, float points, const char *comme
 		character_ids.push_back(gm->character_id);
 
 		AddPointHistory(gm, Timer::GetUnixTimeStamp(), client->GetPlayer()->GetName(), points, comment);
-		client_to->Message(CHANNEL_COLOR_GUILD_MSGS, "%s increased your guild member points by %.1f.", client->GetPlayer()->GetName(), points);
+		client_to->Message(CHANNEL_GUILD_CHAT, "%s increased your guild member points by %.1f.", client->GetPlayer()->GetName(), points);
 		LogWrite(GUILD__DEBUG, 0, "Guilds", "Guild: %s", GetName());
 		LogWrite(GUILD__DEBUG, 0, "Guilds", "\tAwarded By: %s +%.1f pts to Player: %s", client->GetPlayer()->GetName(), points, gm->name);
 
@@ -1030,7 +1030,7 @@ bool Guild::AddPointsToAllOnline(Client *client, float points, const char *comme
 	}
 	mMembers.releasereadlock(__FUNCTION__, __LINE__);
 
-	client->Message(CHANNEL_COLOR_GUILD_MSGS, "Points modified for %u guild members.", character_ids.size());
+	client->Message(CHANNEL_GUILD_CHAT, "Points modified for %u guild members.", character_ids.size());
 	if (send_packet) {
 		LogWrite(GUILD__DEBUG, 0, "Guilds", "%s modified points for %u members. Reason: %s", client->GetPlayer()->GetName(), character_ids.size(), points, comment);
 		SendGuildModification(points, &character_ids);
@@ -1052,7 +1052,7 @@ bool Guild::AddPointsToGroup(Client *client, float points, const char *comment,
 	assert(client);
 
 	if (!client->GetPlayer()->GetGroupMemberInfo()) {
-		client->SimpleMessage(CHANNEL_COLOR_GUILD_MSGS, "Cannot assign points because you aren't in a group.");
+		client->SimpleMessage(CHANNEL_GUILD_CHAT, "Cannot assign points because you aren't in a group.");
 		LogWrite(GUILD__DEBUG, 0, "Guilds", "%s tried to assign points for group and failed: Not in a group", client->GetPlayer()->GetName());
 		return false;
 	}
@@ -1085,7 +1085,7 @@ bool Guild::AddPointsToGroup(Client *client, float points, const char *comment,
 			character_ids.push_back(gm->character_id);
 
 			AddPointHistory(gm, Timer::GetUnixTimeStamp(), client->GetPlayer()->GetName(), points, comment);
-			gmi->client->Message(CHANNEL_COLOR_GUILD_MSGS, "%s increased your guild member points by %.1f.", client->GetPlayer()->GetName(), points);
+			gmi->client->Message(CHANNEL_GUILD_CHAT, "%s increased your guild member points by %.1f.", client->GetPlayer()->GetName(), points);
 			LogWrite(GUILD__DEBUG, 0, "Guilds", "Guild: %s", GetName());
 			LogWrite(GUILD__DEBUG, 0, "Guilds", "\tAwarded By: %s +%.1f pts to Player: %s", client->GetPlayer()->GetName(), points, gm->name);
 
@@ -1099,7 +1099,7 @@ bool Guild::AddPointsToGroup(Client *client, float points, const char *comment,
 	world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
 	mMembers.releasereadlock(__FUNCTION__, __LINE__);
 
-	client->Message(CHANNEL_COLOR_GUILD_MSGS, "Points modified for %u guild members.", character_ids.size());
+	client->Message(CHANNEL_GUILD_CHAT, "Points modified for %u guild members.", character_ids.size());
 	if (send_packet) {
 		LogWrite(GUILD__DEBUG, 0, "Guilds", "%s modified points for %u members. Reason: %s", client->GetPlayer()->GetName(), character_ids.size(), points, comment);
 		SendGuildModification(points, &character_ids);
@@ -1114,7 +1114,7 @@ bool Guild::AddPointsToRaid(Client *client, float points, const char *comment, b
 
 	assert(client);	
 	LogWrite(MISC__TODO, 1, "TODO", "Implement Raiding\n%s, %s, %i", __FILE__, __FUNCTION__, __LINE__);
-	client->SimpleMessage(CHANNEL_COLOR_GUILD_MSGS, "Cannot assign points because you aren't in a raid.");
+	client->SimpleMessage(CHANNEL_GUILD_CHAT, "Cannot assign points because you aren't in a raid.");
 	return false;
 }
 
@@ -1134,7 +1134,7 @@ bool Guild::AddPointsToGuildMember(Client *client, float points, const char *nam
 	if (!permissions.Get(gm->rank)->Get(GUILD_PERMISSIONS_RECEIVE_POINTS)) {
 		mMembers.releasereadlock(__FUNCTION__, __LINE__);
 		LogWrite(GUILD__DEBUG, 0, "Guilds", "PlayerID: %i not allowed to receive points! Skipping...", gm->character_id);
-		client->Message(CHANNEL_COLOR_GUILD_MSGS, "%s does not have permission to receive guild points.", gm->name);
+		client->Message(CHANNEL_GUILD_CHAT, "%s does not have permission to receive guild points.", gm->name);
 		return false;
 	}
 
@@ -1142,13 +1142,13 @@ bool Guild::AddPointsToGuildMember(Client *client, float points, const char *nam
 	character_ids.push_back(gm->character_id);
 
 	if ((client_to = zone_list.GetClientByCharID(gm->character_id))) {
-		client_to->Message(CHANNEL_COLOR_GUILD_MSGS, "%s increased your guild member points by %.1f.", client->GetPlayer()->GetName(), points);
+		client_to->Message(CHANNEL_GUILD_CHAT, "%s increased your guild member points by %.1f.", client->GetPlayer()->GetName(), points);
 		LogWrite(GUILD__DEBUG, 0, "Guilds", "Guild: %s", GetName());
 		LogWrite(GUILD__DEBUG, 0, "Guilds", "\tAwarded By: %s +%.1f pts to Player: %s", client->GetPlayer()->GetName(), points, gm->name);
 	}
 
 	LogWrite(GUILD__DEBUG, 0, "Guilds", "%s modified points for 1 guild member. Reason: %s", client->GetPlayer()->GetName(), comment);
-	client->SimpleMessage(CHANNEL_COLOR_GUILD_MSGS, "Points modified for 1 guild member.");
+	client->SimpleMessage(CHANNEL_GUILD_CHAT, "Points modified for 1 guild member.");
 	AddPointHistory(gm, Timer::GetUnixTimeStamp(), client->GetPlayer()->GetName(), points, comment);
 
 	if (send_packet) {
@@ -1271,12 +1271,12 @@ bool Guild::ChangeMemberFlag(Client *client, int8 member_flag, int8 value, bool
 		case GUILD_MEMBER_FLAGS_NOTIFY_LOGINS: {
 			if (value > 0 && !(gm->member_flags & GUILD_MEMBER_FLAGS_NOTIFY_LOGINS)) {
 				gm->member_flags += GUILD_MEMBER_FLAGS_NOTIFY_LOGINS;
-				client->SimpleMessage(CHANNEL_COLOR_GUILD_MSGS, "Guild online notifications are now enabled.");
+				client->SimpleMessage(CHANNEL_GUILD_CHAT, "Guild online notifications are now enabled.");
 				ret = true;
 			}
 			else if (value == 0 && gm->member_flags & GUILD_MEMBER_FLAGS_NOTIFY_LOGINS) {
 				gm->member_flags -= GUILD_MEMBER_FLAGS_NOTIFY_LOGINS;
-				client->SimpleMessage(CHANNEL_COLOR_GUILD_MSGS, "Guild online notifications are now disabled.");
+				client->SimpleMessage(CHANNEL_GUILD_CHAT, "Guild online notifications are now disabled.");
 				ret = true;
 			}
 			break;
@@ -1284,12 +1284,12 @@ bool Guild::ChangeMemberFlag(Client *client, int8 member_flag, int8 value, bool
 		case GUILD_MEMBER_FLAGS_DONT_GENERATE_EVENTS: {
 			if (value > 1 && !(gm->member_flags & GUILD_MEMBER_FLAGS_DONT_GENERATE_EVENTS)) {
 				gm->member_flags += GUILD_MEMBER_FLAGS_DONT_GENERATE_EVENTS;
-				client->SimpleMessage(CHANNEL_COLOR_GUILD_MSGS, "Guild events are now disabled for this character.");
+				client->SimpleMessage(CHANNEL_GUILD_CHAT, "Guild events are now disabled for this character.");
 				ret = true;
 			}
 			else if (value == 0 && gm->member_flags & GUILD_MEMBER_FLAGS_DONT_GENERATE_EVENTS) {
 				gm->member_flags -= GUILD_MEMBER_FLAGS_DONT_GENERATE_EVENTS;
-				client->SimpleMessage(CHANNEL_COLOR_GUILD_MSGS, "Guild events are now enabled for this character.");
+				client->SimpleMessage(CHANNEL_GUILD_CHAT, "Guild events are now enabled for this character.");
 				ret = true;
 			}
 			break;
@@ -1499,7 +1499,7 @@ int8 Guild::GetRecruitingLookingForPacketValue() {
 void Guild::SendGuildMOTD(Client* client) {
 
 	if (client && strlen(motd) > 0)
-		client->Message(CHANNEL_COLOR_GUILD_MOTD, "Guild MOTD: %s", motd);
+		client->Message(CHANNEL_GUILD_MOTD, "Guild MOTD: %s", motd);
 
 	LogWrite(GUILD__DEBUG, 1, "Guilds", "Sent guild MOTD.\n'%s'", motd);
 }
@@ -2201,7 +2201,7 @@ void Guild::HandleGuildSay(Client* sender, const char* message) {
 		return;
 
 	if (!permissions.Get(gm->rank)->Get(GUILD_PERMISSIONS_SPEAK_IN_GUILD_CHAT)) {
-		sender->SimpleMessage(CHANNEL_COLOR_WHITE, "You do not have permission to speak in guild chat.");
+		sender->SimpleMessage(CHANNEL_NARRATIVE, "You do not have permission to speak in guild chat.");
 		return;
 	}
 
@@ -2211,7 +2211,7 @@ void Guild::HandleGuildSay(Client* sender, const char* message) {
 			continue;
 			
 		if (permissions.Get(itr->second->rank)->Get(GUILD_PERMISSIONS_SEE_GUILD_CHAT))
-			client->GetCurrentZone()->HandleChatMessage(client, sender->GetPlayer(), client->GetPlayer()->GetName(), CHANNEL_GUILD, message, 0, 0, false);
+			client->GetCurrentZone()->HandleChatMessage(client, sender->GetPlayer(), client->GetPlayer()->GetName(), CHANNEL_GUILD_SAY, message, 0, 0, false);
 	}
 	mMembers.releasereadlock(__FUNCTION__, __LINE__);
 	LogWrite(GUILD__DEBUG, 0, "Guilds", "Guild Say");
@@ -2230,7 +2230,7 @@ void Guild::HandleOfficerSay(Client* sender, const char* message) {
 		return;
 
 	if (!permissions.Get(gm->rank)->Get(GUILD_PERMISSIONS_SPEAK_IN_OFFICER_CHAT)) {
-		sender->SimpleMessage(CHANNEL_COLOR_WHITE, "You do not have permission to speak in officer chat.");
+		sender->SimpleMessage(CHANNEL_NARRATIVE, "You do not have permission to speak in officer chat.");
 		return;
 	}
 				
@@ -2240,7 +2240,7 @@ void Guild::HandleOfficerSay(Client* sender, const char* message) {
 			continue;
 
 		if (permissions.Get(itr->second->rank)->Get(GUILD_PERMISSIONS_SEE_OFFICER_CHAT))
-			client->GetCurrentZone()->HandleChatMessage(client, sender->GetPlayer(), client->GetPlayer()->GetName(), CHANNEL_OFFICER, message, 0, 0, false);
+			client->GetCurrentZone()->HandleChatMessage(client, sender->GetPlayer(), client->GetPlayer()->GetName(), CHANNEL_OFFICER_SAY, message, 0, 0, false);
 	}
 	mMembers.releasereadlock(__FUNCTION__, __LINE__);
 	LogWrite(GUILD__DEBUG, 0, "Guilds", "Officer Say");
@@ -2263,7 +2263,7 @@ void Guild::SendMessageToGuild(int8 event_type, const char* message, ...) {
 			continue;
 
 		if (event_filters.Get(itr->second->rank)->Get(GUILD_EVENT_FILTER_CATEGORY_BROADCAST))
-			client->SimpleMessage(CHANNEL_COLOR_GUILD_EVENT, buffer);
+			client->SimpleMessage(CHANNEL_GUILD_EVENT, buffer);
 	}
 	mMembers.releasereadlock(__FUNCTION__, __LINE__);
 	LogWrite(GUILD__DEBUG, 0, "Guilds", "Sent message to entire guild.");

+ 17 - 3
EQ2/source/WorldServer/Items/Items.cpp

@@ -763,6 +763,8 @@ Item::Item(Item* in_item){
 	save_needed = true;
 	SetItem(in_item);
 	details.unique_id = master_item_list.NextUniqueID();
+	if (IsBag())
+		details.bag_id = details.unique_id;
 	generic_info.condition = 100;
 	spell_id = in_item->spell_id;
 	spell_tier = in_item->spell_tier;
@@ -1499,6 +1501,8 @@ void Item::serialize(PacketStruct* packet, bool show_name, Player* player, int16
 	}
 	if(show_name)
 		packet->setSubstructSubstructDataByName("header", "info_header", "show_name", show_name);
+	if (packet_type == 20)
+		cout << "";
 	if(packet_type == 0)
 		packet->setSubstructSubstructDataByName("header", "info_header", "packettype", GetItemPacketType(packet->GetVersion()));
 	else
@@ -3423,8 +3427,11 @@ bool EquipmentItemList::AddItem(int8 slot, Item* item){
 	if(item){
 		MEquipmentItems.lock();
 		SetItem(slot, item);
-		if(item->details.unique_id == 0)
+		if (item->details.unique_id == 0) {
 			GetItem(slot)->details.unique_id = MasterItemList::NextUniqueID();
+			if (item->IsBag())
+				item->details.bag_id = item->details.unique_id;
+		}
 		MEquipmentItems.unlock();
 		return true;
 	}
@@ -3682,8 +3689,15 @@ int8 EquipmentItemList::GetSlotByItem(Item* item) {
 	return slot;
 }
 
-string Item::CreateItemLink(bool bUseUniqueID) {
+string Item::CreateItemLink(int16 client_Version, bool bUseUniqueID) {
 	ostringstream ss;
-	ss << "\\aITEM " << details.item_id << ' ' << (bUseUniqueID ? details.unique_id : 0) << ':' << name << "\\/a";
+	if(client_Version > 546)
+		ss << "\\aITEM " << details.item_id << ' ' << (bUseUniqueID ? details.unique_id : 0) << ':' << name << "\\/a";
+	else {
+		if(bUseUniqueID)
+			ss << "\\aITEM " << details.item_id << ' ' << details.unique_id << ':' << name << "\\/a";
+		else
+			ss << "\\aITEM " << details.item_id << ' ' << name << ':' << name << "\\/a";
+	}
 	return ss.str();
 }

+ 1 - 2
EQ2/source/WorldServer/Items/Items.h

@@ -845,7 +845,6 @@ public:
 	int32					spell_id;
 	int8					spell_tier;
 	string					item_script;
-
 	void AddEffect(string effect, int8 percentage, int8 subbulletflag);
 	void AddBookPage(int8 page, string page_text,int8 valign, int8 halign);
 	int32 GetMaxSellValue();
@@ -910,7 +909,7 @@ public:
 	void SetItemScript(string name);
 	const char*	GetItemScript();
 	int32 CalculateRepairCost();
-	string CreateItemLink(bool bUseUniqueID);
+	string CreateItemLink(int16 client_Version, bool bUseUniqueID=false);
 
 	void SetItemType(int8 in_type);
 	void serialize(PacketStruct* packet, bool show_name = false, Player* player = 0, int16 packet_type = 0, int8 subtype = 0, bool loot_item = false);

+ 504 - 46
EQ2/source/WorldServer/LuaFunctions.cpp

@@ -36,8 +36,10 @@
 #include "ClientPacketFunctions.h"
 #include "Transmute.h"
 #include <boost/algorithm/string/predicate.hpp>
+#include <sstream> 
 #include <boost/algorithm/string.hpp>
 
+extern MasterFactionList master_faction_list;
 extern WorldDatabase database;
 extern LuaInterface* lua_interface;
 extern ConfigReader configReader;
@@ -55,6 +57,98 @@ extern MasterHeroicOPList master_ho_list;
 extern MasterRaceTypeList race_types_list;
 extern MasterLanguagesList master_languages_list;
 
+vector<string> ParseString(string strVal, char delim) {
+	stringstream ss(strVal);
+	vector<string> ret;
+	while (ss.good())
+	{
+		string substr;
+		getline(ss, substr, delim);
+		ret.push_back(substr);
+	}
+	return ret;
+}
+
+vector<unsigned int> ParseStringToInt32(string strVal, char delim) {
+	stringstream ss(strVal);
+	vector<unsigned int> ret;
+	while (ss.good())
+	{
+		string substr;
+		getline(ss, substr, delim);
+		stringstream valss(substr);
+		unsigned int val = 0;
+		valss >> val;
+		ret.push_back(val);
+	}
+	return ret;
+}
+
+map<string, signed  int> ParseStringMap(string strVal, char delim) {
+	vector<string> pairs = ParseString(strVal, delim);
+	vector<string>::iterator itr;
+	map<string, signed int> ret;
+	for (itr = pairs.begin(); itr != pairs.end(); itr++) {
+		vector<string> keyvaluepair = ParseString(*itr, ':');
+		if (keyvaluepair.size() == 2) {
+			stringstream valss(keyvaluepair[1]);
+			int32 val = 0;
+			valss >> val;
+			ret[keyvaluepair[0]] = val;
+		}
+	}	
+	
+	return ret;
+}
+
+map<unsigned int, unsigned short> ParseIntMap(string strVal, char delim) {
+	vector<string> pairs = ParseString(strVal, delim);
+	vector<string>::iterator itr;
+	map<unsigned int, unsigned short> ret;
+	for (itr = pairs.begin(); itr != pairs.end(); itr++) {
+		vector<string> keyvaluepair = ParseString(*itr, ':');
+		int32 key = 0;
+		if (keyvaluepair.size() > 0) {
+			stringstream keyss(keyvaluepair[0]);
+			keyss >> key;
+		}
+		if (keyvaluepair.size() == 1) {
+			ret[key] = 1;
+		}
+		else if (keyvaluepair.size() == 2) {
+			stringstream valss(keyvaluepair[1]);
+			unsigned short val = 0;
+			valss >> val;
+			ret[key] = val;
+		}
+	}
+	return ret;
+}
+
+map<unsigned int, signed int> ParseSInt32Map(string strVal, char delim) {
+	vector<string> pairs = ParseString(strVal, delim);
+	vector<string>::iterator itr;
+	map<unsigned int, signed int> ret;
+	for (itr = pairs.begin(); itr != pairs.end(); itr++) {
+		vector<string> keyvaluepair = ParseString(*itr, ':');
+		int32 key = 0;
+		if (keyvaluepair.size() > 0) {
+			stringstream keyss(keyvaluepair[0]);
+			keyss >> key;
+		}
+		if (keyvaluepair.size() == 1) {
+			ret[key] = 1;
+		}
+		else if (keyvaluepair.size() == 2) {
+			stringstream valss(keyvaluepair[1]);
+			signed int val = 0;
+			valss >> val;
+			ret[key] = val;
+		}
+	}
+	return ret;
+}
+
 int EQ2Emu_lua_PlayFlavor(lua_State* state) {
 	if (!lua_interface)
 		return 0;
@@ -231,6 +325,29 @@ int EQ2Emu_lua_Despawn(lua_State* state) {
 	return 0;
 }
 
+int EQ2Emu_lua_ChangeHandIcon(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	Spawn* spawn = lua_interface->GetSpawn(state);
+	int8 displayHandIcon = lua_interface->GetInt8Value(state, 2);
+	if (spawn) {
+		spawn->info_changed = true;
+		spawn->SetShowHandIcon(displayHandIcon);
+	}
+	return 0;
+}
+
+int EQ2Emu_lua_SendStateCommand(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	Spawn* spawn = lua_interface->GetSpawn(state);
+	int32 new_state = lua_interface->GetInt32Value(state, 2);
+	if (spawn) {
+		spawn->GetZone()->SendStateCommand(spawn, new_state);
+	}
+	return 0;
+}
+
 int EQ2Emu_lua_SpawnSet(lua_State* state) {
 	if (!lua_interface)
 		return 0;
@@ -260,6 +377,88 @@ int EQ2Emu_lua_GetSpawn(lua_State* state) {
 	return 0;
 }
 
+int EQ2Emu_lua_GetSpawnFromList(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	vector<Spawn*>* spawnList = lua_interface->GetSpawnList(state);
+	int32 position = lua_interface->GetInt32Value(state, 2);
+	if (spawnList) {
+		if (spawnList->size() > position) {
+			lua_interface->SetSpawnValue(state, spawnList->at(position));
+			return 1;
+		}
+		else {
+			return 0;
+		}
+		return spawnList->size();
+	}
+	return 0;
+}
+
+int EQ2Emu_lua_GetSpawnListSize(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	vector<Spawn*>* spawnList = lua_interface->GetSpawnList(state);
+	if (spawnList) {
+		return spawnList->size();
+	}
+	return 0;
+}
+
+int EQ2Emu_lua_CreateSpawnList(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	vector<Spawn*>* spawnList = new vector<Spawn*>();
+	lua_interface->SetSpawnListValue(state, spawnList);
+	return 1;
+}
+
+int EQ2Emu_lua_AddSpawnToSpawnList(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	vector<Spawn*>* spawnList = lua_interface->GetSpawnList(state);
+	Spawn* spawn = lua_interface->GetSpawn(state, 2);
+	if (spawnList) {
+		auto it = std::find(spawnList->begin(), spawnList->end(), spawn);
+		if (it == spawnList->end())
+			spawnList->push_back(spawn);
+	}
+	return 0;
+}
+
+int EQ2Emu_lua_RemoveSpawnFromSpawnList(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	vector<Spawn*>* spawnList = lua_interface->GetSpawnList(state);
+	Spawn* spawn = lua_interface->GetSpawn(state, 2);
+	if (spawnList) {
+		auto it = std::find(spawnList->begin(), spawnList->end(), spawn);
+		if(it != spawnList->end())
+			spawnList->erase(it);
+	}
+	return 0;
+}
+
+int EQ2Emu_lua_GetSpawnListBySpawnID(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	Spawn* spawn = lua_interface->GetSpawn(state);
+	int32 spawn_id = lua_interface->GetInt32Value(state, 2);
+	if (spawn) {
+		vector<Spawn*> spawns = spawn->GetZone()->GetSpawnsByID(spawn_id);
+		if (spawns.size() > 0) {
+			vector<Spawn*>* spawnList = new vector<Spawn*>();
+			vector<Spawn*>::iterator itr;
+			for (itr = spawns.begin(); itr != spawns.end(); itr++) {
+				spawnList->push_back(*itr);
+			}
+			lua_interface->SetSpawnListValue(state, spawnList);
+			return 1;
+		}
+	}
+	return 0;
+}
+
 int EQ2Emu_lua_GetVariableValue(lua_State* state) {
 	if (!lua_interface)
 		return 0;
@@ -458,6 +657,18 @@ int EQ2Emu_lua_GetCurrentZoneSafeLocation(lua_State* state) {
 	return 0;
 }
 
+int EQ2Emu_lua_HasLootItem(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	Spawn* spawn = lua_interface->GetSpawn(state);
+	if (spawn) {
+		int32 item_id = lua_interface->GetInt32Value(state, 2);
+		lua_interface->SetBooleanValue(state, spawn->HasLootItemID(item_id));
+		return 1;
+	}
+	return 0;
+}
+
 int EQ2Emu_lua_AddLootItem(lua_State* state) {
 	if (!lua_interface)
 		return 0;
@@ -487,9 +698,9 @@ int EQ2Emu_lua_AddLootCoin(lua_State* state) {
 	if (!lua_interface)
 		return 0;
 	Spawn* spawn = lua_interface->GetSpawn(state);
-	if (spawn && spawn->IsEntity()) {
+	if (spawn) {
 		int32 val = lua_interface->GetInt32Value(state, 2);
-		((Entity*)spawn)->AddLootCoins(val);
+		spawn->AddLootCoins(val);
 	}
 	return 0;
 }
@@ -512,11 +723,11 @@ int EQ2Emu_lua_GiveLoot(lua_State* state) {
 			i++;
 		}
 		Client* client = 0;
-		client = entity->GetZone()->GetClientBySpawn(player);
-		if (client) {
+		client = player->GetZone()->GetClientBySpawn(player);
+		if (client) 
 			((Player*)player)->AddPendingLootItems(entity->GetID(), items);
-			client->Loot(coins, ((Player*)player)->GetPendingLootItems(entity->GetID()), (Entity*)entity);
-		}
+		if(coins > 0)
+			entity->AddLootCoins(coins);
 		safe_delete(items);
 	}
 	return 0;
@@ -540,7 +751,7 @@ int EQ2Emu_lua_HasPendingLoot(lua_State* state) {
 		return 0;
 	Spawn* entity = lua_interface->GetSpawn(state);
 	Spawn* player = lua_interface->GetSpawn(state, 2);
-	if (entity && entity->IsEntity() && player && player->IsPlayer()) {
+	if (entity && player && player->IsPlayer()) {
 		lua_interface->SetBooleanValue(state, ((Player*)player)->HasPendingLootItems(entity->GetID()));
 		return 1;
 	}
@@ -803,6 +1014,16 @@ int EQ2Emu_lua_MoveToLocation(lua_State* state) {
 	return 0;
 }
 
+int EQ2Emu_lua_ClearRunningLocations(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	Spawn* spawn = lua_interface->GetSpawn(state);
+	if (spawn) {
+		spawn->ClearRunningLocations();
+	}
+	return 0;
+}
+
 int EQ2Emu_lua_Say(lua_State* state) {
 	if (!lua_interface)
 		return 0;
@@ -853,9 +1074,9 @@ int EQ2Emu_lua_SayOOC(lua_State* state) {
 		if (player && player->IsPlayer())
 			client = spawn->GetZone()->GetClientBySpawn(player);
 		if (client)
-			spawn->GetZone()->HandleChatMessage(client, spawn, 0, CHANNEL_OOC, message.c_str(), 30);
+			spawn->GetZone()->HandleChatMessage(client, spawn, 0, CHANNEL_OUT_OF_CHARACTER, message.c_str(), 30);
 		else
-			spawn->GetZone()->HandleChatMessage(spawn, 0, CHANNEL_OOC, message.c_str(), 30);
+			spawn->GetZone()->HandleChatMessage(spawn, 0, CHANNEL_OUT_OF_CHARACTER, message.c_str(), 30);
 	}
 	lua_interface->ResetFunctionStack(state);
 	return 0;
@@ -971,7 +1192,7 @@ int EQ2Emu_lua_SummonItem(lua_State* state) {
 			if (send_messages) {
 				Item* item = master_item_list.GetItem(item_id);
 				if (item) {
-					client->Message(CHANNEL_COLOR_YELLOW, "You receive \\aITEM %u 0:%s\\/a.", item->details.item_id, item->name.c_str());
+					client->Message(CHANNEL_COLOR_YELLOW, "You receive %s.", item->CreateItemLink(client->GetVersion()).c_str());
 					string popup_text = "You receive " + item->name;
 					client->SendPopupMessage(10, popup_text.c_str(), "ui_harvested_normal", 3, 0xFF, 0xFF, 0xFF);
 				}
@@ -1072,7 +1293,7 @@ int EQ2Emu_lua_Spawn(lua_State* state) {
 			output = output.append("\t").append("Missing zone reference. \n");
 		if (spawn_id == 0)
 			output = output.append("\t").append("Missing spawn_id.");
-		lua_interface->LogError("%s: Error in EQ2Emu_lua_Zone - %s", lua_interface->GetScriptName(state), output.c_str());
+		lua_interface->LogError("%s: Error in EQ2Emu_lua_Spawn - %s", lua_interface->GetScriptName(state), output.c_str());
 	}
 	return 0;
 }
@@ -2859,6 +3080,7 @@ int EQ2Emu_lua_OfferQuest(lua_State* state) {
 	Spawn* npc = lua_interface->GetSpawn(state);
 	Spawn* player = lua_interface->GetSpawn(state, 2);
 	int32 quest_id = lua_interface->GetInt32Value(state, 3);
+	bool forced = lua_interface->GetBooleanValue(state, 4);
 
 	/* NPC is allowed to be null */
 	if (player && player->IsPlayer() && quest_id > 0) {
@@ -2878,6 +3100,7 @@ int EQ2Emu_lua_OfferQuest(lua_State* state) {
 					quest->SetQuestGiver(npc->GetDatabaseID());
 				else
 					quest->SetQuestGiver(0);
+				client->AddPendingQuest(quest, forced);
 			}
 		}
 		else {
@@ -3110,6 +3333,10 @@ int EQ2Emu_lua_AddQuestStepKill(lua_State* state) {
 		QuestStep* quest_step = quest->AddQuestStep(step, QUEST_STEP_TYPE_KILL, description, ids, quantity, taskgroup, 0, 0, percentage, 0);
 		if (quest_step && icon > 0 && quantity > 0)
 			quest_step->SetIcon(icon);
+		if (quest->GetPlayer()) {
+			Client* client = quest->GetPlayer()->GetZone()->GetClientBySpawn(quest->GetPlayer());
+			quest->GetPlayer()->GetZone()->SendQuestUpdates(client);
+		}
 	}
 	return 0;
 }
@@ -3173,6 +3400,10 @@ int EQ2Emu_lua_AddQuestStepObtainItem(lua_State* state) {
 		QuestStep* quest_step = quest->AddQuestStep(step, QUEST_STEP_TYPE_OBTAIN_ITEM, description, ids, quantity, taskgroup, 0, 0, percentage, 0);
 		if (quest_step && icon > 0 && quantity > 0)
 			quest_step->SetIcon(icon);
+		if (quest->GetPlayer()) {
+			Client* client = quest->GetPlayer()->GetZone()->GetClientBySpawn(quest->GetPlayer());
+			quest->GetPlayer()->GetZone()->SendQuestUpdates(client);
+		}
 	}
 	return 0;
 }
@@ -3207,6 +3438,10 @@ int EQ2Emu_lua_AddQuestStepLocation(lua_State* state) {
 		QuestStep* quest_step = quest->AddQuestStep(step, QUEST_STEP_TYPE_LOCATION, description, 0, 1, taskgroup, locations, max_variation);
 		if (quest_step && icon > 0)
 			quest_step->SetIcon(icon);
+		if (quest->GetPlayer()) {
+			Client* client = quest->GetPlayer()->GetZone()->GetClientBySpawn(quest->GetPlayer());
+			quest->GetPlayer()->GetZone()->SendQuestUpdates(client);
+		}
 	}
 	return 0;
 }
@@ -3240,6 +3475,10 @@ int EQ2Emu_lua_AddQuestUsableItem(lua_State* state) {
 		QuestStep* quest_step = quest->AddQuestStep(step, QUEST_STEP_TYPE_LOCATION, description, 0, 1, taskgroup, locations, max_variation);
 		if (quest_step && icon > 0)
 			quest_step->SetIcon(icon);
+		if (quest->GetPlayer()) {
+			Client* client = quest->GetPlayer()->GetZone()->GetClientBySpawn(quest->GetPlayer());
+			quest->GetPlayer()->GetZone()->SendQuestUpdates(client);
+		}
 	}
 	return 0;
 }
@@ -3267,6 +3506,10 @@ int EQ2Emu_lua_AddQuestStepSpell(lua_State* state) {
 		QuestStep* quest_step = quest->AddQuestStep(step, QUEST_STEP_TYPE_SPELL, description, ids, quantity, taskgroup, 0, 0, percentage, 0);
 		if (quest_step && icon > 0 && quantity > 0)
 			quest_step->SetIcon(icon);
+		if (quest->GetPlayer()) {
+			Client* client = quest->GetPlayer()->GetZone()->GetClientBySpawn(quest->GetPlayer());
+			quest->GetPlayer()->GetZone()->SendQuestUpdates(client);
+		}
 	}
 	return 0;
 }
@@ -3297,6 +3540,10 @@ int EQ2Emu_lua_AddQuestStepCraft(lua_State* state) {
 		QuestStep* quest_step = quest->AddQuestStep(step, QUEST_STEP_TYPE_CRAFT, description, ids, quantity, taskgroup, 0, 0, percentage, 0);
 		if (quest_step && icon > 0 && quantity > 0)
 			quest_step->SetIcon(icon);
+		if (quest->GetPlayer()) {
+			Client* client = quest->GetPlayer()->GetZone()->GetClientBySpawn(quest->GetPlayer());
+			quest->GetPlayer()->GetZone()->SendQuestUpdates(client);
+		}
 	}
 	return 0;
 }
@@ -3327,6 +3574,10 @@ int EQ2Emu_lua_AddQuestStepHarvest(lua_State* state) {
 		QuestStep* quest_step = quest->AddQuestStep(step, QUEST_STEP_TYPE_HARVEST, description, ids, quantity, taskgroup, 0, 0, percentage, 0);
 		if (quest_step && icon > 0 && quantity > 0)
 			quest_step->SetIcon(icon);
+		if (quest->GetPlayer()) {
+			Client* client = quest->GetPlayer()->GetZone()->GetClientBySpawn(quest->GetPlayer());
+			quest->GetPlayer()->GetZone()->SendQuestUpdates(client);
+		}
 	}
 	return 0;
 }
@@ -3418,7 +3669,7 @@ int EQ2Emu_lua_UpdateQuestStepDescription(lua_State* state) {
 		if (quest->GetPlayer()) {
 			Client* client = quest->GetPlayer()->GetZone()->GetClientBySpawn(quest->GetPlayer());
 			if (client)
-				client->SendQuestUpdateStep(quest, step);
+				client->SendQuestUpdateStepImmediately(quest, step);
 		}
 	}
 	return 0;
@@ -3432,6 +3683,148 @@ int EQ2Emu_lua_UpdateQuestZone(lua_State* state) {
 	return 0;
 }
 
+int EQ2Emu_lua_GiveImmediateQuestReward(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	Quest* quest = lua_interface->GetQuest(state);
+	Spawn* playerSpawn = lua_interface->GetSpawn(state, 2);
+	int32 coin = lua_interface->GetInt32Value(state, 3);
+	int32 status_points = lua_interface->GetInt32Value(state, 4);	
+	string rewards_str = lua_interface->GetStringValue(state, 5);
+	string select_rewards_str = lua_interface->GetStringValue(state, 6);
+	string factions_map_str = lua_interface->GetStringValue(state, 7);
+	string text = lua_interface->GetStringValue(state, 8);
+	int32 source_id = 0;
+	if (quest)
+		source_id = quest->GetQuestID();
+	if (playerSpawn && playerSpawn->IsPlayer()) {
+		Player* player = (Player*)playerSpawn;
+		Client* client = player->GetZone()->GetClientBySpawn(player);
+		if (client) {
+			vector<Item*> reward_items;
+			vector<Item*> selectable_reward_items;
+			if (rewards_str.length() > 0) {
+				map<unsigned int, unsigned short> rewards = ParseIntMap(rewards_str);
+				map<unsigned int, unsigned short>::iterator itr;
+				for (itr = rewards.begin(); itr != rewards.end(); itr++) {
+					if (itr->first > 0) {
+						Item* item = new Item(master_item_list.GetItem(itr->first));
+						if (item) {
+							if (itr->second > 0)
+								item->stack_count = itr->second;
+							reward_items.push_back(item);
+						}
+					}
+				}
+			}
+			if (select_rewards_str.length() > 0) {
+				map<unsigned int, unsigned short> rewards = ParseIntMap(select_rewards_str);
+				map<unsigned int, unsigned short>::iterator itr;
+				for (itr = rewards.begin(); itr != rewards.end(); itr++) {
+					if (itr->first > 0) {
+						Item* item = new Item(master_item_list.GetItem(itr->first));
+						if (item) {
+							if (itr->second > 0)
+								item->stack_count = itr->second;
+							selectable_reward_items.push_back(item);
+						}
+					}
+				}
+			}
+			map<unsigned int, signed int> faction_rewards = ParseSInt32Map(factions_map_str);
+			const char* reward_type = "Quest Reward!";
+			if (!quest)
+				reward_type = "Reward!";
+			client->DisplayQuestRewards(0, coin, &reward_items, &selectable_reward_items, &faction_rewards, reward_type, status_points, text.c_str());
+		}
+	}
+			/*PacketStruct* packet2 = configReader.getStruct("WS_QuestRewardPackMsg", client->GetVersion());
+			if (packet2) {
+				player->AddCoins(coin);
+				client->PlaySound("coin_cha_ching");
+				packet2->setSubstructDataByName("reward_data", "unknown1", 255);
+				if(quest)
+					packet2->setSubstructDataByName("reward_data", "reward", "Quest Reward!");
+				else
+					packet2->setSubstructDataByName("reward_data", "reward", "Reward!");
+				packet2->setSubstructDataByName("reward_data", "coin", coin);
+				if (player->GetGuild()) {
+					player->GetInfoStruct()->status_points += status_points;
+					packet2->setSubstructDataByName("reward_data", "status_points", status_points);
+				}
+				if (rewards_str.length() > 0) {
+					map<unsigned int, unsigned short> rewards = ParseIntMap(rewards_str);
+					vector<Item*> reward_items;
+					map<unsigned int, unsigned short>::iterator itr;
+					for (itr = rewards.begin(); itr != rewards.end(); itr++) {
+						if (itr->first > 0) {
+							Item* item = new Item(master_item_list.GetItem(itr->first));
+							if (item) {
+								if (itr->second > 0)
+									item->stack_count = itr->second;
+								reward_items.push_back(item);
+							}
+						}
+					}
+					packet2->setSubstructArrayLengthByName("reward_data", "num_rewards", reward_items.size());
+					for (int i = 0; i < reward_items.size(); i++) {
+						Item* item = reward_items[i];
+						packet2->setArrayDataByName("reward_id", item->details.item_id, i);
+						packet2->setItemArrayDataByName("item", item, client->GetPlayer(), i, 0, -1);
+						player->AddPendingItemReward(item); //item reference will be deleted after the player accepts it
+					}
+				}
+				if (select_rewards_str.length() > 0) {
+					map<unsigned int, unsigned short> rewards = ParseIntMap(select_rewards_str);
+					vector<Item*> reward_items;
+					map<unsigned int, unsigned short>::iterator itr;
+					for (itr = rewards.begin(); itr != rewards.end(); itr++) {
+						if (itr->first > 0) {
+							Item* item = new Item(master_item_list.GetItem(itr->first));
+							if (item) {
+								if (itr->second > 0)
+									item->stack_count = itr->second;
+								reward_items.push_back(item);
+							}
+						}
+					}
+					packet2->setSubstructArrayLengthByName("reward_data", "num_select_rewards", reward_items.size());
+					for (int i = 0; i < reward_items.size(); i++) {
+						Item* item = reward_items[i];
+						packet2->setArrayDataByName("select_reward_id", item->details.item_id, i);
+						packet2->setItemArrayDataByName("select_item", item, client->GetPlayer(), i, 0, -1);
+						player->AddPendingSelectableItemReward(source_id, item); //item reference will be deleted after the player selects one
+					}
+				}
+				if (factions_map_str.length() > 0) {
+					map<unsigned int, signed int> faction_rewards = ParseSInt32Map(factions_map_str);
+					map<unsigned int, signed int>::iterator itr;
+					map<Faction*, signed int> factions;
+					for (itr = faction_rewards.begin(); itr != faction_rewards.end(); itr++) {
+						Faction* faction = master_faction_list.GetFaction(itr->first);
+						if (faction)
+							factions[faction] = itr->second;
+					}
+					packet2->setSubstructArrayLengthByName("reward_data", "num_factions", factions.size());
+					map<Faction*, signed int>::iterator faction_itr;
+					int8 i = 0;
+					for (faction_itr = factions.begin(); faction_itr != factions.end(); faction_itr++) {
+						packet2->setArrayDataByName("faction_name", faction_itr->first->name.c_str(), i);
+						sint32 amount = faction_itr->second;
+						packet2->setArrayDataByName("amount", amount, i);
+						if (amount > 0)
+							player->GetFactions()->IncreaseFaction(faction_itr->first->id, amount);
+						else
+							player->GetFactions()->DecreaseFaction(faction_itr->first->id, (amount * -1));
+						i++;
+					}
+				}				
+				client->QueuePacket(packet2->serialize());
+				safe_delete(packet2);
+			}*/
+	return 0;
+}
+
 int EQ2Emu_lua_GiveQuestReward(lua_State* state) {
 	if (!lua_interface)
 		return 0;
@@ -3585,7 +3978,7 @@ int EQ2Emu_lua_SendMessage(lua_State* state) {
 	Spawn* spawn = lua_interface->GetSpawn(state);
 	string message = lua_interface->GetStringValue(state, 2);
 	string color_str = lua_interface->GetStringValue(state, 3);
-	int8 color = CHANNEL_COLOR_WHITE;
+	int8 color = CHANNEL_NARRATIVE;
 	if (spawn && spawn->IsPlayer() && message.length() > 0) {
 		Client* client = spawn->GetZone()->GetClientBySpawn(spawn);
 		if (client) {
@@ -4122,6 +4515,18 @@ int EQ2Emu_lua_SetTradeskillLevel(lua_State* state) {
 	return 0;
 }
 
+int EQ2Emu_lua_SetAttackable(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	Spawn* spawn = lua_interface->GetSpawn(state);
+	int8 attackable = lua_interface->GetInt8Value(state, 2);
+	if (spawn) {		
+		spawn->SetAttackable(attackable);
+		spawn->vis_changed = true; //some clients store this in vis instead of info, need to make sense both are updated
+	}
+	return 0;
+}
+
 int EQ2Emu_lua_SummonPet(lua_State* state) {
 	// Check to see if we have a valid lua_interface
 	if (!lua_interface)
@@ -4977,7 +5382,7 @@ int EQ2Emu_lua_GiveQuestItem(lua_State* state)
 		safe_delete(packet);
 
 		lua_interface->SetBooleanValue(state, client->AddItem(item_id, 1));
-		client->Message(CHANNEL_COLOR_YELLOW, "You receive \\aITEM %u 0:%s\\/a.", item->details.item_id, item->name.c_str());
+		client->Message(CHANNEL_COLOR_YELLOW, "You receive %s.", item->CreateItemLink(client->GetVersion()).c_str());
 		return 1;
 	}
 
@@ -9386,6 +9791,22 @@ int EQ2Emu_lua_AddLootToObject(lua_State* state) {
 	return 0;
 }
 
+int EQ2Emu_lua_GiveExp(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	Spawn* player = lua_interface->GetSpawn(state);
+	int32 amount = lua_interface->GetInt32Value(state, 2);
+	if (player && player->IsPlayer() && amount > 0) {
+		((Player*)player)->AddXP(amount);
+		((Player*)player)->SetCharSheetChanged(true);
+		Client* client = player->GetZone()->GetClientBySpawn(player);
+		if (client) {
+			client->SimpleMessage(CHANNEL_REWARD, "You gain experience!");
+		}
+	}
+	return 0;
+}
+
 int EQ2Emu_lua_DisplayText(lua_State* state) {
 	if (!lua_interface)
 		return 0;
@@ -9420,32 +9841,7 @@ int EQ2Emu_lua_ShowLootWindow(lua_State* state) {
 		lua_interface->LogError("%s: LUA ShowLootWindow has no items", lua_interface->GetScriptName(state));
 		return 0;
 	}
-	client->Loot(0, items, spawn);
-	/*PacketStruct* packet2 = configReader.getStruct("WS_UpdateLoot", client->GetVersion());
-	if (packet2) {
-		packet2->setArrayLengthByName("loot_count", items->size());
-		for(int i=0;i< items->size();i++){
-			Item* item = (*items)[0];
-			packet2->setArrayDataByName("name", item->name.c_str(), i);
-			packet2->setArrayDataByName("item_id", item->details.item_id, i);
-			packet2->setArrayDataByName("count", item->details.count, i);
-			packet2->setArrayDataByName("icon", item->details.icon, i);
-			if(item->generic_info.skill_req1 > 0 && item->generic_info.skill_req1 < 0xFFFFFFFF)
-				packet2->setArrayDataByName("ability_id", item->generic_info.skill_req1, i);
-			else if (item->generic_info.skill_req2 > 0 && item->generic_info.skill_req2 < 0xFFFFFFFF)
-				packet2->setArrayDataByName("ability_id", item->generic_info.skill_req2, i);
-			else
-				packet2->setArrayDataByName("ability_id", 0xFFFFFFFF, i);
-		}
-		packet2->setDataByName("object_id", spawn->GetID());
-		packet2->setDataByName("unknown3", 1);
-		packet2->setDataByName("unknown4", 1);
-		packet2->setDataByName("unknown5", 60);
-		EQ2Packet* app = packet2->serialize();
-		DumpPacket(app);
-		client->QueuePacket(app);
-		safe_delete(packet2);
-	}*/
+	client->Loot(spawn->GetLootCoins(), items, spawn);
 	return 0;
 }
 
@@ -9507,6 +9903,46 @@ int EQ2Emu_lua_AddPrimaryEntityCommandAllSpawns(lua_State* state) {
 	return 0;
 }
 
+int EQ2Emu_lua_InstructionWindowGoal(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	Client* client = 0;
+	Spawn* player = lua_interface->GetSpawn(state);
+	int8 goal_num = lua_interface->GetInt8Value(state, 2);
+	if (player && player->IsPlayer() && player->GetZone())
+		client = player->GetZone()->GetClientBySpawn(player);
+	else{
+		lua_interface->LogError("LUA InstructionWindowGoal command error: player is not valid");
+		return 0;
+	}
+	if (client) {
+		PacketStruct* packet = configReader.getStruct("WS_InstructionWindow", client->GetVersion());
+		if (packet) {
+			packet->setDataByName("goal_num", goal_num);
+			client->QueuePacket(packet->serialize());
+			safe_delete(packet);
+		}
+	}
+	return 0;
+}
+
+int EQ2Emu_lua_InstructionWindowClose(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	Client* client = 0;
+	Spawn* player = lua_interface->GetSpawn(state);
+	if (player && player->IsPlayer() && player->GetZone())
+		client = player->GetZone()->GetClientBySpawn(player);
+	else {
+		lua_interface->LogError("LUA InstructionWindowClose command error: player is not valid");
+		return 0;
+	}
+	if (client) {
+		client->QueuePacket(new EQ2Packet(OP_EqInstructionWindowCloseCmd, 0, 0));
+	}
+	return 0;
+}
+
 int EQ2Emu_lua_InstructionWindow(lua_State* state) {
 	if (!lua_interface)
 		return 0;
@@ -9535,8 +9971,8 @@ int EQ2Emu_lua_InstructionWindow(lua_State* state) {
 		lua_interface->LogError("LUA InstructionWindow command error: spawn is not a player");
 		return 0;
 	}
-	if (player->GetZone())
-		client = player->GetZone()->GetClientBySpawn(player);
+	else
+		client = ((Player*)player)->GetClient();
 
 	if (!client) {
 		lua_interface->LogError("LUA InstructionWindow command error: could not find client");
@@ -9604,8 +10040,8 @@ int EQ2Emu_lua_ShowWindow(lua_State* state) {
 		lua_interface->LogError("LUA ShowWindow command error: spawn is not a player");
 		return 0;
 	}
-	if (player->GetZone())
-		client = player->GetZone()->GetClientBySpawn(player);
+	else
+		client = ((Player*)player)->GetClient();
 
 	if (!client) {
 		lua_interface->LogError("LUA ShowWindow command error: could not find client");
@@ -9654,6 +10090,28 @@ int EQ2Emu_lua_EnableGameEvent(lua_State* state) {
 	return 0;
 }
 
+int EQ2Emu_lua_GetTutorialStep(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	Spawn* player = lua_interface->GetSpawn(state);
+	if (player && player->IsPlayer()) {
+		lua_interface->SetInt32Value(state, ((Player*)player)->GetTutorialStep());
+		return 1;
+	}
+	return 0;
+}
+
+int EQ2Emu_lua_SetTutorialStep(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	Spawn* player = lua_interface->GetSpawn(state);
+	int8 step = lua_interface->GetInt8Value(state, 2);
+	if (player && player->IsPlayer() && step > 0) {
+		((Player*)player)->SetTutorialStep(step);
+	}
+	return 0;
+}
+
 int EQ2Emu_lua_FlashWindow(lua_State* state) {
 	if (!lua_interface)
 		return 0;
@@ -9669,8 +10127,8 @@ int EQ2Emu_lua_FlashWindow(lua_State* state) {
 		lua_interface->LogError("LUA FlashWindow command error: spawn is not a player");
 		return 0;
 	}
-	if (player->GetZone())
-		client = player->GetZone()->GetClientBySpawn(player);
+	else
+		client = ((Player*)player)->GetClient();
 
 	if (!client) {
 		lua_interface->LogError("LUA FlashWindow command error: could not find client");

+ 28 - 0
EQ2/source/WorldServer/LuaFunctions.h

@@ -21,6 +21,17 @@
 #define LUA_FUNCTIONS_H
 
 #include "../LUA/lua.hpp"
+#include <vector>
+#include <string>
+#include <map>
+using namespace std;
+
+vector<string> ParseString(string strVal, char delim=',');
+vector<unsigned int> ParseStringToInt32(string strVal, char delim=',');
+map<string, signed int> ParseStringMap(string strVal, char delim=',');
+map<unsigned int, unsigned short> ParseIntMap(string strVal, char delim = ',');
+map<unsigned int, signed int> ParseSInt32Map(string strVal, char delim = ',');
+
 
 //Sets
 int EQ2Emu_lua_SetCurrentHP(lua_State* state);
@@ -91,6 +102,12 @@ int EQ2Emu_lua_GetStrBase(lua_State* state);
 int EQ2Emu_lua_GetAgiBase(lua_State* state);
 int EQ2Emu_lua_GetLootCoin(lua_State* state);
 int EQ2Emu_lua_GetSpawn(lua_State* state);
+int EQ2Emu_lua_GetSpawnFromList(lua_State* state);
+int EQ2Emu_lua_GetSpawnListSize(lua_State* state);
+int EQ2Emu_lua_CreateSpawnList(lua_State* state);
+int EQ2Emu_lua_AddSpawnToSpawnList(lua_State* state);
+int EQ2Emu_lua_RemoveSpawnFromSpawnList(lua_State* state);
+int EQ2Emu_lua_GetSpawnListBySpawnID(lua_State* state); 
 int EQ2Emu_lua_GetVariableValue(lua_State* state);
 int EQ2Emu_lua_GetCoinMessage(lua_State* state);
 int EQ2Emu_lua_GetSpawnByGroupID(lua_State* state);
@@ -113,6 +130,8 @@ int EQ2Emu_lua_GetItemType(lua_State* state);
 int EQ2Emu_lua_GetSpellName(lua_State* state);
 
 //Misc
+int EQ2Emu_lua_SetAttackable(lua_State* state);
+int EQ2Emu_lua_SendStateCommand(lua_State* state);
 int EQ2Emu_lua_SpawnSet(lua_State* state);
 int EQ2Emu_lua_KillSpawn(lua_State* state); 
 int EQ2Emu_lua_KillSpawnByDistance(lua_State* state);
@@ -120,6 +139,7 @@ int EQ2Emu_lua_SpawnSetByDistance(lua_State* state);
 int EQ2Emu_lua_SetRequiredQuest(lua_State* state);
 int EQ2Emu_lua_SetRequiredHistory(lua_State* state);
 int EQ2Emu_lua_Despawn(lua_State* state);
+int EQ2Emu_lua_ChangeHandIcon(lua_State* state);
 int EQ2Emu_lua_AddHate(lua_State* state);
 int EQ2Emu_lua_GetZone(lua_State* state);
 int EQ2Emu_lua_GetZoneName(lua_State* state);
@@ -141,6 +161,7 @@ int EQ2Emu_lua_CastSpell(lua_State* state);
 int EQ2Emu_lua_SpellDamage(lua_State* state);
 int EQ2Emu_lua_FaceTarget(lua_State* state);
 int EQ2Emu_lua_MoveToLocation(lua_State* state);
+int EQ2Emu_lua_ClearRunningLocations(lua_State* state);
 int EQ2Emu_lua_Say(lua_State* state);
 int EQ2Emu_lua_Shout(lua_State* state);
 int EQ2Emu_lua_SayOOC(lua_State* state);
@@ -153,6 +174,7 @@ int EQ2Emu_lua_PlaySound(lua_State* state);
 int EQ2Emu_lua_PlayVoice(lua_State* state);
 int EQ2Emu_lua_PlayAnimation(lua_State* state);
 int EQ2Emu_lua_AddLootItem(lua_State* state);
+int EQ2Emu_lua_HasLootItem(lua_State* state);
 int EQ2Emu_lua_RemoveLootItem(lua_State* state);
 int EQ2Emu_lua_AddLootCoin(lua_State* state);
 int EQ2Emu_lua_GiveLoot(lua_State* state);
@@ -229,6 +251,7 @@ int EQ2Emu_lua_AddQuestStepCompleteAction(lua_State* state);
 int EQ2Emu_lua_AddQuestStepProgressAction(lua_State* state);
 int EQ2Emu_lua_SetQuestCompleteAction(lua_State* state);
 int EQ2Emu_lua_GiveQuestReward(lua_State* state);
+int EQ2Emu_lua_GiveImmediateQuestReward(lua_State* state);
 int EQ2Emu_lua_UpdateQuestTaskGroupDescription(lua_State* state);
 int EQ2Emu_lua_UpdateQuestStepDescription(lua_State* state);
 int EQ2Emu_lua_UpdateQuestDescription(lua_State* state);
@@ -417,14 +440,19 @@ int EQ2Emu_lua_StartTransmute(lua_State* state);
 int EQ2Emu_lua_CompleteTransmute(lua_State* state);
 int EQ2Emu_lua_ProcHate(lua_State* state);
 
+int EQ2Emu_lua_GiveExp(lua_State* state);
 int EQ2Emu_lua_DisplayText(lua_State* state);
 int EQ2Emu_lua_ShowLootWindow(lua_State* state);
 int EQ2Emu_lua_GetRandomSpawnByID(lua_State* state);
 int EQ2Emu_lua_AddPrimaryEntityCommandAllSpawns(lua_State* state);
 int EQ2Emu_lua_InstructionWindow(lua_State* state);
+int EQ2Emu_lua_InstructionWindowClose(lua_State* state);
+int EQ2Emu_lua_InstructionWindowGoal(lua_State* state);
 int EQ2Emu_lua_ShowWindow(lua_State* state);
 int EQ2Emu_lua_FlashWindow(lua_State* state);
 int EQ2Emu_lua_EnableGameEvent(lua_State* state);
+int EQ2Emu_lua_GetTutorialStep(lua_State* state);
+int EQ2Emu_lua_SetTutorialStep(lua_State* state);
 
 int EQ2Emu_lua_CheckLOS(lua_State* state);
 int EQ2Emu_lua_CheckLOSByCoordinates(lua_State* state);

+ 80 - 7
EQ2/source/WorldServer/LuaInterface.cpp

@@ -705,6 +705,12 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
 	lua_register(state, "GetSpawnGroupID", EQ2Emu_lua_GetSpawnGroupID);
 	lua_register(state, "GetSpawnLocationID", EQ2Emu_lua_GetSpawnLocationID);
 	lua_register(state, "GetSpawnLocationPlacementID", EQ2Emu_lua_GetSpawnLocationPlacementID);
+	lua_register(state, "CreateSpawnList", EQ2Emu_lua_CreateSpawnList);
+	lua_register(state, "AddSpawnToSpawnList", EQ2Emu_lua_AddSpawnToSpawnList);
+	lua_register(state, "RemoveSpawnFromSpawnList", EQ2Emu_lua_RemoveSpawnFromSpawnList);
+	lua_register(state, "GetSpawnListBySpawnID", EQ2Emu_lua_GetSpawnListBySpawnID);
+	lua_register(state, "GetSpawnFromList", EQ2Emu_lua_GetSpawnFromList);
+	lua_register(state, "GetSpawnListSize", EQ2Emu_lua_GetSpawnListSize);	
 	lua_register(state, "SetFactionID", EQ2Emu_lua_SetFactionID);
 	lua_register(state, "GetFactionID", EQ2Emu_lua_GetFactionID);
 	lua_register(state, "GetFactionAmount", EQ2Emu_lua_GetFactionAmount);
@@ -744,14 +750,17 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
 	lua_register(state, "IsPlayer", EQ2Emu_lua_IsPlayer);
 	lua_register(state, "FaceTarget", EQ2Emu_lua_FaceTarget);
 	lua_register(state, "MoveToLocation", EQ2Emu_lua_MoveToLocation);
+	lua_register(state, "ClearRunningLocations", EQ2Emu_lua_ClearRunningLocations);
 	lua_register(state, "Shout", EQ2Emu_lua_Shout);
 	lua_register(state, "Say", EQ2Emu_lua_Say);
 	lua_register(state, "SayOOC", EQ2Emu_lua_SayOOC);
 	lua_register(state, "Emote", EQ2Emu_lua_Emote);
-	lua_register(state, "MovementLoopAddLocation", EQ2Emu_lua_MovementLoopAdd);
+	lua_register(state, "MovementLoopAdd", EQ2Emu_lua_MovementLoopAdd);
 	lua_register(state, "GetCurrentZoneSafeLocation", EQ2Emu_lua_GetCurrentZoneSafeLocation);
 	lua_register(state, "AddTimer", EQ2Emu_lua_AddTimer);
 	lua_register(state, "Harvest", EQ2Emu_lua_Harvest);
+	lua_register(state, "SetAttackable", EQ2Emu_lua_SetAttackable);
+
 	
 	lua_register(state, "AddSpellBonus", EQ2Emu_lua_AddSpellBonus);
 	lua_register(state, "RemoveSpellBonus", EQ2Emu_lua_RemoveSpellBonus);
@@ -790,6 +799,7 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
 	lua_register(state, "PlayVoice", EQ2Emu_lua_PlayVoice);
 	lua_register(state, "PlayAnimation", EQ2Emu_lua_PlayAnimation);
 	lua_register(state, "AddLootItem", EQ2Emu_lua_AddLootItem);
+	lua_register(state, "HasLootItem", EQ2Emu_lua_HasLootItem);
 	lua_register(state, "RemoveLootItem", EQ2Emu_lua_RemoveLootItem);
 	lua_register(state, "AddLootCoin", EQ2Emu_lua_AddLootCoin);
 	lua_register(state, "GiveLoot", EQ2Emu_lua_GiveLoot);
@@ -805,13 +815,15 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
 	lua_register(state, "CloseConversation", EQ2Emu_lua_CloseConversation);
 	lua_register(state, "CloseItemConversation", EQ2Emu_lua_CloseItemConversation);
 	//lua_register(state, "StartItemConversation", EQ2Emu_lua_StartItemConversation);
-	lua_register(state, "StartDialogConversation", EQ2Emu_lua_StartDialogConversation);
+	lua_register(state, "StartDialogConversation", EQ2Emu_lua_StartDialogConversation);	
+	lua_register(state, "SendStateCommand", EQ2Emu_lua_SendStateCommand);
 	lua_register(state, "SpawnSet", EQ2Emu_lua_SpawnSet);
 	lua_register(state, "SpawnSetByDistance", EQ2Emu_lua_SpawnSetByDistance);
 	lua_register(state, "SpawnMove", EQ2Emu_lua_SpawnMove);
 	lua_register(state, "KillSpawn", EQ2Emu_lua_KillSpawn); 
 	lua_register(state, "KillSpawnByDistance", EQ2Emu_lua_KillSpawnByDistance);
 	lua_register(state, "Despawn", EQ2Emu_lua_Despawn);
+	lua_register(state, "ChangeHandIcon", EQ2Emu_lua_ChangeHandIcon);
 	lua_register(state, "IsBindAllowed", EQ2Emu_lua_IsBindAllowed);
 	lua_register(state, "IsGateAllowed", EQ2Emu_lua_IsGateAllowed);
 	lua_register(state, "Bind", EQ2Emu_lua_Bind);
@@ -867,6 +879,7 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
 	lua_register(state, "AddQuestStepProgressAction", EQ2Emu_lua_AddQuestStepProgressAction);
 	lua_register(state, "SetQuestCompleteAction", EQ2Emu_lua_SetQuestCompleteAction);
 	lua_register(state, "GiveQuestReward", EQ2Emu_lua_GiveQuestReward);
+	lua_register(state, "GiveImmediateQuestReward", EQ2Emu_lua_GiveImmediateQuestReward);
 	lua_register(state, "UpdateQuestStepDescription", EQ2Emu_lua_UpdateQuestStepDescription);
 	lua_register(state, "UpdateQuestDescription", EQ2Emu_lua_UpdateQuestDescription);
 	lua_register(state, "UpdateQuestZone", EQ2Emu_lua_UpdateQuestZone);
@@ -1054,10 +1067,15 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
 	lua_register(state, "ShowLootWindow", EQ2Emu_lua_ShowLootWindow);
 	lua_register(state, "AddPrimaryEntityCommandAllSpawns", EQ2Emu_lua_AddPrimaryEntityCommandAllSpawns);
 	lua_register(state, "InstructionWindow", EQ2Emu_lua_InstructionWindow);
+	lua_register(state, "InstructionWindowClose", EQ2Emu_lua_InstructionWindowClose);
+	lua_register(state, "InstructionWindowGoal", EQ2Emu_lua_InstructionWindowGoal);
 	lua_register(state, "ShowWindow", EQ2Emu_lua_ShowWindow);
 	lua_register(state, "FlashWindow", EQ2Emu_lua_FlashWindow);
 	lua_register(state, "EnableGameEvent", EQ2Emu_lua_EnableGameEvent);
+	lua_register(state, "GetTutorialStep", EQ2Emu_lua_GetTutorialStep);
+	lua_register(state, "SetTutorialStep", EQ2Emu_lua_SetTutorialStep);
 	lua_register(state, "DisplayText", EQ2Emu_lua_DisplayText);
+	lua_register(state, "GiveExp", EQ2Emu_lua_GiveExp);
 
 	lua_register(state, "CheckLOS", EQ2Emu_lua_CheckLOS);
 	lua_register(state, "CheckLOSByCoordinates", EQ2Emu_lua_CheckLOSByCoordinates);
@@ -1209,6 +1227,25 @@ vector<ConversationOption>*	LuaInterface::GetConversation(lua_State* state, int8
 	return ret;
 }
 
+vector<Spawn*>* LuaInterface::GetSpawnList(lua_State* state, int8 arg_num) {
+	vector<Spawn*>* ret = 0;
+	if (lua_islightuserdata(state, arg_num)) {
+		LUAUserData* data = (LUAUserData*)lua_touserdata(state, arg_num);
+		if (!data || !data->IsCorrectlyInitialized()) {
+			LogError("%s: GetSpawnList error while processing %s", GetScriptName(state), lua_tostring(state, -1));
+		}
+		else if (!data->IsSpawnList()) {
+			lua_Debug ar;
+			lua_getstack(state, 1, &ar);
+			lua_getinfo(state, "Sln", &ar);
+			LogError("%s: Invalid data type used for GetSpawnList in %s (line %d)", GetScriptName(state), ar.source, ar.currentline);
+		}
+		else
+			ret = data->spawn_list;
+	}
+	return ret;
+}
+
 vector<OptionWindowOption>*	LuaInterface::GetOptionWindow(lua_State* state, int8 arg_num) {
 	vector<OptionWindowOption>* ret = 0;
 	if(lua_islightuserdata(state, arg_num)){
@@ -1404,6 +1441,13 @@ void LuaInterface::SetSpawnValue(lua_State* state, Spawn* spawn) {
 	lua_pushlightuserdata(state, spawn_wrapper);
 }
 
+void LuaInterface::SetSpawnListValue(lua_State* state, vector<Spawn*>* spawnList) {
+	LUASpawnListWrapper* spawnList_wrapper = new LUASpawnListWrapper();
+	spawnList_wrapper->spawn_list = spawnList;
+	AddUserDataPtr(spawnList_wrapper);
+	lua_pushlightuserdata(state, spawnList_wrapper);
+}
+
 void LuaInterface::SetConversationValue(lua_State* state, vector<ConversationOption>* conversation) {
 	LUAConversationOptionWrapper* conv_wrapper = new LUAConversationOptionWrapper();
 	conv_wrapper->conversation_options = conversation;
@@ -1737,7 +1781,7 @@ bool LuaInterface::RunSpawnScript(string script_name, const char* function_name,
 		return false;
 }
 
-bool LuaInterface::RunZoneScript(string script_name, const char* function_name, ZoneServer* zone, Spawn* spawn, int32 grid_id, const char* signal) {
+bool LuaInterface::RunZoneScript(string script_name, const char* function_name, ZoneServer* zone, Spawn* spawn, int32 int32_arg1, const char* str_arg1, Spawn* spawn_arg1, int32 int32_arg2, const char* str_arg2, Spawn* spawn_arg2) {
 	if (!zone)
 		return false;
 	lua_State* state = GetZoneScript(script_name.c_str(), true, true);
@@ -1763,12 +1807,28 @@ bool LuaInterface::RunZoneScript(string script_name, const char* function_name,
 			SetSpawnValue(state, spawn);
 			num_params++;
 		}
-		if (grid_id > 0) {
-			SetInt32Value(state, grid_id);
+		if (int32_arg1 > 0) {
+			SetInt32Value(state, int32_arg1);
 			num_params++;
 		}
-		if (signal) {
-			SetStringValue(state, signal);
+		if (str_arg1) {
+			SetStringValue(state, str_arg1);
+			num_params++;
+		}
+		if (spawn_arg1) {
+			SetSpawnValue(state, spawn_arg1);
+			num_params++;
+		}
+		if (int32_arg2 > 0) {
+			SetInt32Value(state, int32_arg2);
+			num_params++;
+		}
+		if (str_arg2) {
+			SetStringValue(state, str_arg2);
+			num_params++;
+		}
+		if (spawn_arg2) {
+			SetSpawnValue(state, spawn_arg2);
 			num_params++;
 		}
 		if (!CallZoneScript(state, num_params)) {
@@ -1802,6 +1862,7 @@ LUAUserData::LUAUserData(){
 	skill = 0;
 	option_window_option = 0;
 	item = 0;
+	spawn_list = 0;
 }
 
 bool LUAUserData::IsCorrectlyInitialized(){
@@ -1812,6 +1873,10 @@ bool LUAUserData::IsConversationOption(){
 	return false;
 }
 
+bool LUAUserData::IsSpawnList() {
+	return false;
+}
+
 bool LUAUserData::IsOptionWindow() {
 	return false;
 }
@@ -1848,6 +1913,14 @@ bool LUAConversationOptionWrapper::IsConversationOption(){
 	return true;
 }
 
+LUASpawnListWrapper::LUASpawnListWrapper() {
+	correctly_initialized = true;
+}
+
+bool LUASpawnListWrapper::IsSpawnList() {
+	return true;
+}
+
 LUAOptionWindowWrapper::LUAOptionWindowWrapper() {
 	correctly_initialized = true;
 }

+ 12 - 1
EQ2/source/WorldServer/LuaInterface.h

@@ -100,6 +100,7 @@ public:
 	virtual ~LUAUserData(){};
 	virtual bool IsCorrectlyInitialized();
 	virtual bool IsConversationOption();
+	virtual bool IsSpawnList();
 	virtual bool IsOptionWindow();
 	virtual bool IsSpawn();
 	virtual bool IsQuest();
@@ -113,6 +114,7 @@ public:
 	Spawn* spawn;
 	vector<ConversationOption>* conversation_options;
 	vector<OptionWindowOption>* option_window_option;
+	vector<Spawn*>* spawn_list;
 	Quest* quest;
 	Skill* skill;
 	LuaSpell* spell;
@@ -124,6 +126,13 @@ public:
 	bool IsConversationOption();
 };
 
+class LUASpawnListWrapper: public LUAUserData{
+public:
+	LUASpawnListWrapper();
+	~LUASpawnListWrapper() { safe_delete(spawn_list); }
+	bool IsSpawnList();
+};
+
 class LUAOptionWindowWrapper : public LUAUserData {
 public:
 	LUAOptionWindowWrapper();
@@ -186,6 +195,7 @@ public:
 	Skill*			GetSkill(lua_State* state, int8 arg_num = 1);
 	LuaSpell*		GetSpell(lua_State* state, int8 arg_num = 1);
 	vector<ConversationOption>*	GetConversation(lua_State* state, int8 arg_num = 1);
+	vector<Spawn*>* GetSpawnList(lua_State* state, int8 arg_num = 1);
 	vector<OptionWindowOption>* GetOptionWindow(lua_State* state, int8 arg_num = 1);
 	int8			GetInt8Value(lua_State* state, int8 arg_num = 1);
 	int16			GetInt16Value(lua_State* state, int8 arg_num = 1);
@@ -207,6 +217,7 @@ public:
 	void			SetItemValue(lua_State* state, Item* item);
 	void			SetQuestValue(lua_State* state, Quest* quest);
 	void			SetZoneValue(lua_State* state, ZoneServer* zone);
+	void			SetSpawnListValue(lua_State* state, vector<Spawn*>* spawnList);
 	void			SetSpellValue(lua_State* state, LuaSpell* spell);
 	void			SetConversationValue(lua_State* state, vector<ConversationOption>* conversation);
 	void			SetOptionWindowValue(lua_State* state, vector<OptionWindowOption>* optionWindow);
@@ -230,7 +241,7 @@ public:
 	bool			CallItemScript(lua_State* state, int8 num_parameters);
 	bool			RunSpawnScript(string script_name, const char* function_name, Spawn* npc, Spawn* spawn = 0, const char* message = 0);
 	bool			CallSpawnScript(lua_State* state, int8 num_parameters);
-	bool			RunZoneScript(string script_name, const char* function_name, ZoneServer* zone, Spawn* spawn = 0, int32 grid_id = 0, const char* signal = 0);
+	bool			RunZoneScript(string script_name, const char* function_name, ZoneServer* zone, Spawn* spawn = 0, int32 int32_arg1 = 0, const char* str_arg1 = 0, Spawn* spawn_arg1 = 0, int32 int32_arg2 = 0, const char* str_arg2 = 0, Spawn* spawn_arg2 = 0);
 	bool			CallZoneScript(lua_State* state, int8 num_parameters);
 	void			ResetFunctionStack(lua_State* state);
 	void			DestroySpells();

+ 4 - 4
EQ2/source/WorldServer/NPC_AI.cpp

@@ -78,9 +78,9 @@ void Brain::Think() {
 
 			// Set the NPC's target to the most hated entity if it is not already.
 			if (m_body->GetTarget() != target) {
-				m_body->SetTarget(target);
-				m_body->FaceTarget(target);
+				m_body->SetTarget(target);				
 			}
+			m_body->FaceTarget(target);
 
 			// Check to see if the NPC has exceeded the max chase distance
 			if (run_back_distance > MAX_CHASE_DISTANCE) {
@@ -91,9 +91,9 @@ void Brain::Think() {
 				{
 					// Target is a client so send encounter break messages
 					if (m_body->HasSpawnGroup())
-						client->SimpleMessage(CHANNEL_COLOR_WHITE, "This encounter will no longer give encounter rewards.");
+						client->SimpleMessage(CHANNEL_NARRATIVE, "This encounter will no longer give encounter rewards.");
 					else
-						client->Message(CHANNEL_COLOR_WHITE, "%s is no longer worth any experience or treasure.", m_body->GetName());
+						client->Message(CHANNEL_NARRATIVE, "%s is no longer worth any experience or treasure.", m_body->GetName());
 				}
 				// Clear the hate list for this NPC
 				ClearHate();

+ 42 - 34
EQ2/source/WorldServer/Player.cpp

@@ -48,6 +48,7 @@ extern MasterTitlesList master_titles_list;
 extern MasterLanguagesList master_languages_list;
 
 Player::Player(){
+	tutorial_step = 0;
 	char_id = 0;
 	group = 0;
 	appearance.pos.grid_id = 0;
@@ -194,6 +195,8 @@ Player::~Player(){
 	safe_delete(spawn_info_struct);
 	safe_delete(spawn_vis_struct);
 	safe_delete(spawn_pos_struct);
+	ClearPendingSelectableItemRewards(0, true);
+	ClearPendingItemRewards();
 }
 
 EQ2Packet* Player::serialize(Player* player, int16 version){
@@ -245,7 +248,7 @@ void PlayerInfo::CalculateXPPercentages(){
 	if(info_struct->xp_needed > 0){
 		float percentage = ((double)info_struct->xp / info_struct->xp_needed) * 1000;
 		info_struct->xp_yellow = (int16)percentage;
-		info_struct->xp_blue = (int16)(100-((ceil(percentage/100) - (percentage/100)) * 100));
+		info_struct->xp_blue = (int16)((percentage-info_struct->xp_yellow)*1000);
 		info_struct->xp_blue_vitality_bar = 0;
 		info_struct->xp_yellow_vitality_bar = 0;
 		if(player->GetXPVitality() > 0){
@@ -264,7 +267,7 @@ void PlayerInfo::CalculateTSXPPercentages(){
 	if(info_struct->ts_xp_needed > 0){
 		float percentage = ((double)info_struct->ts_xp / info_struct->ts_xp_needed) * 1000;
 		info_struct->tradeskill_exp_yellow = (int16)percentage;
-		info_struct->tradeskill_exp_blue = (int16)(100-((ceil(percentage/100) - (percentage/100)) * 100));
+		info_struct->tradeskill_exp_blue = (int16)((percentage - info_struct->tradeskill_exp_yellow) * 1000);
 		/*info_struct->xp_blue_vitality_bar = 0;
 		info_struct->xp_yellow_vitality_bar = 0;
 		if(player->GetXPVitality() > 0){
@@ -978,20 +981,26 @@ EQ2Packet* PlayerInfo::serialize(int16 version, int16 modifyPos, int32 modifyVal
 		packet->setDataByName("decrease_falling_dmg", 169);
 
 
-		info_struct->xp_blue = 12;
-		info_struct->xp_yellow = 16;
-		info_struct->weight = 50;
 		info_struct->max_weight = 200;
 		//packet->setDataByName("auto_attack", 1);
 		//492
-
-		packet->setDataByName("exp_yellow", info_struct->xp_yellow);
-		packet->setDataByName("exp_blue", info_struct->xp_blue);
-		packet->setDataByName("tradeskill_exp_yellow", info_struct->tradeskill_exp_yellow);
-		packet->setDataByName("tradeskill_exp_blue", info_struct->tradeskill_exp_blue);
-
-
-
+		if (version <= 546) {
+			packet->setDataByName("exp_yellow", info_struct->xp_yellow / 10);
+			packet->setDataByName("exp_blue", info_struct->xp_blue/10);
+		}
+		else {
+			packet->setDataByName("exp_yellow", info_struct->xp_yellow);
+			packet->setDataByName("exp_blue", info_struct->xp_blue);
+		}
+		
+		if (version <= 546) {
+			packet->setDataByName("tradeskill_exp_yellow", info_struct->tradeskill_exp_yellow / 10);
+			packet->setDataByName("tradeskill_exp_blue", info_struct->tradeskill_exp_blue / 10);
+		}
+		else {
+			packet->setDataByName("tradeskill_exp_yellow", info_struct->tradeskill_exp_yellow);
+			packet->setDataByName("tradeskill_exp_blue", info_struct->tradeskill_exp_blue);
+		}		
 
 		packet->setDataByName("attack", info_struct->cur_attack);
 		packet->setDataByName("attack_base", info_struct->attack_base);
@@ -1000,9 +1009,6 @@ EQ2Packet* PlayerInfo::serialize(int16 version, int16 modifyPos, int32 modifyVal
 		packet->setDataByName("mitigation_skill2", info_struct->mitigation_skill2);
 		packet->setDataByName("mitigation_skill3", info_struct->mitigation_skill3);
 
-
-
-
 		packet->setDataByName("mitigation_max", info_struct->max_mitigation);
 
 		packet->setDataByName("savagery", 250);
@@ -1709,7 +1715,9 @@ vector<EQ2Packet*>	Player::UnequipItem(int16 index, sint32 bag_id, int8 slot, in
 
 			if (item->GetItemScript() && lua_interface)
 				lua_interface->RunItemScript(item->GetItemScript(), "unequipped", item, this);
-
+			const char* zone_script = world.GetZoneScript(GetZone()->GetZoneID());
+			if (zone_script && lua_interface)
+				lua_interface->RunZoneScript(zone_script, "item_unequipped", GetZone(), this, item->details.item_id, item->name.c_str(), 0, item->details.unique_id);
 			item->save_needed = true;
 			EQ2Packet* outapp = item_list.serialize(this, version);
 			if (outapp) {
@@ -1920,9 +1928,9 @@ bool Player::CanEquipItem(Item* item) {
 						if (item->CheckLevel(GetAdventureClass(), GetTradeskillClass(), GetLevel()))
 							return true;
 						else
-							client->Message(CHANNEL_COLOR_RED, "You must be at least level %u to equip \\aITEM %u 0:%s\\/a.", item->generic_info.adventure_default_level, item->details.item_id, item->name.c_str());
+							client->Message(CHANNEL_COLOR_RED, "You must be at least level %u to equip %s.", item->generic_info.adventure_default_level, item->CreateItemLink(client->GetVersion()).c_str());
 					else
-						client->Message(CHANNEL_COLOR_RED, "Your class may not equip \\aITEM %u 0:%s\\/a.", item->details.item_id, item->name.c_str());
+						client->Message(CHANNEL_COLOR_RED, "Your class may not equip %s.", item->CreateItemLink(client->GetVersion()).c_str());
 				}
 				else
 					client->SimpleMessage(0, "You lack the skill required to equip this item.");
@@ -1994,6 +2002,9 @@ vector<EQ2Packet*> Player::EquipItem(int16 index, int16 version, int8 slot_id) {
 			item->save_needed = true;
 			packets.push_back(item->serialize(version, false));
 			SetEquipment(item);
+			const char* zone_script = world.GetZoneScript(GetZone()->GetZoneID());
+			if (zone_script && lua_interface)
+				lua_interface->RunZoneScript(zone_script, "item_equipped", GetZone(), this, item->details.item_id, item->name.c_str(), 0, item->details.unique_id);
 			int32 bag_id = item->details.inv_slot_id;
 			if (item->generic_info.condition == 0) {
 				Client* client = GetZone()->GetClientBySpawn(this);
@@ -2001,7 +2012,7 @@ vector<EQ2Packet*> Player::EquipItem(int16 index, int16 version, int8 slot_id) {
 
 					LogWrite(MISC__TODO, 1, "TODO", "Send popup text in red 'Some of your equipment is broken!'\n\t(%s, function: %s, line #: %i)", __FILE__, __FUNCTION__, __LINE__);
 
-					client->Message(CHANNEL_COLOR_RED, "Your \\aITEM %u %u:%s\\/a is worn out and will not be effective until repaired.", item->details.item_id, item->details.unique_id, item->name.c_str());
+					client->Message(CHANNEL_COLOR_RED, "Your %s is worn out and will not be effective until repaired.", item->CreateItemLink(GetVersion(), true).c_str());
 				}
 			}
 			packets.push_back(equipment_list.serialize(version, this));
@@ -3753,6 +3764,7 @@ bool Player::AddXP(int32 xp_amount){
 	if(current_xp_percent >= miniding_min_percent){
 		SetHP(GetTotalHP());
 		SetPower(GetTotalPower());
+		GetZone()->SendCastSpellPacket(332, this, this); //send mini level up spell effect
 	}
 	return true;
 }
@@ -3980,7 +3992,7 @@ vector<Quest*>* Player::CheckQuestsSpellUpdate(Spell* spell) {
 	return quest_updates;
 }
 
-PacketStruct* Player::GetQuestJournalPacket(bool all_quests, int16 version, int32 crc, int32 current_quest_id){
+PacketStruct* Player::GetQuestJournalPacket(bool all_quests, int16 version, int32 crc, int32 current_quest_id, bool updated){
 	PacketStruct* packet = configReader.getStruct("WS_QuestJournalUpdate", version);
 	Quest* quest = 0;
 	if(packet){
@@ -4035,7 +4047,7 @@ PacketStruct* Player::GetQuestJournalPacket(bool all_quests, int16 version, int3
 				if(!all_quests && !itr->second->GetUpdateRequired())
 					continue;
 				quest = itr->second;
-				if(!quest->GetDeleted())
+				if(!quest->GetDeleted() && !quest->GetCompleted())
 					packet->setArrayDataByName("active", 1, i);
 				packet->setArrayDataByName("name", quest->GetName(), i);
 				packet->setArrayDataByName("quest_type", quest->GetType(), i);
@@ -4047,9 +4059,10 @@ PacketStruct* Player::GetQuestJournalPacket(bool all_quests, int16 version, int3
 					packet->setArrayDataByName("turned_in", 1, i);
 					packet->setArrayDataByName("completed", 1, i);
 					packet->setArrayDataByName("visible", 1, i);
-					display_status += QUEST_DISPLAY_STATUS_COMPLETED;
-					packet->setArrayDataByName("unknown3", 1, i);
+					display_status += QUEST_DISPLAY_STATUS_COMPLETED;					
 				}
+				if (updated)
+					packet->setArrayDataByName("quest_updated", 1, i);
 				packet->setArrayDataByName("quest_id", quest->GetQuestID(), i);
 				packet->setArrayDataByName("day", quest->GetDay(), i);
 				packet->setArrayDataByName("month", quest->GetMonth(), i);
@@ -4091,7 +4104,6 @@ PacketStruct* Player::GetQuestJournalPacket(bool all_quests, int16 version, int3
 				if (itr->second->IsRepeatable())
 					packet->setArrayDataByName("repeatable", 1, i);
 				
-				packet->setArrayDataByName("visible", 0, i);
 				packet->setArrayDataByName("display_status", display_status, i);
 				i++;
 			}
@@ -4113,7 +4125,7 @@ PacketStruct* Player::GetQuestJournalPacket(bool all_quests, int16 version, int3
 	return packet;
 }
 
-PacketStruct* Player::GetQuestJournalPacket(Quest* quest, int16 version, int32 crc) {
+PacketStruct* Player::GetQuestJournalPacket(Quest* quest, int16 version, int32 crc, bool updated) {
 	if (!quest)
 		return 0;
 
@@ -4125,7 +4137,7 @@ PacketStruct* Player::GetQuestJournalPacket(Quest* quest, int16 version, int32 c
 		packet->setArrayDataByName("quest_zones_zone", quest->GetType());
 		packet->setArrayDataByName("quest_zones_zone_id", 0);
 		
-		if(!quest->GetDeleted())
+		if(!quest->GetDeleted() && !quest->GetCompleted())
 			packet->setArrayDataByName("active", 1);
 
 		packet->setArrayDataByName("name", quest->GetName());
@@ -4138,17 +4150,14 @@ PacketStruct* Player::GetQuestJournalPacket(Quest* quest, int16 version, int32 c
 			packet->setArrayDataByName("completed", 1);
 		if(quest->GetTurnedIn()) {
 			packet->setArrayDataByName("turned_in", 1);
-			packet->setArrayDataByName("completed", 1);
-			packet->setArrayDataByName("visible", 1);
+			packet->setArrayDataByName("completed", 1);			
 			display_status += QUEST_DISPLAY_STATUS_COMPLETED;
-			packet->setArrayDataByName("unknown3", 1);
 		}
 		packet->setArrayDataByName("quest_id", quest->GetQuestID());
 		packet->setArrayDataByName("day", quest->GetDay());
 		packet->setArrayDataByName("month", quest->GetMonth());
 		packet->setArrayDataByName("year", quest->GetYear());
 		packet->setArrayDataByName("level", quest->GetQuestLevel());
-
 		int8 difficulty = 0;
 		string category = quest->GetType();
 		if(category == "Tradeskill")
@@ -4187,10 +4196,9 @@ PacketStruct* Player::GetQuestJournalPacket(Quest* quest, int16 version, int32 c
 		if (quest->IsRepeatable())
 			packet->setArrayDataByName("repeatable", 1);
 
-		packet->setArrayDataByName("visible", 0);
 		packet->setArrayDataByName("display_status", display_status);
-		
-		packet->setDataByName("unknown3", 1);
+		if (updated)
+			packet->setDataByName("quest_updated", 1);
 		packet->setDataByName("visible_quest_id", quest->GetQuestID());
 		packet->setDataByName("player_crc", crc);
 		packet->setDataByName("player_name", GetName());

+ 74 - 3
EQ2/source/WorldServer/Player.h

@@ -388,7 +388,8 @@ public:
 	//int8	GetMaxArtLevel(){ return info->GetInfo()->max_art_level; }
 	//int8	GetArtLevel(){ return info->GetInfo()->art_level; }
 
-
+	Client* GetClient() { return client; }
+	void SetClient(Client* client) { this->client = client; }
 	PlayerInfo* GetPlayerInfo();
 	void SetCharSheetChanged(bool val);
 	bool GetCharSheetChanged();
@@ -422,6 +423,12 @@ public:
 	float GetSideSpeed() {
 		return appearance.pos.SideSpeed;
 	}
+	int8 GetTutorialStep() {
+		return tutorial_step;
+	}
+	void SetTutorialStep(int8 val) {
+		tutorial_step = val;
+	}
 	void	AddMaintainedSpell(LuaSpell* spell);
 	void	AddSpellEffect(LuaSpell* spell);
 	void	RemoveMaintainedSpell(LuaSpell* spell);
@@ -568,6 +575,7 @@ public:
 	void	RemoveSpawn(Spawn* spawn);
 	void	ClearRemovedSpawn(Spawn* spawn);
 	bool	ShouldSendSpawn(Spawn* spawn);
+	Client* client = 0;
 
 	Spawn* GetSpawnWithPlayerID(int32 id){
 		Spawn* spawn = 0;
@@ -637,7 +645,7 @@ public:
 		return new_index;
 	}
 
-	PacketStruct*	GetQuestJournalPacket(bool all_quests, int16 version, int32 crc, int32 current_quest_id);
+	PacketStruct*	GetQuestJournalPacket(bool all_quests, int16 version, int32 crc, int32 current_quest_id, bool updated = true);
 	void			RemoveQuest(int32 id, bool delete_quest);
 	vector<Quest*>* CheckQuestsChatUpdate(Spawn* spawn);
 	vector<Quest*>* CheckQuestsItemUpdate(Item* item);
@@ -654,6 +662,7 @@ public:
 	void AddHistoryRequiredSpawn(Spawn* spawn, int32 event_id);
 	int16				spawn_index;
 	int32				spawn_id;
+	int8 tutorial_step;
 	map<int32, vector<int32>*>  player_spawn_quests_required;
 	map<int32, vector<int32>*>   player_spawn_history_required;
 	Mutex				m_playerSpawnQuestsRequired;
@@ -739,6 +748,66 @@ public:
 	PlayerAchievementUpdateList * GetAchievementUpdateList() { return &achievement_update_list; }
 	void				SetPendingCollectionReward(Collection *collection) { pending_collection_reward = collection; }
 	Collection *		GetPendingCollectionReward() { return pending_collection_reward; }
+	void AddPendingSelectableItemReward(int32 source_id, Item* item) {
+		if (pending_selectable_item_rewards.count(source_id) == 0)
+			pending_selectable_item_rewards[source_id] = vector<Item*>();
+		pending_selectable_item_rewards[source_id].push_back(item);
+	}
+	void AddPendingItemReward(Item* item) { 
+		pending_item_rewards.push_back(item);
+	}
+	bool HasPendingItemRewards() { return (pending_item_rewards.size() > 0 || pending_selectable_item_rewards.size() > 0); }
+	vector<Item*> GetPendingItemRewards() { return pending_item_rewards; }
+	map<int32, Item*> GetPendingSelectableItemReward(int32 item_id) { //since the client sends the selected item id, we need to have the associated source and remove all of them.  Yes, there is an edge case if multiple sources have the same Item in them, but limited on what the client sends (just a single item id)
+		map<int32, Item*> ret;
+		if (pending_selectable_item_rewards.size() > 0) {
+			map<int32, vector<Item*>>::iterator map_itr;
+			for (map_itr = pending_selectable_item_rewards.begin(); map_itr != pending_selectable_item_rewards.end(); map_itr++) {
+				vector<Item*>::iterator itr;
+				for (itr = map_itr->second.begin(); itr != map_itr->second.end(); itr++) {
+					if ((*itr)->details.item_id == item_id) {
+						ret[map_itr->first] = *itr;
+						break;
+					}
+				}
+				if (ret.size() > 0)
+					break;
+			}			
+		}
+		return map<int32, Item*>();
+	}
+	void ClearPendingSelectableItemRewards(int32 source_id, bool all = false) { 
+		if (pending_selectable_item_rewards.size() > 0) {
+			map<int32, vector<Item*>>::iterator map_itr;
+			if (all) {
+				for (map_itr = pending_selectable_item_rewards.begin(); map_itr != pending_selectable_item_rewards.end(); map_itr++) {
+					vector<Item*>::iterator itr;
+					for (itr = map_itr->second.begin(); itr != map_itr->second.end(); itr++) {
+						safe_delete(*itr);
+					}
+				}
+				pending_selectable_item_rewards.clear();
+			}
+			else {
+				if (pending_selectable_item_rewards.count(source_id) > 0) {
+					vector<Item*>::iterator itr;
+					for (itr = pending_selectable_item_rewards[source_id].begin(); itr != pending_selectable_item_rewards[source_id].end(); itr++) {
+						safe_delete(*itr);
+					}
+					pending_selectable_item_rewards.erase(source_id);
+				}
+			}			
+		}
+	}	
+	void ClearPendingItemRewards() { //the client doesn't send any reference to where the pending rewards came from, so if they collect one, we should just them all of them at once
+		if (pending_item_rewards.size() > 0) {
+			vector<Item*>::iterator itr;
+			for (itr = pending_item_rewards.begin(); itr != pending_item_rewards.end(); itr++) {
+				safe_delete(*itr);
+			}
+			pending_item_rewards.clear();
+		}
+	}
 	void RemoveSpellBookEntry(int32 spell_id, bool remove_passives_from_list = true);
 	void ResortSpellBook(int32 sort_by, int32 order, int32 pattern, int32 maxlvl_only, int32 book_type);
 
@@ -844,7 +913,7 @@ public:
 	///<summary>Get all the spells the player has with the given id</summary>
 	vector<Spell*> GetSpellBookSpellsByTimer(int32 timerID);
 
-	PacketStruct* GetQuestJournalPacket(Quest* quest, int16 version, int32 crc);
+	PacketStruct* GetQuestJournalPacket(Quest* quest, int16 version, int32 crc, bool updated = true);
 
 	void SetSpawnInfoStruct(PacketStruct* packet) { safe_delete(spawn_info_struct); spawn_info_struct = packet; }
 	void SetSpawnVisStruct(PacketStruct* packet) { safe_delete(spawn_vis_struct); spawn_vis_struct = packet; }
@@ -967,6 +1036,8 @@ private:
 	Guild*				guild;
 	PlayerCollectionList collection_list;
 	Collection *		pending_collection_reward;
+	vector<Item*>		pending_item_rewards;
+	map<int32, vector<Item*>>		pending_selectable_item_rewards;
 	PlayerTitlesList	player_titles_list;
 	PlayerRecipeList	recipe_list;
 	PlayerLanguagesList	player_languages_list;

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

@@ -139,7 +139,7 @@ void PlayerGroup::SimpleGroupMessage(const char* message) {
 	for(itr = m_members.begin(); itr != m_members.end(); itr++) {
 		GroupMemberInfo* info = *itr;
 		if(info->client)
-			info->client->SimpleMessage(CHANNEL_GROUP, message);
+			info->client->SimpleMessage(CHANNEL_GROUP_CHAT, message);
 	}
 	MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
 }

+ 10 - 4
EQ2/source/WorldServer/Quests.cpp

@@ -955,6 +955,7 @@ EQ2Packet* Quest::QuestJournalReply(int16 version, int32 player_crc, Player* pla
 		packet->setDataByName("month", month);
 		packet->setDataByName("year", year);
 		packet->setDataByName("level", level);
+		packet->setDataByName("visible", 1);		
 		/* To get the quest timer to work you need to set unknown, index 4 to 1 and the time stamp
 		to the current time + the time in seconds you want to show in the journal*/
 		if (m_timestamp > 0) {
@@ -1076,8 +1077,9 @@ EQ2Packet* Quest::QuestJournalReply(int16 version, int32 player_crc, Player* pla
 					packet->setSubArrayLengthByName("num_tasks", task_group[primary_order[i]->GetTaskGroup()].size(), index);
 					packet->setSubArrayLengthByName("num_updates", task_group[primary_order[i]->GetTaskGroup()].size(), index);
 					map_data_count += task_group[primary_order[i]->GetTaskGroup()].size();
-					if (task_group[primary_order[i]->GetTaskGroup()].size() > 0)
-						packet->setDataByName("bullets", 1);
+					if (task_group[primary_order[i]->GetTaskGroup()].size() > 0) {
+						packet->setDataByName("bullets", 1);						
+					}
 					for (int32 x = 0; x < task_group[primary_order[i]->GetTaskGroup()].size(); x++) {
 						step = task_group[primary_order[i]->GetTaskGroup()].at(x);
 						if (!step)
@@ -1087,12 +1089,14 @@ EQ2Packet* Quest::QuestJournalReply(int16 version, int32 player_crc, Player* pla
 						packet->setSubArrayDataByName("task_completed", 1, index, x);
 						packet->setSubArrayDataByName("index", x, index, x);
 						packet->setSubArrayDataByName("update_currentval", step->GetQuestCurrentQuantity(), index, x);
+						if(step->GetQuestCurrentQuantity() > 0)
+							packet->setDataByName("journal_updated", 1);
 						packet->setSubArrayDataByName("update_maxval", step->GetQuestNeededQuantity(), index, x);
 						if (step->GetUpdateTargetName())
 							packet->setSubArrayDataByName("update_target_name", step->GetUpdateTargetName(), index, x);
 						packet->setSubArrayDataByName("icon", step->GetIcon(), index, x);
 						if (updateStep && step == updateStep) {
-							packet->setDataByName("update", 1);
+							packet->setDataByName("update", 1);							
 							//	packet->setDataByName("unknown5d", 1);
 							if (!quest_failure)
 								packet->setDataByName("onscreen_update", 1);
@@ -1154,6 +1158,8 @@ EQ2Packet* Quest::QuestJournalReply(int16 version, int32 player_crc, Player* pla
 							packet->setSubArrayDataByName("task_completed", 0, index, x);
 						packet->setSubArrayDataByName("index", x, index, x);
 						packet->setSubArrayDataByName("update_currentval", step->GetQuestCurrentQuantity(), index, x);
+						if (step->GetQuestCurrentQuantity() > 0)
+							packet->setDataByName("journal_updated", 1);
 						packet->setSubArrayDataByName("update_maxval", step->GetQuestNeededQuantity(), index, x);
 						packet->setSubArrayDataByName("icon", step->GetIcon(), index, x);
 						if (step->GetUpdateTargetName())
@@ -1180,7 +1186,7 @@ EQ2Packet* Quest::QuestJournalReply(int16 version, int32 player_crc, Player* pla
 				else {
 					if (task_group_names.count(secondary_order[i]) > 0) {
 						step = secondary_order[i];
-						if (updateStep && step == updateStep) {
+						if (updateStep && step == updateStep) {							
 							packet->setDataByName("update", 1);
 							if (!quest_failure)
 								packet->setDataByName("onscreen_update", 1);

+ 34 - 12
EQ2/source/WorldServer/Spawn.cpp

@@ -203,7 +203,11 @@ void Spawn::InitializeHeaderPacketData(Player* player, PacketStruct* header, int
 				header->setArrayDataByName("command_list_command", primary_command_list[i]->command.c_str(), i);
 			}
 		}
-		header->setMediumStringByName("default_command", primary_command_list[0]->command.c_str());
+		if (header->GetVersion() <= 546) {
+			header->setMediumStringByName("default_command", primary_command_list[0]->name.c_str());
+		}
+		else
+			header->setMediumStringByName("default_command", primary_command_list[0]->command.c_str());
 		header->setDataByName("max_distance", primary_command_list[0]->distance);
 	}
 	if (spawn_group_list && MSpawnGroup){
@@ -236,6 +240,8 @@ void Spawn::InitializeVisPacketData(Player* player, PacketStruct* vis_packet) {
 		vis_packet->setDataByName("targetable", appearance.targetable);
 		vis_packet->setDataByName("show_name", appearance.display_name);
 		vis_packet->setDataByName("attackable", appearance.attackable);
+		if(appearance.attackable == 1)
+			vis_packet->setDataByName("attackable_icon", 1); 
 		if (IsPlayer()) {
 			if (((Player*)this)->IsGroupMember(player))
 				vis_packet->setDataByName("group_member", 1);
@@ -255,7 +261,7 @@ void Spawn::InitializeVisPacketData(Player* player, PacketStruct* vis_packet) {
 
 			if (appearance.attackable == 1)
 				arrow_color = player->GetArrowColor(GetLevel());
-			/*if (version <= 283) {
+			if (version <= 283) {
 				if (GetMerchantID() > 0)
 					arrow_color += 7;
 				else {
@@ -273,9 +279,12 @@ void Spawn::InitializeVisPacketData(Player* player, PacketStruct* vis_packet) {
 						}
 					}
 				}
-			}*/
+			}
 			vis_packet->setDataByName("arrow_color", arrow_color);
-			vis_packet->setDataByName("locked_no_loot", appearance.locked_no_loot);
+			if (appearance.attackable == 0)
+				vis_packet->setDataByName("locked_no_loot", 1);
+			else
+				vis_packet->setDataByName("locked_no_loot", appearance.locked_no_loot);
 			if (player->GetArrowColor(GetLevel()) == ARROW_COLOR_GRAY)
 				if (npc_con == -4)
 					npc_con = -3;
@@ -340,6 +349,9 @@ void Spawn::InitializeVisPacketData(Player* player, PacketStruct* vis_packet) {
 		if ((req_quests_override & 256) > 0)
 			vis_packet->setDataByName("hand_flag", 1);
 	}
+	if (version == 546 && GetMerchantID() > 0) {
+		vis_packet->setDataByName("guild", "<Merchant>");
+	}
 }
 
 void Spawn::InitializeFooterPacketData(Player* player, PacketStruct* footer) {
@@ -712,7 +724,7 @@ uchar* Spawn::spawn_vis_changes(Player* player, int16 version){
 		safe_delete(xor_vis_packet);
 		xor_vis_packet = player->SetTempVisPacketForXOR(size);
 	}
-	if(orig_packet){
+	if(orig_packet){		
 		memcpy(xor_vis_packet, (uchar*)data->c_str(), size);
 		Encode(xor_vis_packet, orig_packet, size);
 	}
@@ -1025,7 +1037,9 @@ uchar* Spawn::spawn_info_changes_ex(Player* player, int16 version) {
 
 	uchar* orig_packet = player->GetSpawnInfoPacketForXOR(id);
 
-	if (orig_packet) {
+	if (orig_packet) {		
+		//if (!IsPlayer() && this->EngagedInCombat())
+			//packet->PrintPacket();
 		memcpy(xor_info_packet, (uchar*)data->c_str(), size);
 		Encode(xor_info_packet, orig_packet, size);
 	}
@@ -1087,7 +1101,9 @@ uchar* Spawn::spawn_vis_changes_ex(Player* player, int16 version) {
 		xor_vis_packet = player->SetTempVisPacketForXOR(size);
 	}
 
-	if (orig_packet) {
+	if (orig_packet) {		
+		//if (!IsPlayer() && this->EngagedInCombat())
+		//	vis_struct->PrintPacket();
 		memcpy(xor_vis_packet, (uchar*)data->c_str(), size);
 		Encode(xor_vis_packet, orig_packet, size);
 	}
@@ -1151,6 +1167,8 @@ uchar* Spawn::spawn_pos_changes_ex(Player* player, int16 version) {
 	}
 
 	if (orig_packet) {
+		//if (!IsPlayer() && this->EngagedInCombat())
+		//	packet->PrintPacket();
 		memcpy(xor_pos_packet, (uchar*)data->c_str(), size);
 		Encode(xor_pos_packet, orig_packet, size);
 	}
@@ -2204,18 +2222,19 @@ void Spawn::InitializeInfoPacketData(Player* spawn, PacketStruct* packet) {
 		if (appearance.show_level == 0)
 			packet->setDataByName("hide_health", 1);
 	}
-	if (GetHP() <= 0 && IsEntity())
+	if (GetHP() <= 0 && IsEntity()) {
 		packet->setDataByName("corpse", 1);
+		packet->setDataByName("loot_icon", 1); 
+	}
 	if (!IsPlayer())
 		packet->setDataByName("npc", 1);
 	if (GetMerchantID() > 0)
-		packet->setDataByName("merchant", 1);
-	if (EngagedInCombat())
-		packet->setDataByName("in_combat", 1);
-
+		packet->setDataByName("merchant", 1);		
+	packet->setDataByName("effective_level", (int8)GetLevel());
 	packet->setDataByName("level", (int8)GetLevel());
 	packet->setDataByName("unknown4", (int8)GetLevel());
 	packet->setDataByName("difficulty", appearance.encounter_level); //6);
+	packet->setDataByName("unknown6", 1);
 	packet->setDataByName("heroic_flag", appearance.heroic_flag);
 	if (!IsObject() && !IsGroundSpawn() && !IsWidget() && !IsSign())
 		packet->setDataByName("interaction_flag", 12); //this makes NPCs head turn to look at you
@@ -2513,6 +2532,9 @@ void Spawn::InitializeInfoPacketData(Player* spawn, PacketStruct* packet) {
 		else
 			packet->setDataByName("follow_target", 0xFFFFFFFF);
 	}
+	else if (!IsPet()) {
+		packet->setDataByName("follow_target", 0xFFFFFFFF);
+	}
 	if (GetTarget() && GetTarget()->GetTargetable())
 		packet->setDataByName("target_id", ((spawn->GetIDWithPlayerSpawn(GetTarget()) * -1) - 1));
 	else

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

@@ -395,7 +395,7 @@ bool SpellProcess::DeleteCasterSpell(LuaSpell* spell, string reason){
 						string fade_message = spell->spell->GetSpellData()->fade_message;
 						if(fade_message.find("%t") != string::npos)
 							fade_message.replace(fade_message.find("%t"), 2, target->GetName());
-						client->Message(CHANNEL_COLOR_SPELL_FADE, fade_message.c_str());
+						client->Message(CHANNEL_SPELLS_OTHER, fade_message.c_str());
 					}
 				}
 			}
@@ -1398,7 +1398,7 @@ bool SpellProcess::CastProcessedSpell(LuaSpell* spell, bool passive){
 					string success_message = spell->spell->GetSpellData()->success_message;
 					if(success_message.find("%t") != string::npos)
 						success_message.replace(success_message.find("%t"), 2, spell->caster->GetName());
-					client->Message(CHANNEL_COLOR_SPELL, success_message.c_str());
+					client->Message(CHANNEL_SPELLS, success_message.c_str());
 				}
 			}
 			if(spell->spell->GetSpellData()->effect_message.length() > 0){
@@ -1407,7 +1407,7 @@ bool SpellProcess::CastProcessedSpell(LuaSpell* spell, bool passive){
 					effect_message.replace(effect_message.find("%t"), 2, target->GetName());
 				if (effect_message.find("%c") != string::npos)
 					effect_message.replace(effect_message.find("%c"), 2, spell->caster->GetName());
-				spell->caster->GetZone()->SimpleMessage(CHANNEL_COLOR_SPELL_EFFECT, effect_message.c_str(), target, 50);
+				spell->caster->GetZone()->SimpleMessage(CHANNEL_SPELLS_OTHER, effect_message.c_str(), target, 50);
 			}
 			target->GetZone()->CallSpawnScript(target, SPAWN_SCRIPT_CASTED_ON, spell->caster, spell->spell->GetName());
 		}
@@ -1623,7 +1623,7 @@ void SpellProcess::Interrupted(Entity* caster, Spawn* interruptor, int16 error_c
 			if(interruptor && interruptor->IsPlayer())
 			{
 				client = interruptor->GetZone()->GetClientBySpawn(interruptor);
-				client->Message(CHANNEL_COLOR_SPELL_INTERRUPT, "You interrupt %s's ability to cast!", interruptor->GetName());
+				client->Message(CHANNEL_SPELLS_OTHER, "You interrupt %s's ability to cast!", interruptor->GetName());
 			}
 			
 		}

+ 3 - 3
EQ2/source/WorldServer/Tradeskills/Tradeskills.cpp

@@ -368,7 +368,7 @@ void TradeskillMgr::StopCrafting(Client* client, bool lock) {
 	else {
 		item->details.count = qty;
 		// use CHANNEL_COLOR_CHAT_RELATIONSHIP as that is the same value (4) as it is in a log for this message
-		client->Message(CHANNEL_COLOR_CHAT_RELATIONSHIP, "You created \\aITEM %u 0:%s\\/a.", item->details.item_id, item->name.c_str());
+		client->Message(CHANNEL_COLOR_CHAT_RELATIONSHIP, "You created %s.", item->CreateItemLink(client->GetVersion()).c_str());
 		client->AddItem(item);
 		//Check for crafting quest updates
 		int8 update_amt = 0;
@@ -383,7 +383,7 @@ void TradeskillMgr::StopCrafting(Client* client, bool lock) {
 	if (xp > 0) {
 		int16 level = client->GetPlayer()->GetTSLevel();
 		if (client->GetPlayer()->AddTSXP((int32)xp)) {
-			client->Message(CHANNEL_COLOR_EXP, "You gain %u Tradeskill XP!", (int32)xp);
+			client->Message(CHANNEL_REWARD, "You gain %u Tradeskill XP!", (int32)xp);
 			LogWrite(PLAYER__DEBUG, 0, "Player", "Player: %s earned %u tradeskill experience.", client->GetPlayer()->GetName(), (int32)xp);
 			if(client->GetPlayer()->GetTSLevel() != level)
 				client->ChangeTSLevel(level, client->GetPlayer()->GetTSLevel());
@@ -431,7 +431,7 @@ void TradeskillMgr::CheckTradeskillEvent(Client* client, int16 icon) {
 	tradeskillList[client]->eventCountered = countered;
 
 	// send the success or fail message to the client
-	client->Message(CHANNEL_COLOR_WHITE, "You %s %s.", countered ? "successfully countered" : "failed to counter", tradeskillList[client]->CurrentEvent->Name);
+	client->Message(CHANNEL_NARRATIVE, "You %s %s.", countered ? "successfully countered" : "failed to counter", tradeskillList[client]->CurrentEvent->Name);
 	
 	// unlock the list and send the result packet
 	m_tradeskills.releasewritelock(__FUNCTION__, __LINE__);

+ 3 - 3
EQ2/source/WorldServer/Transmute.cpp

@@ -221,7 +221,7 @@ void Transmute::CompleteTransmutation(Client* client, Player* player) {
 		if (item2) item2 = new Item(item2);
 	}
 
-	client->Message(89, "You transmute %s and create: ", item->CreateItemLink(false).c_str());
+	client->Message(89, "You transmute %s and create: ", item->CreateItemLink(GetVersion(), false).c_str());
 
 	player->item_list.RemoveItem(item, true);
 
@@ -232,7 +232,7 @@ void Transmute::CompleteTransmutation(Client* client, Player* player) {
 
 	if (item1) {
 		item1->details.count = 1;
-		client->Message(89, "     %s", item1->CreateItemLink(false).c_str());
+		client->Message(89, "     %s", item1->CreateItemLink(GetVersion(), false).c_str());
 		client->AddItem(item1);
 
 		if (packet) {
@@ -248,7 +248,7 @@ void Transmute::CompleteTransmutation(Client* client, Player* player) {
 
 	if (item2) {
 		item2->details.count = 1;
-		client->Message(89, "     %s", item2->CreateItemLink(false).c_str());
+		client->Message(89, "     %s", item2->CreateItemLink(GetVersion(), false).c_str());
 		client->AddItem(item2);
 
 		if (packet) {

+ 6 - 41
EQ2/source/WorldServer/World.cpp

@@ -409,7 +409,7 @@ bool ZoneList::HandleGlobalChatMessage(Client* from, char* to, int16 channel, co
 		return false;
 	}
 
-	if(channel == CHANNEL_TELL){
+	if(channel == CHANNEL_PRIVATE_TELL){
 		Client* find_client = zone_list.GetClientByCharName(to);
 		if(!find_client || find_client->GetPlayer()->IsIgnored(from->GetPlayer()->GetName()))
 			return false;
@@ -419,50 +419,15 @@ bool ZoneList::HandleGlobalChatMessage(Client* from, char* to, int16 channel, co
 		}
 		else
 		{
-			PacketStruct* packet = configReader.getStruct("WS_HearChat", from->GetVersion());
-			if(packet){
-				packet->setMediumStringByName("from", from->GetPlayer()->GetName());
-				packet->setMediumStringByName("to", find_client->GetPlayer()->GetName());
-				packet->setDataByName("channel", CHANNEL_TELL);
-				packet->setDataByName("from_spawn_id", 0xFFFFFFFF);
-				packet->setDataByName("to_spawn_id", 0xFFFFFFFF);
-				packet->setDataByName("unknown2", 1, 1);
-				packet->setDataByName("show_bubble", 1);
-				packet->setDataByName("understood", 1);
-				packet->setDataByName("time", 2); 
-				packet->setMediumStringByName("message", message);
-				if(channel_name)
-					packet->setMediumStringByName("channel_name", channel_name);
-				EQ2Packet* outpacket = packet->serialize();
-				//DumpPacket(outpacket);
-				find_client->QueuePacket(outpacket->Copy());
-				from->QueuePacket(outpacket);
-				safe_delete(packet);
-			}
+			find_client->HandleTellMessage(from, message);
+			from->HandleTellMessage(from, message);			
 			if (find_client->GetPlayer()->get_character_flag(CF_AFK)) {
-				PacketStruct* packet2 = configReader.getStruct("WS_HearChat", from->GetVersion());
-				if (packet2) {
-					packet2->setMediumStringByName("from", find_client->GetPlayer()->GetName());
-					packet2->setMediumStringByName("to", from->GetPlayer()->GetName());
-					packet2->setDataByName("channel", CHANNEL_TELL);
-					packet2->setDataByName("from_spawn_id", 0xFFFFFFFF);
-					packet2->setDataByName("to_spawn_id", 0xFFFFFFFF);
-					packet2->setDataByName("unknown2", 1, 1);
-					packet2->setDataByName("show_bubble", 1);
-					packet2->setDataByName("understood", 1);
-					packet2->setDataByName("time", 2);
-					packet2->setMediumStringByName("message", find_client->GetPlayer()->GetAwayMessage().c_str());
-					if (channel_name)
-						packet2->setMediumStringByName("channel_name", channel_name);
-					EQ2Packet* outpacket = packet2->serialize();
-					from->QueuePacket(outpacket->Copy());
-					find_client->QueuePacket(outpacket);
-					safe_delete(packet2);
-				}
+				find_client->HandleTellMessage(find_client, find_client->GetPlayer()->GetAwayMessage().c_str());
+				from->HandleTellMessage(find_client, find_client->GetPlayer()->GetAwayMessage().c_str());
 			}
 		}
 	}
-	else if(channel == CHANNEL_GROUP) {
+	else if(channel == CHANNEL_GROUP_SAY) {
 		GroupMemberInfo* gmi = from->GetPlayer()->GetGroupMemberInfo();
 		if(gmi)
 			world.GetGroupManager()->GroupMessage(gmi->group_id, message);

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

@@ -4039,7 +4039,7 @@ void WorldDatabase::LoadPlayerMail(Client* client, bool new_only) {
 			result = query.RunQuery2(Q_SELECT, "SELECT `id`, `player_to_id`, `player_from`, `subject`, `mail_body`, `already_read`, `mail_type`, `coin_copper`, `coin_silver`, `coin_gold`, `coin_plat`, `stack`, `postage_cost`, `attachment_cost`, `char_item_id`, `time_sent`, `expire_time` FROM `character_mail` WHERE `player_to_id`=%u", client->GetCharacterID());
 		if (result && mysql_num_rows(result) > 0) {
 			MYSQL_ROW row;
-			client->SimpleMessage(CHANNEL_COLOR_MAIL, "You've got mail! :)");
+			client->SimpleMessage(CHANNEL_NARRATIVE, "You've got mail! :)");
 			while (result && (row = mysql_fetch_row(result))) {
 				Mail* mail = new Mail;
 				mail->mail_id = atoul(row[0]);

Файловите разлики са ограничени, защото са твърде много
+ 434 - 118
EQ2/source/WorldServer/client.cpp


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

@@ -151,6 +151,8 @@ public:
 	void	SetReadyForSpawns(bool val);
 	void	QueuePacket(EQ2Packet* app, bool attemptedCombine=false);
 	void	SendLoginInfo();
+	int8	GetMessageChannelColor(int8 channel_type);
+	void	HandleTellMessage(Client* from, const char* message);
 	void	SimpleMessage(int8 color, const char* message);
 	void	Message(int8 type, const char* message, ...);
 	void	SendSpellUpdate(Spell* spell);
@@ -242,7 +244,7 @@ public:
 	bool	UpdateQuickbarNeeded();
 	void	Save();
 	bool	remove_from_list;
-	void	CloseLoot();
+	void	CloseLoot(int32 spawn_id);
 	void	SendPendingLoot(int32 total_coins, Spawn* entity);
 	void	Loot(int32 total_coins, vector<Item*>* items, Spawn* entity);
 	void	Loot(Spawn* entity, bool attemptDisarm=true);
@@ -253,18 +255,19 @@ public:
 	void	CheckPlayerQuestsItemUpdate(Item* item);
 	void	CheckPlayerQuestsSpellUpdate(Spell* spell);
 	void	CheckPlayerQuestsLocationUpdate();
-	void	AddPendingQuest(Quest* quest);
+	void	AddPendingQuest(Quest* quest, bool forced = false);
 	void	AcceptQuest(int32 id);
 	Quest*	GetPendingQuest(int32 id);
 	void	RemovePendingQuest(Quest* quest);
 	void	SetPlayerQuest(Quest* quest, map<int32, int32>* progress);
 	void	AddPlayerQuest(Quest* quest, bool call_accepted = true, bool send_packets = true);
 	void	RemovePlayerQuest(int32 id, bool send_update = true, bool delete_quest = true);
-	void	SendQuestJournal(bool all_quests = false, Client* client = 0);
+	void	SendQuestJournal(bool all_quests = false, Client* client = 0, bool updated = true);
 	void	SendQuestUpdate(Quest* quest);
 	void	SendQuestFailure(Quest* quest);
 	void	SendQuestUpdateStep(Quest* quest, int32 step, bool display_quest_helper = true);
 	void	SendQuestUpdateStepImmediately(Quest* quest, int32 step, bool display_quest_helper = true);
+	void	DisplayQuestRewards(Quest* quest, int64 coin, vector<Item*>* rewards=0, vector<Item*>* selectable_rewards=0, map<int32, sint32>* factions=0, const char* header="Quest Reward!", int32 status_points=0, const char* text=0);
 	void	DisplayQuestComplete(Quest* quest);
 	void	DisplayRandomizeFeatures(int32 features);
 	void	AcceptQuestReward(Quest* quest, int32 item_id);
@@ -329,6 +332,7 @@ public:
 	void	SearchStore(int32 page);
 	void	SetPlayer(Player* new_player){
 		player = new_player;
+		player->SetClient(this);
 	}
 	void	AddPendingQuestReward(Quest* quest);
 	void	AddPendingQuestUpdate(int32 quest_id, int32 step_id, int32 progress = 0xFFFFFFFF);
@@ -379,7 +383,7 @@ public:
 
 	bool GetInitialSpawnsSent() { return initial_spawns_sent; }
 
-	void SendQuestJournalUpdate(Quest* quest);
+	void SendQuestJournalUpdate(Quest* quest, bool updated=true);
 
 	void AddQuestTimer(int32 quest_id);
 

+ 42 - 54
EQ2/source/WorldServer/zoneserver.cpp

@@ -1892,7 +1892,7 @@ void ZoneServer::ProcessDrowning(){
 		for(itr = dead_list.begin(); itr != dead_list.end(); itr++){
 			RemoveDrowningVictim((*itr)->GetPlayer());
 			KillSpawn(false, (*itr)->GetPlayer(), 0);
-			(*itr)->SimpleMessage(CHANNEL_COLOR_WHITE, "You are sleeping with the fishes!  Glug, glug...");
+			(*itr)->SimpleMessage(CHANNEL_NARRATIVE, "You are sleeping with the fishes!  Glug, glug...");
 		}
 	}
 }
@@ -3174,43 +3174,15 @@ void ZoneServer::SimpleMessage(int8 type, const char* message, Spawn* from, floa
 	MClientList.releasereadlock(__FUNCTION__, __LINE__);
 }
 
-void ZoneServer::HandleChatMessage(Client* client, Spawn* from, const char* to, int16 channel, const char* message, float distance, const char* channel_name, bool show_bubble, int32 language) {
+void ZoneServer::HandleChatMessage(Client* client, Spawn* from, const char* to, int16 channel, const char* message, float distance, const char* channel_name, bool show_bubble, int32 language) {	
 	if ((!distance || from->GetDistance(client->GetPlayer()) <= distance) && (!from || !client->GetPlayer()->IsIgnored(from->GetName()))) {
-		if (client->GetVersion() <= 283) {
-			switch (channel) {
-				case CHANNEL_GROUP: {
-					channel = CLASSIC_CLIENT_CHANNEL_GROUP;
-					break;
-				}
-				case CHANNEL_RAID: {
-					channel = CLASSIC_CLIENT_CHANNEL_RAID;
-					break;
-				}
-				case CHANNEL_GUILD: {
-					channel = CLASSIC_CLIENT_CHANNEL_GUILD;
-					break;
-				}
-				case CHANNEL_SAYTARGET: {
-					channel = CLASSIC_CLIENT_CHANNEL_SAYTARGET;
-					break;
-				}
-				case CHANNEL_TELL: {
-					channel = CLASSIC_CLIENT_CHANNEL_TELL;
-					break;
-				}
-				case CHANNEL_OOC: {
-					channel = CLASSIC_CLIENT_CHANNEL_OOC;
-					break;
-				}
-			}
-		}
 		PacketStruct* packet = configReader.getStruct("WS_HearChat", client->GetVersion());
 		if (packet) {
 			if (from)
 				packet->setMediumStringByName("from", from->GetName());
 			if (client->GetPlayer() != from)
 				packet->setMediumStringByName("to", client->GetPlayer()->GetName());
-			packet->setDataByName("channel", channel);
+			packet->setDataByName("channel", client->GetMessageChannelColor(channel));
 			if (from && ((from == client->GetPlayer()) || (client->GetPlayer()->WasSentSpawn(from->GetID()) && !client->GetPlayer()->WasSpawnRemoved(from))))
 				packet->setDataByName("from_spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(from));
 			else
@@ -3988,7 +3960,7 @@ Spawn* ZoneServer::GetClosestSpawn(Spawn* spawn, int32 spawn_id){
 	MSpawnList.readlock(__FUNCTION__, __LINE__);
 	for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
 		test_spawn = itr->second;
-		if(test_spawn && test_spawn->GetDatabaseID() == spawn_id){
+		if(test_spawn && test_spawn != spawn && test_spawn->GetDatabaseID() == spawn_id){
 			test_distance = test_spawn->GetDistance(spawn);
 			if(test_distance < closest_distance){
 				closest_distance = test_distance;
@@ -4141,7 +4113,7 @@ void ZoneServer::SendCalculatedXP(Player* player, Spawn* victim){
 						if (xp > 0) {
 							int16 level = group_member->GetLevel();
 							if (group_member->AddXP((int32)xp)) {
-								gmi->client->Message(CHANNEL_COLOR_EXP, "You gain %u experience!", (int32)xp);
+								gmi->client->Message(CHANNEL_REWARD, "You gain %u experience!", (int32)xp);
 								LogWrite(PLAYER__DEBUG, 0, "Player", "Player: %s earned %u experience (GroupID %u)", group_member->GetName(), (int32)xp, player->GetGroupMemberInfo()->group_id);
 								if (group_member->GetLevel() != level)
 									gmi->client->ChangeLevel(level, group_member->GetLevel());
@@ -4163,7 +4135,7 @@ void ZoneServer::SendCalculatedXP(Player* player, Spawn* victim){
 					return;
 				int16 level = player->GetLevel();
 				if (player->AddXP((int32)xp)) {
-					client->Message(CHANNEL_COLOR_EXP, "You gain %u XP!", (int32)xp);
+					client->Message(CHANNEL_REWARD, "You gain %u XP!", (int32)xp);
 					LogWrite(PLAYER__DEBUG, 0, "Player", "Player: %s earned %u experience.", player->GetName(), (int32)xp);
 					if(player->GetLevel() != level)
 						client->ChangeLevel(level, player->GetLevel());
@@ -4189,9 +4161,9 @@ void ZoneServer::ProcessFaction(Spawn* spawn, Client* client)
 			faction = master_faction_list.GetFaction(spawn->GetFactionID());
 
 			if(faction && update_result)
-				client->Message(CHANNEL_COLOR_FACTION, "Your faction standing with %s got worse.", faction->name.c_str());
+				client->Message(CHANNEL_FACTION, "Your faction standing with %s got worse.", faction->name.c_str());
 			else if(faction)
-				client->Message(CHANNEL_COLOR_FACTION, "Your faction standing with %s could not possibly get any worse.", faction->name.c_str());
+				client->Message(CHANNEL_FACTION, "Your faction standing with %s could not possibly get any worse.", faction->name.c_str());
 
 			factions = master_faction_list.GetHostileFactions(spawn->GetFactionID());
 
@@ -4207,9 +4179,9 @@ void ZoneServer::ProcessFaction(Spawn* spawn, Client* client)
 						faction = master_faction_list.GetFaction(*itr);
 
 						if(faction && update_result)
-							client->Message(CHANNEL_COLOR_FACTION, "Your faction standing with %s got better.", faction->name.c_str());
+							client->Message(CHANNEL_FACTION, "Your faction standing with %s got better.", faction->name.c_str());
 						else if(faction)
-							client->Message(CHANNEL_COLOR_FACTION, "Your faction standing with %s could not possibly get any better.", faction->name.c_str());
+							client->Message(CHANNEL_FACTION, "Your faction standing with %s could not possibly get any better.", faction->name.c_str());
 					}
 				}
 			}
@@ -4229,9 +4201,9 @@ void ZoneServer::ProcessFaction(Spawn* spawn, Client* client)
 					faction = master_faction_list.GetFaction(*itr);
 
 					if(faction && update_result)
-						client->Message(CHANNEL_COLOR_FACTION, "Your faction standing with %s got worse.", faction->name.c_str());
+						client->Message(CHANNEL_FACTION, "Your faction standing with %s got worse.", faction->name.c_str());
 					else if(faction)
-						client->Message(CHANNEL_COLOR_FACTION, "Your faction standing with %s could not possibly get any worse.", faction->name.c_str());
+						client->Message(CHANNEL_FACTION, "Your faction standing with %s could not possibly get any worse.", faction->name.c_str());
 				}
 			}
 		}
@@ -4374,7 +4346,7 @@ void ZoneServer::KillSpawn(bool spawnListLocked, Spawn* dead, Spawn* killer, boo
 						if (xp > 0) {
 							int16 level = spawn->GetLevel();
 							if (((Player*)spawn)->AddXP((int32)xp)) {
-								client->Message(CHANNEL_COLOR_EXP, "You gain %u XP!", (int32)xp);
+								client->Message(CHANNEL_REWARD, "You gain %u XP!", (int32)xp);
 								LogWrite(PLAYER__DEBUG, 0, "Player", "Player: %s earned %u experience.", spawn->GetName(), (int32)xp);
 								if (spawn->GetLevel() != level)
 									client->ChangeLevel(level, spawn->GetLevel());
@@ -4449,6 +4421,9 @@ void ZoneServer::KillSpawn(bool spawnListLocked, Spawn* dead, Spawn* killer, boo
 
 		// Call the spawn scripts death() function
 		CallSpawnScript(dead, SPAWN_SCRIPT_DEATH, killer);
+		const char* zone_script = world.GetZoneScript(this->GetZoneID());
+		if (zone_script && lua_interface)
+			lua_interface->RunZoneScript(zone_script, "spawn_killed", this, dead, 0, 0, killer);
 	}
 	
 	int32 victim_id = dead->GetID();
@@ -4797,7 +4772,7 @@ void ZoneServer::SendCastSpellPacket(LuaSpell* spell, Entity* caster){
 	safe_delete(packet);
 }
 
-void ZoneServer::SendCastSpellPacket(int32 spell_visual, Spawn* target) {
+void ZoneServer::SendCastSpellPacket(int32 spell_visual, Spawn* target, Spawn* caster) {
 	if (target) {
 		vector<Client*>::iterator client_itr;
 
@@ -4808,15 +4783,22 @@ void ZoneServer::SendCastSpellPacket(int32 spell_visual, Spawn* target) {
 				continue;
 			PacketStruct* packet = configReader.getStruct("WS_HearCastSpell", client->GetVersion());
 			if (packet) {
-				packet->setDataByName("spawn_id", 0xFFFFFFFF);
+				if (!caster) {
+					packet->setDataByName("spawn_id", 0xFFFFFFFF);
+					packet->setDataByName("invoker_id", 0xFFFFFFFF);
+				}
+				else {
+					int32 caster_id = client->GetPlayer()->GetIDWithPlayerSpawn(caster);
+					packet->setDataByName("spawn_id", caster_id);
+					packet->setDataByName("invoker_id", caster_id);
+				}
 				packet->setArrayLengthByName("num_targets", 1);
 				packet->setArrayDataByName("target", client->GetPlayer()->GetIDWithPlayerSpawn(target));
-				packet->setDataByName("spell_id", 0xFFFFFFFF);
 				packet->setDataByName("spell_visual", spell_visual);
 				packet->setDataByName("cast_time", 0);
 				packet->setDataByName("spell_id", 0);
 				packet->setDataByName("spell_level", 0);
-				packet->setDataByName("spell_tier", 0);
+				packet->setDataByName("spell_tier", 1);
 				client->QueuePacket(packet->serialize());
 				safe_delete(packet);
 			}
@@ -5623,6 +5605,12 @@ void ZoneServer::ProcessSpell(Spell* spell, Entity* caster, Spawn* target, bool
 }
 
 void ZoneServer::ProcessEntityCommand(EntityCommand* entity_command, Entity* caster, Spawn* target, bool lock) {
+	if (target && target->GetSpawnScript()) {
+		Player* player = 0;
+		if (caster && caster->IsPlayer())
+			player = (Player*)caster;
+		CallSpawnScript(target, SPAWN_SCRIPT_CUSTOM, player, entity_command->command.c_str());
+	}
 	if (spellProcess)
 		spellProcess->ProcessEntityCommand(this, entity_command, caster, target, lock);
 }
@@ -5885,9 +5873,9 @@ void ZoneServer::FindSpawn(Client* client, char* regSearchStr)
 		client->SimpleMessage(CHANNEL_COLOR_RED, "Try/Catch ZoneServer::FindSpawn(Client*, char* regSearchStr) failure.");
 		return;
 	}
-	client->Message(CHANNEL_COLOR_WHITE, "RegEx Search Spawn List: %s", regSearchStr);
-	client->Message(CHANNEL_COLOR_WHITE, "Database ID | Spawn Name | X , Y , Z");
-	client->Message(CHANNEL_COLOR_WHITE, "========================");
+	client->Message(CHANNEL_NARRATIVE, "RegEx Search Spawn List: %s", regSearchStr);
+	client->Message(CHANNEL_NARRATIVE, "Database ID | Spawn Name | X , Y , Z");
+	client->Message(CHANNEL_NARRATIVE, "========================");
 	map<int32, Spawn*>::iterator itr;
 	MSpawnList.readlock(__FUNCTION__, __LINE__);
 	int32 spawnsFound = 0;
@@ -5907,12 +5895,12 @@ void ZoneServer::FindSpawn(Client* client, char* regSearchStr)
 
 		if (output)
 		{
-			client->Message(CHANNEL_COLOR_WHITE, "%i | %s | %f , %f , %f", spawn->GetDatabaseID(), spawn->GetName(), spawn->GetX(), spawn->GetY(), spawn->GetZ());
+			client->Message(CHANNEL_NARRATIVE, "%i | %s | %f , %f , %f", spawn->GetDatabaseID(), spawn->GetName(), spawn->GetX(), spawn->GetY(), spawn->GetZ());
 			spawnsFound++;
 		}
 	}
-	client->Message(CHANNEL_COLOR_WHITE, "========================", spawnsFound);
-	client->Message(CHANNEL_COLOR_WHITE, "%u Results Found.", spawnsFound);
+	client->Message(CHANNEL_NARRATIVE, "========================", spawnsFound);
+	client->Message(CHANNEL_NARRATIVE, "%u Results Found.", spawnsFound);
 	MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
 }
 
@@ -6169,11 +6157,11 @@ void ZoneServer::SetRain(float val) {
 		client->GetPlayer()->SetCharSheetChanged(true);
 		if( val >= 0.75 && !weather_signaled )
 		{
-			client->SimpleMessage(CHANNEL_COLOR_WHITE, "It starts to rain.");
+			client->SimpleMessage(CHANNEL_NARRATIVE, "It starts to rain.");
 		}
 		else if( val < 0.75 && weather_signaled ) 
 		{
-			client->SimpleMessage(CHANNEL_COLOR_WHITE, "It stops raining.");
+			client->SimpleMessage(CHANNEL_NARRATIVE, "It stops raining.");
 		}
 	}
 	MClientList.releasereadlock(__FUNCTION__, __LINE__);
@@ -6526,7 +6514,7 @@ void ZoneServer::ResurrectSpawn(Spawn* spawn, Client* client) {
 			}
 
 			safe_delete(packet);
-			client->SimpleMessage(CHANNEL_COLOR_REVIVE, "You regain consciousness!");
+			client->SimpleMessage(CHANNEL_NARRATIVE, "You regain consciousness!");
 		}
 	}
 	spawn->SendSpawnChanges(true);

+ 1 - 1
EQ2/source/WorldServer/zoneserver.h

@@ -311,7 +311,7 @@ public:
 	void    SendHealPacket(Spawn* caster, Spawn* target, int16 type, int32 heal_amt, const char* spell_name);
 	
 	void	SendCastSpellPacket(LuaSpell* spell, Entity* caster);
-	void	SendCastSpellPacket(int32 spell_visual, Spawn* target);
+	void	SendCastSpellPacket(int32 spell_visual, Spawn* target, Spawn* caster = 0);
 	void	SendCastEntityCommandPacket(EntityCommand* entity_command, int32 spawn_id, int32 target_id);
 	void	TriggerCharSheetTimer();
 	

+ 1 - 1
EQ2/source/common/ConfigReader.cpp

@@ -78,7 +78,7 @@ PacketStruct* ConfigReader::getStruct(const char* name, int16 version){
 				latest_version = *iter;
 		}		
 		if (latest_version) {
-			if (latest_version->GetOpcode() != OP_Unknown && latest_version->GetOpcodeValue(version) == 0xFFFF) {
+			if (latest_version->GetOpcode() != OP_Unknown && (latest_version->GetOpcodeValue(version) == 0xFFFF || latest_version->GetOpcodeValue(version)==0xCDCD)) {
 				LogWrite(PACKET__ERROR, 0, "Packet", "Could not find valid opcode for Packet Struct '%s' and client version %d", latest_version->GetName(), version);
 			}
 			else if(strlen(latest_version->GetOpcodeType()) == 0 || latest_version->GetOpcode() != OP_Unknown)

+ 17 - 9
EQ2/source/common/PacketStruct.cpp

@@ -1565,28 +1565,36 @@ int32 PacketStruct::GetArraySizeByName(const char* name, int32 index) {
 int16 PacketStruct::GetOpcodeValue(int16 client_version) {
 	int16 opcode = 0xFFFF;
 	bool client_cmd = false;
+	int16 OpcodeVersion = 0;
 #ifndef LOGIN
 	if (GetOpcode() == OP_ClientCmdMsg && strlen(GetOpcodeType()) > 0 && !IsSubPacket())
 		client_cmd = true;
 #endif
 	if (client_cmd) {
 		EmuOpcode sub_opcode = EQOpcodeManager[0]->NameSearch(GetOpcodeType());
-		int16 opcode_val = 0;
 		if (sub_opcode != OP_Unknown) { //numbers should be used at OpcodeTypes, define them!
-			int16 OpcodeVersion = GetOpcodeVersion(client_version);
-			if(EQOpcodeManager.count(OpcodeVersion) > 0)
+			OpcodeVersion = GetOpcodeVersion(client_version);
+			if (EQOpcodeManager.count(OpcodeVersion) > 0) {
 				opcode = EQOpcodeManager[OpcodeVersion]->EmuToEQ(sub_opcode);
+				if (opcode == 0xCDCD) {
+					LogWrite(PACKET__ERROR, 0, "Packet", "Could not find valid opcode for opcode: %s and client_version: %i", EQOpcodeManager[OpcodeVersion]->EmuToName(sub_opcode), client_version);
+				}
+			}
 		}		
 	}
 	else {
-		int16 OpcodeVersion = GetOpcodeVersion(client_version);
-		if (EQOpcodeManager.count(OpcodeVersion) > 0)
+		OpcodeVersion = GetOpcodeVersion(client_version);
+		if (EQOpcodeManager.count(OpcodeVersion) > 0) {
 			opcode = EQOpcodeManager[OpcodeVersion]->EmuToEQ(GetOpcode());
+			if (opcode == 0xCDCD) {
+				LogWrite(PACKET__ERROR, 0, "Packet", "Could not find valid opcode for opcode: %s and client_version: %i", EQOpcodeManager[OpcodeVersion]->EmuToName(GetOpcode()), client_version);
+			}
+		}
 	}
 #ifndef LOGIN
 	if(opcode == 0)
 		opcode = 0xFFFF;
-#endif
+#endif	
 	return opcode;
 }
 
@@ -1754,7 +1762,7 @@ void PacketStruct::serializePacket(bool clear) {
 	}
 #ifndef LOGIN
 	if (client_cmd) {
-		int16 opcode_val = GetOpcodeValue(client_version);		
+		int16 opcode_val = GetOpcodeValue(client_version);			
 		Clear();
 		int32 size = client_data.length() + 3; //gotta add the opcode and oversized
 		int8 oversized = 0xFF;
@@ -2541,8 +2549,8 @@ void PacketStruct::setItem(DataStruct* ds, Item* item, Player* player, int32 ind
 		//DumpPacket((uchar*)generic_string_data->c_str() + (9 + offset), size);
 		//without these it will prompt for your character name
 		if (offset == 0 || offset == -1 || offset == 2) {
-			if (client_version <= 546 && item->stack_count > 0)
-				out_data[0] = item->stack_count;
+			if (client_version <= 546 && item->details.count > 0)
+				out_data[0] = item->details.count;
 			else
 				out_data[0] = 1;
 		}

+ 2 - 1
EQ2/win/EQ2WorldVC10.sln

@@ -16,7 +16,8 @@ Global
 		Release|x64 = Release|x64
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		{FC063D75-089D-4470-B255-032A80DE3CCE}.Debug|Win32.ActiveCfg = Release|x64
+		{FC063D75-089D-4470-B255-032A80DE3CCE}.Debug|Win32.ActiveCfg = Debug|x64
+		{FC063D75-089D-4470-B255-032A80DE3CCE}.Debug|Win32.Build.0 = Debug|x64
 		{FC063D75-089D-4470-B255-032A80DE3CCE}.Debug|x64.ActiveCfg = Debug|x64
 		{FC063D75-089D-4470-B255-032A80DE3CCE}.Debug|x64.Build.0 = Debug|x64
 		{FC063D75-089D-4470-B255-032A80DE3CCE}.EQ2Login|Win32.ActiveCfg = Debug|x64

+ 3 - 2
EQ2/win/VC10Projects/EQ2World.vcxproj

@@ -44,7 +44,7 @@
   <PropertyGroup Label="UserMacros" />
   <PropertyGroup>
     <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
-    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(SolutionDir)worldserver\</OutDir>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">E:\EQ2EMu\my_server\</OutDir>
     <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\$(ProjectName)__Debug_$(Platform)\</IntDir>
     <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</LinkIncremental>
     <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\..\..\bin\</OutDir>
@@ -76,7 +76,7 @@
     </Midl>
     <ClCompile>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>WORLD;_DEBUG;_EQDEBUG;WIN32;_CONSOLE;EQ2;EQN_DEBUG;_CRT_SECURE_NO_DEPRECATE;GLM_ENABLE_EXPERIMENTAL;GLM_FORCE_CTOR_INIT;GLM_FORCE_RADIANS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>WRITE_PACKETS;WORLD;_DEBUG;_EQDEBUG;WIN32;_CONSOLE;EQ2;EQN_DEBUG;_CRT_SECURE_NO_DEPRECATE;GLM_ENABLE_EXPERIMENTAL;GLM_FORCE_CTOR_INIT;GLM_FORCE_RADIANS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <ExceptionHandling>Sync</ExceptionHandling>
       <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
       <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
@@ -99,6 +99,7 @@
       <PrecompiledHeaderFile>
       </PrecompiledHeaderFile>
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
     </ClCompile>
     <ResourceCompile>
       <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>

+ 25 - 28
server/Quests/PeatBog/Reinforcements.lua

@@ -1,42 +1,39 @@
 --[[
-	Script Name		:	Reinforcements.lua
-	Script Purpose	:	Handles the quest, "Reinforcements"
-	Script Author	:	Shatou
-	Script Date		:	1/8/2020
-	Script Notes	:	
-
-	Zone			:	Caves
-	Quest Giver		:	Lieutenant Dawson
-	Preceded by		:	A Final Foe
-	Followed by		:	A Lack of Information (in the Caves)
+	Script Name	: Quests/PeatBog/reinforcements.lua
+	Script Purpose	: Handles the quest, "Reinforcements"
+	Script Author	: Scatman
+	Script Date	: 2009.05.11
+	
+	Zone       : The Peat Bog
+	Quest Giver: Lieutenant Dawson
+	Preceded by: A Final Foe (a_final_foe.lua)
+	Followed by: None
 --]]
 
-local CONSUL_BREE_ID = 1970004
 
 function Init(Quest)
-	AddQuestStepChat(Quest, 1, "I must report to Consul Bree in The Caves.", 1, "I must report to Consul Bree in The Caves to help in the defense of Qeynos.", 11, CONSUL_BREE_ID)
-	AddQuestStepCompleteAction(Quest, 1, "QuestComplete")
-end
 
-function QuestComplete(Quest, QuestGiver, Player)
-	-- The following UpdateQuestStepDescription and UpdateTaskGroupDescription are not needed, parser adds them for completion in case stuff needs to be moved around
-	UpdateQuestStepDescription(Quest, 1, "I have spoken with Consul Bree.")
-	UpdateQuestTaskGroupDescription(Quest, 1, "I have spoken with Consul Bree.")
 
-	UpdateQuestDescription(Quest, "I have spoken with Consul Bree.")
-	GiveQuestReward(Quest, Player)
-end
+	AddQuestStepChat(Quest, 1, "I must report to Consul Bree in The Caves.", 1, "I must report to Consul Bree in The Caves to help in the defense of Qeynos.", 0, 1970010)
+	AddQuestStepCompleteAction(Quest, 1, "QuestComplete")
 
-function Reload(Quest, QuestGiver, Player, Step)
-	if Step == 1 then
-		QuestComplete(Quest, QuestGiver, Player)
-	end
 end
 
 function Accepted(Quest, QuestGiver, Player)
-	-- Add dialog here for when the quest is accepted
+	FaceTarget(QuestGiver, Player)
+	conversation = CreateConversation()
+	
+	AddConversationOption(conversation, "I can do this.")
+	StartConversation(conversation, QuestGiver, Player, "I know you can. Qeynos is counting on you, " .. GetName(Player) .. ". Make us proud!")
 end
 
 function Declined(Quest, QuestGiver, Player)
-	-- Add dialog here for when the quest is declined
-end
+end
+
+function QuestComplete(Quest, QuestGiver, Player)
+	UpdateQuestDescription(Quest, "I have spoken with Consul Bree.")
+	GiveQuestReward(Quest, Player)
+end
+
+function Reload(Quest, QuestGiver, Player, Step)
+end

+ 50 - 63
server/Quests/PeatBog/ambushed.lua

@@ -1,93 +1,80 @@
 --[[
-	Script Name	: Quests/PeatBog/ambushed.lua
-	Script Purpose	: Handles the quest, "Ambushed"
-	Script Author	: Scatman
-	Script Date	: 2009.05.10
-	
-	Zone       : The Peat Bog
-	Quest Giver: Lieutenant Dawson
-	Preceded by: Mysterious Machine (mysterious_machine.lua)
-	Followed by: On the Move (on_the_move.lua)
+	Script Name		:	Ambushed.lua
+	Script Purpose	:	Handles the quest, "Ambushed"
+	Script Author	:	Shatou
+	Script Date		:	1/8/2020
+	Script Notes	:	
+
+	Zone			:	Peat Bog
+	Quest Giver		:	Lieutenant Dawson
+	Preceded by		:	Mysterious Machine
+	Followed by		:	On The Move
 --]]
 
+local LIEUTENANT_DAWSON_ID = 1980012
+local ENTITY_COMMAND_INSPECT = 61
+local AMBUSHED_QUEST_ID = 509
 
 function Init(Quest)
-
-
-	AddQuestStep(Quest, 1, "I need to investigate the ambush site west of Two Logs Pond, which is south of the sewer grate.", 1, 100, "Lieutenant Dawson has asked me to investigate three ambush sites.", 0)
-	AddQuestStep(Quest, 2, "I need to investigate the ambush site in the north eastern corner of the area east of Two Logs Pond.", 1, 100, "Lieutenant Dawson has asked me to investigate three ambush sites.", 0)
-	AddQuestStep(Quest, 3, "I need to investigate the ambush site in the south end of the area east of Two Logs Pond.", 1, 100, "Lieutenant Dawson has asked me to investigate three ambush sites.", 0)
-	AddQuestStepCompleteAction(Quest, 1, "Step1_Complete_Site1")
-	AddQuestStepCompleteAction(Quest, 2, "Step2_Complete_Site2")
-	AddQuestStepCompleteAction(Quest, 3, "Step3_Complete_Site3")
-
-end
-
-function Accepted(Quest, QuestGiver, Player)
-	FaceTarget(QuestGiver, Player)
-	conversation = CreateConversation()
+	AddQuestStepSpell(Quest, 1, "I need to investigate the ambush site west of Two Logs Pond, which is south of the sewer grate.", 1, 100, "Lieutenant Dawson has asked me to investigate three ambush sites.", 11, ENTITY_COMMAND_INSPECT)
+	AddQuestStepCompleteAction(Quest, 1, "Step1Complete")
+	
+	AddQuestStepSpell(Quest, 2, "I need to investigate the ambush site in the north eastern corner of the area east of Two Logs Pond.", 1, 100, "Lieutenant Dawson has asked me to investigate three ambush sites.", 11, ENTITY_COMMAND_INSPECT)
+	AddQuestStepCompleteAction(Quest, 2, "Step2Complete")
 	
-	PlayFlavor(QuestGiver, "voiceover/english/tutorial_revamp/lieutenant_dawson/qey_adv04_bog/quests/dawson/dawson011a.mp3", "", "", 3239354610, 1196418998, Player)
-	AddConversationOption(conversation, "You're welcome, and thank you.")
-	StartConversation(conversation, QuestGiver, Player, "Thank you for your help, be safe.")
+	AddQuestStepSpell(Quest, 3, "I need to investigate the ambush site in the south end of the area east of Two Logs Pond.", 1, 100, "Lieutenant Dawson has asked me to investigate three ambush sites.", 11, ENTITY_COMMAND_INSPECT)
+	AddQuestStepCompleteAction(Quest, 3, "Step3Complete")
 end
 
-function Declined(Quest, QuestGiver, Player)
+function CheckProgress(Quest, QuestGiver, Player)
+	if QuestStepIsComplete(Player, AMBUSHED_QUEST_ID, 1) and QuestStepIsComplete(Player, AMBUSHED_QUEST_ID, 2) and QuestStepIsComplete(Player, AMBUSHED_QUEST_ID, 3) then
+		UpdateQuestTaskGroupDescription(Quest, 1, "I have investigated all three ambush sites.")
+		
+		AddQuestStepChat(Quest, 4, "I need to return to Lieutenant Dawson.", 1, "I need to tell Lieutenant Dawson of what I found at one of the ambush sites.", 11, ENTITY_COMMAND_INSPECT)
+		AddQuestStepCompleteAction(Quest, 4, "QuestComplete")
+	end
 end
 
-function Step1_Complete_Site1(Quest, QuestGiver, Player)
+function Step1Complete(Quest, QuestGiver, Player)
 	UpdateQuestStepDescription(Quest, 1, "I have investigated the ambush site near Two Logs Pond.")
-
-	if QuestIsComplete(Player, 217) then
-		Multiple_Steps_Complete(Quest, QuestGiver, Player)
-	end
+	CheckProgress(Quest, QuestGiver, Player)
 end
 
-function Step2_Complete_Site2(Quest, QuestGiver, Player)
+function Step2Complete(Quest, QuestGiver, Player)
 	UpdateQuestStepDescription(Quest, 2, "I have investigated the ambush site in the area east of Two Logs Pond.")
-
-	if QuestIsComplete(Player, 217) then
-		Multiple_Steps_Complete(Quest, QuestGiver, Player)
-	end
+	CheckProgress(Quest, QuestGiver, Player)
 end
 
-function Step3_Complete_Site3(Quest, QuestGiver, Player)
+function Step3Complete(Quest, QuestGiver, Player)
 	UpdateQuestStepDescription(Quest, 3, "I have investigated the ambush site in the southern end of the area east of Two Logs Pond.")
-	
-	if QuestIsComplete(Player, 217) then
-		Multiple_Steps_Complete(Quest, QuestGiver, Player)
-	end
-end
-
-function Multiple_Steps_Complete(Quest, QuestGiver, Player)
-	UpdateQuestTaskGroupDescription(Quest, 1, "I have investigated all three ambush sites.")
-	
-	-- a gnoll paw
-	if not HasItem(Player, 1711) then
-		SummonItem(Player, 1711)
-		SendMessage(Player, "You receieve [a gnoll paw].", "yellow")
-	end
-
-	AddQuestStepChat(Quest, 4, "I need to return to Lieutenant Dawson.", 1, "I need to tell Lieutenant Dawson of what I found at one of the ambush sites.", 0, 1980022) 
-	AddQuestStepCompleteAction(Quest, 4, "QuestComplete")
+	CheckProgress(Quest, QuestGiver, Player)
 end
 
 function QuestComplete(Quest, QuestGiver, Player)
-	-- a gnoll paw
-	while HasItem(Player, 1711) do
-		RemoveItem(Player, 1711)
-	end
-	
+	-- The following UpdateQuestStepDescription and UpdateTaskGroupDescription are not needed, parser adds them for completion in case stuff needs to be moved around
+	UpdateQuestStepDescription(Quest, 4, "I have spoken with Lieutenant Dawson.")
+	UpdateQuestTaskGroupDescription(Quest, 2, "I have spoken with Lieutenant Dawson.")
+
 	UpdateQuestDescription(Quest, "I found evidence of gnolls at one of the ambush sites.")
 	GiveQuestReward(Quest, Player)
 end
 
 function Reload(Quest, QuestGiver, Player, Step)
 	if Step == 1 then
-		Step1_Complete_Site1(Quest, QuestGiver, Player)
+		Step1Complete(Quest, QuestGiver, Player)
 	elseif Step == 2 then
-		Step2_Complete_Site2(Quest, QuestGiver, Player)
+		Step2Complete(Quest, QuestGiver, Player)
 	elseif Step == 3 then
-		Step3_Complete_Site3(Quest, QuestGiver, Player)
+		Step3Complete(Quest, QuestGiver, Player)
+	elseif Step == 4 then
+		QuestComplete(Quest, QuestGiver, Player)
 	end
 end
+
+function Accepted(Quest, QuestGiver, Player)
+	-- Add dialog here for when the quest is accepted
+end
+
+function Declined(Quest, QuestGiver, Player)
+	-- Add dialog here for when the quest is declined
+end

+ 9 - 8
server/SpawnStructs.xml

@@ -375,7 +375,7 @@
 <Data ElementName="hide_health" Type="int8" Size="1" /> <!-- 39 -->
 <Data ElementName="is_transport" Type="int8" Size="1" /> <!-- 40 -->
 <Data ElementName="house_icon" Type="int8" Size="1" /> <!-- 41 -->
-<Data ElementName="in_combat" Type="int8" Size="1" /> <!-- 42 -->
+<Data ElementName="loot_icon" Type="int8" Size="1" /> <!-- 42 -->
 <Data ElementName="afk" Type="int8" Size="1" /> <!-- 43 -->
 <Data ElementName="roleplaying" Type="int8" Size="1" /> <!-- 44 -->
 <Data ElementName="anonymous" Type="int8" Size="1" /> <!-- 45 -->
@@ -455,9 +455,9 @@
 <Data ElementName="unknown6" Type="int8" Size="1" /> <!-- 299 -->
 <Data ElementName="heroic_flag" Type="int8" Size="1" /> <!-- 300 -->
 <Data ElementName="npc" Type="int8" Size="1" /> <!-- 301 -->
-<Data ElementName="unknown7" Type="int8" Size="1" /> <!-- 303 -->
-<Data ElementName="unknown8" Type="int8" Size="1" /> <!-- 303 -->
-<Data ElementName="merchant" Type="int8" Size="1" /> <!-- 304 -->
+<Data ElementName="unknown7" Type="int8" Size="1" /> <!-- 302 -->
+<Data ElementName="merchant" Type="int8" Size="1" /> <!-- 303 -->
+<Data ElementName="unknown8" Type="int8" Size="1" /> <!-- 304 -->
 <Data ElementName="unknown9" Type="int8" Size="1" /> <!-- 305 -->
 <Data ElementName="unknown10" Type="int8" Size="1" /> <!-- 306 -->
 <Data ElementName="no_arrow_color_or_highlight" Type="int8" Size="1" /> <!-- 307 -->
@@ -466,7 +466,7 @@
 <Data ElementName="hide_health" Type="int8" Size="1" /> <!-- 310 -->
 <Data ElementName="is_transport" Type="int8" Size="1" /> <!-- 311 -->
 <Data ElementName="house_icon" Type="int8" Size="1" /> <!-- 312 -->
-<Data ElementName="in_combat" Type="int8" Size="1" /> <!-- 313 -->
+<Data ElementName="loot_icon" Type="int8" Size="1" /> <!-- 313 -->
 <Data ElementName="afk" Type="int8" Size="1" /> <!-- 314 -->
 <Data ElementName="roleplaying" Type="int8" Size="1" /> <!-- 315 -->
 <Data ElementName="anonymous" Type="int8" Size="1" /> <!-- 316 -->
@@ -554,7 +554,8 @@
 <Data ElementName="mood_state" Type="int16" Size="1" /> <!-- 1083 -->
 <Data ElementName="emote_state" Type="int16" Size="1" /> <!-- 1085 -->
 <Data ElementName="race" Type="int8" Size="1" /> <!-- 1087 -->
-<Data ElementName="gender" Type="int8" Size="1" /> <!-- 1088 --></Struct>
+<Data ElementName="gender" Type="int8" Size="1" /> <!-- 1088 -->
+</Struct>
 <Struct Name="Substruct_SpawnInfoStruct" ClientVersion="547" >
 <Data ElementName="hp_remaining" Type="int8" Size="1" />
 <Data ElementName="unknown2a" Type="int8" Size="3" />
@@ -2505,7 +2506,7 @@
 <Data ElementName="red_glow" Type="int8" Size="1" />
 <Data ElementName="show_name" Type="int8" Size="1" />
 <Data ElementName="attackable" Type="int8" Size="1" />
-<Data ElementName="unknown10" Type="int8" Size="1" />
+<Data ElementName="attackable_icon" Type="int8" Size="1" />
 <Data ElementName="unknown11" Type="int8" Size="1" />
 <Data ElementName="unknown12" Type="int8" Size="1" />
 </Struct>
@@ -2520,7 +2521,7 @@
 <Data ElementName="red_glow" Type="int8" Size="1" />
 <Data ElementName="show_name" Type="int8" Size="1" />
 <Data ElementName="attackable" Type="int8" Size="1" />
-<Data ElementName="unknown2" Type="int8" Size="1" />
+<Data ElementName="attackable_icon" Type="int8" Size="1" />
 <Data ElementName="unknown3" Type="int8" Size="1" />
 <Data ElementName="unknown4" Type="int8" Size="1" />
 <Data ElementName="unknown5" Type="int8" Size="1" />

+ 10 - 3
server/WorldStructs.xml

@@ -358,6 +358,9 @@ to zero and treated like placeholders." />
 <Data ElementName="completed_voice_key1" Type="int32" Size="1" />
 <Data ElementName="completed_voice_key2" Type="int32" Size="1" />
 </Struct>
+<Struct Name="WS_InstructionWindowGoal" ClientVersion="1" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqInstructionWindowGoalCmd">
+<Data ElementName="goal_num" Type="int8" Size="1" />
+</Struct>
 <Struct Name="WS_OnScreenMsg" ClientVersion="1" OpcodeName="OP_OnscreenMsgMsg">
 <Data ElementName="text" Type="EQ2_16Bit_String" />
 <Data ElementName="message_type" Type="EQ2_16Bit_String" />
@@ -366,7 +369,7 @@ to zero and treated like placeholders." />
 <Data ElementName="green" Type="int8" Size="1" />
 <Data ElementName="blue" Type="int8" Size="1" />
 </Struct>
-<Struct Name="WS_OnScreenMsg" ClientVersion="547" OpcodeName="OP_OnscreenMsgMsg">
+<Struct Name="WS_OnScreenMsg" ClientVersion="546" OpcodeName="OP_OnscreenMsgMsg">
 <Data ElementName="unknown" Type="int8" Size="1" />
 <Data ElementName="text" Type="EQ2_16Bit_String" />
 <Data ElementName="message_type" Type="EQ2_16Bit_String" />
@@ -6970,7 +6973,10 @@ to zero and treated like placeholders." />
 <Data ElementName="unknown4" Type="int8" Size="1" />
 <Data ElementName="unknown5" Type="int8" Size="1" />
 </Struct>
-<Struct Name="WS_CloseWindow" ClientVersion="1" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqCloseWindowCmd" >
+<Struct Name="WS_StoppedLooting" ClientVersion="1" OpcodeName="OP_StoppedLootingMsg" >
+<Data ElementName="spawn_id" Type="int32" />
+</Struct>
+<Struct Name="WS_CloseWindow" ClientVersion="547" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqCloseWindowCmd" >
 <Data ElementName="window_id" Type="int16" />
 <Data ElementName="index" Type="int8" />
 </Struct>
@@ -7116,7 +7122,8 @@ to zero and treated like placeholders." />
 	<Data ElementName="difficulty" Type="int8" Size="1" />
 	<Data ElementName="visible" Type="int8" Size="1" />
 </Data>
-<Data ElementName="unknown3" Type="int8" Size="2" />
+<Data ElementName="quest_updated" Type="int8" Size="1" />
+<Data ElementName="collapsed" Type="int8" Size="1" />
 <Data ElementName="visible_quest_id" Type="int32" Size="1" />
 <Data ElementName="player_crc" Type="int32" Size="1" />
 <Data ElementName="player_name" Type="EQ2_16Bit_String" Size="1" />

Някои файлове не бяха показани, защото твърде много файлове са промени