81 Commits

Author SHA1 Message Date
ad39d53c58 fixed issues for newest version 2025-01-24 17:13:52 -05:00
b4013b7024 commented ideas on how the perma stat bonuses will work with custom auras 2025-01-18 11:34:08 -05:00
7d1a9018df Many changes to bring in the Advancement system for upgrading player stats via dice 2025-01-18 11:29:21 -05:00
d4fa53d435 Updates to bring in event handlers for communication and register on server load 2025-01-18 11:28:07 -05:00
2aeafa689a New client event dispatchers to commumicate to WoW Client using formatted event structure 2025-01-18 11:27:30 -05:00
f42cba3099 removed the CreatureHooks calls until later implemented 2024-12-17 17:48:16 -05:00
aa58b493ba move data channel to event processor since it makes more sense with that class 2024-12-17 17:47:28 -05:00
5387960965 Added EventProcessor for client and server communication 2024-12-17 17:43:13 -05:00
4be3fae368 Fixed warnings and updated comments 2024-12-13 19:19:29 -05:00
d410220bd0 Added methods for upgrading an advancement 2024-12-13 19:19:10 -05:00
4df7800afa Fixed all Warnings and added in advancement methods + material types loader 2024-12-13 19:18:44 -05:00
173d19bcd3 Fixed all Warnings and added in advancement methods + material types loader 2024-12-13 19:18:33 -05:00
738194fbfa added many methods related to increasing a players rank 2024-12-12 23:42:16 -05:00
81b7a87ced Added a new constant for MAX Advancement Ranks 2024-12-12 23:41:41 -05:00
8e0809cb1c Added constants for items 2024-12-12 18:53:08 -05:00
cb48af9e9d Added historical record for player advancmenets 2024-12-12 18:52:52 -05:00
acf11d25f2 Added information about the data channel used for client to server communication. 2024-12-11 15:41:36 -05:00
41fd4876cf renamed function 2024-12-11 15:40:44 -05:00
ef62c10df3 Updated schema definition 2024-12-11 15:40:24 -05:00
5fbb0eac74 Added in methods for loading in data for players 2024-12-11 15:40:00 -05:00
0982e24f06 Added Loading of advancments from database on server start and player login. 2024-12-11 15:39:45 -05:00
fa7389f24a Reorganized and fixed issues with material / types and ranks 2024-12-11 15:39:12 -05:00
8436460350 Added scheduling and fixed for death counting 2024-12-02 23:57:21 -05:00
3bd1f3cdc6 Added more handlers for players 2024-12-02 23:57:03 -05:00
6115459150 Added new sql changes for advancement 2024-12-02 23:56:22 -05:00
3093ab3280 Added new Advancement Manager classes for allowing upgrades 2024-12-02 23:56:01 -05:00
fa6f3f3eab Refactored scripts and Creature data 2024-11-29 15:25:38 -05:00
ccf222ec4f fixed issue with misaligned group deaths 2024-11-26 00:15:04 -05:00
91d3f91dfc Group data and player data objects were not referencing the same object corrected using right pointers 2024-11-25 23:55:50 -05:00
9612974b01 Added functions to store player data on bind 2024-11-24 23:53:14 -05:00
fd5e186032 Added boot on deaths limits hit 2024-11-24 21:51:13 -05:00
baf0d8e6e7 Added difficulty to death stats 2024-11-22 19:12:08 -05:00
f3230a6559 Merge branch 'main' of https://github.com/araxiaonline/mod-mythic-plus 2024-11-22 14:14:08 -05:00
86d5ba83f1 merged in branch and handled conflicts 2024-11-22 14:08:50 -05:00
Ben Carter
318244f0fd Merge pull request #9 from araxiaonline/feat/add-data-saves
Feature: Adds in data saves and upgrade system
2024-11-22 13:46:40 -05:00
73018100ac Turned on player scripts have segfault in bind to instance that needs fixed. 2024-11-21 23:52:02 -05:00
fbcb218dd6 player instance saves are working now on group entry 2024-11-21 23:25:46 -05:00
322243abf4 TEsting database stats saves. 2024-11-21 22:52:04 -05:00
9d009d0feb fixed segmentation fault related to null pointer 2024-11-21 22:52:01 -05:00
a0fdfc9836 Added golang script to generate ranks for stat level ups 2024-11-21 22:51:02 -05:00
a6de416efb Added more material types used for currency into game 2024-11-20 23:55:56 -05:00
1695f33f68 Added in new material types for resistances. 2024-11-19 23:28:55 -05:00
88d894eaaf Added schemas and material type data 2024-11-18 23:16:30 -05:00
f66b861079 updated scripts and reoganized them. Addded upgrade ranks script 2024-11-18 23:15:30 -05:00
79cb4d9835 Added more schemas used for tracking player instance data. 2024-11-17 11:22:09 -05:00
c32b64bb1f Added a bunch of methods for saving database information on key events 2024-11-14 23:12:25 -05:00
f083748c4a Added Mythic Plus method isFinalBoss for determining completion of run. 2024-11-14 19:52:53 -05:00
3cf1e17c99 Added more database storage of stats, timers, death and workign on final boss detection. 2024-11-14 00:07:36 -05:00
0a0236b273 updated the schema for stats and run s 2024-11-13 22:22:25 -05:00
8942d53b13 Fixed compile errors 2024-11-11 23:07:55 -05:00
41819432e9 updated command script to use new method signature for group data 2024-11-11 23:02:12 -05:00
d35a934b8a more work on player and group data storage for death counts and other stats 2024-11-11 22:56:18 -05:00
46d5a99f04 fixed issue with wrong type for player instance data 2024-11-08 18:15:56 -05:00
f8ecc0d1b9 Adding in player instance data 2024-11-08 00:53:43 -05:00
fcce3a0453 updated sql and added more methods to store state between server / character login/out 2024-11-07 23:41:40 -05:00
68efd1a732 Added instance resets when difficulty is changed and added personal difficulty tracking 2024-11-05 01:06:30 -05:00
9f4ea9d539 adding rescale all command and updated factors sql 2024-11-04 23:36:27 -05:00
803cb246bf updated to fix horsemen health scaling 2024-11-01 20:14:01 -04:00
1bd94e56fa Added easier way to tune up and down zones 2024-11-01 19:48:30 -04:00
0ee3ead805 Added new AP fixes and working on healing fixes for enemy creature 2024-10-29 19:45:30 -04:00
ec1663aa34 Removed ai change 2024-10-18 21:38:50 -04:00
3d0f314c39 updated to add headless horseman 2024-10-18 20:37:48 -04:00
5877e9122c Added event system and updated Bazzalan as a test. 2024-10-18 19:24:33 -04:00
5847754197 Added scale to respawn trigger, added creaturehook add to instance when creature is scaled. 2024-10-14 23:25:00 -04:00
6936f8b73e Added event handler for extending boss functions without disrupting existing AI or Eluna scripts. 2024-10-14 22:20:23 -04:00
a35e4b646d added changes to include difficulty into ai object 2024-10-13 23:30:27 -04:00
a4d7b00036 Got AI Event system working 2024-10-12 19:53:28 -04:00
73c2b8923b Added new base class for updating creatures for events from AI 2024-10-12 19:53:10 -04:00
d3af7990cb Added updates to all the melee and spell damage 2024-10-11 20:20:28 -04:00
25f74e1b5a Added new event handler system for creature events to allow for balancing 2024-10-10 00:55:00 -04:00
cbec66031d Adding event system for override necessary to balance higher levels 2024-10-08 23:58:59 -04:00
4a8731c876 Added first boss override script for balancing. 2024-10-07 23:43:21 -04:00
a62df56099 New spell scaling spell and command for reloading scaling factors 2024-10-05 22:44:06 -04:00
7f78971e49 added sql for leveling up pets to 90 2024-10-05 22:12:37 -04:00
c73dba69d7 Added new melee scaling 2024-10-04 20:41:52 -04:00
b9825b47e0 Working on spell scaling individually per dungeon to make sur eI account for everything and many different spells 2024-10-03 01:13:29 -04:00
cbde9bbfe2 Updated EligibleDamageTarget additional safeguard on owner check. 2024-10-02 23:27:33 -04:00
af9e7b3f3f Added pets generator script and fixed segfaults in eligible methods 2024-10-02 00:05:32 -04:00
5b6805dcc7 Added studio code props / settins 2024-10-01 22:56:41 -04:00
78648bd511 Added necessary sql files 2024-10-01 22:56:29 -04:00
Ben Carter
fd4c9d2b51 Merge pull request #2 from araxiaonline/feat/add-mythic-v1
Build: Feat/add mythic v1
2024-10-01 22:45:13 -04:00
56 changed files with 44468 additions and 448 deletions

22
.vscode/c_cpp_properties.json vendored Normal file
View File

@@ -0,0 +1,22 @@
{
"configurations": [
{
"name": "AzerothCore",
"includePath": [
"${workspaceFolder}/src/common/Logging/**",
"${workspaceFolder}/src",
"${workspaceFolder}/deps",
"${workspaceFolder}/**",
"/opt/homebrew/include"
],
"defines": [
"AZEROTHCORE"
],
"compilerPath": "/usr/bin/clang++", // macOS typically uses clang as the default compiler
"cStandard": "c11",
"cppStandard": "c++17",
"intelliSenseMode": "macos-clang-x64"
}
],
"version": 4
}

View File

@@ -112,3 +112,8 @@ MythicPlus.Mythic.ItemOffset = 20000000
MythicPlus.Legendary.ItemOffset = 21000000
MythicPlus.Ascendant.ItemOffset = 22000000
##############
# Scaling Adjusters
#############
MythicPlus.MeleeAttackPowerDampener = 800
MythicPlus.MeleeAttackPowerStart = 3000

View File

View File

@@ -0,0 +1,100 @@
-- Used for tracking group instance data for mythic runs
DROP TABLE IF EXISTS mp_group_data;
CREATE TABLE mp_group_data (
groupId INT UNSIGNED NOT NULL DEFAULT '0',
difficulty INT UNSIGNED,
mapId INT UNSIGNED,
instanceId INT UNSIGNED,
instanceTimer INT UNSIGNED,
deaths INT UNSIGNED,
PRIMARY KEY (groupId)
);
-- Used for tracking current instance data for players
DROP TABLE IF EXISTS mp_player_instance_data;
CREATE TABLE mp_player_instance_data (
guid INT UNSIGNED NOT NULL DEFAULT '0',
difficulty INT UNSIGNED NOT NULL DEFAULT '3',
mapId INT UNSIGNED NOT NULL,
instanceId INT UNSIGNED,
deaths INT UNSIGNED NOT NULL,
PRIMARY KEY (guid, mapId, instanceId)
);
-- Used for tracking player deaths to specific creatures in mythic runs
DROP TABLE IF EXISTS mp_player_death_stats;
CREATE TABLE mp_player_death_stats (
guid INT UNSIGNED NOT NULL DEFAULT '0',
creatureEntry INT UNSIGNED NOT NULL,
difficulty TINYINT UNSIGNED NOT NULL DEFAULT '0',
numDeaths INT UNSIGNED NOT NULL DEFAULT '0',
lastUpdated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (guid, creatureEntry, difficulty)
);
-- Used for tracking player runs in mythic dungeons
DROP TABLE IF EXISTS mp_player_runs;
CREATE TABLE mp_player_runs (
runId INT UNSIGNED AUTO_INCREMENT,
guid INT UNSIGNED NOT NULL DEFAULT '0',
difficulty INT UNSIGNED NOT NULL DEFAULT '3',
mapId INT UNSIGNED NOT NULL DEFAULT '0',
groupDeaths INT UNSIGNED NOT NULL DEFAULT '0',
personalDeaths INT UNSIGNED NOT NULL DEFAULT '0',
startTime INT UNSIGNED,
completeTime TIMESTAMP,
botCount TINYINT UNSIGNED DEFAULT '0',
PRIMARY KEY (runId),
INDEX idx_guid (guid),
INDEX idx_mapId (mapId)
);
-- Used for tracking player stats in mythic dungeons
DROP TABLE IF EXISTS mp_player_stats;
CREATE TABLE mp_player_stats (
guid INT UNSIGNED NOT NULL DEFAULT '0',
mapId INT UNSIGNED NOT NULL DEFAULT '0',
difficulty TINYINT UNSIGNED NOT NULL DEFAULT '0',
deaths INT UNSIGNED DEFAULT '0',
runs INT UNSIGNED DEFAULT '0',
completions INT UNSIGNED DEFAULT '0',
totalTime INT UNSIGNED DEFAULT '0',
bestTime INT UNSIGNED DEFAULT '0',
PRIMARY KEY (guid, mapId, difficulty)
);
-- Used to enable custom stat upgrades from materials and drops in mythic dungeons
DROP TABLE IF EXISTS mp_player_advancements;
CREATE TABLE mp_player_advancements (
guid INT UNSIGNED NOT NULL,
advancementId INT UNSIGNED NOT NULL,
bonus FLOAT NOT NULL,
upgradeRank INT UNSIGNED NOT NULL,
diceSpent INT UNSIGNED NOT NULL DEFAULT '0',
PRIMARY KEY (guid, advancementId)
);
-- Used to show historic player roll data by advancement.
DROP TABLE IF EXISTS mp_player_advancement_history;
CREATE TABLE mp_player_advancement_history (
guid INT UNSIGNED NOT NULL,
advancementId INT UNSIGNED NOT NULL,
bonus FLOAT NOT NULL,
upgradeRank INT UNSIGNED NOT NULL,
diceSpent INT UNSIGNED NOT NULL DEFAULT '0',
materialId1 INT UNSIGNED NOT NULL,
materialId2 INT UNSIGNED NOT NULL,
materialId3 INT UNSIGNED NOT NULL,
materialCost1 INT UNSIGNED NOT NULL,
materialCost2 INT UNSIGNED NOT NULL,
materialCost3 INT UNSIGNED NOT NULL,
added TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (guid, advancementId)
);

View File

View File

View File

@@ -0,0 +1,48 @@
-- Used to track upgrade ranks for stat improvements and min/max values
DROP TABLE IF EXISTS mp_stat_upgrade_ranks;
DROP TABLE IF EXISTS mp_upgrade_ranks;
CREATE TABLE mp_upgrade_ranks (
upgradeRank INT UNSIGNED NOT NULL,
advancementId INT UNSIGNED NOT NULL,
materialId1 INT UNSIGNED NOT NULL,
materialId2 INT UNSIGNED NOT NULL,
materialId3 INT UNSIGNED NOT NULL,
materialCost1 INT UNSIGNED NOT NULL,
materialCost2 INT UNSIGNED NOT NULL,
materialCost3 INT UNSIGNED NOT NULL,
minIncrease1 INT UNSIGNED NOT NULL,
maxIncrease1 INT UNSIGNED NOT NULL,
minIncrease2 INT UNSIGNED NOT NULL,
maxIncrease2 INT UNSIGNED NOT NULL,
minIncrease3 INT UNSIGNED NOT NULL,
maxIncrease3 INT UNSIGNED NOT NULL,
chanceCost1 INT UNSIGNED NOT NULL,
chanceCost2 INT UNSIGNED NOT NULL,
chanceCost3 INT UNSIGNED NOT NULL,
PRIMARY KEY (upgradeRank, advancementId)
);
-- Used to allocate trade materials based on slot upgrades
DROP TABLE IF EXISTS mp_material_types;
CREATE TABLE mp_material_types (
materialId INT UNSIGNED NOT NULL,
entry INT UNSIGNED NOT NULL,
name VARCHAR(255) NOT NULL,
PRIMARY KEY (materialId, entry)
);
-- Description: Scale factors for Mythic+ dungeons used to normalize dungeon difficulty across expansions.
DROP TABLE IF EXISTS mp_scale_factors;
CREATE TABLE mp_scale_factors (
mapId SMALLINT NOT NULL,
dmg_bonus INT DEFAULT '100',
spell_bonus INT DEFAULT '100',
hp_bonus INT DEFAULT '100',
difficulty INT DEFAULT '100',
max INT DEFAULT '100',
PRIMARY KEY (mapId)
);

View File

@@ -0,0 +1,537 @@
TRUNCATE TABLE mp_material_types;
-- MaterialID for all droppable cloth items
REPLACE INTO mp_material_types (materialId, entry, name)
SELECT DISTINCT
1 AS materialId, -- Assign the same materialId to all rows
it.entry,
it.name
FROM item_template it
LEFT JOIN creature_loot_template clt ON (it.entry = clt.Item)
WHERE it.name LIKE '%Cloth'
AND it.class = 7
AND it.subclass = 5
AND it.name NOT LIKE 'Bolt%'
AND it.VerifiedBuild = 12340
AND clt.Entry IS NOT NULL;
-- Crafted cloth items that are not dropped by enemies
REPLACE INTO mp_material_types (materialId, entry, name)
SELECT
2 AS materialId, -- Assign the same materialId to all rows
it.entry,
it.name
FROM item_template it
LEFT JOIN creature_loot_template clt ON (it.entry = clt.Item)
WHERE it.name LIKE '%Cloth'
AND it.class = 7
AND it.subclass = 5
AND it.name NOT LIKE 'Bolt%'
AND it.VerifiedBuild = 12340
AND clt.Entry IS NULL;
-- Common Herbs available to herbalists
REPLACE INTO mp_material_types (materialId, entry, name)
SELECT
3 AS materialId,
it.entry,
it.name
FROM item_template it
WHERE it.class = 7
AND it.subclass = 9
AND it.VerifiedBuild = 12340
AND it.RequiredSkillRank != 0
AND it.name NOT IN (
'Black Lotus',
'Ghost Mushroom',
'Dreamfoil',
'Bloodvine',
'Fel Lotus',
'Netherbloom',
'Nightmare Vine',
'Frost Lotus',
"Adder's Tongue",
'Fire Leaf',
'Lichbloom',
'Icethorn',
"Talandra's Rose"
);
-- Rare Herbs that are harder to find or drop in raids
REPLACE INTO mp_material_types (materialId, entry, name)
SELECT
4 AS materialId,
it.entry,
it.name
FROM item_template it
WHERE it.name IN (
'Black Lotus', -- Classic: Rare spawn in Dire Maul and outdoor zones, extremely rare in raids.
'Ghost Mushroom', -- Classic: Found in Maraudon (Dungeon-only herb).
'Bloodvine', -- Classic: Drops from enemies in Zul'Gurub.
'Fel Lotus', -- TBC: Extremely rare herb drop while gathering Felweed, Dreaming Glory, etc., in Outland.
'Netherbloom', -- TBC: Rare spawn in Netherstorm, also dropped by some elites.
'Nightmare Vine', -- TBC: Found in Shadowmoon Valley and drops in Black Temple from specific mobs.
'Frost Lotus', -- Wrath: Extremely rare spawn from herb nodes or dungeons like Utgarde Pinnacle.
"Adder's Tongue", -- Wrath: Rare spawn from dungeon nodes in Drak'Tharon Keep and Gundrak.
'Fire Leaf', -- Wrath: Rare herb found in Wintergrasp, sometimes in dungeon PvP areas.
'Icethorn', -- Wrath: Harvested from specific dungeon nodes in higher-level Northrend zones.
"Talandra's Rose" -- Wrath: Found sparsely in Zul'Drak dungeons like Gundrak.
);
-- Common ores that are easy to find in the world
REPLACE INTO mp_material_types (materialId, entry, name)
SELECT DISTINCT
5 AS materialId,
it.entry,
it.name
FROM item_template it
WHERE it.class = 7
AND it.subclass = 7
AND it.VerifiedBuild = 12340
AND it.name NOT LIKE '%Bar'
AND it.name NOT LIKE '%Ingot'
AND it.name NOT LIKE '%Stone'
AND it.name NOT LIKE '%Shard'
AND it.name NOT IN ('Coal', 'Elemental Flux', 'Hardened Khorium')
AND it.name NOT IN ('Elementium Ore', 'Dark Iron Ore', 'Primal Nether', 'Runed Orb', 'Crusader Orb', 'Blood of the Mountain')
ORDER BY it.name;
-- Rare ores that are harder to find or drop in raids
REPLACE INTO mp_material_types (materialId, entry, name)
SELECT DISTINCT
6 AS materialId,
it.entry,
it.name
FROM item_template it
WHERE
it.name Like '%Obsidian Shard%'
OR it.name IN ('Elementium Ore', 'Dark Iron Ore', 'Primal Nether', 'Runed Orb', 'Crusader Orb', 'Blood of the Mountain')
and entry <= 2000000
ORDER BY it.name;
-- Common skins that are easy to find in the world
REPLACE INTO mp_material_types (materialId, entry, name)
SELECT
7 AS materialId, -- Unique ID for commonly skinnable materials
it.entry,
it.name
FROM item_template it
WHERE it.name IN (
'Borean Leather',
'Heavy Leather',
'Heavy Hide',
'Light Leather',
'Light Hide',
'Medium Leather',
'Medium Hide',
'Rugged Leather',
'Rugged Hide',
'Thick Leather',
'Thick Hide',
'Knothide Leather',
'Icy Dragonscale',
'Jormungar Scale',
'Nerubian Chitin',
'Turtle Scale'
);
-- Rare skinning material found in the world or dropped by enemies
REPLACE INTO mp_material_types (materialId, entry, name)
SELECT
8 AS materialId, -- Unique ID for rare skinnable and non-skinnable materials
it.entry,
it.name
FROM item_template it
WHERE it.name IN (
'Black Dragonscale',
'Blue Dragonscale',
'Green Dragonscale',
'Red Dragonscale',
'Scale of Onyxia',
'Nether Dragonscales',
'Wind Scales',
'Thick Clefthoof Leather',
'Cobra Scales',
'Devilsaur Leather',
'Green Whelp Scale',
'Black Whelp Scale',
'Perfect Deviate Scale',
'Core Leather',
'Dreamscale',
'Primal Bat Leather',
'Primal Tiger Leather'
);
-- Common uncut gems that are easy to find in the world
REPLACE INTO mp_material_types (materialId, entry, name)
SELECT
9 AS materialId, -- Unique ID for common uncut gems
it.entry,
it.name
FROM item_template it
WHERE
it.VerifiedBuild = 12340 -- Wrath of the Lich King build
AND it.name NOT LIKE '%Cut%' -- Exclude pre-cut gems
AND it.name IN (
'Malachite',
'Tigerseye',
'Moss Agate',
'Shadowgem',
'Jade',
'Lesser Moonstone',
'Citrine',
'Aquamarine',
'Star Ruby',
'Azerothian Diamond',
'Huge Emerald',
'Large Opal',
'Blue Sapphire',
'Arcane Crystal',
'Bloodstone',
'Sun Crystal',
'Chalcedony',
'Dark Jade',
'Shadow Crystal',
'Huge Citrine',
'Monarch Topaz',
'Forest Emerald',
'Sky Sapphire',
"Autumn's Glow",
'Twilight Opal',
'Scarlet Ruby'
);
-- Rare gems that are harder to find or drop in raids
REPLACE INTO mp_material_types (materialId, entry, name)
SELECT
10 AS materialId,
it.entry,
it.name
FROM item_template it
WHERE
it.name IN (
-- TBC Epic Gems
'Crimson Spinel',
'Empyrean Sapphire',
'Lionseye',
'Shadowsong Amethyst',
'Pyrestone',
'Seaspray Emerald',
-- WotLK Epic Gems
'Cardinal Ruby',
'Ametrine',
"King's Amber",
'Eye of Zul',
'Majestic Zircon',
'Dreadstone'
);
-- common enchantment materials that are easy to find in the world
REPLACE INTO mp_material_types (materialId, entry, name)
SELECT
11 AS materialId, -- Example materialId for enchantment items
it.entry,
it.name
FROM item_template it
WHERE
it.class = 7
AND it.subclass = 12
AND it.VerifiedBuild = 12340
AND it.name NOT LIKE '%Rod%'
AND it.name NOT IN (
-- Classic Rare Enchantment Items
'Righteous Orb', -- Drops in Stratholme.
'Lava Core', -- Rare drop in Molten Core.
'Black Diamond', -- Rare drop in Blackrock Depths and related areas.
'Essence of the Red', -- Reward from Vaelastrasz in Blackwing Lair.
'Onyxia Scale', -- Dropped by Onyxia, used in fire resistance recipes.
-- TBC Rare Enchantment Items
'Primal Might', -- Crafted using all Primals, very rare due to material costs.
'Heart of Darkness', -- Rare drop in Black Temple.
'Sunmote', -- Drops in Sunwell Plateau.
'Nether Vortex', -- Raid-exclusive crafting material in TBC.
-- WotLK Rare Enchantment Items
'Eternal Might', -- Custom material replacing Eternal combinations, rare in high-level content.
'Frozen Orb', -- Drops in WotLK heroic dungeons.
'Runed Orb', -- Drops in Ulduar.
'Crusader Orb' -- Drops in Trial of the Crusader raid.
);
-- Rare enchantment materials that are harder to find or drop in raids
REPLACE INTO mp_material_types (materialId, entry, name)
SELECT
12 AS materialId, -- Example materialId for enchantment items
it.entry,
it.name
FROM item_template it
WHERE
it.name IN (
-- Classic Rare Enchantment Items
'Righteous Orb', -- Drops in Stratholme.
'Lava Core', -- Rare drop in Molten Core.
'Black Diamond', -- Rare drop in Blackrock Depths and related areas.
'Essence of the Red', -- Reward from Vaelastrasz in Blackwing Lair.
'Onyxia Scale', -- Dropped by Onyxia, used in fire resistance recipes.
-- TBC Rare Enchantment Items
'Primal Might', -- Crafted using all Primals, very rare due to material costs.
'Heart of Darkness', -- Rare drop in Black Temple.
'Sunmote', -- Drops in Sunwell Plateau.
'Nether Vortex', -- Raid-exclusive crafting material in TBC.
-- WotLK Rare Enchantment Items
'Eternal Might', -- Custom material replacing Eternal combinations, rare in high-level content.
'Frozen Orb', -- Drops in WotLK heroic dungeons.
'Runed Orb', -- Drops in Ulduar.
'Crusader Orb' -- Drops in Trial of the Crusader raid.
);
-- Common materials that related to water / frost
REPLACE INTO mp_material_types (materialId, entry, name)
SELECT
13 AS materialId,
it.entry,
it.name
FROM item_template it
WHERE
it.name IN (
-- Classic
'Elemental Water',
'Essence of Water',
'Glacial Fragments',
-- TBC
'Arctic Fur',
'Thick Dawnstone',
-- WotLK
'Frost Lotus',
'Frostweave Cloth'
);
-- Rare materials that related to water / frost
REPLACE INTO mp_material_types (materialId, entry, name)
SELECT
14 AS materialId, -- Unique ID for rare frost resistance materials
it.entry,
it.name
FROM item_template it
WHERE
it.name IN (
-- Classic
'Frozen Rune',
'Core of Earth',
-- TBC
'Frost-Infused Shard',
'Nether Residuum',
'Primal Water',
-- WotLK
'Primordial Saronite',
'Icy Dragonscale',
'Eternal Water'
);
-- Common materials that relate to fire
REPLACE INTO mp_material_types (materialId, entry, name)
SELECT
15 AS materialId,
it.entry,
it.name
FROM item_template it
WHERE
it.name IN (
-- Classic
'Elemental Fire',
'Essence of Fire',
'Heart of Fire',
'Small Flame Sac',
'Core of Flame',
-- TBC
'Mote of Fire',
'Flame Spessarite', -- Common fire-themed gem for jewelcrafting.
'Smoldering Core', -- Fire-themed drop from elementals.
-- WotLK
'Fire Leaf',
'Crystallized Fire'
);
-- Rare materials that relate to fire resistance gear or fire-themed weapons
REPLACE INTO mp_material_types (materialId, entry, name)
SELECT
16 AS materialId, -- Unique ID for rare fire resistance and fire weapon crafting materials
it.entry,
it.name
FROM item_template it
WHERE
it.name IN (
-- Classic
'Fiery Core', -- Key material for crafting fire resistance gear in Molten Core.
'Lava Core', -- Used in crafting fire resistance gear and recipes.
'Sulfuron Ingot', -- Rare drop from Molten Core, used for crafting legendary fire weapons.
-- TBC
'Primal Fire', -- Core material for fire crafting in TBC.
'Hardened Adamantite Bar', -- Material for crafting fire-resistant shields and gear.
-- WotLK
'Primordial Saronite', -- Used for crafting high-level resistance and utility gear.
'Eternal Fire', -- Central material for fire-themed crafting.
'Runed Orb', -- Used in crafting fire-related weapons and gear.
'Smouldering Crystals', -- High-value fire crafting material from fire-themed mobs.
'Burning Embers' -- Rare crafting material for WotLK fire-themed items.
);
-- Common materials that relate to nature
REPLACE INTO mp_material_types (materialId, entry, name)
SELECT
17 AS materialId,
it.entry,
it.name
FROM item_template it
WHERE
it.name IN (
-- Classic
'Elemental Earth',
'Essence of Earth',
'Living Essence',
'Small Venom Sac',
"Un'Goro Soil",
'Ichor of Undeath', -- Used in nature-themed recipes.
-- TBC
'Mote of Earth', -- Combines into Primal Earth.
'Mote of Life', -- Combines into Primal Life.
'Terocone', -- Nature-themed herb used in alchemy.
-- WotLK
'Crystallized Earth', -- Combines into Eternal Earth.
'Crystallized Life', -- Combines into Eternal Life.
'Icethorn', -- Herb with thematic ties to nature resistance potions.
'Ancient Moss'
);
-- Rare Nature resistance materials
REPLACE INTO mp_material_types (materialId, entry, name)
SELECT
18 AS materialId, -- Unique ID for rare nature resistance and nature weapon crafting materials
it.entry,
it.name
FROM item_template it
WHERE
it.name IN (
-- Classic
'Heart of the Wild', -- Rare material for crafting nature resistance gear.
'Living Essence', -- Rare crafting material dropped by nature elementals.
"Gahz'rilla's Electrified Scale",-- Rare item from Zul'Farrak for nature resistance gear.
-- TBC
'Primal Earth', -- Central crafting material for resistance and nature-themed items.
'Primal Life', -- Rare material for nature-themed crafting.
-- WotLK
'Eternal Earth', -- Central material for nature-themed crafting.
'Eternal Life', -- Rare material for nature-themed crafting.
'Runed Orb', -- Rare crafting material for nature-themed gear.
"Adder's Tongue" -- Herb used in nature resistance recipes.
);
-- Common materials that relate to shadow
REPLACE INTO mp_material_types (materialId, entry, name)
SELECT
19 AS materialId,
it.entry,
it.name
FROM item_template it
WHERE
it.name IN (
-- Classic
'Shadowgem', -- Common low-level gem with shadow theme.
'Elemental Shadow', -- Basic crafting reagent from shadow elementals.
'Essence of Shadow', -- Dropped by shadow-themed elementals in high-level zones.
-- TBC
'Mote of Shadow', -- Combines into Primal Shadow, used in crafting.
'Dark Rune', -- Dropped in Scholomance and used for shadow resistance crafting.
-- WotLK
'Crystallized Shadow', -- Combines into Eternal Shadow.
'Deadnettle', -- Herb with thematic ties to shadow crafting.
'Shadowy Essence' -- Rare Northrend material for shadow crafting.
);
-- Rare materials that relate to shadow resistance gear or shadow-themed weapons
REPLACE INTO mp_material_types (materialId, entry, name)
SELECT
20 AS materialId, -- Unique ID for rare shadow resistance and shadow weapon crafting materials
it.entry,
it.name
FROM item_template it
WHERE
it.name IN (
-- Classic
'Heart of Darkness', -- Rare drop used for shadow resistance gear in TBC and Classic.
'Shadow Oil', -- Crafted alchemical reagent for shadow resistance.
'Black Lotus', -- Rare herb used in high-level shadow potions.
-- TBC
'Primal Shadow', -- Central crafting material for shadow resistance.
'Nightmare Seed', -- Rare herb used in shadow resistance recipes.
'Felcloth', -- Cloth with shadow resistance used in tailoring.
-- WotLK
'Eternal Shadow', -- Core material for shadow resistance crafting.
'Runed Orb' -- Rare material for shadow-themed gear and weapons.
);
-- Common materials that relate to arcane resistance gear or arcane-themed weapons
REPLACE INTO mp_material_types (materialId, entry, name)
SELECT
21 AS materialId,
it.entry,
it.name
FROM item_template it
WHERE
it.name IN (
-- Classic
'Arcane Crystal', -- Rare mining material used in Arcanite Bars.
'Essence of Magic', -- Dropped by arcane elementals.
'Lesser Magic Essence', -- Early enchanting material.
'Greater Magic Essence', -- Enchanting material with arcane ties.
-- TBC
'Mote of Mana', -- Combines into Primal Mana.
-- WotLK
'Crystallized Mana' -- Combines into Eternal Mana.
);
-- Rare materials that relate to arcane resistance gear or arcane-themed weapons
REPLACE INTO mp_material_types (materialId, entry, name)
SELECT
22 AS materialId, -- Unique ID for rare arcane resistance and arcane weapon crafting materials
it.entry,
it.name
FROM item_template it
WHERE
it.name IN (
-- Classic
'Golden Pearl', -- Rare material used in crafting arcane resistance gear.
'Arcanite Bar', -- Transmuted from Arcane Crystals, used in crafting high-end gear.
-- TBC
'Primal Mana', -- Essential for arcane resistance crafting.
'Nether Vortex', -- Raid-exclusive crafting material.
'Void Sphere', -- Rare arcane-themed gem obtained through badge purchases or raids.
-- WotLK
'Eternal Mana', -- Central crafting material for arcane resistance gear.
'Saronite Animus' -- Rare drop from arcane-themed enemies in Icecrown Citadel.
);

View File

