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