Browse Source

Fix #304 - quest sharing and delete restrictions, check DB sql for required updates

Emagi 1 year ago
parent
commit
3ee4e5e105

+ 3 - 0
DB/updates/sharequest_feb_16_2023.sql

@@ -0,0 +1,3 @@
+update commands set handler=533 where command='share_quest';
+alter table quests add column shareable_flag int(10) unsigned not null default 0;
+alter table quests add column deleteable tinyint(3) unsigned not null default 1;

+ 40 - 5
EQ2/source/WorldServer/Commands/Commands.cpp

@@ -3127,11 +3127,12 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 			if(quest_id > 0){
 				if(lua_interface && client->GetPlayer()->player_quests.count(quest_id) > 0) {
 					Quest* quest = client->GetPlayer()->player_quests[quest_id];
-					if (quest)
+					if (quest && quest->CanDeleteQuest()) {
 						lua_interface->CallQuestFunction(quest, "Deleted", client->GetPlayer());
+						client->RemovePlayerQuest(quest_id);
+						client->GetCurrentZone()->SendQuestUpdates(client);
+					}
 				}
-				client->RemovePlayerQuest(quest_id);
-				client->GetCurrentZone()->SendQuestUpdates(client);
 			}
 			break;
 								  }
@@ -4752,7 +4753,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 				client->Message(CHANNEL_COLOR_YELLOW, "Faction ID: %u, Merchant ID: %u, Transporter ID: %u", spawn->GetFactionID(), spawn->GetMerchantID(), spawn->GetTransporterID());
 				client->Message(CHANNEL_COLOR_YELLOW, "Grid ID: %u", spawn->GetLocation());
 				client->Message(CHANNEL_COLOR_YELLOW, "Race: %i, Class: %i, Gender: %i", spawn->GetRace(), spawn->GetAdventureClass(), spawn->GetGender());
-				client->Message(CHANNEL_COLOR_YELLOW, "Level: %i, HP: %u, Power: %u", spawn->GetLevel(), spawn->GetHP(), spawn->GetPower());
+				client->Message(CHANNEL_COLOR_YELLOW, "Level: %i, HP: %u / %u, Power: %u", spawn->GetLevel(), spawn->GetHP(), spawn->GetTotalHP(), spawn->GetPower());
 				client->Message(CHANNEL_COLOR_YELLOW, "Respawn Time: %u (sec), X: %f, Y: %f, Z: %f Heading: %f", spawn->GetRespawnTime(), spawn->GetX(), spawn->GetY(), spawn->GetZ(), spawn->GetHeading());
 				client->Message(CHANNEL_COLOR_YELLOW, "Collision Radius: %i, Size: %i, Difficulty: %i, Heroic: %i", spawn->GetCollisionRadius(), spawn->GetSize(), spawn->GetEncounterLevel(), spawn->GetHeroic());
 				client->Message(CHANNEL_COLOR_YELLOW, "Targetable: %i, Show Name: %i, Attackable: %i, Show Level: %i", spawn->GetTargetable(), spawn->GetShowName(), spawn->GetAttackable(), spawn->GetShowLevel());
@@ -4790,7 +4791,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 				details += "Class:		" + to_string(spawn->GetAdventureClass()) + "\n";
 				details += "Gender:		" + to_string(spawn->GetGender()) + "\n";
 				details += "Level:		" + to_string(spawn->GetLevel()) + "\n";
-				details += "HP:		" + to_string(spawn->GetHP()) + "\n";
+				details += "HP:		" + to_string(spawn->GetHP()) + " / " + to_string(spawn->GetTotalHP()) + "\n";
 				details += "Power:		" + to_string(spawn->GetPower()) + "\n";
 				details += "Difficulty:		" + to_string(spawn->GetEncounterLevel()) + "\n";
 				details += "Heroic:		" + to_string(spawn->GetHeroic()) + "\n";
@@ -5712,6 +5713,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 		}
 		case COMMAND_CANCEL_EFFECT: { Command_CancelEffect(client, sep); break; }
 		case COMMAND_CUREPLAYER: { Command_CurePlayer(client, sep); break; }
