Added item to summon DungeonBots
clang PCH / build (push) Has been cancelled
GCC PCH / build (push) Has been cancelled

This commit is contained in:
Nico committed 2026-10-07 19:49:32 +02:00
1 parent e96d617284
commit 634606b5ba
2 files changed
+1026 -18

No files matched your search

+536 -18
View File
@@ -31,6 +31,7 @@ EndContentData */
#include "ScriptMgr.h"
#include "GameObject.h"
#include "Group.h"
#include "Item.h"
#include "Player.h"
#include "ScriptedCreature.h"
@@ -38,6 +39,9 @@ EndContentData */
#include "SpellMgr.h"
#include "TemporarySummon.h"
#include "Log.h"
#include <algorithm>
#include <list>
#include <vector>
namespace ItemScripts
{
@@ -414,26 +418,298 @@ class item_generic_limit_chance_above_60 : public ItemScript
}
};
/*#####
# item_horde_adventurer_horn
# Vcore dungeon horns
#####*/
class item_horde_adventurer_horn : public ItemScript
enum VcoreDungeonHornSpells
{
SPELL_VCORE_HORN_TANK = 120000,
SPELL_VCORE_HORN_HEALER = 120001,
SPELL_VCORE_HORN_DAMAGE = 120002,
SPELL_VCORE_HORN_DAMAGE_3 = 120003
};
enum VcoreDungeonHornNpcs
{
NPC_HORDE_ADVENTURER_DAMAGE = 900001,
NPC_HORDE_ADVENTURER_TANK = 900002,
NPC_HORDE_ADVENTURER_HEALER = 900003,
// TODO: replace once Alliance creature templates exist.
NPC_ALLIANCE_ADVENTURER_DAMAGE = 0,
NPC_ALLIANCE_ADVENTURER_TANK = 0,
NPC_ALLIANCE_ADVENTURER_HEALER = 0
};
static constexpr uint32 VCORE_DUNGEON_GROUP_LIMIT = 5;
struct VcoreHornPlan
{
uint32 occupancy = 0;
uint32 freeSlots = 0;
uint32 ownBots = 0;
uint32 spawnCount = 0;
uint32 replaceCount = 0;
};
static uint32 GetVcoreHornSpellForItem(uint32 itemId)
{
switch (itemId)
{
case 80000:
case 80004:
return SPELL_VCORE_HORN_TANK;
case 80001:
case 80005:
return SPELL_VCORE_HORN_HEALER;
case 80002:
case 80006:
return SPELL_VCORE_HORN_DAMAGE;
case 80003:
case 80007:
return SPELL_VCORE_HORN_DAMAGE_3;
default:
return 0;
}
}
static uint32 GetVcoreHornRequestedCount(uint32 spellId)
{
return spellId == SPELL_VCORE_HORN_DAMAGE_3 ? 3u : 1u;
}
static bool IsVcoreHordeHorn(uint32 itemId)
{
return itemId >= 80000 && itemId <= 80003;
}
static bool IsVcoreAllianceHorn(uint32 itemId)
{
return itemId >= 80004 && itemId <= 80007;
}
static uint32 GetVcoreHornNpcEntry(Player* player, uint32 spellId)
{
if (!player)
return 0;
if (player->GetTeam() == HORDE)
{
switch (spellId)
{
case SPELL_VCORE_HORN_TANK:
return NPC_HORDE_ADVENTURER_TANK;
case SPELL_VCORE_HORN_HEALER:
return NPC_HORDE_ADVENTURER_HEALER;
case SPELL_VCORE_HORN_DAMAGE:
case SPELL_VCORE_HORN_DAMAGE_3:
return NPC_HORDE_ADVENTURER_DAMAGE;
default:
return 0;
}
}
if (player->GetTeam() == ALLIANCE)
{
switch (spellId)
{
case SPELL_VCORE_HORN_TANK:
return NPC_ALLIANCE_ADVENTURER_TANK;
case SPELL_VCORE_HORN_HEALER:
return NPC_ALLIANCE_ADVENTURER_HEALER;
case SPELL_VCORE_HORN_DAMAGE:
case SPELL_VCORE_HORN_DAMAGE_3:
return NPC_ALLIANCE_ADVENTURER_DAMAGE;
default:
return 0;
}
}
return 0;
}
static void CollectVcoreDungeonBots(
Player* player,
std::vector<Creature*>& allBots,
std::vector<Creature*>& ownBots)
{
allBots.clear();
ownBots.clear();
if (!player)
return;
std::list<Unit*> units;
player->GetPartyMembers(units);
for (Unit* unit : units)
{
Creature* creature = unit ? unit->ToCreature() : nullptr;
if (!creature)
continue;
if (!creature->IsAlive())
continue;
if (!creature->IsVianoAdventurer())
continue;
if (!creature->IsInMap(player))
continue;
if (std::find(allBots.begin(), allBots.end(), creature) == allBots.end())
allBots.push_back(creature);
Unit* owner = creature->GetOwner();
if (owner && owner->GetGUID() == player->GetGUID())
ownBots.push_back(creature);
}
// Current Viano summons receive increasing Creature GUIDs.
// The Viano formation code already uses the same ordering.
// Therefore this gives oldest -> newest.
std::sort(
ownBots.begin(),
ownBots.end(),
[](Creature const* left, Creature const* right)
{
return left->GetGUID().GetRawValue()
< right->GetGUID().GetRawValue();
});
}
static uint32 GetVcoreRealPlayerCount(Player* player)
{
if (!player)
return 0;
if (Group* group = player->GetGroup())
return group->GetMembersCount();
return 1;
}
static VcoreHornPlan BuildVcoreHornPlan(
Player* player,
uint32 requestedCount,
std::vector<Creature*>& ownBots)
{
VcoreHornPlan plan;
std::vector<Creature*> allBots;
CollectVcoreDungeonBots(
player,
allBots,
ownBots);
uint32 const realPlayers =
GetVcoreRealPlayerCount(player);
uint32 const botCount =
uint32(allBots.size());
plan.occupancy =
realPlayers + botCount;
plan.ownBots =
uint32(ownBots.size());
// Do not mutate an already invalid >5 state.
if (plan.occupancy > VCORE_DUNGEON_GROUP_LIMIT)
return plan;
plan.freeSlots =
VCORE_DUNGEON_GROUP_LIMIT - plan.occupancy;
plan.spawnCount =
std::min(
requestedCount,
plan.freeSlots + plan.ownBots);
plan.replaceCount =
plan.spawnCount > plan.freeSlots
? plan.spawnCount - plan.freeSlots
: 0;
return plan;
}
class item_vcore_dungeon_horn : public ItemScript
{
public:
item_horde_adventurer_horn() : ItemScript("item_horde_adventurer_horn") { }
bool OnUse(Player* player, Item* item, SpellCastTargets const& /*targets*/) override
item_vcore_dungeon_horn()
: ItemScript("item_vcore_dungeon_horn")
{
}
bool OnUse(
Player* player,
Item* item,
SpellCastTargets const& /*targets*/) override
{
if (!player || !item)
return true;
uint32 const itemId =
item->GetEntry();
uint32 const spellId =
GetVcoreHornSpellForItem(itemId);
TC_LOG_INFO(
"scripts",
"[VianoHorn] OnUse player='%s' item=%u",
"[VianoHorn] OnUse player='%s' item=%u spell=%u",
player->GetName().c_str(),
item->GetEntry());
itemId,
spellId);
if (!spellId)
{
TC_LOG_ERROR(
"scripts",
"[VianoHorn] unknown horn item=%u",
itemId);
return true;
}
if ((IsVcoreHordeHorn(itemId)
&& player->GetTeam() != HORDE)
||
(IsVcoreAllianceHorn(itemId)
&& player->GetTeam() != ALLIANCE))
{
TC_LOG_INFO(
"scripts",
"[VianoHorn] rejected: wrong faction player='%s' item=%u",
player->GetName().c_str(),
itemId);
player->SendEquipError(
EQUIP_ERR_CLIENT_LOCKED_OUT,
item,
nullptr);
return true;
}
// Horns may not be used during combat.
if (player->IsInCombat())
{
TC_LOG_INFO("scripts",
TC_LOG_INFO(
"scripts",
"[VianoHorn] rejected: player is in combat");
player->SendEquipError(
@@ -444,10 +720,10 @@ public:
return true;
}
// Horns may only be used inside dungeon instances.
if (!player->GetMap()->IsDungeon())
{
TC_LOG_INFO("scripts",
TC_LOG_INFO(
"scripts",
"[VianoHorn] rejected: player is not inside a dungeon");
player->SendEquipError(
@@ -458,17 +734,258 @@ public:
return true;
}
// Alliance NPC templates are not configured yet.
// Reject before the normal cast so no cooldown is consumed.
uint32 const npcEntry =
GetVcoreHornNpcEntry(
player,
spellId);
if (!npcEntry)
{
TC_LOG_INFO(
"scripts",
"[VianoHorn] rejected: no creature entry configured "
"for team=%u spell=%u",
uint32(player->GetTeam()),
spellId);
player->SendEquipError(
EQUIP_ERR_CLIENT_LOCKED_OUT,
item,
nullptr);
return true;
}
uint32 const requestedCount =
GetVcoreHornRequestedCount(spellId);
std::vector<Creature*> ownBots;
VcoreHornPlan const plan =
BuildVcoreHornPlan(
player,
requestedCount,
ownBots);
TC_LOG_INFO(
"scripts",
"[VianoHorn] prechecks passed; allowing normal item spell processing");
"[VianoHorn] precheck occupancy=%u free=%u own=%u "
"requested=%u spawn=%u replace=%u",
plan.occupancy,
plan.freeSlots,
plan.ownBots,
requestedCount,
plan.spawnCount,
plan.replaceCount);
// IMPORTANT:
// false means the ItemScript does not consume/suppress the normal item use.
// The item's SpellID1 (120000-120003) may now run through the regular
// Trinity spell lifecycle.
// Full group and nothing belonging to this player
// can be replaced. Do not start the spell/cooldown.
if (!plan.spawnCount)
{
player->SendEquipError(
EQUIP_ERR_CLIENT_LOCKED_OUT,
item,
nullptr);
return true;
}
// Preserve the normal item -> spell lifecycle.
return false;
}
};
};
class spell_vcore_dungeon_horn : public SpellScriptLoader
{
public:
spell_vcore_dungeon_horn()
: SpellScriptLoader("spell_vcore_dungeon_horn")
{
}
class spell_vcore_dungeon_horn_SpellScript
: public SpellScript
{
void HandleAfterCast()
{
Player* player =
GetCaster()
? GetCaster()->ToPlayer()
: nullptr;
if (!player)
return;
uint32 const spellId =
GetSpellInfo()->Id;
if (spellId < SPELL_VCORE_HORN_TANK
|| spellId > SPELL_VCORE_HORN_DAMAGE_3)
{
return;
}
// Re-check immediately before changing world state.
if (player->IsInCombat())
{
TC_LOG_INFO(
"scripts",
"[VianoHorn] AfterCast aborted: player entered combat");
return;
}
if (!player->GetMap()->IsDungeon())
{
TC_LOG_INFO(
"scripts",
"[VianoHorn] AfterCast aborted: player left dungeon");
return;
}
uint32 const npcEntry =
GetVcoreHornNpcEntry(
player,
spellId);
if (!npcEntry)
{
TC_LOG_ERROR(
"scripts",
"[VianoHorn] AfterCast aborted: no NPC entry "
"team=%u spell=%u",
uint32(player->GetTeam()),
spellId);
return;
}
uint32 const requestedCount =
GetVcoreHornRequestedCount(spellId);
std::vector<Creature*> ownBots;
VcoreHornPlan const plan =
BuildVcoreHornPlan(
player,
requestedCount,
ownBots);
TC_LOG_INFO(
"scripts",
"[VianoHorn] AfterCast player='%s' spell=%u npc=%u "
"occupancy=%u free=%u own=%u requested=%u "
"spawn=%u replace=%u",
player->GetName().c_str(),
spellId,
npcEntry,
plan.occupancy,
plan.freeSlots,
plan.ownBots,
requestedCount,
plan.spawnCount,
plan.replaceCount);
if (!plan.spawnCount)
return;
// Replace only this player's oldest active bots.
for (uint32 i = 0;
i < plan.replaceCount
&& i < ownBots.size();
++i)
{
Creature* oldBot =
ownBots[i];
if (!oldBot)
continue;
TC_LOG_INFO(
"scripts",
"[VianoHorn] replacing own bot "
"entry=%u guid=%s",
oldBot->GetEntry(),
oldBot->GetGUID().ToString().c_str());
oldBot->DespawnOrUnsummon();
}
uint32 spawned = 0;
for (uint32 i = 0;
i < plan.spawnCount;
++i)
{
// Prevent 3-DD summons from stacking exactly
// on the same coordinates.
float const yOffset =
(float(i)
- (float(plan.spawnCount) - 1.0f) / 2.0f)
* 1.5f;
Creature* summon =
player->SummonCreature(
npcEntry,
player->GetPositionX() + 2.0f,
player->GetPositionY() + yOffset,
player->GetPositionZ(),
player->GetOrientation(),
TEMPSUMMON_MANUAL_DESPAWN,
0);
if (!summon)
{
TC_LOG_ERROR(
"scripts",
"[VianoHorn] summon failed "
"player='%s' spell=%u npc=%u index=%u",
player->GetName().c_str(),
spellId,
npcEntry,
i);
continue;
}
++spawned;
TC_LOG_INFO(
"scripts",
"[VianoHorn] summoned "
"entry=%u guid=%s owner='%s'",
summon->GetEntry(),
summon->GetGUID().ToString().c_str(),
player->GetName().c_str());
}
TC_LOG_INFO(
"scripts",
"[VianoHorn] completed "
"player='%s' spell=%u spawned=%u "
"requested=%u replaced=%u",
player->GetName().c_str(),
spellId,
spawned,
requestedCount,
plan.replaceCount);
}
void Register() override
{
AfterCast.Register(
&spell_vcore_dungeon_horn_SpellScript::HandleAfterCast);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_vcore_dungeon_horn_SpellScript();
}
};
}// namespace ItemScripts
@@ -486,5 +1003,6 @@ void AddSC_item_scripts()
new item_dehta_trap_smasher();
new item_captured_frog();
new item_generic_limit_chance_above_60();
new item_horde_adventurer_horn();
new item_vcore_dungeon_horn();
new spell_vcore_dungeon_horn();
}
@@ -0,0 +1,490 @@
/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* ScriptData
SDName: Item_Scripts
SD%Complete: 100
SDComment: Items for a range of different items. See content below (in script)
SDCategory: Items
EndScriptData */
/* ContentData
item_nether_wraith_beacon(i31742) Summons creatures for quest Becoming a Spellfire Tailor (q10832)
item_flying_machine(i34060, i34061) Engineering crafted flying machines
item_gor_dreks_ointment(i30175) Protecting Our Own(q10488)
item_only_for_flight Items which should only useable while flying
EndContentData */
#include "ScriptMgr.h"
#include "GameObject.h"
#include "Item.h"
#include "Player.h"
#include "ScriptedCreature.h"
#include "Spell.h"
#include "SpellMgr.h"
#include "TemporarySummon.h"
#include "Log.h"
namespace ItemScripts
{
/*#####
# item_only_for_flight
#####*/
enum OnlyForFlight
{
SPELL_ARCANE_CHARGES = 45072
};
class item_only_for_flight : public ItemScript
{
public:
item_only_for_flight() : ItemScript("item_only_for_flight") { }
bool OnUse(Player* player, Item* item, SpellCastTargets const& /*targets*/) override
{
uint32 itemId = item->GetEntry();
bool disabled = false;
//for special scripts
switch (itemId)
{
case 24538:
if (player->GetAreaId() != 3628)
disabled = true;
break;
case 34489:
if (player->GetZoneId() != 4080)
disabled = true;
break;
case 34475:
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_ARCANE_CHARGES))
Spell::SendCastResult(player, spellInfo, 1, SPELL_FAILED_NOT_ON_GROUND);
break;
}
// allow use in flight only
if (player->IsInFlight() && !disabled)
return false;
// error
player->SendEquipError(EQUIP_ERR_CLIENT_LOCKED_OUT, item, nullptr);
return true;
}
};
/*#####
# item_nether_wraith_beacon
#####*/
class item_nether_wraith_beacon : public ItemScript
{
public:
item_nether_wraith_beacon() : ItemScript("item_nether_wraith_beacon") { }
bool OnUse(Player* player, Item* /*item*/, SpellCastTargets const& /*targets*/) override
{
if (player->GetQuestStatus(10832) == QUEST_STATUS_INCOMPLETE)
{
if (Creature* nether = player->SummonCreature(22408, player->GetPositionX(), player->GetPositionY()+20, player->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 180000))
nether->AI()->AttackStart(player);
if (Creature* nether = player->SummonCreature(22408, player->GetPositionX(), player->GetPositionY()-20, player->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 180000))
nether->AI()->AttackStart(player);
}
return false;
}
};
/*#####
# item_gor_dreks_ointment
#####*/
class item_gor_dreks_ointment : public ItemScript
{
public:
item_gor_dreks_ointment() : ItemScript("item_gor_dreks_ointment") { }
bool OnUse(Player* player, Item* item, SpellCastTargets const& targets) override
{
if (targets.GetUnitTarget() && targets.GetUnitTarget()->GetTypeId() == TYPEID_UNIT &&
targets.GetUnitTarget()->GetEntry() == 20748 && !targets.GetUnitTarget()->HasAura(32578))
return false;
player->SendEquipError(EQUIP_ERR_CLIENT_LOCKED_OUT, item, nullptr);
return true;
}
};
/*#####
# item_incendiary_explosives
#####*/
class item_incendiary_explosives : public ItemScript
{
public:
item_incendiary_explosives() : ItemScript("item_incendiary_explosives") { }
bool OnUse(Player* player, Item* item, SpellCastTargets const & /*targets*/) override
{
if (player->FindNearestCreature(26248, 15) || player->FindNearestCreature(26249, 15))
return false;
else
{
player->SendEquipError(EQUIP_ERR_OUT_OF_RANGE, item, nullptr);
return true;
}
}
};
/*#####
# item_mysterious_egg
#####*/
class item_mysterious_egg : public ItemScript
{
public:
item_mysterious_egg() : ItemScript("item_mysterious_egg") { }
bool OnExpire(Player* player, ItemTemplate const* /*pItemProto*/) override
{
ItemPosCountVec dest;
uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, 39883, 1); // Cracked Egg
if (msg == EQUIP_ERR_OK)
player->StoreNewItem(dest, 39883, true, GenerateItemRandomPropertyId(39883));
return true;
}
};
/*#####
# item_disgusting_jar
#####*/
class item_disgusting_jar : public ItemScript
{
public:
item_disgusting_jar() : ItemScript("item_disgusting_jar") { }
bool OnExpire(Player* player, ItemTemplate const* /*pItemProto*/) override
{
ItemPosCountVec dest;
uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, 44718, 1); // Ripe Disgusting Jar
if (msg == EQUIP_ERR_OK)
player->StoreNewItem(dest, 44718, true, GenerateItemRandomPropertyId(44718));
return true;
}
};
/*#####
# item_pile_fake_furs
#####*/
enum PileFakeFur
{
GO_CARIBOU_TRAP_1 = 187982,
GO_CARIBOU_TRAP_2 = 187995,
GO_CARIBOU_TRAP_3 = 187996,
GO_CARIBOU_TRAP_4 = 187997,
GO_CARIBOU_TRAP_5 = 187998,
GO_CARIBOU_TRAP_6 = 187999,
GO_CARIBOU_TRAP_7 = 188000,
GO_CARIBOU_TRAP_8 = 188001,
GO_CARIBOU_TRAP_9 = 188002,
GO_CARIBOU_TRAP_10 = 188003,
GO_CARIBOU_TRAP_11 = 188004,
GO_CARIBOU_TRAP_12 = 188005,
GO_CARIBOU_TRAP_13 = 188006,
GO_CARIBOU_TRAP_14 = 188007,
GO_CARIBOU_TRAP_15 = 188008,
GO_HIGH_QUALITY_FUR = 187983,
NPC_NESINGWARY_TRAPPER = 25835
};
#define CaribouTrapsNum 15
const uint32 CaribouTraps[CaribouTrapsNum] =
{
GO_CARIBOU_TRAP_1, GO_CARIBOU_TRAP_2, GO_CARIBOU_TRAP_3, GO_CARIBOU_TRAP_4, GO_CARIBOU_TRAP_5,
GO_CARIBOU_TRAP_6, GO_CARIBOU_TRAP_7, GO_CARIBOU_TRAP_8, GO_CARIBOU_TRAP_9, GO_CARIBOU_TRAP_10,
GO_CARIBOU_TRAP_11, GO_CARIBOU_TRAP_12, GO_CARIBOU_TRAP_13, GO_CARIBOU_TRAP_14, GO_CARIBOU_TRAP_15,
};
class item_pile_fake_furs : public ItemScript
{
public:
item_pile_fake_furs() : ItemScript("item_pile_fake_furs") { }
bool OnUse(Player* player, Item* /*item*/, SpellCastTargets const & /*targets*/) override
{
GameObject* go = nullptr;
for (uint8 i = 0; i < CaribouTrapsNum; ++i)
{
go = player->FindNearestGameObject(CaribouTraps[i], 5.0f);
if (go)
break;
}
if (!go)
return false;
if (go->FindNearestCreature(NPC_NESINGWARY_TRAPPER, 10.0f, true) || go->FindNearestCreature(NPC_NESINGWARY_TRAPPER, 10.0f, false) || go->FindNearestGameObject(GO_HIGH_QUALITY_FUR, 2.0f))
return true;
float x, y, z;
go->GetClosePoint(x, y, z, go->GetCombatReach() / 3, 7.0f);
go->SummonGameObject(GO_HIGH_QUALITY_FUR, *go, QuaternionData(), 1);
if (TempSummon* summon = player->SummonCreature(NPC_NESINGWARY_TRAPPER, x, y, z, go->GetOrientation(), TEMPSUMMON_DEAD_DESPAWN, 1000))
{
summon->SetVisible(false);
summon->SetReactState(REACT_PASSIVE);
summon->SetImmuneToPC(true);
}
return false;
}
};
/*#####
# item_petrov_cluster_bombs
#####*/
enum PetrovClusterBombs
{
SPELL_PETROV_BOMB = 42406,
AREA_ID_SHATTERED_STRAITS = 4064,
ZONE_ID_HOWLING = 495
};
class item_petrov_cluster_bombs : public ItemScript
{
public:
item_petrov_cluster_bombs() : ItemScript("item_petrov_cluster_bombs") { }
bool OnUse(Player* player, Item* /*item*/, const SpellCastTargets & /*targets*/) override
{
if (player->GetZoneId() != ZONE_ID_HOWLING)
return false;
if (!player->GetTransport() || player->GetAreaId() != AREA_ID_SHATTERED_STRAITS)
{
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_PETROV_BOMB))
Spell::SendCastResult(player, spellInfo, 1, SPELL_FAILED_NOT_HERE);
return true;
}
return false;
}
};
/*######
# item_dehta_trap_smasher
# For quest 11876, Help Those That Cannot Help Themselves
######*/
enum HelpThemselves
{
QUEST_CANNOT_HELP_THEMSELVES = 11876,
NPC_TRAPPED_MAMMOTH_CALF = 25850,
GO_MAMMOTH_TRAP_1 = 188022,
GO_MAMMOTH_TRAP_2 = 188024,
GO_MAMMOTH_TRAP_3 = 188025,
GO_MAMMOTH_TRAP_4 = 188026,
GO_MAMMOTH_TRAP_5 = 188027,
GO_MAMMOTH_TRAP_6 = 188028,
GO_MAMMOTH_TRAP_7 = 188029,
GO_MAMMOTH_TRAP_8 = 188030,
GO_MAMMOTH_TRAP_9 = 188031,
GO_MAMMOTH_TRAP_10 = 188032,
GO_MAMMOTH_TRAP_11 = 188033,
GO_MAMMOTH_TRAP_12 = 188034,
GO_MAMMOTH_TRAP_13 = 188035,
GO_MAMMOTH_TRAP_14 = 188036,
GO_MAMMOTH_TRAP_15 = 188037,
GO_MAMMOTH_TRAP_16 = 188038,
GO_MAMMOTH_TRAP_17 = 188039,
GO_MAMMOTH_TRAP_18 = 188040,
GO_MAMMOTH_TRAP_19 = 188041,
GO_MAMMOTH_TRAP_20 = 188042,
GO_MAMMOTH_TRAP_21 = 188043,
GO_MAMMOTH_TRAP_22 = 188044,
};
#define MammothTrapsNum 22
const uint32 MammothTraps[MammothTrapsNum] =
{
GO_MAMMOTH_TRAP_1, GO_MAMMOTH_TRAP_2, GO_MAMMOTH_TRAP_3, GO_MAMMOTH_TRAP_4, GO_MAMMOTH_TRAP_5,
GO_MAMMOTH_TRAP_6, GO_MAMMOTH_TRAP_7, GO_MAMMOTH_TRAP_8, GO_MAMMOTH_TRAP_9, GO_MAMMOTH_TRAP_10,
GO_MAMMOTH_TRAP_11, GO_MAMMOTH_TRAP_12, GO_MAMMOTH_TRAP_13, GO_MAMMOTH_TRAP_14, GO_MAMMOTH_TRAP_15,
GO_MAMMOTH_TRAP_16, GO_MAMMOTH_TRAP_17, GO_MAMMOTH_TRAP_18, GO_MAMMOTH_TRAP_19, GO_MAMMOTH_TRAP_20,
GO_MAMMOTH_TRAP_21, GO_MAMMOTH_TRAP_22
};
class item_dehta_trap_smasher : public ItemScript
{
public:
item_dehta_trap_smasher() : ItemScript("item_dehta_trap_smasher") { }
bool OnUse(Player* player, Item* /*item*/, const SpellCastTargets & /*targets*/) override
{
if (player->GetQuestStatus(QUEST_CANNOT_HELP_THEMSELVES) != QUEST_STATUS_INCOMPLETE)
return false;
Creature* pMammoth = player->FindNearestCreature(NPC_TRAPPED_MAMMOTH_CALF, 5.0f);
if (!pMammoth)
return false;
GameObject* pTrap = nullptr;
for (uint8 i = 0; i < MammothTrapsNum; ++i)
{
pTrap = player->FindNearestGameObject(MammothTraps[i], 11.0f);
if (pTrap)
{
pMammoth->AI()->DoAction(1);
pTrap->SetGoState(GO_STATE_READY);
player->KilledMonsterCredit(NPC_TRAPPED_MAMMOTH_CALF);
return true;
}
}
return false;
}
};
enum CapturedFrog
{
QUEST_THE_PERFECT_SPIES = 25444,
NPC_VANIRAS_SENTRY_TOTEM = 40187
};
class item_captured_frog : public ItemScript
{
public:
item_captured_frog() : ItemScript("item_captured_frog") { }
bool OnUse(Player* player, Item* item, SpellCastTargets const& /*targets*/) override
{
if (player->GetQuestStatus(QUEST_THE_PERFECT_SPIES) == QUEST_STATUS_INCOMPLETE)
{
if (player->FindNearestCreature(NPC_VANIRAS_SENTRY_TOTEM, 10.0f))
return false;
else
player->SendEquipError(EQUIP_ERR_OUT_OF_RANGE, item, nullptr);
}
else
player->SendEquipError(EQUIP_ERR_CLIENT_LOCKED_OUT, item, nullptr);
return true;
}
};
// Only used currently for
// 19169: Nightfall
class item_generic_limit_chance_above_60 : public ItemScript
{
public:
item_generic_limit_chance_above_60() : ItemScript("item_generic_limit_chance_above_60") { }
bool OnCastItemCombatSpell(Player* /*player*/, Unit* victim, SpellInfo const* /*spellInfo*/, Item* /*item*/) override
{
// spell proc chance gets severely reduced on victims > 60 (formula unknown)
if (victim->getLevel() > 60)
{
// gives ~0.1% proc chance at lvl 70
float const lvlPenaltyFactor = 9.93f;
float const failureChance = (victim->getLevel() - 60) * lvlPenaltyFactor;
// base ppm chance was already rolled, only roll success chance
return !roll_chance_f(failureChance);
}
return true;
}
};
/*#####
# item_horde_adventurer_horn
#####*/
class item_horde_adventurer_horn : public ItemScript
{
public:
item_horde_adventurer_horn() : ItemScript("item_horde_adventurer_horn") { }
bool OnUse(Player* player, Item* item, SpellCastTargets const& /*targets*/) override
{
TC_LOG_INFO(
"scripts",
"[VianoHorn] OnUse player='%s' item=%u",
player->GetName().c_str(),
item->GetEntry());
// Horns may not be used during combat.
if (player->IsInCombat())
{
TC_LOG_INFO("scripts",
"[VianoHorn] rejected: player is in combat");
player->SendEquipError(
EQUIP_ERR_CLIENT_LOCKED_OUT,
item,
nullptr);
return true;
}
// Horns may only be used inside dungeon instances.
if (!player->GetMap()->IsDungeon())
{
TC_LOG_INFO("scripts",
"[VianoHorn] rejected: player is not inside a dungeon");
player->SendEquipError(
EQUIP_ERR_CLIENT_LOCKED_OUT,
item,
nullptr);
return true;
}
TC_LOG_INFO(
"scripts",
"[VianoHorn] prechecks passed; allowing normal item spell processing");
// IMPORTANT:
// false means the ItemScript does not consume/suppress the normal item use.
// The item's SpellID1 (120000-120003) may now run through the regular
// Trinity spell lifecycle.
return false;
}
};
}// namespace ItemScripts
void AddSC_item_scripts()
{
using namespace ItemScripts;
new item_only_for_flight();
new item_nether_wraith_beacon();
new item_gor_dreks_ointment();
new item_incendiary_explosives();
new item_mysterious_egg();
new item_disgusting_jar();
new item_pile_fake_furs();
new item_petrov_cluster_bombs();
new item_dehta_trap_smasher();
new item_captured_frog();
new item_generic_limit_chance_above_60();
new item_horde_adventurer_horn();
}