Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6a1f8eec89 | |||
| 0bb6b0ef84 | |||
| 295cb6df52 | |||
| fbd6ea47f2 | |||
| a64279ed7e | |||
| 87219cb4eb |
@@ -0,0 +1,10 @@
|
|||||||
|
-- DB update 2026_05_03_00 -> 2026_05_12_00
|
||||||
|
-- RBAC permission for .learn all mounts (Admin 196, Gamemaster 197).
|
||||||
|
DELETE FROM `rbac_permissions` WHERE `id` = 916;
|
||||||
|
INSERT INTO `rbac_permissions` (`id`, `name`) VALUES
|
||||||
|
(916, 'Command: learn all mounts');
|
||||||
|
|
||||||
|
DELETE FROM `rbac_linked_permissions` WHERE `linkedId` = 916;
|
||||||
|
INSERT INTO `rbac_linked_permissions` (`id`, `linkedId`) VALUES
|
||||||
|
(196, 916),
|
||||||
|
(197, 916);
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
-- Fractured / Paragon: multidot Devouring Plague clone (spell IDs 951000-951008).
|
||||||
|
-- Spell rows live in the patched client Spell.dbc (see fractured-tooling
|
||||||
|
-- from-workspace-root/_patch_spell_dbc_paragon_multidot_devouring_plague.py).
|
||||||
|
-- Deploy the same Spell.dbc into the worldserver `data/dbc/` folder OR import
|
||||||
|
-- equivalent `spell_dbc` rows from a full exporter; stock SQL cannot express
|
||||||
|
-- the SpellEntryfmt NA padding columns safely in one INSERT here.
|
||||||
|
|
||||||
|
DELETE FROM `spell_ranks` WHERE `first_spell_id` = 951000;
|
||||||
|
INSERT INTO `spell_ranks` (`first_spell_id`,`spell_id`,`rank`) VALUES
|
||||||
|
(951000,951000,1),
|
||||||
|
(951000,951001,2),
|
||||||
|
(951000,951002,3),
|
||||||
|
(951000,951003,4),
|
||||||
|
(951000,951004,5),
|
||||||
|
(951000,951005,6),
|
||||||
|
(951000,951006,7),
|
||||||
|
(951000,951007,8),
|
||||||
|
(951000,951008,9);
|
||||||
|
|
||||||
|
DELETE FROM `paragon_spell_ae_cost` WHERE `spell_id` IN (2944,951000);
|
||||||
|
INSERT INTO `paragon_spell_ae_cost` (`spell_id`,`ae_cost`) VALUES (951000, 1);
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
-- Fractured / Paragon: spellbook tab for multidot Devouring Plague (951000 chain).
|
||||||
|
-- Shadow priest skill line (78); ClassMask 2064 matches mod-paragon SLA overlay.
|
||||||
|
-- Client: patched SkillLineAbility.dbc in patch-enUS-4 from the same script.
|
||||||
|
|
||||||
|
DELETE FROM `skilllineability_dbc` WHERE `ID` IN (1951000, 1951001, 1951002, 1951003, 1951004, 1951005, 1951006, 1951007, 1951008);
|
||||||
|
INSERT INTO `skilllineability_dbc` (`ID`,`SkillLine`,`Spell`,`RaceMask`,`ClassMask`,`ExcludeRace`,`ExcludeClass`,`MinSkillLineRank`,`SupercededBySpell`,`AcquireMethod`,`TrivialSkillLineRankHigh`,`TrivialSkillLineRankLow`,`CharacterPoints_1`,`CharacterPoints_2`) VALUES
|
||||||
|
(1951000,78,951000,0,2064,0,0,1,0,0,0,0,0,0),
|
||||||
|
(1951001,78,951001,0,2064,0,0,1,0,0,0,0,0,0),
|
||||||
|
(1951002,78,951002,0,2064,0,0,1,0,0,0,0,0,0),
|
||||||
|
(1951003,78,951003,0,2064,0,0,1,0,0,0,0,0,0),
|
||||||
|
(1951004,78,951004,0,2064,0,0,1,0,0,0,0,0,0),
|
||||||
|
(1951005,78,951005,0,2064,0,0,1,0,0,0,0,0,0),
|
||||||
|
(1951006,78,951006,0,2064,0,0,1,0,0,0,0,0,0),
|
||||||
|
(1951007,78,951007,0,2064,0,0,1,0,0,0,0,0,0),
|
||||||
|
(1951008,78,951008,0,2064,0,0,1,0,0,0,0,0,0);
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
-- Fractured / Paragon: Character Advancement stance/presence clones (951010-951015).
|
||||||
|
-- Client: patched Spell.dbc + SpellShapeshiftForm.dbc + SkillLineAbility.dbc in patch-enUS-4.MPQ.
|
||||||
|
-- Server: copy Spell.dbc + SpellShapeshiftForm.dbc into `data/dbc/` (SpellShapeshiftForm is not in stock MPQ); SkillLineAbility is DB-driven on server.
|
||||||
|
|
||||||
|
DELETE FROM `paragon_spell_ae_cost` WHERE `spell_id` IN (951010,951011,951012,951013,951014,951015);
|
||||||
|
INSERT INTO `paragon_spell_ae_cost` (`spell_id`,`ae_cost`) VALUES
|
||||||
|
(951010, 1),
|
||||||
|
(951011, 1),
|
||||||
|
(951012, 1),
|
||||||
|
(951013, 1),
|
||||||
|
(951014, 1),
|
||||||
|
(951015, 1);
|
||||||
|
|
||||||
|
DELETE FROM `skilllineability_dbc` WHERE `ID` IN (1951020,1951021,1951022,1951023,1951024,1951025);
|
||||||
|
INSERT INTO `skilllineability_dbc` (`ID`,`SkillLine`,`Spell`,`RaceMask`,`ClassMask`,`ExcludeRace`,`ExcludeClass`,`MinSkillLineRank`,`SupercededBySpell`,`AcquireMethod`,`TrivialSkillLineRankHigh`,`TrivialSkillLineRankLow`,`CharacterPoints_1`,`CharacterPoints_2`) VALUES
|
||||||
|
(1951020,26,951010,0,2049,0,0,1,0,2,0,0,0,0),
|
||||||
|
(1951021,257,951011,0,2049,0,0,1,0,0,0,0,0,0),
|
||||||
|
(1951022,256,951012,0,2049,0,0,1,0,0,0,0,0,0),
|
||||||
|
(1951023,770,951013,0,2080,0,0,1,0,2,0,0,0,0),
|
||||||
|
(1951024,771,951014,0,2080,0,0,1,0,0,0,0,0,0),
|
||||||
|
(1951025,772,951015,0,2080,0,0,1,0,0,0,0,0,0);
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
-- Fractured / Paragon: run spell_dk_presence on Character Advancement DK presence clones (951013-951015).
|
||||||
|
-- Spell.dbc sets SpellFamilyName=0 on these rows (see fractured-tooling/_patch_spell_dbc_paragon_stance_presence_clones.py)
|
||||||
|
-- so the stock client does not map them onto DK stance buttons; core still needs the aura script for Improved Presence.
|
||||||
|
|
||||||
|
DELETE FROM `spell_script_names` WHERE `spell_id` IN (951013, 951014, 951015);
|
||||||
|
INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES
|
||||||
|
(951013, 'spell_dk_presence'),
|
||||||
|
(951014, 'spell_dk_presence'),
|
||||||
|
(951015, 'spell_dk_presence');
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
-- Fractured / Paragon: Character Advancement stance/presence clones — spellbook + client bits.
|
||||||
|
-- 1) SkillLineAbility: DK presence clones belong on 770/771/772 (Blood/Frost/Unholy tabs), not 760 (General).
|
||||||
|
-- (760 was an experiment; stance bar visibility is driven by Spell.dbc AttributesEx2 USE_SHAPESHIFT_BAR.)
|
||||||
|
-- 2) Idempotent if rows already match.
|
||||||
|
|
||||||
|
UPDATE `skilllineability_dbc` SET `SkillLine` = 770 WHERE `ID` = 1951023 AND `Spell` = 951013;
|
||||||
|
UPDATE `skilllineability_dbc` SET `SkillLine` = 771 WHERE `ID` = 1951024 AND `Spell` = 951014;
|
||||||
|
UPDATE `skilllineability_dbc` SET `SkillLine` = 772 WHERE `ID` = 1951025 AND `Spell` = 951015;
|
||||||
@@ -8,9 +8,11 @@
|
|||||||
* separate (see README).
|
* separate (see README).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "Bag.h"
|
||||||
#include "CharacterDatabase.h"
|
#include "CharacterDatabase.h"
|
||||||
#include "Chat.h"
|
#include "Chat.h"
|
||||||
#include "CommandScript.h"
|
#include "CommandScript.h"
|
||||||
|
#include "Language.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "Pet.h"
|
#include "Pet.h"
|
||||||
#include "Player.h"
|
#include "Player.h"
|
||||||
@@ -23,6 +25,7 @@
|
|||||||
#include "SpellMgr.h"
|
#include "SpellMgr.h"
|
||||||
#include "WorldDatabase.h"
|
#include "WorldDatabase.h"
|
||||||
#include "WorldPacket.h"
|
#include "WorldPacket.h"
|
||||||
|
#include "ObjectMgr.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "DBCEnums.h"
|
#include "DBCEnums.h"
|
||||||
#include "DBCStores.h"
|
#include "DBCStores.h"
|
||||||
@@ -32,6 +35,7 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
@@ -4592,6 +4596,487 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// --- Paragon tester inventory helpers (ICC 25H-style curated lists; edit ids here for your fork) ---
|
||||||
|
// Paragon can equip any armor weight; BiS picks below intentionally mix plate/mail/leather/cloth per slot.
|
||||||
|
|
||||||
|
uint32 ParagonTesterSelectLargestUsableBagItemId(Player const* player)
|
||||||
|
{
|
||||||
|
static constexpr uint32 candidates[] = { 51809, 41600, 41599, 38082 };
|
||||||
|
for (uint32 id : candidates)
|
||||||
|
{
|
||||||
|
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(id);
|
||||||
|
if (!proto || proto->Class != ITEM_CLASS_CONTAINER || proto->InventoryType != INVTYPE_BAG)
|
||||||
|
continue;
|
||||||
|
if (player->CanUseItem(proto) == EQUIP_ERR_OK)
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 ParagonTesterGrantItemList(Player* target, uint32 const* ids, size_t count, ChatHandler* handler)
|
||||||
|
{
|
||||||
|
uint32 granted = 0;
|
||||||
|
for (size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
uint32 const itemId = ids[i];
|
||||||
|
if (!itemId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
|
||||||
|
if (!proto)
|
||||||
|
{
|
||||||
|
handler->PSendSysMessage("Paragon tester kit: item {} is not defined (skipped).", itemId);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemPosCountVec dest;
|
||||||
|
InventoryResult const msg = target->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, 1);
|
||||||
|
if (msg != EQUIP_ERR_OK || dest.empty())
|
||||||
|
{
|
||||||
|
handler->PSendSysMessage("Paragon tester kit: cannot store {} ({}) — free bag space?", itemId, uint32(msg));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Item* item = target->StoreNewItem(dest, itemId, true))
|
||||||
|
{
|
||||||
|
item->SetBinding(false);
|
||||||
|
++granted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return granted;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ParagonTesterStackedGrant
|
||||||
|
{
|
||||||
|
uint32 itemId;
|
||||||
|
uint32 count;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Grants stackable/consumable items in one StoreNewItem per line (gems, scrolls, scopes, etc.).
|
||||||
|
uint32 ParagonTesterGrantStackedItemList(Player* target, ParagonTesterStackedGrant const* grants, size_t grantCount, ChatHandler* handler)
|
||||||
|
{
|
||||||
|
uint32 totalPieces = 0;
|
||||||
|
for (size_t i = 0; i < grantCount; ++i)
|
||||||
|
{
|
||||||
|
uint32 const itemId = grants[i].itemId;
|
||||||
|
uint32 count = grants[i].count;
|
||||||
|
if (!itemId || !count)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
|
||||||
|
if (!proto)
|
||||||
|
{
|
||||||
|
handler->PSendSysMessage("Paragon tester kit: item {} is not defined (skipped).", itemId);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 noSpaceForCount = 0;
|
||||||
|
ItemPosCountVec dest;
|
||||||
|
InventoryResult const msg = target->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, count, &noSpaceForCount);
|
||||||
|
if (msg != EQUIP_ERR_OK)
|
||||||
|
count -= noSpaceForCount;
|
||||||
|
|
||||||
|
if (!count || dest.empty())
|
||||||
|
{
|
||||||
|
handler->PSendSysMessage("Paragon tester kit: cannot store {} x{} ({}) — free bag space?", itemId, grants[i].count, uint32(msg));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Item* item = target->StoreNewItem(dest, itemId, true))
|
||||||
|
{
|
||||||
|
item->SetBinding(false);
|
||||||
|
totalPieces += count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return totalPieces;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParagonTesterClearNonEquipmentInventory(Player* player)
|
||||||
|
{
|
||||||
|
for (uint8 bagSlot = INVENTORY_SLOT_BAG_START; bagSlot < INVENTORY_SLOT_BAG_END; ++bagSlot)
|
||||||
|
{
|
||||||
|
if (Bag* bag = player->GetBagByPos(bagSlot))
|
||||||
|
{
|
||||||
|
for (uint32 j = 0; j < bag->GetBagSize(); ++j)
|
||||||
|
{
|
||||||
|
if (bag->GetItemByPos(j))
|
||||||
|
player->DestroyItem(bagSlot, j, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player->GetItemByPos(INVENTORY_SLOT_BAG_0, bagSlot))
|
||||||
|
player->DestroyItem(INVENTORY_SLOT_BAG_0, bagSlot, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; ++slot)
|
||||||
|
{
|
||||||
|
if (player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
|
||||||
|
player->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParagonTesterStringToLowerAscii(std::string& s)
|
||||||
|
{
|
||||||
|
for (char& c : s)
|
||||||
|
c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ParagonTesterNormalizeWeaponTypeKey(std::string_view raw)
|
||||||
|
{
|
||||||
|
std::string t;
|
||||||
|
for (unsigned char ch : raw)
|
||||||
|
{
|
||||||
|
if (ch == ' ' || ch == '\t' || ch == '-' || ch == '_' || ch == '/')
|
||||||
|
continue;
|
||||||
|
t.push_back(static_cast<char>(std::tolower(ch)));
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If any bag slot 19–22 is free and the item is a container, move it from inventory onto that slot (Player.cpp pattern).
|
||||||
|
bool ParagonTesterTryEquipBagToFirstEmptySlot(Player* player, Item* bag)
|
||||||
|
{
|
||||||
|
if (!player || !bag)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ItemTemplate const* proto = bag->GetTemplate();
|
||||||
|
if (!proto || proto->Class != ITEM_CLASS_CONTAINER || proto->InventoryType != INVTYPE_BAG)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint16 eDest = 0;
|
||||||
|
if (player->CanEquipItem(NULL_SLOT, eDest, bag, false) != EQUIP_ERR_OK)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint8 const srcBag = bag->GetBagSlot();
|
||||||
|
uint8 const srcSlot = bag->GetSlot();
|
||||||
|
player->RemoveItem(srcBag, srcSlot, true);
|
||||||
|
player->EquipItem(eDest, bag, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Curated ICC-era ids (db_world item_template). Extend as needed for your fork.
|
||||||
|
bool ParagonTesterResolveWeaponKit(std::string statRaw, std::string typeRaw, std::vector<uint32>& out, std::string& err)
|
||||||
|
{
|
||||||
|
out.clear();
|
||||||
|
ParagonTesterStringToLowerAscii(statRaw);
|
||||||
|
// trim stat
|
||||||
|
while (!statRaw.empty() && statRaw.front() == ' ')
|
||||||
|
statRaw.erase(statRaw.begin());
|
||||||
|
while (!statRaw.empty() && statRaw.back() == ' ')
|
||||||
|
statRaw.pop_back();
|
||||||
|
|
||||||
|
std::string stat = statRaw;
|
||||||
|
if (stat == "strength")
|
||||||
|
stat = "str";
|
||||||
|
else if (stat == "agility" || stat == "dex" || stat == "dexterity")
|
||||||
|
stat = "agi";
|
||||||
|
else if (stat == "intellect")
|
||||||
|
stat = "int";
|
||||||
|
else if (stat == "spirit")
|
||||||
|
stat = "spi";
|
||||||
|
else if (stat == "apsp" || stat == "spellstrike")
|
||||||
|
stat = "hybrid";
|
||||||
|
|
||||||
|
std::string const wkey = ParagonTesterNormalizeWeaponTypeKey(typeRaw);
|
||||||
|
if (stat.empty() || wkey.empty())
|
||||||
|
{
|
||||||
|
err = "usage: .paragon tester weapons <stat> <type> — see `.paragon tester weapons` with no args for help.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto push = [&](std::initializer_list<uint32> ids)
|
||||||
|
{
|
||||||
|
for (uint32 id : ids)
|
||||||
|
if (id)
|
||||||
|
out.push_back(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (stat == "str")
|
||||||
|
{
|
||||||
|
// "2h sword", "2h/sword", "2h axe" → 2hsword / 2haxe after normalize (slashes stripped like spaces).
|
||||||
|
if (wkey == "2hsword" || wkey == "twohandsword")
|
||||||
|
push({ 50730 }); // Glorenzelg (2H sword)
|
||||||
|
else if (wkey == "2haxe" || wkey == "twohandaxe")
|
||||||
|
push({ 50709 }); // Bryntroll (2H axe)
|
||||||
|
else if (wkey == "2hmace" || wkey == "twohandmace")
|
||||||
|
push({ 50603 }); // Cryptmaker (2H mace)
|
||||||
|
else if (wkey == "1hsword" || wkey == "onehandsword")
|
||||||
|
push({ 50737 }); // Havoc's Call (1H sword)
|
||||||
|
else if (wkey == "1haxe" || wkey == "onehandaxe")
|
||||||
|
push({ 50654 }); // Scourgeborne Waraxe (1H axe)
|
||||||
|
else if (wkey == "1hmace" || wkey == "onehandmace" || wkey == "1hhammer")
|
||||||
|
push({ 50738 }); // Mithrios (1H mace)
|
||||||
|
else if (wkey == "2h" || wkey == "twohand" || wkey == "zwei" || wkey == "great" || wkey == "polearm")
|
||||||
|
push({ 50730 });
|
||||||
|
else if (wkey == "dual" || wkey == "dw" || wkey == "dualwield" || wkey == "dualwielding")
|
||||||
|
push({ 50738, 50737 });
|
||||||
|
else if (wkey == "sword" || wkey == "swords" || wkey == "1h")
|
||||||
|
push({ 50737 });
|
||||||
|
else if (wkey == "mace" || wkey == "hammer")
|
||||||
|
push({ 50738 });
|
||||||
|
else if (wkey == "axe")
|
||||||
|
push({ 50654 });
|
||||||
|
else if (wkey == "ranged" || wkey == "bow" || wkey == "gun" || wkey == "crossbow" || wkey == "thrown")
|
||||||
|
push({ 50733 });
|
||||||
|
else
|
||||||
|
{
|
||||||
|
err = fmt::format(
|
||||||
|
"unknown STR weapon type \"{}\" (try 2h sword, 2h axe, 2h mace, 1h sword, 1h axe, 1h mace, 2h, dual, sword, mace, axe, ranged).",
|
||||||
|
typeRaw);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stat == "agi")
|
||||||
|
{
|
||||||
|
if (wkey == "2h" || wkey == "twohand" || wkey == "polearm")
|
||||||
|
push({ 50735 });
|
||||||
|
else if (wkey == "dual" || wkey == "dw" || wkey == "dualwield" || wkey == "daggers")
|
||||||
|
push({ 50736, 50676 });
|
||||||
|
else if (wkey == "dagger")
|
||||||
|
push({ 50736 });
|
||||||
|
else if (wkey == "sword" || wkey == "swords" || wkey == "1h")
|
||||||
|
push({ 50672 });
|
||||||
|
else if (wkey == "fist" || wkey == "fistweapon" || wkey == "claw")
|
||||||
|
push({ 50676 });
|
||||||
|
else if (wkey == "staff" || wkey == "staves")
|
||||||
|
push({ 50731 }); // caster staff; use as generic high-ilvl staff for testers
|
||||||
|
else if (wkey == "bow")
|
||||||
|
push({ 51940 }); // Windrunner's Heartseeker (hunter-style bow)
|
||||||
|
else if (wkey == "ranged" || wkey == "gun" || wkey == "crossbow" || wkey == "thrown")
|
||||||
|
push({ 50733 }); // Fal'inrush (BiS gun)
|
||||||
|
else
|
||||||
|
{
|
||||||
|
err = fmt::format("unknown AGI weapon type \"{}\" (try 2h, dual, dagger, sword, fist, staff, bow, ranged).", typeRaw);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stat == "int")
|
||||||
|
{
|
||||||
|
if (wkey == "staff" || wkey == "staves")
|
||||||
|
push({ 50731 });
|
||||||
|
else if (wkey == "wand")
|
||||||
|
push({ 50684 });
|
||||||
|
else if (wkey == "mhoh" || wkey == "ohmh" || wkey == "dual" || wkey == "dw" || wkey == "moh")
|
||||||
|
push({ 50732, 50734 });
|
||||||
|
else if (wkey == "mh" || wkey == "mainhand" || wkey == "sword" || wkey == "mace" || wkey == "dagger")
|
||||||
|
push({ 50732 });
|
||||||
|
else if (wkey == "oh" || wkey == "offhand")
|
||||||
|
push({ 50734 });
|
||||||
|
else if (wkey == "shield")
|
||||||
|
push({ 50729 });
|
||||||
|
else if (wkey == "ranged" || wkey == "bow" || wkey == "gun" || wkey == "crossbow")
|
||||||
|
push({ 50733 });
|
||||||
|
else
|
||||||
|
{
|
||||||
|
err = fmt::format("unknown INT weapon type \"{}\" (try staff, wand, mhoh, mh, oh, shield, ranged).", typeRaw);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stat == "spi")
|
||||||
|
{
|
||||||
|
if (wkey == "staff" || wkey == "staves")
|
||||||
|
push({ 50725 });
|
||||||
|
else if (wkey == "wand")
|
||||||
|
push({ 50684 });
|
||||||
|
else if (wkey == "mace" || wkey == "mh")
|
||||||
|
push({ 50732 });
|
||||||
|
else if (wkey == "mhoh" || wkey == "ohmh")
|
||||||
|
push({ 50732, 50734 });
|
||||||
|
else if (wkey == "shield")
|
||||||
|
push({ 50729 });
|
||||||
|
else if (wkey == "ranged" || wkey == "bow" || wkey == "gun" || wkey == "crossbow" || wkey == "thrown")
|
||||||
|
push({ 50733 });
|
||||||
|
else
|
||||||
|
{
|
||||||
|
err = fmt::format("unknown SPI weapon type \"{}\" (try staff, wand, mace, mhoh, shield, ranged).", typeRaw);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stat == "tank")
|
||||||
|
{
|
||||||
|
if (wkey == "shield")
|
||||||
|
push({ 50729 });
|
||||||
|
else if (wkey == "sword" || wkey == "swords" || wkey == "1h")
|
||||||
|
push({ 50738 });
|
||||||
|
else if (wkey == "mace" || wkey == "hammer")
|
||||||
|
push({ 50738 });
|
||||||
|
else if (wkey == "swordboard" || wkey == "sb" || wkey == "mit" || wkey == "1hshield" || wkey == "threat")
|
||||||
|
push({ 50738, 50729 });
|
||||||
|
else if (wkey == "dual" || wkey == "dw")
|
||||||
|
push({ 50738, 50737 });
|
||||||
|
else if (wkey == "2h" || wkey == "twohand")
|
||||||
|
push({ 50730 });
|
||||||
|
else if (wkey == "ranged" || wkey == "bow" || wkey == "gun" || wkey == "crossbow" || wkey == "thrown")
|
||||||
|
push({ 50733 });
|
||||||
|
else if (wkey == "sigil" || wkey == "relic")
|
||||||
|
push({ 50462 });
|
||||||
|
else
|
||||||
|
{
|
||||||
|
err = fmt::format("unknown tank weapon type \"{}\" (try swordboard, shield, sword, mace, dual, 2h, ranged, sigil).", typeRaw);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stat == "hybrid")
|
||||||
|
{
|
||||||
|
if (wkey == "staff")
|
||||||
|
push({ 50731 });
|
||||||
|
else if (wkey == "wand")
|
||||||
|
push({ 50684 });
|
||||||
|
else if (wkey == "shield")
|
||||||
|
push({ 50729 });
|
||||||
|
else if (wkey == "2h" || wkey == "twohand")
|
||||||
|
push({ 50735 });
|
||||||
|
else if (wkey == "dual" || wkey == "dw" || wkey == "mhoh" || wkey == "default")
|
||||||
|
push({ 50732, 50734 });
|
||||||
|
else if (wkey == "ranged" || wkey == "bow" || wkey == "gun" || wkey == "crossbow" || wkey == "thrown")
|
||||||
|
push({ 50733 });
|
||||||
|
else
|
||||||
|
{
|
||||||
|
err = fmt::format("unknown hybrid weapon type \"{}\" (try mhoh, staff, wand, shield, 2h, ranged).", typeRaw);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = fmt::format("unknown stat \"{}\" (use str, agi, int, spi, tank, hybrid).", statRaw);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Order: head, shoulders, chest, hands, legs, bracers, belt, boots, neck, cloak, weapons…, trinkets, rings.
|
||||||
|
// Item ids verified against stock AC db_world item_template (3.3.5a).
|
||||||
|
static constexpr uint32 kTesterBisStr[] = {
|
||||||
|
50712, 51229, 50656, 50675, 50624, // helm, shoulders (51229), chest, hands, legs — not 51211 (that id is Ymirjar legs)
|
||||||
|
54580, 50620, 54578, 54581, 50653, // Umbrage + Coldwraith + Apocalypse's Advance + Penumbra + Shadowvault
|
||||||
|
50730, 50733,
|
||||||
|
50363, 54590,
|
||||||
|
50657, 54576,
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr uint32 kTesterBisAgi[] = {
|
||||||
|
51242, 51299, 51298, 51243, 51241, // Frost Witch (mail) + Lasherweave (leather) mix
|
||||||
|
50670, 50688, 50607, 50633, 50653,
|
||||||
|
50736, 50733,
|
||||||
|
50363, 54590,
|
||||||
|
50657, 54576,
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr uint32 kTesterBisInt[] = {
|
||||||
|
51281, 51245, 51283, 51280, 51246, // Bloodmage cloth + Frost Witch (mail) shoulders/legs
|
||||||
|
50686, 50702, 50699, 50724, 50628,
|
||||||
|
50732, 50734, 50684,
|
||||||
|
50346, 50360,
|
||||||
|
50610, 50664,
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr uint32 kTesterBisSpi[] = {
|
||||||
|
51237, 51257, 51239, 51256, 51258, // Resto Frost Witch (mail) + Crimson Acolyte (cloth)
|
||||||
|
50686, 50702, 50699, 50724, 50628,
|
||||||
|
50725,
|
||||||
|
50360, 50366,
|
||||||
|
50610, 50664,
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr uint32 kTesterBisTank[] = {
|
||||||
|
51306, 51309, 51305, 51307, 51308, // Sanctified Scourgelord (plate, DK tank profile)
|
||||||
|
50611, 50620, 50625, 50609, 50677,
|
||||||
|
50738, 50729, 50462,
|
||||||
|
50364, 54591,
|
||||||
|
50404, 50657,
|
||||||
|
};
|
||||||
|
|
||||||
|
// AP main-hand + SP off-hand + mail enhancer T10 (ICC); for hybrid battlemage-style testers.
|
||||||
|
static constexpr uint32 kTesterBisHybrid[] = {
|
||||||
|
51242, 51240, 51244, 51243, 51241,
|
||||||
|
54580, 50620, 54578, 54581, 50653,
|
||||||
|
50732, 50734,
|
||||||
|
50363, 50346,
|
||||||
|
50657, 50610,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sanctified Ahn'Kahar Blood Hunter (277) + ICC phys offsets; ranged slot only (Windrunner's Heartseeker).
|
||||||
|
static constexpr uint32 kTesterBisHunter[] = {
|
||||||
|
51286, 51288, 51289, 51285, 51287,
|
||||||
|
50670, 50688, 50607, 50633, 50653,
|
||||||
|
0, 51940,
|
||||||
|
50363, 54590,
|
||||||
|
50657, 54576,
|
||||||
|
};
|
||||||
|
|
||||||
|
// ICC-era gems (stacked), enchant scrolls, belt buckle, leg armor/spellthread, Sons of Hodir shoulders, Ebon Blade / Kirin Tor helms.
|
||||||
|
// Item ids from db_world item_template; tweak counts for your fork.
|
||||||
|
static constexpr uint32 kGemStack = 20;
|
||||||
|
static constexpr uint32 kGemStackMed = 12;
|
||||||
|
static constexpr uint32 kGemStackSmall = 8;
|
||||||
|
static constexpr uint32 kScrollPair = 2;
|
||||||
|
static constexpr uint32 kMetaCount = 3;
|
||||||
|
static constexpr uint32 kBeltBuckle = 4;
|
||||||
|
static constexpr uint32 kLegKit = 4;
|
||||||
|
static constexpr uint32 kAugmentPair = 2;
|
||||||
|
static constexpr uint32 kScopeKit = 4;
|
||||||
|
|
||||||
|
static constexpr ParagonTesterStackedGrant kTesterGemsStr[] = {
|
||||||
|
{ 40111, kGemStack }, { 40117, kGemStack }, { 40114, kGemStack }, { 40116, kGemStackMed }, { 40118, kGemStackMed },
|
||||||
|
{ 40119, kGemStackSmall }, { 40142, kGemStackMed }, { 40143, kGemStackMed }, { 40153, kGemStackMed }, { 40162, kGemStackMed },
|
||||||
|
{ 41285, kMetaCount }, { 41398, 2 },
|
||||||
|
{ 44493, kScrollPair }, { 44815, kScrollPair }, { 44465, kScrollPair }, { 39006, kScrollPair }, { 39003, kScrollPair },
|
||||||
|
{ 44458, kScrollPair }, { 41611, kBeltBuckle }, { 38374, kLegKit }, { 50335, kAugmentPair }, { 50367, kAugmentPair },
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr ParagonTesterStackedGrant kTesterGemsAgi[] = {
|
||||||
|
{ 40112, kGemStack }, { 40117, kGemStack }, { 40114, kGemStackMed }, { 40142, kGemStackMed }, { 40152, kGemStackMed },
|
||||||
|
{ 40153, kGemStackMed }, { 40155, kGemStackMed }, { 40125, kGemStackMed }, { 41398, kMetaCount }, { 41285, 2 },
|
||||||
|
{ 44493, kScrollPair }, { 44815, kScrollPair }, { 44465, kScrollPair }, { 39006, kScrollPair }, { 39003, kScrollPair },
|
||||||
|
{ 44458, kScrollPair }, { 38986, kScrollPair }, { 41611, kBeltBuckle }, { 38373, kLegKit }, { 50335, kAugmentPair }, { 50367, kAugmentPair },
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr ParagonTesterStackedGrant kTesterGemsInt[] = {
|
||||||
|
{ 40113, kGemStack }, { 40155, kGemStackMed }, { 40153, kGemStackMed }, { 40133, kGemStackMed }, { 40119, kGemStackSmall },
|
||||||
|
{ 40125, kGemStackMed }, { 41285, kMetaCount },
|
||||||
|
{ 44467, kScrollPair }, { 44470, kScrollPair }, { 38979, kScrollPair }, { 39003, kScrollPair }, { 39006, kScrollPair },
|
||||||
|
{ 44465, kScrollPair }, { 38973, kScrollPair }, { 41611, kBeltBuckle }, { 41602, kLegKit }, { 50338, kAugmentPair }, { 50368, kAugmentPair },
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr ParagonTesterStackedGrant kTesterGemsSpi[] = {
|
||||||
|
{ 40113, kGemStackMed }, { 40133, kGemStack }, { 40120, kGemStackMed }, { 40119, kGemStackSmall }, { 40155, kGemStackMed },
|
||||||
|
{ 41285, kMetaCount },
|
||||||
|
{ 44470, kScrollPair }, { 38853, kScrollPair }, { 38961, kScrollPair }, { 38979, kScrollPair }, { 39006, kScrollPair },
|
||||||
|
{ 44465, kScrollPair }, { 41611, kBeltBuckle }, { 41601, kLegKit }, { 50336, kAugmentPair }, { 50370, kAugmentPair },
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr ParagonTesterStackedGrant kTesterGemsTank[] = {
|
||||||
|
{ 40119, kGemStack }, { 40138, kGemStackMed }, { 40115, kGemStackMed }, { 40118, kGemStackMed }, { 40143, kGemStackMed },
|
||||||
|
{ 41285, 2 },
|
||||||
|
{ 38945, kScrollPair }, { 44489, kScrollPair }, { 38849, kScrollPair }, { 39006, kScrollPair }, { 44465, kScrollPair },
|
||||||
|
{ 41611, kBeltBuckle }, { 38373, kLegKit }, { 50337, kAugmentPair }, { 50369, kAugmentPair },
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr ParagonTesterStackedGrant kTesterGemsHybrid[] = {
|
||||||
|
{ 40113, kGemStackMed }, { 40111, kGemStackMed }, { 40114, kGemStackMed }, { 40153, kGemStackMed }, { 40142, kGemStackMed },
|
||||||
|
{ 40155, kGemStackMed }, { 41285, kMetaCount }, { 41398, 2 },
|
||||||
|
{ 44467, kScrollPair }, { 44493, kScrollPair }, { 44815, kScrollPair }, { 44470, kScrollPair }, { 44465, kScrollPair },
|
||||||
|
{ 39006, kScrollPair }, { 39003, kScrollPair }, { 41611, kBeltBuckle }, { 41602, 2 }, { 38373, 2 }, { 38374, 2 },
|
||||||
|
{ 50338, kAugmentPair }, { 50335, kAugmentPair }, { 50368, 1 }, { 50367, 1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hunter / physical ranged: scopes (engineering attach) + hit/agi gems + physical scrolls.
|
||||||
|
static constexpr ParagonTesterStackedGrant kTesterGemsRanged[] = {
|
||||||
|
{ 44739, kScopeKit }, { 41167, kScopeKit }, { 41146, 2 },
|
||||||
|
{ 40112, kGemStack }, { 40117, kGemStack }, { 40125, kGemStack }, { 40142, kGemStackMed }, { 40152, kGemStackMed },
|
||||||
|
{ 40153, kGemStackMed }, { 41398, kMetaCount }, { 41285, 2 },
|
||||||
|
{ 44493, kScrollPair }, { 44815, kScrollPair }, { 44465, kScrollPair }, { 39006, kScrollPair }, { 39003, kScrollPair },
|
||||||
|
{ 44458, kScrollPair }, { 38986, kScrollPair }, { 41611, kBeltBuckle }, { 38373, kLegKit }, { 50335, kAugmentPair }, { 50367, kAugmentPair },
|
||||||
|
};
|
||||||
|
|
||||||
class Paragon_Essence_CommandScript : public CommandScript
|
class Paragon_Essence_CommandScript : public CommandScript
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -4599,6 +5084,38 @@ public:
|
|||||||
|
|
||||||
ChatCommandTable GetCommands() const override
|
ChatCommandTable GetCommands() const override
|
||||||
{
|
{
|
||||||
|
static ChatCommandTable testerBisGemsTable =
|
||||||
|
{
|
||||||
|
{ "str", HandleTesterBisGemsStr, rbac::RBAC_PERM_COMMAND_ADDITEM, Console::No },
|
||||||
|
{ "agi", HandleTesterBisGemsAgi, rbac::RBAC_PERM_COMMAND_ADDITEM, Console::No },
|
||||||
|
{ "int", HandleTesterBisGemsInt, rbac::RBAC_PERM_COMMAND_ADDITEM, Console::No },
|
||||||
|
{ "spi", HandleTesterBisGemsSpi, rbac::RBAC_PERM_COMMAND_ADDITEM, Console::No },
|
||||||
|
{ "tank", HandleTesterBisGemsTank, rbac::RBAC_PERM_COMMAND_ADDITEM, Console::No },
|
||||||
|
{ "hybrid", HandleTesterBisGemsHybrid, rbac::RBAC_PERM_COMMAND_ADDITEM, Console::No },
|
||||||
|
{ "ranged", HandleTesterBisGemsRanged, rbac::RBAC_PERM_COMMAND_ADDITEM, Console::No },
|
||||||
|
{ "", HandleTesterBisGemsHelp, rbac::RBAC_PERM_COMMAND_ADDITEM, Console::No },
|
||||||
|
};
|
||||||
|
|
||||||
|
static ChatCommandTable testerBisTable =
|
||||||
|
{
|
||||||
|
{ "str", HandleTesterBisStr, rbac::RBAC_PERM_COMMAND_ADDITEM, Console::No },
|
||||||
|
{ "agi", HandleTesterBisAgi, rbac::RBAC_PERM_COMMAND_ADDITEM, Console::No },
|
||||||
|
{ "int", HandleTesterBisInt, rbac::RBAC_PERM_COMMAND_ADDITEM, Console::No },
|
||||||
|
{ "spi", HandleTesterBisSpi, rbac::RBAC_PERM_COMMAND_ADDITEM, Console::No },
|
||||||
|
{ "tank", HandleTesterBisTank, rbac::RBAC_PERM_COMMAND_ADDITEM, Console::No },
|
||||||
|
{ "hybrid", HandleTesterBisHybrid, rbac::RBAC_PERM_COMMAND_ADDITEM, Console::No },
|
||||||
|
{ "hunter", HandleTesterBisHunter, rbac::RBAC_PERM_COMMAND_ADDITEM, Console::No },
|
||||||
|
{ "gems", testerBisGemsTable },
|
||||||
|
};
|
||||||
|
|
||||||
|
static ChatCommandTable testerTable =
|
||||||
|
{
|
||||||
|
{ "bis", testerBisTable },
|
||||||
|
{ "bags", HandleTesterBags, rbac::RBAC_PERM_COMMAND_ADDITEM, Console::No },
|
||||||
|
{ "weapons", HandleTesterWeapons, rbac::RBAC_PERM_COMMAND_ADDITEM, Console::No },
|
||||||
|
{ "clearinv", HandleTesterClearInv, rbac::RBAC_PERM_COMMAND_ADDITEM, Console::No },
|
||||||
|
};
|
||||||
|
|
||||||
static ChatCommandTable paragonSubTable =
|
static ChatCommandTable paragonSubTable =
|
||||||
{
|
{
|
||||||
{ "currency", HandleCurrency, rbac::RBAC_PERM_COMMAND_LEARN, Console::No },
|
{ "currency", HandleCurrency, rbac::RBAC_PERM_COMMAND_LEARN, Console::No },
|
||||||
@@ -4606,6 +5123,7 @@ public:
|
|||||||
{ "runes", HandleRunes, rbac::RBAC_PERM_COMMAND_LEARN, Console::No },
|
{ "runes", HandleRunes, rbac::RBAC_PERM_COMMAND_LEARN, Console::No },
|
||||||
{ "hat", HandleHat, rbac::RBAC_PERM_COMMAND_LEARN, Console::No },
|
{ "hat", HandleHat, rbac::RBAC_PERM_COMMAND_LEARN, Console::No },
|
||||||
{ "recalibrate", HandlePanelRecalibrate, rbac::RBAC_PERM_COMMAND_MODIFY, Console::No },
|
{ "recalibrate", HandlePanelRecalibrate, rbac::RBAC_PERM_COMMAND_MODIFY, Console::No },
|
||||||
|
{ "tester", testerTable },
|
||||||
};
|
};
|
||||||
|
|
||||||
static ChatCommandTable commandTable =
|
static ChatCommandTable commandTable =
|
||||||
@@ -4616,6 +5134,238 @@ public:
|
|||||||
return commandTable;
|
return commandTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool HandleTesterBisKit(ChatHandler* handler, uint32 const* ids, size_t count, char const* label)
|
||||||
|
{
|
||||||
|
Player* target = handler->getSelectedPlayerOrSelf();
|
||||||
|
if (!target)
|
||||||
|
{
|
||||||
|
handler->SendErrorMessage(LANG_PLAYER_NOT_FOUND);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 const n = ParagonTesterGrantItemList(target, ids, count, handler);
|
||||||
|
handler->PSendSysMessage("Paragon tester {} BiS: granted {} items to {}.", label, n, target->GetName());
|
||||||
|
return n > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HandleTesterBisStr(ChatHandler* handler)
|
||||||
|
{
|
||||||
|
return HandleTesterBisKit(handler, kTesterBisStr, sizeof(kTesterBisStr) / sizeof(kTesterBisStr[0]), "STR");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HandleTesterBisAgi(ChatHandler* handler)
|
||||||
|
{
|
||||||
|
return HandleTesterBisKit(handler, kTesterBisAgi, sizeof(kTesterBisAgi) / sizeof(kTesterBisAgi[0]), "AGI");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HandleTesterBisInt(ChatHandler* handler)
|
||||||
|
{
|
||||||
|
return HandleTesterBisKit(handler, kTesterBisInt, sizeof(kTesterBisInt) / sizeof(kTesterBisInt[0]), "INT");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HandleTesterBisSpi(ChatHandler* handler)
|
||||||
|
{
|
||||||
|
return HandleTesterBisKit(handler, kTesterBisSpi, sizeof(kTesterBisSpi) / sizeof(kTesterBisSpi[0]), "SPI");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HandleTesterBisTank(ChatHandler* handler)
|
||||||
|
{
|
||||||
|
return HandleTesterBisKit(handler, kTesterBisTank, sizeof(kTesterBisTank) / sizeof(kTesterBisTank[0]), "tank");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HandleTesterBisHybrid(ChatHandler* handler)
|
||||||
|
{
|
||||||
|
return HandleTesterBisKit(handler, kTesterBisHybrid, sizeof(kTesterBisHybrid) / sizeof(kTesterBisHybrid[0]), "hybrid");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HandleTesterBisHunter(ChatHandler* handler)
|
||||||
|
{
|
||||||
|
return HandleTesterBisKit(handler, kTesterBisHunter, sizeof(kTesterBisHunter) / sizeof(kTesterBisHunter[0]), "hunter (ranged)");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HandleTesterBisGemsHelp(ChatHandler* handler)
|
||||||
|
{
|
||||||
|
handler->SendSysMessage(
|
||||||
|
"Paragon tester gems: .paragon tester bis gems <category>\n"
|
||||||
|
" category: str | agi | int | spi | tank | hybrid | ranged\n"
|
||||||
|
" Grants stacked ICC-era gems, enchant scrolls, Eternal Belt Buckle, leg armor/spellthread, "
|
||||||
|
"Sons of Hodir shoulder inscriptions, and helm arcanums. "
|
||||||
|
"ranged adds engineering scopes (Diamond-cut Refractor, Heartseeker, Sun) plus AGI/hit gems.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HandleTesterBisGemsKit(ChatHandler* handler, ParagonTesterStackedGrant const* grants, size_t count, char const* label)
|
||||||
|
{
|
||||||
|
Player* target = handler->getSelectedPlayerOrSelf();
|
||||||
|
if (!target)
|
||||||
|
{
|
||||||
|
handler->SendErrorMessage(LANG_PLAYER_NOT_FOUND);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 const n = ParagonTesterGrantStackedItemList(target, grants, count, handler);
|
||||||
|
handler->PSendSysMessage("Paragon tester {} gems/enchants: granted {} item pieces (stacked lines) to {}.",
|
||||||
|
label,
|
||||||
|
n,
|
||||||
|
target->GetName());
|
||||||
|
return n > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HandleTesterBisGemsStr(ChatHandler* handler)
|
||||||
|
{
|
||||||
|
return HandleTesterBisGemsKit(handler, kTesterGemsStr, sizeof(kTesterGemsStr) / sizeof(kTesterGemsStr[0]), "STR");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HandleTesterBisGemsAgi(ChatHandler* handler)
|
||||||
|
{
|
||||||
|
return HandleTesterBisGemsKit(handler, kTesterGemsAgi, sizeof(kTesterGemsAgi) / sizeof(kTesterGemsAgi[0]), "AGI");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HandleTesterBisGemsInt(ChatHandler* handler)
|
||||||
|
{
|
||||||
|
return HandleTesterBisGemsKit(handler, kTesterGemsInt, sizeof(kTesterGemsInt) / sizeof(kTesterGemsInt[0]), "INT");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HandleTesterBisGemsSpi(ChatHandler* handler)
|
||||||
|
{
|
||||||
|
return HandleTesterBisGemsKit(handler, kTesterGemsSpi, sizeof(kTesterGemsSpi) / sizeof(kTesterGemsSpi[0]), "SPI");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HandleTesterBisGemsTank(ChatHandler* handler)
|
||||||
|
{
|
||||||
|
return HandleTesterBisGemsKit(handler, kTesterGemsTank, sizeof(kTesterGemsTank) / sizeof(kTesterGemsTank[0]), "tank");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HandleTesterBisGemsHybrid(ChatHandler* handler)
|
||||||
|
{
|
||||||
|
return HandleTesterBisGemsKit(handler, kTesterGemsHybrid, sizeof(kTesterGemsHybrid) / sizeof(kTesterGemsHybrid[0]), "hybrid");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HandleTesterBisGemsRanged(ChatHandler* handler)
|
||||||
|
{
|
||||||
|
return HandleTesterBisGemsKit(handler, kTesterGemsRanged, sizeof(kTesterGemsRanged) / sizeof(kTesterGemsRanged[0]), "RANGED");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HandleTesterBags(ChatHandler* handler)
|
||||||
|
{
|
||||||
|
Player* target = handler->getSelectedPlayerOrSelf();
|
||||||
|
if (!target)
|
||||||
|
{
|
||||||
|
handler->SendErrorMessage(LANG_PLAYER_NOT_FOUND);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 const bagId = ParagonTesterSelectLargestUsableBagItemId(target);
|
||||||
|
if (!bagId)
|
||||||
|
{
|
||||||
|
handler->SendSysMessage("Paragon tester bags: no bag template this character can use (check item ids).");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 granted = 0;
|
||||||
|
uint32 equipped = 0;
|
||||||
|
for (int i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
ItemPosCountVec dest;
|
||||||
|
if (target->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, bagId, 1) != EQUIP_ERR_OK || dest.empty())
|
||||||
|
{
|
||||||
|
handler->PSendSysMessage("Paragon tester bags: could only add {} bag(s); inventory full?", granted);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Item* item = target->StoreNewItem(dest, bagId, true))
|
||||||
|
{
|
||||||
|
item->SetBinding(false);
|
||||||
|
++granted;
|
||||||
|
if (ParagonTesterTryEquipBagToFirstEmptySlot(target, item))
|
||||||
|
++equipped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(bagId);
|
||||||
|
handler->PSendSysMessage("Paragon tester bags: added {} x {} ({}); auto-equipped {} to bag bar for {}.",
|
||||||
|
granted,
|
||||||
|
bagId,
|
||||||
|
proto ? proto->Name1.c_str() : "?",
|
||||||
|
equipped,
|
||||||
|
target->GetName());
|
||||||
|
return granted > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HandleTesterWeapons(ChatHandler* handler, Tail tail)
|
||||||
|
{
|
||||||
|
std::string_view sv = tail;
|
||||||
|
while (!sv.empty() && sv.front() == ' ')
|
||||||
|
sv.remove_prefix(1);
|
||||||
|
|
||||||
|
if (sv.empty())
|
||||||
|
{
|
||||||
|
handler->SendSysMessage(
|
||||||
|
"Paragon tester weapons: .paragon tester weapons <stat> <type>\n"
|
||||||
|
" stat: str | agi | int | spi | tank | hybrid (aliases: strength, agility, intellect, spirit, apsp)\n"
|
||||||
|
" type: depends on stat — e.g. str: 2h sword, 2h axe, 2h mace, 1h sword, 1h axe, 1h mace (or 2h/sword), dual, ranged | "
|
||||||
|
"agi: … bow (hunter bow) or ranged/gun/crossbow (Fal'inrush) | int: staff, wand, mhoh, shield, ranged | "
|
||||||
|
"spi/hybrid: … ranged");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t const sp = sv.find(' ');
|
||||||
|
if (sp == std::string_view::npos)
|
||||||
|
{
|
||||||
|
handler->SendSysMessage("Paragon tester weapons: need both <stat> and <type> (see help with no args).");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string stat(sv.substr(0, sp));
|
||||||
|
sv.remove_prefix(sp + 1);
|
||||||
|
while (!sv.empty() && sv.front() == ' ')
|
||||||
|
sv.remove_prefix(1);
|
||||||
|
if (sv.empty())
|
||||||
|
{
|
||||||
|
handler->SendSysMessage("Paragon tester weapons: missing <type> after stat.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::string weaponType(sv.begin(), sv.end());
|
||||||
|
|
||||||
|
Player* target = handler->getSelectedPlayerOrSelf();
|
||||||
|
if (!target)
|
||||||
|
{
|
||||||
|
handler->SendErrorMessage(LANG_PLAYER_NOT_FOUND);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint32> ids;
|
||||||
|
std::string err;
|
||||||
|
if (!ParagonTesterResolveWeaponKit(stat, weaponType, ids, err))
|
||||||
|
{
|
||||||
|
handler->PSendSysMessage("Paragon tester weapons: {}", err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 const n = ParagonTesterGrantItemList(target, ids.data(), ids.size(), handler);
|
||||||
|
handler->PSendSysMessage("Paragon tester weapons [{} / {}]: granted {} item(s) to {}.",
|
||||||
|
stat,
|
||||||
|
weaponType,
|
||||||
|
n,
|
||||||
|
target->GetName());
|
||||||
|
return n > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HandleTesterClearInv(ChatHandler* handler)
|
||||||
|
{
|
||||||
|
Player* target = handler->getSelectedPlayerOrSelf();
|
||||||
|
if (!target)
|
||||||
|
{
|
||||||
|
handler->SendErrorMessage(LANG_PLAYER_NOT_FOUND);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParagonTesterClearNonEquipmentInventory(target);
|
||||||
|
handler->PSendSysMessage("Paragon tester clearinv: removed backpack + bag contents (equipment untouched) for {}.",
|
||||||
|
target->GetName());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Full Character Advancement reset for the selected player (or self):
|
// Full Character Advancement reset for the selected player (or self):
|
||||||
// unlearn all panel spells/talents, clear panel DB + active build pointer,
|
// unlearn all panel spells/talents, clear panel DB + active build pointer,
|
||||||
// then clamp AE/TE to the level-correct totals (same math as login
|
// then clamp AE/TE to the level-correct totals (same math as login
|
||||||
|
|||||||
Executable
+37
@@ -0,0 +1,37 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Kill AzerothCore authserver + worldserver tmux sessions and any stray processes.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# bash scripts/kill-azeroth-servers.sh
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
AUTH_SESSION="authserver"
|
||||||
|
WORLD_SESSION="worldserver"
|
||||||
|
|
||||||
|
echo "Stopping servers..."
|
||||||
|
|
||||||
|
# Kill tmux sessions
|
||||||
|
if tmux has-session -t "$WORLD_SESSION" 2>/dev/null; then
|
||||||
|
tmux kill-session -t "$WORLD_SESSION"
|
||||||
|
echo " killed tmux session: $WORLD_SESSION"
|
||||||
|
else
|
||||||
|
echo " no tmux session: $WORLD_SESSION"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if tmux has-session -t "$AUTH_SESSION" 2>/dev/null; then
|
||||||
|
tmux kill-session -t "$AUTH_SESSION"
|
||||||
|
echo " killed tmux session: $AUTH_SESSION"
|
||||||
|
else
|
||||||
|
echo " no tmux session: $AUTH_SESSION"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean up any stray processes not managed by tmux
|
||||||
|
if pkill -x worldserver 2>/dev/null; then
|
||||||
|
echo " killed stray worldserver process"
|
||||||
|
fi
|
||||||
|
if pkill -x authserver 2>/dev/null; then
|
||||||
|
echo " killed stray authserver process"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Done."
|
||||||
@@ -1,14 +1,18 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Start AzerothCore authserver + worldserver detached from the SSH session (nohup + disown).
|
# Start AzerothCore authserver + worldserver in named tmux sessions.
|
||||||
# Stops any already-running authserver/worldserver processes first.
|
# Kills any already-running sessions first (acts as a restart).
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# sudo bash scripts/start-azeroth-servers.sh
|
# bash scripts/start-azeroth-servers.sh
|
||||||
# AZEROTH_BIN=/path/to/azeroth-server/bin bash scripts/start-azeroth-servers.sh
|
# AZEROTH_BIN=/path/to/bin bash scripts/start-azeroth-servers.sh
|
||||||
#
|
#
|
||||||
# Environment:
|
# Environment:
|
||||||
# AZEROTH_BIN — directory with authserver and worldserver (default: /home/fractured-panel/azeroth-server/bin)
|
# AZEROTH_BIN — directory with authserver and worldserver (default: /home/fractured-panel/azeroth-server/bin)
|
||||||
# AZEROTH_LOG_DIR — log directory (default: <parent of bin>/logs)
|
# AZEROTH_LOG_DIR — log directory (default: <parent of bin>/logs)
|
||||||
|
#
|
||||||
|
# tmux sessions:
|
||||||
|
# authserver — authserver console
|
||||||
|
# worldserver — worldserver console
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
@@ -20,6 +24,14 @@ CONF_DIR="${BASE_DIR}/etc"
|
|||||||
AUTH_BIN="${BIN_DIR}/authserver"
|
AUTH_BIN="${BIN_DIR}/authserver"
|
||||||
WORLD_BIN="${BIN_DIR}/worldserver"
|
WORLD_BIN="${BIN_DIR}/worldserver"
|
||||||
|
|
||||||
|
AUTH_SESSION="authserver"
|
||||||
|
WORLD_SESSION="worldserver"
|
||||||
|
|
||||||
|
if ! command -v tmux &>/dev/null; then
|
||||||
|
echo "error: tmux is not installed" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ ! -x "$AUTH_BIN" ]]; then
|
if [[ ! -x "$AUTH_BIN" ]]; then
|
||||||
echo "error: not found or not executable: $AUTH_BIN" >&2
|
echo "error: not found or not executable: $AUTH_BIN" >&2
|
||||||
exit 1
|
exit 1
|
||||||
@@ -29,23 +41,30 @@ if [[ ! -x "$WORLD_BIN" ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$LOG_DIR"
|
||||||
|
|
||||||
|
# Tear down existing sessions (ignore errors if they don't exist)
|
||||||
|
tmux kill-session -t "$AUTH_SESSION" 2>/dev/null || true
|
||||||
|
tmux kill-session -t "$WORLD_SESSION" 2>/dev/null || true
|
||||||
|
|
||||||
|
# Also kill any stray processes not managed by tmux
|
||||||
pkill -x authserver 2>/dev/null || true
|
pkill -x authserver 2>/dev/null || true
|
||||||
pkill -x worldserver 2>/dev/null || true
|
pkill -x worldserver 2>/dev/null || true
|
||||||
sleep 1
|
sleep 1
|
||||||
|
|
||||||
mkdir -p "$LOG_DIR"
|
# Launch authserver in a tmux session
|
||||||
|
tmux new-session -d -s "$AUTH_SESSION" -c "$BIN_DIR" \
|
||||||
cd "$BIN_DIR"
|
"$AUTH_BIN -c ${CONF_DIR}/authserver.conf 2>&1 | tee -a ${LOG_DIR}/authserver.log"
|
||||||
|
|
||||||
nohup "$AUTH_BIN" -c "${CONF_DIR}/authserver.conf" >>"$LOG_DIR/authserver.log" 2>&1 &
|
|
||||||
disown || true
|
|
||||||
|
|
||||||
sleep 2
|
sleep 2
|
||||||
|
|
||||||
nohup "$WORLD_BIN" -c "${CONF_DIR}/worldserver.conf" >>"$LOG_DIR/worldserver.log" 2>&1 &
|
# Launch worldserver in a tmux session
|
||||||
disown || true
|
tmux new-session -d -s "$WORLD_SESSION" -c "$BIN_DIR" \
|
||||||
|
"$WORLD_BIN -c ${CONF_DIR}/worldserver.conf 2>&1 | tee -a ${LOG_DIR}/worldserver.log"
|
||||||
|
|
||||||
echo "Started authserver and worldserver (survives SSH disconnect)."
|
echo "Started servers in tmux sessions."
|
||||||
|
echo " tmux attach -t $AUTH_SESSION — authserver console"
|
||||||
|
echo " tmux attach -t $WORLD_SESSION — worldserver console"
|
||||||
echo "Bin: $BIN_DIR"
|
echo "Bin: $BIN_DIR"
|
||||||
echo "Config: $CONF_DIR"
|
echo "Config: $CONF_DIR"
|
||||||
echo "Logs: $LOG_DIR/authserver.log"
|
echo "Logs: $LOG_DIR/authserver.log"
|
||||||
|
|||||||
@@ -6,24 +6,25 @@
|
|||||||
# (see docs/DEPLOY_LINUX_VPS.md).
|
# (see docs/DEPLOY_LINUX_VPS.md).
|
||||||
#
|
#
|
||||||
# What this does:
|
# What this does:
|
||||||
# 1. git pull on the current branch (optional; can skip)
|
# 1. Optionally kill running servers (tmux sessions)
|
||||||
# 2. ./acore.sh compiler build — or compiler all for a full clean rebuild
|
# 2. git pull on the current branch (optional; can skip)
|
||||||
|
# 3. ./acore.sh compiler build — or compiler all for a full clean rebuild
|
||||||
|
# 4. Optionally restart servers in tmux sessions
|
||||||
#
|
#
|
||||||
# Database migrations from data/sql/updates/ run when you next start worldserver/authserver
|
# Database migrations from data/sql/updates/ run when you next start worldserver/authserver
|
||||||
# (Updates.* / SourceDirectory in *.conf). This script does not start or stop daemons unless
|
# (Updates.* / SourceDirectory in *.conf).
|
||||||
# you pass --run-after or set FRACTURED_POST_UPDATE_CMD.
|
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# bash scripts/vps-update-server.sh
|
# bash scripts/vps-update-server.sh # pull + compile only
|
||||||
# bash scripts/vps-update-server.sh --full
|
# bash scripts/vps-update-server.sh --restart # pull + compile + restart servers in tmux
|
||||||
# bash scripts/vps-update-server.sh --no-pull
|
# bash scripts/vps-update-server.sh --full --restart # clean rebuild + restart
|
||||||
|
# bash scripts/vps-update-server.sh --no-pull --restart # compile current tree + restart
|
||||||
# bash scripts/vps-update-server.sh --dry-run
|
# bash scripts/vps-update-server.sh --dry-run
|
||||||
# FRACTURED_POST_UPDATE_CMD='sudo systemctl restart fractured-world' bash scripts/vps-update-server.sh --run-after
|
# bash scripts/vps-update-server.sh --run-after 'custom command here'
|
||||||
# bash scripts/vps-update-server.sh --run-after 'sudo systemctl restart fractured-world'
|
|
||||||
#
|
#
|
||||||
# Environment:
|
# Environment:
|
||||||
# FRACTURED_GIT_REMOTE — remote name (default: origin)
|
# FRACTURED_GIT_REMOTE — remote name (default: origin)
|
||||||
# FRACTURED_POST_UPDATE_CMD — shell command run after a successful compile (if --run-after is passed without an argument, this is used)
|
# FRACTURED_POST_UPDATE_CMD — shell command run after compile (used by bare --run-after)
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
@@ -35,6 +36,7 @@ FULL_BUILD=0
|
|||||||
COMPILE_ONLY=0
|
COMPILE_ONLY=0
|
||||||
DRY_RUN=0
|
DRY_RUN=0
|
||||||
DO_RUN_AFTER=0
|
DO_RUN_AFTER=0
|
||||||
|
DO_RESTART=0
|
||||||
INSTALL_PREFIX=""
|
INSTALL_PREFIX=""
|
||||||
POST_UPDATE_CMD="${FRACTURED_POST_UPDATE_CMD:-}"
|
POST_UPDATE_CMD="${FRACTURED_POST_UPDATE_CMD:-}"
|
||||||
GIT_REMOTE="${FRACTURED_GIT_REMOTE:-origin}"
|
GIT_REMOTE="${FRACTURED_GIT_REMOTE:-origin}"
|
||||||
@@ -52,8 +54,9 @@ Options:
|
|||||||
--compile-only ./acore.sh compiler compile (incremental).
|
--compile-only ./acore.sh compiler compile (incremental).
|
||||||
--prefix PATH Override CMAKE_INSTALL_PREFIX (updates conf/config.sh BINPATH).
|
--prefix PATH Override CMAKE_INSTALL_PREFIX (updates conf/config.sh BINPATH).
|
||||||
--dry-run Print commands without running them.
|
--dry-run Print commands without running them.
|
||||||
--run-after [CMD] Run shell command after successful compile. If CMD is omitted,
|
--restart Kill servers before compile, restart in tmux after.
|
||||||
uses FRACTURED_POST_UPDATE_CMD from the environment.
|
--run-after [CMD] Run a custom shell command after successful compile.
|
||||||
|
If CMD is omitted, uses FRACTURED_POST_UPDATE_CMD.
|
||||||
|
|
||||||
Environment:
|
Environment:
|
||||||
FRACTURED_GIT_REMOTE Git remote (default: origin).
|
FRACTURED_GIT_REMOTE Git remote (default: origin).
|
||||||
@@ -102,6 +105,10 @@ while [[ $# -gt 0 ]]; do
|
|||||||
DRY_RUN=1
|
DRY_RUN=1
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
|
--restart)
|
||||||
|
DO_RESTART=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
--run-after)
|
--run-after)
|
||||||
DO_RUN_AFTER=1
|
DO_RUN_AFTER=1
|
||||||
shift
|
shift
|
||||||
@@ -140,6 +147,18 @@ fi
|
|||||||
|
|
||||||
cd "$ROOT"
|
cd "$ROOT"
|
||||||
|
|
||||||
|
KILL_SCRIPT="${SCRIPT_DIR}/kill-azeroth-servers.sh"
|
||||||
|
START_SCRIPT="${SCRIPT_DIR}/start-azeroth-servers.sh"
|
||||||
|
|
||||||
|
if [[ "$DO_RESTART" -eq 1 ]]; then
|
||||||
|
if [[ ! -f "$KILL_SCRIPT" || ! -f "$START_SCRIPT" ]]; then
|
||||||
|
echo "error: --restart requires kill-azeroth-servers.sh and start-azeroth-servers.sh in scripts/" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "==> stopping servers before compile"
|
||||||
|
run bash "$KILL_SCRIPT"
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ -n "$INSTALL_PREFIX" ]]; then
|
if [[ -n "$INSTALL_PREFIX" ]]; then
|
||||||
echo "==> updating conf/config.sh BINPATH to: $INSTALL_PREFIX"
|
echo "==> updating conf/config.sh BINPATH to: $INSTALL_PREFIX"
|
||||||
if grep -q '^BINPATH=' conf/config.sh; then
|
if grep -q '^BINPATH=' conf/config.sh; then
|
||||||
@@ -189,6 +208,11 @@ else
|
|||||||
run ./acore.sh compiler build
|
run ./acore.sh compiler build
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ "$DO_RESTART" -eq 1 ]]; then
|
||||||
|
echo "==> restarting servers in tmux sessions"
|
||||||
|
run bash "$START_SCRIPT"
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ "$DO_RUN_AFTER" -eq 1 ]]; then
|
if [[ "$DO_RUN_AFTER" -eq 1 ]]; then
|
||||||
echo "==> post-update: $POST_UPDATE_CMD"
|
echo "==> post-update: $POST_UPDATE_CMD"
|
||||||
if [[ "$DRY_RUN" -eq 1 ]]; then
|
if [[ "$DRY_RUN" -eq 1 ]]; then
|
||||||
@@ -199,4 +223,8 @@ if [[ "$DO_RUN_AFTER" -eq 1 ]]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Done. Restart authserver/worldserver (or your service manager) when ready so new binaries and SQL updates apply."
|
if [[ "$DO_RESTART" -eq 0 && "$DO_RUN_AFTER" -eq 0 ]]; then
|
||||||
|
echo "Done. Run 'bash scripts/start-azeroth-servers.sh' to (re)start servers in tmux."
|
||||||
|
else
|
||||||
|
echo "Done."
|
||||||
|
fi
|
||||||
|
|||||||
@@ -1753,9 +1753,9 @@ InstantLogout = 1
|
|||||||
#
|
#
|
||||||
# PlayerSaveInterval
|
# PlayerSaveInterval
|
||||||
# Description: Time (in milliseconds) for player save interval.
|
# Description: Time (in milliseconds) for player save interval.
|
||||||
# Default: 900000 - (15 min)
|
# Default: 300000 - (5 min)
|
||||||
|
|
||||||
PlayerSaveInterval = 900000
|
PlayerSaveInterval = 300000
|
||||||
|
|
||||||
#
|
#
|
||||||
# PlayerSave.Stats.MinLevel
|
# PlayerSave.Stats.MinLevel
|
||||||
@@ -2260,9 +2260,9 @@ Achievement.RealmFirstKillWindow = 60
|
|||||||
# MaxPrimaryTradeSkill
|
# MaxPrimaryTradeSkill
|
||||||
# Description: Maximum number of primary professions a character can learn.
|
# Description: Maximum number of primary professions a character can learn.
|
||||||
# Range: 0-11
|
# Range: 0-11
|
||||||
# Default: 2
|
# Default: 11 - (All WotLK primary professions; set 2 for retail-like two-slot cap.)
|
||||||
|
|
||||||
MaxPrimaryTradeSkill = 2
|
MaxPrimaryTradeSkill = 11
|
||||||
|
|
||||||
#
|
#
|
||||||
# SkillChance.Prospecting
|
# SkillChance.Prospecting
|
||||||
|
|||||||
@@ -678,6 +678,7 @@ enum RBACPermissions
|
|||||||
RBAC_PERM_COMMAND_BF_QUEUE = 913,
|
RBAC_PERM_COMMAND_BF_QUEUE = 913,
|
||||||
RBAC_PERM_COMMAND_PET_LIST = 914,
|
RBAC_PERM_COMMAND_PET_LIST = 914,
|
||||||
RBAC_PERM_COMMAND_PET_DELETE = 915,
|
RBAC_PERM_COMMAND_PET_DELETE = 915,
|
||||||
|
RBAC_PERM_COMMAND_LEARN_ALL_MOUNTS = 916,
|
||||||
// custom permissions 1000+
|
// custom permissions 1000+
|
||||||
RBAC_PERM_MAX
|
RBAC_PERM_MAX
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3640,6 +3640,13 @@ bool Creature::IsMovementPreventedByCasting() const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fractured: cast-time mount summon (player-style mount spells on NPCs are rare but supported).
|
||||||
|
if (Spell* genSpell = m_currentSpells[CURRENT_GENERIC_SPELL])
|
||||||
|
{
|
||||||
|
if (genSpell->getState() == SPELL_STATE_PREPARING && genSpell->m_spellInfo->IsCastTimeRidingMountSpell())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (HasSpellFocus())
|
if (HasSpellFocus())
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -1828,6 +1828,7 @@ public:
|
|||||||
uint32 GetLastPotionId() { return m_lastPotionId; }
|
uint32 GetLastPotionId() { return m_lastPotionId; }
|
||||||
void SetLastPotionId(uint32 item_id) { m_lastPotionId = item_id; }
|
void SetLastPotionId(uint32 item_id) { m_lastPotionId = item_id; }
|
||||||
void UpdatePotionCooldown(Spell* spell = nullptr);
|
void UpdatePotionCooldown(Spell* spell = nullptr);
|
||||||
|
void AtEnterCombat() override;
|
||||||
void AtExitCombat() override;
|
void AtExitCombat() override;
|
||||||
|
|
||||||
void setResurrectRequestData(ObjectGuid guid, uint32 mapId, float X, float Y, float Z, uint32 health, uint32 mana)
|
void setResurrectRequestData(ObjectGuid guid, uint32 mapId, float X, float Y, float Z, uint32 health, uint32 mana)
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
#include "Player.h"
|
#include "Player.h"
|
||||||
#include "ScriptMgr.h"
|
#include "ScriptMgr.h"
|
||||||
#include "SkillDiscovery.h"
|
#include "SkillDiscovery.h"
|
||||||
|
#include "Spell.h"
|
||||||
#include "SpellAuraEffects.h"
|
#include "SpellAuraEffects.h"
|
||||||
#include "SpellMgr.h"
|
#include "SpellMgr.h"
|
||||||
#include "UpdateFieldFlags.h"
|
#include "UpdateFieldFlags.h"
|
||||||
@@ -332,6 +333,28 @@ void Player::Update(uint32 p_time)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_additionalSaveTimer)
|
||||||
|
{
|
||||||
|
if (p_time >= m_additionalSaveTimer)
|
||||||
|
{
|
||||||
|
m_additionalSaveTimer = 0;
|
||||||
|
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
|
||||||
|
|
||||||
|
if (m_additionalSaveMask & ADDITIONAL_SAVING_INVENTORY_AND_GOLD)
|
||||||
|
SaveInventoryAndGoldToDB(trans);
|
||||||
|
|
||||||
|
if (m_additionalSaveMask & ADDITIONAL_SAVING_QUEST_STATUS)
|
||||||
|
_SaveQuestStatus(trans);
|
||||||
|
|
||||||
|
CharacterDatabase.CommitTransaction(trans);
|
||||||
|
m_additionalSaveMask = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_additionalSaveTimer -= p_time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle Water/drowning
|
// Handle Water/drowning
|
||||||
HandleDrowning(p_time);
|
HandleDrowning(p_time);
|
||||||
|
|
||||||
@@ -1539,6 +1562,27 @@ void Player::UpdatePvP(bool state, bool _override)
|
|||||||
sScriptMgr->OnPlayerPVPFlagChange(this, state);
|
sScriptMgr->OnPlayerPVPFlagChange(this, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Player::AtEnterCombat()
|
||||||
|
{
|
||||||
|
Unit::AtEnterCombat();
|
||||||
|
|
||||||
|
// Fractured: cancel cast-time mount summon if combat starts mid-cast.
|
||||||
|
for (uint32 spellType = CURRENT_FIRST_NON_MELEE_SPELL; spellType < CURRENT_MAX_SPELL; ++spellType)
|
||||||
|
{
|
||||||
|
if (Spell* spell = GetCurrentSpell(CurrentSpellTypes(spellType)))
|
||||||
|
{
|
||||||
|
if (SpellInfo const* info = spell->GetSpellInfo())
|
||||||
|
{
|
||||||
|
if (info->IsCastTimeRidingMountSpell())
|
||||||
|
{
|
||||||
|
InterruptSpell(CurrentSpellTypes(spellType), false, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Player::AtExitCombat()
|
void Player::AtExitCombat()
|
||||||
{
|
{
|
||||||
Unit::AtExitCombat();
|
Unit::AtExitCombat();
|
||||||
|
|||||||
@@ -151,6 +151,11 @@ bool IsFracturedExclusiveStanceSpell(uint32 spellId)
|
|||||||
case 71: // Defensive Stance
|
case 71: // Defensive Stance
|
||||||
case 2458: // Berserker Stance
|
case 2458: // Berserker Stance
|
||||||
|
|
||||||
|
// -- Paragon Advancement warrior stance clones (951010-951012).
|
||||||
|
case 951010:
|
||||||
|
case 951011:
|
||||||
|
case 951012:
|
||||||
|
|
||||||
// -- Druid combat forms (engine-shapeshifts).
|
// -- Druid combat forms (engine-shapeshifts).
|
||||||
case 5487: // Bear Form
|
case 5487: // Bear Form
|
||||||
case 9634: // Dire Bear Form
|
case 9634: // Dire Bear Form
|
||||||
@@ -189,6 +194,11 @@ bool IsFracturedExclusiveStanceSpell(uint32 spellId)
|
|||||||
case 48263: // Frost Presence
|
case 48263: // Frost Presence
|
||||||
case 48265: // Unholy Presence
|
case 48265: // Unholy Presence
|
||||||
|
|
||||||
|
// -- Paragon Advancement DK presence clones (951013-951015).
|
||||||
|
case 951013:
|
||||||
|
case 951014:
|
||||||
|
case 951015:
|
||||||
|
|
||||||
// -- Hunter Aspects (combat). Like presences, these are regular
|
// -- Hunter Aspects (combat). Like presences, these are regular
|
||||||
// auras stock AC, not engine-shapeshifts; rank-1 ids cover all
|
// auras stock AC, not engine-shapeshifts; rank-1 ids cover all
|
||||||
// ranks via GetFirstRankSpell. Cheetah / Pack are the utility
|
// ranks via GetFirstRankSpell. Cheetah / Pack are the utility
|
||||||
@@ -4493,6 +4503,13 @@ bool Unit::IsMovementPreventedByCasting() const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fractured: cast-time mount summon may be completed while moving.
|
||||||
|
if (Spell* genSpell = m_currentSpells[CURRENT_GENERIC_SPELL])
|
||||||
|
{
|
||||||
|
if (genSpell->getState() == SPELL_STATE_PREPARING && genSpell->m_spellInfo->IsCastTimeRidingMountSpell())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// channeled spells during channel stage (after the initial cast timer) allow movement with a specific spell attribute
|
// channeled spells during channel stage (after the initial cast timer) allow movement with a specific spell attribute
|
||||||
if (Spell* spell = m_currentSpells[CURRENT_CHANNELED_SPELL])
|
if (Spell* spell = m_currentSpells[CURRENT_CHANNELED_SPELL])
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -99,7 +99,13 @@ enum ShapeshiftForm
|
|||||||
FORM_FLIGHT = 0x1D,
|
FORM_FLIGHT = 0x1D,
|
||||||
FORM_STEALTH = 0x1E,
|
FORM_STEALTH = 0x1E,
|
||||||
FORM_MOONKIN = 0x1F,
|
FORM_MOONKIN = 0x1F,
|
||||||
FORM_SPIRITOFREDEMPTION = 0x20
|
FORM_SPIRITOFREDEMPTION = 0x20,
|
||||||
|
|
||||||
|
// Fractured / Paragon: Character Advancement warrior stance clones (Spell.dbc
|
||||||
|
// MOD_SHAPESHIFT -> SpellShapeshiftForm 33-35, BonusActionBar=0, no client bar swap).
|
||||||
|
FORM_PARAGON_BATTLE_STANCE = 33,
|
||||||
|
FORM_PARAGON_DEFENSIVE_STANCE = 34,
|
||||||
|
FORM_PARAGON_BERSERKER_STANCE = 35,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ShapeshiftFlags
|
enum ShapeshiftFlags
|
||||||
|
|||||||
@@ -1373,12 +1373,15 @@ void AuraEffect::HandleShapeshiftBoosts(Unit* target, bool apply) const
|
|||||||
HotWSpellId = 24899;
|
HotWSpellId = 24899;
|
||||||
break;
|
break;
|
||||||
case FORM_BATTLESTANCE:
|
case FORM_BATTLESTANCE:
|
||||||
|
case FORM_PARAGON_BATTLE_STANCE:
|
||||||
spellId = 21156;
|
spellId = 21156;
|
||||||
break;
|
break;
|
||||||
case FORM_DEFENSIVESTANCE:
|
case FORM_DEFENSIVESTANCE:
|
||||||
|
case FORM_PARAGON_DEFENSIVE_STANCE:
|
||||||
spellId = 7376;
|
spellId = 7376;
|
||||||
break;
|
break;
|
||||||
case FORM_BERSERKERSTANCE:
|
case FORM_BERSERKERSTANCE:
|
||||||
|
case FORM_PARAGON_BERSERKER_STANCE:
|
||||||
spellId = 7381;
|
spellId = 7381;
|
||||||
break;
|
break;
|
||||||
case FORM_MOONKIN:
|
case FORM_MOONKIN:
|
||||||
@@ -1995,7 +1998,7 @@ void AuraEffect::HandlePhase(AuraApplication const* aurApp, uint8 mode, bool app
|
|||||||
/*** UNIT MODEL ***/
|
/*** UNIT MODEL ***/
|
||||||
/**********************/
|
/**********************/
|
||||||
|
|
||||||
void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mode, bool apply) const
|
static void Fractured_ApplyShapeshiftFormFromAuraEffect(AuraEffect const* aurEff, AuraApplication const* aurApp, uint8 mode, bool apply, ShapeshiftForm form)
|
||||||
{
|
{
|
||||||
if (!(mode & AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK))
|
if (!(mode & AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK))
|
||||||
return;
|
return;
|
||||||
@@ -2004,8 +2007,6 @@ void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mo
|
|||||||
|
|
||||||
uint32 modelid = 0;
|
uint32 modelid = 0;
|
||||||
Powers PowerType = POWER_MANA;
|
Powers PowerType = POWER_MANA;
|
||||||
ShapeshiftForm form = ShapeshiftForm(GetMiscValue());
|
|
||||||
|
|
||||||
switch (form)
|
switch (form)
|
||||||
{
|
{
|
||||||
case FORM_CAT: // 0x01
|
case FORM_CAT: // 0x01
|
||||||
@@ -2019,6 +2020,9 @@ void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mo
|
|||||||
case FORM_BATTLESTANCE: // 0x11
|
case FORM_BATTLESTANCE: // 0x11
|
||||||
case FORM_DEFENSIVESTANCE: // 0x12
|
case FORM_DEFENSIVESTANCE: // 0x12
|
||||||
case FORM_BERSERKERSTANCE: // 0x13
|
case FORM_BERSERKERSTANCE: // 0x13
|
||||||
|
case FORM_PARAGON_BATTLE_STANCE:
|
||||||
|
case FORM_PARAGON_DEFENSIVE_STANCE:
|
||||||
|
case FORM_PARAGON_BERSERKER_STANCE:
|
||||||
PowerType = POWER_RAGE;
|
PowerType = POWER_RAGE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -2049,10 +2053,10 @@ void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mo
|
|||||||
case FORM_SPIRITOFREDEMPTION: // 0x20
|
case FORM_SPIRITOFREDEMPTION: // 0x20
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG_ERROR("spells.aura.effect", "Auras: Unknown Shapeshift Type: {}", GetMiscValue());
|
LOG_ERROR("spells.aura.effect", "Auras: Unknown Shapeshift Type: {}", aurEff->GetMiscValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
modelid = target->GetModelForForm(form, GetId());
|
modelid = target->GetModelForForm(form, aurEff->GetId());
|
||||||
|
|
||||||
if (apply)
|
if (apply)
|
||||||
{
|
{
|
||||||
@@ -2087,8 +2091,8 @@ void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mo
|
|||||||
|
|
||||||
// remove other shapeshift before applying a new one
|
// remove other shapeshift before applying a new one
|
||||||
// xinef: rogue shouldnt be wrapped by this check (shadow dance vs stealth)
|
// xinef: rogue shouldnt be wrapped by this check (shadow dance vs stealth)
|
||||||
if (GetSpellInfo()->SpellFamilyName != SPELLFAMILY_ROGUE)
|
if (aurEff->GetSpellInfo()->SpellFamilyName != SPELLFAMILY_ROGUE)
|
||||||
target->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT, ObjectGuid::Empty, GetBase());
|
target->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT, ObjectGuid::Empty, aurEff->GetBase());
|
||||||
|
|
||||||
// stop handling the effect if it was removed by linked event
|
// stop handling the effect if it was removed by linked event
|
||||||
if (aurApp->GetRemoveMode())
|
if (aurApp->GetRemoveMode())
|
||||||
@@ -2112,13 +2116,13 @@ void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mo
|
|||||||
if (AuraEffect const* dummy = target->GetDummyAuraEffect(SPELLFAMILY_DRUID, 238, 0))
|
if (AuraEffect const* dummy = target->GetDummyAuraEffect(SPELLFAMILY_DRUID, 238, 0))
|
||||||
FurorChance = std::max(dummy->GetAmount(), 0);
|
FurorChance = std::max(dummy->GetAmount(), 0);
|
||||||
|
|
||||||
switch (GetMiscValue())
|
switch (aurEff->GetMiscValue())
|
||||||
{
|
{
|
||||||
case FORM_CAT:
|
case FORM_CAT:
|
||||||
{
|
{
|
||||||
int32 basePoints = int32(std::min(oldPower, FurorChance));
|
int32 basePoints = int32(std::min(oldPower, FurorChance));
|
||||||
target->SetPower(POWER_ENERGY, 0);
|
target->SetPower(POWER_ENERGY, 0);
|
||||||
target->CastCustomSpell(target, 17099, &basePoints, nullptr, nullptr, true, nullptr, this);
|
target->CastCustomSpell(target, 17099, &basePoints, nullptr, nullptr, true, nullptr, aurEff);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case FORM_BEAR:
|
case FORM_BEAR:
|
||||||
@@ -2192,13 +2196,16 @@ void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mo
|
|||||||
case FORM_BATTLESTANCE:
|
case FORM_BATTLESTANCE:
|
||||||
case FORM_DEFENSIVESTANCE:
|
case FORM_DEFENSIVESTANCE:
|
||||||
case FORM_BERSERKERSTANCE:
|
case FORM_BERSERKERSTANCE:
|
||||||
|
case FORM_PARAGON_BATTLE_STANCE:
|
||||||
|
case FORM_PARAGON_DEFENSIVE_STANCE:
|
||||||
|
case FORM_PARAGON_BERSERKER_STANCE:
|
||||||
{
|
{
|
||||||
uint32 Rage_val = 0;
|
uint32 Rage_val = 0;
|
||||||
// Defensive Tactics
|
// Defensive Tactics
|
||||||
if (form == FORM_DEFENSIVESTANCE)
|
if (form == FORM_DEFENSIVESTANCE || form == FORM_PARAGON_DEFENSIVE_STANCE)
|
||||||
{
|
{
|
||||||
if (AuraEffect const* aurEff = target->IsScriptOverriden(m_spellInfo, 831))
|
if (AuraEffect const* scriptEff = target->IsScriptOverriden(aurEff->GetSpellInfo(), 831))
|
||||||
Rage_val += aurEff->GetAmount() * 10;
|
Rage_val += scriptEff->GetAmount() * 10;
|
||||||
}
|
}
|
||||||
// Stance mastery + Tactical mastery (both passive, and last have aura only in defense stance, but need apply at any stance switch)
|
// Stance mastery + Tactical mastery (both passive, and last have aura only in defense stance, but need apply at any stance switch)
|
||||||
if (target->IsPlayer())
|
if (target->IsPlayer())
|
||||||
@@ -2238,7 +2245,7 @@ void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mo
|
|||||||
|
|
||||||
// adding/removing linked auras
|
// adding/removing linked auras
|
||||||
// add/remove the shapeshift aura's boosts
|
// add/remove the shapeshift aura's boosts
|
||||||
HandleShapeshiftBoosts(target, apply);
|
aurEff->HandleShapeshiftBoosts(target, apply);
|
||||||
|
|
||||||
if (target->IsPlayer())
|
if (target->IsPlayer())
|
||||||
target->ToPlayer()->InitDataForForm();
|
target->ToPlayer()->InitDataForForm();
|
||||||
@@ -2246,8 +2253,8 @@ void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mo
|
|||||||
if (target->IsClass(CLASS_DRUID, CLASS_CONTEXT_ABILITY))
|
if (target->IsClass(CLASS_DRUID, CLASS_CONTEXT_ABILITY))
|
||||||
{
|
{
|
||||||
// Dash
|
// Dash
|
||||||
if (AuraEffect* aurEff = target->GetAuraEffect(SPELL_AURA_MOD_INCREASE_SPEED, SPELLFAMILY_DRUID, 0, 0, 0x8))
|
if (AuraEffect* dashSpeedEff = target->GetAuraEffect(SPELL_AURA_MOD_INCREASE_SPEED, SPELLFAMILY_DRUID, 0, 0, 0x8))
|
||||||
aurEff->RecalculateAmount();
|
dashSpeedEff->RecalculateAmount();
|
||||||
|
|
||||||
// Disarm handling
|
// Disarm handling
|
||||||
// If druid shifts while being disarmed we need to deal with that since forms aren't affected by disarm
|
// If druid shifts while being disarmed we need to deal with that since forms aren't affected by disarm
|
||||||
@@ -2281,6 +2288,11 @@ void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mo
|
|||||||
if (target->IsPlayer())
|
if (target->IsPlayer())
|
||||||
{
|
{
|
||||||
SpellShapeshiftFormEntry const* shapeInfo = sSpellShapeshiftFormStore.LookupEntry(form);
|
SpellShapeshiftFormEntry const* shapeInfo = sSpellShapeshiftFormStore.LookupEntry(form);
|
||||||
|
if (!shapeInfo)
|
||||||
|
{
|
||||||
|
LOG_ERROR("spells.aura.effect", "Fractured_ApplyShapeshiftFormFromAuraEffect: missing SpellShapeshiftForm {}", uint32(form));
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Learn spells for shapeshift form - no need to send action bars or add spells to spellbook
|
// Learn spells for shapeshift form - no need to send action bars or add spells to spellbook
|
||||||
for (uint8 i = 0; i < MAX_SHAPESHIFT_SPELLS; ++i)
|
for (uint8 i = 0; i < MAX_SHAPESHIFT_SPELLS; ++i)
|
||||||
{
|
{
|
||||||
@@ -2294,6 +2306,11 @@ void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mode, bool apply) const
|
||||||
|
{
|
||||||
|
Fractured_ApplyShapeshiftFormFromAuraEffect(this, aurApp, mode, apply, ShapeshiftForm(GetMiscValue()));
|
||||||
|
}
|
||||||
|
|
||||||
void AuraEffect::HandleAuraTransform(AuraApplication const* aurApp, uint8 mode, bool apply) const
|
void AuraEffect::HandleAuraTransform(AuraApplication const* aurApp, uint8 mode, bool apply) const
|
||||||
{
|
{
|
||||||
if (!(mode & AURA_EFFECT_HANDLE_SEND_FOR_CLIENT_MASK))
|
if (!(mode & AURA_EFFECT_HANDLE_SEND_FOR_CLIENT_MASK))
|
||||||
@@ -5174,6 +5191,20 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool
|
|||||||
|
|
||||||
Unit* caster = GetCaster();
|
Unit* caster = GetCaster();
|
||||||
|
|
||||||
|
// Fractured: Paragon warrior stance clones (951010-951012) use SPELL_AURA_DUMMY on Spell.dbc **effect2**
|
||||||
|
// (misc = Paragon SpellShapeshiftForm 33-35). Effect1 is a separate DUMMY for the buff strip; passive stats
|
||||||
|
// (e.g. armor pen / threat) live on Effect3. **AttributesEx** clears SPELL_ATTR1_NO_AURA_ICON (DBC + SpellInfoCorrections)
|
||||||
|
// so the client shows an aura icon — stock Warrior stances keep that bit set on purpose.
|
||||||
|
if (GetAuraType() == SPELL_AURA_DUMMY && m_effIndex == 1)
|
||||||
|
{
|
||||||
|
uint32 const sid = GetSpellInfo()->Id;
|
||||||
|
if (sid == 951010 || sid == 951011 || sid == 951012)
|
||||||
|
{
|
||||||
|
Fractured_ApplyShapeshiftFormFromAuraEffect(this, aurApp, mode, apply, ShapeshiftForm(GetMiscValue()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (mode & AURA_EFFECT_HANDLE_REAL)
|
if (mode & AURA_EFFECT_HANDLE_REAL)
|
||||||
{
|
{
|
||||||
// pet auras
|
// pet auras
|
||||||
|
|||||||
@@ -3589,7 +3589,10 @@ SpellCastResult Spell::prepare(SpellCastTargets const* targets, AuraEffect const
|
|||||||
|
|
||||||
// don't allow channeled spells / spells with cast time to be casted while moving
|
// don't allow channeled spells / spells with cast time to be casted while moving
|
||||||
// (even if they are interrupted on moving, spells with almost immediate effect get to have their effect processed before movement interrupter kicks in)
|
// (even if they are interrupted on moving, spells with almost immediate effect get to have their effect processed before movement interrupter kicks in)
|
||||||
|
// Fractured: cast-time mount summons (SPELL_AURA_MOUNTED + non-zero base cast) may be started while moving.
|
||||||
if ((m_spellInfo->IsChanneled() || m_casttime) && m_caster->IsPlayer() && m_caster->isMoving() && m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT && !IsTriggered())
|
if ((m_spellInfo->IsChanneled() || m_casttime) && m_caster->IsPlayer() && m_caster->isMoving() && m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT && !IsTriggered())
|
||||||
|
{
|
||||||
|
if (!m_spellInfo->IsCastTimeRidingMountSpell())
|
||||||
{
|
{
|
||||||
// 1. Has casttime, 2. Or doesn't have flag to allow action during channel
|
// 1. Has casttime, 2. Or doesn't have flag to allow action during channel
|
||||||
if (m_casttime || !m_spellInfo->IsActionAllowedChannel())
|
if (m_casttime || !m_spellInfo->IsActionAllowedChannel())
|
||||||
@@ -3599,6 +3602,7 @@ SpellCastResult Spell::prepare(SpellCastTargets const* targets, AuraEffect const
|
|||||||
return SPELL_FAILED_MOVING;
|
return SPELL_FAILED_MOVING;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// xinef: if spell have nearby target entry only, do not allow to cast if no targets are found
|
// xinef: if spell have nearby target entry only, do not allow to cast if no targets are found
|
||||||
if (m_CastItem)
|
if (m_CastItem)
|
||||||
@@ -4436,9 +4440,11 @@ void Spell::update(uint32 difftime)
|
|||||||
|
|
||||||
// check if the player caster has moved before the spell finished
|
// check if the player caster has moved before the spell finished
|
||||||
// xinef: added preparing state (real cast, skip channels as they have other flags for this)
|
// xinef: added preparing state (real cast, skip channels as they have other flags for this)
|
||||||
|
// Fractured: cast-time mount summons are allowed to complete while moving.
|
||||||
if ((m_caster->IsPlayer() && m_timer != 0) &&
|
if ((m_caster->IsPlayer() && m_timer != 0) &&
|
||||||
m_caster->isMoving() && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT) && m_spellState == SPELL_STATE_PREPARING &&
|
m_caster->isMoving() && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT) && m_spellState == SPELL_STATE_PREPARING &&
|
||||||
(m_spellInfo->Effects[0].Effect != SPELL_EFFECT_STUCK || !m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR)))
|
(m_spellInfo->Effects[0].Effect != SPELL_EFFECT_STUCK || !m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR)) &&
|
||||||
|
!m_spellInfo->IsCastTimeRidingMountSpell())
|
||||||
{
|
{
|
||||||
// don't cancel for melee, autorepeat, triggered and instant spells
|
// don't cancel for melee, autorepeat, triggered and instant spells
|
||||||
if (!IsNextMeleeSwingSpell() && !IsAutoRepeat() && !IsTriggered())
|
if (!IsNextMeleeSwingSpell() && !IsAutoRepeat() && !IsTriggered())
|
||||||
@@ -5815,6 +5821,10 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* /*param1*/, uint32* /*para
|
|||||||
|
|
||||||
if (reqCombat && m_caster->IsInCombat() && !m_spellInfo->CanBeUsedInCombat())
|
if (reqCombat && m_caster->IsInCombat() && !m_spellInfo->CanBeUsedInCombat())
|
||||||
return SPELL_FAILED_AFFECTING_COMBAT;
|
return SPELL_FAILED_AFFECTING_COMBAT;
|
||||||
|
|
||||||
|
// Fractured: cast-time mount summons cannot be used while in combat (even if DBC omits NOT_IN_COMBAT_ONLY_PEACEFUL).
|
||||||
|
if (reqCombat && m_caster->IsInCombat() && m_spellInfo->IsCastTimeRidingMountSpell())
|
||||||
|
return SPELL_FAILED_AFFECTING_COMBAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Xinef: exploit protection
|
// Xinef: exploit protection
|
||||||
@@ -5841,12 +5851,16 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* /*param1*/, uint32* /*para
|
|||||||
// cancel autorepeat spells if cast start when moving
|
// cancel autorepeat spells if cast start when moving
|
||||||
// (not wand currently autorepeat cast delayed to moving stop anyway in spell update code)
|
// (not wand currently autorepeat cast delayed to moving stop anyway in spell update code)
|
||||||
if (m_caster->IsPlayer() && m_caster->ToPlayer()->isMoving() && !IsTriggered())
|
if (m_caster->IsPlayer() && m_caster->ToPlayer()->isMoving() && !IsTriggered())
|
||||||
|
{
|
||||||
|
// Fractured: cast-time mount summons may be started while moving.
|
||||||
|
if (!m_spellInfo->IsCastTimeRidingMountSpell())
|
||||||
{
|
{
|
||||||
// skip stuck spell to allow use it in falling case and apply spell limitations at movement
|
// skip stuck spell to allow use it in falling case and apply spell limitations at movement
|
||||||
if ((!m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR) || m_spellInfo->Effects[0].Effect != SPELL_EFFECT_STUCK) &&
|
if ((!m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR) || m_spellInfo->Effects[0].Effect != SPELL_EFFECT_STUCK) &&
|
||||||
(IsAutoRepeat() || (m_spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) != 0))
|
(IsAutoRepeat() || (m_spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) != 0))
|
||||||
return SPELL_FAILED_MOVING;
|
return SPELL_FAILED_MOVING;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Vehicle* vehicle = m_caster->GetVehicle();
|
Vehicle* vehicle = m_caster->GetVehicle();
|
||||||
if (vehicle && !HasTriggeredCastFlag(TRIGGERED_IGNORE_CASTER_MOUNTED_OR_ON_VEHICLE))
|
if (vehicle && !HasTriggeredCastFlag(TRIGGERED_IGNORE_CASTER_MOUNTED_OR_ON_VEHICLE))
|
||||||
|
|||||||
@@ -909,6 +909,15 @@ bool SpellInfo::HasAura(AuraType aura) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SpellInfo::IsCastTimeRidingMountSpell() const
|
||||||
|
{
|
||||||
|
if (IsChanneled())
|
||||||
|
return false;
|
||||||
|
if (!HasAura(SPELL_AURA_MOUNTED))
|
||||||
|
return false;
|
||||||
|
return CalcCastTime(nullptr, nullptr) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool SpellInfo::HasAnyAura() const
|
bool SpellInfo::HasAnyAura() const
|
||||||
{
|
{
|
||||||
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
||||||
@@ -2147,6 +2156,10 @@ SpellSpecificType SpellInfo::LoadSpellSpecific() const
|
|||||||
{
|
{
|
||||||
case SPELLFAMILY_GENERIC:
|
case SPELLFAMILY_GENERIC:
|
||||||
{
|
{
|
||||||
|
// Fractured / Paragon: DK presence advancement clones (SpellFamilyName=0 in Spell.dbc for client UX).
|
||||||
|
if (Id == 951013 || Id == 951014 || Id == 951015)
|
||||||
|
return SPELL_SPECIFIC_PRESENCE;
|
||||||
|
|
||||||
// Food / Drinks (mostly)
|
// Food / Drinks (mostly)
|
||||||
if (AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED)
|
if (AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -434,6 +434,9 @@ public:
|
|||||||
bool HasEffect(SpellEffects effect) const;
|
bool HasEffect(SpellEffects effect) const;
|
||||||
bool HasEffectMechanic(Mechanics mechanic) const;
|
bool HasEffectMechanic(Mechanics mechanic) const;
|
||||||
bool HasAura(AuraType aura) const;
|
bool HasAura(AuraType aura) const;
|
||||||
|
/// Summon mount aura (SPELL_AURA_MOUNTED) with a non-zero base cast time from SpellCastTimes.dbc.
|
||||||
|
/// Used by Fractured mount rules: castable while moving, never in combat, interrupted on combat enter.
|
||||||
|
bool IsCastTimeRidingMountSpell() const;
|
||||||
bool HasAnyAura() const;
|
bool HasAnyAura() const;
|
||||||
bool HasAreaAuraEffect() const;
|
bool HasAreaAuraEffect() const;
|
||||||
bool HasOnlyDamageEffects() const;
|
bool HasOnlyDamageEffects() const;
|
||||||
|
|||||||
@@ -5427,11 +5427,13 @@ void SpellMgr::LoadSpellInfoCorrections()
|
|||||||
2457, // Battle Stance
|
2457, // Battle Stance
|
||||||
71, // Defensive Stance
|
71, // Defensive Stance
|
||||||
2458, // Berserker Stance
|
2458, // Berserker Stance
|
||||||
|
951010, 951011, 951012, // Paragon advancement warrior stance clones
|
||||||
|
|
||||||
// Death Knight Presences.
|
// Death Knight Presences.
|
||||||
48266, // Blood Presence
|
48266, // Blood Presence
|
||||||
48263, // Frost Presence
|
48263, // Frost Presence
|
||||||
48265, // Unholy Presence
|
48265, // Unholy Presence
|
||||||
|
951013, 951014, 951015, // Paragon advancement DK presence clones (SpellFamily GENERIC in Spell.dbc)
|
||||||
|
|
||||||
// Hunter Aspects -- every rank, since AC stores the per-rank
|
// Hunter Aspects -- every rank, since AC stores the per-rank
|
||||||
// SpellInfo as separate objects and `Category` lives on each.
|
// SpellInfo as separate objects and `Category` lives on each.
|
||||||
@@ -5485,14 +5487,33 @@ void SpellMgr::LoadSpellInfoCorrections()
|
|||||||
2457, // Battle Stance
|
2457, // Battle Stance
|
||||||
71, // Defensive Stance
|
71, // Defensive Stance
|
||||||
2458, // Berserker Stance
|
2458, // Berserker Stance
|
||||||
|
951010, 951011, 951012, // Paragon advancement warrior stance clones
|
||||||
48266, // Blood Presence
|
48266, // Blood Presence
|
||||||
48263, // Frost Presence
|
48263, // Frost Presence
|
||||||
48265, // Unholy Presence
|
48265, // Unholy Presence
|
||||||
|
951013, 951014, 951015, // Paragon advancement DK presence clones (SpellFamily GENERIC in Spell.dbc)
|
||||||
}, [](SpellInfo* spellInfo)
|
}, [](SpellInfo* spellInfo)
|
||||||
{
|
{
|
||||||
spellInfo->AttributesEx6 &= ~SPELL_ATTR6_ALLOW_WHILE_RIDING_VEHICLE;
|
spellInfo->AttributesEx6 &= ~SPELL_ATTR6_ALLOW_WHILE_RIDING_VEHICLE;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Fractured / Paragon: advancement warrior stance clones — strip SPELL_ATTR1_NO_AURA_ICON
|
||||||
|
// (copied from stock 2457/71/2458). Stock Warrior stances intentionally hide from the default aura bar;
|
||||||
|
// these clones are meant to show a cancellable buff icon instead. Client Spell.dbc is patched in tandem via
|
||||||
|
// fractured-tooling/_patch_spell_dbc_paragon_stance_presence_clones.py.
|
||||||
|
ApplySpellFix({ 951010, 951011, 951012 }, [](SpellInfo* spellInfo)
|
||||||
|
{
|
||||||
|
spellInfo->AttributesEx &= ~SPELL_ATTR1_NO_AURA_ICON;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fractured / Paragon: advancement DK presence clones — strip SPELL_ATTR2_USE_SHAPESHIFT_BAR (0x10) copied
|
||||||
|
// from 48266/48263/48265. That client-only bit is what parks a spell on the secondary stance bar above the
|
||||||
|
// action bar; SkillLine / SpellFamily alone do not remove it. Spellbook tabs still come from SkillLines 770/771/772.
|
||||||
|
ApplySpellFix({ 951013, 951014, 951015 }, [](SpellInfo* spellInfo)
|
||||||
|
{
|
||||||
|
spellInfo->AttributesEx2 &= ~SPELL_ATTR2_USE_SHAPESHIFT_BAR;
|
||||||
|
});
|
||||||
|
|
||||||
// Fractured: strip reagent requirements from every player-class spell at
|
// Fractured: strip reagent requirements from every player-class spell at
|
||||||
// load time. Filtered by SpellFamilyName != 0 so that profession spells
|
// load time. Filtered by SpellFamilyName != 0 so that profession spells
|
||||||
// (cooking, alchemy, enchanting, blacksmithing, jewelcrafting, leatherworking,
|
// (cooking, alchemy, enchanting, blacksmithing, jewelcrafting, leatherworking,
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ void WorldConfig::BuildConfigCache()
|
|||||||
SetConfigValue<bool>(CONFIG_ALLOW_PLAYER_COMMANDS, "AllowPlayerCommands", 1);
|
SetConfigValue<bool>(CONFIG_ALLOW_PLAYER_COMMANDS, "AllowPlayerCommands", 1);
|
||||||
SetConfigValue<bool>(CONFIG_PRESERVE_CUSTOM_CHANNELS, "PreserveCustomChannels", false);
|
SetConfigValue<bool>(CONFIG_PRESERVE_CUSTOM_CHANNELS, "PreserveCustomChannels", false);
|
||||||
SetConfigValue<uint32>(CONFIG_PRESERVE_CUSTOM_CHANNEL_DURATION, "PreserveCustomChannelDuration", 14);
|
SetConfigValue<uint32>(CONFIG_PRESERVE_CUSTOM_CHANNEL_DURATION, "PreserveCustomChannelDuration", 14);
|
||||||
SetConfigValue<uint32>(CONFIG_INTERVAL_SAVE, "PlayerSaveInterval", 900000);
|
SetConfigValue<uint32>(CONFIG_INTERVAL_SAVE, "PlayerSaveInterval", 300000);
|
||||||
SetConfigValue<uint32>(CONFIG_INTERVAL_DISCONNECT_TOLERANCE, "DisconnectToleranceInterval", 0);
|
SetConfigValue<uint32>(CONFIG_INTERVAL_DISCONNECT_TOLERANCE, "DisconnectToleranceInterval", 0);
|
||||||
SetConfigValue<bool>(CONFIG_STATS_SAVE_ONLY_ON_LOGOUT, "PlayerSave.Stats.SaveOnlyOnLogout", true);
|
SetConfigValue<bool>(CONFIG_STATS_SAVE_ONLY_ON_LOGOUT, "PlayerSave.Stats.SaveOnlyOnLogout", true);
|
||||||
SetConfigValue<bool>(CONFIG_VALIDATE_SKILL_LEARNED_BY_SPELLS, "ValidateSkillLearnedBySpells", true);
|
SetConfigValue<bool>(CONFIG_VALIDATE_SKILL_LEARNED_BY_SPELLS, "ValidateSkillLearnedBySpells", true);
|
||||||
@@ -270,7 +270,8 @@ void WorldConfig::BuildConfigCache()
|
|||||||
SetConfigValue<uint32>(CONFIG_INSTANCE_RESET_TIME_RELATIVE_TIMESTAMP, "Instance.ResetTimeRelativeTimestamp", 1135814400);
|
SetConfigValue<uint32>(CONFIG_INSTANCE_RESET_TIME_RELATIVE_TIMESTAMP, "Instance.ResetTimeRelativeTimestamp", 1135814400);
|
||||||
SetConfigValue<uint32>(CONFIG_INSTANCE_UNLOAD_DELAY, "Instance.UnloadDelay", 1800000);
|
SetConfigValue<uint32>(CONFIG_INSTANCE_UNLOAD_DELAY, "Instance.UnloadDelay", 1800000);
|
||||||
|
|
||||||
SetConfigValue<uint32>(CONFIG_MAX_PRIMARY_TRADE_SKILL, "MaxPrimaryTradeSkill", 2);
|
// WotLK has 11 primary profession skill lines (gathering + crafting); secondary (Cooking, Fishing, First Aid) are not limited here.
|
||||||
|
SetConfigValue<uint32>(CONFIG_MAX_PRIMARY_TRADE_SKILL, "MaxPrimaryTradeSkill", 11);
|
||||||
SetConfigValue<uint32>(CONFIG_MIN_PETITION_SIGNS, "MinPetitionSigns", 9, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= 9; }, "<= 9");
|
SetConfigValue<uint32>(CONFIG_MIN_PETITION_SIGNS, "MinPetitionSigns", 9, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= 9; }, "<= 9");
|
||||||
|
|
||||||
SetConfigValue<uint32>(CONFIG_GM_LOGIN_STATE, "GM.LoginState", 2);
|
SetConfigValue<uint32>(CONFIG_GM_LOGIN_STATE, "GM.LoginState", 2);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "CommandScript.h"
|
#include "CommandScript.h"
|
||||||
|
#include "DBCStores.h"
|
||||||
#include "Language.h"
|
#include "Language.h"
|
||||||
#include "ObjectMgr.h"
|
#include "ObjectMgr.h"
|
||||||
#include "Pet.h"
|
#include "Pet.h"
|
||||||
@@ -51,6 +52,7 @@ public:
|
|||||||
{ "default", HandleLearnAllDefaultCommand, rbac::RBAC_PERM_COMMAND_LEARN_ALL_DEFAULT, Console::No },
|
{ "default", HandleLearnAllDefaultCommand, rbac::RBAC_PERM_COMMAND_LEARN_ALL_DEFAULT, Console::No },
|
||||||
{ "lang", HandleLearnAllLangCommand, rbac::RBAC_PERM_COMMAND_LEARN_ALL_LANG, Console::No },
|
{ "lang", HandleLearnAllLangCommand, rbac::RBAC_PERM_COMMAND_LEARN_ALL_LANG, Console::No },
|
||||||
{ "recipes", HandleLearnAllRecipesCommand, rbac::RBAC_PERM_COMMAND_LEARN_ALL_RECIPES, Console::No },
|
{ "recipes", HandleLearnAllRecipesCommand, rbac::RBAC_PERM_COMMAND_LEARN_ALL_RECIPES, Console::No },
|
||||||
|
{ "mounts", HandleLearnAllMountsCommand, rbac::RBAC_PERM_COMMAND_LEARN_ALL_MOUNTS, Console::No },
|
||||||
};
|
};
|
||||||
|
|
||||||
static ChatCommandTable learnCommandTable =
|
static ChatCommandTable learnCommandTable =
|
||||||
@@ -386,6 +388,66 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void SetRidingSkillToMaxForPlayer(Player* player)
|
||||||
|
{
|
||||||
|
SkillRaceClassInfoEntry const* rcInfo = GetSkillRaceClassInfo(SKILL_RIDING, player->getRace(), player->getClass());
|
||||||
|
if (!rcInfo || GetSkillRangeType(rcInfo) != SKILL_RANGE_RANK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SkillTiersEntry const* tier = sSkillTiersStore.LookupEntry(rcInfo->SkillTierID);
|
||||||
|
if (!tier)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint8 rank = 0;
|
||||||
|
uint16 maxValue = 0;
|
||||||
|
for (uint8 i = 0; i < MAX_SKILL_STEP; ++i)
|
||||||
|
{
|
||||||
|
if (tier->Value[i] == 0)
|
||||||
|
continue;
|
||||||
|
rank = i + 1;
|
||||||
|
maxValue = tier->Value[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rank || !maxValue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
player->SetSkill(SKILL_RIDING, rank, maxValue, maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HandleLearnAllMountsCommand(ChatHandler* handler)
|
||||||
|
{
|
||||||
|
Player* target = handler->getSelectedPlayer();
|
||||||
|
if (!target)
|
||||||
|
{
|
||||||
|
handler->SendSysMessage(LANG_PLAYER_NOT_FOUND);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetRidingSkillToMaxForPlayer(target);
|
||||||
|
handler->PSendSysMessage("Set Riding skill to maximum for {}.", handler->GetNameLink(target));
|
||||||
|
|
||||||
|
uint32 learned = 0;
|
||||||
|
for (uint32 i = 0; i < sSpellMgr->GetSpellInfoStoreSize(); ++i)
|
||||||
|
{
|
||||||
|
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(i);
|
||||||
|
if (!spellInfo || !SpellMgr::IsSpellValid(spellInfo))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!spellInfo->HasAura(SPELL_AURA_MOUNTED))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (target->HasSpell(i))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
target->learnSpell(i, false);
|
||||||
|
if (target->HasSpell(i))
|
||||||
|
++learned;
|
||||||
|
}
|
||||||
|
|
||||||
|
handler->PSendSysMessage("Learned {} mount spell(s) for {}.", learned, handler->GetNameLink(target));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static void HandleLearnSkillRecipesHelper(Player* player, uint32 skillId)
|
static void HandleLearnSkillRecipesHelper(Player* player, uint32 skillId)
|
||||||
{
|
{
|
||||||
uint32 classmask = player->getClassMask();
|
uint32 classmask = player->getClassMask();
|
||||||
|
|||||||
@@ -66,6 +66,9 @@ enum DeathKnightSpells
|
|||||||
SPELL_DK_ITEM_T8_MELEE_4P_BONUS = 64736,
|
SPELL_DK_ITEM_T8_MELEE_4P_BONUS = 64736,
|
||||||
SPELL_DK_MASTER_OF_GHOULS = 52143,
|
SPELL_DK_MASTER_OF_GHOULS = 52143,
|
||||||
SPELL_DK_BLOOD_PLAGUE = 55078,
|
SPELL_DK_BLOOD_PLAGUE = 55078,
|
||||||
|
// Fractured / Paragon: stock Priest Devouring Plague vs Character Advancement multidot clone
|
||||||
|
SPELL_PRIEST_DEVOURING_PLAGUE_R1 = 2944,
|
||||||
|
SPELL_PARAGON_MULTIDOT_DEVOURING_PLAGUE_R1 = 951000,
|
||||||
SPELL_DK_RAISE_DEAD_USE_REAGENT = 48289,
|
SPELL_DK_RAISE_DEAD_USE_REAGENT = 48289,
|
||||||
SPELL_DK_RUNIC_POWER_ENERGIZE = 49088,
|
SPELL_DK_RUNIC_POWER_ENERGIZE = 49088,
|
||||||
SPELL_DK_SCENT_OF_BLOOD = 50422,
|
SPELL_DK_SCENT_OF_BLOOD = 50422,
|
||||||
@@ -107,6 +110,10 @@ enum DeathKnightSpells
|
|||||||
SPELL_DK_RUNE_STRIKE_OFF_HAND_R1 = 66217,
|
SPELL_DK_RUNE_STRIKE_OFF_HAND_R1 = 66217,
|
||||||
SPELL_DK_BLOOD_STRIKE_OFF_HAND_R1 = 66215,
|
SPELL_DK_BLOOD_STRIKE_OFF_HAND_R1 = 66215,
|
||||||
SPELL_DK_KILLING_MACHINE = 51124,
|
SPELL_DK_KILLING_MACHINE = 51124,
|
||||||
|
// Fractured / Paragon: Character Advancement DK presence clones (SpellFamily GENERIC in Spell.dbc).
|
||||||
|
SPELL_PARAGON_ADV_BLOOD_PRESENCE = 951013,
|
||||||
|
SPELL_PARAGON_ADV_FROST_PRESENCE = 951014,
|
||||||
|
SPELL_PARAGON_ADV_UNHOLY_PRESENCE = 951015,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum DeathKnightSpellIcons
|
enum DeathKnightSpellIcons
|
||||||
@@ -126,6 +133,21 @@ enum Misc
|
|||||||
NPC_RISEN_ALLY = 30230
|
NPC_RISEN_ALLY = 30230
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline bool Fractured_UnitHasBloodPresenceAura(Unit const* unit)
|
||||||
|
{
|
||||||
|
return unit->HasAura(SPELL_DK_BLOOD_PRESENCE) || unit->HasAura(SPELL_PARAGON_ADV_BLOOD_PRESENCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool Fractured_UnitHasFrostPresenceAura(Unit const* unit)
|
||||||
|
{
|
||||||
|
return unit->HasAura(SPELL_DK_FROST_PRESENCE) || unit->HasAura(SPELL_PARAGON_ADV_FROST_PRESENCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool Fractured_UnitHasUnholyPresenceAura(Unit const* unit)
|
||||||
|
{
|
||||||
|
return unit->HasAura(SPELL_DK_UNHOLY_PRESENCE) || unit->HasAura(SPELL_PARAGON_ADV_UNHOLY_PRESENCE);
|
||||||
|
}
|
||||||
|
|
||||||
// 50526 - Wandering Plague
|
// 50526 - Wandering Plague
|
||||||
class spell_dk_wandering_plague : public SpellScript
|
class spell_dk_wandering_plague : public SpellScript
|
||||||
{
|
{
|
||||||
@@ -1797,8 +1819,11 @@ class spell_dk_improved_blood_presence : public AuraScript
|
|||||||
return ValidateSpellInfo(
|
return ValidateSpellInfo(
|
||||||
{
|
{
|
||||||
SPELL_DK_BLOOD_PRESENCE,
|
SPELL_DK_BLOOD_PRESENCE,
|
||||||
|
SPELL_PARAGON_ADV_BLOOD_PRESENCE,
|
||||||
SPELL_DK_FROST_PRESENCE,
|
SPELL_DK_FROST_PRESENCE,
|
||||||
|
SPELL_PARAGON_ADV_FROST_PRESENCE,
|
||||||
SPELL_DK_UNHOLY_PRESENCE,
|
SPELL_DK_UNHOLY_PRESENCE,
|
||||||
|
SPELL_PARAGON_ADV_UNHOLY_PRESENCE,
|
||||||
SPELL_DK_IMPROVED_BLOOD_PRESENCE_TRIGGERED
|
SPELL_DK_IMPROVED_BLOOD_PRESENCE_TRIGGERED
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1806,14 +1831,14 @@ class spell_dk_improved_blood_presence : public AuraScript
|
|||||||
void HandleEffectApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
|
void HandleEffectApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
|
||||||
{
|
{
|
||||||
Unit* target = GetTarget();
|
Unit* target = GetTarget();
|
||||||
if (target->HasAnyAuras(SPELL_DK_FROST_PRESENCE, SPELL_DK_UNHOLY_PRESENCE) && !target->HasAura(SPELL_DK_IMPROVED_BLOOD_PRESENCE_TRIGGERED))
|
if (target->HasAnyAuras(SPELL_DK_FROST_PRESENCE, SPELL_DK_UNHOLY_PRESENCE, SPELL_PARAGON_ADV_FROST_PRESENCE, SPELL_PARAGON_ADV_UNHOLY_PRESENCE) && !target->HasAura(SPELL_DK_IMPROVED_BLOOD_PRESENCE_TRIGGERED))
|
||||||
target->CastCustomSpell(SPELL_DK_IMPROVED_BLOOD_PRESENCE_TRIGGERED, SPELLVALUE_BASE_POINT1, aurEff->GetAmount(), target, true, nullptr, aurEff);
|
target->CastCustomSpell(SPELL_DK_IMPROVED_BLOOD_PRESENCE_TRIGGERED, SPELLVALUE_BASE_POINT1, aurEff->GetAmount(), target, true, nullptr, aurEff);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
|
void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
|
||||||
{
|
{
|
||||||
Unit* target = GetTarget();
|
Unit* target = GetTarget();
|
||||||
if (!target->HasAura(SPELL_DK_BLOOD_PRESENCE))
|
if (!Fractured_UnitHasBloodPresenceAura(target))
|
||||||
target->RemoveAura(SPELL_DK_IMPROVED_BLOOD_PRESENCE_TRIGGERED);
|
target->RemoveAura(SPELL_DK_IMPROVED_BLOOD_PRESENCE_TRIGGERED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1834,8 +1859,11 @@ class spell_dk_improved_frost_presence : public AuraScript
|
|||||||
return ValidateSpellInfo(
|
return ValidateSpellInfo(
|
||||||
{
|
{
|
||||||
SPELL_DK_BLOOD_PRESENCE,
|
SPELL_DK_BLOOD_PRESENCE,
|
||||||
|
SPELL_PARAGON_ADV_BLOOD_PRESENCE,
|
||||||
SPELL_DK_FROST_PRESENCE,
|
SPELL_DK_FROST_PRESENCE,
|
||||||
|
SPELL_PARAGON_ADV_FROST_PRESENCE,
|
||||||
SPELL_DK_UNHOLY_PRESENCE,
|
SPELL_DK_UNHOLY_PRESENCE,
|
||||||
|
SPELL_PARAGON_ADV_UNHOLY_PRESENCE,
|
||||||
SPELL_DK_FROST_PRESENCE_TRIGGERED
|
SPELL_DK_FROST_PRESENCE_TRIGGERED
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1843,14 +1871,14 @@ class spell_dk_improved_frost_presence : public AuraScript
|
|||||||
void HandleEffectApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
|
void HandleEffectApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
|
||||||
{
|
{
|
||||||
Unit* target = GetTarget();
|
Unit* target = GetTarget();
|
||||||
if (target->HasAnyAuras(SPELL_DK_BLOOD_PRESENCE, SPELL_DK_UNHOLY_PRESENCE) && !target->HasAura(SPELL_DK_FROST_PRESENCE_TRIGGERED))
|
if (target->HasAnyAuras(SPELL_DK_BLOOD_PRESENCE, SPELL_DK_UNHOLY_PRESENCE, SPELL_PARAGON_ADV_BLOOD_PRESENCE, SPELL_PARAGON_ADV_UNHOLY_PRESENCE) && !target->HasAura(SPELL_DK_FROST_PRESENCE_TRIGGERED))
|
||||||
target->CastCustomSpell(SPELL_DK_FROST_PRESENCE_TRIGGERED, SPELLVALUE_BASE_POINT0, aurEff->GetAmount(), target, true, nullptr, aurEff);
|
target->CastCustomSpell(SPELL_DK_FROST_PRESENCE_TRIGGERED, SPELLVALUE_BASE_POINT0, aurEff->GetAmount(), target, true, nullptr, aurEff);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
|
void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
|
||||||
{
|
{
|
||||||
Unit* target = GetTarget();
|
Unit* target = GetTarget();
|
||||||
if (!target->HasAura(SPELL_DK_FROST_PRESENCE))
|
if (!Fractured_UnitHasFrostPresenceAura(target))
|
||||||
target->RemoveAura(SPELL_DK_FROST_PRESENCE_TRIGGERED);
|
target->RemoveAura(SPELL_DK_FROST_PRESENCE_TRIGGERED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1871,8 +1899,11 @@ class spell_dk_improved_unholy_presence : public AuraScript
|
|||||||
return ValidateSpellInfo(
|
return ValidateSpellInfo(
|
||||||
{
|
{
|
||||||
SPELL_DK_BLOOD_PRESENCE,
|
SPELL_DK_BLOOD_PRESENCE,
|
||||||
|
SPELL_PARAGON_ADV_BLOOD_PRESENCE,
|
||||||
SPELL_DK_FROST_PRESENCE,
|
SPELL_DK_FROST_PRESENCE,
|
||||||
|
SPELL_PARAGON_ADV_FROST_PRESENCE,
|
||||||
SPELL_DK_UNHOLY_PRESENCE,
|
SPELL_DK_UNHOLY_PRESENCE,
|
||||||
|
SPELL_PARAGON_ADV_UNHOLY_PRESENCE,
|
||||||
SPELL_DK_IMPROVED_UNHOLY_PRESENCE_TRIGGERED,
|
SPELL_DK_IMPROVED_UNHOLY_PRESENCE_TRIGGERED,
|
||||||
SPELL_DK_UNHOLY_PRESENCE_TRIGGERED
|
SPELL_DK_UNHOLY_PRESENCE_TRIGGERED
|
||||||
});
|
});
|
||||||
@@ -1881,14 +1912,14 @@ class spell_dk_improved_unholy_presence : public AuraScript
|
|||||||
void HandleEffectApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
|
void HandleEffectApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
|
||||||
{
|
{
|
||||||
Unit* target = GetTarget();
|
Unit* target = GetTarget();
|
||||||
if (target->HasAura(SPELL_DK_UNHOLY_PRESENCE) && !target->HasAura(SPELL_DK_IMPROVED_UNHOLY_PRESENCE_TRIGGERED))
|
if (Fractured_UnitHasUnholyPresenceAura(target) && !target->HasAura(SPELL_DK_IMPROVED_UNHOLY_PRESENCE_TRIGGERED))
|
||||||
{
|
{
|
||||||
// Not listed as any effect, only base points set in dbc
|
// Not listed as any effect, only base points set in dbc
|
||||||
int32 basePoints = GetSpellInfo()->Effects[EFFECT_1].CalcValue();
|
int32 basePoints = GetSpellInfo()->Effects[EFFECT_1].CalcValue();
|
||||||
target->CastCustomSpell(target, SPELL_DK_IMPROVED_UNHOLY_PRESENCE_TRIGGERED, &basePoints, &basePoints, &basePoints, true, nullptr, aurEff);
|
target->CastCustomSpell(target, SPELL_DK_IMPROVED_UNHOLY_PRESENCE_TRIGGERED, &basePoints, &basePoints, &basePoints, true, nullptr, aurEff);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target->HasAnyAuras(SPELL_DK_BLOOD_PRESENCE, SPELL_DK_FROST_PRESENCE) && !target->HasAura(SPELL_DK_UNHOLY_PRESENCE_TRIGGERED))
|
if (target->HasAnyAuras(SPELL_DK_BLOOD_PRESENCE, SPELL_DK_FROST_PRESENCE, SPELL_PARAGON_ADV_BLOOD_PRESENCE, SPELL_PARAGON_ADV_FROST_PRESENCE) && !target->HasAura(SPELL_DK_UNHOLY_PRESENCE_TRIGGERED))
|
||||||
target->CastCustomSpell(SPELL_DK_UNHOLY_PRESENCE_TRIGGERED, SPELLVALUE_BASE_POINT0, aurEff->GetAmount(), target, true, nullptr, aurEff);
|
target->CastCustomSpell(SPELL_DK_UNHOLY_PRESENCE_TRIGGERED, SPELLVALUE_BASE_POINT0, aurEff->GetAmount(), target, true, nullptr, aurEff);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1898,7 +1929,7 @@ class spell_dk_improved_unholy_presence : public AuraScript
|
|||||||
|
|
||||||
target->RemoveAura(SPELL_DK_IMPROVED_UNHOLY_PRESENCE_TRIGGERED);
|
target->RemoveAura(SPELL_DK_IMPROVED_UNHOLY_PRESENCE_TRIGGERED);
|
||||||
|
|
||||||
if (!target->HasAura(SPELL_DK_UNHOLY_PRESENCE))
|
if (!Fractured_UnitHasUnholyPresenceAura(target))
|
||||||
target->RemoveAura(SPELL_DK_UNHOLY_PRESENCE_TRIGGERED);
|
target->RemoveAura(SPELL_DK_UNHOLY_PRESENCE_TRIGGERED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1939,12 +1970,12 @@ class spell_dk_pestilence : public SpellScript
|
|||||||
// and Unit::GetDiseasesByCaster already counts it for Paragon callers
|
// and Unit::GetDiseasesByCaster already counts it for Paragon callers
|
||||||
// (see Unit.cpp), so it is conceptually a disease; stock Pestilence
|
// (see Unit.cpp), so it is conceptually a disease; stock Pestilence
|
||||||
// just hard-codes Blood Plague + Frost Fever and so silently drops it.
|
// just hard-codes Blood Plague + Frost Fever and so silently drops it.
|
||||||
// GetAuraOfRankedSpell with the rank-1 id (2944) covers every rank of
|
// GetAuraOfRankedSpell with the rank-1 id (2944 / 951000) covers every rank of
|
||||||
// Devouring Plague the player has on the target -- we re-cast that
|
// Devouring Plague the player has on the target -- we re-cast that
|
||||||
// exact same rank so the spread copy carries the caster's actual
|
// exact same rank so the spread copy carries the caster's actual
|
||||||
// damage tier rather than always rank 1. Stock DKs cannot cast
|
// damage tier rather than always rank 1. Stock DKs cannot cast
|
||||||
// Devouring Plague at all, so the GetAuraOfRankedSpell will return
|
// Devouring Plague at all, so both lookups return null for them and
|
||||||
// null for them and this branch is a no-op there.
|
// this branch is a no-op there.
|
||||||
bool const paragonSpread = IsParagonWildcardCaller(caster);
|
bool const paragonSpread = IsParagonWildcardCaller(caster);
|
||||||
|
|
||||||
// Spread on others
|
// Spread on others
|
||||||
@@ -1958,11 +1989,17 @@ class spell_dk_pestilence : public SpellScript
|
|||||||
if (target->GetAura(SPELL_DK_FROST_FEVER, caster->GetGUID()))
|
if (target->GetAura(SPELL_DK_FROST_FEVER, caster->GetGUID()))
|
||||||
caster->CastSpell(hitUnit, SPELL_DK_FROST_FEVER, true);
|
caster->CastSpell(hitUnit, SPELL_DK_FROST_FEVER, true);
|
||||||
|
|
||||||
// Fractured / Paragon: Devouring Plague spread.
|
// Fractured / Paragon: Devouring Plague spread (stock 2944 chain or
|
||||||
|
// Character Advancement multidot clone 951000 chain).
|
||||||
if (paragonSpread)
|
if (paragonSpread)
|
||||||
if (Aura const* dp = target->GetAuraOfRankedSpell(2944 /* Devouring Plague r1 */, caster->GetGUID()))
|
{
|
||||||
|
Aura const* dp = target->GetAuraOfRankedSpell(SPELL_PRIEST_DEVOURING_PLAGUE_R1, caster->GetGUID());
|
||||||
|
if (!dp)
|
||||||
|
dp = target->GetAuraOfRankedSpell(SPELL_PARAGON_MULTIDOT_DEVOURING_PLAGUE_R1, caster->GetGUID());
|
||||||
|
if (dp)
|
||||||
caster->CastSpell(hitUnit, dp->GetId(), true);
|
caster->CastSpell(hitUnit, dp->GetId(), true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Refresh on target
|
// Refresh on target
|
||||||
else if (caster->GetAura(SPELL_DK_GLYPH_OF_DISEASE))
|
else if (caster->GetAura(SPELL_DK_GLYPH_OF_DISEASE))
|
||||||
{
|
{
|
||||||
@@ -1985,10 +2022,15 @@ class spell_dk_pestilence : public SpellScript
|
|||||||
|
|
||||||
// Fractured / Paragon: Devouring Plague Glyph-of-Disease refresh.
|
// Fractured / Paragon: Devouring Plague Glyph-of-Disease refresh.
|
||||||
if (paragonSpread)
|
if (paragonSpread)
|
||||||
if (Aura* dp = target->GetAuraOfRankedSpell(2944 /* Devouring Plague r1 */, caster->GetGUID()))
|
{
|
||||||
|
Aura* dp = target->GetAuraOfRankedSpell(SPELL_PRIEST_DEVOURING_PLAGUE_R1, caster->GetGUID());
|
||||||
|
if (!dp)
|
||||||
|
dp = target->GetAuraOfRankedSpell(SPELL_PARAGON_MULTIDOT_DEVOURING_PLAGUE_R1, caster->GetGUID());
|
||||||
|
if (dp)
|
||||||
dp->RefreshDuration();
|
dp->RefreshDuration();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Register() override
|
void Register() override
|
||||||
{
|
{
|
||||||
@@ -2010,6 +2052,9 @@ class spell_dk_presence : public AuraScript
|
|||||||
SPELL_DK_BLOOD_PRESENCE,
|
SPELL_DK_BLOOD_PRESENCE,
|
||||||
SPELL_DK_FROST_PRESENCE,
|
SPELL_DK_FROST_PRESENCE,
|
||||||
SPELL_DK_UNHOLY_PRESENCE,
|
SPELL_DK_UNHOLY_PRESENCE,
|
||||||
|
SPELL_PARAGON_ADV_BLOOD_PRESENCE,
|
||||||
|
SPELL_PARAGON_ADV_FROST_PRESENCE,
|
||||||
|
SPELL_PARAGON_ADV_UNHOLY_PRESENCE,
|
||||||
SPELL_DK_IMPROVED_BLOOD_PRESENCE_R1,
|
SPELL_DK_IMPROVED_BLOOD_PRESENCE_R1,
|
||||||
SPELL_DK_IMPROVED_FROST_PRESENCE_R1,
|
SPELL_DK_IMPROVED_FROST_PRESENCE_R1,
|
||||||
SPELL_DK_IMPROVED_UNHOLY_PRESENCE_R1,
|
SPELL_DK_IMPROVED_UNHOLY_PRESENCE_R1,
|
||||||
@@ -2024,7 +2069,7 @@ class spell_dk_presence : public AuraScript
|
|||||||
{
|
{
|
||||||
Unit* target = GetTarget();
|
Unit* target = GetTarget();
|
||||||
|
|
||||||
if (GetId() == SPELL_DK_BLOOD_PRESENCE)
|
if (GetId() == SPELL_DK_BLOOD_PRESENCE || GetId() == SPELL_PARAGON_ADV_BLOOD_PRESENCE)
|
||||||
target->CastSpell(target, SPELL_DK_IMPROVED_BLOOD_PRESENCE_TRIGGERED, true);
|
target->CastSpell(target, SPELL_DK_IMPROVED_BLOOD_PRESENCE_TRIGGERED, true);
|
||||||
else if (AuraEffect const* impAurEff = target->GetAuraEffectOfRankedSpell(SPELL_DK_IMPROVED_BLOOD_PRESENCE_R1, EFFECT_0))
|
else if (AuraEffect const* impAurEff = target->GetAuraEffectOfRankedSpell(SPELL_DK_IMPROVED_BLOOD_PRESENCE_R1, EFFECT_0))
|
||||||
if (!target->HasAura(SPELL_DK_IMPROVED_BLOOD_PRESENCE_TRIGGERED))
|
if (!target->HasAura(SPELL_DK_IMPROVED_BLOOD_PRESENCE_TRIGGERED))
|
||||||
@@ -2035,7 +2080,7 @@ class spell_dk_presence : public AuraScript
|
|||||||
{
|
{
|
||||||
Unit* target = GetTarget();
|
Unit* target = GetTarget();
|
||||||
|
|
||||||
if (GetId() == SPELL_DK_FROST_PRESENCE)
|
if (GetId() == SPELL_DK_FROST_PRESENCE || GetId() == SPELL_PARAGON_ADV_FROST_PRESENCE)
|
||||||
target->CastSpell(target, SPELL_DK_FROST_PRESENCE_TRIGGERED, true);
|
target->CastSpell(target, SPELL_DK_FROST_PRESENCE_TRIGGERED, true);
|
||||||
else if (AuraEffect const* impAurEff = target->GetAuraEffectOfRankedSpell(SPELL_DK_IMPROVED_FROST_PRESENCE_R1, EFFECT_0))
|
else if (AuraEffect const* impAurEff = target->GetAuraEffectOfRankedSpell(SPELL_DK_IMPROVED_FROST_PRESENCE_R1, EFFECT_0))
|
||||||
if (!target->HasAura(SPELL_DK_FROST_PRESENCE_TRIGGERED))
|
if (!target->HasAura(SPELL_DK_FROST_PRESENCE_TRIGGERED))
|
||||||
@@ -2046,12 +2091,12 @@ class spell_dk_presence : public AuraScript
|
|||||||
{
|
{
|
||||||
Unit* target = GetTarget();
|
Unit* target = GetTarget();
|
||||||
|
|
||||||
if (GetId() == SPELL_DK_UNHOLY_PRESENCE)
|
if (GetId() == SPELL_DK_UNHOLY_PRESENCE || GetId() == SPELL_PARAGON_ADV_UNHOLY_PRESENCE)
|
||||||
target->CastSpell(target, SPELL_DK_UNHOLY_PRESENCE_TRIGGERED, true);
|
target->CastSpell(target, SPELL_DK_UNHOLY_PRESENCE_TRIGGERED, true);
|
||||||
|
|
||||||
if (AuraEffect const* impAurEff = target->GetAuraEffectOfRankedSpell(SPELL_DK_IMPROVED_UNHOLY_PRESENCE_R1, EFFECT_0))
|
if (AuraEffect const* impAurEff = target->GetAuraEffectOfRankedSpell(SPELL_DK_IMPROVED_UNHOLY_PRESENCE_R1, EFFECT_0))
|
||||||
{
|
{
|
||||||
if (GetId() == SPELL_DK_UNHOLY_PRESENCE)
|
if (GetId() == SPELL_DK_UNHOLY_PRESENCE || GetId() == SPELL_PARAGON_ADV_UNHOLY_PRESENCE)
|
||||||
{
|
{
|
||||||
// Not listed as any effect, only base points set
|
// Not listed as any effect, only base points set
|
||||||
int32 bp = impAurEff->GetSpellInfo()->Effects[EFFECT_1].CalcValue();
|
int32 bp = impAurEff->GetSpellInfo()->Effects[EFFECT_1].CalcValue();
|
||||||
|
|||||||
@@ -115,6 +115,17 @@ CI workflow **Sync release to Gitea** (`.github/workflows/gitea-release-sync.yml
|
|||||||
|
|
||||||
**Manual upload:** `bash scripts/upload-release-to-gitea.sh /path/to/files v1.0.0` with the same env vars as CI.
|
**Manual upload:** `bash scripts/upload-release-to-gitea.sh /path/to/files v1.0.0` with the same env vars as CI.
|
||||||
|
|
||||||
|
**Legacy “bridge” after changing `baked-gitea-channel.js`:** Players still using the old Gitea URL only receive launcher updates from that host. Build **Windows + Linux** installers (e.g. download **Fractured launcher CI** artifacts, or run **`npm run pack:win`** / **`npm run pack:linux`**), put **`dist/`** contents in one folder if needed, then:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export GITEA_BASE_URL=http://your-old-host:port # legacy base, no trailing slash
|
||||||
|
export GITEA_TOKEN=... GITEA_OWNER=Dawnsorrow GITEA_REPO=Fractured-Distro
|
||||||
|
bash tools/fractured-launcher-electron/scripts/gitea-replace-launcher-only.sh \
|
||||||
|
tools/fractured-launcher-electron/dist latest
|
||||||
|
```
|
||||||
|
|
||||||
|
That script deletes only **`Fractured-Launcher*`**, **`latest.yml`** (only if you supply a new **`latest.yml`** in **`dist/`**), **`latest-linux.yml`** (only if supplied), **`*.blockmap`**, and **`builder-debug.yml`** on the release, then uploads the new files — **Wow.exe**, MPQs, and **`patch-manifest.json`** are left alone. If you only built Linux locally, merge **Windows CI `dist/`** files into the same folder first so **`latest.yml`** is not removed without a replacement. Use release tag **`latest`** if that is what **`release_tag`** points at.
|
||||||
|
|
||||||
### Sync did not run / Gitea unchanged — checklist
|
### Sync did not run / Gitea unchanged — checklist
|
||||||
|
|
||||||
1. **Git tag ≠ GitHub Release** — Only **Releases** (published on the GitHub **Releases** page) trigger this workflow. If your teammate only **`git push --tags`**, create a **Release** from that tag and click **Publish** (or run **Actions → Sync release to Gitea → Run workflow** and enter the tag).
|
1. **Git tag ≠ GitHub Release** — Only **Releases** (published on the GitHub **Releases** page) trigger this workflow. If your teammate only **`git push --tags`**, create a **Release** from that tag and click **Publish** (or run **Actions → Sync release to Gitea → Run workflow** and enter the tag).
|
||||||
@@ -130,6 +141,7 @@ CI workflow **Sync release to Gitea** (`.github/workflows/gitea-release-sync.yml
|
|||||||
11. **`EBUSY` / file locked on Windows** — The client (or antivirus) may keep **`.MPQ`** files open. The launcher **retries** for a short window and downloads the new file **before** replacing the old one; if sync still fails, **exit WoW** (and any tool previewing that folder) and run **Download updates** again.
|
11. **`EBUSY` / file locked on Windows** — The client (or antivirus) may keep **`.MPQ`** files open. The launcher **retries** for a short window and downloads the new file **before** replacing the old one; if sync still fails, **exit WoW** (and any tool previewing that folder) and run **Download updates** again.
|
||||||
12. **`.bak-*` clutter** — When **Download updates** finishes without error, the launcher removes matching **`*.bak-YYYYMMDD-HHmmss`** files from earlier runs (same pattern it uses when replacing files). Failed syncs do not delete backups.
|
12. **`.bak-*` clutter** — When **Download updates** finishes without error, the launcher removes matching **`*.bak-YYYYMMDD-HHmmss`** files from earlier runs (same pattern it uses when replacing files). Failed syncs do not delete backups.
|
||||||
13. **Gitea still shows an old launcher version** — The sync workflow overlays **`tools/fractured-launcher-electron` from the default branch**, so **`package.json`** there defines the built version. Ensure launcher changes are **merged to `main`** before publishing the GitHub release (or re-run **Actions → Sync release to Gitea** after merging). Previously, **Fractured-Launcher\*** files **attached on the GitHub release** were merged too, which could leave **two** installer versions on Gitea; those assets are now skipped in favor of CI-only builds.
|
13. **Gitea still shows an old launcher version** — The sync workflow overlays **`tools/fractured-launcher-electron` from the default branch**, so **`package.json`** there defines the built version. Ensure launcher changes are **merged to `main`** before publishing the GitHub release (or re-run **Actions → Sync release to Gitea** after merging). Previously, **Fractured-Launcher\*** files **attached on the GitHub release** were merged too, which could leave **two** installer versions on Gitea; those assets are now skipped in favor of CI-only builds.
|
||||||
|
14. **Migrating `baked-gitea-channel.js` to a new host** — Publish **`gitea-replace-launcher-only.sh`** (see **Manual upload** above) on the **old** Gitea **`latest`** release so auto-update still works until clients move to the new URL.
|
||||||
|
|
||||||
### Private Gitea token for players
|
### Private Gitea token for players
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,12 @@
|
|||||||
* Production Gitea mirror (non-secret). Edit here and ship — no inject script,
|
* Production Gitea mirror (non-secret). Edit here and ship — no inject script,
|
||||||
* no fractured-release-channel.json, no CI env needed for these fields.
|
* no fractured-release-channel.json, no CI env needed for these fields.
|
||||||
* Token stays in env: GITEA_TOKEN or launcher.json → gitea.token_env.
|
* Token stays in env: GITEA_TOKEN or launcher.json → gitea.token_env.
|
||||||
|
*
|
||||||
|
* Use origin only (no /releases path): API is {base_url}/api/v1/…
|
||||||
|
* Web: https://git.hisora.dev/Dawnsorrow/Fractured-Distro/releases
|
||||||
*/
|
*/
|
||||||
module.exports = {
|
module.exports = {
|
||||||
// http:// kept as-is; bare host gets https in gitea-release.js
|
base_url: 'https://git.hisora.dev',
|
||||||
base_url: 'http://brassnet.ddns.net:33983',
|
|
||||||
owner: 'Dawnsorrow',
|
owner: 'Dawnsorrow',
|
||||||
repo: 'Fractured-Distro',
|
repo: 'Fractured-Distro',
|
||||||
release_tag: 'latest',
|
release_tag: 'latest',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "fractured-launcher-electron",
|
"name": "fractured-launcher-electron",
|
||||||
"version": "1.0.12",
|
"version": "1.0.13",
|
||||||
"description": "Fractured WoW launcher (Electron) — no console window, native folder picker, auto-update",
|
"description": "Fractured WoW launcher (Electron) — no console window, native folder picker, auto-update",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Replace only launcher installer + latest.yml attachments on a Gitea release.
|
||||||
|
# Does NOT delete Wow.exe, MPQs, or patch-manifest — use this to publish a
|
||||||
|
# "bridge" build (e.g. 1.0.13 with new baked Gitea URL) on a legacy host while
|
||||||
|
# keeping game assets already on that release.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# export GITEA_BASE_URL=http://legacy-host:port # or https://...
|
||||||
|
# export GITEA_TOKEN=gta_...
|
||||||
|
# export GITEA_OWNER=Dawnsorrow
|
||||||
|
# export GITEA_REPO=Fractured-Distro
|
||||||
|
# ./gitea-replace-launcher-only.sh /path/to/electron/dist latest
|
||||||
|
#
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
# shellcheck source=release-sync-filters.sh
|
||||||
|
. "$SCRIPT_DIR/release-sync-filters.sh"
|
||||||
|
|
||||||
|
DIST_DIR="${1:?first arg: electron-builder dist directory (contains .exe / .AppImage / latest*.yml)}"
|
||||||
|
TAG="${2:?second arg: release tag (e.g. latest)}"
|
||||||
|
|
||||||
|
: "${GITEA_BASE_URL:?Set GITEA_BASE_URL}"
|
||||||
|
: "${GITEA_TOKEN:?Set GITEA_TOKEN}"
|
||||||
|
: "${GITEA_OWNER:?Set GITEA_OWNER}"
|
||||||
|
: "${GITEA_REPO:?Set GITEA_REPO}"
|
||||||
|
|
||||||
|
BASE="${GITEA_BASE_URL%/}"
|
||||||
|
API="$BASE/api/v1"
|
||||||
|
AUTH_H=(-H "Authorization: token ${GITEA_TOKEN}" -H "Accept: application/json")
|
||||||
|
|
||||||
|
TAG_ENC=$(python3 -c "import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1], safe=''))" "$TAG")
|
||||||
|
REL_JSON=$(mktemp)
|
||||||
|
trap 'rm -f "$REL_JSON"' EXIT
|
||||||
|
|
||||||
|
code=$(curl -sS -o "$REL_JSON" -w "%{http_code}" "${AUTH_H[@]}" \
|
||||||
|
"$API/repos/${GITEA_OWNER}/${GITEA_REPO}/releases/tags/${TAG_ENC}")
|
||||||
|
|
||||||
|
if [ "$code" != "200" ]; then
|
||||||
|
echo "Gitea GET release by tag failed HTTP $code (release must already exist):" >&2
|
||||||
|
cat "$REL_JSON" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
rel_id=$(jq -r '.id' "$REL_JSON")
|
||||||
|
if [ -z "$rel_id" ] || [ "$rel_id" = "null" ]; then
|
||||||
|
echo "Could not resolve Gitea release id" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
should_delete_attachment() {
|
||||||
|
local l
|
||||||
|
l=$(printf '%s' "${1##*/}" | tr '[:upper:]' '[:lower:]')
|
||||||
|
case "$l" in
|
||||||
|
fractured-launcher*) return 0 ;;
|
||||||
|
*.blockmap) return 0 ;;
|
||||||
|
builder-debug.yml|builder-debug.yaml) return 0 ;;
|
||||||
|
esac
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
should_delete_yml_attachment() {
|
||||||
|
local l
|
||||||
|
l=$(printf '%s' "${1##*/}" | tr '[:upper:]' '[:lower:]')
|
||||||
|
case "$l" in
|
||||||
|
latest.yml) [ -f "$DIST_DIR/latest.yml" ] ;;
|
||||||
|
latest-linux.yml) [ -f "$DIST_DIR/latest-linux.yml" ] ;;
|
||||||
|
latest-mac.yml) [ -f "$DIST_DIR/latest-mac.yml" ] ;;
|
||||||
|
*) return 1 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
while read -r line; do
|
||||||
|
[ -z "$line" ] && continue
|
||||||
|
aid=$(printf '%s' "$line" | cut -f1)
|
||||||
|
aname=$(printf '%s' "$line" | cut -f2-)
|
||||||
|
if should_delete_attachment "$aname" || should_delete_yml_attachment "$aname"; then
|
||||||
|
echo "Removing old attachment: $aname (id=$aid)"
|
||||||
|
curl -fsS -X DELETE "${AUTH_H[@]}" \
|
||||||
|
"$API/repos/${GITEA_OWNER}/${GITEA_REPO}/releases/${rel_id}/assets/${aid}" || true
|
||||||
|
fi
|
||||||
|
done < <(jq -r '(.attachments // .assets // [])[] | "\(.id)\t\(.name)"' "$REL_JSON")
|
||||||
|
|
||||||
|
shopt -s nullglob
|
||||||
|
upload_paths=()
|
||||||
|
for f in "$DIST_DIR"/Fractured-Launcher*.exe "$DIST_DIR"/Fractured-Launcher*.AppImage \
|
||||||
|
"$DIST_DIR"/latest.yml "$DIST_DIR"/latest-linux.yml "$DIST_DIR"/latest-mac.yml; do
|
||||||
|
[ -f "$f" ] || continue
|
||||||
|
bn=$(basename "$f")
|
||||||
|
if should_skip_gitea_upload "$bn"; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
upload_paths+=("$f")
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "${#upload_paths[@]}" -eq 0 ]; then
|
||||||
|
echo "No launcher files to upload under $DIST_DIR (expected Fractured-Launcher*.exe, *.AppImage, latest.yml, latest-linux.yml)." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for f in "${upload_paths[@]}"; do
|
||||||
|
bn=$(basename "$f")
|
||||||
|
echo "Uploading $bn …"
|
||||||
|
curl -fsS -X POST "${AUTH_H[@]}" \
|
||||||
|
-F "attachment=@${f}" \
|
||||||
|
"$API/repos/${GITEA_OWNER}/${GITEA_REPO}/releases/${rel_id}/assets"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Done. Release $TAG (id=$rel_id): replaced ${#upload_paths[@]} launcher file(s); game assets left intact."
|
||||||
Reference in New Issue
Block a user