Browse Source

Lacertae Update #2 - Stability Fixes against Inventory and GM reload options

Fix #121 - Can use /reload luasystem and /reload spells without causing a crash
Log messages at spell/warning level for NPC casting when it doesn't have enough mana/concentration/etc

Fixed additional ASan issues:
Fix #370
Fix #371
Fix #372
Fix #373
Image 2 years ago
parent
commit
30e19ed012

+ 68 - 39
EQ2/source/WorldServer/Commands/Commands.cpp

@@ -2091,7 +2091,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 							if(lua_interface->RunItemScript(item->GetItemScript(), "used", item, player, &flags) && flags >= 0)
 							{
 								//reobtain item make sure it wasn't removed
-								Item* item = player->item_list.GetItemFromIndex(item_index);
+								item = player->item_list.GetItemFromIndex(item_index);
 								if(!item)
 									LogWrite(PLAYER__WARNING, 0, "Command", "%s: Item %s (%i) was used, however after the item looks to be removed.", client->GetPlayer()->GetName(), itemName.c_str(), item_id);
 								else if(!item->generic_info.display_charges && item->generic_info.max_charges == 1) {
@@ -2115,8 +2115,12 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 						}
 						else
 						{
+							//reobtain item make sure it wasn't removed
+							item = player->item_list.GetItemFromIndex(item_index);
 							client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Item is out of charges.");
-							LogWrite(PLAYER__ERROR, 0, "Command", "%s: Item %s (%i) attempted to be used, however details.count is 0.", client->GetPlayer()->GetName(), item->name.c_str(), item->details.item_id);
+							if(item) {
+								LogWrite(PLAYER__ERROR, 0, "Command", "%s: Item %s (%i) attempted to be used, however details.count is 0.", client->GetPlayer()->GetName(), item->name.c_str(), item->details.item_id);
+							}
 						}
 					}
 				}
@@ -2887,12 +2891,12 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 		case COMMAND_RELOADSPAWNSCRIPTS:{
 			client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Reloading Spawn Scripts....");
 			if (lua_interface)
-				lua_interface->SetSpawnScriptsReloading(true);
+				lua_interface->SetLuaSystemReloading(true);
 			world.ResetSpawnScripts();
 			database.LoadSpawnScriptData();
 			if(lua_interface) {
 				lua_interface->DestroySpawnScripts();
-				lua_interface->SetSpawnScriptsReloading(false);
+				lua_interface->SetLuaSystemReloading(false);
 			}
 			client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Success!");
 			break;
@@ -2907,15 +2911,22 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 										}
 		case COMMAND_RELOADLUASYSTEM:{
 			client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Attempting to reload entire LUA system....");
-			map<Client*, int32> debug_clients = lua_interface->GetDebugClients();
-			safe_delete(lua_interface);
-			lua_interface = new LuaInterface();
-			if(lua_interface)
-				lua_interface->SetSpawnScriptsReloading(true);
 
-			if(lua_interface && debug_clients.size() > 0){
+			world.SetReloadingSubsystem("LuaSystem");
+			
+			if(lua_interface) {
+				lua_interface->SetLuaSystemReloading(true);
+			}
+			
+			zone_list.DeleteSpellProcess();
+			master_spell_list.Reload();
+			if (lua_interface)
+				lua_interface->ReloadSpells();
+			zone_list.LoadSpellProcess();
+			if(lua_interface){
+				map<Client*, int32> debug_clients = lua_interface->GetDebugClients();
 				map<Client*, int32>::iterator itr;
-				for(itr = debug_clients.begin(); itr != debug_clients		.end(); itr++){
+				for(itr = debug_clients.begin(); itr != debug_clients.end(); itr++){
 					if(lua_interface)
 						lua_interface->UpdateDebugClients(itr->first);
 				}
@@ -2927,19 +2938,24 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 			world.ResetZoneScripts();
 			database.LoadZoneScriptData();
 
+			if(lua_interface) {
+				lua_interface->DestroySpawnScripts();
+				lua_interface->DestroyRegionScripts();
+				lua_interface->DestroyQuests();
+				lua_interface->DestroyItemScripts();
+				lua_interface->DestroyZoneScripts();
+			}
+
 			int32 quest_count = database.LoadQuests();
 
-			zone_list.DeleteSpellProcess();
-			master_spell_list.Reload();
 			int32 spell_count = 0;
 
 			if(lua_interface) {
-				lua_interface->DestroySpawnScripts();
-				lua_interface->DestroySpells();
 				spell_count = database.LoadSpellScriptData();
-				lua_interface->SetSpawnScriptsReloading(false);
+				lua_interface->SetLuaSystemReloading(false);
 			}
-			zone_list.LoadSpellProcess();
+
+			world.RemoveReloadingSubSystem("LuaSystem");
 
 			client->Message(CHANNEL_COLOR_YELLOW, "Successfully loaded %u spell(s), %u quest(s).", spell_count, quest_count);
 			break;
@@ -3556,7 +3572,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 		{
 			int32 id = 0;
 
-			if (sep->IsNumber(0))
+			if (sep && sep->IsNumber(0))
 				id = atoul(sep->arg[0]);
 
 			Spawn* spawn = 0;
@@ -3576,7 +3592,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 		case COMMAND_PICKUP:
 		{
 			int32 id = 0;
-			if (sep->IsNumber(0))
+			if (sep && sep->IsNumber(0))
 				id = atoul(sep->arg[0]);
 
 			Spawn* spawn = 0;
@@ -5356,14 +5372,17 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 							item->details.count = quantity;
 							// 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 %s.", item->CreateItemLink(client->GetVersion()).c_str());
-							client->AddItem(item);
+							bool itemDeleted = false;
+							client->AddItem(item, &itemDeleted);
 							//Check for crafting quest updates
 							int8 update_amt = 0;
-							if (item->stack_count > 1)
+							if (!itemDeleted && item->stack_count > 1)
 								update_amt = 1;
 							else
 								update_amt = quantity;
-							client->GetPlayer()->CheckQuestsCraftUpdate(item, update_amt);
+
+							if(!itemDeleted)
+								client->GetPlayer()->CheckQuestsCraftUpdate(item, update_amt);
 						}
 
 					}
@@ -6419,16 +6438,16 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma
 				}
 				if(item->GetItemScript() && lua_interface)
 					lua_interface->RunItemScript(item->GetItemScript(), "destroyed", item, client->GetPlayer());
-				int32 bag_id = item->details.inv_slot_id;
-				database.DeleteItem(client->GetCharacterID(), item, 0);
+			
+				//reobtain item make sure it wasn't removed
+				item = player->item_list.GetItemFromIndex(index);
+				int32 bag_id = 0;
+				if(item){
+					bag_id = item->details.inv_slot_id;
+					database.DeleteItem(client->GetCharacterID(), item, 0);
+				}
 				client->GetPlayer()->item_list.DestroyItem(index);
-				EQ2Packet* outapp = client->GetPlayer()->SendInventoryUpdate(client->GetVersion());
-				client->QueuePacket(outapp);
-
-				outapp = client->GetPlayer()->SendBagUpdate(bag_id, client->GetVersion());
-
-				if (outapp)
-					client->QueuePacket(outapp);
+				client->GetPlayer()->UpdateInventory(bag_id);
 			}
 		}
 		else if(sep->arg[4][0] && strncasecmp("move", sep->arg[0], 4) == 0 && sep->IsNumber(1) && sep->IsNumber(2) && sep->IsNumber(3) && sep->IsNumber(4))
@@ -6477,9 +6496,13 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma
 				}
 			}
 
-			EQ2Packet* outapp = client->GetPlayer()->MoveInventoryItem(bag_id, from_index, (int8)to_slot, charges, 0, client->GetVersion());
+			bool item_deleted = false;
+			EQ2Packet* outapp = client->GetPlayer()->MoveInventoryItem(bag_id, from_index, (int8)to_slot, charges, 0, &item_deleted, client->GetVersion());
 			client->QueuePacket(outapp);
 
