Browse Source

Work on Issue #418 for item flags (update #2)
Item Flags Implemented/Updated:
- No Repair implemented
- Evil Only, Good Only implemented
- No Value updated/implemented
- Fixed house items being removed unless properly picked up by player
- Broken items can no longer be used
- Cannot swap equipped items in combat
- Additional indestructible checks
Group member checks to avoid unexpected crashes

Emagi 1 year ago
parent
commit
c94a043cdc

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

@@ -51,7 +51,7 @@ void Commands::Command_Bot(Client* client, Seperator* sep) {
 //devn00b compile says this is no good, commenting out for now.
 //if(!member) 
 //  continue;
-									if ((*itr)->member->IsBot() && ((Bot*)(*itr)->member)->GetOwner() == client->GetPlayer()) {
+									if ((*itr)->member && (*itr)->member->IsBot() && ((Bot*)(*itr)->member)->GetOwner() == client->GetPlayer()) {
                                       
 										((Bot*)(*itr)->member)->SetCombatTarget(target->GetID());
 									}

+ 52 - 27
EQ2/source/WorldServer/Commands/Commands.cpp

@@ -2143,8 +2143,20 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 			if (sep && sep->arg[0] && sep->IsNumber(0)){
 				int32 slot_id = atoul(sep->arg[0]);
 				Item* item = player->GetEquipmentList()->GetItem(slot_id);
-				if (item && item->generic_info.usable && item->GetItemScript())
-					lua_interface->RunItemScript(item->GetItemScript(), "used", item, player);
+				if (item && item->generic_info.usable && item->GetItemScript()) {
+					if(!item->CheckFlag2(INDESTRUCTABLE) && item->generic_info.condition == 0) {
+						client->SimpleMessage(CHANNEL_COLOR_RED, "This item is broken and must be repaired at a mender before it can be used");
+					}
+					else if (item->CheckFlag(EVIL_ONLY) && client->GetPlayer()->GetAlignment() != ALIGNMENT_EVIL) {
+							client->Message(0, "%s requires an evil race.", item->name.c_str());
+					}
+					else if (item->CheckFlag(GOOD_ONLY) && client->GetPlayer()->GetAlignment() != ALIGNMENT_GOOD) {
+							client->Message(0, "%s requires a good race.", item->name.c_str());
+					}
+					else {
+						lua_interface->RunItemScript(item->GetItemScript(), "used", item, player);
+					}
+				}
 			}
 			break;
 		}
@@ -2153,7 +2165,16 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 				int32 item_index = atoul(sep->arg[0]);
 				Item* item = player->item_list.GetItemFromIndex(item_index);
 				if (item && item->GetItemScript()) {
-					if (item->generic_info.max_charges == 0 || item->generic_info.max_charges == 0xFFFF)
+					if(!item->CheckFlag2(INDESTRUCTABLE) && item->generic_info.condition == 0) {
+						client->SimpleMessage(CHANNEL_COLOR_RED, "This item is broken and must be repaired at a mender before it can be used");
+					}
+					else if (item->CheckFlag(EVIL_ONLY) && client->GetPlayer()->GetAlignment() != ALIGNMENT_EVIL) {
+							client->Message(0, "%s requires an evil race.", item->name.c_str());
+					}
+					else if (item->CheckFlag(GOOD_ONLY) && client->GetPlayer()->GetAlignment() != ALIGNMENT_GOOD) {
+							client->Message(0, "%s requires a good race.", item->name.c_str());
+					}
+					else if (item->generic_info.max_charges == 0 || item->generic_info.max_charges == 0xFFFF)
 						lua_interface->RunItemScript(item->GetItemScript(), "used", item, player);
 					else {
 						if (item->details.count > 0) {
@@ -3678,19 +3699,19 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 			if (!spawn || !client->HasOwnerOrEditAccess() || !spawn->GetPickupItemID())
 				break;
 
-			client->AddItem(spawn->GetPickupItemID(), 1);
+			if(client->AddItem(spawn->GetPickupItemID(), 1)) {
+				Query query;
+				query.RunQuery2(Q_INSERT, "delete from spawn_instance_data where spawn_id = %u and spawn_location_id = %u and pickup_item_id = %u", spawn->GetDatabaseID(), spawn->GetSpawnLocationID(), spawn->GetPickupItemID());
 
-			Query query;
-			query.RunQuery2(Q_INSERT, "delete from spawn_instance_data where spawn_id = %u and spawn_location_id = %u and pickup_item_id = %u", spawn->GetDatabaseID(), spawn->GetSpawnLocationID(), spawn->GetPickupItemID());
+				if (database.RemoveSpawnFromSpawnLocation(spawn)) {
+					client->GetCurrentZone()->RemoveSpawn(spawn, true, true, true, true, true);
+				}
 
-			if (database.RemoveSpawnFromSpawnLocation(spawn)) {
-				client->GetCurrentZone()->RemoveSpawn(spawn, true, true, true, true, true);
+				// we had a UI Window displayed, update the house items
+				if ( id > 0 )
+					client->GetCurrentZone()->SendHouseItems(client);
 			}
 
-			// we had a UI Window displayed, update the house items
-			if ( id > 0 )
-				client->GetCurrentZone()->SendHouseItems(client);
-
 			break;
 		}
 		case COMMAND_HOUSE_DEPOSIT:
@@ -5992,7 +6013,7 @@ void Commands::Command_Follow(Client* client, Seperator* sep)
 			// Loop through the group members
 			for (itr = members->begin(); itr != members->end(); itr++) {
 				// If a group member matches a target
-				if ((*itr)->member == client->GetPlayer()->GetTarget()) {
+				if ((*itr)->member && (*itr)->member == client->GetPlayer()->GetTarget()) {
 					// toggle the flag and break the loop
 					targetInGroup = true;
 					break;
@@ -6826,21 +6847,25 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma
 		}
 		else if(sep->arg[2][0] && strncasecmp("swap_equip", sep->arg[0], 10) == 0 && sep->IsNumber(1) && sep->IsNumber(2))
 		{
-			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]);
-			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(client->GetPlayer()->EngagedInCombat()) {
+				client->SimpleMessage(CHANNEL_COLOR_RED, "You may not swap items while in combat.");
+			}
+			else {
+				int16 index1 = atoi(sep->arg[1]);
+				int16 index2 = atoi(sep->arg[2]);
+				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);
-			else
-			{
-				client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Unable to swap items");
-				return;
+				if(outapp)
+					client->QueuePacket(outapp);
+				else
+				{
+					client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Unable to swap items");
+					return;
+				}
 			}
 		}
 		else if (sep->arg[2][0] && strncasecmp("pop", sep->arg[0], 3) == 0 && sep->IsNumber(1) && sep->IsNumber(2)) 

+ 5 - 0
EQ2/source/WorldServer/Entity.h

@@ -89,6 +89,11 @@ struct DetrimentalEffects {
 	float   total_time;
 };
 
+enum RACE_ALIGNMENT {
+	ALIGNMENT_EVIL=0,
+	ALIGNMENT_GOOD=1
+	// neutral?
+};
 struct InfoStruct{
 	InfoStruct()
 	{

+ 1 - 1
EQ2/source/WorldServer/Guilds/GuildDB.cpp

@@ -285,7 +285,7 @@ void WorldDatabase::SaveGuildMembers(Guild* guild) {
 		gm = itr->second;
 		LogWrite(GUILD__DEBUG, 5, "Guilds", "Saving Guild Member '%s' (%u) data...", gm->name, gm->character_id);
 		query.RunQuery2(Q_INSERT, "INSERT INTO `guild_members` (`guild_id`, `char_id`, `recruiter_id`, `guild_status`, `points`, `rank_id`, `member_flags`, `join_date`, `note`, `officer_note`, `recruiting_message`, `recruiter_picture_data`) VALUES (%u, %u, %u, %u, %f, %u, %u, %u, '%s', '%s', '%s', NULL) ON DUPLICATE KEY UPDATE `guild_id`=%u, `recruiter_id`=%u, `guild_status`=%u, `points`=%f, `rank_id`=%u, `member_flags`=%u, `join_date`=%u, `note`='%s', `officer_note`='%s', `recruiting_message`='%s', `recruiter_picture_data`=NULL", guild->GetID(), gm->character_id, gm->recruiter_id, gm->guild_status, gm->points, gm->rank, gm->member_flags, gm->join_date, getSafeEscapeString(gm->note.c_str()).c_str(), getSafeEscapeString(gm->officer_note.c_str()).c_str(), getSafeEscapeString(gm->recruiter_description.c_str()).c_str(), guild->GetID(), gm->recruiter_id, gm->guild_status, gm->points, gm->rank, gm->member_flags, gm->join_date, getSafeEscapeString(gm->note.c_str()).c_str(), getSafeEscapeString(gm->officer_note.c_str()).c_str(), getSafeEscapeString(gm->recruiter_description.c_str()).c_str());
-		if (gm->recruiter_picture_data_size > 0 && gm->recruiter_picture_data) {
+		if (gm && gm->recruiter_picture_data_size > 0 && gm->recruiter_picture_data) {
 			stringstream ss_hex;
 			stringstream ss_query;
 			ss_hex.flags(ios::hex);

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

@@ -1233,7 +1233,7 @@ bool Item::IsShield(){
 
 bool Item::IsAdornment(){
 	LogWrite(MISC__TODO, 1, "TODO", "Item Adornments\n\t(%s, function: %s, line #: %i)", __FILE__, __FUNCTION__, __LINE__);
-	return generic_info.item_type == ITEM_TYPE_ADORNMENT;
+	return generic_info.item_type == ITEM_TYPE_ADORNMENT && !CheckFlag2(ORNATE);
 }
 
 bool Item::IsAmmo(){

+ 7 - 1
EQ2/source/WorldServer/Player.cpp

@@ -2077,7 +2077,13 @@ bool Player::CanEquipItem(Item* item) {
 	if (item) {
 		Client* client = GetZone()->GetClientBySpawn(this);
 		if (client) {
-			if (item->IsArmor() || item->IsWeapon() || item->IsFood() || item->IsRanged() || item->IsShield() || item->IsBauble() || item->IsAmmo() || item->IsThrown()) {
+			if (item->CheckFlag(EVIL_ONLY) && GetAlignment() != ALIGNMENT_EVIL) {
+					client->Message(0, "%s requires an evil race.", item->name.c_str());
+			}
+			else if (item->CheckFlag(GOOD_ONLY) && GetAlignment() != ALIGNMENT_GOOD) {
+					client->Message(0, "%s requires a good race.", item->name.c_str());
+			}
+			else if (item->IsArmor() || item->IsWeapon() || item->IsFood() || item->IsRanged() || item->IsShield() || item->IsBauble() || item->IsAmmo() || item->IsThrown()) {
 				if ((item->generic_info.skill_req1 == 0 || item->generic_info.skill_req1 == 0xFFFFFFFF || skill_list.HasSkill(item->generic_info.skill_req1)) && (item->generic_info.skill_req2 == 0 || item->generic_info.skill_req2 == 0xFFFFFFFF || skill_list.HasSkill(item->generic_info.skill_req2))) {
 					int16 override_level = item->GetOverrideLevel(GetAdventureClass(), GetTradeskillClass());
 					if (override_level > 0 && override_level <= GetLevel())

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

@@ -2358,7 +2358,7 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell)
 								Entity* group_member = (*itr)->member;
 
 								// if the group member is in the same zone as caster, and group member is alive, and group member is within distance
-								if (group_member->GetZone() == caster->GetZone() && group_member->Alive() && caster->GetDistance(group_member) <= data->range
+								if (group_member && group_member->GetZone() == caster->GetZone() && group_member->Alive() && caster->GetDistance(group_member) <= data->range
 									&& (group_member == target || !group_member->IsAOEImmune()))
 									luaspell->targets.push_back(group_member->GetID()); // add as target
 							}
@@ -2406,7 +2406,7 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell)
 							for (itr = members->begin(); itr != members->end(); itr++) {
 								group_member = (*itr)->member;
 								//Check if group member is in the same zone in range of the spell and dead
-								if (group_member->GetZone() == target->GetZone() && !group_member->Alive() && target->GetDistance(group_member) <= data->radius) {
+								if (group_member && group_member->GetZone() == target->GetZone() && !group_member->Alive() && target->GetDistance(group_member) <= data->radius) {
 									luaspell->targets.push_back(group_member->GetID());
 								}
 							}

+ 30 - 19
EQ2/source/WorldServer/client.cpp

@@ -6736,6 +6736,11 @@ void Client::SellItem(int32 item_id, int16 quantity, int32 unique_id) {
 				SimpleMessage(CHANNEL_COLOR_RED, "You cannot sell the item in use.");
 				return;
 			}
+			else if(item->CheckFlag(NO_VALUE))
+			{
+				SimpleMessage(CHANNEL_COLOR_RED, "This item has no value.");
+				return;
+			}
 			int32 sell_price = (int32)(master_item->sell_price * multiplier);
 			if (sell_price > item->sell_price)
 				sell_price = item->sell_price;
@@ -7074,25 +7079,31 @@ void Client::RepairItem(int32 item_id) {
 		if (!item)
 			item = player->GetEquipmentList()->GetItemFromItemID(item_id);
 		if (item) {
-			int32 repair_cost = item->CalculateRepairCost();
-			if (player->RemoveCoins((int32)repair_cost)) {
-				item->generic_info.condition = 100;
-				item->save_needed = true;
-				QueuePacket(player->GetEquipmentList()->serialize(GetVersion(), player));
-				QueuePacket(player->SendInventoryUpdate(GetVersion()));
-				QueuePacket(item->serialize(version, false, player));
-				Message(CHANNEL_MERCHANT, "You give %s %s to repair your %s.", spawn->GetName(), GetCoinMessage(repair_cost).c_str(), item->CreateItemLink(GetVersion()).c_str());
-				PlaySound("coin_cha_ching");
-				if (spawn->GetMerchantType() & MERCHANT_TYPE_REPAIR)
-					SendRepairList();
+			if(item->CheckFlag(NO_REPAIR)) {
+				Message(CHANNEL_MERCHANT, "The mender was unable to repair your items.");
+				PlaySound("buy_failed");
 			}
 			else {
-				string popup_text = "You do not have enough coin to repair ";
-				string popup_item = item->CreateItemLink(GetVersion()).c_str();
-				popup_text.append(popup_item);
-				SendPopupMessage(10, popup_text.c_str(), "", 3, 0xFF, 0xFF, 0xFF);
-				Message(CHANNEL_MERCHANT, "You do not have enough coin to repair %s.", item->CreateItemLink(GetVersion()).c_str());
-				PlaySound("buy_failed");
+				int32 repair_cost = item->CalculateRepairCost();
+				if (player->RemoveCoins((int32)repair_cost)) {
+					item->generic_info.condition = 100;
+					item->save_needed = true;
+					QueuePacket(player->GetEquipmentList()->serialize(GetVersion(), player));
+					QueuePacket(player->SendInventoryUpdate(GetVersion()));
+					QueuePacket(item->serialize(version, false, player));
+					Message(CHANNEL_MERCHANT, "You give %s %s to repair your %s.", spawn->GetName(), GetCoinMessage(repair_cost).c_str(), item->CreateItemLink(GetVersion()).c_str());
+					PlaySound("coin_cha_ching");
+					if (spawn->GetMerchantType() & MERCHANT_TYPE_REPAIR)
+						SendRepairList();
+				}
+				else {
+					string popup_text = "You do not have enough coin to repair ";
+					string popup_item = item->CreateItemLink(GetVersion()).c_str();
+					popup_text.append(popup_item);
+					SendPopupMessage(10, popup_text.c_str(), "", 3, 0xFF, 0xFF, 0xFF);
+					Message(CHANNEL_MERCHANT, "You do not have enough coin to repair %s.", item->CreateItemLink(GetVersion()).c_str());
+					PlaySound("buy_failed");
+				}
 			}
 		}
 	}
@@ -7847,7 +7858,7 @@ vector<Item*>* Client::GetRepairableItems() {
 	if (equipped_items && equipped_items->size() > 0) {
 		for (int32 i = 0; i < equipped_items->size(); i++) {
 			Item* item = equipped_items->at(i);
-			if (item && item->generic_info.condition < 100)
+			if (item && !item->CheckFlag(NO_REPAIR) && item->generic_info.condition < 100)
 				repairable_items->push_back(item);
 		}
 	}
@@ -7855,7 +7866,7 @@ vector<Item*>* Client::GetRepairableItems() {
 		map<int32, Item*>::iterator itr;
 		for (itr = items->begin(); itr != items->end(); itr++) {
 			Item* item = itr->second;
-			if (item && item->generic_info.condition < 100)
+			if (item && !item->CheckFlag(NO_REPAIR) && item->generic_info.condition < 100)
 				repairable_items->push_back(item);
 		}
 	}

+ 12 - 10
EQ2/source/WorldServer/zoneserver.cpp

@@ -4315,15 +4315,17 @@ void ZoneServer::SendCalculatedXP(Player* player, Spawn* victim){
 					GroupMemberInfo* gmi = *itr;
 					if (gmi->client) {
 						Player* group_member = gmi->client->GetPlayer();
-						float xp = group_member->CalculateXP(victim) / members->size();
-						if (xp > 0) {
-							int16 level = group_member->GetLevel();
-							if (group_member->AddXP((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());
-								group_member->SetCharSheetChanged(true);
+						if(group_member) {
+							float xp = group_member->CalculateXP(victim) / members->size();
+							if (xp > 0) {
+								int16 level = group_member->GetLevel();
+								if (group_member->AddXP((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());
+									group_member->SetCharSheetChanged(true);
+								}
 							}
 						}
 					}
@@ -6433,7 +6435,7 @@ void ZoneServer::SendEpicMobDeathToGuild(Player* killer, Spawn* victim) {
 					GroupMemberInfo* gmi = *itr;
 					if (gmi->client) {
 						Player* group_member = gmi->client->GetPlayer();
-						if (group_member->GetGuild()) {
+						if (group_member && group_member->GetGuild()) {
 							Guild* guild = group_member->GetGuild();
 							string message = Guild::GetEpicMobDeathMessage(group_member->GetName(), victim->GetName());
 							guild->AddNewGuildEvent(GUILD_EVENT_KILLS_EPIC_MONSTER, message.c_str(), Timer::GetUnixTimeStamp());