From 564cf2b15b115ad667b5012430b356e0d4c5695f Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Sat, 2 May 2015 02:06:53 +0300 Subject: [PATCH] Add quest add and remove commands and make questcomplete try satisfy the requirements and act like quest complete command. Merge SetLevel and GiveLevel for player methods. --- CreatureMethods.h | 24 ++--- LuaFunctions.cpp | 6 +- PlayerMethods.h | 263 ++++++++++++++++++++++++++++++++++++++++++---- UnitMethods.h | 19 +++- 4 files changed, 274 insertions(+), 38 deletions(-) diff --git a/CreatureMethods.h b/CreatureMethods.h index 8df3808..266fa94 100644 --- a/CreatureMethods.h +++ b/CreatureMethods.h @@ -634,29 +634,27 @@ namespace LuaCreature * Returns a target from the [Creature]'s threat list based on the * supplied arguments. * - *
-     * enum SelectAggroTarget
-     * {
-     *     SELECT_TARGET_RANDOM = 0,  //Just selects a random target
-     *     SELECT_TARGET_TOPAGGRO,    //Selects targets from top aggro to bottom
-     *     SELECT_TARGET_BOTTOMAGGRO, //Selects targets from bottom aggro to top
-     *     SELECT_TARGET_NEAREST,
-     *     SELECT_TARGET_FARTHEST
-     * };
-     * 
+ * enum SelectAggroTarget + * { + * SELECT_TARGET_RANDOM = 0, //Just selects a random target + * SELECT_TARGET_TOPAGGRO, //Selects targets from top aggro to bottom + * SELECT_TARGET_BOTTOMAGGRO, //Selects targets from bottom aggro to top + * SELECT_TARGET_NEAREST, + * SELECT_TARGET_FARTHEST + * }; * * For example, if you wanted to select the third-farthest [Player] * within 50 yards that has the [Aura] "Corrupted Blood" (ID 24328), * you could use this function like so: * - * target = creature:GetAITarget(4, true, 3, 50, 24328) + * target = creature:GetAITarget(4, true, 3, 50, 24328) * - * @param SelectAggroTarget targetType : how the threat list should be sorted + * @param [SelectAggroTarget] targetType : how the threat list should be sorted * @param bool playerOnly = false : if `true`, skips targets that aren't [Player]s * @param uint32 position = 0 : used as an offset into the threat list. If `targetType` is random, used as the number of players from top of aggro to choose from * @param float distance = 0.0 : if positive, the maximum distance for the target. If negative, the minimum distance * @param int32 aura = 0 : if positive, the target must have this [Aura]. If negative, the the target must not have this Aura - * @return Unit target : the target, or `nil` + * @return [Unit] target : the target, or `nil` */ int GetAITarget(Eluna* /*E*/, lua_State* L, Creature* creature) { diff --git a/LuaFunctions.cpp b/LuaFunctions.cpp index a0b75fa..7cce15d 100644 --- a/LuaFunctions.cpp +++ b/LuaFunctions.cpp @@ -576,6 +576,7 @@ ElunaRegister PlayerMethods[] = #ifndef CLASSIC { "IsInArenaTeam", &LuaPlayer::IsInArenaTeam }, // :IsInArenaTeam(type) - type : 0 = 2v2, 1 = 3v3, 2 = 5v5 #endif + { "CanCompleteQuest", &LuaPlayer::CanCompleteQuest }, { "CanEquipItem", &LuaPlayer::CanEquipItem }, // :CanEquipItem(entry/item, slot) - Returns true if the player can equip given item/item entry { "IsFalling", &LuaPlayer::IsFalling }, // :IsFalling() - Returns true if the unit is falling { "ToggleAFK", &LuaPlayer::ToggleAFK }, // :ToggleAFK() - Toggles AFK state for player @@ -661,7 +662,6 @@ ElunaRegister PlayerMethods[] = { "ResetSpellCooldown", &LuaPlayer::ResetSpellCooldown }, // :ResetSpellCooldown(spellId, update(bool~optional)) - Resets cooldown of the specified spellId. If update is true, it will send WorldPacket SMSG_CLEAR_COOLDOWN to the player, else it will just clear the spellId from m_spellCooldowns. This is true by default { "ResetTypeCooldowns", &LuaPlayer::ResetTypeCooldowns }, // :ResetTypeCooldowns(category, update(bool~optional)) - Resets all cooldowns for the spell category(type). If update is true, it will send WorldPacket SMSG_CLEAR_COOLDOWN to the player, else it will just clear the spellId from m_spellCooldowns. This is true by default { "ResetAllCooldowns", &LuaPlayer::ResetAllCooldowns }, // :ResetAllCooldowns() - Resets all spell cooldowns - { "GiveLevel", &LuaPlayer::GiveLevel }, // :GiveLevel(level) - Gives levels to the player { "GiveXP", &LuaPlayer::GiveXP }, // :GiveXP(xp[, victim, pureXP, triggerHook]) - Gives XP to the player. If pure is false, bonuses are count in. If triggerHook is false, GiveXp hook is not triggered. // {"RemovePet", &LuaPlayer::RemovePet}, // :RemovePet([mode, returnreagent]) - Removes the player's pet. Mode determines if the pet is saved and how // {"SummonPet", &LuaPlayer::SummonPet}, // :SummonPet(entry, x, y, z, o, petType, despwtime) - Summons a pet for the player @@ -672,6 +672,8 @@ ElunaRegister PlayerMethods[] = { "CompleteQuest", &LuaPlayer::CompleteQuest }, // :CompleteQuest(entry) - Completes a quest by entry { "IncompleteQuest", &LuaPlayer::IncompleteQuest }, // :IncompleteQuest(entry) - Uncompletes the quest by entry for the player { "FailQuest", &LuaPlayer::FailQuest }, // :FailQuest(entry) - Player fails the quest entry + { "AddQuest", &LuaPlayer::AddQuest }, + { "RemoveQuest", &LuaPlayer::RemoveQuest }, // {"RemoveActiveQuest", &LuaPlayer::RemoveActiveQuest}, // :RemoveActiveQuest(entry) - Removes an active quest // {"RemoveRewardedQuest", &LuaPlayer::RemoveRewardedQuest}, // :RemoveRewardedQuest(entry) - Removes a rewarded quest { "AreaExploredOrEventHappens", &LuaPlayer::AreaExploredOrEventHappens }, // :AreaExploredOrEventHappens(questId) - Satisfies an area or event requrement for the questId @@ -721,7 +723,7 @@ ElunaRegister PlayerMethods[] = { "SendTabardVendorActivate", &LuaPlayer::SendTabardVendorActivate }, // :SendTabardVendorActivate(WorldObject) - Sends tabard vendor window from object to player { "SendSpiritResurrect", &LuaPlayer::SendSpiritResurrect }, // :SendSpiritResurrect() - Sends resurrect window to player { "SendTaxiMenu", &LuaPlayer::SendTaxiMenu }, // :SendTaxiMenu(creature) - Sends flight window to player from creature - { "RewardQuest", &LuaPlayer::RewardQuest }, // :RewardQuest(entry) - Gives quest rewards for the player + { "RewardQuest", &LuaPlayer::RewardQuest }, { "SendAuctionMenu", &LuaPlayer::SendAuctionMenu }, // :SendAuctionMenu(unit) - Sends auction window to player. Auction house is sent by object. { "SendShowMailBox", &LuaPlayer::SendShowMailBox }, // :SendShowMailBox([mailboxguid]) - Sends the mail window to player from the mailbox gameobject. The guid is required on patches below wotlk. { "StartTaxi", &LuaPlayer::StartTaxi }, // :StartTaxi(pathId) - player starts the given flight path diff --git a/PlayerMethods.h b/PlayerMethods.h index 188fb4b..7b2b3a2 100644 --- a/PlayerMethods.h +++ b/PlayerMethods.h @@ -395,6 +395,20 @@ namespace LuaPlayer } #endif + /** + * Returns 'true' if the [Player] satisfies all requirements to complete the quest entry. + * + * @param uint32 entry + * @return bool canComplete + */ + int CanCompleteQuest(Eluna* /*E*/, lua_State* L, Player* player) + { + uint32 entry = Eluna::CHECKVAL(L, 2); + + Eluna::Push(L, player->CanCompleteQuest(entry)); + return 1; + } + /** * Returns 'true' if the [Player] is a part of the Horde faction, 'false' otherwise. * @@ -1193,19 +1207,6 @@ namespace LuaPlayer return 1; } - /** - * Give the amount of levels specified to the [Player] - * - * @param uint8 levelAmt - */ - int GiveLevel(Eluna* /*E*/, lua_State* L, Player* player) - { - uint8 level = Eluna::CHECKVAL(L, 2); - - player->GiveLevel(level); - return 0; - } - int GetChatTag(Eluna* /*E*/, lua_State* L, Player* player) { Eluna::Push(L, player->GetChatTag()); @@ -2088,13 +2089,22 @@ namespace LuaPlayer return 0; } + /** + * Rewards the given quest entry for the [Player] if he has completed it. + * + * @param uint32 entry : quest entry + */ int RewardQuest(Eluna* /*E*/, lua_State* L, Player* player) { uint32 entry = Eluna::CHECKVAL(L, 2); Quest const* quest = eObjectMgr->GetQuestTemplate(entry); - if (quest) - player->RewardQuest(quest, 0, player); + + // If player doesn't have the quest + if (!quest || player->GetQuestStatus(entry) != QUEST_STATUS_COMPLETE) + return 0; + + player->RewardQuest(quest, 0, player); return 0; } @@ -2511,9 +2521,9 @@ namespace LuaPlayer } /** - * Forces a [Player]s [Quest] by entry ID to fail + * Sets the given quest entry failed for the [Player]. * - * @param uint32 entryId + * @param uint32 entry : quest entry */ int FailQuest(Eluna* /*E*/, lua_State* L, Player* player) { @@ -2524,9 +2534,9 @@ namespace LuaPlayer } /** - * Flags a [Player]s [Quest] by entry ID as incomplete + * Sets the given quest entry incomplete for the [Player]. * - * @param uint32 entryId + * @param uint32 entry : quest entry */ int IncompleteQuest(Eluna* /*E*/, lua_State* L, Player* player) { @@ -2537,18 +2547,229 @@ namespace LuaPlayer } /** - * Completes a [Player]s [Quest] by entry ID + * Completes the given quest entry for the [Player] and tries to satisfy all quest requirements. * - * @param uint32 entryId + * The player should have the quest to complete it. + * + * @param uint32 entry : quest entry */ int CompleteQuest(Eluna* /*E*/, lua_State* L, Player* player) { uint32 entry = Eluna::CHECKVAL(L, 2); + Quest const* quest = eObjectMgr->GetQuestTemplate(entry); + + // If player doesn't have the quest + if (!quest || player->GetQuestStatus(entry) == QUEST_STATUS_NONE) + return 0; + + // Add quest items for quests that require items + for (uint8 x = 0; x < QUEST_ITEM_OBJECTIVES_COUNT; ++x) + { +#ifdef TRINITY + uint32 id = quest->RequiredItemId[x]; + uint32 count = quest->RequiredItemCount[x]; +#else + uint32 id = quest->ReqItemId[x]; + uint32 count = quest->ReqItemCount[x]; +#endif + + if (!id || !count) + continue; + + uint32 curItemCount = player->GetItemCount(id, true); + + ItemPosCountVec dest; + uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, id, count - curItemCount); + if (msg == EQUIP_ERR_OK) + { + Item* item = player->StoreNewItem(dest, id, true); + player->SendNewItem(item, count - curItemCount, true, false); + } + } + + // All creature/GO slain/cast (not required, but otherwise it will display "Creature slain 0/10") + for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) + { +#ifdef TRINITY + int32 creature = quest->RequiredNpcOrGo[i]; + uint32 creatureCount = quest->RequiredNpcOrGoCount[i]; + + if (creature > 0) + { + if (CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(creature)) + for (uint16 z = 0; z < creatureCount; ++z) + player->KilledMonster(creatureInfo, ObjectGuid::Empty); + } + else if (creature < 0) + for (uint16 z = 0; z < creatureCount; ++z) + player->KillCreditGO(creature); +#else + int32 creature = quest->ReqCreatureOrGOId[i]; + uint32 creaturecount = quest->ReqCreatureOrGOCount[i]; + + if (uint32 spell_id = quest->ReqSpell[i]) + { + for (uint16 z = 0; z < creaturecount; ++z) + player->CastedCreatureOrGO(creature, ObjectGuid(), spell_id); + } + else if (creature > 0) + { + if (CreatureInfo const* cInfo = ObjectMgr::GetCreatureTemplate(creature)) + for (uint16 z = 0; z < creaturecount; ++z) + player->KilledMonster(cInfo, ObjectGuid()); + } + else if (creature < 0) + { + for (uint16 z = 0; z < creaturecount; ++z) + player->CastedCreatureOrGO(-creature, ObjectGuid(), 0); + } +#endif + } + + + // If the quest requires reputation to complete + if (uint32 repFaction = quest->GetRepObjectiveFaction()) + { + uint32 repValue = quest->GetRepObjectiveValue(); + uint32 curRep = player->GetReputationMgr().GetReputation(repFaction); + if (curRep < repValue) + if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(repFaction)) + player->GetReputationMgr().SetReputation(factionEntry, repValue); + } + +#ifdef TRINITY + // If the quest requires a SECOND reputation to complete + if (uint32 repFaction = quest->GetRepObjectiveFaction2()) + { + uint32 repValue2 = quest->GetRepObjectiveValue2(); + uint32 curRep = player->GetReputationMgr().GetReputation(repFaction); + if (curRep < repValue2) + if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(repFaction)) + player->GetReputationMgr().SetReputation(factionEntry, repValue2); + } +#endif + + // If the quest requires money + int32 ReqOrRewMoney = quest->GetRewOrReqMoney(); + if (ReqOrRewMoney < 0) + player->ModifyMoney(-ReqOrRewMoney); + +#ifdef TRINITY + if (sWorld->getBoolConfig(CONFIG_QUEST_ENABLE_QUEST_TRACKER)) // check if Quest Tracker is enabled + { + // prepare Quest Tracker datas + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_QUEST_TRACK_GM_COMPLETE); + stmt->setUInt32(0, quest->GetQuestId()); + stmt->setUInt32(1, player->GetGUIDLow()); + + // add to Quest Tracker + CharacterDatabase.Execute(stmt); + } +#endif + player->CompleteQuest(entry); return 0; } + /** + * Tries to add the given quest entry for the [Player]. + * + * @param uint32 entry : quest entry + */ + int AddQuest(Eluna* /*E*/, lua_State* L, Player* player) + { + uint32 entry = Eluna::CHECKVAL(L, 2); + + Quest const* quest = eObjectMgr->GetQuestTemplate(entry); + + if (!quest) + return 0; + +#ifdef TRINITY + // check item starting quest (it can work incorrectly if added without item in inventory) + ItemTemplateContainer const* itc = sObjectMgr->GetItemTemplateStore(); + ItemTemplateContainer::const_iterator result = find_if(itc->begin(), itc->end(), Finder(entry, &ItemTemplate::StartQuest)); + + if (result != itc->end()) + return 0; + + // ok, normal (creature/GO starting) quest + if (player->CanAddQuest(quest, true)) + player->AddQuestAndCheckCompletion(quest, NULL); +#else + // check item starting quest (it can work incorrectly if added without item in inventory) + for (uint32 id = 0; id < sItemStorage.GetMaxEntry(); ++id) + { + ItemPrototype const* pProto = sItemStorage.LookupEntry(id); + if (!pProto) + continue; + + if (pProto->StartQuest == entry) + return 0; + } + + // ok, normal (creature/GO starting) quest + if (player->CanAddQuest(quest, true)) + { + player->AddQuest(quest, NULL); + + if (player->CanCompleteQuest(entry)) + player->CompleteQuest(entry); + } +#endif + + return 0; + } + + /** + * Tries to add the given quest entry for the [Player]. + * + * @param uint32 entry : quest entry + */ + int RemoveQuest(Eluna* /*E*/, lua_State* L, Player* player) + { + uint32 entry = Eluna::CHECKVAL(L, 2); + + Quest const* quest = eObjectMgr->GetQuestTemplate(entry); + + if (!quest) + return 0; + + // remove all quest entries for 'entry' from quest log + for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot) + { + uint32 logQuest = player->GetQuestSlotQuestId(slot); + if (logQuest == entry) + { + player->SetQuestSlot(slot, 0); + + // we ignore unequippable quest items in this case, its' still be equipped + player->TakeQuestSourceItem(logQuest, false); + +#ifdef TRINITY + if (quest->HasFlag(QUEST_FLAGS_FLAGS_PVP)) + { + player->pvpInfo.IsHostile = player->pvpInfo.IsInHostileArea || player->HasPvPForcingQuest(); + player->UpdatePvPState(); + } +#endif + } + } + +#ifdef TRINITY + player->RemoveActiveQuest(entry, false); + player->RemoveRewardedQuest(entry); +#else + // set quest status to not started (will updated in DB at next save) + player->SetQuestStatus(entry, QUEST_STATUS_NONE); + + // reset rewarded for restart repeatable quest + player->getQuestStatusMap()[entry].m_rewarded = false; +#endif + return 0; + } + int Whisper(Eluna* /*E*/, lua_State* L, Player* player) { std::string text = Eluna::CHECKVAL(L, 2); diff --git a/UnitMethods.h b/UnitMethods.h index 3cee44e..df6cf58 100644 --- a/UnitMethods.h +++ b/UnitMethods.h @@ -1431,8 +1431,23 @@ namespace LuaUnit */ int SetLevel(Eluna* /*E*/, lua_State* L, Unit* unit) { - uint8 newLevel = Eluna::CHECKVAL(L, 2); - unit->SetLevel(newLevel); + uint8 newlevel = Eluna::CHECKVAL(L, 2); + + if (newlevel < 1) + return 0; + + if (newlevel > STRONG_MAX_LEVEL) + newlevel = STRONG_MAX_LEVEL; + + if (Player* player = unit->ToPlayer()) + { + player->GiveLevel(newlevel); + player->InitTalentForLevel(); + player->SetUInt32Value(PLAYER_XP, 0); + } + else + unit->SetLevel(newlevel); + return 0; }