+		case COMMAND_SHARE_QUEST: { Command_ShareQuest(client, sep); break; }
 		default: 
 		{
 			LogWrite(COMMAND__WARNING, 0, "Command", "Unhandled command: %s", command->command.data.c_str());
@@ -11934,3 +11936,36 @@ void Commands::Command_CurePlayer(Client* client, Seperator* sep)
 		}
 	}
 }
+
+
+/* 
+	Function: Command_ShareQuest()
+	Purpose	: Share quest with the group
+	Example	: /share_quest [quest_id]
+*/ 
+void Commands::Command_ShareQuest(Client* client, Seperator* sep)
+{
+	Entity* target = nullptr;
+	bool use_spells = true;
+	bool use_potions = true;
+	if (sep && sep->arg[0] && sep->IsNumber(0)) {
+		int32 quest_id = atoul(sep->arg[0]);
+		
+		Quest* playerQuest = client->GetPlayer()->GetQuest(quest_id);
+		if(playerQuest) {
+		Quest* quest = master_quest_list.GetQuest(playerQuest->GetQuestID(), false);
+			if(quest) {
+				GroupMemberInfo* gmi = client->GetPlayer()->GetGroupMemberInfo();
+				if (gmi && gmi->group_id) {
+					PlayerGroup* group = world.GetGroupManager()->GetGroup(gmi->group_id);
+					if (group) {
+						group->ShareQuestWithGroup(client, quest);
+					}
+				}
+			}
+		}
+		else {
+			client->SimpleMessage(CHANNEL_COLOR_RED, "Cannot find quest.");
+		}
+	}
+}

+ 2 - 0
EQ2/source/WorldServer/Commands/Commands.h

@@ -447,6 +447,7 @@ public:
 	
 	void Command_CancelEffect(Client* client, Seperator* sep);
 	void Command_CurePlayer(Client* client, Seperator* sep);
+	void Command_ShareQuest(Client* client, Seperator* sep);
 
 	// AA Commands
 	void Get_AA_Xml(Client* client, Seperator* sep);
@@ -933,6 +934,7 @@ private:
 #define COMMAND_CUREPLAYER			531
 
 #define COMMAND_RELOAD_VOICEOVERS		532
+#define COMMAND_SHARE_QUEST				533
 
 
 #define GET_AA_XML						750

+ 9 - 3
EQ2/source/WorldServer/Player.cpp