+			if(item_deleted)
+				item = nullptr;
+
 			//removed from bag send update
 			if(old_inventory_id > 0 && item && item->details.inv_slot_id != old_inventory_id)
 			{ 
@@ -6642,10 +6665,13 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma
 					item->details.slot_id = to_slot;
 					// Flag the item so it gets saved in its new location
 					item->save_needed = true;
+					
 					// Add the item to its new location
-					player->item_list.AddItem(item);
-					// Remove the item from the overflow list
-					player->item_list.RemoveOverflowItem(item);
+					if(player->item_list.AddItem(item)) {
+						// Remove the item from the overflow list
+						player->item_list.RemoveOverflowItem(item);
+					}
+
 					// Send the inventory update packet
 					client->QueuePacket(player->item_list.serialize(player, client->GetVersion()));
 					return;
@@ -6659,8 +6685,9 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma
 					item->details.inv_slot_id = bag_id;
 					item->details.slot_id = to_slot;
 					item->save_needed = true;
-					player->item_list.AddItem(item);
-					player->item_list.RemoveOverflowItem(item);
+					if(player->item_list.AddItem(item)) {
+						player->item_list.RemoveOverflowItem(item);
+					}
 					client->QueuePacket(player->item_list.serialize(player, client->GetVersion()));
 				}
 				else if (bag_id == -4) {
@@ -6678,8 +6705,9 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma
 						item->details.inv_slot_id = bag_id;
 						item->details.slot_id = to_slot;
 						item->save_needed = true;
-						player->item_list.AddItem(item);
+						if(player->item_list.AddItem(item)) {
 						player->item_list.RemoveOverflowItem(item);
+						}
 						client->QueuePacket(player->item_list.serialize(player, client->GetVersion()));
 						return;
 					}
@@ -6696,8 +6724,9 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma
 						item->details.inv_slot_id = bag_id;
 						item->details.slot_id = to_slot;
 						item->save_needed = true;
-						player->item_list.AddItem(item);
+						if(player->item_list.AddItem(item)) {
 						player->item_list.RemoveOverflowItem(item);
+						}
 						client->QueuePacket(player->item_list.serialize(player, client->GetVersion()));
 						return;
 					}

+ 7 - 3
EQ2/source/WorldServer/Entity.cpp

