mirror of
https://github.com/araxiaonline/ets-module-collection.git
synced 2026-06-13 02:52:20 -04:00
mythic changes in development coded new npc
This commit is contained in:
35
modules/UI/mythicplus/advancement_state.ts
Normal file
35
modules/UI/mythicplus/advancement_state.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Logger } from "../../classes/logger";
|
||||
const log = new Logger("AdvancementState");
|
||||
|
||||
export type AdvancementType = "Magic" | "Attack" | "Defense";
|
||||
export class AdvancementState {
|
||||
|
||||
private advancement: string | null = null;
|
||||
private advType: AdvancementType | null = null;
|
||||
|
||||
SetType(type: AdvancementType): void {
|
||||
this.advType = type;
|
||||
}
|
||||
|
||||
GetAdvType(): AdvancementType | null {
|
||||
return this.advType;
|
||||
}
|
||||
|
||||
SetAdvancement(icon: string): void {
|
||||
this.advancement = icon;
|
||||
}
|
||||
|
||||
GetAdvancement(): string | null {
|
||||
return this.advancement;
|
||||
}
|
||||
|
||||
ClearAdvancement() {
|
||||
this.advancement = null;
|
||||
}
|
||||
|
||||
ClearState() {
|
||||
this.advancement = null;
|
||||
this.advType = null;
|
||||
}
|
||||
|
||||
}
|
||||
591
modules/UI/mythicplus/mythic_advancement.client.ts
Normal file
591
modules/UI/mythicplus/mythic_advancement.client.ts
Normal file
@@ -0,0 +1,591 @@
|
||||
/** @ts-expect-error */
|
||||
let aio: AIO = {};
|
||||
|
||||
const id = (name: string) => `MythicAdvUI_${name}`;
|
||||
|
||||
function GetComponent<T extends WoWAPI.Frame = WoWAPI.Frame>(name: string, type: string = "Frame"): T | null {
|
||||
const component = _G[id(name)];
|
||||
return component ? component as T : null;
|
||||
}
|
||||
|
||||
import { colors } from "../../classes/ui-utils";
|
||||
import { AdvancementState } from "./advancement_state";
|
||||
|
||||
/**
|
||||
* Advancement Name and Spell Reference
|
||||
*
|
||||
* ('80000001','spell_mp_titans_strength_aura'),
|
||||
* ('80000002','spell_mp_steel_forged_aura'),
|
||||
* ('80000003','spell_mp_celestial_grace_aura'),
|
||||
* ('80000004','spell_mp_forbidden_knowledge_aura'),
|
||||
* ('80000005','spell_mp_spectral_reflexes_aura'),
|
||||
* ('80000006','spell_mp_eldritch_barrier_aura'),
|
||||
* ('80000007','spell_mp_hellfire_shielding_aura'),
|
||||
* ('80000008','spell_mp_primal_endurance_aura'),
|
||||
* ('80000009','spell_mp_lichbane_aura'),
|
||||
* ('80000010','spell_mp_glacial_fortress_aura');
|
||||
*/
|
||||
|
||||
if (!aio.AddAddon()) {
|
||||
const upgradeUIHandlers = aio.AddHandlers("MythicAdvUI", {});
|
||||
const UpgradeUIFrames: Map<string, WoWAPI.Frame> = new Map();
|
||||
|
||||
// const itemSlots: Map<number, string> = new Map();
|
||||
// let selectedDice: WoWAPI.Button | null = null;
|
||||
// let rolling: boolean = false;
|
||||
|
||||
const mainWidth = 768;
|
||||
const mainHeight = 512;
|
||||
const centerOffset = mainWidth / 2;
|
||||
|
||||
// Load the advancement state based on the requested type for now just do magic type
|
||||
const advancementState = new AdvancementState();
|
||||
advancementState.SetType("Magic");
|
||||
|
||||
const customTextures = {
|
||||
// frame background elements
|
||||
"bgFrame": "Interface\\AddOns\\MythicPlusData\\Textures\\mythic-adv-frame",
|
||||
"bgFrameHq": "Interface\\AddOns\\MythicPlusData\\Textures\\mythic-adv-frame-hq",
|
||||
"darkBg": "Interface\\AddOns\\MythicPlusData\\Textures\\DialogBox-Background-Dark",
|
||||
"diceFrame": "Interface\\AddOns\\MythicPlusData\\Textures\\advancement_frame",
|
||||
|
||||
// advancement icons buttons
|
||||
"str": "Interface\\AddOns\\MythicPlusData\\Textures\\str_adv.tga",
|
||||
"agi": "Interface\\AddOns\\MythicPlusData\\Textures\\agi_adv.tga",
|
||||
"int": "Interface\\AddOns\\MythicPlusData\\Textures\\int_adv.tga",
|
||||
"spr": "Interface\\AddOns\\MythicPlusData\\Textures\\spr_adv.tga",
|
||||
"sta": "Interface\\AddOns\\MythicPlusData\\Textures\\sta_adv.tga",
|
||||
"icon_overlay": "Interface\\AddOns\\MythicPlusData\\Textures\\icon_overlay.tga",
|
||||
"int_selected": "Interface\\AddOns\\MythicPlusData\\Textures\\int_adv_selected.tga",
|
||||
"spr_selected": "Interface\\AddOns\\MythicPlusData\\Textures\\spr_adv_selected.tga",
|
||||
|
||||
// Dice buttons
|
||||
"single_roll": "Interface\\AddOns\\MythicPlusData\\Textures\\single_roll.tga",
|
||||
"double_roll": "Interface\\AddOns\\MythicPlusData\\Textures\\double_roll.tga",
|
||||
"triple_roll": "Interface\\AddOns\\MythicPlusData\\Textures\\triple_roll.tga",
|
||||
"single_roll_selected": "Interface\\AddOns\\MythicPlusData\\Textures\\single_roll_selected.tga",
|
||||
"double_roll_selected": "Interface\\AddOns\\MythicPlusData\\Textures\\double_roll_selected.tga",
|
||||
"triple_roll_selected": "Interface\\AddOns\\MythicPlusData\\Textures\\triple_roll_selected.tga",
|
||||
|
||||
// Advancement Bars
|
||||
"bars": "Interface\\AddOns\\MythicPlusData\\Textures\\adv_bars.tga",
|
||||
|
||||
// Roll Numbers
|
||||
"numbers": "Interface\\AddOns\\MythicPlusData\\Textures\\big_numbers.tga",
|
||||
}
|
||||
|
||||
interface ItemSlot extends WoWAPI.Button {
|
||||
hasItem?: boolean;
|
||||
itemLink?: string;
|
||||
}
|
||||
|
||||
function GetTexture(name: string): string {
|
||||
return customTextures[name] || "";
|
||||
}
|
||||
|
||||
function UpdateDiceVisibility(show: boolean): void {
|
||||
const buttons = ['single_roll', 'double_roll', 'triple_roll'];
|
||||
buttons.forEach(name => {
|
||||
const button = GetComponent<WoWAPI.Button>(`${name}_button`, "Button");
|
||||
if (button) {
|
||||
if (show) {
|
||||
button.SetAlpha(0.8);
|
||||
button.Enable();
|
||||
} else {
|
||||
button.SetAlpha(0.0);
|
||||
button.Disable();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function SelectAdvancement(name: string, button: WoWAPI.Button): void {
|
||||
|
||||
// if the button click is itself, reset it
|
||||
if (advancementState.GetAdvancement() === name) {
|
||||
button.SetNormalTexture(GetTexture(name));
|
||||
advancementState.SetAdvancement(null);
|
||||
UpdateDiceVisibility(false);
|
||||
} else {
|
||||
// Set the new advancement
|
||||
button.SetNormalTexture(GetTexture(`${name}_selected`));
|
||||
|
||||
// Handle opposing button
|
||||
const opposingStats = {
|
||||
'int': 'spr',
|
||||
'spr': 'int',
|
||||
'str': 'agi',
|
||||
'agi': 'str'
|
||||
};
|
||||
|
||||
const opposingStat = opposingStats[name];
|
||||
if (opposingStat) {
|
||||
const opposingButton = GetComponent<WoWAPI.Button>(`${opposingStat}_button`, "Button");
|
||||
if (opposingButton) {
|
||||
opposingButton.SetNormalTexture(GetTexture(opposingStat));
|
||||
}
|
||||
}
|
||||
|
||||
// Update advancement state and show dice buttons
|
||||
advancementState.SetAdvancement(name);
|
||||
UpdateDiceVisibility(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add icons icons used for advancement upgrades.
|
||||
* @param frame Main Frame
|
||||
*/
|
||||
function CreateAdvIcons(frame: WoWAPI.Frame): void {
|
||||
|
||||
const ICON_POS_LEFT = 38;
|
||||
const ICON_POS_TOP = -150;
|
||||
|
||||
let topIcon: string = "";
|
||||
let bottomIcon: string = "";
|
||||
let topText: string = "";
|
||||
let bottomText: string = "";
|
||||
|
||||
if (advancementState.GetAdvType() === "Magic") {
|
||||
topIcon = "spr";
|
||||
bottomIcon = "int";
|
||||
topText = "Celestial Grace";
|
||||
bottomText = "Forbidden Knowledge";
|
||||
}
|
||||
else if (advancementState.GetAdvType() === "Attack") {
|
||||
topIcon = "str";
|
||||
bottomIcon = "agi";
|
||||
topText = "Titan's Strength";
|
||||
bottomText = "Spectral Reflexes";
|
||||
}
|
||||
else if (advancementState.GetAdvType() === "Defense") {
|
||||
topIcon = "sta";
|
||||
topText = "Steel Forged"
|
||||
}
|
||||
|
||||
const topButton = CreateFrame("Button", id(`${topIcon}_button`), frame);
|
||||
topButton.SetSize(132, 132);
|
||||
topButton.SetPoint("TOPLEFT", ICON_POS_LEFT, ICON_POS_TOP);
|
||||
topButton.SetNormalTexture(GetTexture(topIcon));
|
||||
topButton.SetHighlightTexture(GetTexture("icon_overlay"));
|
||||
topButton.SetScript("OnClick", function(self: WoWAPI.Button) {
|
||||
SelectAdvancement(topIcon, self);
|
||||
});
|
||||
|
||||
topButton.Show();
|
||||
|
||||
const bottomButton = CreateFrame("Button", id(`${bottomIcon}_button`), frame);
|
||||
bottomButton.SetSize(132, 132);
|
||||
bottomButton.SetPoint("TOPLEFT", topButton, "BOTTOMLEFT", 0, -20);
|
||||
bottomButton.SetNormalTexture(GetTexture(bottomIcon));
|
||||
bottomButton.SetHighlightTexture(GetTexture("icon_overlay"));
|
||||
bottomButton.SetScript("OnClick", function(self: WoWAPI.Button) {
|
||||
SelectAdvancement(bottomIcon, self);
|
||||
});
|
||||
|
||||
bottomButton.Show();
|
||||
|
||||
// Add advancement bars
|
||||
const topbarTexture = frame.CreateTexture(id("topbar_gold"), "ARTWORK");
|
||||
topbarTexture.SetPoint("LEFT", topButton, "RIGHT", -4, -10);
|
||||
topbarTexture.SetSize(187, 37);
|
||||
topbarTexture.SetTexture(GetTexture("bars"));
|
||||
topbarTexture.SetTexCoord(36/256, 218/256, 0/256, 37/256);
|
||||
|
||||
const bottombarTexture = frame.CreateTexture(id("bottombar_gold"), "ARTWORK");
|
||||
bottombarTexture.SetPoint("LEFT", bottomButton, "RIGHT", -4, -10);
|
||||
bottombarTexture.SetSize(187, 37);
|
||||
bottombarTexture.SetTexture(GetTexture("bars"));
|
||||
bottombarTexture.SetTexCoord(36/256, 218/256, 0/256, 37/256);
|
||||
|
||||
const topbarText = frame.CreateFontString(id("topbar_text"), "OVERLAY");
|
||||
topbarText.SetPoint("CENTER", topbarTexture, "CENTER", 0, 33);
|
||||
topbarText.SetTextColor(1, 1, 1, 1) // White
|
||||
topbarText.SetFont("Fonts\\FRIZQT__.TTF", 16)
|
||||
topbarText.SetText(topText);
|
||||
|
||||
const bottombarText = frame.CreateFontString(id("bottombar_text"), "OVERLAY");
|
||||
bottombarText.SetPoint("CENTER", bottombarTexture, "CENTER", 0, 33);
|
||||
bottombarText.SetTextColor(1, 1, 1, 1) // White
|
||||
bottombarText.SetFont("Fonts\\FRIZQT__.TTF", 16)
|
||||
bottombarText.SetText(bottomText);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates the dice roll buttons that appear below the roll frame
|
||||
* @param frame Main Frame
|
||||
* @param rollFrame The frame containing the roll numbers
|
||||
*/
|
||||
function CreateDiceButtons(frame: WoWAPI.Frame, rollFrame: WoWAPI.Frame): void {
|
||||
const buttonWidth = 72;
|
||||
const buttonHeight = 72;
|
||||
const spacing = 20;
|
||||
const totalWidth = (buttonWidth * 3) + (spacing * 2);
|
||||
const startX = -(totalWidth / 2) + (buttonWidth / 2);
|
||||
|
||||
const buttons = [
|
||||
{ name: 'single_roll', rolls: 1 },
|
||||
{ name: 'double_roll', rolls: 2 },
|
||||
{ name: 'triple_roll', rolls: 3 }
|
||||
];
|
||||
|
||||
let selectedButton = GetComponent<WoWAPI.Button>("selected_dice_button", "Button");
|
||||
|
||||
buttons.forEach((buttonInfo, index) => {
|
||||
const buttonId = `${buttonInfo.name}_button`;
|
||||
let button = GetComponent<WoWAPI.Button>(buttonId, "Button");
|
||||
|
||||
if (!button) {
|
||||
button = CreateFrame("Button", id(buttonId), frame) as WoWAPI.Button;
|
||||
button.SetSize(buttonWidth, buttonHeight);
|
||||
button.SetPoint("TOP", rollFrame, "BOTTOM", startX + (index * (buttonWidth + spacing)), -75);
|
||||
|
||||
const normalTexture = button.CreateTexture(null, "BACKGROUND");
|
||||
normalTexture.SetTexture(GetTexture(buttonInfo.name));
|
||||
normalTexture.SetAllPoints(button);
|
||||
button.SetNormalTexture(normalTexture);
|
||||
|
||||
const highlightTexture = button.CreateTexture(null, "HIGHLIGHT");
|
||||
highlightTexture.SetTexture(GetTexture(`${buttonInfo.name}_selected`));
|
||||
highlightTexture.SetAllPoints(button);
|
||||
button.SetHighlightTexture(highlightTexture);
|
||||
|
||||
// Start hidden by default
|
||||
button.SetAlpha(0.0);
|
||||
button.SetScript("OnClick", function() {
|
||||
if (selectedButton) {
|
||||
const prevButtonInfo = buttons.find(b => b.name === selectedButton.GetName().split('_')[0]);
|
||||
if (prevButtonInfo) {
|
||||
selectedButton.SetNormalTexture(GetTexture(prevButtonInfo.name));
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedButton === button) {
|
||||
selectedButton = null;
|
||||
} else {
|
||||
selectedButton = button;
|
||||
button.SetNormalTexture(GetTexture(`${buttonInfo.name}_selected`));
|
||||
// Here you would typically trigger the roll with the number of dice
|
||||
frame["rollNumbers"](buttonInfo.rolls, 25 * buttonInfo.rolls);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Ensure buttons are hidden/shown based on current advancement state
|
||||
UpdateDiceVisibility(advancementState.GetAdvancement() !== null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add title text to the frame.
|
||||
* @param frame Main Frame
|
||||
*/
|
||||
function AddTitle(frame: WoWAPI.Frame): void {
|
||||
// Add title text
|
||||
const titleBar = frame.CreateFontString(id("TitleBar"), "OVERLAY");
|
||||
titleBar.SetPoint("TOP", -150, -40);
|
||||
titleBar.SetTextColor(1, 0.84, 0, 1) // Golden yellow
|
||||
titleBar.SetFont("Interface/Modules/MythicPlus/Fonts/NOTO.TTF", 16)
|
||||
titleBar.SetText(`Mythic Upgrades`);
|
||||
}
|
||||
|
||||
/**
|
||||
* This adds in the big numbers that will flash to simulate a dice rolls...
|
||||
* even though the dice roll actually happens on the server in the module.
|
||||
* @param frame
|
||||
*/
|
||||
function ShowRollNumbers(frame: WoWAPI.Frame): void {
|
||||
|
||||
let rollFrame = GetComponent("roll_frame");
|
||||
if (!rollFrame) {
|
||||
rollFrame = CreateFrame("Frame", id("roll_frame"), frame);
|
||||
rollFrame.SetSize(120, 120);
|
||||
rollFrame.SetPoint("CENTER", 200, -24);
|
||||
rollFrame.SetBackdrop({
|
||||
bgFile: GetTexture("darkBg"),
|
||||
tile: false,
|
||||
tileSize: 120,
|
||||
insets: { left: 1, right: 1, top: 1, bottom: 1 },
|
||||
});
|
||||
rollFrame.SetBackdropColor(0, 0, 0, 0.3); // RGB + alpha
|
||||
}
|
||||
rollFrame.Show();
|
||||
|
||||
// create a texture that will change the graphic every few seconds between 1-25 using big_number tga
|
||||
// for numbers greater than 9 it will need to add to smaller textures. Below are coordinates of the
|
||||
// big_number tga [UL, UR, LL, LR]
|
||||
|
||||
const coords: {[key: number]: number[]} = {
|
||||
0: [35, 96, 8, 81],
|
||||
1: [174, 210, 8, 81],
|
||||
2: [292, 345, 8, 81],
|
||||
3: [424, 472, 8, 81],
|
||||
4: [33, 95, 98, 163],
|
||||
5: [167, 217, 98, 163],
|
||||
6: [291, 346, 98, 163],
|
||||
7: [425, 471, 98, 163],
|
||||
8: [38, 89, 182, 247],
|
||||
9: [165, 218, 182, 247],
|
||||
}
|
||||
|
||||
let currentTextures: WoWAPI.Texture[] = [];
|
||||
|
||||
function clearTextures() {
|
||||
currentTextures.forEach(texture => {
|
||||
texture.Hide();
|
||||
texture.ClearAllPoints();
|
||||
});
|
||||
currentTextures = [];
|
||||
}
|
||||
|
||||
function createNumberTexture(digit: number, xOffset: number = 0) {
|
||||
const rollTexture = rollFrame.CreateTexture(null, "ARTWORK");
|
||||
rollTexture.SetSize(60, 60);
|
||||
rollTexture.SetPoint("CENTER", rollFrame, "CENTER", xOffset, 0);
|
||||
rollTexture.SetTexture(GetTexture("numbers"));
|
||||
|
||||
|
||||
if (coords[digit]) {
|
||||
rollTexture.SetTexCoord(
|
||||
coords[digit][0] / 512,
|
||||
coords[digit][1] / 512,
|
||||
coords[digit][2] / 256,
|
||||
coords[digit][3] / 256
|
||||
);
|
||||
}
|
||||
currentTextures.push(rollTexture);
|
||||
return rollTexture;
|
||||
}
|
||||
|
||||
function showNumber(num: number) {
|
||||
clearTextures();
|
||||
if (num >= 10) {
|
||||
const tens = Math.floor(num / 10);
|
||||
const ones = num % 10;
|
||||
createNumberTexture(tens, -30).Show();
|
||||
createNumberTexture(ones, 30).Show();
|
||||
} else {
|
||||
createNumberTexture(num, 0).Show(); // Explicitly set offset to 0 for single digits
|
||||
}
|
||||
}
|
||||
|
||||
let rolling = false;
|
||||
let lastUpdate = 0;
|
||||
|
||||
// Make rollNumbers accessible on the frame for external calls
|
||||
frame["rollNumbers"] = function(min: number, max: number, duration: number = 2000) {
|
||||
if (rolling) return;
|
||||
clearTextures();
|
||||
rolling = true;
|
||||
let elapsed = 0;
|
||||
|
||||
frame.SetScript("OnUpdate", function(_, deltaTime) {
|
||||
elapsed = elapsed + deltaTime * 1000;
|
||||
lastUpdate += deltaTime * 1000;
|
||||
|
||||
// Update number every 100ms
|
||||
if (lastUpdate >= 100) {
|
||||
lastUpdate = 0;
|
||||
const randomNum = Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
showNumber(randomNum);
|
||||
}
|
||||
|
||||
if (elapsed >= duration) {
|
||||
frame.SetScript("OnUpdate", null);
|
||||
rolling = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the main upgrade window frame.
|
||||
* @returns Main Frame
|
||||
*/
|
||||
function CreateUpgradeWindow(state: AdvancementState): WoWAPI.Frame {
|
||||
|
||||
// Main frame creation
|
||||
const frame = CreateFrame("Frame", id("main"), UIParent);
|
||||
frame.SetSize(mainWidth, mainHeight);
|
||||
frame.SetPoint("CENTER");
|
||||
frame.SetMovable(true);
|
||||
frame.EnableMouse(true);
|
||||
frame.RegisterForDrag("LeftButton");
|
||||
frame.SetScript("OnDragStart", frame.StartMoving);
|
||||
frame.SetScript("OnDragStop", frame.StopMovingOrSizing);
|
||||
frame.SetFrameLevel(100); // Set high frame level to stay on top
|
||||
|
||||
// Background texture
|
||||
const bgTexture = frame.CreateTexture(id("background"), "BACKGROUND");
|
||||
bgTexture.SetPoint('TOPLEFT');
|
||||
bgTexture.SetWidth(mainWidth);
|
||||
bgTexture.SetHeight(mainHeight);
|
||||
bgTexture.SetTexture(GetTexture("bgFrameHq"));
|
||||
bgTexture.SetTexCoord(0, 768/1024, 0, 1); // Show 75% of width (768/1024) and full height
|
||||
|
||||
// Add components
|
||||
CreateAdvIcons(frame);
|
||||
AddTitle(frame);
|
||||
ShowRollNumbers(frame);
|
||||
|
||||
|
||||
|
||||
const rollFrame = GetComponent("roll_frame");
|
||||
if (rollFrame) {
|
||||
CreateDiceButtons(frame, rollFrame);
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
// Remove the template's close button
|
||||
// const templateCloseButton = frame.GetChildren()[0] as WoWAPI.Frame;
|
||||
// if (templateCloseButton) {
|
||||
// templateCloseButton.Hide();
|
||||
// }
|
||||
|
||||
// Add our custom close button
|
||||
// const closeButton = CreateFrame("Button", id("CloseButton"), frame, "UIPanelCloseButton");
|
||||
// closeButton.SetPoint("TOPRIGHT", -5, -5);
|
||||
// closeButton.SetScript("OnClick", () => {
|
||||
// frame.Hide();
|
||||
// });
|
||||
|
||||
// Header Row: Skill Icons & Ranks
|
||||
// for (let i = 0; i < 3; i++) {
|
||||
// const skillFrame = CreateFrame("Frame", id(`Skill_${i}`), frame);
|
||||
// skillFrame.SetSize(64, 64);
|
||||
// skillFrame.SetPoint("TOP", -125 + i * 125, -55);
|
||||
|
||||
// const skillIcon = skillFrame.CreateTexture(id(`SkillIcon_${i}`), "ARTWORK");
|
||||
// skillIcon.SetTexture("Interface\\Icons\\INV_Misc_QuestionMark");
|
||||
// skillIcon.SetAllPoints();
|
||||
|
||||
// const skillName = skillFrame.CreateFontString(id(`SkillName_${i}`), "OVERLAY", "GameFontNormalLarge");
|
||||
// skillName.SetPoint("TOP", skillFrame, "BOTTOM", 0, -5);
|
||||
// skillName.SetText(`Skill ${i + 1}`);
|
||||
|
||||
// const skillRank = skillFrame.CreateFontString(id(`SkillRank_${i}`), "OVERLAY", "GameFontHighlightLarge");
|
||||
// skillRank.SetPoint("TOP", skillName, "BOTTOM", 0, -2);
|
||||
// skillRank.SetText("0 / 50");
|
||||
// }
|
||||
|
||||
// Add a new texture in the middle of the frame that is 512x512 and the middle layer
|
||||
// const centerTexture = frame.CreateTexture(id("DiceTextureBack"), "ARTWORK");
|
||||
// centerTexture.SetDrawLayer("ARTWORK", 3);
|
||||
// centerTexture.SetSize(128, 128);
|
||||
// centerTexture.SetPoint("CENTER", frame, "CENTER", 0, -20);
|
||||
// centerTexture.SetTexture("Interface\\MythicPlus\\adv-dice-light.blp");
|
||||
|
||||
// const circleTexture = frame.CreateTexture(id("DiceTextureBack"), "BACKGROUND");
|
||||
// circleTexture.SetDrawLayer("ARTWORK", 5);
|
||||
// circleTexture.SetSize(512, 512);
|
||||
// circleTexture.SetAlpha(0.20);
|
||||
// circleTexture.SetPoint("CENTER", frame, "CENTER", 0, -20);
|
||||
// circleTexture.SetTexture("Interface\\MythicPlus\\gold-circle.blp");
|
||||
|
||||
// // Add a glowing effect behind the dice
|
||||
// const glowTexture = frame.CreateTexture(id("DiceGlow"), "ARTWORK");
|
||||
// glowTexture.SetDrawLayer("ARTWORK", -2);
|
||||
// glowTexture.SetSize(300, 300);
|
||||
// glowTexture.SetPoint("CENTER", centerTexture, "CENTER", 0, 0);
|
||||
// glowTexture.SetTexture("Interface\\GLUES\\MODELS\\UI_MainMenu_Legion\\UI_MainMenu_Legion3");
|
||||
// glowTexture.SetAlpha(0.3);
|
||||
|
||||
// Roll Display with better font handling
|
||||
// const rollDisplay = frame.CreateFontString(id("RollDisplay"),"ARTWORK");
|
||||
// rollDisplay.SetFont("Interface\\MythicPlus\\NOTO.TTF", 256, "THICKOUTLINE");
|
||||
// rollDisplay.SetPoint("CENTER", frame, "CENTER", 0, -80);
|
||||
// rollDisplay.SetTextColor(1, 0.84, 0, 1); // More golden yellow color
|
||||
// rollDisplay.SetText("-");
|
||||
|
||||
|
||||
// Dice Multipliers with Selection Logic
|
||||
// const diceMultipliers = ["1x", "2x", "3x"];
|
||||
// for (let k = 0; k < diceMultipliers.length; k++) {
|
||||
// const diceButton = CreateFrame("Button", id(`Dice_${k}`), frame);
|
||||
// diceButton.SetSize(24, 24);
|
||||
// diceButton.SetPoint("TOPLEFT", 150 + (k * 30), -450);
|
||||
|
||||
// const diceText = diceButton.CreateFontString(id(`DiceText_${k}`), "OVERLAY", "GameFontHighlight");
|
||||
// diceText.SetPoint("CENTER", diceButton, "CENTER", 0, 0);
|
||||
// diceText.SetText(diceMultipliers[k]);
|
||||
|
||||
// diceButton.SetScript("OnClick", function() {
|
||||
// if (selectedDice) {
|
||||
// selectedDice.SetAlpha(1.0); // Reset previous selection
|
||||
// }
|
||||
// selectedDice = diceButton;
|
||||
// selectedDice.SetAlpha(1.5); // Glow effect
|
||||
// });
|
||||
// }
|
||||
|
||||
// Roll Button with animation logic
|
||||
// const rollButton = CreateFrame("Button", id("RollButton"), frame, "UIPanelButtonTemplate");
|
||||
// rollButton.SetSize(100, 30);
|
||||
// rollButton.SetPoint("BOTTOM", frame, "BOTTOM", 0, 50);
|
||||
// rollButton.SetText("Roll");
|
||||
|
||||
// function rollDice(min: number, max: number, duration: number = 5000) {
|
||||
// if (rolling) return;
|
||||
// rolling = true;
|
||||
// let elapsed = 0;
|
||||
// let lastUpdate = 0;
|
||||
|
||||
// frame.SetScript("OnUpdate", function(_, deltaTime) {
|
||||
// elapsed = elapsed + deltaTime * 5000;
|
||||
// lastUpdate += deltaTime * 1000;
|
||||
|
||||
// // Only update every 200ms to slow down the rolling animation
|
||||
// if (lastUpdate >= 100) {
|
||||
// lastUpdate = 0;
|
||||
// rollDisplay.SetText(`${Math.floor(Math.random() * (max - min + 1)) + min}`);
|
||||
// }
|
||||
|
||||
// if (elapsed >= duration) {
|
||||
// frame.SetScript("OnUpdate", null);
|
||||
// rolling = false;
|
||||
// // Set final roll
|
||||
// rollDisplay.SetText(`${Math.floor(Math.random() * (max - min + 1)) + min}`);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
// rollButton.SetScript("OnClick", function() {
|
||||
// rollDice(2, 23); // Default to 2-23 range for backward compatibility
|
||||
// });
|
||||
|
||||
// Roll History
|
||||
// const historyFrame = CreateFrame("Frame", id("HistoryFrame"), frame);
|
||||
// historyFrame.SetSize(380, 80);
|
||||
// historyFrame.SetPoint("BOTTOM", frame, "BOTTOM", 0, 10);
|
||||
|
||||
// const historyText = historyFrame.CreateFontString(id("HistoryText"), "OVERLAY", "GameFontHighlight");
|
||||
// historyText.SetPoint("TOPLEFT", historyFrame, "TOPLEFT", 10, -10);
|
||||
// historyText.SetText("Roll History:");
|
||||
|
||||
|
||||
let frame: WoWAPI.Frame | undefined = undefined;
|
||||
|
||||
if(!UpgradeUIFrames.has(advancementState.GetAdvType())) {
|
||||
// frame = CreateUpgradeWindow(advancementState);
|
||||
} else {
|
||||
// if we have the frame already show it
|
||||
// frame = UpgradeUIFrames.get(advancementState.GetAdvType());
|
||||
}
|
||||
|
||||
// For debugging purposes show the window
|
||||
//frame.Show();
|
||||
|
||||
// Triggered from ths server to be set up later
|
||||
upgradeUIHandlers.ShowUpgradeWindow = () => {
|
||||
// if (!UpgradeUIFrames.has(1)) {
|
||||
// CreateUpgradeWindow();
|
||||
// }
|
||||
// UpgradeUIFrames.get(1).Show();
|
||||
};
|
||||
}
|
||||
223
modules/UI/mythicplus/mythic_advancement.server.ts
Normal file
223
modules/UI/mythicplus/mythic_advancement.server.ts
Normal file
@@ -0,0 +1,223 @@
|
||||
/** @ts-expect-error */
|
||||
let aio: AIO = {};
|
||||
|
||||
const SCRIPT_NAME = 'UpgradeUI';
|
||||
import { Logger } from "../../classes/logger";
|
||||
const log = new Logger(SCRIPT_NAME);
|
||||
|
||||
// Flag to track if materials have been loaded
|
||||
let materialsLoaded = false;
|
||||
|
||||
type ItemType = {
|
||||
entry: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
type Materials = {
|
||||
name: string;
|
||||
items: Array<ItemType>
|
||||
}
|
||||
|
||||
const upgradeMaterials: Record<number, Materials> = {};
|
||||
|
||||
/**
|
||||
* Helper function to safely get items from a material
|
||||
*/
|
||||
const getItemsFromMaterial = (material: Materials): Array<ItemType> => {
|
||||
if (!material) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!material.items) {
|
||||
material.items = [];
|
||||
}
|
||||
|
||||
return material.items;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the logic for showing the upgrade UI when a player types .advanceme
|
||||
*/
|
||||
const ShowUpgradeUI: player_event_on_command = (event: number, player: Player, command: string): boolean => {
|
||||
if (command === "advanceme") {
|
||||
log.info(`Showing Upgrade UI for player: ${player.GetName()}`);
|
||||
aio.Handle(player, 'UpgradeUI', 'ShowUpgradeWindow');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a list of materials from the database that can be used for mythic plus advancement
|
||||
* @param event World startup event
|
||||
*/
|
||||
// const GetMaterialsList: eluna_event_on_lua_state_open = (event: number) => {
|
||||
|
||||
// const query = WorldDBQuery(`select materialId, entry, name from mp_material_types`);
|
||||
|
||||
// const categoryMap: Record<number, string> = {
|
||||
// 1: "Cloth",
|
||||
// 2: "Rare Cloth",
|
||||
// 3: "Plants",
|
||||
// 4: "Rare Plants",
|
||||
// 5: "Ore",
|
||||
// 6: "Rare Ore",
|
||||
// 7: "Leather",
|
||||
// 8: "Rare Leather",
|
||||
// 9: "Gems",
|
||||
// 10: "Rare Gems",
|
||||
// 11: "Enchanting",
|
||||
// 12: "Rare Enchanting",
|
||||
// 13: "Water Elements",
|
||||
// 14: "Rare Water Elements",
|
||||
// 15: "Fire Elements",
|
||||
// 16: "Rare Fire Elements",
|
||||
// 17: "Nature Elements",
|
||||
// 18: "Rare Earth Elements",
|
||||
// 19: "Shadow Elements",
|
||||
// 20: "Rare Shadow Elements",
|
||||
// 21: "Arcane Elements",
|
||||
// 22: "Rare Arcane Elements",
|
||||
// 23: "Veilstone",
|
||||
// };
|
||||
|
||||
// if (query) {
|
||||
// do {
|
||||
// const materialId = query.GetUInt32(0);
|
||||
// const entry = query.GetUInt32(1);
|
||||
// const categoryName = categoryMap[materialId] || "Unknown";
|
||||
|
||||
// // Initialize the material group if it doesn't exist
|
||||
// if (!upgradeMaterials[materialId]) {
|
||||
// upgradeMaterials[materialId] = {
|
||||
// name: categoryName,
|
||||
// items: []
|
||||
// };
|
||||
// }
|
||||
|
||||
// // Add the item to the material group
|
||||
// upgradeMaterials[materialId].items.push({
|
||||
// entry: entry,
|
||||
// name: query.GetString(2)
|
||||
// });
|
||||
|
||||
// } while (query.NextRow());
|
||||
// }
|
||||
|
||||
// log.info(`Loaded ${Object.keys(upgradeMaterials).length} material groups with items for mythic plus advancement`);
|
||||
// materialsLoaded = true;
|
||||
// };
|
||||
|
||||
// /**
|
||||
// * This will show all the counts for a player by from the upgradeMaterials object
|
||||
// */
|
||||
// function GetMaterials(this:void, player: Player) {
|
||||
|
||||
// // Create a readable string representation of the materials and their counts
|
||||
// let materialsString = 'Your Mythic+ Advancement Materials:\n';
|
||||
// log.info("Showing material counts");
|
||||
|
||||
// // Track total materials by category
|
||||
// const materialCounts: Record<number, number> = {};
|
||||
|
||||
// // Initialize counts for all categories
|
||||
// const materialIds = Object.keys(upgradeMaterials);
|
||||
// log.info(`Found ${materialIds.length} material groups`);
|
||||
|
||||
// // Initialize all material counts to 0
|
||||
// for (let i = 0; i < materialIds.length; i++) {
|
||||
// const materialId = materialIds[i];
|
||||
// materialCounts[materialId] = 0;
|
||||
// }
|
||||
|
||||
// // Process each material group
|
||||
// for (let i = 0; i < materialIds.length; i++) {
|
||||
// const materialId = materialIds[i];
|
||||
// log.info(`Processing material ID: ${materialId}`);
|
||||
|
||||
// // Get the material object and verify it exists
|
||||
// const material = upgradeMaterials[materialId];
|
||||
// if (!material) {
|
||||
// log.info(`Material with ID ${materialId} is undefined`);
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// // Get the material name
|
||||
// const materialName = material.name || "Unknown Material";
|
||||
// materialsString = materialsString + '\n' + materialName + ':\n';
|
||||
|
||||
// // Get items using our helper function to ensure it's not nil
|
||||
// const items = getItemsFromMaterial(material);
|
||||
// log.info(`Material ${materialName} has ${items.length} items`);
|
||||
|
||||
// if (items.length === 0) {
|
||||
// log.info(`No items found for material ${materialName}`);
|
||||
// materialsString = materialsString + ' No items found in this category\n';
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// // Process each item in the material category
|
||||
// let categoryTotal = 0;
|
||||
// for (let j = 0; j < items.length; j++) {
|
||||
// // Get the item and verify it exists
|
||||
// const item = items[j];
|
||||
// if (!item) {
|
||||
// log.info(`Item at index ${j} is undefined for material ${materialName}`);
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// // Get the item entry and name
|
||||
// const itemEntry = item.entry;
|
||||
// const itemName = item.name || "Unknown Item";
|
||||
|
||||
// log.info(`Checking item ${itemName} (${itemEntry})`);
|
||||
|
||||
// try {
|
||||
// // Get the item count
|
||||
// const count = player.GetItemCount(itemEntry, true); // true = include bank
|
||||
// log.info(`Player has ${count} of item ${itemName}`);
|
||||
|
||||
// // Add to category total
|
||||
// categoryTotal = categoryTotal + count;
|
||||
|
||||
// // Only show items the player has
|
||||
// if (count > 0) {
|
||||
// materialsString = materialsString + ' - ' + itemName + ': ' + count + '\n';
|
||||
// }
|
||||
// } catch (error) {
|
||||
// log.info(`Error getting count for item ${itemName}: ${error}`);
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Store the category total
|
||||
// materialCounts[materialId] = categoryTotal;
|
||||
|
||||
// // Add total for this category
|
||||
// materialsString = materialsString + ' Total ' + materialName + ': ' + categoryTotal + '\n';
|
||||
// }
|
||||
|
||||
// log.info("Initialized material counts");
|
||||
|
||||
// // Send the material counts to the player
|
||||
// player.SendBroadcastMessage(materialsString);
|
||||
|
||||
// // Log the material counts for debugging
|
||||
// log.info('Showed material counts for player: ' + player.GetName());
|
||||
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// const UpgradeHandlers = aio.AddHandlers("UpgradeUI", {
|
||||
// ShowMaterialCount
|
||||
// });
|
||||
|
||||
/**
|
||||
* Register the command event to listen for ".advanceme"
|
||||
*/
|
||||
RegisterPlayerEvent(PlayerEvents.PLAYER_EVENT_ON_COMMAND, (...args) => ShowUpgradeUI(...args));
|
||||
|
||||
/**
|
||||
* Get all the material types from the database
|
||||
* RegisterServerEvent(ServerEvents.ELUNA_EVENT_ON_LUA_STATE_OPEN, (...args) => GetMaterialsList(...args));
|
||||
*/
|
||||
|
||||
356
modules/UI/mythicplus/mythic_custom_spells.ts
Normal file
356
modules/UI/mythicplus/mythic_custom_spells.ts
Normal file
@@ -0,0 +1,356 @@
|
||||
/**
|
||||
* Custom Spells that have been implemented for Mythic Plus components
|
||||
*/
|
||||
import { GetFusedItemFromItemEntry, IsVeilstone, IsMythicFusionItem, IsRareFusionItem, GetFusedItemMaterialId } from "./mythic_items";
|
||||
import { Logger } from "../../classes/logger";
|
||||
|
||||
const log = new Logger("mythic_custom_spells");
|
||||
const MAX_STACK_SIZE = 200;
|
||||
|
||||
/**
|
||||
* Spells for combining old materials
|
||||
* 150000 - Ore Fusion
|
||||
* 150001 - Cloth Fusion
|
||||
* 150002 - Leather Fusion
|
||||
* 150003 - Alchemy Fusion
|
||||
* 150004 - Gem Fusion
|
||||
* 150005 - Essence Fusion
|
||||
* 150006 - Cold Fusion
|
||||
* 150007 - Flame Fusion
|
||||
* 150008 - Arcane Fusion
|
||||
* 150009 - Dark Fusion
|
||||
* 150010 - Earth Fusion
|
||||
*/
|
||||
|
||||
// Spells for combining old materials
|
||||
export const SPELL_ORE_FUSION = 150000;
|
||||
export const SPELL_CLOTH_FUSION = 150001;
|
||||
export const SPELL_LEATHER_FUSION = 150002;
|
||||
export const SPELL_ALCHEMY_FUSION = 150003;
|
||||
export const SPELL_GEM_FUSION = 150004;
|
||||
export const SPELL_ESSENCE_FUSION = 150005;
|
||||
export const SPELL_COLD_FUSION = 150006;
|
||||
export const SPELL_FLAME_FUSION = 150007;
|
||||
export const SPELL_ARCANE_FUSION = 150008;
|
||||
export const SPELL_DARK_FUSION = 150009;
|
||||
export const SPELL_EARTH_FUSION = 150010;
|
||||
export const SPELL_ORE_FUSION_RANK_2 = 150011;
|
||||
export const SPELL_CLOTH_FUSION_RANK_2 = 150012;
|
||||
export const SPELL_LEATHER_FUSION_RANK_2 = 150013;
|
||||
export const SPELL_ALCHEMY_FUSION_RANK_2 = 150014;
|
||||
export const SPELL_GEM_FUSION_RANK_2 = 150015;
|
||||
export const SPELL_ESSENCE_FUSION_RANK_2 = 150016;
|
||||
export const SPELL_COLD_FUSION_RANK_2 = 150017;
|
||||
export const SPELL_FLAME_FUSION_RANK_2 = 150018;
|
||||
export const SPELL_ARCANE_FUSION_RANK_2 = 150019;
|
||||
export const SPELL_DARK_FUSION_RANK_2 = 150020;
|
||||
export const SPELL_EARTH_FUSION_RANK_2 = 150021;
|
||||
|
||||
|
||||
export const FUSION_SPELLS_RANK_1 = [150000, 150001, 150002, 150003, 150004, 150005, 150006, 150007, 150008, 150009, 150010];
|
||||
export const FUSION_SPELLS_RANK_2 = [150011, 150012, 150013, 150014, 150015, 150016, 150017, 150018, 150019, 150020, 150021];
|
||||
|
||||
/**
|
||||
* Stat Advancement Spells
|
||||
* 1. 80000001 - Titans' Strength Aura
|
||||
* 2. 80000002 - Steel Forged Aura
|
||||
* 3. 80000003 - Celestial Grace Aura
|
||||
* 4. 80000004 - Forbidden Knowledge Aura
|
||||
* 5. 80000005 - Spectral Reflexes Aura
|
||||
* 6. 80000006 - Eldritch Barrier Aura
|
||||
* 7. 80000007 - Hellfire Shielding Aura
|
||||
* 8. 80000008 - Primal Endurance Aura
|
||||
* 9. 80000009 - Lichbane Aura
|
||||
* 10. 80000010 - Glacial Fortress Aura
|
||||
*/
|
||||
|
||||
// Stat Advancement Spells
|
||||
export enum StatAdvancementSpell {
|
||||
TITANS_STRENGTH_AURA = 80000001,
|
||||
STEEL_FORGED_AURA = 80000002,
|
||||
CELESTIAL_GRACE_AURA = 80000003,
|
||||
FORBIDDEN_KNOWLEDGE_AURA = 80000004,
|
||||
SPECTRAL_REFLEXES_AURA = 80000005,
|
||||
ELDRITCH_BARRIER_AURA = 80000006,
|
||||
HELLFIRE_SHIELDING_AURA = 80000007,
|
||||
PRIMAL_ENDURANCE_AURA = 80000008,
|
||||
LICHBANE_AURA = 80000009,
|
||||
GLACIAL_FORTRESS_AURA = 80000010,
|
||||
}
|
||||
|
||||
export const STAT_ADVANCEMENT_SPELLS = {
|
||||
TITANS_STRENGTH_AURA: StatAdvancementSpell.TITANS_STRENGTH_AURA,
|
||||
STEEL_FORGED_AURA: StatAdvancementSpell.STEEL_FORGED_AURA,
|
||||
CELESTIAL_GRACE_AURA: StatAdvancementSpell.CELESTIAL_GRACE_AURA,
|
||||
FORBIDDEN_KNOWLEDGE_AURA: StatAdvancementSpell.FORBIDDEN_KNOWLEDGE_AURA,
|
||||
SPECTRAL_REFLEXES_AURA: StatAdvancementSpell.SPECTRAL_REFLEXES_AURA,
|
||||
ELDRITCH_BARRIER_AURA: StatAdvancementSpell.ELDRITCH_BARRIER_AURA,
|
||||
HELLFIRE_SHIELDING_AURA: StatAdvancementSpell.HELLFIRE_SHIELDING_AURA,
|
||||
PRIMAL_ENDURANCE_AURA: StatAdvancementSpell.PRIMAL_ENDURANCE_AURA,
|
||||
LICHBANE_AURA: StatAdvancementSpell.LICHBANE_AURA,
|
||||
GLACIAL_FORTRESS_AURA: StatAdvancementSpell.GLACIAL_FORTRESS_AURA,
|
||||
};
|
||||
|
||||
const RARE_ITEM_REQUIREMENT = 20;
|
||||
const MYTHIC_ITEM_REQUIREMENT = 5;
|
||||
|
||||
// Get the total number of items in the stack for the given item
|
||||
function GetTotalStackCount(item: Item): number {
|
||||
if (!item) return 0;
|
||||
return item.GetCount ? item.GetCount() : 0;
|
||||
}
|
||||
|
||||
const playerAngerCount: Record<number, number> = {};
|
||||
|
||||
const MythicMaterialFusion: player_event_on_spell_cast = (event: number, player: Player, spell: Spell, skipCheck: boolean) => {
|
||||
if(!FUSION_SPELLS_RANK_1.includes(spell.GetEntry()) && !FUSION_SPELLS_RANK_2.includes(spell.GetEntry())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const rank = FUSION_SPELLS_RANK_1.includes(spell.GetEntry()) ? 1 : 2;
|
||||
const target = spell.GetTarget() as Item;
|
||||
log.info(`Item Details ${target.GetName()}`);
|
||||
const hasItem = player.HasItem(target.GetEntry());
|
||||
|
||||
if(!hasItem) {
|
||||
log.info("Did not have an item target");
|
||||
return false;
|
||||
}
|
||||
|
||||
const fusedItemEntry = GetFusedItemFromItemEntry(target.GetEntry());
|
||||
|
||||
log.info(`Fused Item Entry ${fusedItemEntry}`);
|
||||
if(fusedItemEntry == 0) {
|
||||
player.SendBroadcastMessage("You can not cast fuse this material");
|
||||
player.PlayDirectSound(847);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(IsVeilstone(target.GetEntry())) {
|
||||
PrintInfo(`Veilstone detected`);
|
||||
const angerCount = (playerAngerCount[player.GetGUIDLow()] || 0) + 1;
|
||||
playerAngerCount[player.GetGUIDLow()] = angerCount;
|
||||
if(angerCount === 1) {
|
||||
player.SendBroadcastMessage("You are attempting to do something that is forbidden, do no not do it again.");
|
||||
player.PlayDirectSound(847);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(angerCount == 2) {
|
||||
player.SendBroadcastMessage("You are about to anger the gods, stop yourself before you cause a rift in the fabric of the world!!!");
|
||||
player.PlayDirectSound(847);
|
||||
return false
|
||||
}
|
||||
|
||||
if(angerCount >= 3) {
|
||||
player.Teleport(0, -11139.2, -1742.44, -29.7367, 0);
|
||||
player.SendNotification("YOU HAVE ATTEMPTED TO CORRUPT THE VEIL AND HAVE ANGERED THE GODS. YOU HAVE BEEN SENTENCED TO PURGATORY!");
|
||||
player.RemoveItem(target, 1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// make sure the correct spell is mapping to the correct material type
|
||||
// @see CategoryMapToFused
|
||||
const materialId = GetFusedItemMaterialId(target.GetEntry());
|
||||
if(materialId == 0) {
|
||||
PrintInfo(`No material id found for fused item entry: ${fusedItemEntry}`);
|
||||
return false;
|
||||
}
|
||||
if(materialId === 1 && spell.GetEntry() !== SPELL_CLOTH_FUSION && spell.GetEntry() !== SPELL_CLOTH_FUSION_RANK_2) {
|
||||
player.PlayDirectSound(847);
|
||||
return false;
|
||||
}
|
||||
if(materialId === 2 && spell.GetEntry()!== SPELL_CLOTH_FUSION_RANK_2) {
|
||||
player.PlayDirectSound(847);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(materialId === 3 && spell.GetEntry() !== SPELL_ALCHEMY_FUSION && spell.GetEntry() !== SPELL_ALCHEMY_FUSION_RANK_2) {
|
||||
player.PlayDirectSound(847);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(materialId === 4 && spell.GetEntry() !== SPELL_ALCHEMY_FUSION_RANK_2) {
|
||||
player.PlayDirectSound(847);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(materialId === 5 && spell.GetEntry() !== SPELL_ORE_FUSION && spell.GetEntry() !== SPELL_ORE_FUSION_RANK_2) {
|
||||
player.PlayDirectSound(847);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(materialId === 6 && spell.GetEntry() !== SPELL_ORE_FUSION_RANK_2) {
|
||||
player.PlayDirectSound(847);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(materialId === 7 && spell.GetEntry() !== SPELL_LEATHER_FUSION && spell.GetEntry() !== SPELL_LEATHER_FUSION_RANK_2) {
|
||||
player.PlayDirectSound(847);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(materialId === 8 && spell.GetEntry() !== SPELL_LEATHER_FUSION_RANK_2) {
|
||||
player.PlayDirectSound(847);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(materialId === 9 && spell.GetEntry() !== SPELL_GEM_FUSION && spell.GetEntry() !== SPELL_GEM_FUSION_RANK_2) {
|
||||
player.PlayDirectSound(847);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(materialId === 10 && spell.GetEntry() !== SPELL_GEM_FUSION_RANK_2) {
|
||||
player.PlayDirectSound(847);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(materialId === 11 && spell.GetEntry() !== SPELL_ESSENCE_FUSION && spell.GetEntry() !== SPELL_ESSENCE_FUSION_RANK_2) {
|
||||
player.PlayDirectSound(847);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(materialId === 12 && spell.GetEntry() !== SPELL_ESSENCE_FUSION_RANK_2) {
|
||||
player.PlayDirectSound(847);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(materialId === 13 && spell.GetEntry() !== SPELL_COLD_FUSION && spell.GetEntry() !== SPELL_COLD_FUSION_RANK_2) {
|
||||
player.PlayDirectSound(847);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(materialId === 14 && spell.GetEntry() !== SPELL_COLD_FUSION_RANK_2) {
|
||||
player.PlayDirectSound(847);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(materialId === 15 && spell.GetEntry() !== SPELL_FLAME_FUSION && spell.GetEntry() !== SPELL_FLAME_FUSION_RANK_2) {
|
||||
player.PlayDirectSound(847);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(materialId === 16 && spell.GetEntry() !== SPELL_FLAME_FUSION_RANK_2) {
|
||||
player.PlayDirectSound(847);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(materialId === 17 && spell.GetEntry() !== SPELL_ARCANE_FUSION && spell.GetEntry() !== SPELL_ARCANE_FUSION_RANK_2) {
|
||||
player.PlayDirectSound(847);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(materialId === 18 && spell.GetEntry() !== SPELL_ARCANE_FUSION_RANK_2) {
|
||||
player.PlayDirectSound(847);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(materialId === 19 && spell.GetEntry() !== SPELL_DARK_FUSION && spell.GetEntry() !== SPELL_DARK_FUSION_RANK_2) {
|
||||
player.PlayDirectSound(847);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(materialId === 20 && spell.GetEntry() !== SPELL_DARK_FUSION_RANK_2) {
|
||||
player.PlayDirectSound(847);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(materialId === 21 && spell.GetEntry() !== SPELL_EARTH_FUSION && spell.GetEntry() !== SPELL_EARTH_FUSION_RANK_2) {
|
||||
player.PlayDirectSound(847);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(materialId === 22 && spell.GetEntry() !== SPELL_EARTH_FUSION_RANK_2) {
|
||||
player.PlayDirectSound(847);
|
||||
return false;
|
||||
}
|
||||
|
||||
PrintInfo(`materialId: ${materialId} and spell: ${spell.GetEntry()}`);
|
||||
|
||||
if(IsMythicFusionItem(fusedItemEntry)) {
|
||||
if(target.GetCount() < MYTHIC_ITEM_REQUIREMENT) {
|
||||
player.SendBroadcastMessage("You do not have enough materials to apply fusion.");
|
||||
player.PlayDirectSound(847);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
log.info(`Fusing ${target.GetName()}`);
|
||||
|
||||
const random = Math.floor(Math.random() * 3) + 1;
|
||||
player.PlayDirectSound(10720);
|
||||
//player.AddItem(fusedItemEntry, random);
|
||||
target.SetCount(target.GetCount() - MYTHIC_ITEM_REQUIREMENT);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(IsRareFusionItem(fusedItemEntry)) {
|
||||
if(target.GetCount() < RARE_ITEM_REQUIREMENT) {
|
||||
player.SendBroadcastMessage("You do not have enough materials to apply fusion");
|
||||
player.PlayDistanceSound(847);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the target is a max stack then apply it to the entire stack for QoL reasons
|
||||
let totalFused = 0;
|
||||
let removeCount = 0;
|
||||
if(target.GetCount() === MAX_STACK_SIZE) {
|
||||
for(let i = 0; i < 10; i++) {
|
||||
let random = Math.floor(Math.random() * 3) + 1;
|
||||
totalFused += random;
|
||||
}
|
||||
removeCount = MAX_STACK_SIZE;
|
||||
player.RemoveItem(target, MAX_STACK_SIZE);
|
||||
} else {
|
||||
totalFused = Math.floor(Math.random() * 3) + 1;
|
||||
removeCount = RARE_ITEM_REQUIREMENT;
|
||||
target.SetCount(target.GetCount() - RARE_ITEM_REQUIREMENT);
|
||||
player.SaveToDB();
|
||||
player.RemoveItem(target, 1);
|
||||
}
|
||||
|
||||
player.PlayDirectSound(12334);
|
||||
player.AddItem(fusedItemEntry, totalFused);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// // Get the item
|
||||
// const stackCount = GetTotalStackCount(item);
|
||||
|
||||
// // Determine if this item can be fused
|
||||
// // const fusedItemEntry = GetFusedItemFromItemEntry(itemEntry);
|
||||
// if (!fusedItemEntry || fusedItemEntry === 0) {
|
||||
// player.SendBroadcastMessage("This item cannot be fused.");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// // Calculate number of batches (every 20 items)
|
||||
// const batchSize = 20;
|
||||
// const numBatches = Math.floor(stackCount / batchSize);
|
||||
// if (numBatches === 0) {
|
||||
// player.SendBroadcastMessage(`You need at least ${batchSize} items to fuse.`);
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// // Remove items from player's inventory
|
||||
// item.Remove(batchSize * numBatches);
|
||||
|
||||
// // For each batch, roll 1-3 and give that many fused items
|
||||
// let totalFused = 0;
|
||||
// for (let i = 0; i < numBatches; i++) {
|
||||
// const amount = 1 + Math.floor(Math.random() * 3); // 1-3
|
||||
// player.AddItem(fusedItemEntry, amount);
|
||||
// totalFused += amount;
|
||||
// }
|
||||
|
||||
// player.SendBroadcastMessage(
|
||||
// `You fused ${numBatches * batchSize} items into ${totalFused} rare materials!`
|
||||
// );
|
||||
|
||||
// Register
|
||||
RegisterPlayerEvent(PlayerEvents.PLAYER_EVENT_ON_SPELL_CAST, (...args) => MythicMaterialFusion(...args));
|
||||
172
modules/UI/mythicplus/mythic_items.ts
Normal file
172
modules/UI/mythicplus/mythic_items.ts
Normal file
@@ -0,0 +1,172 @@
|
||||
// custom items for advancement
|
||||
export const ANCIENT_DICE = 911000;
|
||||
export const ONYX_SPIKE_RELIC = 911001;
|
||||
export const VEILSTONE = 911002;
|
||||
export const FUSED_RARE_ORE = 911003;
|
||||
export const FUSED_MYTHIC_ORE = 911004;
|
||||
export const FUSED_RARE_CLOTH = 911005;
|
||||
export const FUSED_MYTHIC_CLOTH = 911006;
|
||||
export const FUSED_RARE_LEATHER = 911007;
|
||||
export const FUSED_MYTHIC_LEATHER = 911008;
|
||||
export const FUSED_RARE_ALCHEMY = 911009;
|
||||
export const FUSED_MYTHIC_ALCHEMY = 911010;
|
||||
export const FUSED_RARE_GEM = 911011;
|
||||
export const FUSED_MYTHIC_GEM = 911012;
|
||||
export const FUSED_RARE_ESSENCE = 911013;
|
||||
export const FUSED_MYTHIC_ESSENCE = 911014;
|
||||
export const FUSED_RARE_ICE_STONE = 911015;
|
||||
export const FUSED_MYTHIC_ICE_STONE = 911016;
|
||||
export const FUSED_RARE_INFERNAL_STONE = 911017;
|
||||
export const FUSED_MYTHIC_INFERNAL_STONE = 911018;
|
||||
export const FUSED_RARE_ARCANE_CRYSTAL = 911019;
|
||||
export const FUSED_MYTHIC_ARCANE_CRYSTAL = 911020;
|
||||
export const FUSED_RARE_DARK_CRYSTAL = 911021;
|
||||
export const FUSED_MYTHIC_DARK_CRYSTAL = 911022;
|
||||
export const FUSED_RARE_EARTH_STONE = 911023;
|
||||
export const FUSED_MYTHIC_EARTH_STONE = 911024;
|
||||
|
||||
export const MYTHIC_MATERIALS = {
|
||||
ANCIENT_DICE,
|
||||
ONYX_SPIKE_RELIC,
|
||||
VEILSTONE,
|
||||
FUSED_RARE_ORE,
|
||||
FUSED_MYTHIC_ORE,
|
||||
FUSED_RARE_CLOTH,
|
||||
FUSED_MYTHIC_CLOTH,
|
||||
FUSED_RARE_LEATHER,
|
||||
FUSED_MYTHIC_LEATHER,
|
||||
FUSED_RARE_ALCHEMY,
|
||||
FUSED_MYTHIC_ALCHEMY,
|
||||
FUSED_RARE_GEM,
|
||||
FUSED_MYTHIC_GEM,
|
||||
FUSED_RARE_ESSENCE,
|
||||
FUSED_MYTHIC_ESSENCE,
|
||||
FUSED_RARE_ICE_STONE,
|
||||
FUSED_MYTHIC_ICE_STONE,
|
||||
FUSED_RARE_INFERNAL_STONE,
|
||||
FUSED_MYTHIC_INFERNAL_STONE,
|
||||
FUSED_RARE_ARCANE_CRYSTAL,
|
||||
FUSED_MYTHIC_ARCANE_CRYSTAL,
|
||||
FUSED_RARE_DARK_CRYSTAL,
|
||||
FUSED_MYTHIC_DARK_CRYSTAL,
|
||||
FUSED_RARE_EARTH_STONE,
|
||||
FUSED_MYTHIC_EARTH_STONE
|
||||
};
|
||||
|
||||
export const CategoryMapToFused: Record<number, number> = {
|
||||
1: FUSED_RARE_CLOTH,
|
||||
2: FUSED_MYTHIC_CLOTH,
|
||||
3: FUSED_RARE_ALCHEMY,
|
||||
4: FUSED_MYTHIC_ALCHEMY,
|
||||
5: FUSED_RARE_ORE,
|
||||
6: FUSED_MYTHIC_ORE,
|
||||
7: FUSED_RARE_LEATHER,
|
||||
8: FUSED_MYTHIC_LEATHER,
|
||||
9: FUSED_RARE_GEM,
|
||||
10: FUSED_MYTHIC_GEM,
|
||||
11: FUSED_RARE_ESSENCE,
|
||||
12: FUSED_MYTHIC_ESSENCE,
|
||||
13: FUSED_RARE_ICE_STONE,
|
||||
14: FUSED_MYTHIC_ICE_STONE,
|
||||
15: FUSED_RARE_INFERNAL_STONE,
|
||||
16: FUSED_MYTHIC_INFERNAL_STONE,
|
||||
17: FUSED_RARE_ARCANE_CRYSTAL,
|
||||
18: FUSED_MYTHIC_ARCANE_CRYSTAL,
|
||||
19: FUSED_RARE_DARK_CRYSTAL,
|
||||
20: FUSED_MYTHIC_DARK_CRYSTAL,
|
||||
21: FUSED_RARE_EARTH_STONE,
|
||||
22: FUSED_MYTHIC_EARTH_STONE
|
||||
};
|
||||
|
||||
export const RARE_FUSED_ITEMS: number[] = [
|
||||
FUSED_RARE_ORE,
|
||||
FUSED_RARE_CLOTH,
|
||||
FUSED_RARE_LEATHER,
|
||||
FUSED_RARE_ALCHEMY,
|
||||
FUSED_RARE_GEM,
|
||||
FUSED_RARE_ESSENCE,
|
||||
FUSED_RARE_ICE_STONE,
|
||||
FUSED_RARE_INFERNAL_STONE,
|
||||
FUSED_RARE_ARCANE_CRYSTAL,
|
||||
FUSED_RARE_DARK_CRYSTAL,
|
||||
FUSED_RARE_EARTH_STONE
|
||||
];
|
||||
|
||||
export const MYTHIC_FUSED_ITEMS: number[] = [
|
||||
FUSED_MYTHIC_ORE,
|
||||
FUSED_MYTHIC_CLOTH,
|
||||
FUSED_MYTHIC_LEATHER,
|
||||
FUSED_MYTHIC_ALCHEMY,
|
||||
FUSED_MYTHIC_GEM,
|
||||
FUSED_MYTHIC_ESSENCE,
|
||||
FUSED_MYTHIC_ICE_STONE,
|
||||
FUSED_MYTHIC_INFERNAL_STONE,
|
||||
FUSED_MYTHIC_ARCANE_CRYSTAL,
|
||||
FUSED_MYTHIC_DARK_CRYSTAL,
|
||||
FUSED_MYTHIC_EARTH_STONE
|
||||
];
|
||||
|
||||
// Populated after load with items mapped to fused counterparts.
|
||||
// key is the material id, value is the item entries that can be used in fusion
|
||||
export const FusedToItemMap: Record<number, number[]> = {};
|
||||
|
||||
// create a lookup of item ids from the database to the fused items as the items in each category will
|
||||
// produce that fused item when the fusion spell is cast on it.
|
||||
const CreateItemToFusedMap: eluna_event_on_lua_state_open = (event: number) => {
|
||||
const query = WorldDBQuery(`select materialId, entry, name from mp_material_types`);
|
||||
if (query) {
|
||||
do {
|
||||
const materialId = query.GetUInt32(0);
|
||||
const entry = query.GetUInt32(1);
|
||||
|
||||
// Initialize the material group if it doesn't exist
|
||||
if (!FusedToItemMap[materialId]) {
|
||||
FusedToItemMap[materialId] = [entry];
|
||||
} else {
|
||||
FusedToItemMap[materialId].push(entry);
|
||||
}
|
||||
|
||||
} while (query.NextRow());
|
||||
}
|
||||
|
||||
PrintInfo(`Loaded ${Object.keys(FusedToItemMap).length} material groups and mapped to fused items`);
|
||||
|
||||
}
|
||||
RegisterServerEvent(ServerEvents.ELUNA_EVENT_ON_LUA_STATE_OPEN, (...args) => CreateItemToFusedMap(...args));
|
||||
|
||||
export function GetFusedItemFromItemEntry(itemEntry: number): number {
|
||||
for (const [materialId, entries] of Object.entries(FusedToItemMap)) {
|
||||
if (entries.includes(itemEntry)) {
|
||||
return CategoryMapToFused[Number(materialId)];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get the category of the fused item
|
||||
export function GetFusedItemMaterialId(itemEntry: number): number {
|
||||
for (const [materialId, entries] of Object.entries(FusedToItemMap)) {
|
||||
if (entries.includes(itemEntry)) {
|
||||
return Number(materialId);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function IsRareFusionItem(fusedItemEntry: number): boolean {
|
||||
|
||||
PrintInfo(`looking up ${fusedItemEntry}`);
|
||||
return (RARE_FUSED_ITEMS.includes(fusedItemEntry)) ? true : false;
|
||||
}
|
||||
|
||||
export function IsMythicFusionItem(fusedItemEntry: number): boolean {
|
||||
return (MYTHIC_FUSED_ITEMS.includes(fusedItemEntry)) ? true : false;
|
||||
}
|
||||
|
||||
export function IsVeilstone(itemEntry: number): boolean {
|
||||
if(itemEntry == VEILSTONE) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
444
modules/UI/mythicplus/mythic_npcs.ts
Normal file
444
modules/UI/mythicplus/mythic_npcs.ts
Normal file
@@ -0,0 +1,444 @@
|
||||
/** @ts-expect-error */
|
||||
let aio: AIO = {};
|
||||
|
||||
import { MapNames, MapIds, BossIDs } from "../../classes/mapzones";
|
||||
import * as Spells from "./mythic_custom_spells";
|
||||
/**
|
||||
* This is the file for managing new NPC interactions
|
||||
* - Mick Ashwild - 9500561 teaches leather/fire fusions
|
||||
* - Thorin Firehand - 9500562 teaches ore/cold fusions
|
||||
* - Elowyn Threadbinder - 9500563 teaches cloth/arcane fusions
|
||||
* - Shivey - 9500564 teaches alchemy/nature fusions
|
||||
* - ??? - 9500565 teaches gem/essence fusions
|
||||
* - ??? - Old Witch teaches shadow fusion
|
||||
*
|
||||
*/
|
||||
|
||||
export enum NPCType {
|
||||
MICK_ASHWILD = 'mick',
|
||||
THORIN_FIREHAND = 'thorin',
|
||||
ELOWYN_THREADBINDER = 'elowyn',
|
||||
SHIVEY = 'shivey',
|
||||
OLD_WITCH = 'old_witch'
|
||||
}
|
||||
|
||||
export const NPCIds: Record<NPCType, number> = {
|
||||
[NPCType.MICK_ASHWILD]: 9500561,
|
||||
[NPCType.THORIN_FIREHAND]: 9500562,
|
||||
[NPCType.ELOWYN_THREADBINDER]: 9500563,
|
||||
[NPCType.SHIVEY]: 9500564,
|
||||
[NPCType.OLD_WITCH]: 9500565
|
||||
};
|
||||
|
||||
export enum GobjectType {
|
||||
PORTAL = 'portal',
|
||||
|
||||
}
|
||||
|
||||
export const GobjectIds: Record<GobjectType, number> = {
|
||||
[GobjectType.PORTAL]: 181508
|
||||
}
|
||||
|
||||
const AUDIO_BASE_PATH = "Interface\\Modules\\MythicPlus\\Audio\\";
|
||||
|
||||
export const AudioPaths: Record<NPCType, string> = {
|
||||
[NPCType.MICK_ASHWILD]: AUDIO_BASE_PATH + "Mick\\mick-",
|
||||
[NPCType.THORIN_FIREHAND]: AUDIO_BASE_PATH + "Thorin\\thorin-",
|
||||
[NPCType.ELOWYN_THREADBINDER]: AUDIO_BASE_PATH + "Elowyn\\elowyn-",
|
||||
[NPCType.SHIVEY]: AUDIO_BASE_PATH + "Shivey\\shivey-",
|
||||
[NPCType.OLD_WITCH]: AUDIO_BASE_PATH + "OldWitch\\old-witch-"
|
||||
};
|
||||
|
||||
function getAudioFile(npcName: NPCType, file: string): string {
|
||||
return AudioPaths[npcName] + file + ".mp3";
|
||||
}
|
||||
|
||||
function isInMythicPlus(playerId: number, instanceId: number): boolean {
|
||||
const result = CharDBQuery("select guid, instanceId, difficulty from mp_player_instance_data where guid = " + playerId + " and instanceId = " + instanceId);
|
||||
if(result && result.GetUInt32(2) >= 3) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* the player will meet different characters at different times so the hello will check to see whch
|
||||
* audio file and gossip menu needs to be shown. The list of locations an npc will show up is below
|
||||
*
|
||||
* Mick
|
||||
* Rare Leather Fusion : Wailing Caverns Verdan the everliving
|
||||
* Mythic Leather Fusion : Sartharion Obsidian Sanctum
|
||||
* Rare Fire Fusion : Hellfire Ramparts Nazan + Vazruden
|
||||
* Mythic Fire Fusion : Onyxia Lair Flamegor
|
||||
* Casual Spawns : World ends Tavern Shatrath
|
||||
*
|
||||
* Thorin
|
||||
* Rare Ore Fusion : BRD Magmus
|
||||
* Mythic Ore Fusion : Ragnaros
|
||||
* Casual Spawns : The Great Forge
|
||||
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
// Track NPC dialog animation state
|
||||
interface ActiveNpcState {
|
||||
time: number;
|
||||
intro: boolean;
|
||||
audioActive: boolean;
|
||||
players?: string[]; // list of players nearby when the NPC spawns
|
||||
lastEmote?: number;
|
||||
outro?: string[]; // list of players that have heard the outro
|
||||
}
|
||||
|
||||
interface PlayedAudioState {
|
||||
playerName: string;
|
||||
audioFile: string;
|
||||
played: boolean;
|
||||
}
|
||||
|
||||
// Track the ai update loop for each instance of a NPC which
|
||||
let npcState: Record<string, ActiveNpcState> = {};
|
||||
|
||||
// Create emote maps for npcs while they are talking keys are seconds into audio and values are emote types
|
||||
const emotesMap: Record<string, Record<number, EmoteType>> = {};
|
||||
|
||||
// Track the audio that has been played for each for this instance id.
|
||||
// key: InstanceId value: playerId: audioFile
|
||||
let playedAudio: Record<string, Record<string, string[]>> = {};
|
||||
|
||||
// Mick + WC
|
||||
emotesMap[`${NPCIds[NPCType.MICK_ASHWILD]}-${MapIds[MapNames.WAILING_CAVERNS]}`] = {
|
||||
1: EmoteType.STATE_TALK,
|
||||
6: EmoteType.STATE_EXCLAIM,
|
||||
8: EmoteType.STATE_TALK,
|
||||
10: EmoteType.ONESHOT_NONE,
|
||||
12: EmoteType.STATE_POINT,
|
||||
14: EmoteType.ONESHOT_NONE,
|
||||
15: EmoteType.STATE_TALK,
|
||||
26: EmoteType.STATE_EXCLAIM,
|
||||
28: EmoteType.STATE_TALK,
|
||||
35: EmoteType.ONESHOT_EXCLAMATION,
|
||||
37: EmoteType.STATE_TALK,
|
||||
41: EmoteType.STATE_POINT,
|
||||
43: EmoteType.ONESHOT_NONE
|
||||
};
|
||||
|
||||
// Spell to spell MapId
|
||||
const spellToMap: Record<number, number> = {
|
||||
[MapIds[MapNames.WAILING_CAVERNS]]: Spells.SPELL_LEATHER_FUSION
|
||||
};
|
||||
|
||||
// Does this player already know what the special NPC is offering.
|
||||
function PlayerHasSpell(player: Player): boolean {
|
||||
const mapId = player.GetMapId();
|
||||
if(spellToMap[mapId]) {
|
||||
return player.HasSpell(spellToMap[mapId]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// This will prevent audio from being played more than once for the same player using a global map
|
||||
function PlayAudioOnce(player: Player, audioFile: string): void {
|
||||
const instanceId = player.GetInstanceId();
|
||||
|
||||
if(!playedAudio[instanceId]) {
|
||||
playedAudio[instanceId] = {};
|
||||
}
|
||||
// If nothing has been played to the player create the audio entry
|
||||
if(!playedAudio[instanceId][player.GetName()]) {
|
||||
playedAudio[instanceId][player.GetName()] = [];
|
||||
playedAudio[instanceId][player.GetName()].push(audioFile);
|
||||
PrintDebug(`Playing audio ${audioFile} for player ${player.GetName()} in instance ${instanceId}`);
|
||||
aio.Handle(player, 'AIOAudioPlayer', 'PlaySingleSound', audioFile);
|
||||
return;
|
||||
}
|
||||
|
||||
if(playedAudio[instanceId][player.GetName()].includes(audioFile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
playedAudio[instanceId][player.GetName()].push(audioFile);
|
||||
aio.Handle(player, 'AIOAudioPlayer', 'PlaySingleSound', audioFile);
|
||||
|
||||
}
|
||||
|
||||
// Get the instance state of the NPC for this group and instance.
|
||||
function GetNPCState(creature: Creature): ActiveNpcState {
|
||||
const instanceId = creature.GetInstanceId();
|
||||
const creatureGuid = creature.GetGUIDLow();
|
||||
let state = npcState[`${instanceId}-${creatureGuid}`];
|
||||
|
||||
if(!state) {
|
||||
state = {
|
||||
time: 0,
|
||||
intro: false,
|
||||
outro: [],
|
||||
audioActive: false,
|
||||
players: []
|
||||
};
|
||||
npcState[`${instanceId}-${creatureGuid}`] = state;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle AI Dialogs for Mick and others.
|
||||
*/
|
||||
const handleMickAIUpdates: creature_event_on_aiupdate = (event: number, creature: Creature, diff: number): boolean => {
|
||||
|
||||
const creatureGuid = creature.GetGUIDLow();
|
||||
const instanceId = creature.GetInstanceId();
|
||||
const state = npcState[`${instanceId}-${creatureGuid}`];
|
||||
const previousTime = state.time || 0;
|
||||
|
||||
state.time = (previousTime + diff);
|
||||
const seconds = Math.ceil(state.time / 1000);
|
||||
|
||||
const npcEmoteMap = emotesMap[`${creature.GetEntry()}-${creature.GetMapId()}`];
|
||||
if(npcEmoteMap && npcEmoteMap[seconds] && npcEmoteMap[seconds] !== state.lastEmote) {
|
||||
|
||||
PrintDebug(`Mick player state: ${state.players.length}`);
|
||||
creature.EmoteState(npcEmoteMap[seconds]);
|
||||
state.lastEmote = npcEmoteMap[seconds];
|
||||
}
|
||||
|
||||
// Handle the AI Updates for Wailing Caverns
|
||||
if(MapIds[MapNames.WAILING_CAVERNS] === creature.GetMapId()) {
|
||||
if(seconds >= 43) {
|
||||
ClearUniqueCreatureEvents(creature.GetGUID(), instanceId);
|
||||
state.audioActive = false;
|
||||
state.intro = true;
|
||||
for(let i=0; i < state.players.length; i++) {
|
||||
const playerId = state.players[i];
|
||||
|
||||
if(!playerId) {
|
||||
PrintError(`${creature.GetName()} gossip menu failed no valid player in state`);
|
||||
continue;
|
||||
}
|
||||
const player = GetPlayerByName(playerId);
|
||||
if(player) {
|
||||
npcHello(player, creature, player.GetMapId());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* the player will meet different characters at different times so the hello will check to see whch
|
||||
* audio file and gossip menu needs to be shown. The list of locations an npc will show up is below
|
||||
*
|
||||
* Mick
|
||||
* Rare Leather Fusion : Wailing Caverns Verdan the everliving
|
||||
* Mythic Leather Fusion : Sartharion Obsidian Sanctum
|
||||
* Rare Fire Fusion : Hellfire Ramparts Nazan + Vazruden
|
||||
* Mythic Fire Fusion : Onyxia Lair Flamegor
|
||||
* Casual Spawns : World ends Tavern Shatrath
|
||||
*
|
||||
* Thorin
|
||||
* Rare Ore Fusion : BRD Magmus
|
||||
* Mythic Ore Fusion : Ragnaros
|
||||
* Casual Spawns : The Great Forge
|
||||
|
||||
*
|
||||
*/
|
||||
|
||||
// When verdan dies Mike should show phase in from a portal
|
||||
const verdanDied: creature_event_on_died = (event: number, creature: Creature, killer: Creature): boolean => {
|
||||
|
||||
// Spawn the portal and Mick
|
||||
creature.SummonGameObject(GobjectIds[GobjectType.PORTAL], -79.273, 4.999, -30.962, 2.20);
|
||||
const mick = creature.SpawnCreature(NPCIds[NPCType.MICK_ASHWILD], -79.273, 4.999, -30.962, 2.20, TempSummonType.TEMPSUMMON_TIMED_DESPAWN, 1200000);
|
||||
mick.SetWalk(true);
|
||||
mick.MoveTo(1, -83.252, 19.723, -31.076);
|
||||
|
||||
return false; // return false to continue normal action
|
||||
};
|
||||
|
||||
function npcHello(player: Player, creature: Creature, mapId: number, known?: boolean): void {
|
||||
player.GossipClearMenu();
|
||||
PrintDebug("sending menu to player: " + player.GetName());
|
||||
|
||||
if(mapId === MapIds[MapNames.WAILING_CAVERNS]) {
|
||||
if(known) {
|
||||
player.GossipMenuAddItem(0, "Got nuthin' for ya friend.", 1, 999);
|
||||
player.GossipSendMenu(1, creature, 90000);
|
||||
return;
|
||||
}
|
||||
|
||||
player.GossipMenuAddItem(3, "Learn Leather Fusion (requires grandmaster)",1, Spells.SPELL_LEATHER_FUSION);
|
||||
player.GossipMenuAddItem(0, "Best to come another time", 1, 999);
|
||||
}
|
||||
|
||||
player.GossipSendMenu(1, creature, 90000);
|
||||
}
|
||||
|
||||
const mickSelect: gossip_event_on_select = (event: number, player: Player, creature: Creature, sender: number, selection: number): boolean => {
|
||||
|
||||
const state = GetNPCState(creature);
|
||||
|
||||
PrintDebug(`Player ${player.GetName()} selected ${selection}`);
|
||||
|
||||
// 999 is a signal nothing to do
|
||||
if(selection === 999) {
|
||||
// play the outro audio for the player if they are done.
|
||||
if(!state.outro[player.GetName()]) {
|
||||
aio.Handle(player, 'AIOAudioPlayer', 'PlaySingleSound', getAudioFile(NPCType.MICK_ASHWILD, "rare-goodbye"));
|
||||
state.outro[player.GetName()] = true;
|
||||
}
|
||||
player.GossipClearMenu();
|
||||
player.GossipComplete();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check the range of the spell we are trying to learn to make sure there is not a problem.
|
||||
if(selection < Spells.SPELL_ORE_FUSION || selection > Spells.SPELL_EARTH_FUSION_RANK_2) {
|
||||
PrintError(`The selection ${selection} is not in the range of learnable spells check coding!!`);
|
||||
return true;
|
||||
}
|
||||
PrintDebug(`Learning spell ${selection} for player ${player.GetName()}`);
|
||||
PrintDebug(`Player skill ${player.GetSkillValue(165)}`);
|
||||
|
||||
switch(player.GetMapId()) {
|
||||
|
||||
case MapIds[MapNames.WAILING_CAVERNS]:
|
||||
if(player.GetSkillValue(165) == 450) { // if they are a grandmaster leather worker.
|
||||
player.LearnSpell(selection);
|
||||
|
||||
PrintDebug(`Learning spell ${selection} for player ${player.GetName()}`);
|
||||
PlayAudioOnce(player, getAudioFile(NPCType.MICK_ASHWILD, "teach-rare-yes"));
|
||||
} else {
|
||||
PrintDebug(`FAILING ${selection} for player ${player.GetName()}`);
|
||||
PlayAudioOnce(player, getAudioFile(NPCType.MICK_ASHWILD, "teach-rare-no"));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
player.GossipComplete();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Micks events
|
||||
const mickHello: gossip_event_on_hello = (event: number, player: Player, creature: Creature): boolean => {
|
||||
const zoneId = player.GetMapId();
|
||||
const instanceId = player.GetInstanceId();
|
||||
|
||||
// Get the global creature state for special NPCs
|
||||
const myState = GetNPCState(creature);
|
||||
|
||||
// if(isInMythicPlus(player.GetGUID(), instanceId)) {
|
||||
if (MapIds[MapNames.WAILING_CAVERNS] === zoneId) {
|
||||
|
||||
creature.SetFacingToObject(player);
|
||||
|
||||
// Do play outro dialog if they already know the spell.
|
||||
if(PlayerHasSpell(player)) {
|
||||
npcHello(player, creature, zoneId, true);
|
||||
if(!myState.outro[player.GetName()]) {
|
||||
PlayAudioOnce(player, getAudioFile(NPCType.MICK_ASHWILD, "rare-goodbye"));
|
||||
myState.outro[player.GetName()] = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Audio should play for all players in range when the NPC spawns..
|
||||
// however the menu will show up only to the player that clicked hello,
|
||||
// or other players after the audio has played.
|
||||
if (!myState.audioActive) {
|
||||
|
||||
|
||||
const nearPlayers = creature.GetPlayersInRange(35);
|
||||
for (let i = 0; i < nearPlayers.length; i++) {
|
||||
myState.players.push(nearPlayers[i].GetName());
|
||||
PlayAudioOnce(player, getAudioFile(NPCType.MICK_ASHWILD, "rare-hello"));
|
||||
}
|
||||
|
||||
creature.EmoteState(EmoteType.ONESHOT_NONE);
|
||||
myState.audioActive = true;
|
||||
ClearUniqueCreatureEvents(creature.GetGUID(), instanceId, CreatureEvents.CREATURE_EVENT_ON_AIUPDATE);
|
||||
RegisterUniqueCreatureEvent(creature.GetGUID(), instanceId, CreatureEvents.CREATURE_EVENT_ON_AIUPDATE, (...args) => handleMickAIUpdates(...args));
|
||||
} else {
|
||||
myState.players.push(player.GetName()); // player wants to interact with the NPC
|
||||
if(myState.players.length === 1 && myState.intro) {
|
||||
npcHello(player, creature, player.GetMapId());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**** Boss Kill Handlers ****/
|
||||
|
||||
// Wailing Caverns - Verdan the Everliving is killed
|
||||
RegisterCreatureEvent(5775, CreatureEvents.CREATURE_EVENT_ON_DIED, (...args) => verdanDied(...args));
|
||||
|
||||
/**** NPC Gossip Events Handlers ****/
|
||||
|
||||
// Mick Ashwild
|
||||
RegisterCreatureGossipEvent(NPCIds[NPCType.MICK_ASHWILD], GossipEvents.GOSSIP_EVENT_ON_HELLO, (...args) => mickHello(...args));
|
||||
RegisterCreatureGossipEvent(NPCIds[NPCType.MICK_ASHWILD], GossipEvents.GOSSIP_EVENT_ON_SELECT, (...args) => mickSelect(...args));
|
||||
|
||||
|
||||
/**** NPC Events Handlers ****/
|
||||
|
||||
// Registers the creature state to our instance state map and sets default state values.
|
||||
function commonCreatureRegister(creature: Creature) {
|
||||
ClearUniqueCreatureEvents(creature.GetGUID(), creature.GetInstanceId());
|
||||
npcState[`${creature.GetInstanceId()}-${creature.GetGUIDLow()}`] = {
|
||||
time: 0,
|
||||
intro: false,
|
||||
outro: [],
|
||||
audioActive: false,
|
||||
players: []
|
||||
};
|
||||
}
|
||||
|
||||
// Micks Events
|
||||
RegisterCreatureEvent(NPCIds[NPCType.MICK_ASHWILD], CreatureEvents.CREATURE_EVENT_ON_SPAWN, (event: number, creature: Creature): boolean => {
|
||||
commonCreatureRegister(creature);
|
||||
return false;
|
||||
});
|
||||
RegisterCreatureEvent(NPCIds[NPCType.MICK_ASHWILD], CreatureEvents.CREATURE_EVENT_ON_REMOVE, (event: number, creature: Creature): boolean => {
|
||||
commonCreatureRegister(creature);
|
||||
return false;
|
||||
});
|
||||
|
||||
/**** Server events for state management around instances ****/
|
||||
|
||||
const cleanupAudio: map_event_on_destroy = (event: number, map: EMap) => {
|
||||
playedAudio[map.GetInstanceId()] = {};
|
||||
};
|
||||
|
||||
// Register Map Event on Destroy
|
||||
RegisterServerEvent(ServerEvents.MAP_EVENT_ON_DESTROY, (...args) => cleanupAudio(...args));
|
||||
|
||||
const createPlayedAudio: map_event_on_destroy = (event: number, map: EMap) => {
|
||||
playedAudio[map.GetInstanceId()] = {};
|
||||
};
|
||||
|
||||
// Register Map Event on Destroy
|
||||
RegisterServerEvent(ServerEvents.MAP_EVENT_ON_DESTROY, (...args) => createPlayedAudio(...args));
|
||||
|
||||
const resetPlayedAudio: eluna_event_on_lua_state_open = (event: number) => {
|
||||
PrintDebug("resetting played audio");
|
||||
playedAudio = {};
|
||||
};
|
||||
const resetPlayedAudioClose: eluna_event_on_lua_state_close = (event: number) => {
|
||||
PrintDebug("resetting played audio");
|
||||
playedAudio = {};
|
||||
}
|
||||
|
||||
|
||||
// Register Server Event on Lua State Open
|
||||
RegisterServerEvent(ServerEvents.ELUNA_EVENT_ON_LUA_STATE_OPEN, (...args) => resetPlayedAudio(...args));
|
||||
RegisterServerEvent(ServerEvents.ELUNA_EVENT_ON_LUA_STATE_CLOSE, (...args) => resetPlayedAudioClose(...args));
|
||||
@@ -1,4 +1,5 @@
|
||||
// Purpose: Logger class to log messages to the console.
|
||||
|
||||
export class Logger {
|
||||
|
||||
public logname: string;
|
||||
|
||||
536
modules/classes/mapzones.ts
Normal file
536
modules/classes/mapzones.ts
Normal file
@@ -0,0 +1,536 @@
|
||||
import { Logger } from "./logger";
|
||||
|
||||
const logger = new Logger("MapZones");
|
||||
|
||||
export enum MapNames {
|
||||
// Classic WoW dungeons
|
||||
RAGEFIRE_CHASM = 'ragefire_chasm',
|
||||
WAILING_CAVERNS = 'wailing_caverns',
|
||||
DEADMINES = 'deadmines',
|
||||
SHADOWFANG_KEEP = 'shadowfang_keep',
|
||||
STOCKADE = 'stockade',
|
||||
BLACKFATHOM_DEEPS = 'blackfathom_deeps',
|
||||
GNOMEREGAN = 'gnomeregan',
|
||||
RAZORFEN_KRAUL = 'razorfen_kraul',
|
||||
SCARLET_MONASTERY = 'scarlet_monastery',
|
||||
SCHOLOMANCE = 'scholomance',
|
||||
SUNKEN_TEMPLE = 'sunken_temple',
|
||||
RAZORFEN_DOWNS = 'razorfen_downs',
|
||||
ULDAMAN = 'uldaman',
|
||||
STRATHOLME = 'stratholme',
|
||||
BLACKROCK_SPIRE_LOWER = 'blackrock_spire_lower',
|
||||
BLACKROCK_SPIRE_UPPER = 'blackrock_spire_upper',
|
||||
DIRE_MAUL = 'dire_maul',
|
||||
ZUL_FARRAK = 'zul_farrak',
|
||||
MARAUDON = 'maraudon',
|
||||
TEMPLE_ATAL_HAKKAR = 'temple_atal_hakkar',
|
||||
BLACKROCK_DEPTHS = 'blackrock_depths',
|
||||
|
||||
// Classic WoW raids
|
||||
MOLTEN_CORE = 'molten_core',
|
||||
BLACKWING_LAIR = 'blackwing_lair',
|
||||
RUINS_OF_AHNQIRAJ = 'ruins_of_ahnqiraj',
|
||||
TEMPLE_OF_AHNQIRAJ = 'temple_of_ahnqiraj',
|
||||
ZUL_GURUB = 'zul_gurub',
|
||||
ONYXIAS_LAIR = 'onyxias_lair',
|
||||
EMERALD_DREAM = 'emerald_dream',
|
||||
|
||||
// The Burning Crusade dungeons
|
||||
SHATTERED_HALLS = 'shattered_halls',
|
||||
BLOOD_FURNACE = 'blood_furnace',
|
||||
HELLFIRE_RAMPARTS = 'hellfire_ramparts',
|
||||
STEAMVAULTS = 'steamvaults',
|
||||
UNDERBOG = 'underbog',
|
||||
SLAVE_PENS = 'slave_pens',
|
||||
MANA_TOMBS = 'mana_tombs',
|
||||
AUCHENAI_CRYPTS = 'auchenai_crypts',
|
||||
SETHEKK_HALLS = 'sethekk_halls',
|
||||
SHADOW_LABYRINTH = 'shadow_labyrinth',
|
||||
OLD_HILLSBRAD = 'old_hillsbrad',
|
||||
BOTANICA = 'botanica',
|
||||
MECHANAR = 'mechanar',
|
||||
ARCATRAZ = 'arcatraz',
|
||||
MAGISTERS_TERRACE = 'magisters_terrace',
|
||||
BLACK_MORASS = 'black_morass',
|
||||
|
||||
// The Burning Crusade raids
|
||||
KARAZHAN = 'karazhan',
|
||||
GRUULS_LAIR = 'gruuls_lair',
|
||||
MAGTHERIDONS_LAIR = 'magtheridons_lair',
|
||||
SERPENTSHRINE_CAVERN = 'serpentshrine_cavern',
|
||||
TEMPEST_KEEP = 'tempest_keep',
|
||||
MOUNT_HYJAL = 'mount_hyjal',
|
||||
BLACK_TEMPLE = 'black_temple',
|
||||
SUNWELL_PLATEAU = 'sunwell_plateau',
|
||||
ZUL_AMAN = 'zul_aman',
|
||||
|
||||
// Wrath of the Lich King dungeons
|
||||
UTGARDE_KEEP = 'utgarde_keep',
|
||||
UTGARDE_PINNACLE = 'utgarde_pinnacle',
|
||||
AHNKAHET = 'ahnkahet',
|
||||
NEXUS = 'nexus',
|
||||
CULLING_STRATHOLME = 'culling_stratholme',
|
||||
DRAK_THARON = 'drak_tharon',
|
||||
AZJOL_NERUB = 'azjol_nerub',
|
||||
VIOLET_HOLD = 'violet_hold',
|
||||
GUNDRAK = 'gundrak',
|
||||
HALLS_OF_STONE = 'halls_of_stone',
|
||||
HALLS_OF_LIGHTNING = 'halls_of_lightning',
|
||||
OCULUS = 'oculus',
|
||||
TRIAL_OF_CHAMPION = 'trial_of_champion',
|
||||
FORGE_OF_SOULS = 'forge_of_souls',
|
||||
PIT_OF_SARON = 'pit_of_saron',
|
||||
HALLS_OF_REFLECTION = 'halls_of_reflection',
|
||||
|
||||
// Wrath of the Lich King raids
|
||||
NAXXRAMAS = 'naxxramas',
|
||||
OBSIDIAN_SANCTUM = 'obsidian_sanctum',
|
||||
EYE_OF_ETERNITY = 'eye_of_eternity',
|
||||
ULDUAR = 'ulduar',
|
||||
TRIAL_OF_CRUSADER = 'trial_of_crusader',
|
||||
VAULT_OF_ARCHAVON = 'vault_of_archavon',
|
||||
ICECROWN_CITADEL = 'icecrown_citadel',
|
||||
RUBY_SANCTUM = 'ruby_sanctum'
|
||||
}
|
||||
|
||||
export const MapIds: Record<MapNames, number> = {
|
||||
// Classic WoW dungeons
|
||||
[MapNames.RAGEFIRE_CHASM]: 389,
|
||||
[MapNames.WAILING_CAVERNS]: 43,
|
||||
[MapNames.DEADMINES]: 36,
|
||||
[MapNames.SHADOWFANG_KEEP]: 33,
|
||||
[MapNames.STOCKADE]: 34,
|
||||
[MapNames.BLACKFATHOM_DEEPS]: 48,
|
||||
[MapNames.GNOMEREGAN]: 90,
|
||||
[MapNames.RAZORFEN_KRAUL]: 47,
|
||||
[MapNames.SCARLET_MONASTERY]: 189,
|
||||
[MapNames.SCHOLOMANCE]: 289,
|
||||
[MapNames.SUNKEN_TEMPLE]: 109,
|
||||
[MapNames.RAZORFEN_DOWNS]: 129,
|
||||
[MapNames.ULDAMAN]: 70,
|
||||
[MapNames.STRATHOLME]: 329,
|
||||
[MapNames.BLACKROCK_SPIRE_LOWER]: 229,
|
||||
[MapNames.BLACKROCK_SPIRE_UPPER]: 230,
|
||||
[MapNames.DIRE_MAUL]: 429,
|
||||
[MapNames.ZUL_FARRAK]: 209,
|
||||
[MapNames.MARAUDON]: 349,
|
||||
[MapNames.TEMPLE_ATAL_HAKKAR]: 269,
|
||||
[MapNames.BLACKROCK_DEPTHS]: 230,
|
||||
|
||||
// Classic WoW raids
|
||||
[MapNames.MOLTEN_CORE]: 409,
|
||||
[MapNames.BLACKWING_LAIR]: 469,
|
||||
[MapNames.RUINS_OF_AHNQIRAJ]: 509,
|
||||
[MapNames.TEMPLE_OF_AHNQIRAJ]: 531,
|
||||
[MapNames.ZUL_GURUB]: 309,
|
||||
[MapNames.ONYXIAS_LAIR]: 249,
|
||||
[MapNames.EMERALD_DREAM]: 169,
|
||||
|
||||
// The Burning Crusade dungeons
|
||||
[MapNames.SHATTERED_HALLS]: 540,
|
||||
[MapNames.BLOOD_FURNACE]: 542,
|
||||
[MapNames.HELLFIRE_RAMPARTS]: 543,
|
||||
[MapNames.STEAMVAULTS]: 545,
|
||||
[MapNames.UNDERBOG]: 546,
|
||||
[MapNames.SLAVE_PENS]: 547,
|
||||
[MapNames.MANA_TOMBS]: 557,
|
||||
[MapNames.AUCHENAI_CRYPTS]: 558,
|
||||
[MapNames.SETHEKK_HALLS]: 556,
|
||||
[MapNames.SHADOW_LABYRINTH]: 555,
|
||||
[MapNames.OLD_HILLSBRAD]: 560,
|
||||
[MapNames.BOTANICA]: 553,
|
||||
[MapNames.MECHANAR]: 554,
|
||||
[MapNames.ARCATRAZ]: 552,
|
||||
[MapNames.MAGISTERS_TERRACE]: 585,
|
||||
[MapNames.BLACK_MORASS]: 269,
|
||||
|
||||
// The Burning Crusade raids
|
||||
[MapNames.KARAZHAN]: 532,
|
||||
[MapNames.GRUULS_LAIR]: 565,
|
||||
[MapNames.MAGTHERIDONS_LAIR]: 544,
|
||||
[MapNames.SERPENTSHRINE_CAVERN]: 548,
|
||||
[MapNames.TEMPEST_KEEP]: 550,
|
||||
[MapNames.MOUNT_HYJAL]: 534,
|
||||
[MapNames.BLACK_TEMPLE]: 564,
|
||||
[MapNames.SUNWELL_PLATEAU]: 580,
|
||||
[MapNames.ZUL_AMAN]: 568,
|
||||
|
||||
// Wrath of the Lich King dungeons
|
||||
[MapNames.UTGARDE_KEEP]: 574,
|
||||
[MapNames.UTGARDE_PINNACLE]: 575,
|
||||
[MapNames.AHNKAHET]: 619,
|
||||
[MapNames.NEXUS]: 576,
|
||||
[MapNames.CULLING_STRATHOLME]: 595,
|
||||
[MapNames.DRAK_THARON]: 600,
|
||||
[MapNames.AZJOL_NERUB]: 601,
|
||||
[MapNames.VIOLET_HOLD]: 608,
|
||||
[MapNames.GUNDRAK]: 604,
|
||||
[MapNames.HALLS_OF_STONE]: 599,
|
||||
[MapNames.HALLS_OF_LIGHTNING]: 602,
|
||||
[MapNames.OCULUS]: 578,
|
||||
[MapNames.TRIAL_OF_CHAMPION]: 650,
|
||||
[MapNames.FORGE_OF_SOULS]: 632,
|
||||
[MapNames.PIT_OF_SARON]: 658,
|
||||
[MapNames.HALLS_OF_REFLECTION]: 668,
|
||||
|
||||
// Wrath of the Lich King raids
|
||||
[MapNames.NAXXRAMAS]: 533,
|
||||
[MapNames.OBSIDIAN_SANCTUM]: 615,
|
||||
[MapNames.EYE_OF_ETERNITY]: 616,
|
||||
[MapNames.ULDUAR]: 603,
|
||||
[MapNames.TRIAL_OF_CRUSADER]: 649,
|
||||
[MapNames.VAULT_OF_ARCHAVON]: 624,
|
||||
[MapNames.ICECROWN_CITADEL]: 631,
|
||||
[MapNames.RUBY_SANCTUM]: 724
|
||||
};
|
||||
|
||||
export const DungeonLevels: Record<MapNames, number> = {
|
||||
// Classic WoW dungeons
|
||||
[MapNames.RAGEFIRE_CHASM]: 18,
|
||||
[MapNames.WAILING_CAVERNS]: 25,
|
||||
[MapNames.DEADMINES]: 23,
|
||||
[MapNames.SHADOWFANG_KEEP]: 30,
|
||||
[MapNames.STOCKADE]: 30,
|
||||
[MapNames.BLACKFATHOM_DEEPS]: 32,
|
||||
[MapNames.GNOMEREGAN]: 38,
|
||||
[MapNames.RAZORFEN_KRAUL]: 40,
|
||||
[MapNames.SCARLET_MONASTERY]: 45,
|
||||
[MapNames.SCHOLOMANCE]: 60,
|
||||
[MapNames.SUNKEN_TEMPLE]: 60,
|
||||
[MapNames.RAZORFEN_DOWNS]: 33,
|
||||
[MapNames.ULDAMAN]: 40,
|
||||
[MapNames.STRATHOLME]: 60,
|
||||
[MapNames.BLACKROCK_SPIRE_LOWER]: 60,
|
||||
[MapNames.BLACKROCK_SPIRE_UPPER]: 60,
|
||||
[MapNames.DIRE_MAUL]: 60,
|
||||
[MapNames.ZUL_FARRAK]: 50,
|
||||
[MapNames.MARAUDON]: 55,
|
||||
[MapNames.TEMPLE_ATAL_HAKKAR]: 57,
|
||||
[MapNames.BLACKROCK_DEPTHS]: 60,
|
||||
|
||||
// Classic WoW raids
|
||||
[MapNames.MOLTEN_CORE]: 60,
|
||||
[MapNames.BLACKWING_LAIR]: 60,
|
||||
[MapNames.RUINS_OF_AHNQIRAJ]: 60,
|
||||
[MapNames.TEMPLE_OF_AHNQIRAJ]: 60,
|
||||
[MapNames.ZUL_GURUB]: 60,
|
||||
[MapNames.ONYXIAS_LAIR]: 60,
|
||||
[MapNames.EMERALD_DREAM]: 60,
|
||||
|
||||
// The Burning Crusade dungeons
|
||||
[MapNames.SHATTERED_HALLS]: 70,
|
||||
[MapNames.BLOOD_FURNACE]: 65,
|
||||
[MapNames.HELLFIRE_RAMPARTS]: 62,
|
||||
[MapNames.STEAMVAULTS]: 64,
|
||||
[MapNames.UNDERBOG]: 65,
|
||||
[MapNames.SLAVE_PENS]: 64,
|
||||
[MapNames.MANA_TOMBS]: 66,
|
||||
[MapNames.AUCHENAI_CRYPTS]: 67,
|
||||
[MapNames.SETHEKK_HALLS]: 70,
|
||||
[MapNames.SHADOW_LABYRINTH]: 70,
|
||||
[MapNames.OLD_HILLSBRAD]: 68,
|
||||
[MapNames.BOTANICA]: 70,
|
||||
[MapNames.MECHANAR]: 70,
|
||||
[MapNames.ARCATRAZ]: 70,
|
||||
[MapNames.MAGISTERS_TERRACE]: 70,
|
||||
[MapNames.BLACK_MORASS]: 70,
|
||||
|
||||
// The Burning Crusade raids
|
||||
[MapNames.KARAZHAN]: 70,
|
||||
[MapNames.GRUULS_LAIR]: 70,
|
||||
[MapNames.MAGTHERIDONS_LAIR]: 70,
|
||||
[MapNames.SERPENTSHRINE_CAVERN]: 70,
|
||||
[MapNames.TEMPEST_KEEP]: 70,
|
||||
[MapNames.MOUNT_HYJAL]: 70,
|
||||
[MapNames.BLACK_TEMPLE]: 70,
|
||||
[MapNames.SUNWELL_PLATEAU]: 70,
|
||||
[MapNames.ZUL_AMAN]: 70,
|
||||
|
||||
// Wrath of the Lich King dungeons
|
||||
[MapNames.UTGARDE_KEEP]: 72,
|
||||
[MapNames.UTGARDE_PINNACLE]: 76,
|
||||
[MapNames.AHNKAHET]: 75,
|
||||
[MapNames.NEXUS]: 73,
|
||||
[MapNames.CULLING_STRATHOLME]: 80,
|
||||
[MapNames.DRAK_THARON]: 76,
|
||||
[MapNames.AZJOL_NERUB]: 75,
|
||||
[MapNames.VIOLET_HOLD]: 77,
|
||||
[MapNames.GUNDRAK]: 78,
|
||||
[MapNames.HALLS_OF_STONE]: 78,
|
||||
[MapNames.HALLS_OF_LIGHTNING]: 80,
|
||||
[MapNames.OCULUS]: 78,
|
||||
[MapNames.TRIAL_OF_CHAMPION]: 80,
|
||||
[MapNames.FORGE_OF_SOULS]: 80,
|
||||
[MapNames.PIT_OF_SARON]: 80,
|
||||
[MapNames.HALLS_OF_REFLECTION]: 80,
|
||||
|
||||
// Wrath of the Lich King raids
|
||||
[MapNames.NAXXRAMAS]: 80,
|
||||
[MapNames.OBSIDIAN_SANCTUM]: 80,
|
||||
[MapNames.EYE_OF_ETERNITY]: 80,
|
||||
[MapNames.ULDUAR]: 80,
|
||||
[MapNames.TRIAL_OF_CRUSADER]: 80,
|
||||
[MapNames.VAULT_OF_ARCHAVON]: 80,
|
||||
[MapNames.ICECROWN_CITADEL]: 80,
|
||||
[MapNames.RUBY_SANCTUM]: 80
|
||||
};
|
||||
|
||||
// Boss info
|
||||
export type Boss = {
|
||||
entry: number;
|
||||
name: string;
|
||||
location: string;
|
||||
isFinalBoss?: boolean;
|
||||
}
|
||||
|
||||
export const Bosses: Boss[] = [
|
||||
// Classic WoW dungeons
|
||||
{ entry: 11520, name: "Taragaman the Hungerer", location: "Ragefire Chasm" },
|
||||
{ entry: 3654, name: "Mutanus the Devourer", location: "Wailing Caverns", isFinalBoss: true },
|
||||
{ entry: 639, name: "Edwin VanCleef", location: "The Deadmines", isFinalBoss: true },
|
||||
{ entry: 4275, name: "Archmage Arugal", location: "Shadowfang Keep", isFinalBoss: true },
|
||||
{ entry: 4829, name: "Aku'mai", location: "Blackfathom Deeps", isFinalBoss: true },
|
||||
{ entry: 1716, name: "Bazil Thredd", location: "Stormwind Stockade", isFinalBoss: true },
|
||||
{ entry: 7800, name: "Mekgineer Thermaplugg", location: "Gnomeregan", isFinalBoss: true },
|
||||
{ entry: 4421, name: "Charlga Razorflank", location: "Razorfen Kraul", isFinalBoss: true },
|
||||
{ entry: 4543, name: "Bloodmage Thalnos", location: "Scarlet Monastery Graveyard", isFinalBoss: true },
|
||||
{ entry: 6487, name: "Arcanist Doan", location: "Scarlet Monastery Library", isFinalBoss: true },
|
||||
{ entry: 3975, name: "Herod", location: "Scarlet Monastery Armory", isFinalBoss: true },
|
||||
{ entry: 3977, name: "High Inquisitor Whitemane", location: "Scarlet Monastery Cathedral", isFinalBoss: true },
|
||||
{ entry: 7358, name: "Amnennar the Coldbringer", location: "Razorfen Downs", isFinalBoss: true },
|
||||
{ entry: 2748, name: "Archaedas", location: "Uldaman", isFinalBoss: true },
|
||||
{ entry: 7267, name: "Chief Ukorz Sandscalp", location: "Zul'Farrak", isFinalBoss: true },
|
||||
{ entry: 12201, name: "Princess Theradras", location: "Maraudon", isFinalBoss: true },
|
||||
{ entry: 8443, name: "Avatar of Hakkar", location: "Sunken Temple", isFinalBoss: true },
|
||||
{ entry: 9019, name: "Emperor Dagran Thaurissan", location: "Blackrock Depths", isFinalBoss: true },
|
||||
{ entry: 9568, name: "Overlord Wyrmthalak", location: "Lower Blackrock Spire", isFinalBoss: true },
|
||||
{ entry: 10363, name: "General Drakkisath", location: "Upper Blackrock Spire", isFinalBoss: true },
|
||||
{ entry: 11492, name: "Alzzin the Wildshaper", location: "Dire Maul East", isFinalBoss: true },
|
||||
{ entry: 11489, name: "Tendris Warpwood", location: "Dire Maul West", isFinalBoss: true },
|
||||
{ entry: 11501, name: "King Gordok", location: "Dire Maul North", isFinalBoss: true },
|
||||
{ entry: 10440, name: "Baron Rivendare", location: "Stratholme Undead Side", isFinalBoss: true },
|
||||
{ entry: 10813, name: "Balnazzar", location: "Stratholme Live Side", isFinalBoss: true },
|
||||
{ entry: 1853, name: "Darkmaster Gandling", location: "Scholomance", isFinalBoss: true },
|
||||
|
||||
// The Burning Crusade dungeons
|
||||
{ entry: 17307, name: "Vazruden", location: "Hellfire Ramparts" },
|
||||
{ entry: 17536, name: "Nazan", location: "Hellfire Ramparts", isFinalBoss: true },
|
||||
{ entry: 17377, name: "Keli'dan the Breaker", location: "The Blood Furnace", isFinalBoss: true },
|
||||
{ entry: 16808, name: "Warchief Kargath Bladefist", location: "The Shattered Halls", isFinalBoss: true },
|
||||
{ entry: 17942, name: "Quagmirran", location: "The Slave Pens", isFinalBoss: true },
|
||||
{ entry: 17826, name: "Swamplord Musel'ek", location: "The Underbog", isFinalBoss: true },
|
||||
{ entry: 17798, name: "Warlord Kalithresh", location: "The Steamvault", isFinalBoss: true },
|
||||
{ entry: 18344, name: "Nexus-Prince Shaffar", location: "Mana-Tombs", isFinalBoss: true },
|
||||
{ entry: 18373, name: "Exarch Maladaar", location: "Auchenai Crypts", isFinalBoss: true },
|
||||
{ entry: 18473, name: "Talon King Ikiss", location: "Sethekk Halls", isFinalBoss: true },
|
||||
{ entry: 18708, name: "Murmur", location: "Shadow Labyrinth", isFinalBoss: true },
|
||||
{ entry: 19220, name: "Pathaleon the Calculator", location: "The Mechanar", isFinalBoss: true },
|
||||
{ entry: 17977, name: "Warp Splinter", location: "The Botanica", isFinalBoss: true },
|
||||
{ entry: 20912, name: "Harbinger Skyriss", location: "The Arcatraz", isFinalBoss: true },
|
||||
{ entry: 17881, name: "Aeonus", location: "The Black Morass", isFinalBoss: true },
|
||||
{ entry: 18096, name: "Epoch Hunter", location: "Old Hillsbrad Foothills", isFinalBoss: true },
|
||||
{ entry: 24664, name: "Kael'thas Sunstrider", location: "Magisters' Terrace", isFinalBoss: true },
|
||||
|
||||
// The Burning Crusade raids
|
||||
{ entry: 22887, name: "Attumen the Huntsman", location: "Karazhan" },
|
||||
{ entry: 22888, name: "Moroes", location: "Karazhan" },
|
||||
{ entry: 22889, name: "Maiden of Virtue", location: "Karazhan" },
|
||||
{ entry: 22890, name: "The Big Bad Wolf", location: "Karazhan" },
|
||||
{ entry: 22891, name: "The Crone", location: "Karazhan" },
|
||||
{ entry: 22892, name: "Romulo", location: "Karazhan" },
|
||||
{ entry: 22893, name: "Julianne", location: "Karazhan" },
|
||||
{ entry: 22894, name: "The Curator", location: "Karazhan" },
|
||||
{ entry: 22895, name: "Terestian Illhoof", location: "Karazhan" },
|
||||
{ entry: 22896, name: "Shade of Aran", location: "Karazhan" },
|
||||
{ entry: 22897, name: "Netherspite", location: "Karazhan" },
|
||||
{ entry: 22898, name: "Chess Event", location: "Karazhan" },
|
||||
{ entry: 22899, name: "Prince Malchezaar", location: "Karazhan", isFinalBoss: true },
|
||||
{ entry: 22900, name: "Nightbane", location: "Karazhan" },
|
||||
{ entry: 22901, name: "Gruul the Dragonkiller", location: "Gruul's Lair", isFinalBoss: true },
|
||||
{ entry: 22902, name: "Magtheridon", location: "Magtheridon's Lair", isFinalBoss: true },
|
||||
{ entry: 22903, name: "Lady Vashj", location: "Serpentshrine Cavern", isFinalBoss: true },
|
||||
{ entry: 22904, name: "Kael'thas Sunstrider", location: "The Eye", isFinalBoss: true },
|
||||
{ entry: 22905, name: "Al'ar", location: "The Eye", isFinalBoss: true },
|
||||
{ entry: 22906, name: "Solarian", location: "The Eye", isFinalBoss: true },
|
||||
{ entry: 22907, name: "Void Reaver", location: "The Eye", isFinalBoss: true },
|
||||
{ entry: 22908, name: "High Astromancer Solarian", location: "The Eye", isFinalBoss: true },
|
||||
{ entry: 22909, name: "Kael'thas Sunstrider", location: "The Eye", isFinalBoss: true },
|
||||
{ entry: 22910, name: "Rage Winterchill", location: "Mount Hyjal", isFinalBoss: true },
|
||||
{ entry: 22911, name: "Anetheron", location: "Mount Hyjal", isFinalBoss: true },
|
||||
{ entry: 22912, name: "Kaz'rogal", location: "Mount Hyjal", isFinalBoss: true },
|
||||
{ entry: 22913, name: "Azgalor", location: "Mount Hyjal", isFinalBoss: true },
|
||||
{ entry: 22914, name: "Archimonde", location: "Mount Hyjal", isFinalBoss: true },
|
||||
{ entry: 22915, name: "Najentus", location: "Black Temple", isFinalBoss: true },
|
||||
{ entry: 22916, name: "Supremus", location: "Black Temple", isFinalBoss: true },
|
||||
{ entry: 22917, name: "Shade of Akama", location: "Black Temple", isFinalBoss: true },
|
||||
{ entry: 22918, name: "Teron Gorefiend", location: "Black Temple", isFinalBoss: true },
|
||||
{ entry: 22919, name: "Gurtogg Bloodboil", location: "Black Temple", isFinalBoss: true },
|
||||
{ entry: 22920, name: "Reliquary of Souls", location: "Black Temple", isFinalBoss: true },
|
||||
{ entry: 22921, name: "Mother Shahraz", location: "Black Temple", isFinalBoss: true },
|
||||
{ entry: 22922, name: "The Illidari Council", location: "Black Temple", isFinalBoss: true },
|
||||
{ entry: 22923, name: "Illidan Stormrage", location: "Black Temple", isFinalBoss: true },
|
||||
{ entry: 22924, name: "Kalecgos", location: "Sunwell Plateau", isFinalBoss: true },
|
||||
{ entry: 22925, name: "Brutallus", location: "Sunwell Plateau", isFinalBoss: true },
|
||||
{ entry: 22926, name: "Felmyst", location: "Sunwell Plateau", isFinalBoss: true },
|
||||
{ entry: 22927, name: "Eredar Twins", location: "Sunwell Plateau", isFinalBoss: true },
|
||||
{ entry: 22928, name: "M'uru", location: "Sunwell Plateau", isFinalBoss: true },
|
||||
{ entry: 22929, name: "Kil'jaeden", location: "Sunwell Plateau", isFinalBoss: true },
|
||||
|
||||
// Wrath of the Lich King dungeons
|
||||
{ entry: 23954, name: "Ingvar the Plunderer", location: "Utgarde Keep", isFinalBoss: true },
|
||||
{ entry: 26723, name: "Keristrasza", location: "The Nexus", isFinalBoss: true },
|
||||
{ entry: 29120, name: "Anub'arak", location: "Azjol-Nerub", isFinalBoss: true },
|
||||
{ entry: 29311, name: "Herald Volazj", location: "Ahn'kahet: The Old Kingdom", isFinalBoss: true },
|
||||
{ entry: 26632, name: "The Prophet Tharon'ja", location: "Drak'Tharon Keep", isFinalBoss: true },
|
||||
{ entry: 31134, name: "Cyanigosa", location: "Violet Hold", isFinalBoss: true },
|
||||
{ entry: 29306, name: "Gal'darah", location: "Gundrak", isFinalBoss: true },
|
||||
{ entry: 27978, name: "Sjonnir the Ironshaper", location: "Halls of Stone", isFinalBoss: true },
|
||||
{ entry: 28923, name: "Loken", location: "Halls of Lightning", isFinalBoss: true },
|
||||
{ entry: 27656, name: "Ley-Guardian Eregos", location: "The Oculus", isFinalBoss: true },
|
||||
{ entry: 26533, name: "Mal'Ganis", location: "Culling of Stratholme", isFinalBoss: true },
|
||||
{ entry: 26861, name: "King Ymiron", location: "Utgarde Pinnacle", isFinalBoss: true },
|
||||
{ entry: 35451, name: "The Black Knight", location: "Trial of the Champion", isFinalBoss: true },
|
||||
{ entry: 36502, name: "Devourer of Souls", location: "Forge of Souls", isFinalBoss: true },
|
||||
{ entry: 36658, name: "Scourgelord Tyrannus", location: "Pit of Saron", isFinalBoss: true },
|
||||
{ entry: 37226, name: "The Lich King", location: "Halls of Reflection", isFinalBoss: true },
|
||||
|
||||
// Wrath of the Lich King raids
|
||||
{ entry: 29341, name: "Anub'Rekhan", location: "Naxxramas", isFinalBoss: true },
|
||||
{ entry: 29342, name: "Grand Widow Faerlina", location: "Naxxramas", isFinalBoss: true },
|
||||
{ entry: 29343, name: "Maexxna", location: "Naxxramas", isFinalBoss: true },
|
||||
{ entry: 29344, name: "Noth the Plaguebringer", location: "Naxxramas", isFinalBoss: true },
|
||||
{ entry: 29345, name: "Heigan the Unclean", location: "Naxxramas", isFinalBoss: true },
|
||||
{ entry: 29346, name: "Loatheb", location: "Naxxramas", isFinalBoss: true },
|
||||
{ entry: 29347, name: "Instructor Razuvious", location: "Naxxramas", isFinalBoss: true },
|
||||
{ entry: 29348, name: "Gothik the Harvester", location: "Naxxramas", isFinalBoss: true },
|
||||
{ entry: 29349, name: "The Four Horsemen", location: "Naxxramas", isFinalBoss: true },
|
||||
{ entry: 29350, name: "Patchwerk", location: "Naxxramas", isFinalBoss: true },
|
||||
{ entry: 29351, name: "Grobbulus", location: "Naxxramas", isFinalBoss: true },
|
||||
{ entry: 29352, name: "Gluth", location: "Naxxramas", isFinalBoss: true },
|
||||
{ entry: 29353, name: "Thaddius", location: "Naxxramas", isFinalBoss: true },
|
||||
{ entry: 29354, name: "Sapphiron", location: "Naxxramas", isFinalBoss: true },
|
||||
{ entry: 29355, name: "Kel'Thuzad", location: "Naxxramas", isFinalBoss: true },
|
||||
{ entry: 29356, name: "Sartharion", location: "Obsidian Sanctum", isFinalBoss: true },
|
||||
{ entry: 29357, name: "Malygos", location: "Eye of Eternity", isFinalBoss: true },
|
||||
{ entry: 29358, name: "Ignis the Furnace Master", location: "Ulduar", isFinalBoss: true },
|
||||
{ entry: 29359, name: "XT-002 Deconstructor", location: "Ulduar", isFinalBoss: true },
|
||||
{ entry: 29360, name: "Kologarn", location: "Ulduar", isFinalBoss: true },
|
||||
{ entry: 29361, name: "Auriaya", location: "Ulduar", isFinalBoss: true },
|
||||
{ entry: 29362, name: "Hodir", location: "Ulduar", isFinalBoss: true },
|
||||
{ entry: 29363, name: "Thorim", location: "Ulduar", isFinalBoss: true },
|
||||
{ entry: 29364, name: "Freya", location: "Ulduar", isFinalBoss: true },
|
||||
{ entry: 29365, name: "Mimiron", location: "Ulduar", isFinalBoss: true },
|
||||
{ entry: 29366, name: "General Vezax", location: "Ulduar", isFinalBoss: true },
|
||||
{ entry: 29367, name: "Yogg-Saron", location: "Ulduar", isFinalBoss: true },
|
||||
{ entry: 29368, name: "The Northrend Beasts", location: "Trial of the Crusader", isFinalBoss: true },
|
||||
{ entry: 29369, name: "Lord Jaraxxus", location: "Trial of the Crusader", isFinalBoss: true },
|
||||
{ entry: 29370, name: "The Faction Champions", location: "Trial of the Crusader", isFinalBoss: true },
|
||||
{ entry: 29371, name: "Twin Val'kyr", location: "Trial of the Crusader", isFinalBoss: true },
|
||||
{ entry: 29372, name: "Anub'arak", location: "Trial of the Crusader", isFinalBoss: true },
|
||||
{ entry: 29373, name: "Onyxia", location: "Onyxia's Lair", isFinalBoss: true },
|
||||
{ entry: 29374, name: "Lord Marrowgar", location: "Icecrown Citadel", isFinalBoss: true },
|
||||
{ entry: 29375, name: "Lady Deathwhisper", location: "Icecrown Citadel", isFinalBoss: true },
|
||||
{ entry: 29376, name: "Gunship Battle", location: "Icecrown Citadel", isFinalBoss: true },
|
||||
{ entry: 29377, name: "Deathbringer Saurfang", location: "Icecrown Citadel", isFinalBoss: true },
|
||||
{ entry: 29378, name: "Festergut", location: "Icecrown Citadel", isFinalBoss: true },
|
||||
{ entry: 29379, name: "Rotface", location: "Icecrown Citadel", isFinalBoss: true },
|
||||
{ entry: 29380, name: "Professor Putricide", location: "Icecrown Citadel", isFinalBoss: true },
|
||||
{ entry: 29381, name: "Blood-Queen Lana'thel", location: "Icecrown Citadel", isFinalBoss: true },
|
||||
{ entry: 29382, name: "Valithria Dreamwalker", location: "Icecrown Citadel", isFinalBoss: true },
|
||||
{ entry: 29383, name: "Sindragosa", location: "Icecrown Citadel", isFinalBoss: true },
|
||||
{ entry: 29384, name: "The Lich King", location: "Icecrown Citadel", isFinalBoss: true },
|
||||
{ entry: 29385, name: "Halion", location: "Ruby Sanctum", isFinalBoss: true },
|
||||
|
||||
// Molten Core
|
||||
{ entry: 12118, name: "Lucifron", location: "Molten Core" },
|
||||
{ entry: 11982, name: "Magmadar", location: "Molten Core" },
|
||||
{ entry: 12259, name: "Gehennas", location: "Molten Core" },
|
||||
{ entry: 12057, name: "Garr", location: "Molten Core" },
|
||||
{ entry: 12264, name: "Shazzrah", location: "Molten Core" },
|
||||
{ entry: 12056, name: "Baron Geddon", location: "Molten Core" },
|
||||
{ entry: 12098, name: "Sulfuron Harbinger", location: "Molten Core" },
|
||||
{ entry: 11988, name: "Golemagg the Incinerator", location: "Molten Core" },
|
||||
{ entry: 12018, name: "Majordomo Executus", location: "Molten Core" },
|
||||
{ entry: 11502, name: "Ragnaros", location: "Molten Core", isFinalBoss: true },
|
||||
|
||||
// Blackwing Lair
|
||||
{ entry: 12435, name: "Razorgore the Untamed", location: "Blackwing Lair" },
|
||||
{ entry: 13020, name: "Vaelastrasz the Corrupt", location: "Blackwing Lair" },
|
||||
{ entry: 12017, name: "Broodlord Lashlayer", location: "Blackwing Lair" },
|
||||
{ entry: 11983, name: "Firemaw", location: "Blackwing Lair" },
|
||||
{ entry: 14601, name: "Ebonroc", location: "Blackwing Lair" },
|
||||
{ entry: 11981, name: "Flamegor", location: "Blackwing Lair" },
|
||||
{ entry: 14020, name: "Chromaggus", location: "Blackwing Lair" },
|
||||
{ entry: 11583, name: "Nefarian", location: "Blackwing Lair", isFinalBoss: true },
|
||||
|
||||
// Ruins of Ahn'Qiraj
|
||||
{ entry: 15348, name: "Kurinnaxx", location: "Ruins of Ahn'Qiraj" },
|
||||
{ entry: 15341, name: "General Rajaxx", location: "Ruins of Ahn'Qiraj" },
|
||||
{ entry: 15340, name: "Moam", location: "Ruins of Ahn'Qiraj" },
|
||||
{ entry: 15370, name: "Buru the Gorger", location: "Ruins of Ahn'Qiraj" },
|
||||
{ entry: 15369, name: "Ayamiss the Hunter", location: "Ruins of Ahn'Qiraj" },
|
||||
{ entry: 15339, name: "Ossirian the Unscarred", location: "Ruins of Ahn'Qiraj", isFinalBoss: true },
|
||||
|
||||
// Temple of Ahn'Qiraj
|
||||
{ entry: 15263, name: "The Prophet Skeram", location: "Temple of Ahn'Qiraj" },
|
||||
{ entry: 15516, name: "Battleguard Sartura", location: "Temple of Ahn'Qiraj" },
|
||||
{ entry: 15510, name: "Fankriss the Unyielding", location: "Temple of Ahn'Qiraj" },
|
||||
{ entry: 15509, name: "Princess Huhuran", location: "Temple of Ahn'Qiraj" },
|
||||
{ entry: 15275, name: "Emperor Vek'lor", location: "Temple of Ahn'Qiraj" },
|
||||
{ entry: 15276, name: "Emperor Vek'nilash", location: "Temple of Ahn'Qiraj" },
|
||||
{ entry: 15727, name: "C'Thun", location: "Temple of Ahn'Qiraj", isFinalBoss: true },
|
||||
|
||||
// Zul'Gurub
|
||||
{ entry: 14517, name: "High Priestess Jeklik", location: "Zul'Gurub" },
|
||||
{ entry: 14507, name: "High Priest Venoxis", location: "Zul'Gurub" },
|
||||
{ entry: 14510, name: "High Priestess Mar'li", location: "Zul'Gurub" },
|
||||
{ entry: 14509, name: "High Priest Thekal", location: "Zul'Gurub" },
|
||||
{ entry: 14515, name: "High Priestess Arlokk", location: "Zul'Gurub" },
|
||||
{ entry: 14834, name: "Hakkar the Soulflayer", location: "Zul'Gurub", isFinalBoss: true },
|
||||
{ entry: 11382, name: "Bloodlord Mandokir", location: "Zul'Gurub" },
|
||||
{ entry: 11380, name: "Jin'do the Hexxer", location: "Zul'Gurub" },
|
||||
{ entry: 15114, name: "Gahz'ranka", location: "Zul'Gurub" },
|
||||
{ entry: 15082, name: "Renataki", location: "Zul'Gurub" },
|
||||
{ entry: 15083, name: "Grilek", location: "Zul'Gurub" },
|
||||
{ entry: 15084, name: "Hazza'rah", location: "Zul'Gurub" },
|
||||
{ entry: 15085, name: "Wushoolay", location: "Zul'Gurub" },
|
||||
|
||||
// Karazhan
|
||||
{ entry: 16152, name: "Attumen the Huntsman", location: "Karazhan" },
|
||||
{ entry: 15687, name: "Moroes", location: "Karazhan" },
|
||||
{ entry: 16457, name: "Maiden of Virtue", location: "Karazhan" },
|
||||
{ entry: 17521, name: "The Big Bad Wolf", location: "Karazhan" },
|
||||
{ entry: 18168, name: "The Crone", location: "Karazhan" },
|
||||
{ entry: 17533, name: "Romulo", location: "Karazhan" },
|
||||
{ entry: 17534, name: "Julianne", location: "Karazhan" },
|
||||
{ entry: 15691, name: "The Curator", location: "Karazhan" },
|
||||
{ entry: 15688, name: "Terestian Illhoof", location: "Karazhan" },
|
||||
{ entry: 16524, name: "Shade of Aran", location: "Karazhan" },
|
||||
{ entry: 15689, name: "Netherspite", location: "Karazhan" },
|
||||
{ entry: 16816, name: "Chess Event", location: "Karazhan" },
|
||||
{ entry: 15690, name: "Prince Malchezaar", location: "Karazhan", isFinalBoss: true },
|
||||
{ entry: 17225, name: "Nightbane", location: "Karazhan" }
|
||||
];
|
||||
|
||||
// Quickly check if a creature is a final boss
|
||||
export const FinalBossIDs = new Set(Bosses.filter(b => b.isFinalBoss).map(b => b.entry));
|
||||
|
||||
// Example usage:
|
||||
export function isFinalBoss(entry: number): boolean {
|
||||
return FinalBossIDs.has(entry);
|
||||
}
|
||||
|
||||
// Look up by name
|
||||
export const BossesByName = Object.fromEntries(
|
||||
Bosses.map(b => [b.name.toLowerCase(), b])
|
||||
);
|
||||
|
||||
// Example usage:
|
||||
export function getBossByName(name: string): Boss | undefined {
|
||||
return BossesByName[name.toLowerCase()];
|
||||
}
|
||||
|
||||
// For backward compatibility
|
||||
export const BossIDs: Record<number, boolean> = Object.fromEntries(
|
||||
Bosses.map(b => [b.entry, true])
|
||||
);
|
||||
@@ -147,7 +147,7 @@ const GossipSelect: gossip_event_on_select = (event: number, player: Player, cre
|
||||
1
|
||||
);
|
||||
|
||||
player.RemoveItem(PlayerItem, PlayerItem.GetEntry(), 1);
|
||||
player.RemoveItem(PlayerItem, 1);
|
||||
|
||||
player.GossipClearMenu();
|
||||
player.GossipComplete();
|
||||
|
||||
Reference in New Issue
Block a user