mirror of
https://github.com/araxiaonline/mod-mythic-plus.git
synced 2026-06-13 11:12:23 -04:00
Compare commits
57 Commits
feat/ap-fi
...
refactor-s
| Author | SHA1 | Date | |
|---|---|---|---|
| ad39d53c58 | |||
| b4013b7024 | |||
| 7d1a9018df | |||
| d4fa53d435 | |||
| 2aeafa689a | |||
| f42cba3099 | |||
| aa58b493ba | |||
| 5387960965 | |||
| 4be3fae368 | |||
| d410220bd0 | |||
| 4df7800afa | |||
| 173d19bcd3 | |||
| 738194fbfa | |||
| 81b7a87ced | |||
| 8e0809cb1c | |||
| cb48af9e9d | |||
| acf11d25f2 | |||
| 41fd4876cf | |||
| ef62c10df3 | |||
| 5fbb0eac74 | |||
| 0982e24f06 | |||
| fa7389f24a | |||
| 8436460350 | |||
| 3bd1f3cdc6 | |||
| 6115459150 | |||
| 3093ab3280 | |||
| fa6f3f3eab | |||
| ccf222ec4f | |||
| 91d3f91dfc | |||
| 9612974b01 | |||
| fd5e186032 | |||
| baf0d8e6e7 | |||
| f3230a6559 | |||
| 86d5ba83f1 | |||
|
|
318244f0fd | ||
| 73018100ac | |||
| fbcb218dd6 | |||
| 322243abf4 | |||
| 9d009d0feb | |||
| a0fdfc9836 | |||
| a6de416efb | |||
| 1695f33f68 | |||
| 88d894eaaf | |||
| f66b861079 | |||
| 79cb4d9835 | |||
| c32b64bb1f | |||
| f083748c4a | |||
| 3cf1e17c99 | |||
| 0a0236b273 | |||
| 8942d53b13 | |||
| 41819432e9 | |||
| d35a934b8a | |||
| 46d5a99f04 | |||
| f8ecc0d1b9 | |||
| fcce3a0453 | |||
| 68efd1a732 | |||
| 9f4ea9d539 |
@@ -112,3 +112,8 @@ MythicPlus.Mythic.ItemOffset = 20000000
|
||||
MythicPlus.Legendary.ItemOffset = 21000000
|
||||
MythicPlus.Ascendant.ItemOffset = 22000000
|
||||
|
||||
##############
|
||||
# Scaling Adjusters
|
||||
#############
|
||||
MythicPlus.MeleeAttackPowerDampener = 800
|
||||
MythicPlus.MeleeAttackPowerStart = 3000
|
||||
|
||||
100
data/sql/db-characters/base/01_mp_schema.sql
Normal file
100
data/sql/db-characters/base/01_mp_schema.sql
Normal 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)
|
||||
);
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
DROP TABLE IF EXISTS group_difficulty;
|
||||
|
||||
CREATE TABLE group_difficulty (
|
||||
guid INT UNSIGNED NOT NULL DEFAULT '0',
|
||||
difficulty TINYINT UNSIGNED NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (guid)
|
||||
);
|
||||
48
data/sql/db-world/base/01_mp_world_schema.sql
Normal file
48
data/sql/db-world/base/01_mp_world_schema.sql
Normal 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)
|
||||
);
|
||||
537
data/sql/db-world/base/02_mp_material_types.sql
Normal file
537
data/sql/db-world/base/02_mp_material_types.sql
Normal 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.
|
||||
);
|
||||
@@ -1,17 +1,7 @@
|
||||
-- Last Update: 2021/08/15
|
||||
-- Description: Scale factors for Mythic+ dungeons used to normalize dungeon difficulty across expansions.
|
||||
DROP TABLE IF EXISTS mythic_plus_scale_factors;
|
||||
CREATE TABLE IF NOT EXISTS mythic_plus_scale_factors (
|
||||
mapId SMALLINT PRIMARY KEY,
|
||||
dmg_bonus TINYINT,
|
||||
spell_bonus TINYINT,
|
||||
hp_bonus TINYINT,
|
||||
difficulty TINYINT,
|
||||
max TINYINT
|
||||
);
|
||||
|
||||
-- 1. Pre 60 level dungeons (13 dmg_bonus, 2 hp_bonus, max 23, difficulty 3)
|
||||
INSERT INTO mythic_plus_scale_factors (mapId, dmg_bonus, spell_bonus, hp_bonus, difficulty, max)
|
||||
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
|
||||
@@ -29,7 +19,7 @@ VALUES
|
||||
ON DUPLICATE KEY UPDATE mapId = mapId;
|
||||
|
||||
-- 2. Level 60 dungeons for classic (15 dmg_bonus, 3 hp_bonus, max 25, difficulty 3)
|
||||
INSERT INTO mythic_plus_scale_factors (mapId, dmg_bonus, spell_bonus, hp_bonus, difficulty, max)
|
||||
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
|
||||
@@ -41,7 +31,7 @@ VALUES
|
||||
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 mythic_plus_scale_factors (mapId, dmg_bonus, spell_bonus, hp_bonus, difficulty, max)
|
||||
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
|
||||
@@ -54,7 +44,7 @@ VALUES
|
||||
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 mythic_plus_scale_factors (mapId, dmg_bonus, spell_bonus, hp_bonus, difficulty, max)
|
||||
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
|
||||
@@ -66,7 +56,7 @@ VALUES
|
||||
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 mythic_plus_scale_factors (mapId, dmg_bonus, spell_bonus, hp_bonus, difficulty, max)
|
||||
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
|
||||
@@ -77,7 +67,7 @@ VALUES
|
||||
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 mythic_plus_scale_factors (mapId, dmg_bonus, spell_bonus, hp_bonus, difficulty, max)
|
||||
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
|
||||
253
data/sql/db-world/base/07_mp_upgrade_ranks.sql
Normal file
253
data/sql/db-world/base/07_mp_upgrade_ranks.sql
Normal 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)
|
||||
;
|
||||
253
scripts/advancement_stats_ranks/generate_stat_upgrades.sql
Normal file
253
scripts/advancement_stats_ranks/generate_stat_upgrades.sql
Normal 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),
|
||||
;
|
||||
77
scripts/advancement_stats_ranks/main.go
Normal file
77
scripts/advancement_stats_ranks/main.go
Normal 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")
|
||||
}
|
||||
110
scripts/upgrade_ranks/main.go
Normal file
110
scripts/upgrade_ranks/main.go
Normal 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
415
src/AdvancementMgr.cpp
Normal 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
144
src/AdvancementMgr.h
Normal 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
|
||||
32
src/Creature/CreatureOverride.h
Normal file
32
src/Creature/CreatureOverride.h
Normal 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() {}
|
||||
// };
|
||||
45
src/Event/MpClientDispatch.cpp
Normal file
45
src/Event/MpClientDispatch.cpp
Normal 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;
|
||||
}
|
||||
35
src/Event/MpClientDispatcher.h
Normal file
35
src/Event/MpClientDispatcher.h
Normal 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
47
src/Event/MpEvent.h
Normal 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
|
||||
139
src/Event/MpEventHandlers.cpp
Normal file
139
src/Event/MpEventHandlers.cpp
Normal 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);
|
||||
}
|
||||
131
src/Event/MpEventProcessor.cpp
Normal file
131
src/Event/MpEventProcessor.cpp
Normal 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;
|
||||
}
|
||||
86
src/Event/MpEventProcessor.h
Normal file
86
src/Event/MpEventProcessor.h
Normal 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
|
||||
@@ -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();
|
||||
}
|
||||
11
src/MpConstants.h
Normal file
11
src/MpConstants.h
Normal 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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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) {
|
||||
@@ -185,7 +272,7 @@ int32 MpDataStore::LoadScaleFactors() {
|
||||
_scaleFactors->clear();
|
||||
|
||||
// 0 1 2 3 4 5
|
||||
QueryResult result = WorldDatabase.Query("SELECT mapId, dmg_bonus, spell_bonus, hp_bonus, difficulty, max FROM mythic_plus_scale_factors");
|
||||
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;
|
||||
@@ -213,3 +300,162 @@ int32 MpDataStore::LoadScaleFactors() {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "Group.h"
|
||||
#include "MapMgr.h"
|
||||
#include "Player.h"
|
||||
#include "MpLogger.h"
|
||||
#include "ObjectGuid.h"
|
||||
|
||||
#include <unordered_map>
|
||||
@@ -14,7 +15,8 @@
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
enum MpDifficulty {
|
||||
enum MpDifficulty
|
||||
{
|
||||
MP_DIFFICULTY_NORMAL = 0,
|
||||
MP_DIFFICULTY_HEROIC = 1,
|
||||
MP_DIFFICULTY_EPIC = 2,
|
||||
@@ -22,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) + " }";
|
||||
@@ -45,13 +143,6 @@ struct MpGroupData
|
||||
|
||||
};
|
||||
|
||||
struct MpPlayerData
|
||||
{
|
||||
Player* player;
|
||||
uint8 difficulty;
|
||||
uint32 deaths;
|
||||
};
|
||||
|
||||
struct MpScaleFactor
|
||||
{
|
||||
int32 dmgBonus;
|
||||
@@ -62,8 +153,7 @@ struct MpScaleFactor
|
||||
std::string ToString() const {
|
||||
return "MpScaleFactor: { dmgBonus: " + std::to_string(dmgBonus) +
|
||||
", healthBonus: " + std::to_string(healthBonus) +
|
||||
", healthBonus: " + std::to_string(healthBonus) +
|
||||
", maxDamageBonus: " + std::to_string(maxDamageBonus) + " }";
|
||||
", spellBonus: " + std::to_string(spellBonus) + "}";
|
||||
}
|
||||
|
||||
};
|
||||
@@ -196,10 +286,11 @@ struct MpCreatureData
|
||||
/**@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>>()),
|
||||
@@ -210,9 +301,15 @@ private:
|
||||
_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}
|
||||
@@ -235,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);
|
||||
@@ -252,7 +349,7 @@ public:
|
||||
}
|
||||
|
||||
}
|
||||
const MpGroupData* GetGroupData(Player *player) const {
|
||||
MpGroupData* GetGroupData(Player *player) {
|
||||
return GetGroupData(player->GetGroup()->GetGUID());
|
||||
};
|
||||
|
||||
@@ -262,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 );
|
||||
@@ -274,6 +373,7 @@ 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
|
||||
@@ -301,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
61
src/MpScheduler.h
Normal 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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "Creature.h"
|
||||
#include "CreatureAI.h"
|
||||
#include "CreatureHooks.h"
|
||||
// #include "CreatureHooks.h"
|
||||
#include "MpLogger.h"
|
||||
#include "MythicPlus.h"
|
||||
#include "ScriptMgr.h"
|
||||
@@ -27,12 +27,12 @@ public:
|
||||
}
|
||||
|
||||
void JustDied(Unit* killer) override {
|
||||
sCreatureHooks->JustDied(me->ToCreature(), killer);
|
||||
// sCreatureHooks->JustDied(me->ToCreature(), killer);
|
||||
BaseAI::JustDied(killer);
|
||||
}
|
||||
|
||||
void Reset() override {
|
||||
sCreatureHooks->JustSpawned(me->ToCreature());
|
||||
// sCreatureHooks->JustSpawned(me->ToCreature());
|
||||
|
||||
BaseAI::Reset();
|
||||
}
|
||||
|
||||
@@ -113,8 +113,6 @@ bool MythicPlus::EligibleDamageTarget(Unit* target)
|
||||
|
||||
bool MythicPlus::IsCreatureEligible(Creature* creature)
|
||||
{
|
||||
CreatureTemplate const * cInfo = creature->GetCreatureTemplate();
|
||||
|
||||
if (!creature) {
|
||||
return false;
|
||||
}
|
||||
@@ -128,7 +126,7 @@ bool MythicPlus::IsCreatureEligible(Creature* creature)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (creature->GetEntry() == 23682) {
|
||||
if (creature->GetEntry() == HEADLESS_HORSEMAN) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -198,7 +196,7 @@ void MythicPlus::AddScaledCreature(Creature* creature, MpInstanceData* instanceD
|
||||
// creature->SetAI(ai);
|
||||
|
||||
// We know the creature is scaled and in the instance to fire the event.
|
||||
sCreatureHooks->AddToInstance(creature);
|
||||
// sCreatureHooks->AddToInstance(creature);
|
||||
std::string name = creature->GetName();
|
||||
|
||||
// Assign random affix for now.
|
||||
@@ -235,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)
|
||||
{
|
||||
@@ -453,7 +459,6 @@ int32 MythicPlus::ScaleHealSpell(SpellInfo const * spellInfo, uint32 heal, MpCre
|
||||
auto effects = spellInfo->GetEffects();
|
||||
for (uint8 i = 0; i < effects.size(); ++i)
|
||||
{
|
||||
SpellEffectInfo effect = effects[i];
|
||||
MpLogger::debug(" >>> Spell {} effect {} value: {} by creature {}", spellInfo->SpellName[i], effects[i].Effect, heal, creature->GetName());
|
||||
}
|
||||
|
||||
@@ -462,12 +467,151 @@ int32 MythicPlus::ScaleHealSpell(SpellInfo const * spellInfo, uint32 heal, MpCre
|
||||
float levelDifference = creature->GetLevel() - originalLevel;
|
||||
float spellBonus = sMpDataStore->GetSpellScaleFactor(creature->GetMapId(), creature->GetInstanceId());
|
||||
|
||||
float scalingFactor = CalculateScaling(levelDifference, spellBonus);
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function is copied because was not accessible in core creature class
|
||||
|
||||
@@ -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:
|
||||
@@ -60,7 +73,6 @@ public:
|
||||
uint32 meleeAttackPowerDampener;
|
||||
uint32 meleeAttackPowerStart;
|
||||
|
||||
|
||||
enum MP_UNIT_EVENT_TYPE
|
||||
{
|
||||
UNIT_EVENT_MELEE,
|
||||
@@ -105,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);
|
||||
|
||||
@@ -117,6 +132,9 @@ public:
|
||||
// This scales a heal spell up based on the how much % the original heal spell was
|
||||
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() { }
|
||||
~MythicPlus() { }
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
#include "Instances/Ragefire/boss_bazzalan.cpp"
|
||||
#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();
|
||||
void Add_MP_AllMapScripts();
|
||||
@@ -14,20 +21,19 @@ void Add_MP_GroupScripts();
|
||||
void Add_MP_PlayerScripts();
|
||||
void Add_MP_UnitScripts();
|
||||
void Add_MP_WorldScripts();
|
||||
void Add_MP_PlayerMessageEvents();
|
||||
|
||||
// Mythic custom encounters for mythic+ dungeons
|
||||
|
||||
|
||||
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();
|
||||
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
#include "MpLogger.h"
|
||||
#include "MpDataStore.h"
|
||||
#include "MythicPlus.h"
|
||||
#include "Player.h"
|
||||
#include "ScriptMgr.h"
|
||||
|
||||
class MythicPlus_PlayerScript : public PlayerScript
|
||||
{
|
||||
public:
|
||||
MythicPlus_PlayerScript() : PlayerScript("MythicPlus_PlayerScript") { }
|
||||
|
||||
void OnPlayerJustDied(Player* player, Unit* killer)
|
||||
{
|
||||
Map* map = player->GetMap();
|
||||
if(!sMythicPlus->IsMapEligible(map)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!player || !killer) {
|
||||
return;
|
||||
}
|
||||
|
||||
MpGroupData *data = sMpDataStore->GetGroupData(player->GetGroup());
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
data->deaths++; //
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
void Add_MP_PlayerScripts()
|
||||
{
|
||||
MpLogger::debug("Add_MP_PlayerScripts()");
|
||||
new MythicPlus_PlayerScript();
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -27,6 +27,7 @@ public:
|
||||
{"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}
|
||||
@@ -137,13 +138,13 @@ public:
|
||||
}
|
||||
|
||||
if (difficulty == "mythic") {
|
||||
sMpDataStore->AddGroupData(group, MpGroupData(group, MP_DIFFICULTY_MYTHIC, 0));
|
||||
sMpDataStore->AddGroupData(group, MpGroupData(group, MP_DIFFICULTY_MYTHIC));
|
||||
}
|
||||
else if (difficulty == "legendary") {
|
||||
sMpDataStore->AddGroupData(group,MpGroupData(group, MP_DIFFICULTY_LEGENDARY, 0));
|
||||
sMpDataStore->AddGroupData(group,MpGroupData(group, MP_DIFFICULTY_LEGENDARY));
|
||||
}
|
||||
else if (difficulty == "ascendant") {
|
||||
sMpDataStore->AddGroupData(group, MpGroupData(group, MP_DIFFICULTY_ASCENDANT, 0));
|
||||
sMpDataStore->AddGroupData(group, MpGroupData(group, MP_DIFFICULTY_ASCENDANT));
|
||||
}
|
||||
else if (difficulty == "heroic") {
|
||||
sMpDataStore->RemoveGroupData(group);
|
||||
@@ -207,8 +208,8 @@ public:
|
||||
" Group Difficulty: %u\n"
|
||||
" Group Deaths: %u\n"
|
||||
" Scale FactorStr %s\n",
|
||||
groupData->difficulty,
|
||||
groupData->deaths,
|
||||
(groupData->difficulty) ? groupData->difficulty : 0,
|
||||
(groupData->GetDeaths(player->GetMapId(), player->GetInstanceId())),
|
||||
scaleFactors.ToString()
|
||||
);
|
||||
} else {
|
||||
@@ -251,6 +252,35 @@ public:
|
||||
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()");
|
||||
@@ -315,7 +345,7 @@ public:
|
||||
if(groupData) {
|
||||
uint32 value = std::stoi(args[0]);
|
||||
sMpDataStore->SetSpellScaleFactor(player->GetMapId(), groupData->difficulty, value);
|
||||
handler->PSendSysMessage(Acore::StringFormat("Melee scale factor set to: %u", value));
|
||||
handler->PSendSysMessage(Acore::StringFormat("Spell scale factor set to: %u", value));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -343,7 +373,7 @@ public:
|
||||
if(groupData) {
|
||||
uint32 value = std::stoi(args[0]);
|
||||
sMpDataStore->SetHealthScaleFactor(player->GetMapId(), groupData->difficulty, value);
|
||||
handler->PSendSysMessage(Acore::StringFormat("Melee scale factor set to: %u", value));
|
||||
handler->PSendSysMessage(Acore::StringFormat("Health scale factor set to: %u", value));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -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
100
src/Scripts/GroupScript.cpp
Normal 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();
|
||||
}
|
||||
91
src/Scripts/PlayerMessageEvents.cpp
Normal file
91
src/Scripts/PlayerMessageEvents.cpp
Normal 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();
|
||||
}
|
||||
194
src/Scripts/PlayerScript.cpp
Normal file
194
src/Scripts/PlayerScript.cpp
Normal 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();
|
||||
}
|
||||
@@ -187,7 +187,6 @@ public:
|
||||
* @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)) {
|
||||
bool isHeal = true;
|
||||
if(creature->IsDungeonBoss()) {
|
||||
if(spellInfo) {
|
||||
alteredDmgHeal = sMythicPlus->ScaleHealSpell(spellInfo, damageOrHeal, sMpDataStore->GetCreatureData(attacker->GetGUID()), creature, attacker->ToCreature(), instanceData->boss.spell);
|
||||
@@ -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
|
||||
{
|
||||
@@ -95,6 +97,16 @@ 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
70
src/Spells/Toughness.cpp
Normal 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);
|
||||
// }
|
||||
Reference in New Issue
Block a user