Browse Source

Broke fixes, 1h/2h equip fixes, swapping equip works, more crash fixes

Partial Address Issue #328 - tier 0 items display, 2h now display

Fix #298 - can't equip a 1h while 2h is equipped.  Swapping equipment slots now works for both combat/appearance equip.

Fix #201 - when scribing, new tier spell will display on hover over of spellbook or hotbar

Fixed a crash with spell conflict doing double delete on lua spell

Fix #152 - addressed divine awakening, via new target_type (10) SPELL_TARGET_ALLGROUPTARGETS - this will call cast for each player instead of just the direct target
righteousness and decree now supported as a group target AE

Better connection closure from client to world on camping/zoning out
Image 3 years ago
parent
commit
4c9197f54e

+ 12 - 2
EQ2/source/WorldServer/Commands/Commands.cpp

@@ -1773,7 +1773,11 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 								player->item_list.RemoveItem(item, true);
 								client->QueuePacket(player->GetSpellBookUpdatePacket(client->GetVersion()));
 								client->QueuePacket(player->SendInventoryUpdate(client->GetVersion()));
-							}
+
+								// force purge client cache and display updated spell for hover over
+								EQ2Packet* app = spell->SerializeSpell(client, false, false);
+								client->QueuePacket(app);
+													}
 						}
 						else
 							LogWrite(COMMAND__ERROR, 0, "Command", "Unknown spell ID: %u and tier: %u", item->skill_info->spell_id, item->skill_info->spell_tier);
