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

This commit is contained in:
Emagi 2023-02-16 18:14:48 -05:00
parent 6f21dcd29d
commit 3ee4e5e105
11 changed files with 151 additions and 17 deletions

View file

@ -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;

View file

@ -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.");
}
}
}

View file

@ -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

View file

@ -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);

View file

@ -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();

View file

@ -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 ********************************************************/

View file

@ -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);

View file

@ -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;
}

View file

@ -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{

View file

@ -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);
}

View file

@ -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)) {