@@ -0,0 +1,81 @@
-- Last Update: 2021/08/15
-- Description: Scale factors for Mythic+ dungeons used to normalize dungeon difficulty across expansions.
-- 1. Pre 60 level dungeons (13 dmg_bonus, 2 hp_bonus, max 23, difficulty 3)
INSERT INTO mp_scale_factors (mapId, dmg_bonus, spell_bonus, hp_bonus, difficulty, max)
VALUES
(389, 22, 19,2, 3, 23), -- Ragefire Chasm
(43, 19, 18,2, 3, 23), -- Wailing Caverns
(36, 19, 19,2, 3, 23), -- The Deadmines
(33, 19, 19,2, 3, 23), -- Shadowfang Keep
(34, 19, 19,2, 3, 23), -- The Stockade
(48, 19, 19,2, 3, 23), -- Blackfathom Deeps
(90, 19, 19,2, 3, 23), -- Gnomeregan
(47, 19, 19,2, 3, 23), -- Razorfen Kraul
(189, 19, 19,2, 3, 23), -- Scarlet Monastery (Graveyard)
(129, 19, 19,2, 3, 23), -- Razorfen Downs
(70, 19, 19,2, 3, 23), -- Uldaman
(209, 19, 19,2, 3, 23), -- Zul'Farrak
(349, 19, 19,2, 3, 23) -- Maraudon
ON DUPLICATE KEY UPDATE mapId = mapId;
-- 2. Level 60 dungeons for classic (15 dmg_bonus, 3 hp_bonus, max 25, difficulty 3)
INSERT INTO mp_scale_factors (mapId, dmg_bonus, spell_bonus, hp_bonus, difficulty, max)
VALUES
(289, 17, 20,3, 3, 25), -- Scholomance
(109, 17, 20,3, 3, 25), -- Sunken Temple
(329, 17, 20,3, 3, 25), -- Stratholme
(229, 17, 20,3, 3, 25), -- Blackrock Spire (Lower)
(230, 17, 20,3, 3, 25), -- Blackrock Spire (Upper)
(429, 17, 20,3, 3, 25), -- Dire Maul
(269, 17, 20,3, 3, 25) -- Temple of Atal'Hakkar
ON DUPLICATE KEY UPDATE mapId = mapId;
-- 3. Pre 70 dungeons in Burning Crusade (15 dmg_bonus, 4 hp_bonus, max 26, difficulty 3)
INSERT INTO mp_scale_factors (mapId, dmg_bonus, spell_bonus, hp_bonus, difficulty, max)
VALUES
(542, 16, 14,4, 3, 26), -- Hellfire The Blood Furnace
(543, 16, 14,4, 3, 26), -- Hellfire Ramparts
(545, 16, 14,4, 3, 26), -- Coilfang Steamvaults
(546, 16, 14,4, 3, 26), -- Coilfang Reservoir: The Underbog
(547, 16, 14,4, 3, 26), -- Coilfang Reservoir: The Underbog
(557, 16, 14,4, 3, 26), -- Auchindoun: Mana-Tombs
(558, 16, 14,4, 3, 26), -- Auchindoun: Auchenai Crypts
(560, 16, 14,4, 3, 26) -- Caverns of Time: Old Hillsbrad Foothills
ON DUPLICATE KEY UPDATE mapId = mapId;
-- 4. Level 70 dungeons in Burning Crusade (14 dmg_bonus, 4 hp_bonus, max 29, difficulty 3)
INSERT INTO mp_scale_factors (mapId, dmg_bonus, spell_bonus, hp_bonus, difficulty, max)
VALUES
(540, 14, 13,4, 3, 29), -- Shattered Halls
(556, 14, 13,4, 3, 29), -- Auchindoun: Sethekk Halls
(555, 14, 13,4, 3, 29), -- Auchindoun: Shadow Labyrinth
(553, 14, 13,4, 3, 29), -- Tempest Keep: The Botanica
(554, 14, 13,4, 3, 29), -- Tempest Keep: The Mechanar
(552, 14, 13,4, 3, 29), -- Tempest Keep: The Arcatraz
(585, 14, 13,4, 3, 29) -- Magisters' Terrace
ON DUPLICATE KEY UPDATE mapId = mapId;
-- 5. Pre 80 dungeons in Wrath of the Lich King (17 dmg_bonus, 3 hp_bonus, max 30, difficulty 3)
INSERT INTO mp_scale_factors (mapId, dmg_bonus, spell_bonus, hp_bonus, difficulty, max)
VALUES
(574, 19, 12,3, 3, 30), -- Utgarde Keep
(619, 19, 12,3, 3, 30), -- Ahn'kahet: The Old Kingdom
(576, 19, 12,3, 3, 30), -- The Nexus
(600, 19, 12,3, 3, 30), -- Drak'Tharon Keep
(601, 19, 12,3, 3, 30), -- Azjol-Nerub
(608, 19, 12,3, 3, 30) -- The Violet Hold
ON DUPLICATE KEY UPDATE mapId = mapId;
-- 6. Level 80 dungeons in Wrath of the Lich King (19 dmg_bonus, 4 hp_bonus, max 33, difficulty 3)
INSERT INTO mp_scale_factors (mapId, dmg_bonus, spell_bonus, hp_bonus, difficulty, max)
VALUES
(595, 19, 13,5, 3, 33), -- The Culling of Stratholme
(604, 19, 13,5, 3, 33), -- Gundrak
(599, 19, 13,5, 3, 33), -- Halls of Stone
(602, 19, 13,5, 3, 33), -- Halls of Lightning
(578, 19, 13,5, 3, 33), -- The Oculus
(650, 19, 13,5, 3, 33), -- Trial of the Champion
(632, 19, 13,5, 3, 33), -- The Forge of Souls
(658, 19, 13,5, 3, 33), -- Pit of Saron
(668, 19, 13,5, 3, 33) -- Halls of Reflection
ON DUPLICATE KEY UPDATE mapId = mapId;

View File

@@ -0,0 +1,82 @@
REPLACE creature_classlevelstats (class, `level`, basehp0, basehp1, basehp2,
basemana, basearmor, attackpower, rangedattackpower, damage_base, damage_exp1, damage_exp2) VALUES
(1, 81, 5489.19, 9449.55, 12640.32, 0.00, 10033.36, 714.66, 111.97, 47.9043, 131.5509, 166.3098),
(1, 82, 5641.53, 9700.79, 12855.00, 0.00, 10337.65, 790.54, 121.09, 48.5708, 131.9685, 169.2029),
(1, 83, 5797.46, 9956.22, 13042.96, 0.00, 10642.47, 872.71, 130.74, 49.2374, 132.0348, 171.8720),
(1, 84, 5956.98, 10215.83, 13204.22, 0.00, 10947.81, 961.17, 140.89, 49.9039, 131.7497, 174.3170),
(1, 85, 6120.07, 10479.62, 13338.76, 0.00, 11253.67, 1055.90, 151.56, 50.5705, 131.1133, 176.5379),
(1, 86, 6286.76, 10747.60, 13446.59, 0.00, 11560.05, 1156.93, 162.75, 51.2370, 130.1256, 178.5348),
(1, 87, 6457.02, 11019.76, 13527.71, 0.00, 11866.95, 1264.23, 174.45, 51.9035, 128.7865, 180.3077),
(1, 88, 6630.87, 11296.10, 13582.12, 0.00, 12174.38, 1377.82, 186.66, 52.5700, 127.0960, 181.8565),
(1, 89, 6808.31, 11576.62, 13609.82, 0.00, 12482.32, 1497.69, 199.39, 53.2365, 125.0542, 183.1813),
(1, 90, 6989.33, 11861.33, 13610.80, 0.00, 12790.79, 1623.85, 212.64, 53.9030, 122.6611, 184.2821),
(1, 91, 7173.93, 12150.22, 13585.08, 0.00, 13099.78, 1756.29, 226.40, 54.5695, 119.9166, 185.1587),
(1, 92, 7362.12, 12443.29, 13532.64, 0.00, 13409.29, 1895.01, 240.67, 55.2360, 116.8207, 185.8114),
(1, 93, 7553.90, 12740.55, 13453.49, 0.00, 13719.32, 2040.02, 255.46, 55.9024, 113.3736, 186.2400),
(1, 94, 7749.25, 13041.99, 13347.63, 0.00, 14029.88, 2191.31, 270.76, 56.5689, 109.5750, 186.4446),
(1, 95, 7948.20, 13347.61, 13215.06, 0.00, 14340.95, 2348.88, 286.57, 57.2353, 105.4251, 186.4251),
(1, 96, 8150.72, 13657.41, 13055.78, 0.00, 14652.55, 2512.74, 302.90, 57.9018, 100.9239, 186.1815),
(1, 97, 8356.83, 13971.40, 12869.79, 0.00, 14964.67, 2682.88, 319.75, 58.5682, 96.0713, 185.7140),
(1, 98, 8566.53, 14289.57, 12657.08, 0.00, 15277.30, 2859.31, 337.11, 59.2346, 90.8674, 185.0224),
(1, 99, 8779.81, 14611.92, 12417.67, 0.00, 15590.47, 3042.02, 354.98, 59.9010, 85.3122, 184.1067),
(1, 100, 8996.67, 14938.46, 12151.54, 0.00, 15904.15, 3231.01, 373.37, 60.5674, 79.4055, 182.9670),
(2, 81, 4391.71, 7560.03, 12640.32, 4078.27, 10008.59, 677.15, 87.20, 44.8582, 112.1715, 166.3095),
(2, 82, 4513.65, 7760.95, 12855.00, 4169.46, 10311.04, 749.46, 94.48, 45.5146, 112.7114, 169.2025),
(2, 83, 4638.44, 7965.20, 13042.96, 4261.79, 10613.90, 827.78, 102.16, 46.1711, 113.0756, 171.8714),
(2, 84, 4766.11, 8172.77, 13204.22, 4355.28, 10917.17, 912.11, 110.26, 46.8275, 113.2640, 174.3162),
(2, 85, 4896.64, 8383.67, 13338.76, 4449.92, 11220.87, 1002.43, 118.77, 47.4839, 113.2768, 176.5370),
(2, 86, 5030.04, 8597.90, 13446.59, 4545.71, 11524.99, 1098.77, 127.69, 48.1402, 113.1139, 178.5337),
(2, 87, 5166.31, 8815.44, 13527.71, 4642.65, 11829.52, 1201.10, 137.02, 48.7965, 112.7753, 180.3063),
(2, 88, 5305.45, 9036.31, 13582.12, 4740.74, 12134.48, 1309.44, 146.76, 49.4528, 112.2609, 181.8549),
(2, 89, 5447.45, 9260.51, 13609.82, 4839.99, 12439.85, 1423.78, 156.92, 50.1091, 111.5709, 183.1795),
(2, 90, 5592.32, 9488.03, 13610.80, 4940.38, 12745.64, 1544.13, 167.48, 50.7653, 110.7051, 184.2800),
(2, 91, 5740.05, 9718.88, 13585.08, 5041.93, 13051.85, 1670.48, 178.46, 51.4215, 109.6637, 185.1564),
(2, 92, 5890.66, 9953.05, 13532.64, 5144.64, 13358.47, 1802.84, 189.85, 52.0777, 108.4465, 185.8087),
(2, 93, 6044.13, 10190.55, 13453.49, 5248.49, 13665.52, 1941.20, 201.65, 52.7338, 107.0536, 186.2370),
(2, 94, 6200.47, 10431.37, 13347.63, 5353.49, 13972.98, 2085.56, 213.87, 53.3899, 105.4850, 186.4413),
(2, 95, 6359.67, 10675.51, 13215.06, 5459.65, 14280.87, 2235.93, 226.49, 54.0460, 103.7407, 186.4215),
(2, 96, 6521.74, 10922.98, 13055.78, 5566.96, 14589.17, 2392.30, 239.53, 54.7021, 101.8207, 186.1776),
(2, 97, 6686.68, 11173.77, 12869.79, 5675.42, 14897.89, 2554.68, 252.97, 55.3581, 99.7250, 185.7096),
(2, 98, 6854.49, 11427.89, 12657.08, 5785.03, 15207.03, 2723.06, 266.83, 56.0141, 97.4536, 185.0176),
(2, 99, 7025.16, 11685.34, 12417.67, 5895.80, 15516.59, 2897.44, 281.10, 56.6700, 95.0065, 184.1016),
(2, 100, 7198.70, 11946.10, 12151.54, 6007.71, 15826.56, 3077.83, 295.79, 57.3260, 92.3837, 182.9615),
(4, 81, 5489.19, 8862.85, 12640.32, 0.00, 9050.32, 714.66, 111.97, 47.9043, 131.5509, 166.3098),
(4, 82, 5641.53, 8991.59, 12855.00, 0.00, 9324.66, 790.54, 121.09, 48.5708, 131.9685, 169.2029),
(4, 83, 5797.46, 9113.21, 13042.96, 0.00, 9599.46, 872.71, 130.74, 49.2374, 132.0348, 171.8720),
(4, 84, 5956.98, 9227.74, 13204.22, 0.00, 9874.72, 961.17, 140.89, 49.9039, 131.7497, 174.3170),
(4, 85, 6120.07, 9335.15, 13338.76, 0.00, 10150.45, 1055.90, 151.56, 50.5705, 131.1133, 176.5379),
(4, 86, 6286.76, 9435.46, 13446.59, 0.00, 10426.65, 1156.93, 162.75, 51.2370, 130.1256, 178.5348),
(4, 87, 6457.02, 9528.66, 13527.71, 0.00, 10703.30, 1264.23, 174.45, 51.9035, 128.7865, 180.3077),
(4, 88, 6630.87, 9614.75, 13582.12, 0.00, 10980.43, 1377.82, 186.66, 52.5700, 127.0960, 181.8565),
(4, 89, 6808.31, 9693.74, 13609.82, 0.00, 11258.01, 1497.69, 199.39, 53.2365, 125.0542, 183.1813),
(4, 90, 6989.33, 9765.61, 13610.80, 0.00, 11536.07, 1623.85, 212.64, 53.9030, 122.6611, 184.2821),
(4, 91, 7173.93, 9830.39, 13585.08, 0.00, 11814.58, 1756.29, 226.40, 54.5695, 119.9166, 185.1587),
(4, 92, 7362.12, 9888.05, 13532.64, 0.00, 12093.56, 1895.01, 240.67, 55.2360, 116.8207, 185.8114),
(4, 93, 7553.90, 9938.61, 13453.49, 0.00, 12373.01, 2040.02, 255.46, 55.9024, 113.3736, 186.2400),
(4, 94, 7749.25, 9982.06, 13347.63, 0.00, 12652.92, 2191.31, 270.76, 56.5689, 109.5750, 186.4446),
(4, 95, 7948.20, 10018.40, 13215.06, 0.00, 12933.29, 2348.88, 286.57, 57.2353, 105.4251, 186.4251),
(4, 96, 8150.72, 10047.64, 13055.78, 0.00, 13214.13, 2512.74, 302.90, 57.9018, 100.9239, 186.1815),
(4, 97, 8356.83, 10069.76, 12869.79, 0.00, 13495.44, 2682.88, 319.75, 58.5682, 96.0713, 185.7140),
(4, 98, 8566.53, 10084.79, 12657.08, 0.00, 13777.20, 2859.31, 337.11, 59.2346, 90.8674, 185.0224),
(4, 99, 8779.81, 10092.70, 12417.67, 0.00, 14059.44, 3042.02, 354.98, 59.9010, 85.3122, 184.1067),
(4, 100, 8996.67, 10093.51, 12151.54, 0.00, 14342.13, 3231.01, 373.37, 60.5674, 79.4055, 182.9670),
(8, 81, 3853.15, 6497.25, 10112.28, 8981.17, 8066.17, 319.55, 75.82, 40.6653, 133.4978, 153.8368),
(8, 82, 3957.29, 6645.09, 10284.07, 9154.91, 8310.42, 353.50, 82.44, 41.2315, 137.6156, 156.5130),
(8, 83, 4063.40, 6793.05, 10434.49, 9330.41, 8555.06, 390.26, 89.47, 41.7978, 141.7353, 158.9818),
(8, 84, 4171.48, 6941.12, 10563.54, 9507.68, 8800.11, 429.82, 96.90, 42.3640, 145.8569, 161.2435),
(8, 85, 4281.53, 7089.30, 10671.23, 9686.70, 9045.55, 472.19, 104.72, 42.9302, 149.9803, 163.2979),
(8, 86, 4393.56, 7237.60, 10757.54, 9867.49, 9291.39, 517.36, 112.95, 43.4965, 154.1056, 165.1450),
(8, 87, 4507.55, 7386.01, 10822.49, 10050.04, 9537.63, 565.34, 121.58, 44.0627, 158.2327, 166.7849),
(8, 88, 4623.52, 7534.53, 10866.07, 10234.35, 9784.27, 616.12, 130.61, 44.6289, 162.3617, 168.2176),
(8, 89, 4741.45, 7683.17, 10888.28, 10420.42, 10031.31, 669.71, 140.04, 45.1951, 166.4926, 169.4430),
(8, 90, 4861.36, 7831.91, 10889.12, 10608.25, 10278.75, 726.10, 149.87, 45.7613, 170.6253, 170.4611),
(8, 91, 4983.24, 7980.78, 10868.59, 10797.85, 10526.58, 785.30, 160.10, 46.3276, 174.7599, 171.2721),
(8, 92, 5107.09, 8129.75, 10826.70, 10989.20, 10774.82, 847.30, 170.73, 46.8938, 178.8964, 171.8757),
(8, 93, 5232.91, 8278.84, 10763.43, 11182.32, 11023.45, 912.11, 181.76, 47.4600, 183.0347, 172.2722),
(8, 94, 5360.71, 8428.05, 10678.80, 11377.19, 11272.48, 979.72, 193.19, 48.0262, 187.1749, 172.4614),
(8, 95, 5490.47, 8577.36, 10572.80, 11573.83, 11521.91, 1050.14, 205.03, 48.5924, 191.3170, 172.4433),
(8, 96, 5622.20, 8726.79, 10445.43, 11772.23, 11771.74, 1123.36, 217.26, 49.1586, 195.4609, 172.2180),
(8, 97, 5755.91, 8876.34, 10296.69, 11972.39, 12021.96, 1199.39, 229.90, 49.7248, 199.6067, 171.7855),
(8, 98, 5891.58, 9025.99, 10126.58, 12174.31, 12272.59, 1278.23, 242.93, 50.2910, 203.7543, 171.1457),
(8, 99, 6029.23, 9175.76, 9935.10, 12378.00, 12523.61, 1359.87, 256.37, 50.8572, 207.9038, 170.2986),
(8, 100, 6168.85, 9325.65, 9722.26, 12583.44, 12775.04, 1444.31, 270.20, 51.4234, 212.0552, 169.2444);

View File