@@ -3669,6 +3673,8 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 			if(sep && sep->arg[0][0]){
 				const char* values = sep->argplus[0];
 				if(values){
+					LogWrite(ITEM__WARNING, 0, "Item", "SearchStores: %s", values);
+
 					map<string, string> str_values = TranslateBrokerRequest(values);
 					vector<Item*>* items = master_item_list.GetItems(str_values);
 					if(items){
@@ -6177,7 +6183,11 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma
 			LogWrite(MISC__TODO, 1, "TODO", " fix this, need to get how live does it\n\t(%s, function: %s, line #: %i)", __FILE__, __FUNCTION__, __LINE__);
 			int16 index1 = atoi(sep->arg[1]);
 			int16 index2 = atoi(sep->arg[2]);
-			EQ2Packet* outapp = client->GetPlayer()->SwapEquippedItems(index1, index2, client->GetVersion());
+			int8 type = 0;
+			if(sep->IsNumber(3))
+				type = atoul(sep->arg[3]); // type 0 is combat, 3 = appearance
+			
+			EQ2Packet* outapp = client->GetPlayer()->SwapEquippedItems(index1, index2, client->GetVersion(), type);
 
 			if(outapp)
 				client->QueuePacket(outapp);

+ 31 - 5
EQ2/source/WorldServer/Items/Items.cpp

@@ -88,7 +88,7 @@ MasterItemList::~MasterItemList(){
 	RemoveAll();
 }
 
-vector<Item*>* MasterItemList::GetItems(string name, int32 itype, int32 ltype, int32 btype, int64 minprice, int64 maxprice, int8 minskill, int8 maxskill, string seller, string adornment, int8 mintier, int8 maxtier, int16 minlevel, int16 maxlevel, sint8 itemclass){
+vector<Item*>* MasterItemList::GetItems(string name, int64 itype, int32 ltype, int32 btype, int64 minprice, int64 maxprice, int8 minskill, int8 maxskill, string seller, string adornment, int8 mintier, int8 maxtier, int16 minlevel, int16 maxlevel, sint8 itemclass){
 	vector<Item*>* ret = new vector<Item*>;
 	map<int32,Item*>::iterator iter;
     Item* item = 0;
@@ -101,11 +101,12 @@ vector<Item*>* MasterItemList::GetItems(string name, int32 itype, int32 ltype, i
 	//	chkseller = seller.c_str();
 	//if(adornment.length() > 0)
 	//	chkadornment = adornment.c_str();
+	LogWrite(ITEM__WARNING, 0, "Item", "Get Items: %s (itype: %llu, ltype: %u, btype: %u, minskill: %u, maxskill: %u, mintier: %u, maxtier: %u, minlevel: %u, maxlevel: %u itemclass %i)", name.c_str(), itype, ltype, btype, minskill, maxskill, mintier, maxtier, minlevel, maxlevel, itemclass);
 	bool should_add = true;
 	for(iter = items.begin();iter != items.end(); iter++){
 		item = iter->second;
 		if(item){
-			if(itype != ITEM_BROKER_TYPE_ANY){
+			if(itype != ITEM_BROKER_TYPE_ANY && itype != ITEM_BROKER_TYPE_ANY64BIT){
 				should_add = false;
 				switch(itype){
 					case ITEM_BROKER_TYPE_ADORNMENT:{
@@ -253,6 +254,18 @@ vector<Item*>* MasterItemList::GetItems(string name, int32 itype, int32 ltype, i
 							should_add = true;
 						break;
 					}
+					case ITEM_BROKER_TYPE_2H_CRUSH:{
+						should_add = item->IsWeapon() && item->weapon_info->wield_type == ITEM_WIELD_TYPE_TWO_HAND && item->generic_info.skill_req1 == SKILL_ID_STAFF;
+						break;
+					}
+					case ITEM_BROKER_TYPE_2H_PIERCE:{
+						should_add = item->IsWeapon() && item->weapon_info->wield_type == ITEM_WIELD_TYPE_TWO_HAND && item->generic_info.skill_req1 == SKILL_ID_GREATSPEAR;
+						break;
+					}
+					case ITEM_BROKER_TYPE_2H_SLASH:{
+						should_add = item->IsWeapon() && item->weapon_info->wield_type == ITEM_WIELD_TYPE_TWO_HAND && item->generic_info.skill_req1 == SKILL_ID_GREATSWORD;
+						break;
+					}
 				}
 				if(!should_add)
 					continue;
@@ -635,7 +648,8 @@ vector<Item*>* MasterItemList::GetItems(string name, int32 itype, int32 ltype, i
 				if(maxlevel > 0 && ((item->generic_info.adventure_default_level > 0 && item->generic_info.adventure_default_level > maxlevel) || (item->generic_info.tradeskill_default_level > 0 && item->generic_info.tradeskill_default_level > maxlevel)))
 					continue;
 			}
-			if(mintier > 0 && item->details.tier < mintier)
+			// mintier of 1 is 'ANY'
+			if(mintier > 1 && item->details.tier < mintier)
 				continue;
 			if(maxtier > 0 && item->details.tier > maxtier)
 				continue;
@@ -651,7 +665,7 @@ vector<Item*>* MasterItemList::GetItems(string name, int32 itype, int32 ltype, i
 
 vector<Item*>* MasterItemList::GetItems(map<string, string> criteria){
 	string name, seller, adornment;
-	int32 itype = 0xFFFFFFFF;
+	int64 itype = 0xFFFFFFFFFFFFFFFF;
 	int32 ltype = 0xFFFFFFFF;
 	int32 btype = 0xFFFFFFFF;
 	int64 minprice = 0;
@@ -691,7 +705,7 @@ vector<Item*>* MasterItemList::GetItems(map<string, string> criteria){
 	if(criteria.count("MAXLEVEL") > 0)
 		maxlevel = (int16)ParseIntValue(criteria["MAXLEVEL"]);
 	if(criteria.count("ITYPE") > 0)
-		itype = ParseIntValue(criteria["ITYPE"]);
+		itype = ParseLongLongValue(criteria["ITYPE"]);
 	if(criteria.count("LTYPE") > 0)
 		ltype = ParseIntValue(criteria["LTYPE"]);
 	if(criteria.count("BTYPE") > 0)
@@ -3910,6 +3924,12 @@ bool EquipmentItemList::CheckEquipSlot(Item* tmp, int8 slot){
 		if(tmp->slot_data[i] == slot){
 			Item* tmp_item = GetItem(tmp->slot_data[i]);
 			if(!tmp_item || tmp_item->details.item_id == 0){
+				if(slot == EQ2_SECONDARY_SLOT)
+				{
+					Item* primary = GetItem(EQ2_PRIMARY_SLOT);
+					if(primary && primary->weapon_info->wield_type == ITEM_WIELD_TYPE_TWO_HAND)
+						continue;
+				}
 				MEquipmentItems.unlock();
 				return true;
 			}
@@ -3927,6 +3947,12 @@ int8 EquipmentItemList::GetFreeSlot(Item* tmp, int8 slot_id){
 		if(slot_id == 255 || slot == slot_id){
 			Item* tmp_item = GetItem(slot);
 			if(!tmp_item || tmp_item->details.item_id == 0){
+				if(slot == EQ2_SECONDARY_SLOT)
+				{
+					Item* primary = GetItem(EQ2_PRIMARY_SLOT);
+					if(primary && primary->weapon_info->wield_type == ITEM_WIELD_TYPE_TWO_HAND)
+						continue;
+				}
 				MEquipmentItems.unlock();
 				return slot;
 			}

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

@@ -264,6 +264,7 @@ extern MasterItemList master_item_list;
 #define ITEM_TAG_MYTHICAL				12
 
 #define ITEM_BROKER_TYPE_ANY			0xFFFFFFFF
+#define ITEM_BROKER_TYPE_ANY64BIT		0xFFFFFFFFFFFFFFFF
 #define ITEM_BROKER_TYPE_ADORNMENT		134217728
 #define ITEM_BROKER_TYPE_AMMO			1024
 #define ITEM_BROKER_TYPE_ATTUNEABLE		16384
@@ -294,6 +295,10 @@ extern MasterItemList master_item_list;
 #define ITEM_BROKER_TYPE_TINKERED		268435456
 #define ITEM_BROKER_TYPE_TRADESKILL		256
 
+#define ITEM_BROKER_TYPE_2H_CRUSH		17179869184
+#define ITEM_BROKER_TYPE_2H_PIERCE		34359738368
+#define ITEM_BROKER_TYPE_2H_SLASH		8589934592
+
 #define ITEM_BROKER_SLOT_ANY			0xFFFFFFFF
 #define ITEM_BROKER_SLOT_AMMO			65536
 #define ITEM_BROKER_SLOT_CHARM			524288
@@ -988,7 +993,7 @@ public:
 	Item* GetItemByName(const char *name);
 	ItemStatsValues* CalculateItemBonuses(int32 item_id, Entity* entity = 0);
 	ItemStatsValues* CalculateItemBonuses(Item* desc, Entity* entity = 0, ItemStatsValues* values = 0);
-	vector<Item*>* GetItems(string name, int32 itype, int32 ltype, int32 btype, int64 minprice, int64 maxprice, int8 minskill, int8 maxskill, string seller, string adornment, int8 mintier, int8 maxtier, int16 minlevel, int16 maxlevel, sint8 itemclass);
+	vector<Item*>* GetItems(string name, int64 itype, int32 ltype, int32 btype, int64 minprice, int64 maxprice, int8 minskill, int8 maxskill, string seller, string adornment, int8 mintier, int8 maxtier, int16 minlevel, int16 maxlevel, sint8 itemclass);
 	vector<Item*>* GetItems(map<string, string> criteria);
 	void AddItem(Item* item);
 	bool IsBag(int32 item_id);

+ 6 - 2
EQ2/source/WorldServer/LuaInterface.cpp

@@ -535,7 +535,7 @@ bool LuaInterface::LoadRegionScript(string name) {
 	return LoadRegionScript(name.c_str());
 }
 
-std::string LuaInterface::AddSpawnPointers(LuaSpell* spell, bool first_cast, bool precast, const char* function, SpellScriptTimer* timer, bool passLuaSpell) {
+std::string LuaInterface::AddSpawnPointers(LuaSpell* spell, bool first_cast, bool precast, const char* function, SpellScriptTimer* timer, bool passLuaSpell, Spawn* altTarget) {
 	std::string functionCalled = string(""); 
 	if (function)
 	{
@@ -578,7 +578,11 @@ std::string LuaInterface::AddSpawnPointers(LuaSpell* spell, bool first_cast, boo
 	if (temp_spawn)
 		SetSpawnValue(spell->state, temp_spawn);
 	else {
-		if(spell->caster && spell->caster->GetZone() != nullptr && spell->initial_target)
+		if(altTarget)
+		{
+			SetSpawnValue(spell->state, altTarget);
+		}
+		else if(spell->caster && spell->caster->GetZone() != nullptr && spell->initial_target)
 		{
 			// easier to debug target id as ptr
 			Spawn* new_target = spell->caster->GetZone()->GetSpawnByID(spell->initial_target);

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

@@ -233,7 +233,7 @@ public:
 	void			SetConversationValue(lua_State* state, vector<ConversationOption>* conversation);
 	void			SetOptionWindowValue(lua_State* state, vector<OptionWindowOption>* optionWindow);
 
-	std::string		AddSpawnPointers(LuaSpell* spell, bool first_cast, bool precast = false, const char* function = 0, SpellScriptTimer* timer = 0, bool passLuaSpell=false);
+	std::string		AddSpawnPointers(LuaSpell* spell, bool first_cast, bool precast = false, const char* function = 0, SpellScriptTimer* timer = 0, bool passLuaSpell=false, Spawn* altTarget = 0);
 	LuaSpell*		GetCurrentSpell(lua_State* state);
 	bool			CallSpellProcess(LuaSpell* spell, int8 num_parameters, std::string functionCalled);
 	LuaSpell*		GetSpell(const char* name);

+ 31 - 8
EQ2/source/WorldServer/Player.cpp

@@ -2016,19 +2016,42 @@ void Player::SetEquippedItemAppearances(){
 	GetZone()->SendSpawnChanges(this);
 }
 
-EQ2Packet* Player::SwapEquippedItems(int8 slot1, int8 slot2, int16 version){
-	Item* item_from = equipment_list.items[slot1];
-	Item* item_to = equipment_list.items[slot2];
-	if(item_from && equipment_list.CanItemBeEquippedInSlot(item_from, slot2)){
+EQ2Packet* Player::SwapEquippedItems(int8 slot1, int8 slot2, int16 version, int16 equip_type){
+	EquipmentItemList* equipList = &equipment_list;
+	
+	// right now client seems to pass 3 for this? Not sure why when other fields has appearance equipment as type 1
+	if(equip_type == 3)
+		equipList = &appearance_equipment_list;
+	
+	Item* item_from = equipList->items[slot1];
+	Item* item_to = equipList->items[slot2];
+	if(item_from && equipList->CanItemBeEquippedInSlot(item_from, slot2)){
 		if(item_to){
-			if(!equipment_list.CanItemBeEquippedInSlot(item_to, slot1))
+			if(!equipList->CanItemBeEquippedInSlot(item_to, slot1))
 				return 0;
-			item_to->details.slot_id = slot1;
+		}
+		equipList->items[slot1] = nullptr;
+		equipList->SetItem(slot2, item_from);
+		if(item_to)
+		{
+			equipList->SetItem(slot1, item_to);
 			item_to->save_needed = true;
 		}
 		item_from->save_needed = true;
-		item_from->details.slot_id = slot2;
-		return equipment_list.serialize(version, this);
+		
+		if (GetClient())
+		{
+			//EquipmentItemList* equipList = &equipment_list;
+			
+			//if(appearance_type)
+			//	equipList = &appearance_equipment_list;
+			
+			if(item_to)
+				GetClient()->QueuePacket(item_to->serialize(version, false, this));
+			GetClient()->QueuePacket(item_from->serialize(version, false, this));
+			GetClient()->QueuePacket(item_list.serialize(this, version));
+		}
+		return equipList->serialize(version, this);
 	}
 	return 0;
 }

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

@@ -453,7 +453,7 @@ public:
 	vector<EQ2Packet*>	UnequipItem(int16 index, sint32 bag_id, int8 slot, int16 version, int8 appearance_type = 0);
 	int8 ConvertSlotToClient(int8 slot, int16 version);
 	int8 ConvertSlotFromClient(int8 slot, int16 version);
-	EQ2Packet* SwapEquippedItems(int8 slot1, int8 slot2, int16 version);
+	EQ2Packet* SwapEquippedItems(int8 slot1, int8 slot2, int16 version, int16 equiptype);
 	EQ2Packet*	RemoveInventoryItem(int8 bag_slot, int8 slot);
 	EQ2Packet*	SendInventoryUpdate(int16 version);
 	EQ2Packet*	SendBagUpdate(int32 bag_unique_id, int16 version);

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

@@ -66,6 +66,10 @@
 #define SKILL_ID_DESTROYING 3429135390
 #define SKILL_ID_MAGIC_AFFINITY 2072844078
 
+#define SKILL_ID_GREATSWORD 2292577688 // aka 2h slashing
+#define SKILL_ID_GREATSPEAR 2380184628 // aka 2h piercing
+#define SKILL_ID_STAFF		3180399725 // aka 2h crushing
+
 /* Each SkillBonus is comprised of multiple possible skill bonus values.  This is so one spell can modify
    more than one skill */
 struct SkillBonusValue {

+ 83 - 36
EQ2/source/WorldServer/SpellProcess.cpp

@@ -447,44 +447,69 @@ bool SpellProcess::DeleteCasterSpell(LuaSpell* spell, string reason, bool removi
 	return ret;
 }
 
-bool SpellProcess::ProcessSpell(LuaSpell* spell, bool first_cast, const char* function, SpellScriptTimer* timer) {
+bool SpellProcess::ProcessSpell(LuaSpell* spell, bool first_cast, const char* function, SpellScriptTimer* timer, bool all_targets) {
 	bool ret = false;
 	if(!spell->state)
 	{
 		LogWrite(SPELL__ERROR, 0, "Spell", "Error: State is NULL!  SpellProcess::ProcessSpell for Spell '%s'", (spell->spell != nullptr) ? spell->spell->GetName() : "Unknown");
 	}
 	else if(lua_interface && !spell->interrupted){
-		std::string functionCall = lua_interface->AddSpawnPointers(spell, first_cast, false, function, timer);
-		vector<LUAData*>* data = spell->spell->GetLUAData();
-		for(int32 i=0;i<data->size();i++){
-			switch(data->at(i)->type){
-				case 0:{
-					lua_interface->SetSInt32Value(spell->state, data->at(i)->int_value);
-					break;
-				}
-				case 1:{
-					lua_interface->SetFloatValue(spell->state, data->at(i)->float_value);
-					break;
-				}
-				case 2:{
-					lua_interface->SetBooleanValue(spell->state, data->at(i)->bool_value);
-					break;
-				}
-				case 3:{
-					lua_interface->SetStringValue(spell->state, data->at(i)->string_value.c_str());
-					break;
-				}
-				default:{
-					LogWrite(SPELL__ERROR, 0, "Spell", "Error: Unknown LUA Type '%i' in SpellProcess::ProcessSpell for Spell '%s'", (int)data->at(i)->type, spell->spell->GetName());
-					return false;
+		if(all_targets)
+		{
+			for(int t=0;t<spell->targets.size();t++)
+			{
+				Spawn* altSpawn = spell->caster->GetZone()->GetSpawnByID(spell->targets[t]);
+				if(altSpawn)
+				{
+					std::string functionCall = ApplyLuaFunction(spell, first_cast, function, timer, altSpawn);
+					if(functionCall.length() < 1)
+						ret = false;
+					else
+						ret = lua_interface->CallSpellProcess(spell, 2 + spell->spell->GetLUAData()->size(), functionCall);
 				}
 			}
+			return true;
 		}
-		ret = lua_interface->CallSpellProcess(spell, 2 + data->size(), functionCall);
+		std::string functionCall = ApplyLuaFunction(spell, first_cast, function, timer);
+		if(functionCall.length() < 1)
+			ret = false;
+		else
+			ret = lua_interface->CallSpellProcess(spell, 2 + spell->spell->GetLUAData()->size(), functionCall);
 	}
 	return ret;
 }
 
+std::string SpellProcess::ApplyLuaFunction(LuaSpell* spell, bool first_cast, const char* function, SpellScriptTimer* timer, Spawn* altTarget)
+{
+	std::string functionCall = lua_interface->AddSpawnPointers(spell, first_cast, false, function, timer, false, altTarget);
+	vector<LUAData*>* data = spell->spell->GetLUAData();
+	for(int32 i=0;i<data->size();i++){
+		switch(data->at(i)->type){
+			case 0:{
+				lua_interface->SetSInt32Value(spell->state, data->at(i)->int_value);
+				break;
+			}
+			case 1:{
+				lua_interface->SetFloatValue(spell->state, data->at(i)->float_value);
+				break;
+			}
+			case 2:{
+				lua_interface->SetBooleanValue(spell->state, data->at(i)->bool_value);
+				break;
+			}
+			case 3:{
+				lua_interface->SetStringValue(spell->state, data->at(i)->string_value.c_str());
+				break;
+			}
+				default:{
+				LogWrite(SPELL__ERROR, 0, "Spell", "Error: Unknown LUA Type '%i' in SpellProcess::ProcessSpell for Spell '%s'", (int)data->at(i)->type, spell->spell->GetName());
+				return string("");
+			}
+		}
+	}
+	return functionCall;
+}
+
 bool SpellProcess::CastPassives(Spell* spell, Entity* caster, bool remove) {
 	CastInstant(spell, caster, caster, remove, true);
 	return true;
@@ -1050,14 +1075,12 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 					else if(lua_spell->spell->GetSpellData()->spell_type == SPELL_TYPE_DEBUFF)
 					{
 						SpellCannotStack(zone, client, lua_spell->caster, lua_spell, conflictSpell);
-						DeleteSpell(lua_spell);
 						return;
 					}
 				}
 				else
 				{
 					SpellCannotStack(zone, client, lua_spell->caster, lua_spell, conflictSpell);
-					DeleteSpell(lua_spell);
 					return;
 				}	
 			}
@@ -1102,7 +1125,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 			return;
 		}
 
-		if (target_type != SPELL_TARGET_SELF && target_type != SPELL_TARGET_GROUP_AE && target_type != SPELL_TARGET_NONE && spell->GetSpellData()->max_aoe_targets == 0)
+		if (target_type != SPELL_TARGET_SELF && target_type != SPELL_TARGET_GROUP_AE && target_type != SPELL_TARGET_ALLGROUPTARGETS && target_type != SPELL_TARGET_NONE && spell->GetSpellData()->max_aoe_targets == 0)
 		{
 			LogWrite(SPELL__DEBUG, 1, "Spell", "%s: Not Self, Not Group AE, Not None, Max Targets = 0", spell->GetName());
 
@@ -1562,8 +1585,10 @@ bool SpellProcess::CastProcessedSpell(LuaSpell* spell, bool passive, bool in_her
 			}
 		}
 	}*/
+	
+	bool allTargets = (spell->spell->GetSpellData()->target_type == SPELL_TARGET_ALLGROUPTARGETS);
 	if (!processedSpell)
-		processedSpell = ProcessSpell(spell);
+		processedSpell = ProcessSpell(spell, true, 0, 0, allTargets);
 
 	// Quick hack to prevent a crash on spells that zones the caster (Gate)
 	if (!spell->caster)
@@ -2000,26 +2025,48 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell)
 				}
 			}
 		}
-		else if (target_type == SPELL_TARGET_GROUP_AE || target_type == SPELL_TARGET_RAID_AE) {
+		else if (target_type == SPELL_TARGET_GROUP_AE || target_type == SPELL_TARGET_ALLGROUPTARGETS || target_type == SPELL_TARGET_RAID_AE) {
 
 			// player handling
 			if (target)
 			{
-				if (data->icon_backdrop == 316) // using TARGET backdrop icon
+				if (data->icon_backdrop == 316 || data->icon_backdrop == 312) // using TARGET backdrop icon
 				{
 					// PLAYER LOGIC:
-					if ((target->IsPlayer() && luaspell->caster->IsPlayer() && target != luaspell->caster && ((Player*)target)->GetGroupMemberInfo() != NULL && ((Player*)luaspell->caster)->GetGroupMemberInfo() != NULL
+					if ((data->friendly_spell && (target->IsPlayer() && luaspell->caster->IsPlayer() && target != luaspell->caster && ((Player*)target)->GetGroupMemberInfo() != NULL && ((Player*)luaspell->caster)->GetGroupMemberInfo() != NULL
 						&& ((Player*)target)->GetGroupMemberInfo()->group_id == ((Player*)luaspell->caster)->GetGroupMemberInfo()->group_id))
+						|| (!data->friendly_spell && (target->IsPlayer() && luaspell->caster->IsPlayer() && target != luaspell->caster && ((Player*)target)->GetGroupMemberInfo() != NULL)))
 					{
 						GetPlayerGroupTargets((Player*)target, caster, luaspell, true, false);
-					}//TODO: NEED RAID SUPPORT
+					}
+					//TODO: NEED RAID SUPPORT
 
 					// NPC LOGIC:
-					else if (target->group_id > 0 && target->group_id == luaspell->caster->group_id)
+					else if (target->IsNPC())
 					{
+						// Check to see if the npc is a spawn group by getting the group and checikng if valid
+						vector<Spawn*>* group = ((NPC*)target)->GetSpawnGroup();
+						if (group) 
+						{
+							vector<Spawn*>::iterator itr;
+
+							// iterate through spawn group members
+							for (itr = group->begin(); itr != group->end(); itr++) 
+							{
+								Spawn* group_member = *itr;
 
+								// if NPC group member is (still) an NPC (wtf?) and is alive, send the NPC group member back as a successful target of non-friendly spell group_member->Alive()
+								if (group_member->GetZone() == caster->GetZone() && 
+								group_member->IsNPC() && group_member->Alive() && !((Entity*)group_member)->IsAOEImmune() && (!((Entity*)group_member)->IsMezzed() || group_member == target))
+									luaspell->targets.push_back(group_member->GetID());
+
+								// note: this should generate some hate towards the caster
+							}
+						} // end is spawngroup
+						else
+							luaspell->targets.push_back(target->GetID()); // return single target NPC for non-friendly spell
 					}
-					else
+					else if(data->friendly_spell)
 					{
 						// add self
 						target = NULL;
@@ -2060,7 +2107,7 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell)
 
 		luaspell->MSpellTargets.writelock(__FUNCTION__, __LINE__);
 		// Group AE type                            NOTE: Add support for RAID AE to affect raid members once raids have been completed
-		if (target_type == SPELL_TARGET_GROUP_AE || target_type == SPELL_TARGET_RAID_AE) 
+		if (target_type == SPELL_TARGET_GROUP_AE || target_type == SPELL_TARGET_ALLGROUPTARGETS || target_type == SPELL_TARGET_RAID_AE) 
 		{
 			if (data->icon_backdrop == 316) // single target in a group/raid
 			{

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

@@ -385,7 +385,8 @@ public:
 
 	void SpellCannotStack(ZoneServer* zone, Client* client, Entity* caster, LuaSpell* lua_spell, LuaSpell* conflictSpell);
 
-	bool ProcessSpell(LuaSpell* spell, bool first_cast = true, const char* function = 0, SpellScriptTimer* timer = 0);
+	bool ProcessSpell(LuaSpell* spell, bool first_cast = true, const char* function = 0, SpellScriptTimer* timer = 0, bool all_targets = false);
+	std::string ApplyLuaFunction(LuaSpell* spell, bool first_cast, const char* function, SpellScriptTimer* timer, Spawn* altTarget = 0);
 
 	void AddActiveSpell(LuaSpell* spell);
 	static void AddSelfAndPet(LuaSpell* spell, Spawn* caster, bool onlyPet=false);

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

@@ -40,6 +40,7 @@
 #define SPELL_TARGET_NONE			7
 #define SPELL_TARGET_RAID_AE		8
 #define SPELL_TARGET_OTHER_GROUP_AE	9
+#define SPELL_TARGET_ALLGROUPTARGETS 10 // use this with cast/tick only containing DIRECT target/spawn.
 
 
 #define SPELL_BOOK_TYPE_SPELL		0

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

@@ -7405,7 +7405,7 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int
 		if(custom_spell)
 		{
 			if((lua_spell = lua_interface->GetSpell(lua_file.c_str())) == nullptr)
-			{		
+			{
 				LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: GetSpell(%u, %u, '%s'), custom lua script not loaded, when attempting to load.", spell_id, tier, lua_file.c_str());
 				lua_interface->LoadLuaSpell(lua_file);
 			}
@@ -7458,6 +7458,9 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int
 		else if(db_spell_type == DB_TYPE_MAINTAINEDEFFECTS)
 		{
 			safe_delete(lua_spell);
+			if(!target_char_id)
+				continue;
+			
 			lua_spell = lua_interface->GetSpell(spell->GetSpellData()->lua_script.c_str());
 			if(lua_spell)
 				lua_spell->spell = spell;

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

@@ -3241,7 +3241,7 @@ void ClientList::RemoveConnection(EQStream* eqs) {
 		for (client_iter = client_list.begin(); client_iter != client_list.end(); client_iter++) {
 			client = *client_iter;
 			if (client->getConnection() == eqs)
-				client->Disconnect(false);
+				client->Disconnect(true);
 		}
 		MClients.releasereadlock(__FUNCTION__, __LINE__);
 	}

+ 15 - 0
EQ2/source/common/MiscFunctions.cpp

@@ -568,6 +568,21 @@ int32 ParseIntValue(string input){
 	return ret;
 }
 
+int64 ParseLongLongValue(string input){
+	int64 ret = 0xFFFFFFFFFFFFFFFF;
+	try{
+		if(input.length() > 0){
+#ifdef WIN32
+			ret = _strtoui64(input.c_str(), NULL, 10);
+#else
+			ret = strtoull(input.c_str(), 0, 10);
+#endif
+		}
+	}
+	catch(...){}
+	return ret;
+}
+
 map<string, string> TranslateBrokerRequest(string request){
 	map<string, string> ret;
 	string key;

+ 1 - 0
EQ2/source/common/MiscFunctions.h

@@ -67,6 +67,7 @@ void	Decode(uchar* dst, uchar* src, int16 len);
 string	ToUpper(string input);
 string	ToLower(string input);
 int32 ParseIntValue(string input);
+int64 ParseLongLongValue(string input);
 map<string, string> TranslateBrokerRequest(string request);
 void	MovementDecode(uchar* dst, uchar* newval, uchar* orig, int16 len);
 vector<string>* SplitString(string str, char delim);