@@ -4347,7 +4347,7 @@ PacketStruct* Player::GetQuestJournalPacket(bool all_quests, int16 version, int3
 				packet->setArrayDataByName("name", quest->GetName(), i);
 				packet->setArrayDataByName("quest_type", quest->GetType(), i);
 				packet->setArrayDataByName("quest_zone", quest->GetZone(), i);
-				int8 display_status = QUEST_DISPLAY_STATUS_NORMAL;
+				int8 display_status = 0;
 				if(itr->second->GetCompleted())
 					packet->setArrayDataByName("completed", 1, i);
 				if(itr->second->GetTurnedIn()){
@@ -4396,6 +4396,9 @@ PacketStruct* Player::GetQuestJournalPacket(bool all_quests, int16 version, int3
 
 					if (quest->IsHidden() && !quest->GetTurnedIn())
 						display_status = QUEST_DISPLAY_STATUS_HIDDEN;
+					else if(quest->CanShareQuestCriteria(GetClient(),false)) {
+						display_status += QUEST_DISPLAY_STATUS_CAN_SHARE;
+					}
 				}
 				else
 					packet->setArrayDataByName("visible", quest->GetVisible(), i);
@@ -4443,7 +4446,7 @@ PacketStruct* Player::GetQuestJournalPacket(Quest* quest, int16 version, int32 c
 		packet->setArrayDataByName("quest_type", quest->GetType());
 		packet->setArrayDataByName("quest_zone", quest->GetZone());
 
-		int8 display_status = QUEST_DISPLAY_STATUS_NORMAL;
+		int8 display_status = 0;
 		if(quest->GetCompleted())
 			packet->setArrayDataByName("completed", 1);
 		if(quest->GetTurnedIn()) {
@@ -4489,12 +4492,15 @@ PacketStruct* Player::GetQuestJournalPacket(Quest* quest, int16 version, int32 c
 
 			if (quest->IsHidden() && !quest->GetTurnedIn())
 				display_status = QUEST_DISPLAY_STATUS_HIDDEN;
+			else if(quest->CanShareQuestCriteria(GetClient(),false)) {
+			display_status += QUEST_DISPLAY_STATUS_CAN_SHARE;
+		}
 		}
 		else
 			packet->setArrayDataByName("visible", quest->GetVisible());
 		if (quest->IsRepeatable())
 			packet->setArrayDataByName("repeatable", 1);
-
+				
 		packet->setArrayDataByName("display_status", display_status);
 		if (updated) {
 			packet->setArrayDataByName("quest_updated", 1);

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

@@ -514,6 +514,7 @@ public:
 	Quest* SetStepComplete(int32 quest_id, int32 step);
 	Quest* AddStepProgress(int32 quest_id, int32 step, int32 progress);
 	int32  GetStepProgress(int32 quest_id, int32 step_id);
+	Quest* GetQuestByPositionID(int32 list_position_id);
 	bool AddItem(Item* item, AddItemType type = AddItemType::NOT_SET);
 	bool AddItemToBank(Item* item);
 	int16 GetSpellSlotMappingCount();

+ 28 - 0
EQ2/source/WorldServer/PlayerGroups.cpp

@@ -193,6 +193,34 @@ bool PlayerGroup::MakeLeader(Entity* new_leader) {
 }
 
 
+bool PlayerGroup::ShareQuestWithGroup(Client* quest_sharer, Quest* quest) {
+	if(!quest || !quest_sharer)
+		return false;
+	
+	bool canShare = quest->CanShareQuestCriteria(quest_sharer);
+	
+	if(!canShare) {
+		return false;
+	}
+	
+	deque<GroupMemberInfo*>::iterator itr;
+	MGroupMembers.readlock(__FUNCTION__, __LINE__);
+	for(itr = m_members.begin(); itr != m_members.end(); itr++) {
+		GroupMemberInfo* info = *itr;
+		if(info && info->client && info->client->GetCurrentZone()) {
+			if( quest_sharer != info->client && info->client->GetPlayer()->GetCompletedQuest(quest->GetQuestID()) == 0 && 
+				info->client->GetPlayer()->GetQuest(quest->GetQuestID()) == 0 ) {
+				info->client->AddPendingQuest(new Quest(quest));
+				info->client->Message(CHANNEL_COLOR_YELLOW, "%s has shared the quest %s with you.", quest_sharer->GetPlayer()->GetName(), quest->GetName());
+			}
+		}
+	}
+	MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
+	
+	return true;
+}
+
+
 
 /******************************************************** PlayerGroupManager ********************************************************/
 

+ 3 - 1
EQ2/source/WorldServer/PlayerGroups.h

@@ -96,7 +96,9 @@ public:
 	void SimpleGroupMessage(const char* message);
 	void GroupChatMessage(Spawn* from, int32 language, const char* message);
 	bool MakeLeader(Entity* new_leader);
-
+	
+	bool ShareQuestWithGroup(Client* quest_sharer, Quest* quest);
+	
 	void RemoveClientReference(Client* remove);
 	void UpdateGroupMemberInfo(Entity* ent, bool groupMembersLocked=false);
 	Entity* GetGroupMemberByPosition(Entity* seeker, int32 mapped_position);

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

@@ -315,6 +315,8 @@ Quest::Quest(int32 in_id){
 	tmp_reward_coins = 0;
 	completed_description = string("");
 	quest_temporary_description = string("");
+	quest_shareable_flag = 0;
+	can_delete_quest = false;
 }
 
 Quest::Quest(Quest* old_quest){
@@ -389,6 +391,8 @@ Quest::Quest(Quest* old_quest){
 	tmp_reward_status = 0;
 	tmp_reward_coins = 0;
 	quest_temporary_description = string("");
+	quest_shareable_flag = old_quest->GetQuestShareableFlag();
+	can_delete_quest = old_quest->CanDeleteQuest();
 }
 
 Quest::~Quest(){
@@ -978,12 +982,12 @@ EQ2Packet* Quest::QuestJournalReply(int16 version, int32 player_crc, Player* pla
 		packet->setDataByName("unknown8b", 255);
 
 		if (version >= 1096) {
-			packet->setDataByName("deletable", 1);
-			packet->setDataByName("shareable", 1);
+			packet->setDataByName("deletable", (int8)CanDeleteQuest());
+			packet->setDataByName("shareable", (int8)CanShareQuestCriteria(player->GetClient(),false));
 		}
 		else {
-			packet->setDataByName("unknown3", 1, 5);
-			packet->setDataByName("unknown3", 1, 6);
+			packet->setDataByName("unknown3", 1, 5); // this supposed to be CanDeleteQuest?
+			packet->setDataByName("unknown3", 1, 6); // this supposed to be CanShareQuestCriteria?
 		}
 
 		int8 map_data_count = 0;
@@ -1805,4 +1809,40 @@ void Quest::SetQuestTemporaryState(bool tempState, std::string customDescription
 
 	quest_state_temporary = tempState;
 	quest_temporary_description = customDescription;
+}
+
+bool Quest::CanShareQuestCriteria(Client* quest_sharer, bool display_client_msg) {
+	Quest* clientQuest = quest_sharer->GetPlayer()->GetQuest(GetQuestID()); // gets active quest if available
+	if(((GetQuestShareableFlag() & QUEST_SHAREABLE_COMPLETED) == 0) && quest_sharer->GetPlayer()->GetCompletedQuest(GetQuestID())) {
+		if(display_client_msg)
+			quest_sharer->SimpleMessage(CHANNEL_COLOR_RED, "You cannot share this quest after it is already completed.");
+		return false;
+	}
+	else if((GetQuestShareableFlag() == QUEST_SHAREABLE_COMPLETED) && !quest_sharer->GetPlayer()->GetCompletedQuest(GetQuestID())) {
+		if(display_client_msg)
+			quest_sharer->SimpleMessage(CHANNEL_COLOR_RED, "You cannot share this quest until it is completed.");
+		return false;
+	}
+	else if(((GetQuestShareableFlag() & QUEST_SHAREABLE_DURING) == 0) && clientQuest && clientQuest->GetQuestStep() > 1) {
+		if(display_client_msg)
+			quest_sharer->SimpleMessage(CHANNEL_COLOR_RED, "You cannot share this quest after already completing a step.");
+		return false;
+	}
+	else if(((GetQuestShareableFlag() & QUEST_SHAREABLE_ACTIVE) == 0) && clientQuest) {
+		if(display_client_msg)
+			quest_sharer->SimpleMessage(CHANNEL_COLOR_RED, "You cannot share this quest while it is active.");
+		return false;
+	}
+	else if(!GetQuestShareableFlag()) {
+		if(display_client_msg)
+			quest_sharer->SimpleMessage(CHANNEL_COLOR_RED, "You cannot share this quest.");
+		return false;
+	}
+	else if(((GetQuestShareableFlag() & QUEST_SHAREABLE_COMPLETED) == 0) && clientQuest == nullptr) {
+		if(display_client_msg)
+			quest_sharer->SimpleMessage(CHANNEL_COLOR_RED, "You do not have this quest.");
+		return false;
+	}
+	
+	return true;
 }

+ 16 - 3
EQ2/source/WorldServer/Quests.h

@@ -39,13 +39,16 @@
 #define QUEST_DISPLAY_STATUS_YELLOW			2
 #define QUEST_DISPLAY_STATUS_COMPLETED		4
 #define QUEST_DISPLAY_STATUS_REPEATABLE		8
-#define QUEST_DISPLAY_STATUS_UNKNOWN1		16
+#define QUEST_DISPLAY_STATUS_CAN_SHARE		16
 #define QUEST_DISPLAY_STATUS_COMPLETE_FLAG	32
 #define	QUEST_DISPLAY_STATUS_UNKNOWN2		64
 #define QUEST_DISPLAY_STATUS_CHECK			128
 
-// Almost all quests have these values, but they don't see to effect anything
-#define QUEST_DISPLAY_STATUS_NORMAL			QUEST_DISPLAY_STATUS_UNKNOWN1 + QUEST_DISPLAY_STATUS_UNKNOWN2
+
+#define QUEST_SHAREABLE_NONE				0
+#define QUEST_SHAREABLE_ACTIVE				1
+#define QUEST_SHAREABLE_DURING				2
+#define QUEST_SHAREABLE_COMPLETED			4
 
 struct QuestFactionPrereq{
 	int32	faction_id;
@@ -317,6 +320,14 @@ public:
 	void				SetQuestTemporaryState(bool tempState, std::string customDescription = string(""));
 	bool				GetQuestTemporaryState() { return quest_state_temporary; }
 	std::string			GetQuestTemporaryDescription() { return quest_temporary_description; }
+	
+	void				SetQuestShareableFlag(int32 flag) { quest_shareable_flag = flag; }
+	void				SetCanDeleteQuest(bool newval) { can_delete_quest = newval; }
+	
+	int32				GetQuestShareableFlag() { return quest_shareable_flag; }
+	bool				CanDeleteQuest() { return can_delete_quest; }
+	
+	bool				CanShareQuestCriteria(Client* quest_sharer, bool display_client_msg = true);
 protected:
 	bool				needs_save;
 	Mutex				MQuestSteps;
@@ -397,6 +408,8 @@ protected:
 
 	bool				quest_state_temporary;
 	std::string			quest_temporary_description;
+	int32				quest_shareable_flag;
+	bool				can_delete_quest;
 };
 
 class MasterQuestList{

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

@@ -5018,7 +5018,8 @@ void WorldDatabase::FixBugReport(){
 int32 WorldDatabase::LoadQuests(){
 	Query query;
 	MYSQL_ROW row;
-	MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `quest_id`, `name`, `type`, `zone`, `level`, `enc_level`, `description`, `lua_script`, `completed_text`, `spawn_id` FROM `quests`");
+	std::string querystr = std::string("SELECT `quest_id`, `name`, `type`, `zone`, `level`, `enc_level`, `description`, `lua_script`, `completed_text`, `spawn_id`, `shareable_flag`, `deleteable` FROM `quests`");
+	MYSQL_RES* result = query.RunQuery2(Q_SELECT, querystr.c_str());
 	Quest* quest = 0;
 	char* name = 0;
 	char* type = 0;
@@ -5063,6 +5064,8 @@ int32 WorldDatabase::LoadQuests(){
 				quest->SetCompletedDescription(string(compDescription));
 				quest->SetQuestReturnNPC(return_npc_id);
 				quest->SetEncounterLevel(enc_level);
+				quest->SetQuestShareableFlag(atoul(row[10]));
+				quest->SetCanDeleteQuest(atoul(row[11]));
 				total++;
 				master_quest_list.AddQuest(id, quest);
 			}

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

@@ -10521,6 +10521,7 @@ bool Client::HandleNewLogin(int32 account_id, int32 access_code)
 		delayTimer.Disable();
 
 		firstlogin = zar->isFirstLogin();
+		SetReadyForSpawns(false);
 		ready_for_updates = false;
 		LogWrite(ZONE__INFO, 0, "ZoneAuth", "Access Key: %u, Character Name: %s, Account ID: %u, Client Data Version: %u", zar->GetAccessKey(), zar->GetCharacterName(), zar->GetAccountID(), version);
 		if (database.loadCharacter(zar->GetCharacterName(), zar->GetAccountID(), this)) {