@@ -0,0 +1,247 @@
-- acore_world.pet_levelstats
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (575, 81, 4399, 2307, 53, 112, 68, 39, 148, 96, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (575, 82, 4473, 2349, 53, 114, 70, 40, 151, 98, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (575, 83, 4548, 2390, 52, 116, 71, 41, 154, 100, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (575, 84, 4622, 2432, 51, 118, 72, 41, 156, 102, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (575, 85, 4697, 2473, 50, 120, 73, 42, 159, 103, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (575, 86, 4771, 2515, 49, 121, 74, 43, 162, 105, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (575, 87, 4846, 2556, 49, 123, 75, 43, 164, 107, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (1860, 81, 4219, 2751, 11953, 200, 57, 325, 142, 159, 185, 284);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (1860, 82, 4280, 2790, 12131, 203, 58, 330, 144, 161, 188, 288);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (1860, 83, 4341, 2829, 12308, 205, 59, 334, 146, 163, 191, 293);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (1860, 84, 4402, 2868, 12486, 208, 60, 338, 148, 165, 194, 297);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (1860, 85, 4463, 2907, 12664, 211, 61, 342, 150, 167, 197, 302);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (1860, 86, 4525, 2945, 12841, 213, 61, 347, 152, 169, 200, 306);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (1860, 87, 4586, 2984, 13019, 216, 62, 351, 153, 171, 203, 311);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (3450, 81, 1279, 2497, 1, 188, 43, 118, 386, 388, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (3450, 82, 1297, 2530, 1, 190, 43, 120, 391, 393, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (3450, 83, 1315, 2562, 1, 193, 43, 121, 396, 398, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (3450, 84, 1333, 2595, 1, 195, 44, 123, 401, 403, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (3450, 85, 1351, 2628, 1, 197, 44, 124, 406, 409, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (3450, 86, 1368, 2661, 1, 200, 44, 125, 411, 414, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (3450, 87, 1386, 2694, 1, 202, 45, 127, 417, 419, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (10979, 81, 1279, 2497, 1, 188, 43, 118, 386, 388, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (10979, 82, 1297, 2530, 1, 190, 43, 120, 391, 393, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (10979, 83, 1315, 2562, 1, 193, 43, 121, 396, 398, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (10979, 84, 1333, 2595, 1, 195, 44, 123, 401, 403, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (10979, 85, 1351, 2628, 1, 197, 44, 124, 406, 409, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (10979, 86, 1368, 2661, 1, 200, 44, 125, 411, 414, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (10979, 87, 1386, 2694, 1, 202, 45, 127, 417, 419, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (14385, 81, 4635, 2170, 5204, 180, 97, 94, 101, 89, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (14385, 82, 4693, 2197, 5273, 182, 98, 95, 102, 90, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (14385, 83, 4750, 2223, 5341, 184, 99, 96, 103, 91, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (14385, 84, 4808, 2249, 5410, 186, 100, 97, 104, 92, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (14385, 85, 4865, 2275, 5478, 188, 101, 98, 105, 93, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (14385, 86, 4923, 2301, 5547, 190, 102, 99, 106, 94, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (14385, 87, 4980, 2328, 5616, 192, 103, 100, 107, 95, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (15438, 81, 4280, 1253, 601, 139, 86, 65, 211, 233, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (15438, 82, 4356, 1275, 612, 142, 88, 66, 214, 237, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (15438, 83, 4432, 1297, 622, 144, 89, 67, 218, 241, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (15438, 84, 4508, 1319, 633, 147, 91, 68, 222, 245, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (15438, 85, 4584, 1341, 644, 149, 92, 69, 225, 249, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (15438, 86, 4660, 1364, 654, 152, 94, 71, 229, 253, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (15438, 87, 4735, 1386, 665, 154, 95, 72, 233, 257, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (329, 81, 4251, 2478, 1602, 131, 40, 225, 367, 352, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (329, 82, 4319, 2511, 1623, 133, 40, 227, 372, 357, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (329, 83, 4387, 2545, 1644, 134, 41, 230, 377, 361, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (329, 84, 4455, 2578, 1665, 135, 41, 232, 382, 366, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (329, 85, 4523, 2611, 1685, 137, 42, 234, 387, 371, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (329, 86, 4591, 2645, 1706, 138, 42, 237, 392, 376, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (329, 87, 4658, 2678, 1727, 139, 43, 239, 397, 380, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (26101, 81, 1045, 1133, 275, 84, 21, 56, 185, 206, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (26101, 82, 1064, 1153, 280, 85, 21, 57, 188, 209, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (26101, 83, 1082, 1174, 285, 87, 21, 58, 191, 213, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (26101, 84, 1100, 1194, 289, 88, 22, 59, 195, 217, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (26101, 85, 1119, 1214, 294, 90, 22, 60, 198, 220, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (26101, 86, 1137, 1234, 299, 91, 23, 61, 201, 224, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (26101, 87, 1155, 1255, 303, 93, 23, 62, 205, 228, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (24815, 81, 1279, 2497, 1, 188, 43, 118, 386, 388, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (24815, 82, 1297, 2530, 1, 190, 43, 120, 391, 393, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (24815, 83, 1315, 2562, 1, 193, 43, 121, 396, 398, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (24815, 84, 1333, 2595, 1, 195, 44, 123, 401, 403, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (24815, 85, 1351, 2628, 1, 197, 44, 124, 406, 409, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (24815, 86, 1368, 2661, 1, 200, 44, 125, 411, 414, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (24815, 87, 1386, 2694, 1, 202, 45, 127, 417, 419, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (15352, 81, 4774, 1, 4996, 122, 51, 75, 97, 257, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (15352, 82, 4858, 1, 5084, 124, 52, 76, 99, 261, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (15352, 83, 4942, 1, 5172, 127, 53, 78, 100, 266, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (15352, 84, 5026, 1, 5260, 129, 54, 79, 102, 270, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (15352, 85, 5111, 1, 5349, 131, 54, 80, 104, 275, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (15352, 86, 5195, 1, 5437, 133, 55, 82, 106, 279, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (15352, 87, 5279, 1, 5525, 135, 56, 83, 107, 284, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (24656, 81, 3348, 2277, 4266, 83, 59, 161, 78, 77, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (24656, 82, 3408, 2318, 4343, 84, 60, 164, 79, 78, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (24656, 83, 3467, 2358, 4419, 85, 61, 167, 81, 80, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (24656, 84, 3527, 2399, 4496, 87, 63, 170, 82, 81, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (24656, 85, 3587, 2440, 4572, 88, 64, 173, 83, 82, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (24656, 86, 3647, 2481, 4649, 90, 65, 176, 85, 84, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (24656, 87, 3707, 2522, 4725, 91, 66, 178, 86, 85, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (25566, 81, 3701, 3657, 4117, 168, 80, 219, 265, 259, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (25566, 82, 3763, 3711, 4191, 170, 80, 222, 268, 262, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (25566, 83, 3824, 3765, 4265, 172, 81, 226, 271, 265, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (25566, 84, 3885, 3818, 4338, 174, 82, 229, 274, 268, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (25566, 85, 3947, 3872, 4412, 176, 83, 232, 277, 271, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (25566, 86, 4008, 3926, 4486, 178, 84, 235, 280, 274, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (25566, 87, 4069, 3980, 4560, 180, 85, 238, 283, 276, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (26125, 81, 4635, 2170, 5204, 187, 105, 107, 101, 89, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (26125, 82, 4693, 2197, 5273, 189, 106, 108, 102, 90, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (26125, 83, 4750, 2223, 5341, 192, 107, 109, 103, 91, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (26125, 84, 4808, 2249, 5410, 194, 108, 111, 104, 92, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (26125, 85, 4865, 2275, 5478, 196, 109, 112, 105, 93, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (26125, 86, 4923, 2301, 5547, 198, 110, 113, 106, 94, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (26125, 87, 4980, 2328, 5616, 200, 112, 115, 107, 95, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (5766, 81, 2931, 1473, 5001, 178, 88, 327, 226, 210, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (5766, 82, 2976, 1488, 5083, 181, 89, 331, 228, 212, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (5766, 83, 3020, 1504, 5165, 183, 90, 335, 231, 214, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (5766, 84, 3064, 1519, 5247, 185, 91, 340, 233, 217, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (5766, 85, 3108, 1534, 5329, 187, 93, 344, 236, 219, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (5766, 86, 3152, 1550, 5411, 190, 94, 348, 238, 221, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (5766, 87, 3196, 1565, 5492, 192, 95, 352, 241, 223, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (416, 81, 2865, 2704, 4623, 185, 49, 113, 373, 306, 207, 312);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (416, 82, 2906, 2741, 4691, 188, 50, 114, 377, 310, 211, 317);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (416, 83, 2947, 2778, 4758, 190, 50, 116, 382, 314, 214, 322);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (416, 84, 2989, 2815, 4826, 192, 51, 117, 387, 318, 217, 327);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (416, 85, 3030, 2852, 4894, 195, 51, 118, 391, 322, 220, 332);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (416, 86, 3071, 2890, 4962, 197, 51, 119, 396, 326, 224, 337);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (416, 87, 3112, 2927, 5030, 200, 52, 121, 401, 330, 227, 342);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (5058, 81, 5070, 2339, 7812, 179, 131, 359, 121, 120, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (5058, 82, 5147, 2367, 7932, 181, 133, 364, 123, 121, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (5058, 83, 5224, 2396, 8053, 184, 135, 368, 124, 122, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (5058, 84, 5300, 2424, 8174, 186, 136, 373, 125, 124, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (5058, 85, 5377, 2453, 8295, 188, 138, 378, 127, 125, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (5058, 86, 5454, 2481, 8415, 190, 139, 382, 128, 126, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (5058, 87, 5530, 2509, 8536, 193, 141, 387, 130, 127, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (6250, 81, 2931, 1473, 5001, 178, 88, 327, 226, 210, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (6250, 82, 2976, 1488, 5083, 181, 89, 331, 228, 212, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (6250, 83, 3020, 1504, 5165, 183, 90, 335, 231, 214, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (6250, 84, 3064, 1519, 5247, 185, 91, 340, 233, 217, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (6250, 85, 3108, 1534, 5329, 187, 93, 344, 236, 219, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (6250, 86, 3152, 1550, 5411, 190, 94, 348, 238, 221, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (6250, 87, 3196, 1565, 5492, 192, 95, 352, 241, 223, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (8996, 81, 3280, 2467, 4389, 197, 90, 244, 252, 310, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (8996, 82, 3319, 2500, 4438, 199, 90, 247, 256, 315, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (8996, 83, 3357, 2533, 4487, 202, 91, 250, 260, 319, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (8996, 84, 3396, 2566, 4536, 205, 92, 253, 264, 324, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (8996, 85, 3435, 2600, 4585, 207, 93, 255, 268, 328, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (8996, 86, 3473, 2633, 4634, 210, 94, 258, 271, 333, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (8996, 87, 3512, 2666, 4683, 212, 95, 261, 275, 337, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (30230, 81, 4635, 2170, 5204, 187, 105, 107, 101, 89, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (30230, 82, 4693, 2197, 5273, 189, 106, 108, 102, 90, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (30230, 83, 4750, 2223, 5341, 192, 107, 109, 103, 91, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (30230, 84, 4808, 2249, 5410, 194, 108, 111, 104, 92, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (30230, 85, 4865, 2275, 5478, 196, 109, 112, 105, 93, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (30230, 86, 4923, 2301, 5547, 198, 110, 113, 106, 94, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (30230, 87, 4980, 2328, 5616, 200, 112, 115, 107, 95, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (1, 81, 4531, 1, 7244, 183, 141, 347, 65, 108, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (1, 82, 4596, 1, 7351, 185, 143, 352, 66, 109, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (1, 83, 4661, 1, 7458, 187, 145, 356, 66, 110, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (1, 84, 4725, 1, 7565, 189, 146, 360, 67, 111, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (1, 85, 4790, 1, 7671, 192, 148, 365, 67, 113, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (1, 86, 4855, 1, 7778, 194, 150, 369, 68, 114, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (1, 87, 4920, 1, 7885, 196, 152, 374, 69, 115, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (37994, 81, 6409, 2978, 4804, 177, 126, 128, 311, 215, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (37994, 82, 6505, 3018, 4873, 179, 127, 129, 316, 218, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (37994, 83, 6602, 3057, 4943, 181, 129, 130, 320, 221, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (37994, 84, 6698, 3096, 5013, 184, 131, 131, 325, 223, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (37994, 85, 6794, 3135, 5083, 186, 132, 132, 330, 226, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (37994, 86, 6891, 3175, 5153, 188, 134, 133, 334, 229, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (37994, 87, 6987, 3214, 5223, 190, 135, 134, 339, 232, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (22362, 81, 1279, 2497, 1084, 188, 43, 118, 386, 388, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (22362, 82, 1297, 2530, 1095, 190, 43, 120, 391, 393, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (22362, 83, 1315, 2562, 1107, 193, 43, 121, 396, 398, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (22362, 84, 1333, 2595, 1118, 195, 44, 123, 401, 403, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (22362, 85, 1351, 2628, 1129, 197, 44, 124, 406, 409, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (22362, 86, 1368, 2661, 1141, 200, 44, 125, 411, 414, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (22362, 87, 1386, 2694, 1152, 202, 45, 127, 417, 419, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (1863, 81, 3508, 2779, 7257, 204, 59, 330, 146, 162, 240, 361);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (1863, 82, 3560, 2819, 7368, 207, 60, 335, 148, 165, 244, 367);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (1863, 83, 3612, 2859, 7479, 210, 60, 339, 150, 167, 248, 372);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (1863, 84, 3664, 2899, 7589, 213, 61, 344, 152, 169, 252, 378);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (1863, 85, 3717, 2940, 7700, 216, 62, 349, 154, 171, 255, 384);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (1863, 86, 3769, 2980, 7810, 218, 63, 353, 156, 174, 259, 390);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (1863, 87, 3821, 3020, 7921, 221, 64, 358, 158, 176, 263, 395);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (19668, 81, 1728, 3131, 35, 126, 88, 54, 202, 131, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (19668, 82, 1756, 3186, 34, 128, 90, 55, 206, 133, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (19668, 83, 1785, 3241, 33, 130, 91, 56, 209, 136, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (19668, 84, 1814, 3296, 32, 132, 93, 57, 213, 138, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (19668, 85, 1842, 3351, 30, 134, 94, 58, 217, 140, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (19668, 86, 1871, 3407, 29, 136, 95, 59, 220, 143, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (19668, 87, 1900, 3462, 28, 139, 97, 60, 224, 145, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (89, 81, 16398, 3013, 11256, 186, 54, 296, 132, 148, 823, 1145);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (89, 82, 16677, 3063, 11445, 189, 55, 301, 134, 150, 837, 1165);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (89, 83, 16955, 3113, 11634, 192, 56, 306, 137, 153, 851, 1184);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (89, 84, 17234, 3162, 11823, 195, 56, 311, 139, 155, 865, 1204);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (89, 85, 17512, 3212, 12012, 198, 57, 316, 141, 158, 879, 1223);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (89, 86, 17790, 3262, 12201, 201, 58, 320, 143, 160, 892, 1243);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (89, 87, 18069, 3311, 12390, 204, 59, 325, 145, 163, 906, 1262);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (3939, 81, 3783, 3632, 5236, 172, 79, 331, 260, 223, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (3939, 82, 3842, 3686, 5322, 175, 80, 335, 263, 226, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (3939, 83, 3902, 3740, 5408, 177, 81, 339, 266, 228, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (3939, 84, 3961, 3795, 5494, 179, 82, 343, 269, 231, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (3939, 85, 4021, 3849, 5580, 181, 83, 348, 272, 233, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (3939, 86, 4080, 3903, 5666, 183, 84, 352, 275, 235, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (3939, 87, 4139, 3957, 5752, 185, 85, 356, 278, 238, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (10928, 81, 1900, 2464, 2053, 202, 89, 248, 242, 316, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (10928, 82, 1924, 2497, 2079, 205, 90, 251, 246, 321, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (10928, 83, 1948, 2530, 2105, 207, 91, 254, 250, 326, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (10928, 84, 1973, 2564, 2130, 210, 92, 257, 254, 330, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (10928, 85, 1997, 2597, 2156, 213, 93, 260, 258, 335, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (10928, 86, 2021, 2630, 2182, 216, 94, 263, 262, 340, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (10928, 87, 2046, 2663, 2208, 219, 95, 266, 265, 345, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (12922, 81, 1279, 2497, 1, 188, 43, 118, 386, 388, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (12922, 82, 1297, 2530, 1, 190, 43, 120, 391, 393, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (12922, 83, 1315, 2562, 1, 193, 43, 121, 396, 398, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (12922, 84, 1333, 2595, 1, 195, 44, 123, 401, 403, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (12922, 85, 1351, 2628, 1, 197, 44, 124, 406, 409, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (12922, 86, 1368, 2661, 1, 200, 44, 125, 411, 414, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (12922, 87, 1386, 2694, 1, 202, 45, 127, 417, 419, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (510, 81, 6409, 2978, 4804, 177, 126, 128, 311, 215, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (510, 82, 6505, 3018, 4873, 179, 127, 129, 316, 218, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (510, 83, 6602, 3057, 4943, 181, 129, 130, 320, 221, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (510, 84, 6698, 3096, 5013, 184, 131, 131, 325, 223, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (510, 85, 6794, 3135, 5083, 186, 132, 132, 330, 226, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (510, 86, 6891, 3175, 5153, 188, 134, 133, 334, 229, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (510, 87, 6987, 3214, 5223, 190, 135, 134, 339, 232, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (8477, 81, 3280, 2467, 4389, 197, 90, 244, 252, 310, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (8477, 82, 3319, 2500, 4438, 199, 90, 247, 256, 315, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (8477, 83, 3357, 2533, 4487, 202, 91, 250, 260, 319, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (8477, 84, 3396, 2566, 4536, 205, 92, 253, 264, 324, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (8477, 85, 3435, 2600, 4585, 207, 93, 255, 268, 328, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (8477, 86, 3473, 2633, 4634, 210, 94, 258, 271, 333, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (8477, 87, 3512, 2666, 4683, 212, 95, 261, 275, 337, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (15214, 81, 3783, 3632, 5236, 172, 79, 331, 260, 223, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (15214, 82, 3842, 3686, 5322, 175, 80, 335, 263, 226, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (15214, 83, 3902, 3740, 5408, 177, 81, 339, 266, 228, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (15214, 84, 3961, 3795, 5494, 179, 82, 343, 269, 231, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (15214, 85, 4021, 3849, 5580, 181, 83, 348, 272, 233, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (15214, 86, 4080, 3903, 5666, 183, 84, 352, 275, 235, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (15214, 87, 4139, 3957, 5752, 185, 85, 356, 278, 238, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (17252, 81, 3926, 2540, 9780, 186, 54, 296, 132, 148, 216, 327);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (17252, 82, 3992, 2582, 9944, 189, 55, 301, 134, 150, 219, 333);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (17252, 83, 4057, 2624, 10109, 192, 56, 306, 137, 153, 223, 338);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (17252, 84, 4122, 2666, 10273, 195, 56, 311, 139, 155, 227, 344);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (17252, 85, 4187, 2708, 10437, 198, 57, 316, 141, 158, 230, 349);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (17252, 86, 4253, 2750, 10601, 201, 58, 320, 143, 160, 234, 355);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (17252, 87, 4318, 2792, 10765, 204, 59, 325, 145, 163, 238, 361);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (24476, 81, 2931, 1473, 5001, 178, 88, 327, 226, 210, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (24476, 82, 2976, 1488, 5083, 181, 89, 331, 228, 212, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (24476, 83, 3020, 1504, 5165, 183, 90, 335, 231, 214, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (24476, 84, 3064, 1519, 5247, 185, 91, 340, 233, 217, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (24476, 85, 3108, 1534, 5329, 187, 93, 344, 236, 219, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (24476, 86, 3152, 1550, 5411, 190, 94, 348, 238, 221, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (24476, 87, 3196, 1565, 5492, 192, 95, 352, 241, 223, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (25553, 81, 3783, 3632, 5236, 172, 79, 331, 260, 223, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (25553, 82, 3842, 3686, 5322, 175, 80, 335, 263, 226, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (25553, 83, 3902, 3740, 5408, 177, 81, 339, 266, 228, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (25553, 84, 3961, 3795, 5494, 179, 82, 343, 269, 231, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (25553, 85, 4021, 3849, 5580, 181, 83, 348, 272, 233, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (25553, 86, 4080, 3903, 5666, 183, 84, 352, 275, 235, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (25553, 87, 4139, 3957, 5752, 185, 85, 356, 278, 238, 1, 1);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (417, 81, 3713, 2790, 5836, 205, 59, 332, 147, 163, 170, 262);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (417, 82, 3770, 2832, 5928, 208, 60, 337, 149, 166, 173, 267);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (417, 83, 3827, 2874, 6019, 211, 61, 342, 151, 168, 175, 271);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (417, 84, 3884, 2916, 6111, 214, 62, 347, 153, 171, 178, 275);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (417, 85, 3941, 2958, 6203, 217, 63, 352, 155, 173, 181, 280);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (417, 86, 3998, 3000, 6294, 221, 63, 357, 158, 176, 184, 284);
REPLACE INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) VALUES (417, 87, 4055, 3042, 6386, 224, 64, 361, 160, 178, 187, 295);

View File

@@ -0,0 +1,51 @@
/** Attempts to insert all post-80 player class stats into the database. */
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (1, 81, 9485, 0, 209, 138, 193, 45, 71);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (1, 82, 10434, 0, 211, 139, 195, 45, 72);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (1, 83, 11477, 0, 213, 140, 197, 45, 73);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (1, 84, 12594, 0, 215, 142, 199, 46, 74);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (1, 85, 13793, 0, 218, 143, 201, 46, 75);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (2, 81, 7449, 4564, 166, 100, 158, 109, 112);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (2, 82, 8078, 4693, 169, 101, 161, 111, 113);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (2, 83, 8737, 4822, 172, 103, 164, 112, 115);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (2, 84, 9427, 4951, 175, 104, 166, 114, 116);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (2, 85, 10151, 5080, 178, 106, 169, 116, 118);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (3, 81, 7482, 4916, 78, 189, 134, 94, 102);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (3, 82, 8070, 5084, 79, 192, 136, 95, 103);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (3, 83, 8681, 5252, 80, 195, 138, 97, 105);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (3, 84, 9315, 5419, 81, 198, 140, 98, 107);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (3, 85, 9973, 5587, 82, 201, 142, 100, 108);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (4, 81, 7246, 0, 121, 200, 113, 47, 71);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (4, 82, 7814, 0, 123, 203, 114, 48, 72);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (4, 83, 8407, 0, 124, 206, 116, 48, 73);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (4, 84, 9025, 0, 126, 209, 117, 49, 74);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (4, 85, 9670, 0, 128, 212, 119, 50, 75);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (5, 81, 7395, 4153, 49, 57, 74, 197, 205);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (5, 82, 7975, 4280, 49, 58, 75, 201, 208);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (5, 83, 8581, 4407, 50, 58, 76, 204, 211);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (5, 84, 9215, 4534, 50, 59, 77, 207, 215);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (5, 85, 9876, 4661, 51, 60, 78, 210, 218);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (6, 81, 8118, 0, 176, 114, 166, 37, 58);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (6, 82, 8740, 0, 179, 116, 169, 37, 59);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (6, 83, 9390, 0, 181, 117, 172, 37, 60);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (6, 84, 10070, 0, 184, 119, 175, 38, 61);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (6, 85, 10802, 0, 187, 121, 178, 38, 61);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (7, 81, 7382, 4562, 123, 77, 139, 131, 145);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (7, 82, 7981, 4692, 123, 78, 141, 134, 147);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (7, 83, 8607, 4823, 125, 79, 143, 137, 149);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (7, 84, 9259, 4953, 126, 80, 145, 139, 151);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (7, 85, 9939, 5083, 128, 81, 147, 142, 153);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (8, 81, 7382, 3446, 39, 47, 61, 207, 197);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (8, 82, 7981, 3546, 39, 47, 62, 211, 201);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (8, 83, 8607, 3647, 40, 48, 63, 214, 204);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (8, 84, 9259, 3748, 40, 48, 63, 218, 208);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (8, 85, 9939, 3849, 40, 49, 64, 221, 211);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (9, 81, 7153, 3690, 61, 69, 92, 158, 165);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (9, 82, 7700, 3817, 62, 70, 93, 161, 168);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (9, 83, 8266, 3944, 63, 71, 94, 164, 171);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (9, 84, 8852, 4072, 63, 72, 95, 166, 175);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (9, 85, 9460, 4199, 64, 73, 96, 169, 178);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (11, 81, 5694, 3227, 99, 90, 106, 163, 180);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (11, 82, 6132, 3342, 100, 91, 108, 166, 183);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (11, 83, 6596, 3458, 101, 92, 109, 168, 186);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (11, 84, 7086, 3573, 102, 94, 111, 171, 190);
insert ignore into acore_world.player_class_stats (Class, Level, BaseHP, BaseMana, Strength, Agility, Stamina, Intellect, Spirit) values (11, 85, 7601, 3688, 104, 95, 113, 174, 193);

View File

@@ -0,0 +1,253 @@
-- SQL Script to Insert 50 Ranks for Each Stat
REPLACE INTO mp_upgrade_ranks (upgradeRank, advancementId, materialId1, materialCost1, materialId2, materialCost2, materialId3, materialCost3, minIncrease1, maxIncrease1, minIncrease2, maxIncrease2, minIncrease3, maxIncrease3, chanceCost1, chanceCost2, chanceCost3) VALUES
(1, 3, 7, 100, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 20, 50, 75),
(2, 3, 7, 150, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 23, 53, 78),
(3, 3, 7, 200, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 26, 56, 81),
(4, 3, 7, 250, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 29, 59, 84),
(5, 3, 7, 300, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 32, 62, 87),
(6, 3, 7, 350, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 35, 65, 90),
(7, 3, 7, 400, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 38, 68, 93),
(8, 3, 7, 450, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 41, 71, 96),
(9, 3, 7, 500, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 44, 74, 99),
(10, 3, 7, 550, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 47, 77, 102),
(11, 3, 7, 500, 8, 0, 0, 0, 3, 12, 7, 12, 12, 15, 50, 80, 105),
(12, 3, 7, 525, 8, 10, 0, 0, 3, 12, 7, 12, 12, 15, 53, 83, 108),
(13, 3, 7, 550, 8, 20, 0, 0, 3, 12, 7, 12, 12, 15, 56, 86, 111),
(14, 3, 7, 575, 8, 30, 0, 0, 3, 12, 7, 12, 12, 15, 59, 89, 114),
(15, 3, 7, 600, 8, 40, 0, 0, 3, 12, 7, 12, 12, 15, 62, 92, 117),
(16, 3, 7, 625, 8, 50, 0, 0, 3, 12, 7, 12, 12, 15, 65, 95, 120),
(17, 3, 7, 650, 8, 60, 0, 0, 3, 12, 7, 12, 12, 15, 68, 98, 123),
(18, 3, 7, 675, 8, 70, 0, 0, 3, 12, 7, 12, 12, 15, 71, 101, 126),
(19, 3, 7, 700, 8, 80, 0, 0, 3, 12, 7, 12, 12, 15, 74, 104, 129),
(20, 3, 7, 725, 8, 90, 0, 0, 3, 12, 7, 12, 12, 15, 77, 107, 132),
(21, 3, 7, 750, 8, 100, 0, 0, 5, 14, 9, 14, 16, 19, 80, 110, 135),
(22, 3, 7, 775, 8, 110, 0, 0, 5, 14, 9, 14, 16, 19, 83, 113, 138),
(23, 3, 7, 800, 8, 120, 0, 0, 5, 14, 9, 14, 16, 19, 86, 116, 141),
(24, 3, 7, 825, 8, 130, 0, 0, 5, 14, 9, 14, 16, 19, 89, 119, 144),
(25, 3, 7, 850, 8, 140, 0, 0, 5, 14, 9, 14, 16, 19, 92, 122, 147),
(26, 3, 7, 875, 8, 150, 0, 0, 5, 14, 9, 14, 16, 19, 95, 125, 150),
(27, 3, 7, 900, 8, 160, 0, 0, 5, 14, 9, 14, 16, 19, 98, 128, 153),
(28, 3, 7, 925, 8, 170, 0, 0, 5, 14, 9, 14, 16, 19, 101, 131, 156),
(29, 3, 7, 950, 8, 180, 0, 0, 5, 14, 9, 14, 16, 19, 104, 134, 159),
(30, 3, 7, 1000, 8, 190, 20, 3, 5, 14, 9, 14, 16, 19, 107, 137, 162),
(31, 3, 7, 1018, 8, 200, 20, 6, 7, 16, 11, 16, 20, 23, 110, 140, 165),
(32, 3, 7, 1036, 8, 210, 20, 9, 7, 16, 11, 16, 20, 23, 113, 143, 168),
(33, 3, 7, 1054, 8, 220, 20, 12, 7, 16, 11, 16, 20, 23, 116, 146, 171),
(34, 3, 7, 1072, 8, 230, 20, 15, 7, 16, 11, 16, 20, 23, 119, 149, 174),
(35, 3, 7, 1090, 8, 240, 20, 18, 7, 16, 11, 16, 20, 23, 122, 152, 177),
(36, 3, 7, 1108, 8, 250, 20, 21, 7, 16, 11, 16, 20, 23, 125, 155, 180),
(37, 3, 7, 1126, 8, 260, 20, 24, 7, 16, 11, 16, 20, 23, 128, 158, 183),
(38, 3, 7, 1144, 8, 270, 20, 27, 7, 16, 11, 16, 20, 23, 131, 161, 186),
(39, 3, 7, 1162, 8, 280, 20, 30, 7, 16, 11, 16, 20, 23, 134, 164, 189),
(40, 3, 7, 1180, 8, 290, 20, 33, 7, 16, 11, 16, 20, 23, 137, 167, 192),
(41, 3, 7, 1198, 8, 300, 20, 36, 9, 18, 13, 18, 24, 27, 140, 170, 195),
(42, 3, 7, 1216, 8, 310, 20, 39, 9, 18, 13, 18, 24, 27, 143, 173, 198),
(43, 3, 7, 1234, 8, 320, 20, 42, 9, 18, 13, 18, 24, 27, 146, 176, 201),
(44, 3, 7, 1252, 8, 330, 20, 45, 9, 18, 13, 18, 24, 27, 149, 179, 204),
(45, 3, 7, 1270, 8, 340, 20, 48, 9, 18, 13, 18, 24, 27, 152, 182, 207),
(46, 3, 7, 1288, 8, 350, 20, 51, 9, 18, 13, 18, 24, 27, 155, 185, 210),
(47, 3, 7, 1306, 8, 360, 20, 54, 9, 18, 13, 18, 24, 27, 158, 188, 213),
(48, 3, 7, 1324, 8, 370, 20, 57, 9, 18, 13, 18, 24, 27, 161, 191, 216),
(49, 3, 7, 1342, 8, 380, 20, 60, 9, 18, 13, 18, 24, 27, 164, 194, 219),
(50, 3, 7, 1360, 8, 390, 20, 63, 9, 18, 13, 18, 24, 27, 167, 197, 222),
(1, 4, 9, 100, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 20, 50, 75),
(2, 4, 9, 150, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 23, 53, 78),
(3, 4, 9, 200, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 26, 56, 81),
(4, 4, 9, 250, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 29, 59, 84),
(5, 4, 9, 300, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 32, 62, 87),
(6, 4, 9, 350, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 35, 65, 90),
(7, 4, 9, 400, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 38, 68, 93),
(8, 4, 9, 450, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 41, 71, 96),
(9, 4, 9, 500, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 44, 74, 99),
(10, 4, 9, 550, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 47, 77, 102),
(11, 4, 9, 500, 10, 0, 0, 0, 3, 12, 7, 12, 12, 15, 50, 80, 105),
(12, 4, 9, 525, 10, 10, 0, 0, 3, 12, 7, 12, 12, 15, 53, 83, 108),
(13, 4, 9, 550, 10, 20, 0, 0, 3, 12, 7, 12, 12, 15, 56, 86, 111),
(14, 4, 9, 575, 10, 30, 0, 0, 3, 12, 7, 12, 12, 15, 59, 89, 114),
(15, 4, 9, 600, 10, 40, 0, 0, 3, 12, 7, 12, 12, 15, 62, 92, 117),
(16, 4, 9, 625, 10, 50, 0, 0, 3, 12, 7, 12, 12, 15, 65, 95, 120),
(17, 4, 9, 650, 10, 60, 0, 0, 3, 12, 7, 12, 12, 15, 68, 98, 123),
(18, 4, 9, 675, 10, 70, 0, 0, 3, 12, 7, 12, 12, 15, 71, 101, 126),
(19, 4, 9, 700, 10, 80, 0, 0, 3, 12, 7, 12, 12, 15, 74, 104, 129),
(20, 4, 9, 725, 10, 90, 0, 0, 3, 12, 7, 12, 12, 15, 77, 107, 132),
(21, 4, 9, 750, 10, 100, 0, 0, 5, 14, 9, 14, 16, 19, 80, 110, 135),
(22, 4, 9, 775, 10, 110, 0, 0, 5, 14, 9, 14, 16, 19, 83, 113, 138),
(23, 4, 9, 800, 10, 120, 0, 0, 5, 14, 9, 14, 16, 19, 86, 116, 141),
(24, 4, 9, 825, 10, 130, 0, 0, 5, 14, 9, 14, 16, 19, 89, 119, 144),
(25, 4, 9, 850, 10, 140, 0, 0, 5, 14, 9, 14, 16, 19, 92, 122, 147),
(26, 4, 9, 875, 10, 150, 0, 0, 5, 14, 9, 14, 16, 19, 95, 125, 150),
(27, 4, 9, 900, 10, 160, 0, 0, 5, 14, 9, 14, 16, 19, 98, 128, 153),
(28, 4, 9, 925, 10, 170, 0, 0, 5, 14, 9, 14, 16, 19, 101, 131, 156),
(29, 4, 9, 950, 10, 180, 0, 0, 5, 14, 9, 14, 16, 19, 104, 134, 159),
(30, 4, 9, 1000, 10, 190, 20, 3, 5, 14, 9, 14, 16, 19, 107, 137, 162),
(31, 4, 9, 1018, 10, 200, 20, 6, 7, 16, 11, 16, 20, 23, 110, 140, 165),
(32, 4, 9, 1036, 10, 210, 20, 9, 7, 16, 11, 16, 20, 23, 113, 143, 168),
(33, 4, 9, 1054, 10, 220, 20, 12, 7, 16, 11, 16, 20, 23, 116, 146, 171),
(34, 4, 9, 1072, 10, 230, 20, 15, 7, 16, 11, 16, 20, 23, 119, 149, 174),
(35, 4, 9, 1090, 10, 240, 20, 18, 7, 16, 11, 16, 20, 23, 122, 152, 177),
(36, 4, 9, 1108, 10, 250, 20, 21, 7, 16, 11, 16, 20, 23, 125, 155, 180),
(37, 4, 9, 1126, 10, 260, 20, 24, 7, 16, 11, 16, 20, 23, 128, 158, 183),
(38, 4, 9, 1144, 10, 270, 20, 27, 7, 16, 11, 16, 20, 23, 131, 161, 186),
(39, 4, 9, 1162, 10, 280, 20, 30, 7, 16, 11, 16, 20, 23, 134, 164, 189),
(40, 4, 9, 1180, 10, 290, 20, 33, 7, 16, 11, 16, 20, 23, 137, 167, 192),
(41, 4, 9, 1198, 10, 300, 20, 36, 9, 18, 13, 18, 24, 27, 140, 170, 195),
(42, 4, 9, 1216, 10, 310, 20, 39, 9, 18, 13, 18, 24, 27, 143, 173, 198),
(43, 4, 9, 1234, 10, 320, 20, 42, 9, 18, 13, 18, 24, 27, 146, 176, 201),
(44, 4, 9, 1252, 10, 330, 20, 45, 9, 18, 13, 18, 24, 27, 149, 179, 204),
(45, 4, 9, 1270, 10, 340, 20, 48, 9, 18, 13, 18, 24, 27, 152, 182, 207),
(46, 4, 9, 1288, 10, 350, 20, 51, 9, 18, 13, 18, 24, 27, 155, 185, 210),
(47, 4, 9, 1306, 10, 360, 20, 54, 9, 18, 13, 18, 24, 27, 158, 188, 213),
(48, 4, 9, 1324, 10, 370, 20, 57, 9, 18, 13, 18, 24, 27, 161, 191, 216),
(49, 4, 9, 1342, 10, 380, 20, 60, 9, 18, 13, 18, 24, 27, 164, 194, 219),
(50, 4, 9, 1360, 10, 390, 20, 63, 9, 18, 13, 18, 24, 27, 167, 197, 222),
(1, 0, 1, 100, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 20, 50, 75),
(2, 0, 1, 150, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 23, 53, 78),
(3, 0, 1, 200, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 26, 56, 81),
(4, 0, 1, 250, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 29, 59, 84),
(5, 0, 1, 300, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 32, 62, 87),
(6, 0, 1, 350, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 35, 65, 90),
(7, 0, 1, 400, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 38, 68, 93),
(8, 0, 1, 450, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 41, 71, 96),
(9, 0, 1, 500, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 44, 74, 99),
(10, 0, 1, 550, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 47, 77, 102),
(11, 0, 1, 500, 2, 0, 0, 0, 3, 12, 7, 12, 12, 15, 50, 80, 105),
(12, 0, 1, 525, 2, 10, 0, 0, 3, 12, 7, 12, 12, 15, 53, 83, 108),
(13, 0, 1, 550, 2, 20, 0, 0, 3, 12, 7, 12, 12, 15, 56, 86, 111),
(14, 0, 1, 575, 2, 30, 0, 0, 3, 12, 7, 12, 12, 15, 59, 89, 114),
(15, 0, 1, 600, 2, 40, 0, 0, 3, 12, 7, 12, 12, 15, 62, 92, 117),
(16, 0, 1, 625, 2, 50, 0, 0, 3, 12, 7, 12, 12, 15, 65, 95, 120),
(17, 0, 1, 650, 2, 60, 0, 0, 3, 12, 7, 12, 12, 15, 68, 98, 123),
(18, 0, 1, 675, 2, 70, 0, 0, 3, 12, 7, 12, 12, 15, 71, 101, 126),
(19, 0, 1, 700, 2, 80, 0, 0, 3, 12, 7, 12, 12, 15, 74, 104, 129),
(20, 0, 1, 725, 2, 90, 0, 0, 3, 12, 7, 12, 12, 15, 77, 107, 132),
(21, 0, 1, 750, 2, 100, 0, 0, 5, 14, 9, 14, 16, 19, 80, 110, 135),
(22, 0, 1, 775, 2, 110, 0, 0, 5, 14, 9, 14, 16, 19, 83, 113, 138),
(23, 0, 1, 800, 2, 120, 0, 0, 5, 14, 9, 14, 16, 19, 86, 116, 141),
(24, 0, 1, 825, 2, 130, 0, 0, 5, 14, 9, 14, 16, 19, 89, 119, 144),
(25, 0, 1, 850, 2, 140, 0, 0, 5, 14, 9, 14, 16, 19, 92, 122, 147),
(26, 0, 1, 875, 2, 150, 0, 0, 5, 14, 9, 14, 16, 19, 95, 125, 150),
(27, 0, 1, 900, 2, 160, 0, 0, 5, 14, 9, 14, 16, 19, 98, 128, 153),
(28, 0, 1, 925, 2, 170, 0, 0, 5, 14, 9, 14, 16, 19, 101, 131, 156),
(29, 0, 1, 950, 2, 180, 0, 0, 5, 14, 9, 14, 16, 19, 104, 134, 159),
(30, 0, 1, 1000, 2, 190, 20, 3, 5, 14, 9, 14, 16, 19, 107, 137, 162),
(31, 0, 1, 1018, 2, 200, 20, 6, 7, 16, 11, 16, 20, 23, 110, 140, 165),
(32, 0, 1, 1036, 2, 210, 20, 9, 7, 16, 11, 16, 20, 23, 113, 143, 168),
(33, 0, 1, 1054, 2, 220, 20, 12, 7, 16, 11, 16, 20, 23, 116, 146, 171),
(34, 0, 1, 1072, 2, 230, 20, 15, 7, 16, 11, 16, 20, 23, 119, 149, 174),
(35, 0, 1, 1090, 2, 240, 20, 18, 7, 16, 11, 16, 20, 23, 122, 152, 177),
(36, 0, 1, 1108, 2, 250, 20, 21, 7, 16, 11, 16, 20, 23, 125, 155, 180),
(37, 0, 1, 1126, 2, 260, 20, 24, 7, 16, 11, 16, 20, 23, 128, 158, 183),
(38, 0, 1, 1144, 2, 270, 20, 27, 7, 16, 11, 16, 20, 23, 131, 161, 186),
(39, 0, 1, 1162, 2, 280, 20, 30, 7, 16, 11, 16, 20, 23, 134, 164, 189),
(40, 0, 1, 1180, 2, 290, 20, 33, 7, 16, 11, 16, 20, 23, 137, 167, 192),
(41, 0, 1, 1198, 2, 300, 20, 36, 9, 18, 13, 18, 24, 27, 140, 170, 195),
(42, 0, 1, 1216, 2, 310, 20, 39, 9, 18, 13, 18, 24, 27, 143, 173, 198),
(43, 0, 1, 1234, 2, 320, 20, 42, 9, 18, 13, 18, 24, 27, 146, 176, 201),
(44, 0, 1, 1252, 2, 330, 20, 45, 9, 18, 13, 18, 24, 27, 149, 179, 204),
(45, 0, 1, 1270, 2, 340, 20, 48, 9, 18, 13, 18, 24, 27, 152, 182, 207),
(46, 0, 1, 1288, 2, 350, 20, 51, 9, 18, 13, 18, 24, 27, 155, 185, 210),
(47, 0, 1, 1306, 2, 360, 20, 54, 9, 18, 13, 18, 24, 27, 158, 188, 213),
(48, 0, 1, 1324, 2, 370, 20, 57, 9, 18, 13, 18, 24, 27, 161, 191, 216),
(49, 0, 1, 1342, 2, 380, 20, 60, 9, 18, 13, 18, 24, 27, 164, 194, 219),
(50, 0, 1, 1360, 2, 390, 20, 63, 9, 18, 13, 18, 24, 27, 167, 197, 222),
(1, 1, 3, 100, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 20, 50, 75),
(2, 1, 3, 150, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 23, 53, 78),
(3, 1, 3, 200, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 26, 56, 81),
(4, 1, 3, 250, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 29, 59, 84),
(5, 1, 3, 300, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 32, 62, 87),
(6, 1, 3, 350, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 35, 65, 90),
(7, 1, 3, 400, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 38, 68, 93),
(8, 1, 3, 450, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 41, 71, 96),
(9, 1, 3, 500, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 44, 74, 99),
(10, 1, 3, 550, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 47, 77, 102),
(11, 1, 3, 500, 4, 0, 0, 0, 3, 12, 7, 12, 12, 15, 50, 80, 105),
(12, 1, 3, 525, 4, 10, 0, 0, 3, 12, 7, 12, 12, 15, 53, 83, 108),
(13, 1, 3, 550, 4, 20, 0, 0, 3, 12, 7, 12, 12, 15, 56, 86, 111),
(14, 1, 3, 575, 4, 30, 0, 0, 3, 12, 7, 12, 12, 15, 59, 89, 114),
(15, 1, 3, 600, 4, 40, 0, 0, 3, 12, 7, 12, 12, 15, 62, 92, 117),
(16, 1, 3, 625, 4, 50, 0, 0, 3, 12, 7, 12, 12, 15, 65, 95, 120),
(17, 1, 3, 650, 4, 60, 0, 0, 3, 12, 7, 12, 12, 15, 68, 98, 123),
(18, 1, 3, 675, 4, 70, 0, 0, 3, 12, 7, 12, 12, 15, 71, 101, 126),
(19, 1, 3, 700, 4, 80, 0, 0, 3, 12, 7, 12, 12, 15, 74, 104, 129),
(20, 1, 3, 725, 4, 90, 0, 0, 3, 12, 7, 12, 12, 15, 77, 107, 132),
(21, 1, 3, 750, 4, 100, 0, 0, 5, 14, 9, 14, 16, 19, 80, 110, 135),
(22, 1, 3, 775, 4, 110, 0, 0, 5, 14, 9, 14, 16, 19, 83, 113, 138),
(23, 1, 3, 800, 4, 120, 0, 0, 5, 14, 9, 14, 16, 19, 86, 116, 141),
(24, 1, 3, 825, 4, 130, 0, 0, 5, 14, 9, 14, 16, 19, 89, 119, 144),
(25, 1, 3, 850, 4, 140, 0, 0, 5, 14, 9, 14, 16, 19, 92, 122, 147),
(26, 1, 3, 875, 4, 150, 0, 0, 5, 14, 9, 14, 16, 19, 95, 125, 150),
(27, 1, 3, 900, 4, 160, 0, 0, 5, 14, 9, 14, 16, 19, 98, 128, 153),
(28, 1, 3, 925, 4, 170, 0, 0, 5, 14, 9, 14, 16, 19, 101, 131, 156),
(29, 1, 3, 950, 4, 180, 0, 0, 5, 14, 9, 14, 16, 19, 104, 134, 159),
(30, 1, 3, 1000, 4, 190, 20, 3, 5, 14, 9, 14, 16, 19, 107, 137, 162),
(31, 1, 3, 1018, 4, 200, 20, 6, 7, 16, 11, 16, 20, 23, 110, 140, 165),
(32, 1, 3, 1036, 4, 210, 20, 9, 7, 16, 11, 16, 20, 23, 113, 143, 168),
(33, 1, 3, 1054, 4, 220, 20, 12, 7, 16, 11, 16, 20, 23, 116, 146, 171),
(34, 1, 3, 1072, 4, 230, 20, 15, 7, 16, 11, 16, 20, 23, 119, 149, 174),
(35, 1, 3, 1090, 4, 240, 20, 18, 7, 16, 11, 16, 20, 23, 122, 152, 177),
(36, 1, 3, 1108, 4, 250, 20, 21, 7, 16, 11, 16, 20, 23, 125, 155, 180),
(37, 1, 3, 1126, 4, 260, 20, 24, 7, 16, 11, 16, 20, 23, 128, 158, 183),
(38, 1, 3, 1144, 4, 270, 20, 27, 7, 16, 11, 16, 20, 23, 131, 161, 186),
(39, 1, 3, 1162, 4, 280, 20, 30, 7, 16, 11, 16, 20, 23, 134, 164, 189),
(40, 1, 3, 1180, 4, 290, 20, 33, 7, 16, 11, 16, 20, 23, 137, 167, 192),
(41, 1, 3, 1198, 4, 300, 20, 36, 9, 18, 13, 18, 24, 27, 140, 170, 195),
(42, 1, 3, 1216, 4, 310, 20, 39, 9, 18, 13, 18, 24, 27, 143, 173, 198),
(43, 1, 3, 1234, 4, 320, 20, 42, 9, 18, 13, 18, 24, 27, 146, 176, 201),
(44, 1, 3, 1252, 4, 330, 20, 45, 9, 18, 13, 18, 24, 27, 149, 179, 204),
(45, 1, 3, 1270, 4, 340, 20, 48, 9, 18, 13, 18, 24, 27, 152, 182, 207),
(46, 1, 3, 1288, 4, 350, 20, 51, 9, 18, 13, 18, 24, 27, 155, 185, 210),
(47, 1, 3, 1306, 4, 360, 20, 54, 9, 18, 13, 18, 24, 27, 158, 188, 213),
(48, 1, 3, 1324, 4, 370, 20, 57, 9, 18, 13, 18, 24, 27, 161, 191, 216),
(49, 1, 3, 1342, 4, 380, 20, 60, 9, 18, 13, 18, 24, 27, 164, 194, 219),
(50, 1, 3, 1360, 4, 390, 20, 63, 9, 18, 13, 18, 24, 27, 167, 197, 222),
(1, 2, 5, 100, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 20, 50, 75),
(2, 2, 5, 150, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 23, 53, 78),
(3, 2, 5, 200, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 26, 56, 81),
(4, 2, 5, 250, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 29, 59, 84),
(5, 2, 5, 300, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 32, 62, 87),
(6, 2, 5, 350, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 35, 65, 90),
(7, 2, 5, 400, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 38, 68, 93),
(8, 2, 5, 450, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 41, 71, 96),
(9, 2, 5, 500, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 44, 74, 99),
(10, 2, 5, 550, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 47, 77, 102),
(11, 2, 5, 500, 6, 0, 0, 0, 3, 12, 7, 12, 12, 15, 50, 80, 105),
(12, 2, 5, 525, 6, 10, 0, 0, 3, 12, 7, 12, 12, 15, 53, 83, 108),
(13, 2, 5, 550, 6, 20, 0, 0, 3, 12, 7, 12, 12, 15, 56, 86, 111),
(14, 2, 5, 575, 6, 30, 0, 0, 3, 12, 7, 12, 12, 15, 59, 89, 114),
(15, 2, 5, 600, 6, 40, 0, 0, 3, 12, 7, 12, 12, 15, 62, 92, 117),
(16, 2, 5, 625, 6, 50, 0, 0, 3, 12, 7, 12, 12, 15, 65, 95, 120),
(17, 2, 5, 650, 6, 60, 0, 0, 3, 12, 7, 12, 12, 15, 68, 98, 123),
(18, 2, 5, 675, 6, 70, 0, 0, 3, 12, 7, 12, 12, 15, 71, 101, 126),
(19, 2, 5, 700, 6, 80, 0, 0, 3, 12, 7, 12, 12, 15, 74, 104, 129),
(20, 2, 5, 725, 6, 90, 0, 0, 3, 12, 7, 12, 12, 15, 77, 107, 132),
(21, 2, 5, 750, 6, 100, 0, 0, 5, 14, 9, 14, 16, 19, 80, 110, 135),
(22, 2, 5, 775, 6, 110, 0, 0, 5, 14, 9, 14, 16, 19, 83, 113, 138),
(23, 2, 5, 800, 6, 120, 0, 0, 5, 14, 9, 14, 16, 19, 86, 116, 141),
(24, 2, 5, 825, 6, 130, 0, 0, 5, 14, 9, 14, 16, 19, 89, 119, 144),
(25, 2, 5, 850, 6, 140, 0, 0, 5, 14, 9, 14, 16, 19, 92, 122, 147),
(26, 2, 5, 875, 6, 150, 0, 0, 5, 14, 9, 14, 16, 19, 95, 125, 150),
(27, 2, 5, 900, 6, 160, 0, 0, 5, 14, 9, 14, 16, 19, 98, 128, 153),
(28, 2, 5, 925, 6, 170, 0, 0, 5, 14, 9, 14, 16, 19, 101, 131, 156),
(29, 2, 5, 950, 6, 180, 0, 0, 5, 14, 9, 14, 16, 19, 104, 134, 159),
(30, 2, 5, 1000, 6, 190, 20, 3, 5, 14, 9, 14, 16, 19, 107, 137, 162),
(31, 2, 5, 1018, 6, 200, 20, 6, 7, 16, 11, 16, 20, 23, 110, 140, 165),
(32, 2, 5, 1036, 6, 210, 20, 9, 7, 16, 11, 16, 20, 23, 113, 143, 168),
(33, 2, 5, 1054, 6, 220, 20, 12, 7, 16, 11, 16, 20, 23, 116, 146, 171),
(34, 2, 5, 1072, 6, 230, 20, 15, 7, 16, 11, 16, 20, 23, 119, 149, 174),
(35, 2, 5, 1090, 6, 240, 20, 18, 7, 16, 11, 16, 20, 23, 122, 152, 177),
(36, 2, 5, 1108, 6, 250, 20, 21, 7, 16, 11, 16, 20, 23, 125, 155, 180),
(37, 2, 5, 1126, 6, 260, 20, 24, 7, 16, 11, 16, 20, 23, 128, 158, 183),
(38, 2, 5, 1144, 6, 270, 20, 27, 7, 16, 11, 16, 20, 23, 131, 161, 186),
(39, 2, 5, 1162, 6, 280, 20, 30, 7, 16, 11, 16, 20, 23, 134, 164, 189),
(40, 2, 5, 1180, 6, 290, 20, 33, 7, 16, 11, 16, 20, 23, 137, 167, 192),
(41, 2, 5, 1198, 6, 300, 20, 36, 9, 18, 13, 18, 24, 27, 140, 170, 195),
(42, 2, 5, 1216, 6, 310, 20, 39, 9, 18, 13, 18, 24, 27, 143, 173, 198),
(43, 2, 5, 1234, 6, 320, 20, 42, 9, 18, 13, 18, 24, 27, 146, 176, 201),
(44, 2, 5, 1252, 6, 330, 20, 45, 9, 18, 13, 18, 24, 27, 149, 179, 204),
(45, 2, 5, 1270, 6, 340, 20, 48, 9, 18, 13, 18, 24, 27, 152, 182, 207),
(46, 2, 5, 1288, 6, 350, 20, 51, 9, 18, 13, 18, 24, 27, 155, 185, 210),
(47, 2, 5, 1306, 6, 360, 20, 54, 9, 18, 13, 18, 24, 27, 158, 188, 213),
(48, 2, 5, 1324, 6, 370, 20, 57, 9, 18, 13, 18, 24, 27, 161, 191, 216),
(49, 2, 5, 1342, 6, 380, 20, 60, 9, 18, 13, 18, 24, 27, 164, 194, 219),
(50, 2, 5, 1360, 6, 390, 20, 63, 9, 18, 13, 18, 24, 27, 167, 197, 222)
;

View File

View File

@@ -0,0 +1,253 @@
-- SQL Script to Insert 50 Ranks for Each Stat
INSERT INTO mp_stat_upgrade_ranks (upgradeRank, statTypeId, materialId1, materialCost, materialId2, materialCost2, materialId3, materialCost3, minIncrease1, maxIncrease1, minIncrease2, maxIncrease2, minIncrease3, maxIncrease3, chanceCost1, chanceCost2, chanceCost3) VALUES
(1, 1, 1001, 100, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 20, 50, 75),
(2, 1, 1001, 150, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 23, 53, 78),
(3, 1, 1001, 200, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 26, 56, 81),
(4, 1, 1001, 250, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 29, 59, 84),
(5, 1, 1001, 300, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 32, 62, 87),
(6, 1, 1001, 350, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 35, 65, 90),
(7, 1, 1001, 400, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 38, 68, 93),
(8, 1, 1001, 450, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 41, 71, 96),
(9, 1, 1001, 500, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 44, 74, 99),
(10, 1, 1001, 550, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 47, 77, 102),
(11, 1, 1001, 600, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 50, 80, 105),
(12, 1, 1001, 650, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 53, 83, 108),
(13, 1, 1001, 700, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 56, 86, 111),
(14, 1, 1001, 750, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 59, 89, 114),
(15, 1, 1001, 800, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 62, 92, 117),
(16, 1, 1001, 850, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 65, 95, 120),
(17, 1, 1001, 900, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 68, 98, 123),
(18, 1, 1001, 950, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 71, 101, 126),
(19, 1, 1001, 1000, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 74, 104, 129),
(20, 1, 1001, 1050, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 77, 107, 132),
(21, 1, 1001, 1100, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 80, 110, 135),
(22, 1, 1001, 1150, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 83, 113, 138),
(23, 1, 1001, 1200, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 86, 116, 141),
(24, 1, 1001, 1250, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 89, 119, 144),
(25, 1, 1001, 1300, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 92, 122, 147),
(26, 1, 1001, 1350, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 95, 125, 150),
(27, 1, 1001, 1400, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 98, 128, 153),
(28, 1, 1001, 1450, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 101, 131, 156),
(29, 1, 1001, 1500, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 104, 134, 159),
(30, 1, 1001, 1550, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 107, 137, 162),
(31, 1, 1001, 1600, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 110, 140, 165),
(32, 1, 1001, 1650, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 113, 143, 168),
(33, 1, 1001, 1700, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 116, 146, 171),
(34, 1, 1001, 1750, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 119, 149, 174),
(35, 1, 1001, 1800, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 122, 152, 177),
(36, 1, 1001, 1850, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 125, 155, 180),
(37, 1, 1001, 1900, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 128, 158, 183),
(38, 1, 1001, 1950, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 131, 161, 186),
(39, 1, 1001, 2000, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 134, 164, 189),
(40, 1, 1001, 2050, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 137, 167, 192),
(41, 1, 1001, 2100, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 140, 170, 195),
(42, 1, 1001, 2150, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 143, 173, 198),
(43, 1, 1001, 2200, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 146, 176, 201),
(44, 1, 1001, 2250, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 149, 179, 204),
(45, 1, 1001, 2300, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 152, 182, 207),
(46, 1, 1001, 2350, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 155, 185, 210),
(47, 1, 1001, 2400, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 158, 188, 213),
(48, 1, 1001, 2450, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 161, 191, 216),
(49, 1, 1001, 2500, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 164, 194, 219),
(50, 1, 1001, 2550, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 167, 197, 222),
(1, 2, 1001, 100, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 20, 50, 75),
(2, 2, 1001, 150, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 23, 53, 78),
(3, 2, 1001, 200, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 26, 56, 81),
(4, 2, 1001, 250, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 29, 59, 84),
(5, 2, 1001, 300, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 32, 62, 87),
(6, 2, 1001, 350, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 35, 65, 90),
(7, 2, 1001, 400, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 38, 68, 93),
(8, 2, 1001, 450, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 41, 71, 96),
(9, 2, 1001, 500, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 44, 74, 99),
(10, 2, 1001, 550, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 47, 77, 102),
(11, 2, 1001, 600, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 50, 80, 105),
(12, 2, 1001, 650, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 53, 83, 108),
(13, 2, 1001, 700, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 56, 86, 111),
(14, 2, 1001, 750, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 59, 89, 114),
(15, 2, 1001, 800, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 62, 92, 117),
(16, 2, 1001, 850, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 65, 95, 120),
(17, 2, 1001, 900, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 68, 98, 123),
(18, 2, 1001, 950, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 71, 101, 126),
(19, 2, 1001, 1000, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 74, 104, 129),
(20, 2, 1001, 1050, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 77, 107, 132),
(21, 2, 1001, 1100, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 80, 110, 135),
(22, 2, 1001, 1150, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 83, 113, 138),
(23, 2, 1001, 1200, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 86, 116, 141),
(24, 2, 1001, 1250, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 89, 119, 144),
(25, 2, 1001, 1300, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 92, 122, 147),
(26, 2, 1001, 1350, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 95, 125, 150),
(27, 2, 1001, 1400, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 98, 128, 153),
(28, 2, 1001, 1450, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 101, 131, 156),
(29, 2, 1001, 1500, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 104, 134, 159),
(30, 2, 1001, 1550, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 107, 137, 162),
(31, 2, 1001, 1600, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 110, 140, 165),
(32, 2, 1001, 1650, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 113, 143, 168),
(33, 2, 1001, 1700, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 116, 146, 171),
(34, 2, 1001, 1750, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 119, 149, 174),
(35, 2, 1001, 1800, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 122, 152, 177),
(36, 2, 1001, 1850, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 125, 155, 180),
(37, 2, 1001, 1900, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 128, 158, 183),
(38, 2, 1001, 1950, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 131, 161, 186),
(39, 2, 1001, 2000, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 134, 164, 189),
(40, 2, 1001, 2050, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 137, 167, 192),
(41, 2, 1001, 2100, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 140, 170, 195),
(42, 2, 1001, 2150, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 143, 173, 198),
(43, 2, 1001, 2200, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 146, 176, 201),
(44, 2, 1001, 2250, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 149, 179, 204),
(45, 2, 1001, 2300, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 152, 182, 207),
(46, 2, 1001, 2350, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 155, 185, 210),
(47, 2, 1001, 2400, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 158, 188, 213),
(48, 2, 1001, 2450, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 161, 191, 216),
(49, 2, 1001, 2500, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 164, 194, 219),
(50, 2, 1001, 2550, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 167, 197, 222),
(1, 3, 1001, 100, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 20, 50, 75),
(2, 3, 1001, 150, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 23, 53, 78),
(3, 3, 1001, 200, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 26, 56, 81),
(4, 3, 1001, 250, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 29, 59, 84),
(5, 3, 1001, 300, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 32, 62, 87),
(6, 3, 1001, 350, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 35, 65, 90),
(7, 3, 1001, 400, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 38, 68, 93),
(8, 3, 1001, 450, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 41, 71, 96),
(9, 3, 1001, 500, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 44, 74, 99),
(10, 3, 1001, 550, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 47, 77, 102),
(11, 3, 1001, 600, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 50, 80, 105),
(12, 3, 1001, 650, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 53, 83, 108),
(13, 3, 1001, 700, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 56, 86, 111),
(14, 3, 1001, 750, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 59, 89, 114),
(15, 3, 1001, 800, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 62, 92, 117),
(16, 3, 1001, 850, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 65, 95, 120),
(17, 3, 1001, 900, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 68, 98, 123),
(18, 3, 1001, 950, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 71, 101, 126),
(19, 3, 1001, 1000, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 74, 104, 129),
(20, 3, 1001, 1050, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 77, 107, 132),
(21, 3, 1001, 1100, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 80, 110, 135),
(22, 3, 1001, 1150, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 83, 113, 138),
(23, 3, 1001, 1200, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 86, 116, 141),
(24, 3, 1001, 1250, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 89, 119, 144),
(25, 3, 1001, 1300, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 92, 122, 147),
(26, 3, 1001, 1350, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 95, 125, 150),
(27, 3, 1001, 1400, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 98, 128, 153),
(28, 3, 1001, 1450, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 101, 131, 156),
(29, 3, 1001, 1500, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 104, 134, 159),
(30, 3, 1001, 1550, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 107, 137, 162),
(31, 3, 1001, 1600, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 110, 140, 165),
(32, 3, 1001, 1650, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 113, 143, 168),
(33, 3, 1001, 1700, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 116, 146, 171),
(34, 3, 1001, 1750, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 119, 149, 174),
(35, 3, 1001, 1800, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 122, 152, 177),
(36, 3, 1001, 1850, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 125, 155, 180),
(37, 3, 1001, 1900, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 128, 158, 183),
(38, 3, 1001, 1950, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 131, 161, 186),
(39, 3, 1001, 2000, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 134, 164, 189),
(40, 3, 1001, 2050, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 137, 167, 192),
(41, 3, 1001, 2100, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 140, 170, 195),
(42, 3, 1001, 2150, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 143, 173, 198),
(43, 3, 1001, 2200, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 146, 176, 201),
(44, 3, 1001, 2250, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 149, 179, 204),
(45, 3, 1001, 2300, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 152, 182, 207),
(46, 3, 1001, 2350, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 155, 185, 210),
(47, 3, 1001, 2400, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 158, 188, 213),
(48, 3, 1001, 2450, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 161, 191, 216),
(49, 3, 1001, 2500, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 164, 194, 219),
(50, 3, 1001, 2550, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 167, 197, 222),
(1, 4, 1001, 100, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 20, 50, 75),
(2, 4, 1001, 150, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 23, 53, 78),
(3, 4, 1001, 200, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 26, 56, 81),
(4, 4, 1001, 250, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 29, 59, 84),
(5, 4, 1001, 300, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 32, 62, 87),
(6, 4, 1001, 350, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 35, 65, 90),
(7, 4, 1001, 400, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 38, 68, 93),
(8, 4, 1001, 450, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 41, 71, 96),
(9, 4, 1001, 500, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 44, 74, 99),
(10, 4, 1001, 550, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 47, 77, 102),
(11, 4, 1001, 600, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 50, 80, 105),
(12, 4, 1001, 650, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 53, 83, 108),
(13, 4, 1001, 700, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 56, 86, 111),
(14, 4, 1001, 750, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 59, 89, 114),
(15, 4, 1001, 800, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 62, 92, 117),
(16, 4, 1001, 850, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 65, 95, 120),
(17, 4, 1001, 900, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 68, 98, 123),
(18, 4, 1001, 950, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 71, 101, 126),
(19, 4, 1001, 1000, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 74, 104, 129),
(20, 4, 1001, 1050, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 77, 107, 132),
(21, 4, 1001, 1100, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 80, 110, 135),
(22, 4, 1001, 1150, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 83, 113, 138),
(23, 4, 1001, 1200, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 86, 116, 141),
(24, 4, 1001, 1250, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 89, 119, 144),
(25, 4, 1001, 1300, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 92, 122, 147),
(26, 4, 1001, 1350, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 95, 125, 150),
(27, 4, 1001, 1400, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 98, 128, 153),
(28, 4, 1001, 1450, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 101, 131, 156),
(29, 4, 1001, 1500, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 104, 134, 159),
(30, 4, 1001, 1550, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 107, 137, 162),
(31, 4, 1001, 1600, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 110, 140, 165),
(32, 4, 1001, 1650, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 113, 143, 168),
(33, 4, 1001, 1700, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 116, 146, 171),
(34, 4, 1001, 1750, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 119, 149, 174),
(35, 4, 1001, 1800, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 122, 152, 177),
(36, 4, 1001, 1850, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 125, 155, 180),
(37, 4, 1001, 1900, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 128, 158, 183),
(38, 4, 1001, 1950, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 131, 161, 186),
(39, 4, 1001, 2000, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 134, 164, 189),
(40, 4, 1001, 2050, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 137, 167, 192),
(41, 4, 1001, 2100, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 140, 170, 195),
(42, 4, 1001, 2150, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 143, 173, 198),
(43, 4, 1001, 2200, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 146, 176, 201),
(44, 4, 1001, 2250, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 149, 179, 204),
(45, 4, 1001, 2300, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 152, 182, 207),
(46, 4, 1001, 2350, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 155, 185, 210),
(47, 4, 1001, 2400, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 158, 188, 213),
(48, 4, 1001, 2450, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 161, 191, 216),
(49, 4, 1001, 2500, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 164, 194, 219),
(50, 4, 1001, 2550, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 167, 197, 222)
(1, 0, 1001, 100, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 20, 50, 75),
(2, 0, 1001, 150, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 23, 53, 78),
(3, 0, 1001, 200, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 26, 56, 81),
(4, 0, 1001, 250, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 29, 59, 84),
(5, 0, 1001, 300, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 32, 62, 87),
(6, 0, 1001, 350, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 35, 65, 90),
(7, 0, 1001, 400, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 38, 68, 93),
(8, 0, 1001, 450, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 41, 71, 96),
(9, 0, 1001, 500, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 44, 74, 99),
(10, 0, 1001, 550, 0, 0, 0, 0, 1, 10, 5, 10, 8, 11, 47, 77, 102),
(11, 0, 1001, 600, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 50, 80, 105),
(12, 0, 1001, 650, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 53, 83, 108),
(13, 0, 1001, 700, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 56, 86, 111),
(14, 0, 1001, 750, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 59, 89, 114),
(15, 0, 1001, 800, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 62, 92, 117),
(16, 0, 1001, 850, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 65, 95, 120),
(17, 0, 1001, 900, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 68, 98, 123),
(18, 0, 1001, 950, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 71, 101, 126),
(19, 0, 1001, 1000, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 74, 104, 129),
(20, 0, 1001, 1050, 0, 0, 0, 0, 3, 12, 7, 12, 12, 15, 77, 107, 132),
(21, 0, 1001, 1100, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 80, 110, 135),
(22, 0, 1001, 1150, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 83, 113, 138),
(23, 0, 1001, 1200, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 86, 116, 141),
(24, 0, 1001, 1250, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 89, 119, 144),
(25, 0, 1001, 1300, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 92, 122, 147),
(26, 0, 1001, 1350, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 95, 125, 150),
(27, 0, 1001, 1400, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 98, 128, 153),
(28, 0, 1001, 1450, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 101, 131, 156),
(29, 0, 1001, 1500, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 104, 134, 159),
(30, 0, 1001, 1550, 0, 0, 0, 0, 5, 14, 9, 14, 16, 19, 107, 137, 162),
(31, 0, 1001, 1600, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 110, 140, 165),
(32, 0, 1001, 1650, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 113, 143, 168),
(33, 0, 1001, 1700, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 116, 146, 171),
(34, 0, 1001, 1750, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 119, 149, 174),
(35, 0, 1001, 1800, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 122, 152, 177),
(36, 0, 1001, 1850, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 125, 155, 180),
(37, 0, 1001, 1900, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 128, 158, 183),
(38, 0, 1001, 1950, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 131, 161, 186),
(39, 0, 1001, 2000, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 134, 164, 189),
(40, 0, 1001, 2050, 0, 0, 0, 0, 7, 16, 11, 16, 20, 23, 137, 167, 192),
(41, 0, 1001, 2100, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 140, 170, 195),
(42, 0, 1001, 2150, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 143, 173, 198),
(43, 0, 1001, 2200, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 146, 176, 201),
(44, 0, 1001, 2250, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 149, 179, 204),
(45, 0, 1001, 2300, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 152, 182, 207),
(46, 0, 1001, 2350, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 155, 185, 210),
(47, 0, 1001, 2400, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 158, 188, 213),
(48, 0, 1001, 2450, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 161, 191, 216),
(49, 0, 1001, 2500, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 164, 194, 219),
(50, 0, 1001, 2550, 0, 0, 0, 0, 9, 18, 13, 18, 24, 27, 167, 197, 222),
;

View File

@@ -0,0 +1,77 @@
package main
import (
"fmt"
"os"
)
// Stats enum
const (
STAT_STRENGTH = iota
STAT_AGILITY
STAT_STAMINA
STAT_INTELLECT
STAT_SPIRIT
)
func main() {
// Output file for the SQL script
outputFile, err := os.Create("generate_stat_upgrades.sql")
if err != nil {
fmt.Println("Error creating file:", err)
return
}
defer outputFile.Close()
// Stats names for clarity
stats := map[int]string{
STAT_STRENGTH: "STAT_STRENGTH",
STAT_AGILITY: "STAT_AGILITY",
STAT_STAMINA: "STAT_STAMINA",
STAT_INTELLECT: "STAT_INTELLECT",
STAT_SPIRIT: "STAT_SPIRIT",
}
// Start writing the SQL script
fmt.Fprintln(outputFile, "-- SQL Script to Insert 50 Ranks for Each Stat")
fmt.Fprintln(outputFile, "INSERT INTO mp_stat_upgrade_ranks (upgradeRank, statTypeId, materialId1, materialCost, materialId2, materialCost2, materialId3, materialCost3, minIncrease1, maxIncrease1, minIncrease2, maxIncrease2, minIncrease3, maxIncrease3, chanceCost1, chanceCost2, chanceCost3) VALUES")
// Iterate over stats
for statID := range stats {
for rank := 1; rank <= 50; rank++ {
// Material cost increases by 50 per rank
materialCost := 100 + (rank-1)*50
// Stat growth
minIncrease1 := 1 + (rank-1)/10*2
maxIncrease1 := 10 + (rank-1)/10*2
minIncrease2 := (minIncrease1 + maxIncrease1) / 2
maxIncrease3Bonus := (rank-1)/10*2 + 1
maxIncrease3 := maxIncrease1 + maxIncrease3Bonus
minIncrease3 := maxIncrease3 - 3
// Dice costs
chanceCost1 := 20 + (rank-1)*3
chanceCost2 := 50 + (rank-1)*3
chanceCost3 := 75 + (rank-1)*3
// Write SQL insert statement for this rank
sql := fmt.Sprintf(
"(%d, %d, 1001, %d, 0, 0, 0, 0, %d, %d, %d, %d, %d, %d, %d, %d, %d)",
rank, statID, materialCost,
minIncrease1, maxIncrease1, minIncrease2, maxIncrease1, minIncrease3, maxIncrease3,
chanceCost1, chanceCost2, chanceCost3,
)
// Add a comma for all but the last line
if !(statID == STAT_SPIRIT && rank == 50) {
sql += ","
}
fmt.Fprintln(outputFile, sql)
}
}
fmt.Fprintln(outputFile, ";")
fmt.Println("SQL script generated: generate_stat_upgrades.sql")
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,153 @@
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"math"
"os"
)
type Creature struct {
CreatureEntry int `json:"creature_entry"`
Level int `json:"level"`
HP int `json:"hp"`
Mana int `json:"mana"`
Armor int `json:"armor"`
Str int `json:"str"`
Agi int `json:"agi"`
Sta int `json:"sta"`
Int int `json:"inte"`
Spi int `json:"spi"`
MinDmg int `json:"min_dmg"`
MaxDmg int `json:"max_dmg"`
}
// RegressionResult stores the slope and intercept of a linear regression
type RegressionResult struct {
Slope float64
Intercept float64
}
// LinearRegression performs a simple linear regression on the given data points
func LinearRegression(levels []float64, stats []float64) RegressionResult {
n := len(levels)
if n != len(stats) {
panic("levels and stats arrays must have the same length")
}
// Calculate averages
var sumX, sumY, sumXY, sumXX float64
for i := 0; i < n; i++ {
sumX += levels[i]
sumY += stats[i]
sumXY += levels[i] * stats[i]
sumXX += levels[i] * levels[i]
}
avgX := sumX / float64(n)
avgY := sumY / float64(n)
// Calculate slope and intercept
slope := (sumXY - float64(n)*avgX*avgY) / (sumXX - float64(n)*avgX*avgX)
intercept := avgY - slope*avgX
return RegressionResult{
Slope: slope,
Intercept: intercept,
}
}
// PredictStat returns the predicted stat value for a given level based on a linear regression model
func PredictStat(reg RegressionResult, level int) float64 {
return reg.Intercept + reg.Slope*float64(level)
}
// ScaleCreatures generates stats for levels 81 to 87 based on linear regression and outputs SQL statements
func ScaleCreatures(creatures []Creature) {
creatureMap := make(map[int][]Creature)
// Group creatures by their creature_entry
for _, creature := range creatures {
creatureMap[creature.CreatureEntry] = append(creatureMap[creature.CreatureEntry], creature)
}
// Process each creature entry independently
for creatureEntry, creatureList := range creatureMap {
var levels []float64
var hp, mana, armor, str, agi, sta, inte, spi, minDmg, maxDmg []float64
// Collect data for linear regression from levels 1 to 80
for _, creature := range creatureList {
if creature.Level <= 80 {
levels = append(levels, float64(creature.Level))
hp = append(hp, float64(creature.HP))
mana = append(mana, float64(creature.Mana))
armor = append(armor, float64(creature.Armor))
str = append(str, float64(creature.Str))
agi = append(agi, float64(creature.Agi))
sta = append(sta, float64(creature.Sta))
inte = append(inte, float64(creature.Int))
spi = append(spi, float64(creature.Spi))
minDmg = append(minDmg, float64(creature.MinDmg))
maxDmg = append(maxDmg, float64(creature.MaxDmg))
}
}
// Perform linear regression on each stat
hpReg := LinearRegression(levels, hp)
manaReg := LinearRegression(levels, mana)
armorReg := LinearRegression(levels, armor)
strReg := LinearRegression(levels, str)
agiReg := LinearRegression(levels, agi)
staReg := LinearRegression(levels, sta)
inteReg := LinearRegression(levels, inte)
spiReg := LinearRegression(levels, spi)
minDmgReg := LinearRegression(levels, minDmg)
maxDmgReg := LinearRegression(levels, maxDmg)
// Generate SQL insert statements for levels 81 to 87 for each creature_entry
for level := 81; level <= 87; level++ {
newCreature := Creature{
CreatureEntry: creatureEntry, // Process per creature_entry
Level: level,
HP: int(math.Round(math.Max(PredictStat(hpReg, level), 1))), // Round and ensure positive
Mana: int(math.Round(math.Max(PredictStat(manaReg, level), 1))),
Armor: int(math.Round(math.Max(PredictStat(armorReg, level), 1))), // Armor scaling
Str: int(math.Round(math.Max(PredictStat(strReg, level), 1))),
Agi: int(math.Round(math.Max(PredictStat(agiReg, level), 1))),
Sta: int(math.Round(math.Max(PredictStat(staReg, level), 1))),
Int: int(math.Round(math.Max(PredictStat(inteReg, level), 1))),
Spi: int(math.Round(math.Max(PredictStat(spiReg, level), 1))),
MinDmg: int(math.Round(math.Max(PredictStat(minDmgReg, level), 1))),
MaxDmg: int(math.Round(math.Max(PredictStat(maxDmgReg, level), 1))),
}
// Output SQL INSERT statement for each creature_entry and level
fmt.Printf("INSERT INTO pet_levelstats (creature_entry, level, hp, mana, armor, str, agi, sta, inte, spi, min_dmg, max_dmg) "+
"VALUES (%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d);\n",
newCreature.CreatureEntry, newCreature.Level, newCreature.HP, newCreature.Mana, newCreature.Armor,
newCreature.Str, newCreature.Agi, newCreature.Sta, newCreature.Int, newCreature.Spi,
newCreature.MinDmg, newCreature.MaxDmg)
}
}
}
func main() {
// Read the JSON file
file, err := os.Open("creatures.json")
if err != nil {
fmt.Println("Error reading file:", err)
return
}
defer file.Close()
byteValue, _ := ioutil.ReadAll(file)
// Parse the JSON data
var creatures []Creature
json.Unmarshal(byteValue, &creatures)
// Scale creatures and generate SQL for levels 81 to 87
ScaleCreatures(creatures)
}

View File

@@ -0,0 +1,110 @@
package main
import (
"fmt"
"os"
)
// Stats enum
const (
STAT_INTELLECT = iota
STAT_SPIRIT
STAT_STRENGTH
STAT_AGILITY
STAT_STAMINA
)
const (
RESIST_FROST = iota
RESIST_FIRE
RESIST_NATURE
RESIST_SHADOW
RESIST_ARCANE
)
func main() {
// Output file for the SQL script
outputFile, err := os.Create("generate_stat_upgrades.sql")
if err != nil {
fmt.Println("Error creating file:", err)
return
}
defer outputFile.Close()
// Stats names for clarity
stats := map[int]string{
STAT_INTELLECT: "Intellect",
STAT_SPIRIT: "Spirit",
STAT_STRENGTH: "Strength",
STAT_AGILITY: "Agility",
STAT_STAMINA: "Stamina",
}
// Start writing the SQL script
fmt.Fprintln(outputFile, "-- SQL Script to Insert 50 Ranks for Each Stat")
fmt.Fprintln(outputFile, "INSERT INTO mp_upgrade_ranks (upgradeRank, advancementId, materialId1, materialCost1, materialId2, materialCost2, materialId3, materialCost3, minIncrease1, maxIncrease1, minIncrease2, maxIncrease2, minIncrease3, maxIncrease3, chanceCost1, chanceCost2, chanceCost3) VALUES")
// Iterate over stats
for statID := range stats {
for rank := 1; rank <= 50; rank++ {
// Material cost increases by 50 per rank
materialCost := 50
if rank < 11 {
materialCost = 100 + (rank-1)*50
}
if rank >= 11 && rank < 30 {
materialCost = 500 + (rank-11)*25
}
if rank >= 30 {
materialCost = 1000 + (rank-30)*18
}
// Stat growth
minIncrease1 := 1 + (rank-1)/10*2
maxIncrease1 := 10 + (rank-1)/10*2
minIncrease2 := (minIncrease1 + maxIncrease1) / 2
maxIncrease3Bonus := (rank-1)/10*2 + 1
maxIncrease3 := maxIncrease1 + maxIncrease3Bonus
minIncrease3 := maxIncrease3 - 3
// Dice costs
chanceCost1 := 20 + (rank-1)*3
chanceCost2 := 50 + (rank-1)*3
chanceCost3 := 75 + (rank-1)*3
// use material ids from the mp_material_types table material1 should be common stuff.
materialId1 := statID*2 + 1
// material2 should be rare stuff only required after rank 10 at growth rate of 5 per rank
materialId2, materialCost2 := 0, 0
if rank > 10 {
materialId2 = statID*2 + 2
materialCost2 = (rank - 11) * 10
}
materialId3, materialCost3 := 0, 0
if rank >= 30 {
materialId3 = 20 // Group lot of raid only items
materialCost3 = (rank - 29) * 3
}
// Write SQL insert statement for this rank
sql := fmt.Sprintf(
"(%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d)",
rank, statID, materialId1, materialCost, materialId2, materialCost2, materialId3, materialCost3,
minIncrease1, maxIncrease1, minIncrease2, maxIncrease1, minIncrease3, maxIncrease3,
chanceCost1, chanceCost2, chanceCost3,
)
// Add a comma for all but the last line
if !(statID == STAT_SPIRIT && rank == 50) {
sql += ","
}
fmt.Fprintln(outputFile, sql)
}
}
fmt.Fprintln(outputFile, ";")
fmt.Println("SQL script generated: generate_stat_upgrades.sql")
}

415
src/AdvancementMgr.cpp Normal file
View File

@@ -0,0 +1,415 @@
#include "AdvancementMgr.h"
#include "CharacterDatabase.h"
#include "WorldDatabase.h"
#include "Player.h"
#include "MpLogger.h"
#include "MythicPlus.h"
#include "MpConstants.h"
#include <string_view>
#include <tuple>
#include <mutex>
/**
* Table schema for upgrade ranks populated by go script:
*
* upgradeRank INT UNSIGNED NOT NULL,
* advancementId INT UNSIGNED NOT NULL,
* materialId1 INT UNSIGNED NOT NULL,
* materialId2 INT UNSIGNED NOT NULL,
* materialId3 INT UNSIGNED NOT NULL,
* materialCost INT UNSIGNED NOT NULL,
* materialCost2 INT UNSIGNED NOT NULL,
* materialCost3 INT UNSIGNED NOT NULL,
* minIncrease1 INT UNSIGNED NOT NULL,
* maxIncrease1 INT UNSIGNED NOT NULL,
* minIncrease2 INT UNSIGNED NOT NULL,
* maxIncrease2 INT UNSIGNED NOT NULL,
* minIncrease3 INT UNSIGNED NOT NULL,
* maxIncrease3 INT UNSIGNED NOT NULL,
* chanceCost1 INT UNSIGNED NOT NULL,
* chanceCost2 INT UNSIGNED NOT NULL,
* chanceCost3 INT UNSIGNED NOT NULL,
*
* PRIMARY KEY (upgradeRank, statTypeId)
*
* This loads the ranks from the database and stores them into memory for access. This should only be
* called on server start-up as it is static data that should only change no new builds.
*/
int32 AdvancementMgr::LoadAdvancementRanks() {
_advancementRanks.clear();
//
// const char*
constexpr std::string_view query = R"(
SELECT
upgradeRank,
advancementId,
materialId1,
materialId2,
materialId3,
materialCost1,
materialCost2,
materialCost3,
minIncrease1,
maxIncrease1,
minIncrease2,
maxIncrease2,
minIncrease3,
maxIncrease3,
chanceCost1,
chanceCost2,
chanceCost3
FROM mp_upgrade_ranks
)";
QueryResult result = WorldDatabase.Query(query);
if (!result) {
MpLogger::error("Failed to load mythic scale factors from database");
return 0;
}
do {
Field* fields = result->Fetch();
uint32 upgradeRank = fields[0].Get<uint32>();
uint32 advancementId = fields[1].Get<uint32>();
uint32 materialId1 = fields[2].Get<uint32>();
uint32 materialId2 = fields[3].Get<uint32>();
uint32 materialId3 = fields[4].Get<uint32>();
uint32 materialCost1 = fields[5].Get<uint32>();
uint32 materialCost2 = fields[6].Get<uint32>();
uint32 materialCost3 = fields[7].Get<uint32>();
uint32 minIncrease1 = fields[8].Get<uint32>();
uint32 maxIncrease1 = fields[9].Get<uint32>();
uint32 minIncrease2 = fields[10].Get<uint32>();
uint32 maxIncrease2 = fields[11].Get<uint32>();
uint32 minIncrease3 = fields[12].Get<uint32>();
uint32 maxIncrease3 = fields[13].Get<uint32>();
uint32 chanceCost1 = fields[14].Get<uint32>();
uint32 chanceCost2 = fields[15].Get<uint32>();
uint32 chanceCost3 = fields[16].Get<uint32>();
// Should add validator... but let's do it without and trust in the o-DB-Wan-kenobe
MpAdvancements advancement = static_cast<MpAdvancements>(advancementId);
// List of all ranks keyed by rank, advancementId
MpAdvancementRank rank = {
.rank = upgradeRank,
.advancementId = advancement,
.materialCost = std::unordered_map<uint32, uint32>(),
.rollCost = {chanceCost1, chanceCost2, chanceCost3},
.lowRange = std::make_pair(minIncrease1, maxIncrease1),
.midRange = std::make_pair(minIncrease2, maxIncrease2),
.highRange = std::make_pair(minIncrease3, maxIncrease3)
};
rank.materialCost.emplace(materialId1, materialCost1);
rank.materialCost.emplace(materialId2, materialCost2);
rank.materialCost.emplace(materialId3, materialCost3);
_advancementRanks.try_emplace(std::make_pair(upgradeRank, advancement), rank);
} while (result->NextRow());
return _advancementRanks.size();
}
/**
* guid INT UNSIGNED NOT NULL,
* advancementId INT UNSIGNED NOT NULL,
* bonus FLOAT NOT NULL,
* upgradeRank INT UNSIGNED NOT NULL,
* diceSpent INT UNSIGNED NOT NULL DEFAULT '0',
*
* PRIMARY KEY (guid, advancementId)
*
* This loads the player advancements when a player logs in stores it into memory for access by spell scripts that
* are applied to the player on login to apply the bonuses.
*/
int32 AdvancementMgr::LoadPlayerAdvancements(Player* player) {
std::lock_guard<std::mutex> lock(_playerAdvancementMutex);
constexpr std::string_view query = R"(
SELECT
guid,
advancementId,
bonus,
upgradeRank,
diceSpent
FROM mp_player_advancements
WHERE guid = {}
)";
QueryResult result = CharacterDatabase.Query(query, player->GetGUID().GetCounter());
// If the player does not have any upgrades just return perfectly fine not a problem until they purchase one.
if(!result) {
return 0;
}
do {
Field* fields = result->Fetch();
uint32 guid = fields[0].Get<uint32>();
uint32 advancementId = fields[1].Get<uint32>();
float bonus = fields[2].Get<float>();
uint32 upgradeRank = fields[3].Get<uint32>();
uint32 diceSpent = fields[4].Get<uint32>();
MpAdvancements advancement = static_cast<MpAdvancements>(advancementId);
MpPlayerRank playerRank = MpPlayerRank();
playerRank.rank = upgradeRank;
playerRank.advancementId = advancement;
playerRank.diceSpent = diceSpent;
playerRank.bonus = bonus;
// List of all ranks keyed by rank, advancementId
_playerAdvancements[guid][advancement] = playerRank;
} while (result->NextRow());
return result->GetRowCount();
}
/**
* Load Material Types from the database into memory
*/
int32 AdvancementMgr::LoadMaterialTypes() {
constexpr std::string_view query = R"(
SELECT
materialId,
entry
FROM mp_material_types
)";
QueryResult result = WorldDatabase.Query(query);
do {
Field* fields = result->Fetch();
uint32 materialId = fields[0].Get<uint32>();
uint32 entry = fields[1].Get<uint32>();
if(!_materialTypes.contains(materialId)) {
_materialTypes.emplace(materialId,std::vector<uint32>());
}
_materialTypes.at(materialId).push_back(entry);
} while (result->NextRow());
return result->GetRowCount();
}
MpAdvancementRank* AdvancementMgr::GetAdvancementRank(uint32 rank, MpAdvancements advancement)
{
auto key = std::make_pair(rank, advancement);
if (_advancementRanks.contains(key))
{
return &_advancementRanks.at(key);
}
else
{
MpLogger::error("Advancement Id {} and rank {} could not be found", rank, advancement);
return nullptr;
}
}
MpPlayerRank* AdvancementMgr::GetPlayerAdvancementRank(Player* player, MpAdvancements advancement)
{
if(!player) {
MpLogger::error("Could not retrieve player advancement for null player {}", player->GetName());
return nullptr;
}
if (_playerAdvancements.contains(player->GetGUID().GetCounter()) && _playerAdvancements[player->GetGUID().GetCounter()].contains(advancement))
{
return &_playerAdvancements[player->GetGUID().GetCounter()][advancement];
}
return nullptr;
}
bool AdvancementMgr::UpgradeAdvancement(Player* player, MpAdvancements advancement, uint32 diceCostLevel, uint32 itemEntry1, uint32 itemEntry2, uint32 itemEntry3)
{
std::lock_guard<std::mutex> lock(_playerAdvancementMutex);
// Validators to make sure inputs are correct to perform the upgrade
if(!player) {
MpLogger::error("Could not upgrade advancement for player, player was nullpointer");
return false;
}
if(diceCostLevel < 1 || diceCostLevel > 3) {
MpLogger::error("Invalid dice cost level valid vales (1,2,3) received {} for player {}", diceCostLevel, player->GetName());
return false;
}
if(itemEntry1 == 0) {
MpLogger::error("Material1 can not be 0 can not perform advancement upgrade for player {} Advancement {}", player->GetName(), advancement);
return false;
}
MpPlayerRank* playerRank = GetPlayerAdvancementRank(player, advancement);
// IF there is not create the base struct and add to the player map for this advancement
if(!playerRank) {
MpPlayerRank newPlayerRank;
newPlayerRank.advancementId = advancement;
auto& playerAdvMap = _playerAdvancements[player->GetGUID().GetCounter()];
playerAdvMap.emplace(advancement, newPlayerRank);
playerRank = &playerAdvMap.at(advancement);
}
if(playerRank->rank == MP_MAX_ADVANCEMENT_RANK) {
MpLogger::error("Player {} has reached the maximum rank for advancement {}", player->GetName(), advancement);
return false;
}
uint32 newRank = playerRank->rank + 1;
MpAdvancementRank* advancementRank = GetAdvancementRank(newRank, advancement);
if(!advancementRank->IsValid()) {
MpLogger::error("Advancement {} rank {} could not be found", advancement, newRank);
return false;
}
// If the player has the items needed to upgrade this advancement, then remove the items from the player inventory and apply the upgrade
if(!_PlayerHasItems(player, advancementRank, diceCostLevel, itemEntry1, itemEntry2, itemEntry3)) {
MpLogger::info("Player {} does not have the required items to upgrade advancement {}", player->GetName(), advancement);
return false;
}
// Charge the player the cost of the upgrade
_ChargeItemCost(player, advancementRank, diceCostLevel, itemEntry1, itemEntry2, itemEntry3);
// Finally get the bonus to apply for the player
float roll = _RollAdvancement(advancementRank, diceCostLevel);
// Update the player advancement rank in memory and database
playerRank->rank = newRank;
playerRank->diceSpent += advancementRank->rollCost[diceCostLevel];
playerRank->bonus += roll;
return true;
}
bool AdvancementMgr::ResetPlayerAdvancements(Player* /*player*/)
{
std::lock_guard<std::mutex> lock(_playerAdvancementMutex);
return true;
}
void AdvancementMgr::_ResetPlayerAdvancement(Player* player, MpAdvancements advancement)
{
std::lock_guard<std::mutex> lock(_playerAdvancementMutex);
return;
}
// Roll them stats DnD style.
float AdvancementMgr::_RollAdvancement(MpAdvancementRank* advancementRank, uint32 diceCostLevel)
{
uint32 min, max;
switch (diceCostLevel)
{
case 1:
min = advancementRank->lowRange.first;
max = advancementRank->lowRange.second;
break;
case 2:
min = advancementRank->midRange.first;
max = advancementRank->midRange.second;
break;
case 3:
min = advancementRank->highRange.first;
max = advancementRank->highRange.second;
break;
default:
MpLogger::error("Invalid dice cost level valid vales (1,2,3) received {} for rank roll", diceCostLevel, advancementRank->rank);
break;
}
return frand(min, max);
}
// Checks the players inventory to validate they have the required items to perform an upgrade based on the set cost for the passed in level.
bool AdvancementMgr::_PlayerHasItems(Player* player, MpAdvancementRank* advancementRank, uint32 diceCostLevel, uint32 itemEntry1, uint32 itemEntry2, uint32 itemEntry3)
{
// Check if player has the required dice to upgrade the advancement if not do nothing.
uint32 diceCost = advancementRank->materialCost.at(diceCostLevel);
if(!player->HasItemCount(MpConstants::ANCIENT_DICE, diceCost)) {
MpLogger::info("Player {} does not have enough dice to upgrade advancement {}", player->GetName(), advancementRank->advancementId);
return false;
}
// Validate the passed in item for materialId 1 is valid and the player has enough to purchase.
if(itemEntry1 > 0) {
if(!advancementRank->HasMaterial(itemEntry1)) {
MpLogger::error("Material1 {} is not a valid material for advancement {}", itemEntry1, advancementRank->advancementId);
return false;
}
if(!player->HasItemCount(itemEntry1, advancementRank->materialCost[itemEntry1])) {
MpLogger::info("Player {} does not have enough material {} to upgrade advancement {}", player->GetName(), itemEntry1, advancementRank->advancementId);
return false;
}
}
if(itemEntry2 > 0) {
if(!advancementRank->HasMaterial(itemEntry2)) {
MpLogger::error("Material1 {} is not a valid material for advancement {}", itemEntry2, advancementRank->advancementId);
return false;
}
if(!player->HasItemCount(itemEntry2, advancementRank->materialCost[itemEntry2])) {
MpLogger::info("Player {} does not have enough material {} to upgrade advancement {}", player->GetName(), itemEntry2, advancementRank->advancementId);
return false;
}
}
if(itemEntry3 > 0) {
if(!advancementRank->HasMaterial(itemEntry3)) {
MpLogger::error("Material1 {} is not a valid material for advancement {}", itemEntry3, advancementRank->advancementId);
return false;
}
if(!player->HasItemCount(itemEntry3, advancementRank->materialCost[itemEntry3])) {
MpLogger::info("Player {} does not have enough material {} to upgrade advancement {}", player->GetName(), itemEntry3, advancementRank->advancementId);
return false;
}
}
return true;
}
// Remove all items required for the upgrade.
void AdvancementMgr::_ChargeItemCost(Player *player, MpAdvancementRank* advancementRank, uint32 diceCostLevel, uint32 itemEntry1, uint32 itemEntry2, uint32 itemEntry3)
{
uint32 diceCost = advancementRank->materialCost[diceCostLevel];
Item* item = player->GetItemByEntry(MpConstants::ANCIENT_DICE);
item->SetCount(item->GetCount() - diceCost);
item->SendUpdateToPlayer(player);
// Remove the material from the player inventory
if(itemEntry1 > 0) {
item = player->GetItemByEntry(itemEntry1);
item->SetCount(item->GetCount() - advancementRank->materialCost[itemEntry1]);
item->SendUpdateToPlayer(player); // Update the client with the new dice count
}
if(itemEntry2 > 0) {
item = player->GetItemByEntry(itemEntry2);
item->SetCount(item->GetCount() - advancementRank->materialCost[itemEntry2]);
item->SendUpdateToPlayer(player); // Update the client with the new dice count
}
if(itemEntry3 > 0) {
item = player->GetItemByEntry(itemEntry3);
item->SetCount(item->GetCount() - advancementRank->materialCost[itemEntry3]);
item->SendUpdateToPlayer(player); // Update the client with the new dice count
}
return;
}

144
src/AdvancementMgr.h Normal file
View File

@@ -0,0 +1,144 @@
#ifndef ADVANCEMENT_MGR_H
#define ADVANCEMENT_MGR_H
#include "SharedDefines.h"
#include "Player.h"
#include <memory>
#include <unordered_map>
#include <map>
#include <mutex>
enum MpAdvancements
{
MP_ADV_STRENGTH = 0,
MP_ADV_AGILITY = 1,
MP_ADV_STAMINA = 2,
MP_ADV_INTELLECT = 3,
MP_ADV_SPIRIT = 4,
MP_ADV_RESIST_FIRE = 5,
MP_ADV_RESIST_NATURE = 6,
MP_ADV_RESIST_FROST = 7,
MP_ADV_RESIST_SHADOW = 8,
MP_ADV_RESIST_ARCANE = 9,
MP_ADV_MAX = 10
};
/**
* Advancement Rank represents each level for a stat increase that has can be purchases.
* It includes materials, type, and range of successful roll.
*/
struct MpAdvancementRank
{
uint32 rank;
MpAdvancements advancementId;
std::unordered_map<uint32 /*item_entry*/,uint32 /*quantity*/> materialCost;
std::array<int, 3> rollCost; // 0 = low, 1 = mid, 2 = high
// Range of status based on bet dice roll.
std::pair<uint32 /*min*/, uint32 /*max*/> lowRange;
std::pair<uint32 /*min*/, uint32 /*max*/> midRange;
std::pair<uint32 /*min*/, uint32 /*max*/> highRange;
// Used to validate this struct is set correctly
bool IsValid() {
return (rank > 0 && advancementId >= 0 && advancementId < MP_ADV_MAX);
}
// Check if the map has an the item entry for the passed in material
bool HasMaterial(uint32 itemEntry) {
return materialCost.contains(itemEntry);
}
};
// Struct is used for tracking player advancement bonuses for improving stats
struct MpPlayerRank
{
uint32 rank;
MpAdvancements advancementId;
uint32 diceSpent;
float bonus;
MpPlayerRank() : rank(0), diceSpent(0), bonus(0.0f) {}
};
/**
* This singleton class is responsible for managing the advancement system
* used to improve player stats enough to challenge harder difficulties on existing dungeons.
*
* Advancements are purchased by players using based on the mp_material_type table with a "bet"
* on dice roll. This enables players to increase their stats in a random way that is not
* guaranteed to be successful. (Similar to how DND stats rolls work on character creation. )
*/
class AdvancementMgr
{
// Shared mutex for handling writes to shared player advancement data
std::mutex _playerAdvancementMutex;
// All advancement levels for each stat [rank][advancement_id] = AdvancementLevel
std::map<std::pair<uint32 /*rank*/, MpAdvancements>, MpAdvancementRank> _advancementRanks;
// Map of player advancements [player_guid][advancement_id] = rank
std::unordered_map<uint32 /*player_guid*/, std::unordered_map<MpAdvancements, MpPlayerRank>> _playerAdvancements;
// Map of different material types used fo advancing stats for players
std::unordered_map<uint32 /*material_id*/, std::vector<uint32> /* item entries */> _materialTypes;
public:
static AdvancementMgr* instance() {
static AdvancementMgr instance;
return &instance;
}
// Loads advancement information from the database into memory when players are logged in or server starts.
int32 LoadAdvancementRanks();
int32 LoadMaterialTypes();
int32 LoadPlayerAdvancements(Player* player);
// Methods for looking up advancement rank data
MpAdvancementRank* GetAdvancementRank(uint32 rank, MpAdvancements advancement);
// Methods for updating and setting data related to current player advancements
MpPlayerRank* GetPlayerAdvancementRank(Player* player, MpAdvancements advancement);
/**
* This upgrades a player Advancement on the server side, which will handle the following actions:
* 1. Validating player has enough dice and materials to upgrade the advancement
* 2. Rolling the dice to see what bonus is rewarded
* 3. Removing the dice and materials from the player inventory
* 4. Updating the player advancement rank in memory and database
*
* Since different materials can be used for each advancement, at the moment only support one material type from the list. supporting
* mixed materials is more complicated and the UI to support it is much more complex, while this is not as nice it is much simpler to implement.
* That means all materials have to be selected and passed in at the time of making this call.
*/
bool UpgradeAdvancement(Player* player, MpAdvancements advancement, uint32 diceCostLevel, uint32 itemEntry1, uint32 itemEntry2, uint32 itemEntry3);
// Used to reset all advancements for a specific player
bool ResetPlayerAdvancements(Player* player);
private:
AdvancementMgr() {}
~AdvancementMgr() {}
// Will reset all the player advancements and refund the spent dice and material with a penalty for the reset.
void _ResetPlayerAdvancement(Player* player, MpAdvancements advancement);
// Rolls the dice to see how much a bonus is given based on the dice spend level
float _RollAdvancement(MpAdvancementRank* advancementRank, uint32 diceCostLevel);
// Determines if a player has required items to upgrade
bool _PlayerHasItems(Player* player, MpAdvancementRank* advancementRank, uint32 diceCostLevel, uint32 itemEntry1, uint32 itemEntry2, uint32 itemEntry3);
// Removes items from player inventory based on the required advancement rank.
void _ChargeItemCost(Player* player, MpAdvancementRank* advancementRank, uint32 diceCostLevel, uint32 itemEntry1, uint32 itemEntry2, uint32 itemEntry3);
// This will save the advancement purchase to the history database
void _DBSaveAdvancement(Player* player, MpAdvancementRank* advancementRank, MpPlayerRank* playerRank, uint32 diceCost, float roll);
};
#define sAdvancementMgr AdvancementMgr::instance()
#endif

View File

@@ -1,214 +0,0 @@
#include "Chat.h"
#include "MpDataStore.h"
#include "MythicPlus.h"
#include "MpDataStore.h"
#include "MpLogger.h"
#include "Player.h"
#include "ScriptMgr.h"
using namespace Acore::ChatCommands;
// make sure this is the new way to do this, i think it's the old busted shit
class MythicPlus_CommandScript : public CommandScript
{
public:
MythicPlus_CommandScript() : CommandScript("MythicPlus_CommandScript")
{
}
ChatCommandTable GetCommands() const override
{
static ChatCommandTable commandTableMain =
{
{"", HandleHelp, SEC_PLAYER, Console::No},
{"status", HandleStatus, SEC_PLAYER, Console::No},
{"showstats", HandleDebug, SEC_PLAYER, Console::No},
// {"mythic",HandleMythic, SEC_PLAYER, Console::No},
// {"legendary",HandleLegendary, SEC_PLAYER, Console::No},
// {"ascendant",HandleAscendant, SEC_PLAYER, Console::No},
{"set", HandleSetDifficulty, SEC_PLAYER, Console::No},
{"disable", HandleDisable, SEC_ADMINISTRATOR, Console::Yes},
{"enable", HandleEnable, SEC_ADMINISTRATOR, Console::Yes}
};
static ChatCommandTable commandTable =
{
{"mp", commandTableMain},
{"mythicplus", commandTableMain},
{"mp debug", HandleDebug, SEC_PLAYER, Console::No}
};
return commandTable;
}
static bool HandleHelp(ChatHandler* handler, const std::vector<std::string>& /*args*/)
{
std::string helpText = "Mythic+ Commands:\n"
" .mp status - show current global settings of Mythic+ mod\n"
" .mp set [mythic,legendary,ascendant] - Set Mythic+ difficulty in current beta only supports mythic.\n"
" .mp [enable,disable] - enable or disable this mod\n"
" .mp - Show this help message\n";
handler->PSendSysMessage(helpText);
return true;
}
static bool HandleDebug(ChatHandler* handler, const std::vector<std::string>& args)
{
Creature* target = handler->getSelectedCreature();
if(!target) {
handler->PSendSysMessage("You must select a creature to debug.");
return true;
}
handler->PSendSysMessage(LANG_NPCINFO_LEVEL, target->GetLevel());
handler->PSendSysMessage(LANG_NPCINFO_HEALTH, target->GetCreateHealth(), target->GetMaxHealth(), target->GetHealth());
handler->PSendSysMessage("WeaponDmg Main {} - {}",
target->GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE),
target->GetWeaponDamageRange(BASE_ATTACK, MAXDAMAGE)
);
handler->PSendSysMessage("WeaponDmg Range {} - {}",
target->GetWeaponDamageRange(RANGED_ATTACK, MINDAMAGE),
target->GetWeaponDamageRange(RANGED_ATTACK, MAXDAMAGE)
);
handler->PSendSysMessage("WeaponDmg Offhand {} - {}",
target->GetWeaponDamageRange(OFF_ATTACK, MINDAMAGE),
target->GetWeaponDamageRange(OFF_ATTACK, MAXDAMAGE)
);
handler->PSendSysMessage("Attack Power Main {}", target->GetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE));
handler->PSendSysMessage("Attack Power Ranged {}", target->GetModifierValue(UNIT_MOD_ATTACK_POWER_RANGED, BASE_VALUE));
handler->PSendSysMessage(LANG_NPCINFO_ARMOR, target->GetArmor());
handler->PSendSysMessage("Damage Modifier {}", target->GetModifierValue(UNIT_MOD_DAMAGE_MAINHAND, BASE_VALUE));
return true;
}
// sets the difficluty for the group
static bool HandleSetDifficulty(ChatHandler* handler, const std::vector<std::string>& args)
{
Player* player = handler->GetSession()->GetPlayer();
Group* group = player->GetGroup();
if (!group) {
MpLogger::debug("HandleSetMythic() No Group for player: {}", player->GetName());
handler->PSendSysMessage("|cFFFF0000 You must be in a group to be able to set a Mythic+ difficulty.");
return true;
}
if (args.empty()) {
handler->PSendSysMessage("|cFFFF0000 You must specify a difficulty level. Expected values are 'mythic', 'legendary', or 'ascendant'.");
return true;
}
std::string difficulty = args[0];
// if(!sMythicPlus->IsDifficultyEnabled(difficulty)) {
// handler->PSendSysMessage("|cFFFF0000 The difficulty level you have selected is not enabled.");
// return true;
// }
if (!group->IsLeader(player->GetGUID())) {
handler->PSendSysMessage("|cFFFF0000 You must be the group leader to set a Mythic+ difficulty.");
return true;
}
if (player->GetMap()->IsDungeon()) {
player->ResetInstances(player->GetGUID(), INSTANCE_RESET_CHANGE_DIFFICULTY, false);
player->SendResetInstanceSuccess(player->GetMap()->GetId());
return true;
}
if (difficulty == "mythic") {
sMpDataStore->AddGroupData(group, MpGroupData(group, MP_DIFFICULTY_MYTHIC, 0));
}
else if (difficulty == "legendary") {
sMpDataStore->AddGroupData(group,MpGroupData(group, MP_DIFFICULTY_LEGENDARY, 0));
}
else if (difficulty == "ascendant") {
sMpDataStore->AddGroupData(group, MpGroupData(group, MP_DIFFICULTY_ASCENDANT, 0));
}
else {
handler->PSendSysMessage("|cFFFF0000 Invalid difficulty level. Expected values are 'mythic', 'legendary', or 'ascendant'.");
return true;
}
handler->PSendSysMessage("Mythic+ difficulty set to: " + difficulty);
MpLogger::debug("HandleSetMythic() Set difficulty player: {} {}", player->GetName(), difficulty);
return true;
}
static bool HandleMythic(ChatHandler* handler, const std::vector<std::string>& /*args*/)
{
return HandleSetDifficulty(handler, {"mythic"});
}
static bool HandleLegendary(ChatHandler* handler, const std::vector<std::string>& /*args*/)
{
return HandleSetDifficulty(handler, {"legendary"});
}
static bool HandleAscendant(ChatHandler* handler, const std::vector<std::string>& /*args*/)
{
return HandleSetDifficulty(handler, {"ascendant"});
}
static bool HandleStatus(ChatHandler* handler)
{
Player* player = handler->GetPlayer();
std::string status = Acore::StringFormat(
"Mythic+ Status:\n"
" Mythic+ Enabled: %s\n"
" Mythic+ Item Rewards: %s\n"
" Mythic+ DeathLimits: %s\n",
sMythicPlus->Enabled ? "Yes" : "No",
sMythicPlus->EnableItemRewards ? "Yes" : "No",
sMythicPlus->EnableDeathLimits ? "Yes" : "No");
if (player->GetGroup()) {
auto groupData = sMpDataStore->GetGroupData(player->GetGroup()->GetGUID());
if (groupData) {
status += Acore::StringFormat(
" Group Difficulty: %u\n"
" Group Deaths: %u\n",
groupData->difficulty,
groupData->deaths);
} else {
status += " Group Difficulty: Not Set\n";
}
}
handler->PSendSysMessage(status);
return true;
}
static bool HandleDisable(ChatHandler* handler)
{
MpLogger::debug("HandleDisable()");
sMythicPlus->Enabled = false;
handler->SendSysMessage("Mythic+ mod has been disabled.");
return true;
}
static bool HandleEnable(ChatHandler* handler)
{
MpLogger::debug("HandleEnable()");
sMythicPlus->Enabled = false;
handler->SendSysMessage("Mythic+ mod has been enabled.");
return true;
}
};
void Add_MP_CommandScripts()
{
MpLogger::debug("Add_MP_CommandScripts()");
new MythicPlus_CommandScript();
}

View File

@@ -0,0 +1,35 @@
#include "CreatureHooks.h"
#include "MpLogger.h"
class BaseCreatureHandler {
public:
BaseCreatureHandler(uint32 entry) {
ASSERT(entry > 0);
MpLogger::debug("Registering JustDied and OnSpawn events for entry: ", entry);
// Register the JustDied event
sCreatureHooks->RegisterJustDied(entry, [this](Creature* creature, Unit* killer) {
this->OnJustDied(creature, killer);
});
// Register the OnSpawn event
sCreatureHooks->RegisterOnSpawn(entry, [this](Creature* creature) {
this->OnJustSpawned(creature);
});
sCreatureHooks->RegisterOnAddToInstance(entry, [this](Creature* creature) {
this->OnAddToInstance(creature);
});
}
// Virtual event handlers to be overridden by bosses
virtual void OnJustDied(Creature* /*creature*/, Unit* /*killer*/) {}
virtual void OnJustSpawned(Creature* /*creature*/) {}
virtual void OnAddToInstance(Creature* /*creature*/) {}
virtual ~BaseCreatureHandler() {}
};

View File

@@ -0,0 +1,84 @@
#include "CreatureHooks.h"
#include "MythicPlus.h"
#include "MpLogger.h"
void CreatureHooks::RegisterJustDied(uint32 entry, CreatureHook<Creature*, Unit*> callback) {
(*_JustDiedHandlers)[entry].push_back(callback);
}
void CreatureHooks::RegisterOnSpawn(uint32 entry, CreatureHook<Creature*>callback) {
(*_OnSpawnHandlers)[entry].push_back(callback);
}
void CreatureHooks::RegisterOnAddToInstance(uint32 entry, CreatureHook<Creature*> callback) {
(*_OnAddToInstanceHandlers)[entry].push_back(callback);
}
// Call health events if the creature's health is at or below the percentage
// void CheckHealthEvents(Creature* creature) {
// uint32 entry = creature->GetEntry();
// uint32 currentHealthPct = creature->GetHealthPct();
// if (_healthPercentEvents->contains(entry)) {
// for (const auto& [percent, callbacks] : _healthPercentEvents->at(entry)) {
// if (currentHealthPct <= percent) {
// for (auto& callback : callbacks) {
// callback(creature); // Trigger custom behavior
// }
// }
// }
// }
// }
void CreatureHooks::JustDied(Creature* creature, Unit* killer) {
if(!creature) {
MpLogger::debug("JustDied() called with nullptr for creature");
return;
}
if(!killer) {
MpLogger::debug("JustDied() called with nullptr for killer");
return;
}
uint32 entry = creature->GetEntry();
if (_JustDiedHandlers->contains(entry)) {
for (auto& callback : _JustDiedHandlers->at(entry)) {
MpLogger::debug("JustDied() called for creature: {}", entry);
callback(creature, killer);
}
}
}
void CreatureHooks::JustSpawned(Creature* creature) {
if(!creature) {
MpLogger::debug("JustSpawned() called with nullptr for creature");
return;
}
uint32 entry = creature->GetEntry();
MpInstanceData* instanceData = sMpDataStore->GetInstanceData(creature->GetMapId(), creature->GetInstanceId());
if(instanceData) {
sMythicPlus->AddScaledCreature(creature, instanceData);
}
if (_OnSpawnHandlers->contains(entry)) {
for (auto& callback : _OnSpawnHandlers->at(entry)) {
callback(creature);
MpLogger::debug("JustSpawned() called in CreatureHook: {}", entry);
}
}
}
void CreatureHooks::AddToInstance(Creature* creature) {
uint32 entry = creature->GetEntry();
if (_OnAddToInstanceHandlers->contains(entry)) {
for (auto& callback : _OnAddToInstanceHandlers->at(entry)) {
callback(creature);
MpLogger::debug("AddedToInstance() called in CreatureHook: {}", entry);
}
}
}

View File

@@ -0,0 +1,92 @@
#ifndef CREATUREHOOKS_H
#define CREATUREHOOKS_H
#include <functional>
#include <unordered_map>
#include <vector>
#include <memory>
#include "Creature.h"
#include "ObjectGuid.h"
// Struct to store the state of which handlers have been fired for a creature
struct CreatureEventState {
bool onDeathEventFired = false;
bool onSpawnEventFired = false;
bool onAddedToInstanceEventFired = false;
uint8 deaths = 0; // Count of deaths
};
// Type aliases for creature event handling
// using CreatureHook = std::function<void(Creature*)>;
// using HookList = std::vector<CreatureHook>;
// using HandlerMap = std::unordered_map<uint32, HookList>;
// Type alias for variadic event hooks
template<typename... Args>
using CreatureHook = std::function<void(Args...)>;
// Type alias for a list of hooks that take varying arguments
template<typename... Args>
using HandlersList = std::vector<CreatureHook<Args...>>;
// HandlerMap using variadic templates
template<typename... Args>
using HandlerMap = std::unordered_map<uint32, HandlersList<Args...>>;
using CreatureEventStateMap = std::map<ObjectGuid, CreatureEventState>;
class CreatureHooks {
private:
CreatureHooks():
_OnSpawnHandlers(std::make_unique<HandlerMap<Creature*>>()),
_JustDiedHandlers(std::make_unique<HandlerMap<Creature*, Unit*>>()),
_OnAddToInstanceHandlers(std::make_unique<HandlerMap<Creature*>>()),
_eventStates(std::make_unique<CreatureEventStateMap>())
{
_OnSpawnHandlers->reserve(128);
_JustDiedHandlers->reserve(128);
_OnAddToInstanceHandlers->reserve(100);
}
~CreatureHooks() {
_OnSpawnHandlers->clear();
_JustDiedHandlers->clear();
_OnAddToInstanceHandlers->clear();
_eventStates->clear();
}
// ensure we only ever have one instance of this class
CreatureHooks(const CreatureHooks&) = delete;
CreatureHooks& operator=(const CreatureHooks&) = delete;
// Data members for storing event handlers
std::unique_ptr<HandlerMap<Creature*>> _OnSpawnHandlers;
std::unique_ptr<HandlerMap<Creature*, Unit*>> _JustDiedHandlers;
std::unique_ptr<HandlerMap<Creature*>> _OnAddToInstanceHandlers;
// Tracks state to know which handlers need to be fired again
std::unique_ptr<CreatureEventStateMap> _eventStates;
public:
static CreatureHooks* instance() {
static CreatureHooks instance;
return &instance;
}
// Register events for specific actions
void RegisterJustDied(uint32 entry, CreatureHook<Creature*, Unit*> callback);
void RegisterOnSpawn(uint32 entry, CreatureHook<Creature*> callback);
void RegisterOnAddToInstance(uint32 entry, CreatureHook<Creature*> callback);
// Event triggers
void JustDied(Creature* creature, Unit* killer);
void JustSpawned(Creature* creature);
void AddToInstance(Creature* creature);
};
#define sCreatureHooks CreatureHooks::instance()
#endif // CREATUREHOOKS_H

View File

@@ -0,0 +1,32 @@
// #include "MpDataStore.h"
// #include "MpLogger.h"
// class CreatureOverride {
// public:
// uint32 entry;
// MpDifficulty difficulty;
// Creature* creature;
// CreatureOverride(uint32 entry): entry(entry), difficulty(difficulty) {
// sMpDataStore->AddCreatureOverride(this->entry, this);
// }
// virtual void Modify(Creature* creature) final {
// this->creature = creature;
// }
// virtual void HandleModify() = 0;
// void ModifyAttackPower(uint32 ap) {
// if(this)
// }
// void ModifyHealth(uint32 health) {}
// void ModifyArmor(uint32 armor) {}
// void ModifySpellPower(uint32 sp) {}
// ~CreatureOverride() {}
// };

View File

@@ -0,0 +1,45 @@
#include "MpClientDispatcher.h"
#include "MpEventProcessor.h"
#include "Player.h"
#include "MpLogger.h"
#include "Chat.h"
#include "WorldPacket.h"
#include <string_view>
#include <string>
/**
* In order to allow communication from client UIs without modifying mod-eluna directly to support
* This sends a packet directly using the AddOn message channel in the background.
*/
bool MpClientDispatcher::Dispatch(MpClientEvent event, Player* player, std::vector<std::string>& args)
{
if(!MpClientEventNames.contains(event)) {
MpLogger::warn("No event registered for event: {}", event);
return false;
}
// Build the message string in same format to send to the client.
std::string_view eventName = MpClientEventNames.at(event);
std::string message = std::to_string(player->GetGUID().GetCounter()) + "|" + eventName.data();
for(auto& arg : args) {
message += "|" + arg;
}
std::string prefix = std::string(MP_DATA_CHAT_CHANNEL);
std::string fullmsg = prefix + "\t" + message;
WorldPacket data(SMSG_MESSAGECHAT, 100);
data << uint8(CHAT_MSG_ADDON);
data << int32(LANG_ADDON);
data << player->GetGUID().GetCounter();
data << uint32(0);
data << player->GetGUID().GetCounter();
data << uint32(fullmsg.length() + 1);
data << fullmsg;
data << uint8(0);
player->GetSession()->SendPacket(&data);
return 0;
return true;
}

View File

@@ -0,0 +1,35 @@
#ifndef MYTHIC_PLUS_CLIENT_DISPATCHER_H
#define MYTHIC_PLUS_CLIENT_DISPATCHER_H
#include "MpEvent.h"
#include "Player.h"
#include <string>
#include <string_view>
#include <vector>
class MpClientDispatcher
{
public:
static MpClientDispatcher* instance() {
static MpClientDispatcher instance;
return &instance;
}
MpClientDispatcher(const MpClientDispatcher&) = delete;
MpClientDispatcher& operator=(const MpClientDispatcher&) = delete;
// encode and send a message to the client for an event in the map
bool Dispatch(MpClientEvent event, Player* player, std::vector<std::string>& args);
private:
MpClientDispatcher() {};
~MpClientDispatcher() {};
};
#define sMpClientDispatcher MpClientDispatcher::instance()
#endif

47
src/Event/MpEvent.h Normal file
View File

@@ -0,0 +1,47 @@
#include <array>
#include <string_view>
#include <string>
#include <unordered_map>
#ifndef MP_EVENTS_H
#define MP_EVENTS_H
// This defines list of incoming events typically from the client
enum class MpEvent
{
None, // Default catch all for no event
Invalid, // Used to capture identify error events
UpgradeAdvancement, // Upgrades a player advancement 1 level
ResetAdvancement, // Resets a player Advancement back to 0
ResetAllAdvancements, // Resets all player advancements
GetAdvancementRank // Get the details about the rank of a specific advancement (cost, bonus, etc)
};
// Client events that are communicated to the over an addon message to the player client
enum class MpClientEvent
{
Error,
UpgradeAdvancement,
ResetAdvancement,
ResetAllAdvancements,
GetAdvancementRank
};
// Mapping of Event Strings to EventNames for Event Callbacks from client
inline std::unordered_map<std::string_view, MpEvent> MpEventMap = {{
{"UpgradeAdvancement", MpEvent::UpgradeAdvancement},
{"ResetAdvancement", MpEvent::ResetAdvancement},
{"ResetAllAdvancements", MpEvent::ResetAllAdvancements},
{"GetAdvancementRank", MpEvent::ResetAllAdvancements}
}};
inline std::unordered_map<MpClientEvent, std::string_view> MpClientEventNames = {{
{MpClientEvent::Error, "Error"},
{MpClientEvent::UpgradeAdvancement, "UpgradeAdvancement"},
{MpClientEvent::ResetAdvancement, "ResetAdvancement"},
{MpClientEvent::ResetAllAdvancements, "ResetAllAdvancements"},
{MpClientEvent::GetAdvancementRank, "GetAdvancementRank"}
}};
#endif

View File

@@ -0,0 +1,139 @@
#include "MpEvent.h"
#include "MpLogger.h"
#include "AdvancementMgr.h"
#include "MpEventProcessor.h"
#include "MpClientDispatcher.h"
#include "Player.h"
#include <string>
/**
* All handlers for passed between client and server handlers are managed
* here.
*/
/**
* Handles Updates to a players advancement system
* Event format
* p|playerGuid|UpgradeAdvancement|advancementId|rank|diceSpent|bonus
*/
enum class MP_EVENT_CODE
{
SUCCESS = 0,
INVALID_EVENT = 100,
INVALID_ARGUMENT_SIZE = 201,
INVALID_ARGUMENT = 202,
INVALID_ARGUMENT_TYPE = 203,
};
// Send an error event to the client
bool SendEventError(Player* player, const std::string& method, MP_EVENT_CODE code, std::string message)
{
std::vector<std::string> clientError = { std::to_string(static_cast<int>(code)), message };
MpLogger::error("Event Processor) Sending client error: {} {}", code, message);
sMpClientDispatcher->Dispatch(MpClientEvent::Error, player, clientError);
return false;
}
class UpdateAdvancements : public MpEventInterface
{
public:
const std::string EventName() const override
{
return "UpgradeAdvancement";
}
bool Execute(Player* player, std::vector<std::string>& args)
{
// Store the event data to send back to the client for parsing
std::vector<std::string> eventData;
MpLogger::info("(EventProcessor) Executing {}}", EventName());
for(auto& arg : args) {
MpLogger::info("{} Arg: {}", EventName(), arg);
}
// Validate the message is int he right format
if(args.size() != 5) {
return SendEventError(player, EventName(),MP_EVENT_CODE::INVALID_ARGUMENT_SIZE, "Invalid number of arguments expected 5, found " + std::to_string(args.size()));
}
uint32 advancementId = std::stoi(args[0]);
if(advancementId >= MpAdvancements::MP_ADV_MAX) {
return SendEventError(player, EventName(),MP_EVENT_CODE::INVALID_ARGUMENT, "Invalid advancement id " + args[0] + " max is " + std::to_string(MpAdvancements::MP_ADV_MAX));
}
uint32 diceLevel = std::stoi(args[1]);
if(diceLevel < 1 || diceLevel > 3) {
return SendEventError(player, EventName(),MP_EVENT_CODE::INVALID_ARGUMENT, "Invalid dice level " + args[1] + " valid values are 1,2,3");
}
uint32 itemEntry1 = std::stoi(args[2]);
if(itemEntry1 == 0) {
return SendEventError(player, EventName(),MP_EVENT_CODE::INVALID_ARGUMENT, "Invalid item entry1 can not be empty " + args[2]);
}
uint32 itemEntry2 = std::stoi(args[3]);
uint32 itemEntry3 = std::stoi(args[4]);
// Upgrade the advancement for the player!
if(! sAdvancementMgr->UpgradeAdvancement(player, static_cast<MpAdvancements>(advancementId), diceLevel, itemEntry1, itemEntry2, itemEntry3)) {
return SendEventError(player, EventName(),MP_EVENT_CODE::INVALID_ARGUMENT, "Failed to upgrade advancement for player " + player->GetName());
}
eventData = {"0", "success"};
// Send response back to the client
sMpClientDispatcher->Dispatch(MpClientEvent::UpgradeAdvancement, player, eventData);
return true;
}
};
class GetAdvancementRank : public MpEventInterface
{
public:
const std::string EventName() const override
{
return "GetAdvancementRank";
}
bool Execute(Player* player, std::vector<std::string>& args)
{
// Store the event data to send back to the client for parsing
std::vector<std::string> eventData;
MpLogger::info("(EventProcessor) Executing {}}", EventName());
for(auto& arg : args) {
MpLogger::info("{} Arg: {}", EventName(), arg);
}
// Validate the message is int he right format
if(args.size() != 1) {
return SendEventError(player, EventName(),MP_EVENT_CODE::INVALID_ARGUMENT_SIZE, "Invalid number of arguments expected 1, found " + std::to_string(args.size()));
}
uint32 advancementId = std::stoi(args[0]);
if(advancementId >= MpAdvancements::MP_ADV_MAX) {
return SendEventError(player, EventName(),MP_EVENT_CODE::INVALID_ARGUMENT, "Invalid advancement id " + args[0] + " max is " + std::to_string(MpAdvancements::MP_ADV_MAX));
}
MpAdvancementRank* rank = sAdvancementMgr->GetAdvancementRank(1, static_cast<MpAdvancements>(advancementId));
if(!rank) {
return SendEventError(player, EventName(),MP_EVENT_CODE::INVALID_ARGUMENT, "Failed to get advancement rank for player " + player->GetName());
}
eventData = {std::to_string(rank->rank), std::to_string(rank->advancementId)};
// Send response back to the client
sMpClientDispatcher->Dispatch(MpClientEvent::GetAdvancementRank, player, eventData);
return true;
}
};
void MP_Register_EventHandlers()
{
auto updateAdvancements = std::make_shared<UpdateAdvancements>();
sMpEventProcessor->RegisterHandler(MpEvent::UpgradeAdvancement, updateAdvancements);
}

View File

@@ -0,0 +1,131 @@
#include "MpEventProcessor.h"
#include "MythicPlus.h"
#include "MpLogger.h"
#include "Player.h"
#include <boost/algorithm/string/replace.hpp>
#include <string_view>
#include <string>
#include <vector>
bool MpEventProcessor::ProcessMessage(Player* player, const std::string& msg) {
if(!player) {
MpLogger::error("Null player passed to processMessage");
return false;
}
// check prefix of message channel is formatted correctly
if(! msg.starts_with(MP_DATA_CHAT_CHANNEL)) {
MpLogger::error("Invalid message format received from player {} message: {}", player->GetName(), msg);
return false;
}
std::string message = msg;
// shift the message identifier off the front including first '|' character
message.erase(0, MP_DATA_CHAT_CHANNEL.size()+1);
// clean up the message before passing it to the parser
boost::replace_all(message, "||", "|");
EventParseRslt result = _parsePlayerMessage(player, message);
MpEvent event = std::get<0>(result);
uint32 guid = std::get<1>(result);
std::vector<std::string> args = std::get<2>(result);
MpLogger::info("MpEvent Processor - event: {} guid: {} args: {}", event, guid, args.size());
// If th message was not able to be parsed it is a failure
if(event == MpEvent::Invalid) {
MpLogger::warn("Invalid event, could not be parsed for player {} message: {}", player->GetName(), message);
return false;
}
// if the message is not from the same player who called it ignore it as it is attempt to hack the system
if(player->GetGUID().GetCounter() != guid) {
MpLogger::warn("Player {} sent a message {} for eventId: {} player guid does not match", player->GetName(), message, event);
return false;
}
// If the event is not registered ignore it
if(!_eventHandlers.contains(event)) {
MpLogger::info("No handler registered for event: {}", event);
return false;
}
return Dispatch(event, player, args);
}
void MpEventProcessor::RegisterHandler(MpEvent event, std::shared_ptr<MpEventInterface> handler) {
_eventHandlers[event] = handler;
}
// This fires the execution to the actual event.
bool MpEventProcessor::Dispatch(MpEvent event, Player* player, std::vector<std::string>& args) {
if(!_eventHandlers.contains(event)) {
MpLogger::warn("No handler registered for event: {}", event);
return false;
}
return _eventHandlers[event]->Execute(player, args);
}
// Find our eventId using the string name
MpEvent MpEventProcessor::_getEventByName(std::string_view eventName)
{
auto it = MpEventMap.find(eventName);
return (it != MpEventMap.end()) ? it->second : MpEvent::None;
}
/**
* Parse the incoming message into id, event, and handler arguments
*/
EventParseRslt MpEventProcessor::_parsePlayerMessage(Player* player, const std::string& msg)
{
if(msg[0] != 'p') {
MpLogger::warn("Invalid player message format received from player {} message: {}", player->GetName(), msg);
return EventParseRslt{MpEvent::Invalid, 0, {}};
}
std::vector<std::string> handlerArgs;
char delimiter = '|';
// split the protocol into valid parts
std::vector<std::string> parts = _splitString(msg, delimiter);
if (parts.size() < 3) {
MpLogger::warn("Malformed player message received from player {}: {}", player->GetName(), msg);
return EventParseRslt{MpEvent::Invalid, 0, {}};
}
MpEvent event = _getEventByName(parts[2]);
handlerArgs.assign(parts.begin() + 3, parts.end());
MpLogger::info("Player {} sent a client event message {} for event: {} eventId: {} ", player->GetName(), msg, MP_DATA_CHAT_CHANNEL, parts[2], event);
return EventParseRslt{event, player->GetGUID().GetCounter(), std::move(handlerArgs)};
}
// Split the string passed in by delimiters
std::vector<std::string> MpEventProcessor::_splitString(const std::string& s, char delimiter) {
std::vector<std::string> tokens;
size_t start = 0;
size_t end = s.find(delimiter);
while (end != std::string::npos) {
if (end != start) {
tokens.emplace_back(s.substr(start, end - start));
}
start = end + 1;
end = s.find(delimiter, start);
}
// Add the last token if it's not empty
if (start < s.length()) {
tokens.emplace_back(s.substr(start));
}
return tokens;
}

View File

@@ -0,0 +1,86 @@
#ifndef MYTHIC_PLUS_EVENT_PROCESSOR_H
#define MYTHIC_PLUS_EVENT_PROCESSOR_H
#include "MpEvent.h"
#include "Player.h"
#include <string>
#include <string_view>
#include <vector>
/**
* In order to allow communication from client UIs without modifying mod-eluna directly to support
* this mods functionality the following custom chat channel below is for all MP UI Client interactions.
*
* p|playerGuid|action|input1|input2|input3...
*/
inline const std::string_view MP_DATA_CHAT_CHANNEL = "MPUi";
/**
* Interface for all Mythic+ Event communication between client and server
*/
class MpEventInterface
{
public:
MpEventInterface() = default;
virtual ~MpEventInterface() = default;
[[nodiscard]] virtual bool Execute(Player* player, std::vector<std::string>& args) = 0;
[[nodiscard]] virtual const std::string EventName() const = 0;
};
using EventParseRslt = std::tuple<MpEvent, uint32_t, std::vector<std::string>>;
/**
* This class is responsible for processing events that are send from the client. Events
* are processed FIFO and executed. This is specifically to allow AddOn or AIO
* communication to this module without requiring additional changes to mod-eluna or the core.
*
* Message Body Format
* Client Player Events
* - p|playerGuid|action|input1|input2|input3...
* i.e) p|5793|UpgradeAdvancement|0|10|2
*/
class MpEventProcessor
{
public:
static MpEventProcessor* instance() {
static MpEventProcessor instance;
return &instance;
}
MpEventProcessor(const MpEventProcessor&) = delete;
MpEventProcessor& operator=(const MpEventProcessor&) = delete;
// Process a message from a specific player
bool ProcessMessage(Player* player, const std::string& msg);
// Registers a handler for a valid MpEvent specified in the MpEvent enum
// In this design Event:Handler is 1:1
void RegisterHandler(MpEvent event, std::shared_ptr<MpEventInterface> handler);
// Dispatch will execute the event handler
bool Dispatch(MpEvent event, Player* player, std::vector<std::string>& args);
private:
MpEventProcessor() {}
~MpEventProcessor() {}
// List of registered handlers for event management
std::unordered_map<MpEvent, std::shared_ptr<MpEventInterface>> _eventHandlers;
// Get the correct Event associated to handlers by the message strong
MpEvent _getEventByName(std::string_view eventName);
// Parse a message from the player
EventParseRslt _parsePlayerMessage(Player* player, const std::string& msg);
// Helper to break up a string into pieces used in parsing the message
std::vector<std::string> _splitString(const std::string& s, char delimiter = '|');
};
#define sMpEventProcessor MpEventProcessor::instance()
#endif // MYTHIC_PLUS_EVENT_PROCESSOR_H

View File

@@ -1,35 +0,0 @@
#include "MpDataStore.h"
#include "MpLogger.h"
#include "ScriptMgr.h"
#include "Group.h"
// this handles updating custom group difficulties used in auto balancing mobs and
// scripts that enable buffs on mobs randomly
class MythicPlus_GroupScript : public GroupScript
{
public:
MythicPlus_GroupScript() : GroupScript("MythicPlus_GroupScript") { }
void OnCreate(Group* group, Player* leader) override {
if (!group) {
return;
}
if(!leader) {
return;
}
// sMpDataStore->AddGroupData(group->GetGUID(), gd);
}
void OnDisband(Group* group) override {
sMpDataStore->RemoveGroupData(group);
}
};
void Add_MP_GroupScripts()
{
MpLogger::debug("Add_MP_GroupScripts()");
new MythicPlus_GroupScript();
}

View File

@@ -0,0 +1,26 @@
// #include "BaseCreatureHandler.h"
// #include "Spell.h"
// /**
// * Bazzalan need some upgrades so made him more formidable
// */
// class Ragefire_Bazzalan_Mythic : public BaseCreatureHandler
// {
// public:
// Ragefire_Bazzalan_Mythic() : BaseCreatureHandler(11519) {}
// void OnAddToInstance(Creature* creature) override {
// uint32 health = creature->GetMaxHealth() * 2;
// creature->SetCreateHealth(health);
// creature->SetMaxHealth(health);
// creature->SetHealth(health);
// creature->ResetPlayerDamageReq();
// creature->SetModifierValue(UNIT_MOD_HEALTH, BASE_VALUE, (float)health);
// creature->AddExtraAttacks(3);
// creature->SetObjectScale(2.0f);
// }
// };

11
src/MpConstants.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef MP_CONSTANTS_H
#define MP_CONSTANTS_H
namespace MpConstants
{
constexpr int ANCIENT_DICE = 911000;
constexpr int DARK_SPIKE = 911001;
}
#endif

View File

@@ -1,9 +1,16 @@
#include "CharacterDatabase.h"
#include "MpDataStore.h"
#include "Chat.h"
#include "Group.h"
#include "MpLogger.h"
// Adds an entry for the group difficult to memory and updats database
void MpDataStore::AddGroupData(Group *group, MpGroupData groupData) {
if(!group) {
MpLogger::error("AddGroupData called with null group pointer");
return;
}
ObjectGuid guid = group->GetGUID();
if (!guid) {
@@ -15,19 +22,81 @@ void MpDataStore::AddGroupData(Group *group, MpGroupData groupData) {
MpLogger::error("AddGroupData called with invalid difficulty");
return;
}
// if we already have data override it
if (_groupData->contains(guid)) {
_groupData->at(guid) = groupData;
} else {
_groupData->emplace(guid, groupData);
Player* leader = group->GetLeader();
if(!leader) {
MpLogger::error("AddGroupData called with null group leader");
return;
}
MpLogger::debug("Add Group difficulty for group {} to {}", guid.GetCounter());
Map* map = leader->GetMap();
if (!map) {
MpLogger::error("AddGroupData called with null map for group leader");
return;
}
CharacterDatabase.Execute("REPLACE INTO group_difficulty (guid, difficulty) VALUES ({},{}) ",
guid.GetCounter(),
groupData.difficulty
);
auto instance = map->ToInstanceMap();
// if we already have data override it
if (_groupData->contains(guid)) {
MpGroupData existingData = _groupData->at(guid);
if(groupData.difficulty == MP_DIFFICULTY_HEROIC || groupData.difficulty == MP_DIFFICULTY_NORMAL || groupData.difficulty != existingData.difficulty) {
// if we set a lower difficulty and we are in an instance we need to kick the group out and reset the instance.
if(instance && instance->HavePlayers()) {
instance->Reset(2); // 2 = reset all
const Map::PlayerList players = map->GetPlayers();
for (auto itr = players.begin(); itr != players.end(); ++itr) {
Player* player = itr->GetSource();
if(!player) {
MpLogger::error("AddGroupData called with null player in instance");
}
ChatHandler(player->GetSession()).SendSysMessage("The group leader has changed the difficulty setting. You have been removed from the instance.");
//("The group leader has changed the difficulty setting. You have been removed from the instance.");
// player->GetSession()->SendNotification("The group leader has changed the difficulty setting. You have been removed from the instance.");
}
}
}
_groupData->at(guid) = groupData;
} else {
if(instance && instance->HavePlayers()) {
instance->Reset(2); // 2 = reset all
const Map::PlayerList players = map->GetPlayers();
for (auto itr = players.begin(); itr != players.end(); ++itr) {
Player* player = itr->GetSource();
if(!player) {
MpLogger::error("AddGroupData called with null player in instance");
}
if(player) {
ChatHandler(player->GetSession()).SendSysMessage("The group leader has changed the difficulty setting. You have been removed from the instance.");
// player->GetSession()->SendNotification("The group leader has changed the difficulty setting. You have been removed from the instance."); -- previous implementation for older core users
}
}
}
_groupData->emplace(guid, groupData);
}
const Group::MemberSlotList members = group->GetMemberSlots();
for (const auto &memberSlot : members) {
Player* player = ObjectAccessor::FindPlayer(memberSlot.guid);
if (player) {
DBUpdatePlayerInstanceData(player->GetGUID(), groupData.difficulty);
}
}
}
MpGroupData* MpDataStore::GetGroupData(Group* group) {
@@ -61,7 +130,7 @@ void MpDataStore::PushGroupInstanceKey(Group *group, uint32 mapId, uint32 instan
return;
}
_groupData->at(guid).instanceDataKeys.push_back(GetInstanceDataKey(mapId, instanceId));
// Need to potentialy reset here?
}
// This clears out any group data from memory and the database
@@ -72,9 +141,10 @@ void MpDataStore::RemoveGroupData(Group *group) {
CharacterDatabase.Execute("DELETE FROM group_difficulty WHERE guid = {}) ", group->GetGUID().GetCounter());
}
void MpDataStore::AddPlayerData(ObjectGuid guid, MpPlayerData pd) {
MpLogger::debug("AddPlayerData for player {}", guid.GetCounter());
// Adds PlayerData related to MythicRun Status to map
void MpDataStore::AddPlayerData(ObjectGuid guid, MpPlayerData* pd) {
_playerData->emplace(guid, pd);
}
void MpDataStore::RemovePlayerData(ObjectGuid guid) {
@@ -82,6 +152,11 @@ void MpDataStore::RemovePlayerData(ObjectGuid guid) {
_playerData->erase(guid);
}
void MpDataStore::ResetPlayerData(ObjectGuid guid) {
MpLogger::debug("ResetPlayerData for player {}", guid.GetCounter());
_playerData->erase(guid);
}
void MpDataStore::AddInstanceData(uint32 mapId, uint32 instanceId, MpInstanceData instanceSettings) {
_instanceData->emplace(GetInstanceDataKey(mapId, instanceId), instanceSettings);
}
@@ -100,7 +175,7 @@ void MpDataStore::RemoveInstanceData(uint32 mapId, uint32 instanceId) {
}
void MpDataStore::AddCreatureData(ObjectGuid guid, MpCreatureData creatureData) {
MpLogger::debug("AddInstanceCreatureData for creature {}", guid.GetCounter());
// MpLogger::debug("AddInstanceCreatureData for creature {}", guid.GetCounter());
_instanceCreatureData->emplace(guid, creatureData);
}
@@ -112,6 +187,18 @@ MpCreatureData* MpDataStore::GetCreatureData(ObjectGuid guid) {
return &_instanceCreatureData->at(guid);
}
std::vector<MpCreatureData*> MpDataStore::GetInstanceCreatures(uint32 mapId, uint32 instanceId) {
std::vector<MpCreatureData*> creatures;
for (auto& [guid, creatureData] : *_instanceCreatureData) {
Creature* creature = creatureData.creature;
if (creature->GetMapId() == mapId && creature->GetInstanceId() == instanceId) {
creatures.push_back(&creatureData);
}
}
return creatures;
}
std::vector<MpCreatureData*> MpDataStore::GetUnscaledCreatures(uint32 mapId, uint32 instanceId) {
std::vector<MpCreatureData*> creatures;
for (auto& [guid, creatureData] : *_instanceCreatureData) {
@@ -151,13 +238,41 @@ int32 MpDataStore::GetDamageScaleFactor(int32 mapId, int32 difficulty) const {
return GetScaleFactor(mapId, difficulty).dmgBonus;
}
int32 MpDataStore::GetSpellScaleFactor(int32 mapId, int32 difficulty) const {
return GetScaleFactor(mapId, difficulty).spellBonus;
}
int32 MpDataStore::GetMaxDamageScaleFactor(int32 mapId, int32 difficulty) const {
return GetScaleFactor(mapId, difficulty).maxDamageBonus;
}
void MpDataStore::SetHealthScaleFactor(int32 mapId, int32 difficulty, int32 newValue) {
auto key = GetScaleFactorKey(mapId, difficulty);
if (_scaleFactors && _scaleFactors->contains(key)) {
_scaleFactors->at(key).healthBonus = newValue;
}
}
void MpDataStore::SetDamageScaleFactor(int32 mapId, int32 difficulty, int32 newValue) {
auto key = GetScaleFactorKey(mapId, difficulty);
if (_scaleFactors && _scaleFactors->contains(key)) {
_scaleFactors->at(key).dmgBonus = newValue;
}
}
void MpDataStore::SetSpellScaleFactor(int32 mapId, int32 difficulty, int32 newValue) {
auto key = GetScaleFactorKey(mapId, difficulty);
if (_scaleFactors && _scaleFactors->contains(key)) {
_scaleFactors->at(key).spellBonus = newValue;
}
}
int32 MpDataStore::LoadScaleFactors() {
// 0 1 2 3 4
QueryResult result = WorldDatabase.Query("SELECT mapId, dmg_bonus, hp_bonus, difficulty, max FROM mythic_plus_scale_factors");
_scaleFactors->clear();
// 0 1 2 3 4 5
QueryResult result = WorldDatabase.Query("SELECT mapId, dmg_bonus, spell_bonus, hp_bonus, difficulty, max FROM mp_scale_factors");
if (!result) {
MpLogger::error("Failed to load mythic scale factors from database");
return 0;
@@ -167,22 +282,180 @@ int32 MpDataStore::LoadScaleFactors() {
Field* fields = result->Fetch();
uint32 mapId = fields[0].Get<uint32>();
int32 damageBonus = fields[1].Get<int32>();
int32 healthBonus = fields[2].Get<int32>();
int32 difficulty = fields[3].Get<int32>();
int32 maxDamageBonus = fields[4].Get<int32>();
int32 spellBonus = fields[2].Get<int32>();
int32 healthBonus = fields[3].Get<int32>();
int32 difficulty = fields[4].Get<int32>();
int32 maxDamageBonus = fields[5].Get<int32>();
MpScaleFactor scaleFactor = {
.dmgBonus = damageBonus,
.healthBonus = healthBonus,
.spellBonus = spellBonus,
.maxDamageBonus = maxDamageBonus
};
_mutableScaleFactors->emplace(GetScaleFactorKey(mapId, difficulty), scaleFactor);
_scaleFactors->emplace(GetScaleFactorKey(mapId, difficulty), scaleFactor);
} while (result->NextRow());
// move to const map one loaded so can not be changed after
_scaleFactors = std::move(_mutableScaleFactors);
return int32(_scaleFactors->size());
}
/**
* Database Calls below for storing player data.
* @todo refactor to use prepared statements
*/
void MpDataStore::DBUpdatePlayerInstanceData(ObjectGuid playerGuid, MpDifficulty difficulty, uint32 mapId, uint32 instanceId, uint32 deaths) {
if (!playerGuid) {
MpLogger::error("DBAddPlayerData called with invalid playerData");
return;
}
CharacterDatabase.Execute("REPLACE INTO mp_player_instance_data (guid, difficulty, mapId, instanceId, deaths) VALUES ({},{},{},{},{}) ",
playerGuid.GetCounter(),
difficulty,
mapId,
instanceId,
deaths
);
}
void MpDataStore::DBResetPlayerDeaths(Player* player) {
if (!player) {
MpLogger::error("DBUpdateDeaths called with invalid playerId");
return;
}
CharacterDatabase.Execute("UPDATE mp_player_instance_data SET deaths = 0 WHERE guid = {} and mapId = {} and instanceId = {}",
player->GetGUID().GetCounter(),
player->GetMapId(),
player->GetInstanceId()
);
}
void MpDataStore::DBAddPlayerDeath(Player* player) {
if (!player) {
MpLogger::error("DBAddPlayerDeath called with invalid player");
return;
}
CharacterDatabase.Execute("UPDATE mp_player_instance_data SET deaths = deaths + 1 WHERE guid = {} and mapId = {} and instanceId = {}",
player->GetGUID().GetCounter(),
player->GetMapId(),
player->GetInstanceId()
);
}
// Logs death for player that occurs by a creature directly.
void MpDataStore::DBAddPlayerDeath(Player* player, Creature* creature, MpDifficulty difficulty) {
if (!player) {
MpLogger::error("DBAddPlayerDeath called with invalid player");
return;
}
CharacterDatabase.Execute("UPDATE mp_player_instance_data SET deaths = deaths + 1 WHERE guid = {} and mapId = {} and instanceId = {}",
player->GetGUID().GetCounter(),
player->GetMapId(),
player->GetInstanceId()
);
CharacterDatabase.Execute(
"INSERT INTO mp_player_death_stats (guid, creatureEntry, difficulty, numDeaths, lastUpdated) "
"VALUES ({}, {}, {}, 1, CURRENT_TIMESTAMP) "
"ON DUPLICATE KEY UPDATE numDeaths = numDeaths + 1, lastUpdated = CURRENT_TIMESTAMP",
player->GetGUID().GetCounter(),
creature->GetEntry(),
difficulty
);
}
void MpDataStore::DBUpdateGroupData(ObjectGuid groupGuid, MpDifficulty difficulty, uint32 mapId, uint32 instanceId, uint32 deaths) {
if (!groupGuid) {
MpLogger::error("DBUpdateGroupData called with invalid groupGuid");
return;
}
CharacterDatabase.Execute("REPLACE INTO mp_group_data (groupId, difficulty, mapId, instanceId, deaths) VALUES ({},{},{},{},{}) ",
groupGuid.GetCounter(),
difficulty,
mapId,
instanceId,
deaths
);
}
void MpDataStore::DBAddGroupDeath(Group* group, uint32 mapId, uint32 instanceId, MpDifficulty difficulty) {
if (!group) {
MpLogger::error("DBAddGroupDeath called with invalid group");
return;
}
if(!difficulty) {
MpLogger::error("DBAddGroupDeath called with invalid difficulty");
return;
}
if(!mapId || !instanceId) {
MpLogger::error("DBAddGroupDeath called with invalid mapId or instanceId");
return;
}
CharacterDatabase.Execute("UPDATE mp_group_data SET deaths = deaths + 1 WHERE groupId = {} and mapId = {} and instanceId = {} and difficulty = {}",
group->GetGUID().GetCounter(),
mapId,
instanceId,
difficulty
);
}
void MpDataStore::DBRemovePlayerData(ObjectGuid playerGuid) {
if (!playerGuid) {
MpLogger::error("DBRemovePlayerData called with invalid playerGuid");
return;
}
CharacterDatabase.Execute("DELETE FROM mp_player_instance_data WHERE guid = {} ", playerGuid.GetCounter());
}
void MpDataStore::DBRemovePlayerInstanceData(uint32 instanceId) {
if (!instanceId) {
MpLogger::error("DBRemovePlayerInstanceData: missing instanceId to remove player instance ");
return;
}
CharacterDatabase.Execute("DELETE FROM mp_player_instance_data WHERE instanceId = {} ", instanceId);
}
void MpDataStore::DBUpdateGroupTimerDeaths(ObjectGuid groupGuid, uint32 mapId, uint32 instanceId, uint32 timer, uint32 deaths) {
if (!groupGuid) {
MpLogger::error("DBUpdateGroupTimerDeaths called with invalid groupGuid");
return;
}
CharacterDatabase.Execute("REPLACE INTO mp_group_data (groupId, mapId, instanceId, instanceTimer, deaths) VALUES ({},{},{},{},{}) ",
groupGuid.GetCounter(),
mapId,
instanceId,
timer,
deaths
);
}
void MpDataStore::DBRemoveGroupData(ObjectGuid groupGuid) {
if (!groupGuid) {
MpLogger::error("DBRemoveGroupData called with invalid groupGuid");
return;
}
CharacterDatabase.Execute("DELETE FROM mp_group_data WHERE groupId = {} ", groupGuid.GetCounter());
}
// Remove instance data using the instanceId
void MpDataStore::DBRemoveGroupInstanceData(uint32 instanceId) {
if (!instanceId) {
MpLogger::error("DBRemoveGroupData called with invalid groupGuid");
return;
}
CharacterDatabase.Execute("DELETE FROM mp_group_data WHERE instanceId = {} ", instanceId);
}

View File

@@ -2,9 +2,11 @@
#define MYTHICPLUS_DATASTORE_H
#include "Creature.h"
// #include "CreatureOverride.h"
#include "Group.h"
#include "MapMgr.h"
#include "Player.h"
#include "MpLogger.h"
#include "ObjectGuid.h"
#include <unordered_map>
@@ -13,7 +15,8 @@
#include <vector>
#include <memory>
enum MpDifficulty {
enum MpDifficulty
{
MP_DIFFICULTY_NORMAL = 0,
MP_DIFFICULTY_HEROIC = 1,
MP_DIFFICULTY_EPIC = 2,
@@ -21,22 +24,118 @@ enum MpDifficulty {
MP_DIFFICULTY_LEGENDARY = 4,
MP_DIFFICULTY_ASCENDANT = 5
};
class MpDataStore;
struct MpPlayerInstanceData
{
uint32 deaths = 0;
};
// struct used to track player performance in an instance.
struct MpPlayerData
{
Player* player;
MpDifficulty difficulty;
uint32 groupId;
// list of maps and instance player is bound to and mythic data related to it
std::map<std::pair<uint32,uint32>,MpPlayerInstanceData> instanceData;
MpPlayerData(Player* p, MpDifficulty diff, uint32_t groupId)
: player(p), difficulty(diff), groupId(groupId) {
}
void AddDeath(uint32 mapId, uint32 instanceId) {
auto key = std::make_pair(mapId, instanceId);
if(instanceData.contains(key)) {
instanceData[key].deaths++;
} else {
instanceData[key] = MpPlayerInstanceData{.deaths = 1};
}
MpLogger::info("========= Player {} added death to instance data {}", player->GetName(), instanceData[key].deaths);
}
uint32 GetDeaths(uint32 mapId, uint32 instanceId) const {
auto key = std::make_pair(mapId, instanceId);
if(instanceData.contains(key)) {
return instanceData.at(key).deaths;
}
return 0;
}
void ResetDeathCount(uint32 mapId, uint32 instanceId) {
auto key = std::make_pair(mapId, instanceId);
if(instanceData.contains(key)) {
instanceData[key].deaths = 0;
}
}
void ResetAllDeathCounts() {
for(auto& [key, data] : instanceData) {
data.deaths = 0;
}
}
};
// Struct used to track entire group performance.
struct MpGroupData
{
Group* group;
MpDifficulty difficulty;
uint32 deaths;
std::vector<MpPlayerData*> players;
MpGroupData() : group(nullptr), difficulty(MpDifficulty{}) {
players.reserve(5);
std::vector<std::pair<uint32,uint32>> instanceDataKeys;
MpGroupData() : group(nullptr), difficulty(MpDifficulty{}), deaths(0) {
instanceDataKeys.reserve(32);
}
MpGroupData(Group* group, MpDifficulty difficulty, uint32 deaths)
: group(group), difficulty(difficulty), deaths(deaths) {
instanceDataKeys.reserve(32);
MpGroupData(Group* group, MpDifficulty difficulty)
: group(group), difficulty(difficulty) {
players.reserve(5);
}
uint32 GetDeaths(uint32 mapId, uint32 instanceId) const {
uint32 deaths = 0;
for (const MpPlayerData* player : players) {
deaths += player->GetDeaths(mapId, instanceId);
}
return deaths;
}
void ResetGroupDeaths(uint32 mapId, uint32 instanceId) {
for (MpPlayerData* player : players) {
player->ResetDeathCount(mapId, instanceId);
}
}
void AddPlayerData(MpPlayerData* playerData) {
// Check if the playerData is already in the vector
if (std::any_of(players.begin(), players.end(), [playerData](MpPlayerData* existingData) {
return existingData->player == playerData->player;
})) {
MpLogger::warn("PlayerData for player {} is already in the players vector", playerData->player->GetName());
return;
}
// Add playerData to the vector
players.push_back(playerData);
}
std::string ToString() const {
Map* map = group->GetLeader()->GetMap();
uint32 deaths = 0;
if(map) {
deaths = GetDeaths(map->GetId(), map->GetInstanceId());
}
return "MpGroupData: { group: " + std::to_string(group->GetGUID().GetCounter()) +
", difficulty: " + std::to_string(difficulty) +
", deaths: " + std::to_string(deaths) + " }";
@@ -44,23 +143,17 @@ struct MpGroupData
};
struct MpPlayerData
{
Player* player;
uint8 difficulty;
uint32 deaths;
};
struct MpScaleFactor
{
int32 dmgBonus;
int32 healthBonus;
int32 spellBonus;
int32 maxDamageBonus;
std::string ToString() const {
return "MpScaleFactor: { dmgBonus: " + std::to_string(dmgBonus) +
", healthBonus: " + std::to_string(healthBonus) +
", maxDamageBonus: " + std::to_string(maxDamageBonus) + " }";
", spellBonus: " + std::to_string(spellBonus) + "}";
}
};
@@ -110,8 +203,6 @@ struct MpInstanceData
};
/**
* Simple struct for managing information about creatures that
* are in a mythic+ instance.
@@ -121,9 +212,17 @@ struct MpCreatureData
Creature* creature;
bool scaled;
// AttackPower calculated based on settings
uint32 NewAttackPower;
// New Scaling Multiplier based on database factors + level growth formula
float AttackPowerScaleMultiplier;
// Original information about the creature that was altered.
uint8 originalLevel;
CreatureBaseStats const* originalStats;
MpDifficulty difficulty;
// Custom difficulty modifiers to creatures at higher difficulties.
std::vector<uint32> auras;
@@ -148,30 +247,69 @@ struct MpCreatureData
this->scaled = scaled;
}
void SetDifficulty(MpDifficulty difficulty) {
this->difficulty = difficulty;
}
bool IsScaled() {
return scaled;
}
std::string ToString() const {
std::string origStatsStr;
if(originalStats) {
uint32 health = *originalStats->BaseHealth;
uint32 mana = originalStats->BaseMana;
uint32 armor = originalStats->BaseArmor;
uint32 ap = originalStats->AttackPower;
origStatsStr = "Original Stats: \n Health: " + std::to_string(health) + "\n" +
"Mana: " + std::to_string(mana) + "\n" +
"Armor: " + std::to_string(armor) + "\n" +
"Attack Power: " + std::to_string(ap) + "\n";
} else {
origStatsStr = "Original Stats Display Failed: \n Did you select target or in an non-scaled instance? \n";
}
return " MpCreatureData: \n Original level: " + std::to_string(originalLevel) + "\n" +
origStatsStr +
" NewAttackPower: " + std::to_string(NewAttackPower) + "\n" +
" AttackPowerScaleMultiplier: " + std::to_string(AttackPowerScaleMultiplier) + "\n" +
" Difficulty: " + std::to_string(difficulty) + "\n" +
" Scaled: " + (scaled ? "true" : "false") + "\n";
}
/**@todo Add Affixes and Aura Spell methods */
};
class MpDataStore {
class MpDataStore
{
private:
MpDataStore()
: _playerData(std::make_unique<std::unordered_map<ObjectGuid, MpPlayerData>>()),
: _playerData(std::make_unique<std::unordered_map<ObjectGuid, MpPlayerData*>>()),
_instanceData(std::make_unique<std::map<std::pair<uint32, uint32>, MpInstanceData>>()),
_groupData(std::make_unique<std::unordered_map<ObjectGuid, MpGroupData>>()),
_instanceCreatureData(std::make_unique<std::unordered_map<ObjectGuid, MpCreatureData>>()),
_mutableScaleFactors(std::make_unique<std::map<std::pair<int32, int32>,MpScaleFactor>>())
_scaleFactors(std::make_unique<std::map<std::pair<int32, int32>,MpScaleFactor>>())
{
_playerData->reserve(32);
_groupData->reserve(32);
_instanceCreatureData->reserve(500);
};
inline ~MpDataStore() {}
inline ~MpDataStore() {
for (auto& [guid, playerData] : *_playerData) {
delete playerData;
}
std::unique_ptr<std::unordered_map<ObjectGuid, MpPlayerData>> _playerData;
_playerData->clear();
}
std::unique_ptr<std::unordered_map<ObjectGuid, MpPlayerData*>> _playerData;
// Instance data containes information about how to scale creatures
std::unique_ptr<std::map<std::pair<uint32,uint32>,MpInstanceData>> _instanceData; // {mapId,instanceId}
@@ -183,9 +321,10 @@ private:
std::unique_ptr<std::unordered_map<ObjectGuid, MpCreatureData>> _instanceCreatureData;
// use to mimic pattern normals scale to heroic (loaded at server start)
std::unique_ptr<std::map<std::pair<int32,int32>,MpScaleFactor>> _mutableScaleFactors; // {mapId,difficulty}
std::unique_ptr<const std::map<std::pair<int32,int32>,MpScaleFactor>> _scaleFactors; // {mapId,difficulty}
std::unique_ptr<std::map<std::pair<int32,int32>,MpScaleFactor>> _scaleFactors; // {mapId,difficulty}
// Single creature multipliers used to scale creatures individually that may need tuned up or down.
// std::unique_ptr<std::unordered_map<uint32, CreatureOverride>> _creatureOverrides;
public:
@@ -193,15 +332,15 @@ public:
MpDataStore(const MpDataStore&) = delete;
MpDataStore& operator=(const MpDataStore&) = delete;
const MpPlayerData* GetPlayerData(ObjectGuid guid) const {
MpPlayerData* GetPlayerData(ObjectGuid guid) {
try {
return &_playerData->at(guid);
return _playerData->at(guid);
} catch (const std::out_of_range& oor) {
return nullptr;
}
}
const MpGroupData* GetGroupData(ObjectGuid guid) const {
MpGroupData* GetGroupData(ObjectGuid guid) {
if (_groupData->contains(guid)) {
return &_groupData->at(guid);
@@ -210,7 +349,7 @@ public:
}
}
const MpGroupData* GetGroupData(Player *player) const {
MpGroupData* GetGroupData(Player *player) {
return GetGroupData(player->GetGroup()->GetGUID());
};
@@ -220,8 +359,10 @@ public:
MpGroupData* GetGroupData(Group *group);
void PushGroupInstanceKey(Group *group, uint32 mapId, uint32 instanceId);
void AddPlayerData(ObjectGuid guid, MpPlayerData pd);
// Data related to player settings and stags
void AddPlayerData(ObjectGuid guid, MpPlayerData* pd);
void RemovePlayerData(ObjectGuid guid);
void ResetPlayerData(ObjectGuid guid);
// Each Map/Instance is a unique key that contains scaling information based on difficulty
void AddInstanceData(uint32 mapId, uint32 instanceId, MpInstanceData );
@@ -232,14 +373,24 @@ public:
void AddCreatureData(ObjectGuid guid, MpCreatureData creatureData);
MpCreatureData* GetCreatureData(ObjectGuid guid);
void RemoveCreatureData(ObjectGuid guid);
std::vector<MpCreatureData*> GetInstanceCreatures(uint32 mapId, uint32 instanceId);
std::vector<MpCreatureData*> GetUnscaledCreatures(uint32 mapId, uint32 instanceId);
// Scale factors are used to determine a base bonus for enemies base on the instance difficulty
int32 GetHealthScaleFactor(int32 mapId, int32 difficulty) const;
int32 GetDamageScaleFactor(int32 mapId, int32 difficulty) const;
int32 GetMaxDamageScaleFactor(int32 mapId, int32 difficulty) const;
int32 GetSpellScaleFactor(int32 mapId, int32 difficulty) const;
MpScaleFactor GetScaleFactor(int32 mapId, int32 difficulty) const;
void SetDamageScaleFactor(int32 mapId, int32 difficulty, int32 value);
void SetHealthScaleFactor(int32 mapId, int32 difficulty, int32 value);
void SetSpellScaleFactor(int32 mapId, int32 difficulty, int32 value);
// Individual Creature Scaling Multipliers
// void AddCreatureOverride(uint32 entry, CreatureOverride* override);
// MpMultipliers* GetCreatureOverride(uint32 entry);
auto GetInstanceDataKey(uint32 mapId, uint32 instanceId) const {
return std::make_pair(mapId, instanceId);
}
@@ -250,6 +401,23 @@ public:
// Used at initial server load
int32 LoadScaleFactors();
// Database API calls
void DBUpdatePlayerInstanceData(ObjectGuid playerGuid, MpDifficulty difficulty, uint32 mapId = 0, uint32 instanceId = 0, uint32 deaths = 0);
void DBResetPlayerDeaths(Player* player);
void DBAddPlayerDeath(Player* player, Creature* killer, MpDifficulty difficulty);
void DBAddPlayerDeath(Player* player);
void DBRemovePlayerData(ObjectGuid playerGuid);
void DBRemovePlayerInstanceData(uint32 instanceId);
void DBRemoveGroupInstanceData(uint32 instanceId);
void DBUpdateGroupData(ObjectGuid groupGuid, MpDifficulty difficulty, uint32 mapId, uint32 instanceId, uint32 deaths);
void DBUpdateGroupTimerDeaths(ObjectGuid groupGuid, uint32 mapId, uint32 instanceId, uint32 timer, uint32 deaths);
void DBRemoveGroupData(ObjectGuid groupGuid);
void DBAddGroupDeath(Group* group, uint32 mapId, uint32 instanceId, MpDifficulty difficulty);
//SavePlayerDungeonStats(Group* group, MpGroupData const* groupData);
static MpDataStore* instance() {
static MpDataStore instance;
return &instance;

61
src/MpScheduler.h Normal file
View File

@@ -0,0 +1,61 @@
#ifndef MYTHICPLUS_SCHEDULER_H
#define MYTHICPLUS_SCHEDULER_H
#include "MpLogger.h"
#include "ScriptMgr.h"
#include "TaskScheduler.h"
#include <chrono>
enum MP_SCHEDULE_GROUP
{
MP_WORLD_TASK_GROUP = 100
};
/**
* Global scheduler specifically for handling events fired inside of mythic
* scripts
*
* Example usage:
* void ScheduleWorldTask(std::chrono::nanoseconds const& time, std::function<void (TaskContext)> task) {
* sMpScheduler.Schedule(time, MP_WORLD_TASK_GROUP, [](TaskContext ctx) {
* // do the things you want for Mp Task Group
* return;
* });
* }
*/
class MpScheduler
{
public:
static MpScheduler* instance () {
static MpScheduler instance;
return &instance;
}
MpScheduler(const MpScheduler&) = delete;
MpScheduler& operator=(const MpScheduler&) = delete;
TaskScheduler& GetWorldScheduler() {
return _worldScheduler;
}
private:
MpScheduler() {}
~MpScheduler() {}
TaskScheduler _worldScheduler;
};
#define sMpScheduler MpScheduler::instance()
#endif // MYTHICPLUS_SCHEDULER_H
// Attach the world scheduler to listen to world events
class MpScheduler_WorldScript : public WorldScript
{
public:
MpScheduler_WorldScript() : WorldScript("MpScheduler_GlobalScript") { }
void OnUpdate(uint32 diff) override {
sMpScheduler->GetWorldScheduler().Update(diff);
}
};

39
src/MpScriptAI.h Normal file
View File

@@ -0,0 +1,39 @@
#include "Creature.h"
#include "CreatureAI.h"
// #include "CreatureHooks.h"
#include "MpLogger.h"
#include "MythicPlus.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#ifdef _ELUNA_CREATURE_AI_H
#include "ElunaCreatureAI.h"
using BaseAI = ElunaCreatureAI;
#else
using BaseAI = ScriptedAI;
#endif
class MpScriptAI : public BaseAI
{
MpDifficulty _difficulty;
public:
MpScriptAI(Creature* creature, MpDifficulty difficulty) : BaseAI(creature) {
_difficulty = difficulty;
}
MpScriptAI(Creature* creature) : BaseAI(creature) {
_difficulty = MpDifficulty::MP_DIFFICULTY_MYTHIC;
}
void JustDied(Unit* killer) override {
// sCreatureHooks->JustDied(me->ToCreature(), killer);
BaseAI::JustDied(killer);
}
void Reset() override {
// sCreatureHooks->JustSpawned(me->ToCreature());
BaseAI::Reset();
}
};

View File

@@ -5,10 +5,16 @@
#include "ScriptMgr.h"
#include "Group.h"
#include "Unit.h"
#include "WorldPacket.h"
#include "UpdateMask.h"
#include "MpScriptAI.h"
#include <algorithm>
#include <cmath>
// Special case for Headless Horseman Event
const uint32 HEADLESS_HORSEMAN = 23682;
bool MythicPlus::IsMapEligible(Map* map)
{
if (!Enabled) {
@@ -62,12 +68,15 @@ bool MythicPlus::EligibleHealTarget(Unit* target)
return false;
}
if ((target->IsPet() || target->IsSummon() || target->IsHunterPet()) && target->GetOwner()->IsNPCBot()) {
// Null check for GetOwner to avoid dereferencing a null pointer
if ((target->IsPet() || target->IsSummon() || target->IsHunterPet()) && target->GetOwner() && target->GetOwner()->IsNPCBot()) {
return false;
}
#endif
if(sMythicPlus->IsCreatureEligible(target->ToCreature())) {
// Ensure target is a valid creature before checking eligibility
Creature* creatureTarget = target->ToCreature();
if (creatureTarget && sMythicPlus->IsCreatureEligible(creatureTarget)) {
return true;
}
@@ -81,7 +90,6 @@ bool MythicPlus::EligibleDamageTarget(Unit* target)
}
if (target->GetTypeId() == TYPEID_PLAYER) {
MpLogger::debug("Target {} is a player", target->GetName());
return true;
}
@@ -90,13 +98,13 @@ bool MythicPlus::EligibleDamageTarget(Unit* target)
return true;
}
if ((target->IsPet() || target->IsSummon() || target->IsHunterPet()) && target->GetOwner()->IsNPCBot()) {
if ((target->IsPet() || target->IsSummon() || target->IsHunterPet()) && target->GetOwner() && target->GetOwner()->IsNPCBot()) {
return true;
}
#endif
Creature* creature = target->ToCreature();
if((creature->IsPet() || creature->IsSummon() || creature->IsHunterPet()) && creature->IsControlledByPlayer()) {
if (creature && (creature->IsPet() || creature->IsSummon() || creature->IsHunterPet()) && creature->GetOwner() && creature->IsControlledByPlayer()) {
return true;
}
@@ -105,40 +113,51 @@ bool MythicPlus::EligibleDamageTarget(Unit* target)
bool MythicPlus::IsCreatureEligible(Creature* creature)
{
if(!creature) {
if (!creature) {
return false;
}
std::string scriptName = creature->GetScriptName();
if(scriptName.starts_with("boss_")) {
return true;
}
if (creature->IsDungeonBoss()) {
return true;
}
if (creature->GetEntry() == HEADLESS_HORSEMAN) {
return true;
}
// Check if the creature is a pet or summon controlled by a player
if ((creature->IsHunterPet() || creature->IsPet() || creature->IsSummon()) && creature->IsControlledByPlayer()) {
return false;
}
// Skip critters, totems, and triggers
if (creature->IsCritter() || creature->IsTotem() || creature->IsTrigger()) {
return false;
}
# if defined(MOD_PRESENT_NPCBOTS)
#if defined(MOD_PRESENT_NPCBOTS)
// Safely check if the creature is an NPC Bot
if (creature->IsNPCBot()) {
MpLogger::debug("Creature {} is an NPC Bot do not scale", creature->GetName());
MpLogger::debug("Creature {} is an NPC Bot, do not scale", creature->GetName());
return false;
}
# endif
#endif
// throw out NPCs
if ((creature->IsVendor() ||
creature->HasNpcFlag(UNIT_NPC_FLAG_GOSSIP) ||
creature->HasNpcFlag(UNIT_NPC_FLAG_QUESTGIVER) ||
creature->HasNpcFlag(UNIT_NPC_FLAG_TRAINER) ||
creature->HasNpcFlag(UNIT_NPC_FLAG_TRAINER_PROFESSION) ||
creature->HasNpcFlag(UNIT_NPC_FLAG_REPAIR) ||
creature->HasUnitFlag(UNIT_FLAG_IMMUNE_TO_PC) ||
creature->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) &&
(!creature->IsDungeonBoss())
)
// Check for NPC-related flags (vendor, gossip, quest giver, trainer, etc.)
if ((creature->IsVendor() ||
creature->HasNpcFlag(UNIT_NPC_FLAG_GOSSIP) ||
creature->HasNpcFlag(UNIT_NPC_FLAG_QUESTGIVER) ||
creature->HasNpcFlag(UNIT_NPC_FLAG_TRAINER) ||
creature->HasNpcFlag(UNIT_NPC_FLAG_TRAINER_PROFESSION) ||
creature->HasNpcFlag(UNIT_NPC_FLAG_REPAIR) ||
creature->HasUnitFlag(UNIT_FLAG_IMMUNE_TO_PC) ||
creature->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) &&
(!creature->IsDungeonBoss()))
{
return false;
}
@@ -153,25 +172,49 @@ void MythicPlus::AddCreatureForScaling(Creature* creature)
}
sMpDataStore->AddCreatureData(creature->GetGUID(), MpCreatureData(creature));
MpLogger::debug("Added creature {} to instance data for instance {}",
creature->GetName(),
creature->GetMap()->GetMapName()
);
// MpLogger::debug("Added creature {} to instance data for instance {}",
// creature->GetName(),
// creature->GetMap()->GetMapName()
// );
}
void MythicPlus::AddScaledCreature(Creature* creature, MpInstanceData* instanceData)
{
MpCreatureData creatureData = MpCreatureData(creature);
sMpDataStore->AddCreatureData(creature->GetGUID(), creatureData);
// allow small variance in level for non-boss creatures
uint8 level = uint8(urand(instanceData->creature.avgLevel - 1, instanceData->creature.avgLevel + 1));
if(creature->IsDungeonBoss()) {
if(creature->IsDungeonBoss() || creature->GetEntry() == 23682) {
ScaleCreature(instanceData->boss.avgLevel, creature, &instanceData->boss, instanceData->difficulty);
} else {
ScaleCreature(level, creature, &instanceData->creature, instanceData->difficulty);
}
MpCreatureData creatureData = MpCreatureData(creature);
// Update AI now the creature has been scaled.
// auto ai = new MpScriptAI(creature, instanceData->difficulty);
// creature->SetAI(ai);
// We know the creature is scaled and in the instance to fire the event.
// sCreatureHooks->AddToInstance(creature);
std::string name = creature->GetName();
// Assign random affix for now.
if (roll_chance_i(50)) {
uint32 irand = urand(0, 2);
if(irand == 0) {
creature->AddAura(23341, creature);
} else if(irand == 1) {
creature->AddAura(34711, creature);
} else {
creature->AddAura(774, creature);
}
}
creatureData.SetScaled(true);
sMpDataStore->AddCreatureData(creature->GetGUID(), creatureData);
creatureData.SetDifficulty(instanceData->difficulty);
// MpLogger::debug("Scaled Creature {} Entry {} Id {} level from {} to {}",
// creature->GetName(),
@@ -190,6 +233,14 @@ void MythicPlus::ScaleRemaining(Player* player, MpInstanceData* instanceData)
}
}
void MythicPlus::ScaleAll(Player* player, MpInstanceData* instanceData)
{
std::vector<MpCreatureData*> creatures = sMpDataStore->GetInstanceCreatures(player->GetMapId(), player->GetInstanceId());
for (MpCreatureData* creatureData : creatures) {
ScaleCreature(creatureData->creature->GetLevel(), creatureData->creature, &instanceData->creature, instanceData->difficulty);
}
}
// Perform any memory cleanup when the creature is removed from the world and no longer needed.
void MythicPlus::RemoveCreature(Creature* creature)
{
@@ -214,13 +265,7 @@ void MythicPlus::ScaleCreature(uint8 level, Creature* creature, MpMultipliers* m
);
uint32 basehp = stats->BaseHealth[EXPANSION_WRATH_OF_THE_LICH_KING];
uint32 health = CalculateNewHealth(cInfo, mapId, difficulty, basehp, multipliers->health);
MpLogger::debug("Creature {} base health scaled from {} to {}",
creature->GetName(),
basehp,
health
);
uint32 health = CalculateNewHealth(creature, cInfo, mapId, difficulty, basehp, multipliers->health);
creature->SetCreateHealth(health);
creature->SetMaxHealth(health);
@@ -244,94 +289,158 @@ void MythicPlus::ScaleCreature(uint8 level, Creature* creature, MpMultipliers* m
creature->SetModifierValue(UNIT_MOD_MANA, BASE_VALUE, (float)mana * 3.0f);
}
// need to calcuate a new base weapon damage that makes sense for the level and class
uint32 ap = stats->AttackPower;
uint32 rangeAp = irand(115, 157);
MpLogger::debug("Creature {} base attack power{}",
creature->GetName(),
ap
);
MpInstanceData *instanceData = sMpDataStore->GetInstanceData(creature->GetMapId(), creature->GetInstanceId());
int32 meleeDamage = sMpDataStore->GetDamageScaleFactor(creature->GetMapId(), instanceData->difficulty);
if(creature->IsDungeonBoss() || creature->GetEntry() == 23682) {
// Give the boss an increase in casting speed.
creature->SetFloatValue(UNIT_MOD_CAST_SPEED, 1.20f);
}
ap = pow(float(creature->GetLevel() / origLevel), 1.8f) * 1000;
// Calculate the level difference
float levelDifference = creature->GetLevel() - origLevel;
// New formula with adjusted divisor for smoother scaling
float scalingFactor;
uint32 ap = uint32(sMythicPlus->meleeAttackPowerStart - sMythicPlus->meleeAttackPowerDampener);
uint32 rangeAp = irand(215, 357);
scalingFactor = CalculateScaling(levelDifference, meleeDamage);
ap = uint32(stats->AttackPower * scalingFactor);
rangeAp = uint32(rangeAp * scalingFactor);
MpCreatureData* creatureData = sMpDataStore->GetCreatureData(creature->GetGUID());
if(creatureData) {
creatureData->NewAttackPower = ap;
creatureData->AttackPowerScaleMultiplier = scalingFactor;
}
// Set scaled attack power
creature->SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, ap);
creature->SetModifierValue(UNIT_MOD_ATTACK_POWER_RANGED, BASE_VALUE, rangeAp);
// This works out a bonus damage to apply to the mob using the database and original mod settings.
// the thought behind this is some mobs in dungeons are intended to hit harder than others
// so applying a flat bonus keeps the ratios the same but increases the overall difficulty.
// Of course within reason.
int32 damageBonus = sMpDataStore->GetDamageScaleFactor(mapId, difficulty);
int32 maxBonus = sMpDataStore->GetMaxDamageScaleFactor(mapId, difficulty);
float dmgMod = cInfo->DamageModifier + damageBonus;
// Allow bosses to scale as high as they want but limit non-bosses to a max bonus
if(!creature->IsDungeonBoss() && damageBonus > maxBonus) {
dmgMod = maxBonus;
}
float oldDmgModifier = creature->GetModifierValue(UNIT_MOD_DAMAGE_MAINHAND, BASE_VALUE);
creature->SetModifierValue(UNIT_MOD_DAMAGE_MAINHAND,BASE_VALUE, dmgMod);
creature->SetModifierValue(UNIT_MOD_DAMAGE_OFFHAND,BASE_VALUE, dmgMod*0.85f);
creature->SetModifierValue(UNIT_MOD_DAMAGE_RANGED,BASE_VALUE, dmgMod);
MpLogger::debug("Creature new attack damage scaled from {} to {}",
oldDmgModifier,
dmgMod
);
// set the base weapon damage
creature->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, stats->BaseDamage[EXPANSION_WRATH_OF_THE_LICH_KING], 0);
creature->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, stats->BaseDamage[EXPANSION_WRATH_OF_THE_LICH_KING], 0);
// Update all stats to apply the new damage values
creature->UpdateAllStats();
// Scale up the armor with some variance also to make some tougher enemies in the mix
uint32 armor = uint32(std::ceil(stats->BaseArmor * multipliers->armor * cInfo->ModArmor));
creature->SetArmor(armor);
/**
* @TODO: Explore scaling other variable stats and resistances on the creature type at a later date.
*/
// creature->SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, stats->AttackPower * multipliers->melee);
// creature->SetModifierValue(UNIT_MOD_ATTACK_POWER_RANGED, BASE_VALUE, stats->RangedAttackPower * multipliers->melee);
}
int32 MythicPlus::ScaleDamageSpell(SpellInfo const * spellInfo, MpCreatureData* creatureData, Creature* creature, float damageMultiplier)
int32 MythicPlus::ScaleDamageSpell(SpellInfo const * spellInfo, uint32 damage, MpCreatureData* creatureData, Creature* creature, Unit* target, float damageMultiplier)
{
if (!spellInfo) {
MpLogger::error("Invalid spell info ScaleDamageSpell()");
return 0;
return damage;
}
if(!creatureData) {
// this is probably a summoned object or totem so going to cheat here and just use the flat modifer
// Handle totems that do some nasty things to us Slave Pens anyone
if(creature->IsTotem()) {
Unit* owner = creature->GetOwner();
if(owner) {
float lvlDmgBonus = float(85 - owner->GetLevel() / 10.0f);
return int32(damage * lvlDmgBonus * damageMultiplier);
} else {
return damage * damageMultiplier;
}
}
MpLogger::error("Invalid creature data ScaleDamageSpell()");
return 0;
return damage * damageMultiplier;
}
if(!creature) {
MpLogger::error("Invalid creature ScaleDamageSpell()");
return 0;
return damage * damageMultiplier;
}
int32 originalLevel = creatureData->originalLevel;
MpInstanceData *instanceData = sMpDataStore->GetInstanceData(creature->GetMapId(), creature->GetInstanceId());
int32 spellBonus = sMpDataStore->GetSpellScaleFactor(creature->GetMapId(), instanceData->difficulty);
// if((creature->IsDungeonBoss() && creature->isElite()) || creature->GetEntry() == 23682) {
// spellBonus *= 1.15;
// }
// Calculate the level difference
float levelDifference = creature->GetLevel() - originalLevel;
// New formula with adjusted divisor for smoother scaling
// float scalingFactor = 1 + (std::log2(levelDifference + 1) * (float(spellBonus)));
float scalingFactor = CalculateScaling(levelDifference, spellBonus);
// float scalingFactor = pow(float((creature->GetLevel() - originalLevel) / 10.0f ), float(spellBonus) / 5.0f);
// MpLogger::debug("Creature {} original level: {} New Level{} and Scaling level {}", creature->GetName(), originalLevel, creature->GetLevel(), scalingFactor);
int32 totalDamage = 0;
// Calculate the scaling factor using the 1.8 exponent
float scalingFactor = pow(float(creature->GetLevel() / originalLevel), 1.8f);
auto effects = spellInfo->GetEffects();
// Loop through all spell effects to scale their base damage
for (uint8 i = 0; i < effects.size(); ++i)
{
SpellEffectInfo effect = effects[i];
totalDamage += effect.CalcValue(creature, nullptr,nullptr);
if(effect.IsAura()) {
switch(spellInfo->Effects[i].ApplyAuraName) {
case SPELL_AURA_PERIODIC_DAMAGE:
case SPELL_AURA_PERIODIC_DAMAGE_PERCENT:
case SPELL_AURA_POWER_BURN:
case SPELL_AURA_PERIODIC_LEECH:
case SPELL_AURA_PERIODIC_TRIGGER_SPELL:
case SPELL_AURA_PERIODIC_TRIGGER_SPELL_WITH_VALUE:
case SPELL_AURA_PERIODIC_DUMMY:
case SPELL_AURA_DUMMY:
totalDamage += effect.CalcValue(creature, nullptr, target);
break;
default:
break;
}
} else {
switch(effect.Effect)
{
case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE:
case SPELL_EFFECT_WEAPON_DAMAGE:
case SPELL_EFFECT_NORMALIZED_WEAPON_DMG:
return damage;
case SPELL_EFFECT_SCHOOL_DAMAGE:
case SPELL_EFFECT_ENVIRONMENTAL_DAMAGE:
case SPELL_EFFECT_POWER_BURN:
case SPELL_EFFECT_HEALTH_LEECH:
case SPELL_EFFECT_TRIGGER_SPELL:
case SPELL_EFFECT_TRIGGER_SPELL_WITH_VALUE:
case SPELL_EFFECT_DUMMY:
totalDamage += effect.CalcValue(creature, nullptr, target);
break;
default:
break;
}
}
}
if(totalDamage == 0) {
return damage;
}
MpLogger::debug(" >>> Spell {} damage scaled from for spell New Damage: {} using: scaling Factor: {} and damage Multi: {}",spellInfo->SpellName[0], totalDamage, scalingFactor, damageMultiplier);
// Apply scaling factor and the set multiplier from the instance data
totalDamage = int32(totalDamage * scalingFactor * damageMultiplier);
MpLogger::debug("Spell damage scaled from for spell New Damage: {}", totalDamage);
// MpLogger::debug("Spell {} damage scaled from for spell New Damage: {} using: scaling Factor: {} and damage Multi: {}",spellInfo->SpellName[0], totalDamage, scalingFactor, damageMultiplier);
return totalDamage;
}
int32 MythicPlus::ScaleHealSpell(SpellInfo const * spellInfo, MpCreatureData* creatureData, Creature* creature, Creature* target, float healMultiplier)
int32 MythicPlus::ScaleHealSpell(SpellInfo const * spellInfo, uint32 heal, MpCreatureData* creatureData, Creature* creature, Creature* /* target */, float healMultiplier)
{
if (!spellInfo) {
MpLogger::error("Invalid spell info ScaleHealSpell()");
return 0;
@@ -347,26 +456,161 @@ int32 MythicPlus::ScaleHealSpell(SpellInfo const * spellInfo, MpCreatureData* cr
return 0;
}
if(!target) {
MpLogger::error("Invalid target ScaleHealSpell()");
return 0;
}
int32 originalHp = creatureData->originalStats->BaseHealth[EXPANSION_WRATH_OF_THE_LICH_KING];
int32 currentHealth = creature->GetHealth();
int32 totalHeal = 0;
auto effects = spellInfo->GetEffects();
// Loop through all spell effects to scale their base healing
for (uint8 i = 0; i < effects.size(); ++i)
{
SpellEffectInfo effect = effects[i];
totalHeal += effect.CalcValue(creature, nullptr, target);
MpLogger::debug(" >>> Spell {} effect {} value: {} by creature {}", spellInfo->SpellName[i], effects[i].Effect, heal, creature->GetName());
}
// Apply scaling factor and the set multiplier from the instance data
MpLogger::debug("Spell healing scaled from for spell New Damage: {}", totalHeal);
return pow((totalHeal / originalHp) * currentHealth, 0.8f) * healMultiplier;
int32 originalLevel = creatureData->originalLevel;
float levelDifference = creature->GetLevel() - originalLevel;
float spellBonus = sMpDataStore->GetSpellScaleFactor(creature->GetMapId(), creature->GetInstanceId());
float scalingFactor = CalculateScaling(levelDifference, spellBonus, 2.5f);
MpLogger::debug(" >>> Spell {} healed scaled from for spell New Heal: {} using: scaling Factor: {} and damage Multi: {}",spellInfo->SpellName[0], heal, scalingFactor, healMultiplier);
return int32(heal * scalingFactor * healMultiplier);
}
void MythicPlus::GroupReset(Group* /*group*/, Map* /* map */) {
// Stubbed out for later implementation
}
bool MythicPlus::IsFinalBoss(Creature* creature) {
std::array<uint32, 128> finalBosses = {
// --- WoW Classic Dungeons ---
11519, /* Bazzalan Ragefire */
639, /* Edwin VanCleef Deadmines */
3654, /* Mutanus the Devourer Wailing Caverns */
4275, /* Archmage Arugal Shadowfang Keep */
1716, /* Bazil Thredd Stockades */
4829, /* Aku'mai blackfathom Deeps */
7800, /* Mekgineer Thermaplugg Gnomeregan */
4421, /* Charlga Razorflank Razorfen Kraul */
4543, /* Bloodmage Thalnos Scarlet Monastery */
3975, /* Herod Scarlet Monastery */
3977, /* High Inquisitor Whitemane Scarlet Monastery */
7350, /* Amnennar the Coldbringer Razorfen Downs */
2748, /* Archaedas Uldaman */
7267, /* Chief Ukorz Sandscalp Zul'Farrak */
12201, /* Princess Theradras Maraudon */
5709, /* Shade of Eranikus Sunken Temple */
9019, /* Emperor Dagran Thaurissan Blackrock Depths */
9568, /* Overlord Wyrmthalak Lower Blackrock Spire */
10363, /* General Drakkisath Upper Blackrock Spire */
11492, /* alzzin the Wildshaper Dire Maul */
11496, /* Immol'thar Dire Maul */
11501, /* King Gordok Dire Maul */
1853, /* Darkmaster Gandling Scholomance */
10812, /* Grand Crusader Dathrohan Stratholme */
10440, /* Baron Rivendare Stratholme */
// --- WoW Classic Raids ---
11583, /* Nefarian Blackwing Lair */
11502, /* Ragnaros Molten Core */
14834, /* Hakkar Zul'Gurub */
15727, /* C'Thun Temple of Ahn'Qiraj */
15339, /* Ossirian the Unscarred Ruins of Ahn'Qiraj */
// --- The Burning Crusade ---
17536, /* Nazan - Hellfire Ramparts Normal */
17536, /* Nazan - Hellfire Ramparts Normal */
17377, /* Kelidan the Breaker - Blood Furnace Normal */
18607, /* Kelidan the Breaker - Blood Furnace Heroic */
17942, /* Quagmirran - The Slave Pens Normal */
19894, /* Quagmirran - The Slave Pens Heoric */
17882, /* The Black Stalker - The Underbog Normal */
20184, /* The Black Stalker - The Underbog Heroic */
24420, /* The Black Stalker - The Underbog Heroic(2) */
18344, /* Nexus-Prince Shaffar - Mana-Tombs Normal */
20256, /* Nexus-Prince Shaffar - Mana-Tombs Heroic */
18373, /* Exarch Maladaar - Auchenai Crypts Normal */
20306, /* Exarch Maladaar - Auchenai Crypts Heoric */
18096, /* Epoch Hunter - Old Hillsbrad Foothills Normal */
20531, /* Epoch Hunter - Old Hillsbrad Foothills Heroic */
18473, /* Talon King Ikiss - Sethekk Halls Normal */
20706, /* Talon King Ikiss - Sethekk Halls Heroic */
17798, /* Warlord Kalithresh - The Steamvault Normal */
20633, /* Warlord Kalithresh - The Steamvault Heroic */
18708, /* Murmur - Shadow Labyrinth Normal */
20657, /* Murmur - Shadow Labyrinth Heroic */
16808, /* Warchief Kargath Bladefist - The Shattered Halls Normal */
20597, /* Warchief Kargath Bladefist - The Shattered Halls Heroic */
17881, /* Aeonus - The Black Morass Normal */
20737, /* Aeonus - The Black Morass Heroic */
17977, /* Warp Splinter - The Botanica Normal */
21582, /* Warp Splinter - The Botanica Heroic */
19220, /* Pathaleon the Calculator - The Mechanar Normal */
21537, /* Pathaleon the Calculator - The Mechanar Heroic */
20912, /* Harbinger Skyriss - The Arcatraz Normal */
21601, /* Harbinger Skyriss - The Arcatraz Heroic */
19622, /* Kael'thas Sunstrider - Magisters' Terrace Normal */
24857, /* Kael'thas Sunstrider - Magisters' Terrace Heroic */
// --- Burning Crusade Raids ---
15690, /* Prince Malchezaar - Karazhan */
23863, /* Zul'jin - Zul'Aman */
19044, /* Gruul the Dragonkiller - Gruul's Lair */
17257, /* Magtheridon - Magtheridon's Lair */
21212, /* Lady Vashj - Serpentshrine Cavern */
24664, /* Kael'thas Sunstrider - The Eye */
24855, /* Kael'thas Sunstrider - The Eye */
17968, /* Archimonde - Hyjal Summit */
22917, /* Illidan Stormrage - Black Temple */
25315, /* Kil'jaeden - Sunwell Plateau */
// --- Wrath of the Lich King ---
23954, /* Ingvar the Plunderer - Utgarde Keep Normal */
31673, /* Ingvar the Plunderer - Utgarde Keep Heroic */
26861, /* King Ymiron - Utgarde Pinnacle Normal */
30788, /* King Ymiron - Utgarde Pinnacle Heroic */
26723, /* Keristrasza - The Nexus Normal */
30540, /* Keristrasza - The Nexus Heroic */
26632, /* The Prophet Tharon'ja - Drak'Tharon Keep Normal */
31360, /* The Prophet Tharon'ja - Drak'Tharon Keep Heroic */
27656, /* Ley-Guardian Eregos - The Oculus Normal */
31561, /* Ley-Guardian Eregos - The Oculus Heroic */
29311, /* Herald Volazj - Ahn'kahet: The Old Kingdom Normal */
31464, /* Herald Volazj - Ahn'kahet: The Old Kingdom Heroic */
29120, /* Anub'arak - Azjol-Nerub Normal */
31610, /* Anub'arak - Azjol-Nerub Heroic */
29306, /* Gal'darah - Gundrak Normal */
31368, /* Gal'darah - Gundrak Heroic */
26533, /* Mal'Ganis - Culling of Stratholme Normal */
31217, /* Mal'Ganis - Culling of Stratholme Heroic */
31134, /* Cyanigosa - Violet Hold Normal */
31506, /* Cyanigosa - Violet Hold Heroic */
27978, /* Sjonnir the Ironshaper - Halls of Stone Normal */
31386, /* Sjonnir the Ironshaper - Halls of Stone Heroic */
28923, /* Loken - Halls of Lightning Normal */
31538, /* Loken - Halls of Lightning Heroic */
35451, /* The Black Knight - Trial of the Champion Normal */
35490, /* The Black Knight - Trial of the Champion Heroic */
36502, /* Devourer of Souls - The Forge of Souls Normal */
37677, /* Devourer of Souls - The Forge of Souls Heroic */
36658, /* Scourgelord Tyrannus - Pit of Saron Normal */
36938, /* Scourgelord Tyrannus - Pit of Saron Heroic */
37226, /* The Lich King Encounter - Halls of Reflection Normal */
39166, /* The Lich King Encounter - Halls of Reflection Heroic */
// --- Wrath of the Lich King Raids ---
15990, /* Kel'Thuzad - Naxxramas */
28859, /* Malygos - The Eye of Eternity */
28860, /* Sartharion - Obsidian Sanctum */
31125, /* Archavon the Stone Watcher - Vault of Archavon */
33993, /* Emalon the Storm Watcher - Vault of Archavon */
35013, /* Koralon the Flame Watcher - Vault of Archavon */
38433, /* Toravon the Ice Watcher - Vault of Archavon */
33288, /* Yogg-Saron - Ulduar */
34564, /* Anub'arak - Trial of the Crusader */
10184, /* Onyxia - Onyxia's Lair (re-released in Wrath) */
36597, /* The Lich King - Icecrown Citadel */
39863, /* Halion - Ruby Sanctum */
};
return std::find(finalBosses.begin(), finalBosses.end(), creature->GetEntry()) != finalBosses.end();
}
/**
@@ -392,19 +636,38 @@ float GetTypeHealthModifier(int32 Rank)
}
// This takes the orignal health and scales flat based on the factor then applies the configuration modifier from the conf file
uint32 CalculateNewHealth(CreatureTemplate const* cInfo, uint32 mapId, MpDifficulty difficulty, uint32 origHealth, float confHPMod)
uint32 CalculateNewHealth(Creature* creature, CreatureTemplate const* cInfo, uint32 mapId, MpDifficulty difficulty, uint32 origHealth, float confHPMod)
{
int32 rank = 0;
if(cInfo && cInfo->rank > 0) {
rank = cInfo->rank;
}
float healthVariation;
// if(creature->IsPet() || creature->IsSummon() || creature->IsTotem()) {
// return origHealth;
// }
int32 hpScaleFactor = sMpDataStore->GetHealthScaleFactor(mapId, difficulty);
// Add some variance to the healthpool so enemies are not all the same
float healthVariation = frand(0.85f, 1.15f);
if(creature->IsDungeonBoss() || creature->GetEntry() == HEADLESS_HORSEMAN || creature->isWorldBoss()) { // Is a boss of some kind
healthVariation = frand(1.1f, 1.2f);
} else if(creature->isElite() || cInfo->rank == CREATURE_ELITE_RARE) { // Is Elite Mob
healthVariation = frand(1.0f, 1.10f);
hpScaleFactor *= 0.90;
} else if(creature->IsSummon() || creature->IsPet() || creature->IsTotem()) { // Is a pet or summon
healthVariation = frand(1.0f, 1.05f);
hpScaleFactor *= 0.65;
} else {
healthVariation = frand(1.0f, 1.1f);
hpScaleFactor *= 0.55;
}
float unitTypeMod = GetTypeHealthModifier(rank);
uint32 basehp = uint32(std::ceil(origHealth * unitTypeMod * healthVariation));
int32 hpScaleFactor = sMpDataStore->GetHealthScaleFactor(mapId, difficulty);
if(cInfo->ModHealth > 0.0f) {
return uint32(basehp * (cInfo->ModHealth + hpScaleFactor) * confHPMod);
} else {
@@ -412,6 +675,12 @@ uint32 CalculateNewHealth(CreatureTemplate const* cInfo, uint32 mapId, MpDifficu
}
}
// Calculates a logarithmic growth curve using scaling factor of percentages increase 50 = 1.5, 100 = 2.0,... this allows for fine grain tuning per instance.
float CalculateScaling(int levelDifference, float scaleFactor, float constant, float growthFactor) {
float scaling = constant * std::pow(2.0f, levelDifference / growthFactor) * (1 + (scaleFactor / 100.0f));
return scaling;
}
float GetTypeDamageModifier(int32 Rank)
{
switch (Rank)
@@ -430,3 +699,4 @@ float GetTypeDamageModifier(int32 Rank)
return sWorld->getRate(RATE_CREATURE_ELITE_ELITE_DAMAGE);
}
}

View File

@@ -8,12 +8,25 @@
#include "Player.h"
#include "SpellInfo.h"
#include "Unit.h"
#include "TaskScheduler.h"
#include <map>
#include <string>
#include <vector>
#include <unordered_map>
// Used to limit the total advancment rank and allow for changing the max rank in one place.
inline const uint8 MP_MAX_ADVANCEMENT_RANK = 50;
/**
* Main Class for the mod responsible for controls related to scaling instances,
* handling logic related to setting up instances for MythicPlus to work.
*
* MpDataStore is heavily used as well for storing data in memory and interactions with
* database storage.
*
* This is a singleton instance that can be accessed through sMythicPlus.
*/
class MythicPlus
{
public:
@@ -56,6 +69,10 @@ public:
uint32 legendaryItemOffset;
uint32 ascendantItemOffset;
// Scaling modifiers
uint32 meleeAttackPowerDampener;
uint32 meleeAttackPowerStart;
enum MP_UNIT_EVENT_TYPE
{
UNIT_EVENT_MELEE,
@@ -100,6 +117,9 @@ public:
*/
void ScaleRemaining(Player* player, MpInstanceData* instanceData);
// Rescales all creatures for an instance based on set data
void ScaleAll(Player* player, MpInstanceData* instanceData);
// This will attempt to scale a creature using instancedata
void AddScaledCreature(Creature* creature, MpInstanceData* instanceData);
@@ -107,10 +127,13 @@ public:
void ScaleCreature(uint8 level, Creature* creature, MpMultipliers* multipliers, MpDifficulty difficulty);
// Scales a damage spell up based on the level increase
int32 ScaleDamageSpell(SpellInfo const * spellInfo, MpCreatureData* creatureData, Creature* creature, float damageMultiplier);
int32 ScaleDamageSpell(SpellInfo const * spellInfo, uint32 damage, MpCreatureData* creatureData, Creature* creature, Unit* target, float damageMultiplier);
// This scales a heal spell up based on the how much % the original heal spell was
int32 ScaleHealSpell(SpellInfo const * spellInfo, MpCreatureData* creatureData, Creature* creature, Creature* target, float healMultiplier);
int32 ScaleHealSpell(SpellInfo const * spellInfo, uint32 heal, MpCreatureData* creatureData, Creature* creature, Creature* target, float healMultiplier);
static bool IsFinalBoss(Creature* creature);
static void GroupReset(Group* group, Map* map);
private:
MythicPlus() { }
@@ -119,7 +142,8 @@ public:
float GetTypeHealthModifier(int32 rank);
float GetTypeDamageModifier(int32 rank);
uint32 CalculateNewHealth(CreatureTemplate const* cInfo, uint32 mapId, MpDifficulty difficulty, uint32 origHealth, float confHPMod);
float CalculateScaling(int levelDifference, float scaleFactor, float constant = 1.25f, float growthFactor = 20.0f);
uint32 CalculateNewHealth(Creature* creature, CreatureTemplate const* cInfo, uint32 mapId, MpDifficulty difficulty, uint32 origHealth, float confHPMod);
float CalculateNewBaseDamage(CreatureTemplate const* cInfo, uint32 mapId, MpDifficulty difficulty, float origDamage);
#define sMythicPlus MythicPlus::instance()

View File

@@ -1,3 +1,16 @@
#include "MpScheduler.h"
#include "MpLogger.h"
// Creature Overrides
enum {
RAGEFIRE_BAZZALAN = 11519
};
// This adds schedulers for use across scripts scoped to MythicPlus
void Add_MP_Schedulers() {
MpLogger::debug("Add_MP_Schedulers()");
new MpScheduler_WorldScript();
}
void Addmod_mythic_plusScripts();
void Add_MP_AllCreatureScripts();
@@ -8,15 +21,23 @@ void Add_MP_GroupScripts();
void Add_MP_PlayerScripts();
void Add_MP_UnitScripts();
void Add_MP_WorldScripts();
void Add_MP_PlayerMessageEvents();
void Addmod_mythic_plusScripts()
{
void Addmod_mythic_plusScripts() {
Add_MP_AllCreatureScripts();
Add_MP_AllMapScripts();
Add_MP_CommandScripts();
Add_MP_GlobalScripts();
// Add_MP_GroupScripts();
// Add_MP_PlayerScripts();
Add_MP_PlayerScripts();
Add_MP_UnitScripts();
Add_MP_WorldScripts();
Add_MP_Schedulers();
Add_MP_PlayerMessageEvents();
// new Ragefire_Bazzalan_Mythic();
// list of boss / creature event handlers
// new Ragefire_Bazzalan_Mythic(RAGEFIRE_BAZZALAN);
}

View File

@@ -1,16 +0,0 @@
#include "MpLogger.h"
#include "Player.h"
#include "ScriptMgr.h"
class MythicPlus_PlayerScript : public PlayerScript
{
public:
MythicPlus_PlayerScript() : PlayerScript("MythicPlus_PlayerScript") { }
};
void Add_MP_PlayerScripts()
{
MpLogger::debug("Add_MP_PlayerScripts()");
new MythicPlus_PlayerScript();
}

View File

@@ -1,6 +1,8 @@
#include "CreatureAI.h"
#include "MpDataStore.h"
#include "MpLogger.h"
#include "MythicPlus.h"
#include "MpScriptAI.h"
#include "ScriptMgr.h"
class MythicPlus_AllCreatureScript : public AllCreatureScript

View File

@@ -14,7 +14,7 @@ public:
{
}
void OnCreateMap(Map* map) { }
void OnCreateMap(Map* /*map*/) { }
/**
* When a player enters the map check it needs to set up the instance data
@@ -101,6 +101,11 @@ public:
);
sMpDataStore->AddInstanceData(map->GetId(), map->GetInstanceId(), instanceData);
// Save the instance data for the user to the database
if (player) {
sMpDataStore->DBUpdatePlayerInstanceData(player->GetGUID(), groupData->difficulty, map->GetId(), map->GetInstanceId(), 0);
}
// Once we have instance data set we can scale the remaining characters in our instance
sMythicPlus->ScaleRemaining(player, &instanceData);
}
@@ -111,7 +116,13 @@ public:
if (!sMythicPlus->IsMapEligible(map)) {
return;
}
// Removes currenct GroupData Instance Data and removes from database storage
sMpDataStore->RemoveInstanceData(map->GetId(), map->GetInstanceId());
// remove group instance and group instance data from database during a reset
sMpDataStore->DBRemovePlayerInstanceData(map->GetInstanceId());
sMpDataStore->DBRemoveGroupInstanceData(map->GetInstanceId());
}
};

View File

@@ -0,0 +1,391 @@
#include "Chat.h"
#include "MpDataStore.h"
#include "MythicPlus.h"
#include "MpDataStore.h"
#include "MpLogger.h"
#include "Player.h"
#include "ScriptMgr.h"
using namespace Acore::ChatCommands;
// make sure this is the new way to do this, i think it's the old busted shit
class MythicPlus_CommandScript : public CommandScript
{
public:
MythicPlus_CommandScript() : CommandScript("MythicPlus_CommandScript")
{
}
ChatCommandTable GetCommands() const override
{
static ChatCommandTable commandTableMain =
{
{"", HandleHelp, SEC_PLAYER, Console::No},
{"status", HandleStatus, SEC_PLAYER, Console::No},
{"set", HandleSetDifficulty, SEC_PLAYER, Console::No},
{"disable", HandleDisable, SEC_ADMINISTRATOR, Console::Yes},
{"enable", HandleEnable, SEC_ADMINISTRATOR, Console::Yes},
{"rescale", HandleReScale, SEC_GAMEMASTER, Console::No},
{"rescale all", HandleReScaleAll, SEC_GAMEMASTER, Console::No},
{"change melee", HandleChangeMelee, SEC_ADMINISTRATOR, Console::Yes},
{"change spell", HandleChangeSpell, SEC_ADMINISTRATOR, Console::Yes},
{"change health", HandleChangeHealth, SEC_ADMINISTRATOR, Console::Yes}
};
static ChatCommandTable commandTable =
{
{"mp", commandTableMain},
{"mythicplus", commandTableMain},
{"mp debug", HandleDebug, SEC_PLAYER, Console::No},
{"mp reload", HandleReload, SEC_GAMEMASTER, Console::No}
};
return commandTable;
}
static bool HandleHelp(ChatHandler* handler, const std::vector<std::string>& /*args*/)
{
std::string helpText = "Mythic+ Commands:\n"
" .mp status - show current global settings of Mythic+ mod\n"
" .mp set [normal, heroic, mythic,legendary,ascendant] - Set Mythic+ difficulty in current beta only supports mythic.\n"
" .mp [enable,disable] - enable or disable this mod\n"
" .mp - Show this help message\n";
handler->PSendSysMessage(helpText);
return true;
}
static bool HandleReload(ChatHandler* handler)
{
sMpDataStore->LoadScaleFactors();
handler->PSendSysMessage("Mythic+ scale factors updated.");
return true;
}
static bool HandleDebug(ChatHandler* handler)
{
Creature* target = handler->getSelectedCreature();
if(!target) {
handler->PSendSysMessage("You must select a creature to debug.");
return true;
}
CreatureTemplate const* creatureTemplate = target->GetCreatureTemplate();
MpCreatureData* creatureData = sMpDataStore->GetCreatureData(target->GetGUID());
handler->PSendSysMessage(LANG_NPCINFO_LEVEL, target->GetLevel());
handler->PSendSysMessage(LANG_NPCINFO_HEALTH, target->GetCreateHealth(), target->GetMaxHealth(), target->GetHealth());
handler->PSendSysMessage("WeaponDmg Main {} - {}",
target->GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE),
target->GetWeaponDamageRange(BASE_ATTACK, MAXDAMAGE)
);
handler->PSendSysMessage("WeaponDmg Range {} - {}",
target->GetWeaponDamageRange(RANGED_ATTACK, MINDAMAGE),
target->GetWeaponDamageRange(RANGED_ATTACK, MAXDAMAGE)
);
handler->PSendSysMessage("WeaponDmg Offhand {} - {}",
target->GetWeaponDamageRange(OFF_ATTACK, MINDAMAGE),
target->GetWeaponDamageRange(OFF_ATTACK, MAXDAMAGE)
);
handler->PSendSysMessage("Attack Power Main {}", target->GetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE));
handler->PSendSysMessage("Attack Power Ranged {}", target->GetModifierValue(UNIT_MOD_ATTACK_POWER_RANGED, BASE_VALUE));
handler->PSendSysMessage("Armor {}", target->GetArmor());
handler->PSendSysMessage("Damage Modifier on template {}",creatureTemplate->DamageModifier);
if(creatureData) {
handler->PSendSysMessage("CreatureData: {}", creatureData->ToString());
}
return true;
}
// sets the difficluty for the group
static bool HandleSetDifficulty(ChatHandler* handler, const std::vector<std::string>& args)
{
Player* player = handler->GetSession()->GetPlayer();
Group* group = player->GetGroup();
if (!group) {
MpLogger::debug("HandleSetMythic() No Group for player: {}", player->GetName());
handler->PSendSysMessage("|cFFFF0000 You must be in a group to be able to set a Mythic+ difficulty.");
return true;
}
if (args.empty()) {
handler->PSendSysMessage("|cFFFF0000 You must specify a difficulty level. Expected values are 'mythic', 'legendary', or 'ascendant'.");
return true;
}
std::string difficulty = args[0];
// if(!sMythicPlus->IsDifficultyEnabled(difficulty)) {
// handler->PSendSysMessage("|cFFFF0000 The difficulty level you have selected is not enabled.");
// return true;
// }
if (!group->IsLeader(player->GetGUID())) {
handler->PSendSysMessage("|cFFFF0000 You must be the group leader to set a Mythic+ difficulty.");
return true;
}
if (player->GetMap()->IsDungeon()) {
player->ResetInstances(player->GetGUID(), INSTANCE_RESET_CHANGE_DIFFICULTY, false);
player->SendResetInstanceSuccess(player->GetMap()->GetId());
return true;
}
if (difficulty == "mythic") {
sMpDataStore->AddGroupData(group, MpGroupData(group, MP_DIFFICULTY_MYTHIC));
}
else if (difficulty == "legendary") {
sMpDataStore->AddGroupData(group,MpGroupData(group, MP_DIFFICULTY_LEGENDARY));
}
else if (difficulty == "ascendant") {
sMpDataStore->AddGroupData(group, MpGroupData(group, MP_DIFFICULTY_ASCENDANT));
}
else if (difficulty == "heroic") {
sMpDataStore->RemoveGroupData(group);
group->SetDungeonDifficulty(DUNGEON_DIFFICULTY_HEROIC);
}
else if (difficulty == "normal") {
sMpDataStore->RemoveGroupData(group);
group->SetDungeonDifficulty(DUNGEON_DIFFICULTY_NORMAL);
}
else {
handler->PSendSysMessage("|cFFFF0000 Invalid difficulty level. Expected values are 'mythic', 'legendary', or 'ascendant'.");
return true;
}
handler->PSendSysMessage("Mythic+ difficulty set to: " + difficulty);
return true;
}
static bool HandleMythic(ChatHandler* handler, const std::vector<std::string>& /*args*/)
{
return HandleSetDifficulty(handler, {"mythic"});
}
static bool HandleLegendary(ChatHandler* handler, const std::vector<std::string>& /*args*/)
{
return HandleSetDifficulty(handler, {"legendary"});
}
static bool HandleAscendant(ChatHandler* handler, const std::vector<std::string>& /*args*/)
{
return HandleSetDifficulty(handler, {"ascendant"});
}
static bool HandleStatus(ChatHandler* handler)
{
Player* player = handler->GetPlayer();
Map* map = player->GetMap();
uint32 mapId = player->GetMapId();
std::string status = Acore::StringFormat(
"Mythic+ Status:\n"
" Mythic+ Enabled: %s\n"
" Mythic+ Item Rewards: %s\n"
" Mythic+ DeathLimits: %s\n",
sMythicPlus->Enabled ? "Yes" : "No",
sMythicPlus->EnableItemRewards ? "Yes" : "No",
sMythicPlus->EnableDeathLimits ? "Yes" : "No");
if (player->GetGroup()) {
auto groupData = sMpDataStore->GetGroupData(player->GetGroup()->GetGUID());
if (groupData) {
MpScaleFactor scaleFactors;
if(map->IsDungeon()) {
scaleFactors = sMpDataStore->GetScaleFactor(mapId, groupData->difficulty);
}
status += Acore::StringFormat(
" Group Difficulty: %u\n"
" Group Deaths: %u\n"
" Scale FactorStr %s\n",
(groupData->difficulty) ? groupData->difficulty : 0,
(groupData->GetDeaths(player->GetMapId(), player->GetInstanceId())),
scaleFactors.ToString()
);
} else {
status += " Group Difficulty: Not Set\n";
}
}
handler->PSendSysMessage(status);
return true;
}
static bool HandleReScale(ChatHandler* handler)
{
Creature* creature = handler->getSelectedCreature();
if(!creature) {
handler->PSendSysMessage("You must select a creature to rescale.");
return true;
}
MpCreatureData* creatureData = sMpDataStore->GetCreatureData(creature->GetGUID());
if(!creatureData) {
handler->PSendSysMessage("Creature is not eligible for rescaling.");
return true;
}
auto instanceData = sMpDataStore->GetInstanceData(creature->GetMapId(), creature->GetInstanceId());
if(!instanceData) {
handler->PSendSysMessage("No instance data found for this creature.");
return true;
}
if(creature->IsDungeonBoss() || creature->GetEntry() == 23682) {
sMythicPlus->ScaleCreature(creature->GetLevel(), creature, &instanceData->boss, instanceData->difficulty);
} else {
sMythicPlus->ScaleCreature(creature->GetLevel(), creature, &instanceData->creature, instanceData->difficulty);
}
handler->PSendSysMessage("Creature rescaled: %s", creature->GetName());
return true;
}
static bool HandleReScaleAll(ChatHandler* handler)
{
Player* player = handler->GetPlayer();
if(!player) {
handler->PSendSysMessage("You must be a player to rescale all creatures.");
return true;
}
Map* map = player->GetMap();
if(!map) {
handler->PSendSysMessage("You must be in a map to rescale all creatures.");
return true;
}
int32 mapId = map->GetId();
int32 instanceId = map->GetInstanceId();
auto instanceData = sMpDataStore->GetInstanceData(mapId, instanceId);
if(!instanceData) {
handler->PSendSysMessage("No mythic instance data found for this map.");
return true;
}
sMythicPlus->ScaleAll(player, instanceData);
handler->PSendSysMessage("All creatures rescaled.");
return true;
}
static bool HandleDisable(ChatHandler* handler)
{
MpLogger::debug("HandleDisable()");
sMythicPlus->Enabled = false;
handler->SendSysMessage("Mythic+ mod has been disabled.");
return true;
}
static bool HandleEnable(ChatHandler* handler)
{
MpLogger::debug("HandleEnable()");
sMythicPlus->Enabled = false;
handler->SendSysMessage("Mythic+ mod has been enabled.");
return true;
}
static bool HandleChangeMelee(ChatHandler* handler, const std::vector<std::string>& args)
{
if (args.empty()) {
handler->PSendSysMessage("|cFFFF0000 You must specify a value to set the melee scale factor.");
return true;
}
Player* player = handler->GetSession()->GetPlayer();
if (!player) {
handler->PSendSysMessage("|cFFFF0000 Invalid session or player.");
return true;
}
if (player->GetGroup()) {
auto groupData = sMpDataStore->GetGroupData(player->GetGroup()->GetGUID());
if(groupData) {
uint32 value = std::stoi(args[0]);
sMpDataStore->SetDamageScaleFactor(player->GetMapId(), groupData->difficulty, value);
handler->PSendSysMessage(Acore::StringFormat("Melee scale factor set to: %u", value));
return true;
}
}
handler->PSendSysMessage("|cFFFF0000 You must be in a group and mythic+ instance to set a melee scale factor.");
return true;
}
static bool HandleChangeSpell(ChatHandler* handler, const std::vector<std::string>& args)
{
if (args.empty()) {
handler->PSendSysMessage("|cFFFF0000 You must specify a value to set the spell scale factor.");
return true;
}
Player* player = handler->GetSession()->GetPlayer();
if (!player) {
handler->PSendSysMessage("|cFFFF0000 Invalid session or player.");
return true;
}
if (player->GetGroup()) {
auto groupData = sMpDataStore->GetGroupData(player->GetGroup()->GetGUID());
if(groupData) {
uint32 value = std::stoi(args[0]);
sMpDataStore->SetSpellScaleFactor(player->GetMapId(), groupData->difficulty, value);
handler->PSendSysMessage(Acore::StringFormat("Spell scale factor set to: %u", value));
return true;
}
}
handler->PSendSysMessage("|cFFFF0000 You must be in a group and mythic+ instance to set a melee scale factor.");
return true;
}
static bool HandleChangeHealth(ChatHandler* handler, const std::vector<std::string>& args)
{
if (args.empty()) {
handler->PSendSysMessage("|cFFFF0000 You must specify a value to set the health scale factor.");
return true;
}
Player* player = handler->GetSession()->GetPlayer();
if (!player) {
handler->PSendSysMessage("|cFFFF0000 Invalid session or player.");
return true;
}
if (player->GetGroup()) {
auto groupData = sMpDataStore->GetGroupData(player->GetGroup()->GetGUID());
if(groupData) {
uint32 value = std::stoi(args[0]);
sMpDataStore->SetHealthScaleFactor(player->GetMapId(), groupData->difficulty, value);
handler->PSendSysMessage(Acore::StringFormat("Health scale factor set to: %u", value));
return true;
}
}
handler->PSendSysMessage("|cFFFF0000 You must be in a group and mythic+ instance to set a melee scale factor.");
return true;
}
};
void Add_MP_CommandScripts()
{
MpLogger::debug("Add_MP_CommandScripts()");
new MythicPlus_CommandScript();
}

View File

@@ -11,7 +11,7 @@ public:
MythicPlus_GlobalScript() : GlobalScript("MythicPlus_GlobalScript") { }
// This adds the mythic+ item scaling to the loot table for enemies
void OnBeforeDropAddItem(Player const* player, Loot& loot, bool /*canRate*/, uint16 /*lootMode*/, LootStoreItem* LootStoreItem, LootStore const& store) override {
void OnBeforeDropAddItem(Player const* player, Loot& /*loot*/, bool /*canRate*/, uint16 /*lootMode*/, LootStoreItem* LootStoreItem, LootStore const& store) override {
if(LootStoreItem->itemid == 0) {
return;

100
src/Scripts/GroupScript.cpp Normal file
View File

@@ -0,0 +1,100 @@
#include "MpDataStore.h"
#include "MpLogger.h"
#include "ScriptMgr.h"
#include "Group.h"
// this handles updating custom group difficulties used in auto balancing mobs and
// scripts that enable buffs on mobs randomly
class MythicPlus_GroupScript : public GroupScript
{
public:
MythicPlus_GroupScript() : GroupScript("MythicPlus_GroupScript") { }
void OnAddMember(Group* group, ObjectGuid guid) override {
if (!group || !guid) {
return;
}
Player* player = ObjectAccessor::FindPlayer(guid);
if (!player) {
MpLogger::warn("Player not found for guid {}", guid.GetCounter());
return;
}
MpGroupData *gd = sMpDataStore->GetGroupData(group->GetGUID());
MpPlayerData* pd = sMpDataStore->GetPlayerData(guid);
if(!pd) {
MpDifficulty difficulty = GetPlayerDifficulty(player);
MpPlayerData playerData = MpPlayerData(player, difficulty, group->GetGUID().GetCounter());
sMpDataStore->AddPlayerData(guid, &playerData);
} else {
// If the player is joining a new group then reset the death counters otherwise let them ride
if (pd->groupId != group->GetGUID().GetCounter()) {
pd->groupId = group->GetGUID().GetCounter();
pd->ResetAllDeathCounts();
}
}
if(!gd) {
MpLogger::warn("Group data not found for group {}", group->GetGUID().GetCounter());
return;
}
gd->AddPlayerData(pd);
}
void OnCreate(Group* group, Player* leader) override {
if (!group) {
return;
}
if(!leader) {
return;
}
// Start a group and set the data up for the group
MpDifficulty difficulty = GetPlayerDifficulty(leader);
MpGroupData gd = MpGroupData(group, difficulty);
// Insert the leader of the group after group data struct is built
MpPlayerData* pd = sMpDataStore->GetPlayerData(leader->GetGUID());
if(pd) {
gd.AddPlayerData(pd);
} else {
MpPlayerData playerData = MpPlayerData(leader, difficulty, group->GetGUID().GetCounter());
gd.AddPlayerData(&playerData);
}
// Store the data into our memory store
sMpDataStore->AddGroupData(group, gd);
}
void OnDisband(Group* group) override {
sMpDataStore->RemoveGroupData(group);
}
// Get the difficulty for a player that is assigned
MpDifficulty GetPlayerDifficulty(Player* player) {
if(!player) {
return MP_DIFFICULTY_NORMAL;
}
MpPlayerData* pd = sMpDataStore->GetPlayerData(player->GetGUID());
if(pd) {
return pd->difficulty;
} else {
return player->GetDifficulty(false) == Difficulty::DUNGEON_DIFFICULTY_NORMAL ? MP_DIFFICULTY_NORMAL : MP_DIFFICULTY_HEROIC;
}
return MP_DIFFICULTY_NORMAL;
}
};
void Add_MP_GroupScripts()
{
MpLogger::debug("Add_MP_GroupScripts()");
new MythicPlus_GroupScript();
}

View File

@@ -0,0 +1,91 @@
#include "MpLogger.h"
#include "MythicPlus.h"
#include "Player.h"
#include "ScriptMgr.h"
#include "AdvancementMgr.h"
#include "Chat.h"
#include "Channel.h"
#include "ChannelMgr.h"
#include "MpEventProcessor.h"
#include <boost/algorithm/string/predicate.hpp> // For starts_with
#include <vector>
#include <string>
#include <ranges>
/**
* This script file is a special event handler attached to the chat channel for MythicPlus to intercept
* message events from the client hidden chat channel and process them, as well as return events back to
* the client. It's a very simplified version of how Eluna / AIO manage messages from UI to C++ mods.
*
* All Messages come into a chat channel from a specific user on a hidden channel with details in the MythicPlus.h
* class for the protocol data definition.
*/
class MythicPlus_PlayerMessageEvents : public PlayerScript
{
public:
MythicPlus_PlayerMessageEvents() : PlayerScript("MythicPlus_PlayerMessageEvents") {}
/**
* Listens to AddOn Chat channel for Mythic+ communication between UI and server mythic+ functionality
*/
void OnChat(Player* player, uint32 type, uint32 lang, std::string& msg, Player* receiver) override
{
// All communication from the client should be a whisper to themselves over tha addon channel
if(!player || !receiver) {
return;
}
if(lang == LANG_ADDON) {
if(msg.empty()) {
MpLogger::info("Empty AddOn message received from player: {}", player->GetName());
return;
}
// if the message begins with our prefix for our data channel then process the event
if(boost::starts_with(msg, MP_DATA_CHAT_CHANNEL)) {
sMpEventProcessor->ProcessMessage(player, msg);
}
}
}
/**
* When a player logs in add them to the data channel specifically for Mythic+ communication
* between UI and server module.
*
* Load advancement data for the player at load time used to apply buffs.
*/
// void OnLogin(Player* player) override
// {
// if(!player) {
// return;
// }
// // Create a channel called MpEx if it does not exist
// MpLogger::info("Player {} logged in on team {}", player->GetName(), player->GetTeamId());
// ChannelMgr* cmg = ChannelMgr::forTeam(player->GetTeamId());
// if(!cmg) {
// MpLogger::error("Failed to get channel manager for team {}", player->GetTeamId());
// return;
// }
// Channel* channel = cmg->GetChannel(static_cast<std::string>(MP_DATA_CHAT_CHANNEL), player);
// if(!channel) {
// MpLogger::error("Failed to get mythic data channel for player {}", player->GetName());
// Channel* nchan = new Channel(static_cast<std::string>(MP_DATA_CHAT_CHANNEL), 17, 0, player->GetTeamId());
// if(!nchan) {
// MpLogger::error("Failed to create mythic data channel for player {}", player->GetName());
// return;
// }
// }
// }
};
void Add_MP_PlayerMessageEvents()
{
MpLogger::debug("Add_MP_PlayerEventMessages");
new MythicPlus_PlayerMessageEvents();
}

View File

@@ -0,0 +1,194 @@
#include "MpLogger.h"
#include "MpDataStore.h"
#include "MpScheduler.h"
#include "MythicPlus.h"
#include "Player.h"
#include "Group.h"
#include "ScriptMgr.h"
#include "TaskScheduler.h"
#include "AdvancementMgr.h"
class MythicPlus_PlayerScript : public PlayerScript
{
public:
MythicPlus_PlayerScript() : PlayerScript("MythicPlus_PlayerScript") {}
/**
* Mythic+ special actions when a player dies:
* - Track the death for the group
* - Update the player death stats
* - Determine whether or not the Group failed the instance due to death count setting.
*/
void OnPlayerKilledByCreature(Creature* killer, Player* player) override
{
Map* map = player->GetMap();
if(!sMythicPlus->IsMapEligible(map)) {
return;
}
if (!player) {
return;
}
Group* group = player->GetGroup();
if(!group) {
MpLogger::warn("Missing group data for player {}", player->GetName());
return;
}
MpGroupData *data = sMpDataStore->GetGroupData(player->GetGroup());
if (!data) {
MpLogger::warn("Missin group data for player {}", player->GetName());
return;
}
MpPlayerData *playerData = sMpDataStore->GetPlayerData(player->GetGUID());
if (!playerData) {
MpLogger::warn("Missin player data for player {}", player->GetName());
return;
}
playerData->AddDeath(map->GetId(), map->GetInstanceId());
if(killer) {
sMpDataStore->DBAddPlayerDeath(player, killer, data->difficulty);
} else {
sMpDataStore->DBAddPlayerDeath(player);
}
sMpDataStore->DBAddGroupDeath(group, player->GetMapId(), player->GetInstanceId(), data->difficulty);
uint32 totalDeaths = data->GetDeaths(player->GetMapId(), player->GetInstanceId());
MpLogger::info("Total Deaths: {}", totalDeaths);
if(totalDeaths > 1) {
MpLogger::debug(" :::: Player Deaths for Group too high! ::::::");
TaskScheduler& wScheduler = sMpScheduler->GetWorldScheduler();
wScheduler.Schedule(10s, MP_WORLD_TASK_GROUP, [player, map](TaskContext /*ctx*/) {
Group* group = player->GetGroup();
if(!group) {
return;
}
MythicPlus::GroupReset(group, map);
});
}
// if(totalDeaths > 1) {
// Map* map = player->GetMap();
// if(!map) {
// return;
// }
// Group* group = player->GetGroup();
// if(!group) {
// MpLogger::warn("Player {} is not in a group.", player->GetName());
// return;
// }
// // map->RemoveAllPlayers();
// MpLogger::info("Starting scheduled failure notification");
// // auto testlambda = [](TaskContext ctx) { return; };
// sMpScheduler->ScheduleWorldTask(1s, [](TaskContext ctx) {
// MpLogger::info("<<<<<<<<<<< Player Death Scheduler fire >>>>>>>>>>>>>");
// });
// sMpScheduler->GetWorldScheduler().Schedule(1s, [playerName = player->GetName()](TaskContext ctx) {
// MpLogger::info("<<<<<<<<<<< Player Death Scheduler fire {} >>>>>>>>>>>>>", playerName);
// return;
// });
// std::vector<Player*> players = GetGroupMembers(player);
// MpLogger::info("Failed mythic+ instance run notification fired. ");
// WorldPacket data;
// for(Player* player : players)
// {
// MpLogger::info("Seding notification of failure to player: {}", player->GetName());
// player->GetSession()->SendShowBank(player->GetGUID());
// // player->GetSession()->SendNotification("Your group has died too many time to continue.");
// // ChatHandler::BuildChatPacket(data, CHAT_MSG_RAID_BOSS_EMOTE, LANG_UNIVERSAL, nullptr, player, message);
// // player->GetSession()->SendPacket(&data);
// }
// map->ToInstanceMap()->Reset(0);
// );
// }
}
void OnLogin(Player* player) override
{
// Load the player advancement data for the player when they login
int32 size = sAdvancementMgr->LoadPlayerAdvancements(player);
MpLogger::info("Loaded {} player advancements for player {}", size, player->GetName());
}
// When a player is bound to an instance need to make sure they are saved in the data soure to retrieve later.
void OnBindToInstance(Player* player, Difficulty /*difficulty*/, uint32 mapId, bool /*permanent*/) override
{
if(!player) {
return;
}
Group* group = player->GetGroup();
// If they are not in a group do nothing.
if(!group) {
return;
}
MpGroupData* data = sMpDataStore->GetGroupData(group);
// If there is not any mythic+ data set for this group do nothing.
if(!data) {
return;
}
Map* map = player->GetMap();
if(!map) {
MpLogger::warn("Player {} is not in a map", player->GetName());
return;
}
// get the player data or set it up
MpPlayerData* playerData = sMpDataStore->GetPlayerData(player->GetGUID());
if(!playerData) {
playerData = new MpPlayerData(player, data->difficulty, group->GetGUID().GetCounter());
sMpDataStore->AddPlayerData(player->GetGUID(), playerData);
}
// Add this players data to the group data
data->AddPlayerData(playerData);
auto mapKey = sMpDataStore->GetInstanceDataKey(mapId, player->GetInstanceId());
playerData->instanceData.emplace(mapKey, MpPlayerInstanceData{
.deaths = 0,
});
sMpDataStore->DBUpdatePlayerInstanceData(player->GetGUID(), data->difficulty, map->GetId(), map->GetInstanceId());
sMpDataStore->DBUpdateGroupData(group->GetGUID(), data->difficulty, map->GetId(), map->GetInstanceId(), 0);
}
std::vector<Player*> GetGroupMembers(Player* currentPlayer)
{
std::vector<Player*> groupPlayers;
Group* group = currentPlayer->GetGroup();
if (!group)
{
MpLogger::warn("Player is not in a group.");
return groupPlayers;
}
group->DoForAllMembers([&](Player* member) {
groupPlayers.push_back(member);
});
return groupPlayers;
}
};
void Add_MP_PlayerScripts()
{
MpLogger::debug("Add_MP_PlayerScripts()");
new MythicPlus_PlayerScript();
}

View File

@@ -18,22 +18,29 @@ public:
return;
}
bool haspositiveeffect = false;
auto effects = spellInfo->Effects;
bool isHot = false;
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) {
if (spellInfo->_IsPositiveEffect(i, true)) {
haspositiveeffect = true;
break;
switch(effects[i].Effect) {
case SPELL_EFFECT_HEAL:
case SPELL_EFFECT_HEAL_MAX_HEALTH:
case SPELL_EFFECT_HEAL_MECHANICAL:
case SPELL_EFFECT_HEAL_PCT:
case SPELL_EFFECT_SPIRIT_HEAL:
isHot = true;
break;
}
}
if(haspositiveeffect) {
damage = modifyIncomingDmgHeal(MythicPlus::UNIT_EVENT_HOT, target, attacker, damage);
if(isHot) {
damage = modifyIncomingDmgHeal(MythicPlus::UNIT_EVENT_HOT, target, attacker, damage, spellInfo);
} else {
damage = modifyIncomingDmgHeal(MythicPlus::UNIT_EVENT_DOT, target, attacker, damage);
damage = modifyIncomingDmgHeal(MythicPlus::UNIT_EVENT_DOT, target, attacker, damage, spellInfo);
}
}
void ModifySpellDamageTaken(Unit* target, Unit* attacker, int32& damage, SpellInfo const* /*spellInfo*/) override {
void ModifySpellDamageTaken(Unit* target, Unit* attacker, int32& damage, SpellInfo const* spellInfo) override {
if (!target && !attacker) {
return;
}
@@ -43,7 +50,11 @@ public:
return;
}
damage = modifyIncomingDmgHeal(MythicPlus::UNIT_EVENT_SPELL, target, attacker, damage);
if(sMythicPlus->EligibleDamageTarget(target)) {
MpLogger::debug("ModifySpellDamageTaken: {} hits {} with spell: {}", attacker->GetName(), target->GetName(), spellInfo->SpellName[0]);
}
damage = modifyIncomingDmgHeal(MythicPlus::UNIT_EVENT_SPELL, target, attacker, damage, spellInfo);
}
/**
@@ -64,7 +75,7 @@ public:
}
// When a healing spell hits a mythic+ enemy modify based on the modifiers for the difficulty
void ModifyHealReceived(Unit* target, Unit* healer, uint32& healing, SpellInfo const* /*spellInfo*/) override {
void ModifyHealReceived(Unit* target, Unit* healer, uint32& healing, SpellInfo const* spellInfo) override {
if (!target && !healer) {
return;
}
@@ -74,7 +85,7 @@ public:
return;
}
healing = modifyIncomingDmgHeal(MythicPlus::UNIT_EVENT_HEAL, target, healer, healing);
healing = modifyIncomingDmgHeal(MythicPlus::UNIT_EVENT_HEAL, target, healer, healing, spellInfo);
}
uint32 modifyIncomingDmgHeal(MythicPlus::MP_UNIT_EVENT_TYPE eventType,Unit* target, Unit* attacker, uint32 damageOrHeal, SpellInfo const* spellInfo = nullptr) {
@@ -129,9 +140,6 @@ public:
break;
}
MpLogger::debug("Incoming Event Type ({}): {} hits {} before mod: {}", eventName, attacker->GetName(), target->GetName(), damageOrHeal);
bool isHeal = false;
// If the target is the enemy then increase the amount of healing by the instance data modifier for spell output.
if(sMythicPlus->EligibleDamageTarget(target)) {
/**
@@ -140,7 +148,7 @@ public:
*/
switch (eventType) {
case MythicPlus::UNIT_EVENT_MELEE:
if(creature->IsDungeonBoss()) {
if(creature->IsDungeonBoss() || creature->GetEntry() == 23682) {
alteredDmgHeal = damageOrHeal * instanceData->boss.melee;
} else {
alteredDmgHeal = damageOrHeal * instanceData->creature.melee;
@@ -149,40 +157,45 @@ public:
break;
case MythicPlus::UNIT_EVENT_DOT:
case MythicPlus::UNIT_EVENT_SPELL:
if(creature->IsDungeonBoss()) {
if(creature->IsDungeonBoss() || creature->GetEntry() == 23682) {
if(spellInfo) {
alteredDmgHeal = sMythicPlus->ScaleDamageSpell(spellInfo, sMpDataStore->GetCreatureData(attacker->GetGUID()), creature, instanceData->boss.spell);
alteredDmgHeal = sMythicPlus->ScaleDamageSpell(spellInfo, damageOrHeal, sMpDataStore->GetCreatureData(attacker->GetGUID()), creature, target, instanceData->boss.spell);
} else {
alteredDmgHeal = damageOrHeal * instanceData->boss.spell;
}
} else {
if(spellInfo) {
alteredDmgHeal = sMythicPlus->ScaleDamageSpell(spellInfo, sMpDataStore->GetCreatureData(attacker->GetGUID()), creature, instanceData->creature.spell);
alteredDmgHeal = sMythicPlus->ScaleDamageSpell(spellInfo, damageOrHeal, sMpDataStore->GetCreatureData(attacker->GetGUID()), creature, target, instanceData->creature.spell);
} else {
alteredDmgHeal = damageOrHeal * instanceData->creature.spell;
}
}
MpLogger::debug("Incoming spell or dot New Damage: {}({}) {} hits {}", alteredDmgHeal, damageOrHeal, attacker->GetName(), target->GetName());
if(spellInfo) {
MpLogger::debug("Incoming spell New Damage: {}({}) {} hits {} spell: {} ID: {}", alteredDmgHeal, damageOrHeal, attacker->GetName(), target->GetName(), spellInfo->SpellName[0], spellInfo->Id);
} else {
MpLogger::debug("Incoming spell New Damage: {}({}) {} hits {}", alteredDmgHeal, damageOrHeal, attacker->GetName(), target->GetName());
}
break;
case MythicPlus::UNIT_EVENT_HEAL:
case MythicPlus::UNIT_EVENT_HOT:
break;
}
}
/**
* @TODO: Add more granular control over the scaling of healing spells
*/
if(sMythicPlus->EligibleHealTarget(target) && (eventType == MythicPlus::UNIT_EVENT_HEAL || eventType == MythicPlus::UNIT_EVENT_HOT)) {
isHeal = true;
if(creature->IsDungeonBoss()) {
if(spellInfo) {
alteredDmgHeal = sMythicPlus->ScaleHealSpell(spellInfo, sMpDataStore->GetCreatureData(attacker->GetGUID()), creature, attacker->ToCreature(), instanceData->boss.spell);
alteredDmgHeal = sMythicPlus->ScaleHealSpell(spellInfo, damageOrHeal, sMpDataStore->GetCreatureData(attacker->GetGUID()), creature, attacker->ToCreature(), instanceData->boss.spell);
} else {
alteredDmgHeal = damageOrHeal * instanceData->boss.spell;
}
} else {
if(spellInfo) {
alteredDmgHeal = sMythicPlus->ScaleHealSpell(spellInfo, sMpDataStore->GetCreatureData(attacker->GetGUID()), creature, attacker->ToCreature(), instanceData->creature.spell);
alteredDmgHeal = sMythicPlus->ScaleHealSpell(spellInfo, damageOrHeal, sMpDataStore->GetCreatureData(attacker->GetGUID()), creature, attacker->ToCreature(), instanceData->creature.spell);
} else {
alteredDmgHeal = damageOrHeal * instanceData->creature.spell;
}

View File

@@ -1,9 +1,11 @@
#include "Config.h"
#include "MythicPlus.h"
#include "MpDataStore.h"
#include "AdvancementMgr.h"
#include "MpLogger.h"
#include "Player.h"
#include "ScriptMgr.h"
#include "MpEventHandlers.cpp"
class MythicPlus_WorldScript : public WorldScript
{
@@ -86,6 +88,9 @@ public:
sMythicPlus->mythicItemOffset = sConfigMgr->GetOption<uint32>("MythicPlus.Mythic.ItemOffset", 20000000);
sMythicPlus->legendaryItemOffset = sConfigMgr->GetOption<uint32>("MythicPlus.Legendary.ItemOffset", 21000000);
sMythicPlus->ascendantItemOffset = sConfigMgr->GetOption<uint32>("MythicPlus.Ascendant.ItemOffset", 22000000);
sMythicPlus->meleeAttackPowerDampener = sConfigMgr->GetOption<uint32>("MythicPlus.MeleeAttackPowerDampener", 2000);
sMythicPlus->meleeAttackPowerStart = sConfigMgr->GetOption<uint32>("MythicPlus.MeleeAttackPowerStart", 10000);
}
void OnStartup() override
@@ -93,6 +98,15 @@ public:
int32 size = sMpDataStore->LoadScaleFactors();
MpLogger::info("Loaded {} Mythic+ Scaling Factors from database...", size);
size = sAdvancementMgr->LoadAdvancementRanks();
MpLogger::info("Loaded {} advancement ranks...", size);
size = sAdvancementMgr->LoadMaterialTypes();
MpLogger::info("Loaded {} material types...", size);
// Registering event handlers for the Mythic+ events from client
MP_Register_EventHandlers();
MpLogger::info("Registered Mythic+ Event Handlers...");
}
};

70
src/Spells/Toughness.cpp Normal file
View File

@@ -0,0 +1,70 @@
// #include "CreatureScript.h"
// #include "PetDefines.h"
// #include "Player.h"
// #include "MpLogger.h"
// #include "SpellAuraEffects.h"
// #include "SpellInfo.h"
// #include "SpellMgr.h"
// #include "SpellScript.h"
// #include "SpellScriptLoader.h"
// #include "UnitAI.h"
// #include "World.h"
// class spell_mp_toughness_aura : public AuraScript
// {
// PrepareAuraScript(spell_mp_toughness_aura);
// void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
// {
// Player* player = GetTarget()->ToPlayer();
// if (!player)
// return;
// MpLogger::info("Applying Advancement Toughness to Player {}", player->GetName());
// if (Unit* caster = GetCaster())
// {
// if (caster->IsPlayer())
// {
// Player* player = caster->ToPlayer();
// // Add 500 Strength
// player->HandleStatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, 500, true);
// // Apply red glow visual effect
// caster->SendPlaySpellVisual(11674); // Visual ID 11674 for red glow
// }
// }
// }
// void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
// {
// LOG_INFO("server.worldserver", "spell_custom_red_glow_strength_aura::HandleEffectRemove");
// if (Unit* caster = GetCaster())
// {
// if (caster->IsPlayer())
// {
// Player* player = caster->ToPlayer();
// // Remove 500 Strength
// player->HandleStatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, 500, false);
// }
// }
// }
// void Register() override
// {
// LOG_INFO("server.worldserver", "spell_custom_red_glow_strength_aura::Register");
// OnEffectApply += AuraEffectApplyFn(spell_custom_red_glow_strength_aura::HandleEffectApply, EFFECT_0, SPELL_AURA_MOD_STAT, AURA_EFFECT_HANDLE_REAL);
// OnEffectRemove += AuraEffectRemoveFn(spell_custom_red_glow_strength_aura::HandleEffectRemove, EFFECT_0, SPELL_AURA_MOD_STAT, AURA_EFFECT_HANDLE_REAL);
// }
// };
// void AddSC_spell_custom_red_glow_strength_aura()
// {
// // LOG_INFO("server.loading", "Registering spell custom_red_glow_strength_aura");
// RegisterSpellScript(spell_custom_red_glow_strength_aura);
// }