@@ -1116,6 +1116,9 @@ float Entity::CalculateSkillStatChance(char* skillName, int16 item_stat, float m
 }
 
 void Entity::CalculateBonuses(){
+	if(lua_interface->IsLuaSystemReloading())
+		return;
+
 	InfoStruct* info = &info_struct;
 
 	int16 effective_level = info->get_effective_level() != 0 ? info->get_effective_level() : GetLevel();
@@ -1492,11 +1495,12 @@ vector<BonusValues*>* Entity::GetAllSpellBonuses(LuaSpell* spell) {
 	return list;
 }
 
-void Entity::RemoveSpellBonus(const LuaSpell* spell){
+void Entity::RemoveSpellBonus(const LuaSpell* spell, bool remove_all){
+	// spell can be null!
 	MutexList<BonusValues*>::iterator itr = bonus_list.begin();
 	while(itr.Next()){
-		if(itr.value->luaspell == spell)
-			bonus_list.Remove(itr.value, true);
+		if(itr.value->luaspell == spell || remove_all)
+		bonus_list.Remove(itr.value, true);
 	}
 	
 	if(IsNPC())

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

@@ -1445,7 +1445,8 @@ public:
 	BonusValues* GetSpellBonus(int32 spell_id);
 	vector<BonusValues*>* GetAllSpellBonuses(LuaSpell* spell);
 	bool CheckSpellBonusRemoval(LuaSpell* spell, int16 type);
-	void RemoveSpellBonus(const LuaSpell* spell);
+	void RemoveSpellBonus(const LuaSpell* spell, bool remove_all = false);
+	void RemoveAllSpellBonuses();
 	void CalculateSpellBonuses(ItemStatsValues* stats);
 	void AddMezSpell(LuaSpell* spell);
 	void RemoveMezSpell(LuaSpell* spell);

+ 53 - 51
EQ2/source/WorldServer/GroundSpawn.cpp

@@ -392,63 +392,65 @@ void GroundSpawn::ProcessHarvest(Client* client) {
 						// chat box update for normal item (todo: verify output text)
 						client->Message(CHANNEL_HARVESTING, "You %s %i %s from the %s.", GetHarvestMessageName(true).c_str(), item->details.count, item->CreateItemLink(client->GetVersion(), true).c_str(), GetName());
 						// add Normal item to player inventory
-						client->AddItem(item);
-						//Check if the player has a harvesting quest for this
-						client->GetPlayer()->CheckQuestsHarvestUpdate(item, reward_total);
-
-
-
-						// if this is a 10+rare, handle sepErately
-						if (harvest_type == 6 && rare_item == 1) {
-							LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Item ID %u is Normal. Qty %i", item_harvested, item->details.count);
-
-							// send Normal harvest message to client
-							sprintf(tmp, "\\#64FFFFYou have %s:\12\\#C8FFFF%i %s", GetHarvestMessageName().c_str(), item->details.count, item->name.c_str());
-							client->SendPopupMessage(10, tmp, "ui_harvested_normal", 2.25, 0xFF, 0xFF, 0xFF);
-							client->GetPlayer()->UpdatePlayerStatistic(STAT_PLAYER_ITEMS_HARVESTED, item->details.count);
-
-							// set Rare item harvested
-							master_rare = master_item_list.GetItem(rare_harvested);
-							if (master_rare) {
-								// set details of Rare item
-								item_rare = new Item(master_rare);
-								// count of Rare is always 1
-								item_rare->details.count = 1;
-
-								LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Item ID %u is RARE!", rare_harvested);
+						bool itemDeleted = false;
+						client->AddItem(item, &itemDeleted);
+
+						if(!itemDeleted) {
+							//Check if the player has a harvesting quest for this
+							client->GetPlayer()->CheckQuestsHarvestUpdate(item, reward_total);
+
+							// if this is a 10+rare, handle sepErately
+							if (harvest_type == 6 && rare_item == 1) {
+								LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Item ID %u is Normal. Qty %i", item_harvested, item->details.count);
+
+								// send Normal harvest message to client
+								sprintf(tmp, "\\#64FFFFYou have %s:\12\\#C8FFFF%i %s", GetHarvestMessageName().c_str(), item->details.count, item->name.c_str());
+								client->SendPopupMessage(10, tmp, "ui_harvested_normal", 2.25, 0xFF, 0xFF, 0xFF);
+								client->GetPlayer()->UpdatePlayerStatistic(STAT_PLAYER_ITEMS_HARVESTED, item->details.count);
+
+								// set Rare item harvested
+								master_rare = master_item_list.GetItem(rare_harvested);
+								if (master_rare) {
+									// set details of Rare item
+									item_rare = new Item(master_rare);
+									// count of Rare is always 1
+									item_rare->details.count = 1;
+
+									LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Item ID %u is RARE!", rare_harvested);
+
+									// 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_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_HARVESTING, "You %s %i %s from the %s.", GetHarvestMessageName(true).c_str(), item_rare->details.count, item->CreateItemLink(client->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
+									client->GetPlayer()->CheckQuestsHarvestUpdate(item_rare, 1);
+								}
+							}
+							else if (rare_item == 1) {
+								// if harvest signaled rare or imbue type
+								LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Item ID %u is RARE! Qty: %i", item_harvested, item->details.count);
 
 								// 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());
+								sprintf(tmp, "\\#FFFF6ERare item found!\12%s: \\#C8FFFF%i %s", GetHarvestMessageName().c_str(), item->details.count, item->name.c_str());
 								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_HARVESTING, "You %s %i %s from the %s.", GetHarvestMessageName(true).c_str(), item_rare->details.count, item->CreateItemLink(client->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
-								client->GetPlayer()->CheckQuestsHarvestUpdate(item_rare, 1);
+								client->GetPlayer()->UpdatePlayerStatistic(STAT_PLAYER_RARES_HARVESTED, item->details.count);
+							}
+							else {
+								// send Normal harvest message to client
+								LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Item ID %u is Normal. Qty %i", item_harvested, item->details.count);
+								sprintf(tmp, "\\#64FFFFYou have %s:\12\\#C8FFFF%i %s", GetHarvestMessageName().c_str(), item->details.count, item->name.c_str());
+								client->SendPopupMessage(10, tmp, "ui_harvested_normal", 2.25, 0xFF, 0xFF, 0xFF);
+								client->GetPlayer()->UpdatePlayerStatistic(STAT_PLAYER_ITEMS_HARVESTED, item->details.count);
 							}
-						}
-						else if (rare_item == 1) {
-							// if harvest signaled rare or imbue type
-							LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Item ID %u is RARE! Qty: %i", item_harvested, item->details.count);
-
-							// 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_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);
-						}
-						else {
-							// send Normal harvest message to client
-							LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Item ID %u is Normal. Qty %i", item_harvested, item->details.count);
-							sprintf(tmp, "\\#64FFFFYou have %s:\12\\#C8FFFF%i %s", GetHarvestMessageName().c_str(), item->details.count, item->name.c_str());
-							client->SendPopupMessage(10, tmp, "ui_harvested_normal", 2.25, 0xFF, 0xFF, 0xFF);
-							client->GetPlayer()->UpdatePlayerStatistic(STAT_PLAYER_ITEMS_HARVESTED, item->details.count);
-						}
 
+						}
 					}
 					else {
 						// error!

+ 25 - 15
EQ2/source/WorldServer/Items/Items.cpp

@@ -2776,9 +2776,9 @@ PlayerItemList::~PlayerItemList(){
 
 map<int32, Item*>* PlayerItemList::GetAllItems(){
 	map<int32, Item*>* ret = new map<int32, Item*>;
-	MPlayerItems.writelock(__FUNCTION__, __LINE__);
+	MPlayerItems.readlock(__FUNCTION__, __LINE__);
 	ret->insert(indexed_items.begin(), indexed_items.end());
-	MPlayerItems.releasewritelock(__FUNCTION__, __LINE__);
+	MPlayerItems.releasereadlock(__FUNCTION__, __LINE__);
 	return ret;
 }
 
@@ -2800,10 +2800,10 @@ Item* PlayerItemList::GetItem(sint32 bag_slot, int16 slot, int8 appearance_type)
 	return ret;
 }
 
-void PlayerItemList::AddItem(Item* item){ //is called with a slot already set
+bool PlayerItemList::AddItem(Item* item){ //is called with a slot already set
 	//quick check to verify item
 	if(!item)
-		return;
+		return false;
 	else{
 		if(item->details.inv_slot_id != 0){
 			Item* bag = GetItemFromUniqueID(item->details.inv_slot_id, true);
@@ -2812,7 +2812,7 @@ void PlayerItemList::AddItem(Item* item){ //is called with a slot already set
 					LogWrite(ITEM__ERROR, 0, "Item", "Error Adding Item: Invalid slot for item unique id: %u (%s - %i), InvSlotID: %u, slotid: %u, numslots: %u", item->details.unique_id, item->name.c_str(), 
 					item->details.item_id, item->details.inv_slot_id, item->details.slot_id, bag->details.num_slots);
 					safe_delete(item);
-					return;
+					return false;
 				}
 			}
 		}
@@ -2835,8 +2835,11 @@ void PlayerItemList::AddItem(Item* item){ //is called with a slot already set
 		new_index = max_index;
 
 	indexed_items[new_index] = item;
+	item->details.index = new_index;
 	items[item->details.inv_slot_id][item->details.appearance_type][item->details.slot_id] = item;
 	MPlayerItems.releasewritelock(__FUNCTION__, __LINE__);
+
+	return true;
 }
 
 Item* PlayerItemList::GetBag(int8 inventory_slot, bool lock){
@@ -3109,8 +3112,8 @@ bool PlayerItemList::AssignItemToFreeSlot(Item* item){
 							item->details.inv_slot_id = bag->details.bag_id;
 							item->details.slot_id = x;
 							MPlayerItems.releasewritelock(__FUNCTION__, __LINE__);
-							AddItem(item);
-							return true;
+							bool ret = AddItem(item);
+							return ret;
 						}
 					}
 				}
@@ -3122,8 +3125,8 @@ bool PlayerItemList::AssignItemToFreeSlot(Item* item){
 				item->details.inv_slot_id = 0;
 				item->details.slot_id = i;
 				MPlayerItems.releasewritelock(__FUNCTION__, __LINE__);
-				AddItem(item);
-				return true;
+				bool ret = AddItem(item);
+				return ret;
 			}
 		}
 		MPlayerItems.releasewritelock(__FUNCTION__, __LINE__);
@@ -3142,12 +3145,18 @@ void PlayerItemList::RemoveItem(Item* item, bool delete_item){
 		for(itr = items[item->details.bag_id][item->details.appearance_type].begin(); itr != items[item->details.bag_id][item->details.appearance_type].end(); itr++){
 			indexed_items[itr->second->details.index] = 0;
 			if(delete_item){
+				if(itr->second == item) {
+					item = nullptr;
+				}
 				safe_delete(itr->second);
 			}
 		}
 		items.erase(item->details.bag_id);
 	}
 	if(delete_item){
+		map<int32, Item*>::iterator itr = indexed_items.find(item->details.index);
+		if(itr != indexed_items.end() && item == indexed_items[item->details.index])
+			indexed_items[item->details.index] = 0;
 		safe_delete(item);
 	}
 	MPlayerItems.releasewritelock(__FUNCTION__, __LINE__);
@@ -3242,13 +3251,14 @@ bool PlayerItemList::MoveItem(sint32 to_bag_id, int16 from_index, sint8 to, int8
 			}
 			else {
 				if (item_from->details.count == charges) {
+					MPlayerItems.releasewritelock(__FUNCTION__, __LINE__);
 					if (item_to) 
 						MoveItem(item_to, item_from->details.inv_slot_id, item_from->details.slot_id, BASE_EQUIPMENT, true);
 
 					MoveItem(item_from, to_bag_id, to, BASE_EQUIPMENT, item_to ? false:true);
-					MPlayerItems.releasewritelock(__FUNCTION__, __LINE__);
 				}
 				else {
+					MPlayerItems.releasewritelock(__FUNCTION__, __LINE__);
 					if (item_to) {
 						MPlayerItems.releasewritelock(__FUNCTION__, __LINE__);
 						return false;
@@ -3259,7 +3269,6 @@ bool PlayerItemList::MoveItem(sint32 to_bag_id, int16 from_index, sint8 to, int8
 					new_item->details.slot_id = to;
 					new_item->details.inv_slot_id = to_bag_id;
 					new_item->details.appearance_type = 0;
-					MPlayerItems.releasewritelock(__FUNCTION__, __LINE__);
 					new_item->save_needed = true;
 					AddItem(new_item);
 					if (item_from->details.count == 0)
@@ -3289,17 +3298,18 @@ bool PlayerItemList::MoveItem(sint32 to_bag_id, int16 from_index, sint8 to, int8
 				}
 			}
 			else{
-				MoveItem(item_from, item_to->details.bag_id, 0, BASE_EQUIPMENT, true);
 				MPlayerItems.releasewritelock(__FUNCTION__, __LINE__);
+				MoveItem(item_from, item_to->details.bag_id, 0, BASE_EQUIPMENT, true);
 				return true;
 			}
 		}
+		MPlayerItems.releasewritelock(__FUNCTION__, __LINE__);
+		
 		if (item_to) 
 			MoveItem(item_to, item_from->details.inv_slot_id, item_from->details.slot_id, BASE_EQUIPMENT, true);
 
 		MoveItem(item_from, to_bag_id, to, BASE_EQUIPMENT, item_to ? false:true);
 		
-		MPlayerItems.releasewritelock(__FUNCTION__, __LINE__);
 		return true;
 	}
 	MPlayerItems.releasewritelock(__FUNCTION__, __LINE__);
@@ -3548,10 +3558,10 @@ void PlayerItemList::RemoveOverflowItem(Item* item) {
 
 vector<Item*>* PlayerItemList::GetOverflowItemList() {
 	vector<Item*>* ret = new vector<Item*>;
-	MPlayerItems.writelock(__FUNCTION__, __LINE__);
+	MPlayerItems.readlock(__FUNCTION__, __LINE__);
 	vector<Item*>::iterator itr= ret->begin();
 	ret->insert(itr, overflowItems.begin(), overflowItems.end());
-	MPlayerItems.releasewritelock(__FUNCTION__, __LINE__);
+	MPlayerItems.releasereadlock(__FUNCTION__, __LINE__);
 	return ret;
 }
 

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

@@ -1051,7 +1051,7 @@ public:
 	Item* CanStack(Item* item, bool include_bank = false);
 	
 	void RemoveItem(Item* item, bool delete_item = false);
-	void AddItem(Item* item);
+	bool AddItem(Item* item);
 
 	Item* GetItem(sint32 bag_slot, int16 slot, int8 appearance_type = 0);
 	
@@ -1088,10 +1088,10 @@ public:
 	
 	void	ResetPackets();
 
+	Mutex MPlayerItems;
 private:
 	void AddItemToPacket(PacketStruct* packet, Player* player, Item* item, int16 i, bool overflow = false);
 	void Stack(Item* orig_item, Item* item);
-	Mutex MPlayerItems;
 	int16 packet_count;
 	vector<Item*> overflowItems;
 };

+ 11 - 12
EQ2/source/WorldServer/Items/ItemsDB.cpp

@@ -1306,23 +1306,22 @@ void WorldDatabase::LoadCharacterItemList(int32 account_id, int32 char_id, Playe
 						int stacks = item->details.count / 255;
 						int8 remainder = item->details.count % 255;
 						item->details.count = remainder;
+						
 						if (item->details.inv_slot_id == -2)
 							player->item_list.AddOverflowItem(item);
-						else
-							player->item_list.AddItem(item);
-
-
-						for (int stack = 1; stack <= stacks; stack++) {
-							item->details.count = 255;
-							item->details.inv_slot_id = -2;
-							player->item_list.AddOverflowItem(item);
+						else {
+								if(!player->item_list.AddItem(item))
+									item = nullptr;
 						}
 
-
-
-
+						if(item) {
+							for (int stack = 1; stack <= stacks; stack++) {
+								item->details.count = 255;
+								item->details.inv_slot_id = -2;
+								player->item_list.AddOverflowItem(item);
+							}
+						}
 					}
-
 					else {
 						if (item->details.inv_slot_id == -2)
 							player->item_list.AddOverflowItem(item);

+ 22 - 5
EQ2/source/WorldServer/LuaInterface.cpp

@@ -40,7 +40,7 @@ extern ZoneList zone_list;
 
 LuaInterface::LuaInterface() {
 	shutting_down = false;
-	spawn_scripts_reloading = false;
+	lua_system_reloading = false;
 	MDebugClients.SetName("LuaInterface::MDebugClients");
 	MSpells.SetName("LuaInterface::MSpells");
 	MSpawnScripts.SetName("LuaInterface::MSpawnScripts");
@@ -125,6 +125,7 @@ LuaInterface::~LuaInterface() {
 	DeletePendingSpells(true);
 	safe_delete(user_data_timer);
 	safe_delete(spell_delete_timer);
+	MLUAMain.unlock();
 }
 
 int LuaInterface::GetNumberOfArgs(lua_State* state) {
@@ -689,7 +690,7 @@ bool LuaInterface::CallItemScript(lua_State* state, int8 num_parameters, sint64*
 }
 
 bool LuaInterface::CallSpawnScript(lua_State* state, int8 num_parameters) {
-	if(shutting_down)
+	if(shutting_down || lua_system_reloading)
 		return false;
 	if(!state || lua_pcall(state, num_parameters, 0, 0) != 0){
 		if (state){
@@ -865,7 +866,7 @@ void LuaInterface::RemoveSpell(LuaSpell* spell, bool call_remove_function, bool
 	}
 	if (spell->caster)
 	{
-		spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(spell, false);
+		spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(spell);
 		spell->caster->RemoveProc(0, spell);
 		spell->caster->RemoveMaintainedSpell(spell);
 
@@ -1498,6 +1499,22 @@ void LuaInterface::DeletePendingSpells(bool all) {
 		LuaSpell* spell = 0;
 		for (del_itr = tmp_deletes.begin(); del_itr != tmp_deletes.end(); del_itr++) {
 			spell = *del_itr;
+			
+			
+			if (spell->caster) {
+				spell->caster->GetZone()->GetSpellProcess()->DeleteActiveSpell(spell);
+			}
+			else if(spell->targets.size() > 0) {
+				spell->MSpellTargets.readlock(__FUNCTION__, __LINE__);
+				for (int8 i = 0; i < spell->targets.size(); i++) {
+					Spawn* target = spell->caster->GetZone()->GetSpawnByID(spell->targets.at(i));
+					if (!target || !target->IsEntity())
+						continue;
+					target->GetZone()->GetSpellProcess()->DeleteActiveSpell(spell);
+				}
+				spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__);
+			}
+
 			spells_pending_delete.erase(spell);
 
 			if (spell->spell->IsCopiedSpell())
@@ -2025,7 +2042,7 @@ lua_State* LuaInterface::GetItemScript(const char* name, bool create_new, bool u
 }
 
 lua_State* LuaInterface::GetSpawnScript(const char* name, bool create_new, bool use) {
-	if (spawn_scripts_reloading)
+	if (lua_system_reloading)
 		return 0;
 	map<string, map<lua_State*, bool> >::iterator itr;
 	map<lua_State*, bool>::iterator spawn_script_itr;
@@ -2212,7 +2229,7 @@ bool LuaInterface::RunItemScriptWithReturnString(string script_name, const char*
 
 
 bool LuaInterface::RunSpawnScript(string script_name, const char* function_name, Spawn* npc, Spawn* spawn, const char* message, bool is_door_open, sint32 input_value, sint32* return_value) {
-	if(!npc || spawn_scripts_reloading)
+	if(!npc || lua_system_reloading)
 		return false;
 
 	bool isUseDoorFunction = false;

+ 4 - 3
EQ2/source/WorldServer/LuaInterface.h

@@ -290,8 +290,9 @@ public:
 	Mutex*			GetRegionScriptMutex(const char* name);
 	Mutex*			GetQuestMutex(Quest* quest);
 
-	void			SetSpawnScriptsReloading(bool val) { spawn_scripts_reloading = val; }
-
+	void			SetLuaSystemReloading(bool val) { lua_system_reloading = val; }
+	bool			IsLuaSystemReloading() { return lua_system_reloading; }
+	
 	void			AddPendingSpellDelete(LuaSpell* spell);
 
 	void			AddCustomSpell(LuaSpell* spell);
@@ -304,7 +305,7 @@ public:
 	int32			GetFreeCustomSpellID();
 private:
 	bool			shutting_down;
-	bool			spawn_scripts_reloading;
+	bool			lua_system_reloading;
 	map<LuaSpell*, int32> spells_pending_delete;
 	Timer*			user_data_timer;
 	Timer*			spell_delete_timer;

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

@@ -1851,7 +1851,7 @@ vector<EQ2Packet*>	Player::UnequipItem(int16 index, sint32 bag_id, int8 slot, in
 				lua_interface->RunItemScript(item->GetItemScript(), "unequipped", item, this);
 
 			if (to_item->GetItemScript() && lua_interface)
-				lua_interface->RunItemScript(item->GetItemScript(), "equipped", to_item, this);
+				lua_interface->RunItemScript(to_item->GetItemScript(), "equipped", to_item, this);
 
 			item_list.RemoveItem(to_item);
 			equipList->SetItem(item->details.slot_id, to_item);
@@ -1861,12 +1861,18 @@ vector<EQ2Packet*>	Player::UnequipItem(int16 index, sint32 bag_id, int8 slot, in
 			item->details.inv_slot_id = bag_id;
 			item->details.slot_id = slot;
 			item->details.appearance_type = 0;
-			item_list.AddItem(item);
-			item->save_needed = true;
-			SetEquippedItemAppearances();
-			packets.push_back(item->serialize(version, false));
-			packets.push_back(equipList->serialize(version, this));
-			packets.push_back(item_list.serialize(this, version));
+			
+			if(item_list.AddItem(item)) {
+				item->save_needed = true;
+				SetEquippedItemAppearances();
+				// SerializeItemPackets serves item and equipList in opposite order is why we don't use that function here..
+				packets.push_back(item->serialize(version, false));
+				packets.push_back(equipList->serialize(version, this));
+				packets.push_back(item_list.serialize(this, version));
+			}
+			else {
+				LogWrite(PLAYER__ERROR, 0, "Player", "failed to add item to item_list during UnequipItem, index %u, bag id %i, slot %u, version %u, appearance type %u", index, bag_id, slot, version, appearance_type);
+			}
 		}
 		else if (to_item && to_item->IsBag() && to_item->details.num_slots > 0) {
 			bool free_slot = false;
@@ -1886,13 +1892,8 @@ vector<EQ2Packet*>	Player::UnequipItem(int16 index, sint32 bag_id, int8 slot, in
 					item->details.inv_slot_id = to_item->details.bag_id;
 					item->details.slot_id = i;
 					item->details.appearance_type = to_item->details.appearance_type;
-					item_list.AddItem(item);
-					item->save_needed = true;
-					SetEquippedItemAppearances();
-					packets.push_back(equipList->serialize(version, this));
-					packets.push_back(item->serialize(version, false));
-					packets.push_back(to_item->serialize(version, false, this));
-					packets.push_back(item_list.serialize(this, version));
+					
+					SerializeItemPackets(equipList, &packets, item, version, to_item);
 					free_slot = true;
 					break;
 				}
@@ -1945,12 +1946,7 @@ vector<EQ2Packet*>	Player::UnequipItem(int16 index, sint32 bag_id, int8 slot, in
 					item->details.inv_slot_id = bag_id;
 					item->details.slot_id = slot;
 					item->details.appearance_type = 0;
-					item_list.AddItem(item);
-					item->save_needed = true;
-					SetEquippedItemAppearances();
-					packets.push_back(equipList->serialize(version, this));
-					packets.push_back(item->serialize(version, false));
-					packets.push_back(item_list.serialize(this, version));
+					SerializeItemPackets(equipList, &packets, item, version);
 				}
 			}
 			else {
@@ -1969,12 +1965,7 @@ vector<EQ2Packet*>	Player::UnequipItem(int16 index, sint32 bag_id, int8 slot, in
 					item->details.inv_slot_id = bag_id;
 					item->details.slot_id = slot;
 					item->details.appearance_type = 0;
-					item_list.AddItem(item);
-					item->save_needed = true;
-					SetEquippedItemAppearances();
-					packets.push_back(equipList->serialize(version, this));
-					packets.push_back(item->serialize(version, false));
-					packets.push_back(item_list.serialize(this, version));
+					SerializeItemPackets(equipList, &packets, item, version);
 				}
 			}
 		}
@@ -2104,17 +2095,23 @@ vector<EQ2Packet*> Player::EquipItem(int16 index, int16 version, int8 appearance
 		equipList = &appearance_equipment_list;
 
 	vector<EQ2Packet*>	packets;
-	if (item_list.indexed_items.count(index) == 0)
+	item_list.MPlayerItems.readlock(__FUNCTION__, __LINE__);
+	if (item_list.indexed_items.count(index) == 0) {
+		item_list.MPlayerItems.releasereadlock(__FUNCTION__, __LINE__);
 		return packets;
+	}
 	Item* item = item_list.indexed_items[index];
 	slot_id = ConvertSlotFromClient(slot_id, version);
 	if (item) {
-		if (slot_id != 255 && !item->HasSlot(slot_id))
+		if (slot_id != 255 && !item->HasSlot(slot_id)) {
+			item_list.MPlayerItems.releasereadlock(__FUNCTION__, __LINE__);
 			return packets;
+		}
 		int8 slot = equipList->GetFreeSlot(item, slot_id);
 		bool canEquip = CanEquipItem(item);
 		if(canEquip && !appearance_type && item->CheckFlag2(APPEARANCE_ONLY))
 		{
+			item_list.MPlayerItems.releasereadlock(__FUNCTION__, __LINE__);
 			GetClient()->SimpleMessage(CHANNEL_COLOR_RED, "This item is for appearance slots only.");
 			return packets;
 		}
@@ -2133,6 +2130,7 @@ vector<EQ2Packet*> Player::EquipItem(int16 index, int16 version, int8 appearance
 			packet->setDataByName("unknown4", 1);
 			packets.push_back(packet->serialize());
 			safe_delete(packet);
+			item_list.MPlayerItems.releasereadlock(__FUNCTION__, __LINE__);
 			return packets;
 		}
 		if (canEquip && slot == 255)
@@ -2141,21 +2139,37 @@ vector<EQ2Packet*> Player::EquipItem(int16 index, int16 version, int8 appearance
 				slot = item->slot_data.at(0);
 			else
 				slot = slot_id;
+			item_list.MPlayerItems.releasereadlock(__FUNCTION__, __LINE__);
 			packets = UnequipItem(slot, item->details.inv_slot_id, item->details.slot_id, version, appearance_type, false);
+			// grab player items lock again and assure item still present
+			item_list.MPlayerItems.readlock(__FUNCTION__, __LINE__);
+			if (item_list.indexed_items.count(index) == 0) {
+				item_list.MPlayerItems.releasereadlock(__FUNCTION__, __LINE__);
+				return packets;
+			}
 			// If item is a 2handed weapon and something is in the secondary, unequip the secondary
 			if (item->IsWeapon() && item->weapon_info->wield_type == ITEM_WIELD_TYPE_TWO_HAND && equipList->GetItem(EQ2_SECONDARY_SLOT) != 0) {
+				item_list.MPlayerItems.releasereadlock(__FUNCTION__, __LINE__);
 				vector<EQ2Packet*> tmp_packets = UnequipItem(EQ2_SECONDARY_SLOT, -999, 0, version, appearance_type, false);
 				//packets.reserve(packets.size() + tmp_packets.size());
 				packets.insert(packets.end(), tmp_packets.begin(), tmp_packets.end());
 			}
+			
+			// release for delete item / scripting etc
+			item_list.MPlayerItems.releasereadlock(__FUNCTION__, __LINE__);
 		}
 		else if (canEquip && slot < 255) {
 			// If item is a 2handed weapon and something is in the secondary, unequip the secondary
 			if (item->IsWeapon() && item->weapon_info->wield_type == ITEM_WIELD_TYPE_TWO_HAND && equipList->GetItem(EQ2_SECONDARY_SLOT) != 0) {
+				item_list.MPlayerItems.releasereadlock(__FUNCTION__, __LINE__);
 				vector<EQ2Packet*> tmp_packets = UnequipItem(EQ2_SECONDARY_SLOT, -999, 0, version, appearance_type, false);
 				//packets.reserve(packets.size() + tmp_packets.size());
 				packets.insert(packets.end(), tmp_packets.begin(), tmp_packets.end());
 			}
+			else {
+				// release for delete item / scripting etc
+				item_list.MPlayerItems.releasereadlock(__FUNCTION__, __LINE__);
+			}
 
 			database.DeleteItem(GetCharacterID(), item, "NOT-EQUIPPED");
 
@@ -2219,9 +2233,8 @@ bool Player::AddItemToBank(Item* item) {
 			item->details.inv_slot_id = bag;
 			item->details.slot_id = slot;
 			item->save_needed = true;
-			item_list.AddItem(item);
 
-			return true;
+			return item_list.AddItem(item);
 		}
 		else if (item_list.AddOverflowItem(item))
 			return true;
@@ -2235,7 +2248,19 @@ EQ2Packet* Player::SendInventoryUpdate(int16 version) {
 	
 	return item_list.serialize(this, version);
 }
-EQ2Packet* Player::MoveInventoryItem(sint32 to_bag_id, int16 from_index, int8 new_slot, int8 charges, int8 appearance_type, int16 version) {
+
+void Player::UpdateInventory(int32 bag_id) {
+
+	EQ2Packet* outapp = client->GetPlayer()->SendInventoryUpdate(client->GetVersion());
+	client->QueuePacket(outapp);
+
+	outapp = client->GetPlayer()->SendBagUpdate(bag_id, client->GetVersion());
+
+	if (outapp)
+		client->QueuePacket(outapp);
+
+}
+EQ2Packet* Player::MoveInventoryItem(sint32 to_bag_id, int16 from_index, int8 new_slot, int8 charges, int8 appearance_type, bool* item_deleted, int16 version) {
 	Item* item = item_list.GetItemFromIndex(from_index);
 	int8 result = item_list.MoveItem(to_bag_id, from_index, new_slot, appearance_type, charges);
 	if (result == 1) {
@@ -2244,7 +2269,10 @@ EQ2Packet* Player::MoveInventoryItem(sint32 to_bag_id, int16 from_index, int8 ne
 				item->save_needed = true;
 			else if (item->needs_deletion) {
 				database.DeleteItem(GetCharacterID(), item, 0);
-				safe_delete(item);
+				client->GetPlayer()->item_list.DestroyItem(from_index);
+				client->GetPlayer()->UpdateInventory(to_bag_id);
+				if(item_deleted)
+					*item_deleted = true;
 			}
 		}
 		return item_list.serialize(this, version);
@@ -4302,7 +4330,7 @@ void Player::CheckQuestsCraftUpdate(Item* item, int32 qty){
 	for(itr = player_quests.begin(); itr != player_quests.end(); itr++){
 		if(itr->second){
 			if(item && qty > 0){
-				if(itr->second->CheckQuestCraftUpdate(item->details.item_id, qty)){
+				if(itr->second->CheckQuestRefIDUpdate(item->details.item_id, qty)){
 					update_list->push_back(itr->second);
 				}
 			}
@@ -4329,7 +4357,7 @@ void Player::CheckQuestsHarvestUpdate(Item* item, int32 qty){
 	for(itr = player_quests.begin(); itr != player_quests.end(); itr++){
 		if(itr->second){
 			if(item && qty > 0){
-				if(itr->second->CheckQuestHarvestUpdate(item->details.item_id, qty)){
+				if(itr->second->CheckQuestRefIDUpdate(item->details.item_id, qty)){
 					update_list->push_back(itr->second);
 				}
 			}
@@ -6800,4 +6828,21 @@ void Player::SetLevel(int16 level, bool setUpdateFlags) {
 	SetInfo(&appearance.level, level, setUpdateFlags);
 	SetXP(0);
 	SetNeededXP();
+}
+
+bool Player::SerializeItemPackets(EquipmentItemList* equipList, vector<EQ2Packet*>* packets, Item* item, int16 version, Item* to_item) {
+	if(item_list.AddItem(item)) {
+		item->save_needed = true;
+		SetEquippedItemAppearances();
+		packets->push_back(equipList->serialize(version, this));
+		packets->push_back(item->serialize(version, false));
+		if(to_item)
+			packets->push_back(to_item->serialize(version, false, this));
+		packets->push_back(item_list.serialize(this, version));
+		return true;
+	}
+	else {
+		LogWrite(PLAYER__ERROR, 0, "Player", "failed to add item to item_list");
+	}
+	return false;
 }

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

@@ -507,7 +507,8 @@ public:
 	/// <param name='skill_id'>The id of the skill to check</param>
 	/// <returns>A vector of int32's of the spell id's</returns>
 	vector<int32> GetSpellBookSpellIDBySkill(int32 skill_id);
-	EQ2Packet* MoveInventoryItem(sint32 to_bag_id, int16 from_index, int8 new_slot, int8 charges, int8 appearance_type, int16 version = 1);
+	void UpdateInventory(int32 bag_id);
+	EQ2Packet* MoveInventoryItem(sint32 to_bag_id, int16 from_index, int8 new_slot, int8 charges, int8 appearance_type, bool* item_deleted, int16 version = 1);
 	bool IsPlayer(){ return true; }
 	MaintainedEffects* GetFreeMaintainedSpellSlot();
 	MaintainedEffects* GetMaintainedSpell(int32 id);
@@ -986,11 +987,11 @@ public:
 		return mentorship_status;
 	}
 
-	void EnableResetMentorship()
-	{
+	void EnableResetMentorship() {
 		reset_mentorship = true;
 	}
 	
+	bool SerializeItemPackets(EquipmentItemList* equipList, vector<EQ2Packet*>* packets, Item* item, int16 version, Item* to_item = 0);
 	Mutex MPlayerQuests;
 private:
 	bool reset_mentorship;

+ 50 - 112
EQ2/source/WorldServer/Quests.cpp

@@ -45,9 +45,9 @@ QuestStep::QuestStep(int32 in_id, int8 in_type, string in_description, vector<in
 		task_group = string(in_task_group);
 	if(type != QUEST_STEP_TYPE_LOCATION) {
 		if (in_ids){
-			ids = new vector<int32>;
+			ids = new std::map<int32, bool>();
 			for(int32 i=0;i<in_ids->size();i++)
-				ids->push_back(in_ids->at(i));
+				ids->insert(make_pair(in_ids->at(i), true));
 		}
 	}
 	else { // location step
@@ -78,9 +78,10 @@ QuestStep::QuestStep(QuestStep* old_step){
 	locations = 0;
 	if(type != QUEST_STEP_TYPE_LOCATION) {
 		if (old_step->ids){
-			ids = new vector<int32>;
-			for(int32 i=0;i<old_step->ids->size();i++)
-				ids->push_back(old_step->ids->at(i));
+			ids = new std::map<int32, bool>();
+			std::map<int32, bool>::iterator itr;
+			for(itr = old_step->ids->begin();itr != old_step->ids->end();itr++)
+				ids->insert(make_pair(itr->first, itr->second));
 		}
 	}
 	else { // location step
@@ -133,15 +134,13 @@ int8 QuestStep::GetStepType(){
 	return type;
 }
 
-bool QuestStep::CheckStepReferencedSpawnID(int32 id){
+bool QuestStep::CheckStepReferencedID(int32 id){
 	bool ret = false;
 	if(ids){
-		for(int32 i=0;i<ids->size();i++){
-			if(ids->at(i) == id){
-				ret = true;
-				break;
-			}
-		}
+		std::map<int32, bool>::iterator itr;
+		itr = ids->find(id);
+		if(itr != ids->end())
+			ret = true;
 	}
 	return ret;
 }
@@ -149,23 +148,12 @@ bool QuestStep::CheckStepReferencedSpawnID(int32 id){
 bool QuestStep::CheckStepKillRaceReqUpdate(Spawn* spawn){
 	bool ret = false;
 	if(ids){
-		for(int32 i=0;i<ids->size();i++){
-			if(ids->at(i) == spawn->GetRace() ||
-			ids->at(i) == race_types_list.GetRaceType(spawn->GetModelType()) ||
-			ids->at(i) == race_types_list.GetRaceBaseType(spawn->GetModelType())){
-				ret = true;
-				break;
-			}
-		}
-	}
-	return ret;
-}
-
-bool QuestStep::CheckStepChatUpdate(int32 id){
-	bool ret = false;
-	if(ids){
-		for(int32 i=0;i<ids->size();i++){
-			if(ids->at(i) == id){
+		std::map<int32, bool>::iterator itr;
+		for(itr = ids->begin();itr != ids->end();itr++){
+			int32 curid = itr->first;
+			if(curid == spawn->GetRace() ||
+			curid == race_types_list.GetRaceType(spawn->GetModelType()) ||
+			curid == race_types_list.GetRaceBaseType(spawn->GetModelType())){
 				ret = true;
 				break;
 			}
@@ -202,19 +190,6 @@ void QuestStep::SetUpdateTargetName(const char* name){
 	update_target_name = string(name);
 }
 
-bool QuestStep::CheckStepItemUpdate(int32 id){
-	bool ret = false;
-	if(ids){
-		for(int32 i=0;i<ids->size();i++){
-			if(ids->at(i) == id){
-				ret = true;
-				break;
-			}
-		}
-	}
-	return ret;
-}
-
 bool QuestStep::CheckStepLocationUpdate(float char_x, float char_y, float char_z, int32 zone_id){
 	bool ret = false;
 	if (locations) {
@@ -255,19 +230,6 @@ bool QuestStep::CheckStepLocationUpdate(float char_x, float char_y, float char_z
 	return ret;
 }
 
-bool QuestStep::CheckStepSpellUpdate(int32 id){
-	bool ret = false;
-	if (ids) {
-		for (int32 i = 0; i < ids->size(); i++) {
-			if (ids->at(i) == id) {
-				ret = true;
-				break;
-			}
-		}
-	}
-	return ret;
-}
-
 void QuestStep::SetStepProgress(int32 val){
 	step_progress = val;
 }
@@ -548,7 +510,7 @@ bool Quest::CheckQuestReferencedSpawns(Spawn* spawn){
 			{
 				case QUEST_STEP_TYPE_KILL:
 				case QUEST_STEP_TYPE_NORMAL: {
-					if(step->CheckStepReferencedSpawnID(spawnDBID))
+					if(step->CheckStepReferencedID(spawnDBID))
 						ret = true;
 					
 					break;
@@ -576,7 +538,7 @@ bool Quest::CheckQuestKillUpdate(Spawn* spawn, bool update){
 		if(!step)
 			continue;
 
-			if((step->GetStepType() == QUEST_STEP_TYPE_KILL && !step->Complete() && step->CheckStepReferencedSpawnID(id)) ||
+			if((step->GetStepType() == QUEST_STEP_TYPE_KILL && !step->Complete() && step->CheckStepReferencedID(id)) ||
 			 (step->GetStepType() == QUEST_STEP_TYPE_KILL_RACE_REQ && !step->Complete() && step->CheckStepKillRaceReqUpdate(spawn)))
 			{
 				if (update == true) {
@@ -750,7 +712,7 @@ bool Quest::CheckQuestChatUpdate(int32 id, bool update){
 	MQuestSteps.lock();
 	for(int32 i=0;i<quest_steps.size(); i++){
 		step = quest_steps[i];
-		if(step && step->GetStepType() == QUEST_STEP_TYPE_CHAT && !step->Complete() && step->CheckStepChatUpdate(id)){
+		if(step && step->GetStepType() == QUEST_STEP_TYPE_CHAT && !step->Complete() && step->CheckStepReferencedID(id)){
 			if(update){
 				//Call the progress action function with the total amount of progress actually granted
 				prog_added = step->AddStepProgress(1);
@@ -774,7 +736,7 @@ bool Quest::CheckQuestItemUpdate(int32 id, int8 quantity){
 	MQuestSteps.lock();
 	for(int32 i=0;i<quest_steps.size(); i++){
 		step = quest_steps[i];
-		if(step && step->GetStepType() == QUEST_STEP_TYPE_OBTAIN_ITEM && !step->Complete() && step->CheckStepItemUpdate(id)){
+		if(step && step->GetStepType() == QUEST_STEP_TYPE_OBTAIN_ITEM && !step->Complete() && step->CheckStepReferencedID(id)){
 			bool passed = true;
 			if(step->GetPercentage() < 100)
 				passed = (step->GetPercentage() > MakeRandomFloat(0, 100));
@@ -796,66 +758,42 @@ bool Quest::CheckQuestItemUpdate(int32 id, int8 quantity){
 	return ret;
 }
 
-bool Quest::CheckQuestCraftUpdate(int32 id, int32 quantity){
+bool Quest::CheckQuestRefIDUpdate(int32 id, int32 quantity){
 	QuestStep* step = 0;
 	bool ret = false;
 	int32 prog_added = 0;
 	MQuestSteps.lock();
 	for(int32 i=0;i<quest_steps.size(); i++){
 		step = quest_steps[i];
-		if(step && step->GetStepType() == QUEST_STEP_TYPE_CRAFT && !step->Complete()){
-			vector<int32>* id_list = step->GetUpdateIDs();
-			for(int32 i=0;i<id_list->size(); i++){
-				int32 update_id = id_list->at(i);
-				if(update_id == id){
-					bool passed = true;
-					if(step->GetPercentage() < 100)
-						passed = (step->GetPercentage() > MakeRandomFloat(0, 100));
-					if(passed){
-						//Call the progress action function with the total amount of progress actually granted
-						prog_added = step->AddStepProgress(quantity);
-						if(lua_interface && progress_actions[step->GetStepID()].length() > 0 && prog_added > 0)
-							lua_interface->CallQuestFunction(this, progress_actions[step->GetStepID()].c_str(), player, prog_added);
-						step_updates.push_back(step);
-					}
-					else
-						step_failures.push_back(step);
-					ret = true;
-				}
-			}
-		}
-	}
-	MQuestSteps.unlock();
-	if(ret)
-		SetSaveNeeded(true);
-	return ret;
-}
+		if(step)
+		{
+			if(step->Complete())
+				continue;
 
-bool Quest::CheckQuestHarvestUpdate(int32 id, int32 quantity){
-	QuestStep* step = 0;
-	bool ret = false;
-	int32 prog_added = 0;
-	MQuestSteps.lock();
-	for(int32 i=0;i<quest_steps.size(); i++){
-		step = quest_steps[i];
-		if(step && step->GetStepType() == QUEST_STEP_TYPE_HARVEST && !step->Complete()){
-			vector<int32>* id_list = step->GetUpdateIDs();
-			for(int32 i=0;i<id_list->size(); i++){
-				int32 update_id = id_list->at(i);
-				if(update_id == id){
-					bool passed = true;
-					if(step->GetPercentage() < 100)
-						passed = (step->GetPercentage() > MakeRandomFloat(0, 100));
-					if(passed){
-						//Call the progress action function with the total amount of progress actually granted
-						prog_added = step->AddStepProgress(quantity);
-						if(lua_interface && progress_actions[step->GetStepID()].length() > 0 && prog_added > 0)
-							lua_interface->CallQuestFunction(this, progress_actions[step->GetStepID()].c_str(), player, prog_added);
-						step_updates.push_back(step);
+			switch(step->GetStepType()) {
+				case QUEST_STEP_TYPE_HARVEST:
+				case QUEST_STEP_TYPE_CRAFT: {
+					map<int32, bool>* id_list = step->GetUpdateIDs();
+					map<int32, bool>::iterator itr;
+					for(itr = id_list->begin();itr != id_list->end(); itr++){
+						int32 update_id = itr->first;
+						if(update_id == id){
+							bool passed = true;
+							if(step->GetPercentage() < 100)
+								passed = (step->GetPercentage() > MakeRandomFloat(0, 100));
+							if(passed){
+								//Call the progress action function with the total amount of progress actually granted
+								prog_added = step->AddStepProgress(quantity);
+								if(lua_interface && progress_actions[step->GetStepID()].length() > 0 && prog_added > 0)
+									lua_interface->CallQuestFunction(this, progress_actions[step->GetStepID()].c_str(), player, prog_added);
+								step_updates.push_back(step);
+							}
+							else
+								step_failures.push_back(step);
+							ret = true;
+						}
 					}
-					else
-						step_failures.push_back(step);
-					ret = true;
+					break;
 				}
 			}
 		}
@@ -896,7 +834,7 @@ bool Quest::CheckQuestSpellUpdate(Spell* spell){
 	MQuestSteps.lock();
 	for (int32 i = 0; i < quest_steps.size(); i++) {
 		step = quest_steps[i];
-		if(step && step->GetStepType() == QUEST_STEP_TYPE_SPELL && !step->Complete() && step->CheckStepSpellUpdate(id)){
+		if(step && step->GetStepType() == QUEST_STEP_TYPE_SPELL && !step->Complete() && step->CheckStepReferencedID(id)){
 			bool passed = true;
 			if(step->GetPercentage() < 100)
 				passed = (step->GetPercentage() > MakeRandomFloat(0, 100));

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

@@ -67,11 +67,8 @@ public:
 	QuestStep(QuestStep* old_step);
 	~QuestStep();
 	bool			CheckStepKillRaceReqUpdate(Spawn* spawn);
-	bool			CheckStepReferencedSpawnID(int32 id);
-	bool			CheckStepChatUpdate(int32 id);
-	bool			CheckStepItemUpdate(int32 id);
+	bool			CheckStepReferencedID(int32 id);
 	bool			CheckStepLocationUpdate(float char_x, float char_y, float char_z, int32 zone_id);
-	bool			CheckStepSpellUpdate(int32 id);
 	int32			AddStepProgress(int32 val);
 	void			SetStepProgress(int32 val);
 	int32			GetStepProgress();
@@ -84,7 +81,7 @@ public:
 	void			SetDescription(string desc);
 	int16			GetQuestCurrentQuantity();
 	int16			GetQuestNeededQuantity();
-	vector<int32>*  GetUpdateIDs() { return ids; }
+	map<int32, bool>*  GetUpdateIDs() { return ids; }
 	int16			GetIcon();
 	void			SetIcon(int16 in_icon);
 	const char*		GetUpdateTargetName();
@@ -107,7 +104,7 @@ private:
 	int16				icon;
 	int8				type;
 	string				description;
-	vector<int32>*		ids;
+	std::map<int32, bool>*	ids;
 	int32				quantity;
 	string				task_group;
 	vector<Location>*	locations;
@@ -166,8 +163,7 @@ public:
 	bool				CheckQuestItemUpdate(int32 id, int8 quantity = 1);
 	bool				CheckQuestLocationUpdate(float char_x, float char_y, float char_z, int32 zone_id);
 	bool				CheckQuestSpellUpdate(Spell* spell);
-	bool                CheckQuestCraftUpdate(int32 id, int32 quantity = 1);
-	bool                CheckQuestHarvestUpdate(int32 id, int32 quantity = 1);
+	bool                CheckQuestRefIDUpdate(int32 id, int32 quantity = 1);
 
 	int8				GetQuestLevel();
 	int8				GetVisible();

+ 10 - 1
EQ2/source/WorldServer/SpellProcess.cpp

@@ -50,7 +50,7 @@ void SpellProcess::RemoveAllSpells(){
 
 	MutexList<LuaSpell*>::iterator active_spells_itr = active_spells.begin();
 	while(active_spells_itr.Next()){
-		DeleteCasterSpell(active_spells_itr->value);
+		DeleteCasterSpell(active_spells_itr->value, "", true);
 	}
 
 	active_spells_itr = active_spells.begin();
@@ -1364,6 +1364,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 
 		if(!CheckPower(lua_spell)) 
 		{
+			LogWrite(SPELL__WARNING, 1, "Spell", "%s: Caster Lacked Power to cast (%s).", spell->GetName(), caster->GetName());
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ENOUGH_POWER);
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			DeleteSpell(lua_spell);
@@ -1372,6 +1373,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 
 		if (!CheckHP(lua_spell)) 
 		{ 
+			LogWrite(SPELL__WARNING, 1, "Spell", "%s: Caster Lacked Health to cast (%s).", spell->GetName(), caster->GetName());
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ENOUGH_HEALTH);
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			DeleteSpell(lua_spell);
@@ -1380,6 +1382,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 
 		if (!CheckSavagery(lua_spell))
 		{
+			LogWrite(SPELL__WARNING, 1, "Spell", "%s: Caster Lacked Savagery to cast (%s).", spell->GetName(), caster->GetName());
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ENOUGH_SAVAGERY);
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			DeleteSpell(lua_spell);
@@ -1388,6 +1391,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 
 		if (!CheckDissonance(lua_spell))
 		{
+			LogWrite(SPELL__WARNING, 1, "Spell", "%s: Caster Lacked Dissonance to cast (%s).", spell->GetName(), caster->GetName());
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ENOUGH_DISSONANCE);
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			DeleteSpell(lua_spell);
@@ -1396,6 +1400,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 
 		if (!CheckConcentration(lua_spell)) 
 		{
+			LogWrite(SPELL__WARNING, 1, "Spell", "%s: Caster Lacked Concentration to cast (%s).", spell->GetName(), caster->GetName());
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ENOUGH_CONC);
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			DeleteSpell(lua_spell);
@@ -2856,4 +2861,8 @@ void SpellProcess::AddSelfAndPetToCharTargets(LuaSpell* spell, Spawn* caster, bo
 		spell->char_id_targets.insert(make_pair(charID, player->GetPet()->GetPetType()));
 	if(!onlyPet)
 		spell->char_id_targets.insert(make_pair(charID, 0x00));
+}
+
+void SpellProcess::DeleteActiveSpell(LuaSpell* spell) {
+	active_spells.Remove(spell);
 }

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

@@ -394,6 +394,7 @@ public:
 	void AddActiveSpell(LuaSpell* spell);
 	static void AddSelfAndPet(LuaSpell* spell, Spawn* self, bool onlyPet=false);
 	static void AddSelfAndPetToCharTargets(LuaSpell* spell, Spawn* caster, bool onlyPet=false);
+	void DeleteActiveSpell(LuaSpell* spell);
 private:
 	Mutex MSpellProcess;
 	MutexMap<Entity*,Spell*> spell_que;

+ 6 - 4
EQ2/source/WorldServer/Transmute.cpp

@@ -233,9 +233,10 @@ void Transmute::CompleteTransmutation(Client* client, Player* player) {
 	if (item1) {
 		item1->details.count = 1;
 		client->Message(89, "     %s", item1->CreateItemLink(client->GetVersion(), false).c_str());
-		client->AddItem(item1);
+		bool itemDeleted = false;
+		client->AddItem(item1, &itemDeleted);
 
-		if (packet) {
+		if (packet && !itemDeleted) {
 			packet->setArrayDataByName("reward_id", item1->details.item_id, 0);
 			if (client->GetVersion() < 860)
 				packet->setItemArrayDataByName("item", item1, player, 0, 0, -1);
@@ -249,9 +250,10 @@ void Transmute::CompleteTransmutation(Client* client, Player* player) {
 	if (item2) {
 		item2->details.count = 1;
 		client->Message(89, "     %s", item2->CreateItemLink(client->GetVersion(), false).c_str());
-		client->AddItem(item2);
+		bool itemDeleted = false;
+		client->AddItem(item2, &itemDeleted);
 
-		if (packet) {
+		if (packet && !itemDeleted) {
 			int32 dataIndex = 1;
 			if (!item1) {
 				packet->setArrayLengthByName("num_rewards", 1);

+ 31 - 21
EQ2/source/WorldServer/client.cpp

@@ -6372,7 +6372,7 @@ bool Client::AddItem(int32 item_id, int16 quantity) {
 	return false;
 }
 
-bool Client::AddItem(Item* item) {
+bool Client::AddItem(Item* item, bool* item_deleted) {
 	if (!item) {
 		return false;
 	}
@@ -6403,6 +6403,10 @@ bool Client::AddItem(Item* item) {
 	else {
 		SimpleMessage(CHANNEL_COLOR_RED, "Could not find free slot to place item.");
 		safe_delete(item);
+
+		if(item_deleted)
+			*item_deleted = true;
+
 		return false;
 	}
 
@@ -6686,6 +6690,7 @@ void Client::BuyBack(int32 item_id, int16 quantity) {
 				else
 					item->details.count = closest->quantity;
 			}
+			bool itemDeleted = false;
 			bool itemAdded = false;
 			sint64 dispFlags = 0;
 			if (item && item->GetItemScript() && lua_interface && lua_interface->RunItemScript(item->GetItemScript(), "buyback_display_flags", item, player, &dispFlags) && (dispFlags & DISPLAY_FLAG_NO_BUY))
@@ -6709,8 +6714,7 @@ void Client::BuyBack(int32 item_id, int16 quantity) {
 					closest->quantity -= quantity;
 					closest->save_needed = true;
 				}
-				AddItem(item);
-				itemAdded = true;
+				itemAdded = AddItem(item, &itemDeleted);
 				
 				if (removed) {
 					database.DeleteBuyBack(GetCharacterID(), closest->item_id, closest->quantity, closest->price);
@@ -6723,7 +6727,7 @@ void Client::BuyBack(int32 item_id, int16 quantity) {
 			else
 				SimpleMessage(CHANNEL_COLOR_RED, "You cannot afford this item.");
 
-			if(!itemAdded)
+			if(!itemAdded && !itemDeleted)
 				safe_delete(item);
 		}
 	}
@@ -6794,15 +6798,18 @@ void Client::BuyItem(int32 item_id, int16 quantity) {
 							Message(CHANNEL_MERCHANT_BUY_SELL, "You buy %i %s from %s for%s.", quantity, master_item->CreateItemLink(GetVersion()).c_str(), spawn->GetName(), GetCoinMessage(total_buy_price).c_str());
 						else
 							Message(CHANNEL_MERCHANT_BUY_SELL, "You buy %s from %s for%s.", master_item->CreateItemLink(GetVersion()).c_str(), spawn->GetName(), GetCoinMessage(total_buy_price).c_str());
-						AddItem(item);
-						CheckPlayerQuestsItemUpdate(item);
-						if (item && total_available < 0xFF) {
-							world.DecreaseMerchantQuantity(spawn->GetMerchantID(), item_id, quantity);
-							SendBuyMerchantList();
+						bool itemDeleted = false;
+						AddItem(item, &itemDeleted);
+						if(!itemDeleted) {
+							CheckPlayerQuestsItemUpdate(item);
+							if (item && total_available < 0xFF) {
+								world.DecreaseMerchantQuantity(spawn->GetMerchantID(), item_id, quantity);
+								SendBuyMerchantList();
+							}
+							
+							if (spawn->GetMerchantType() & MERCHANT_TYPE_LOTTO)
+								PlayLotto(total_buy_price, item->details.item_id);
 						}
-						
-						if (spawn->GetMerchantType() & MERCHANT_TYPE_LOTTO)
-							PlayLotto(total_buy_price, item->details.item_id);
 					}
 					else {
 						Message(CHANNEL_COLOR_RED, "You do not have enough coin to purchase %s.", master_item->CreateItemLink(GetVersion()).c_str());
@@ -6888,16 +6895,19 @@ void Client::BuyItem(int32 item_id, int16 quantity) {
 								Message(CHANNEL_MERCHANT_BUY_SELL, "You buy %i %s from %s for%s.", quantity, master_item->CreateItemLink(GetVersion()).c_str(), spawn->GetName(), GetCoinMessage(ItemInfo->price_coins * quantity).c_str());
 							else
 								Message(CHANNEL_MERCHANT_BUY_SELL, "You buy %s from %s for%s.", master_item->CreateItemLink(GetVersion()).c_str(), spawn->GetName(), GetCoinMessage(ItemInfo->price_coins * quantity).c_str());
-							AddItem(item);
-							CheckPlayerQuestsItemUpdate(item);
-							if (item && total_available < 0xFF) {
-								world.DecreaseMerchantQuantity(spawn->GetMerchantID(), item_id, quantity);
-								SendBuyMerchantList();
+							bool itemDeleted = false;
+							AddItem(item, &itemDeleted);
+							if(itemDeleted) {
+								CheckPlayerQuestsItemUpdate(item);
+								if (item && total_available < 0xFF) {
+									world.DecreaseMerchantQuantity(spawn->GetMerchantID(), item_id, quantity);
+									SendBuyMerchantList();
+								}
+								
+								SendSellMerchantList();
+								if (spawn->GetMerchantType() & MERCHANT_TYPE_LOTTO)
+									PlayLotto(total_buy_price, item->details.item_id);
 							}
-							
-							SendSellMerchantList();
-							if (spawn->GetMerchantType() & MERCHANT_TYPE_LOTTO)
-								PlayLotto(total_buy_price, item->details.item_id);
 
 						}
 						else {

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

@@ -199,7 +199,7 @@ public:
 	Spawn*	GetBanker();
 	void	SetBanker(Spawn* in_banker);
 	bool	AddItem(int32 item_id, int16 quantity = 0);
-	bool	AddItem(Item* item);
+	bool	AddItem(Item* item, bool* item_deleted = 0);
 	bool	AddItemToBank(int32 item_id, int16 quantity = 0);
 	bool	AddItemToBank(Item* item);
 	bool	RemoveItem(Item *item, int16 quantity, bool force_override_no_delete = false);

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

@@ -381,7 +381,6 @@ void ZoneServer::DeleteSpellProcess(){
 	MMasterSpawnLock.writelock(__FUNCTION__, __LINE__);
 	MMasterZoneLock->lock();
 	reloading_spellprocess = true;
-
 	// Remove spells from NPC's
 	Spawn* spawn = 0;
 	map<int32, Spawn*>::iterator itr;
@@ -390,6 +389,9 @@ void ZoneServer::DeleteSpellProcess(){
 		spawn = itr->second;
 		if(spawn && spawn->IsNPC())
 			((NPC*)spawn)->SetSpells(0);
+		
+		if(spawn->IsEntity())
+			((Entity*)spawn)->RemoveSpellBonus(nullptr, true);
 	}
 	MSpawnList.releasereadlock(__FUNCTION__, __LINE__);