latest changes on botequipment

This commit is contained in:
2024-02-08 18:14:02 -05:00
parent b5da54a8d5
commit 501a063dec
5 changed files with 465 additions and 75 deletions

View File

@@ -1,5 +1,10 @@
import { BotData } from './botmgr.server';
import { Equipment } from './botmgr.server';
/**
* This is the UI bot data manager class to make it easier
* to managed bot data in the UI.
*/
export class BotStorage {
private storage: Map<number, BotData> = new Map();
@@ -9,7 +14,7 @@ export class BotStorage {
return this.storage.get(entry);
}
GetBotItem(botId: number, slot: BotEquipmentSlotNum): number | undefined {
GetBotItem(botId: number, slot: BotEquipmentSlotNum): Equipment | undefined {
const bot = this.GetBotData(botId);
if(bot) {
return bot.equipment[slot];
@@ -20,7 +25,7 @@ export class BotStorage {
}
SetBotItem(botId: number, slot: BotEquipmentSlotNum, item: number): void {
SetBotItem(botId: number, slot: BotEquipmentSlotNum, item: Equipment): void {
const bot = this.GetBotData(botId);
if(bot) {
bot.equipment[slot] = item;

View File

@@ -0,0 +1,78 @@
import * as Common from '../../constants/idmaps';
import { Equipment, EquipmentList } from './botmgr.server';
type CharInfo = {
name: string,
level: number,
className: Common.CharacterClass,
classId: keyof typeof Common.ClassesMapping,
raceName: Common.CharacterRace,
raceId: keyof typeof Common.RacesMapping
}
type CharStats = Record<keyof typeof Common.BotStat, number>;
type CharTalentSpec = typeof Common.BotTalentSpecs[keyof typeof Common.BotTalentSpecs];
type CharRoles = typeof Common.BotRoles[keyof typeof Common.BotRoles];
export class BotUnit {
protected myself: Creature;
protected myOwner: Player;
protected charinfo: CharInfo;
protected equipment: EquipmentList;
protected stats: CharStats;
protected talentSpecId: CharTalentSpec;
protected roles: CharRoles;
constructor(creature: Creature) {
if(!creature.IsNPCBot()) {
return;
}
this.myself = creature;
this.myOwner = <Player>creature.GetOwner();
this.charinfo = {
name: creature.GetName(),
level: creature.GetLevel(),
className: Common.ClassesMapping[creature.GetClass()],
classId: creature.GetClass(),
raceName: Common.RacesMapping[creature.GetRace()],
raceId: creature.GetRace()
};
this.equipment = this._lookupEquipment();
this.roles = creature.GetBotRoles();
}
// public isMeleeDps(): boolean {
// const meleeClassMap = [
// Common.Characte
// ]
// if(this.charinfo.classId)
// // return this.roles === Common.BotRoles.MeleeDps;
// }
private _lookupEquipment(): EquipmentList {
const myEquipment = {} as EquipmentList;
for(let slot=0; slot <= Common.BotEquipLast; slot++) {
const equipment = this.myself.GetBotEquipment(<BotEquipmentSlotNum>slot);
if(equipment) {
myEquipment[slot] = {
entry: equipment.GetEntry(),
link: equipment.GetItemLink(),
quality: <Common.QualityType>equipment.GetQuality(),
itemLevel: equipment.GetItemLevel(),
enchantmentId: equipment.GetEnchantmentId(0), // Only the permenant enchantments
}
} else {
myEquipment[slot] = undefined;
}
}
return myEquipment;
}
}

View File

@@ -19,12 +19,11 @@ let aio: AIO = {};
*/
import { UIInvSlot, BotEquipSlot, BotSlotName } from "../../constants/idmaps";
import { BotData } from "./botmgr.server";
import { UIInvSlot, BotEquipSlot, BotSlotName, BotStat } from "../../constants/idmaps";
import { BotData, Equipment } from "./botmgr.server";
import { BotStorage } from "./bot";
// Helper functions to create unique ids for frames and components
const id = (name: string, entry: number = null) => `BotMgr${name}` + (entry ? entry : '');
const compId = (botId: number, name: string) => `${botId}:BotMgr${name}`;
@@ -42,7 +41,6 @@ function ucase(input: string): string {
return firstLetter + restOfTheString;
}
// If we are a client file. aio.AddAddon() will return false and this file will be serialized and sent to client.
if(!aio.AddAddon()) {
@@ -50,7 +48,6 @@ if(!aio.AddAddon()) {
const InfoFramePool: Map<number, WoWAPI.Frame> = new Map();
const ComponentsPool: Map<string, unknown> = new Map(); // key botId + ":" + componentid
const botStorage: BotStorage = new BotStorage();
let BotItemTooltip: WoWAPI.GameTooltip;
@@ -65,7 +62,6 @@ if(!aio.AddAddon()) {
resistFrame.SetSize(32, 160);
resistFrame.SetPoint("TOPRIGHT", parent, "TOPLEFT", 297, -77);
const magicRes1 = CreateFrame("Frame", id("MagicResFrame1"), resistFrame, "MagicResistanceFrameTemplate", 6);
magicRes1.SetPoint("TOP", resistFrame, "TOP", 0, 0);
magicRes1.SetSize(32, 32);
@@ -82,7 +78,7 @@ if(!aio.AddAddon()) {
/**
* This is for the Characters left picture and class,race, name details.
* Only created once.
* Only created once per bot panel.
*/
function AddPortrait(parent: WoWAPI.Frame, botData: BotData) {
const portrait = parent.CreateTexture(id("Portrait", botData.entry), "ARTWORK");
@@ -122,14 +118,16 @@ if(!aio.AddAddon()) {
infoTextFont.SetPoint("CENTER",0,0);
}
function AddCharacterModel(parent: WoWAPI.Frame, botData: BotData) {
const frameChar = CreateFrame("PlayerModel", id("ModelFrame", botData.entry), parent, null, botData.entry);
frameChar.SetPoint("TOP", -5, -82);
frameChar.SetSize(240, 160);
frameChar.SetSize(240, 175);
frameChar.SetUnit("target");
frameChar.SetFacing(0.3);
frameChar.SetFrameStrata("MEDIUM");
frameChar.SetFacing(0.3);
frameChar.SetAlpha(0.65);
frameChar.SetGlow(0.9);
frameChar.SetFrameStrata("MEDIUM");
}
function UpdateEquipFrame(group: 'left' | 'right' | 'weapons', parent: WoWAPI.Frame, botData: BotData) {
@@ -156,12 +154,13 @@ if(!aio.AddAddon()) {
equipSlot.SetPoint("TOPLEFT", i*40+1, 0);
else
equipSlot.SetPoint("TOPLEFT", 0, -i*40-1);
equipSlot.SetSize(40, 40);
equipSlot.SetScript("OnEnter", ItemSlotOnEnter);
equipSlot.SetScript("OnLeave", ItemSlotOnLeave);
equipSlot.SetScript("OnClick", ItemSlotOnClick);
const equippedItemId = botData.equipment[slotOrder[i]];
const equippedItem: Equipment = botData.equipment[slotOrder[i]];
let itemIcon: WoWAPI.TexturePath;
let itemId: number;
let idsuffix: string | number;
@@ -174,13 +173,13 @@ if(!aio.AddAddon()) {
}
// If we have a piece of equipment add the icon template
if(equippedItemId && equippedItemId > 0) {
itemIcon = GetItemIcon(equippedItemId);
if(equippedItem && equippedItem.entry > 0) {
itemIcon = GetItemIcon(equippedItem.entry);
idsuffix = slotOrder[i];
}
// If there is not a piece of equipment add the background texture
if(!equippedItemId && slotOrder[i] > 0) {
if(!equippedItem && slotOrder[i] > 0) {
let slotName = BotSlotName[slotOrder[i]];
if(slotOrder[i] === BotEquipSlot.FINGER1) slotName = "FINGER0";
@@ -196,11 +195,11 @@ if(!aio.AddAddon()) {
const itemTexture = equipSlot.CreateTexture(id(`ItemTexture-${idsuffix}`), "OVERLAY");
itemTexture.SetTexture(itemIcon);
itemTexture.SetPoint("CENTER", 0, 0);
itemTexture.SetSize(36,36);
itemTexture.SetSize(36,36);
ComponentsPool.set(compId(botData.entry, `ItemSlotTexture-${itemSlotId}`), itemTexture);
}
}
function AddEquipmentFrames(parent: WoWAPI.Frame, botData: BotData) {
// Get all our frames
@@ -215,7 +214,7 @@ if(!aio.AddAddon()) {
equipFrame = CreateFrame("Frame", id("LeftEquipment"), parent, null, 1);
equipFrame.SetPoint("TOPLEFT", 20, -73);
equipFrame.SetSize(40, 330);
UpdateEquipFrame('left', equipFrame, botData);
UpdateEquipFrame('left', equipFrame, botData);
ComponentsPool.set(compId(botData.entry, "LeftEquipment"), equipFrame);
}
@@ -225,7 +224,7 @@ if(!aio.AddAddon()) {
equipFrame.SetPoint("TOPRIGHT", -40, -73);
equipFrame.SetSize(40, 330);
ComponentsPool.set(compId(botData.entry, "RightEquipment"), equipFrame);
UpdateEquipFrame('right', equipFrame, botData);
UpdateEquipFrame('right', equipFrame, botData);
}
@@ -239,8 +238,68 @@ if(!aio.AddAddon()) {
// placeholder.SetAllPoints(equipFrame);
// placeholder.SetTexture(0,0,0,0.8);
}
}
function CreateStats(parent: WoWAPI.Frame, botData: BotData) {
const reverseObject = (obj: { [key: string]: any }): { [key: string]: string } =>
Object.fromEntries(Object.entries(obj).map(([k, v]) => [v, k]));
const lookup = reverseObject(BotStat);
const statsFrame = CreateFrame("Frame", id("CharacterAttr"), parent, null, 1);
statsFrame.SetSize(230,78);
statsFrame.SetPoint("TOPLEFT", 67, -231);
statsFrame.SetFrameStrata("HIGH");
statsFrame.SetAlpha(1.0);
statsFrame.SetBackdropColor(0,0,0,1.0);
const leftTop = statsFrame.CreateTexture(id("StatLeftTop"), "BACKGROUND");
leftTop.SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-StatBackground");
leftTop.SetSize(115,16);
leftTop.SetPoint("TOPLEFT", 0, 0);
leftTop.SetTexCoord(0, 0.8984375, 0, 0.125);
const leftmiddle = statsFrame.CreateTexture(id("StatLeftMiddle"), "BACKGROUND");
leftmiddle.SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-StatBackground");
leftmiddle.SetSize(115,113);
leftmiddle.SetPoint("TOPLEFT", leftTop, "BOTTOMLEFT", 0, 0);
leftmiddle.SetTexCoord(0, 0.8984375, 0.125, 0.1953125);
const leftBottom = statsFrame.CreateTexture(id("StatLeftBottom"), "BACKGROUND");
leftBottom.SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-StatBackground");
leftBottom.SetSize(115,16);
leftBottom.SetPoint("TOPLEFT", leftmiddle, "BOTTOMLEFT", 0, 0);
leftBottom.SetTexCoord(0, 0.8984375, 0.484375, 0.609375);
const rightTop = statsFrame.CreateTexture(id("StatRightTop"), "BACKGROUND");
rightTop.SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-StatBackground");
rightTop.SetSize(115,16);
rightTop.SetPoint("TOPLEFT", leftTop, "TOPRIGHT",0, 0);
rightTop.SetTexCoord(0, 0.8984375, 0, 0.125);
const rightMiddle = statsFrame.CreateTexture(id("StatRightMiddle"), "BACKGROUND");
rightMiddle.SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-StatBackground");
rightMiddle.SetSize(115,113);
rightMiddle.SetPoint("TOPLEFT", leftmiddle, "TOPRIGHT", 0, 0);
rightMiddle.SetTexCoord(0, 0.8984375, 0.125, 0.1953125);
const rightBottom = statsFrame.CreateTexture(id("StatRightBottom"), "BACKGROUND");
rightBottom.SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-StatBackground");
rightBottom.SetSize(115,16);
rightBottom.SetPoint("TOPLEFT", leftBottom, "TOPRIGHT", 0, 0);
rightBottom.SetTexCoord(0, 0.8984375, 0.484375, 0.609375);
let counter = 0;
// for(const stats in botData.stats.left) {
// const stat = statsFrame.CreateFontString(id(`Stat-${stats}`), "ARTWORK", "GameFontNormalSmall");
// stat.SetPoint("TOPLEFT", statsFrame, "TOPLEFT", 10, -10 - (counter * 14));
// stat.SetText(lookup[stats] + " " + botData.stats[stats]);
// counter++;
// }
}
function SetBackground(parent: WoWAPI.Frame) {
@@ -286,12 +345,16 @@ if(!aio.AddAddon()) {
});
}
/**
* START OF EVENT HANDLERS
*/
function ItemSlotOnEnter(frame: WoWAPI.Button) {
const botId = botStorage.GetActive();
const itemId = botStorage.GetBotItem(botId, <BotEquipmentSlotNum>frame.GetID());
const theItem = botStorage.GetBotItem(botId, <BotEquipmentSlotNum>frame.GetID());
GameTooltip.SetOwner(frame, "ANCHOR_RIGHT");
if(itemId) {
GameTooltip.SetHyperlink(`item:${itemId}:0:0:0:0:0:0:0`);
if(theItem) {
GameTooltip.SetHyperlink(theItem.link);
} else {
if(frame.GetID() == 90) {
GameTooltip.SetText("Tabard");
@@ -325,14 +388,14 @@ if(!aio.AddAddon()) {
function ItemSlotOnClick(frame: WoWAPI.Button, button: string) {
const botId = botStorage.GetActive();
const itemId = botStorage.GetBotItem(botId, <BotEquipmentSlotNum>frame.GetID());
const theItem = botStorage.GetBotItem(botId, <BotEquipmentSlotNum>frame.GetID());
const [compItem, compItemId, compItemLink] = GetCursorInfo();
print(`CursorHasItem: ${compItemLink}`);
if(itemId && !compItem) {
if(theItem && !compItem) {
if(button == "LeftButton") {
PickupItem(itemId);
PickupItem(theItem.link);
return;
}
}
@@ -340,21 +403,29 @@ if(!aio.AddAddon()) {
if(compItem) {
const slot = frame.GetID();
aio.Handle("BotMgr", "EquipItem",botId, slot, compItemId);
aio.Handle("BotMgr", "EquipTheItem", GetUnitName("player", false), botId, slot, compItemId, compItemLink);
// Attempt to equip the item.
PlaySound("INTERFACESOUND_CURSORDROPOBJECT");
ClearCursor();
}
// else {
// if(CursorHasItem()) {
// const [compItem, compItemId, compItemLink] = GetCursorInfo();
// botStorage.EquipBotItem(botId, <BotEquipmentSlotNum>frame.GetID(), compItemId);
// }
// }
}
botMgrHandlers.OnEquipSuccess = (botId: number, slot: BotEquipmentSlotNum, itemId: number, itemLink: string) => {
const itemTexture = <WoWAPI.Texture>ComponentsPool.get(compId(botId, `ItemSlotTexture-${slot}`));
itemTexture.SetTexture(GetItemIcon(itemId));
// Hide Tooltips otherwise it will show old item.
const BotTooltip = <WoWAPI.GameTooltip>ComponentsPool.get(compId(botId, "tooltip"));
BotTooltip.Hide();
GameTooltip.Hide();
}
botMgrHandlers.OnEquipFail = (botId: number, slot: BotEquipmentSlotNum, itemId: number, itemLink: string) => {
PlaySound("ITEMGENERICSOUND");
ClearCursor();
}
/**
* Shows or Creates a new Bot Equipment Management Frame
* Every NPC Bot that is requested to be managed will get their own unique frame. This
@@ -394,7 +465,23 @@ if(!aio.AddAddon()) {
mainFrame.SetScript("OnLeave", (frame) => {
frame.SetFrameLevel(5);
});
mainFrame.RegisterEvent("CURSOR_UPDATE");
mainFrame.RegisterEvent("ITEM_LOCK_CHANGED");
mainFrame.RegisterEvent("ITEM_UNLOCKED");
mainFrame.SetScript("OnEvent", (frame: WoWAPI.Frame, eventName: WoWAPI.Event, ...args) => {
if(eventName == "ITEM_LOCKED") {
GetCursorInfo()
print(args);
for(const arg of args) {
print(arg);
}
}
});
// mainFrame.Hide();
BotItemTooltip = CreateFrame("GameTooltip", id("ItemToolTip"+botData.entry), mainFrame, "GameTooltipTemplate", botData.entry);
@@ -407,8 +494,10 @@ if(!aio.AddAddon()) {
AddCharacterModel(mainFrame, botData);
AddResistFrame(mainFrame);
AddEquipmentFrames(mainFrame, botData);
CreateStats(mainFrame, botData);
AddSoundEffects(mainFrame);
InfoFramePool.set(botData.entry, mainFrame);
ComponentsPool.set(compId(botData.entry, "tooltip"), <WoWAPI.GameTooltip>BotItemTooltip);
@@ -501,11 +590,11 @@ if(!aio.AddAddon()) {
}
botMgrHandlers.ShowFrame = (botData: BotData) => {
// Update the botData manager for this Bot on panel show.
botMgrHandlers.ShowFrame = (botData: BotData) =>{
botStorage.UpdateBotData(botData.entry, botData);
ShowBotFrame(botData);
}
ShowBotFrame(botData);
}
}

View File

@@ -14,10 +14,20 @@ import {
ClassesMapping,
CharacterClass,
RacesMapping,
CharacterRace
CharacterRace,
ItemQuality,
QualityType
} from "../../constants/idmaps";
export type Equipment = {
entry: number,
link: string,
quality?: QualityType,
itemLevel?: number,
enchantmentId?: number,
}
export type EquipmentList = Record<BotEquipmentSlotNum, Equipment>;
/**
* Everything we ever wanted to know about the bot info on load
@@ -30,7 +40,7 @@ import {
classId: number,
race: CharacterRace,
raceId: number,
equipment?: Record<string, number>, // SlotName - ItemId See BotEquipSlot
equipment?: EquipmentList, // SlotName - ItemId See BotEquipSlot
stats?: Record<number, number>, // StatId - Value
};
@@ -39,9 +49,6 @@ import {
*/
const NpcDetailStorage = {} as Record<number, BotData>;
// // cheap way to load everything on the server side and force so client as access. (Don't love this at all will need to work back into plugin);
// const loader = BotStat || BotEquipSlot || BotEquipLast || BotStatLast || ClassesMapping || RacesMapping;
/**
* Get the current targetted npc bot or returns undefined if not a bot.
* @param player
@@ -83,12 +90,42 @@ function TargetIsEligible(player: Player) {
return false;
}
function GetMeleeStats () {
return {
left: [
BotStat.STRENGTH,
BotStat.AGILITY,
BotStat.DAMAGE_MIN,
BotStat.DAMAGE_MAX,
BotStat.ATTACK_POWER,
BotStat.HIT_RATING,
BotStat.CRIT_RATING,
BotStat.EXPERTISE,
BotStat.ARMOR_PENETRATION_RATING,
],
right: [
BotStat.HASTE_RATING,
BotStat.ARMOR,
BotStat.STAMINA,
BotStat.DEFENSE_SKILL_RATING,
BotStat.DODGE_RATING,
BotStat.PARRY_RATING,
BotStat.BLOCK_RATING,
BotStat.BLOCK_VALUE
]
}
}
function GetCasterStats() {
}
/**
* @noSelf
*/
function GetBotDetails(bot: Creature): BotData {
const owner = bot.GetBotOwner();
const owner = bot.GetBotOwner();
// We can use bot entrys since they are 1:1 with GUIDs for shorter storage keys
NpcDetailStorage[bot.GetEntry()] = {
@@ -98,21 +135,44 @@ function GetBotDetails(bot: Creature): BotData {
class: ClassesMapping[bot.GetBotClass()],
classId: bot.GetBotClass(),
race: RacesMapping[bot.GetRace()],
raceId: bot.GetRace(),
equipment: {},
// stats: {},
raceId: bot.GetRace(),
equipment: {} as EquipmentList,
stats: {},
};
print(bot.GetBotRoles());
// Get all the equipment
for(let slot=0; slot <= BotEquipLast; slot++) {
const equipment = bot.GetBotEquipment(<BotEquipmentSlotNum>slot);
if(equipment) {
NpcDetailStorage[bot.GetEntry()].equipment[slot] = equipment.GetEntry();
print(`Slot: ${BotSlotName[slot]} Item: ${equipment.GetEntry()}`);
if(equipment) {
NpcDetailStorage[bot.GetEntry()].equipment[slot] = {
entry: equipment.GetEntry(),
link: equipment.GetItemLink(),
quality: <QualityType>equipment.GetQuality(),
itemLevel: equipment.GetItemLevel(),
enchantmentId: equipment.GetEnchantmentId(0),
}
} else {
NpcDetailStorage[bot.GetEntry()].equipment[slot] = undefined;
}
}
// get the stats we care about by Class
// This will determine what stats to lookup for the bot.
const lookups = GetMeleeStats();
lookups.left.forEach(stat => {
const result = bot.GetBotStat(stat);
NpcDetailStorage[bot.GetEntry()].stats[stat] = result;
});
lookups.right.forEach(stat => {
const result = bot.GetBotStat(stat);
NpcDetailStorage[bot.GetEntry()].stats[stat] = result;
});
return NpcDetailStorage[bot.GetEntry()];
}
@@ -124,27 +184,30 @@ function GetBotDetails(bot: Creature): BotData {
* @param command
* @returns
*/
function EquipItem(botEntry: number, slot: BotEquipmentSlotNum, item: number): void {
function EquipTheItem(player: string, botEntry: number, slot: BotEquipmentSlotNum, item: number, link: string ): void {
if(botEntry && typeof botEntry !== 'number') {
return;
}
print(`Bot: ${botEntry} Slot: ${slot} Item: ${item}`);
// const isEligible = bot.BotCanEquipItem(item, slot);
const owner = GetPlayerByName(player);
const creatures = owner.GetCreaturesInRange(60, botEntry) as Creature[];
const bot = creatures[0];
const isEligible = bot.BotCanEquipItem(item, slot);
if(!isEligible) {
log.error(`Bot cannot equip item: ${item} in slot: ${slot}`);
return;
}
// if(!isEligible) {
// log.error(`Bot cannot equip item: ${item} in slot: ${slot}`);
// return;
// }
// if(bot.BotEquipItem(item, slot)) {
// NpcDetailStorage[bot.GetEntry()].equipment[slot] = item;
// aio.Handle(bot.GetBotOwner(), 'BotMgr', 'EquipSuccess', { slot, item});
// } else {
// log.error(`Bot failed to equip item: ${item} in slot: ${slot}`);
// aio.Handle(bot.GetBotOwner(), 'BotMgr', 'EquipFail', { slot, item});
// }
if(bot.BotEquipItem(item, slot)) {
GetBotDetails(bot);
log.log(`Bot successfully equipped item: ${item} in slot: ${slot}`);
aio.Handle(bot.GetBotOwner(), 'BotMgr', 'OnEquipSuccess',botEntry, slot, item, link);
} else {
log.error(`Bot failed to equip item: ${item} in slot: ${slot}`);
aio.Handle(bot.GetBotOwner(), 'BotMgr', 'OnEquipFail', botEntry, slot, item, link);
}
}
@@ -201,10 +264,11 @@ function GetBotPanelInfo(player: Player): void {
const botMgrHandlers = aio.AddHandlers('BotMgr', {
TargetIsEligible,
GetBotPanelInfo,
EquipItem
"EquipTheItem": EquipTheItem
});
RegisterPlayerEvent(
PlayerEvents.PLAYER_EVENT_ON_COMMAND,
(...args) => ShowBotMgr(...args)
);
);

View File

@@ -101,7 +101,7 @@ export const BotStat = {
BOT_STAT_MOD_RESISTANCE_START: 51, // Assuming BOT_STAT_MOD_ARMOR is defined somewhere
} as const;
export const BotStatLast = 58;
export const BotStatLast = 58;
export const UIInvSlot = {
AMMOSLOT: "AMMOSLOT",
@@ -165,4 +165,158 @@ export const RacesMapping: Record<number, string> = {
12: "Worgen",
} as const;
export type CharacterRace = typeof RacesMapping[keyof typeof RacesMapping];
export type CharacterRace = typeof RacesMapping[keyof typeof RacesMapping];
export const BotTalentSpecs = {
WARRIOR_ARMS : 1,
WARRIOR_FURY : 2,
WARRIOR_PROTECTION : 3,
PALADIN_HOLY : 4,
PALADIN_PROTECTION : 5,
PALADIN_RETRIBUTION : 6,
HUNTER_BEASTMASTERY : 7,
HUNTER_MARKSMANSHIP : 8,
HUNTER_SURVIVAL : 9,
ROGUE_ASSASSINATION : 10,
ROGUE_COMBAT : 11,
ROGUE_SUBTLETY : 12,
PRIEST_DISCIPLINE : 13,
PRIEST_HOLY : 14,
PRIEST_SHADOW : 15,
DK_BLOOD : 16,
DK_FROST : 17,
DK_UNHOLY : 18,
SHAMAN_ELEMENTAL : 19,
SHAMAN_ENHANCEMENT : 20,
SHAMAN_RESTORATION : 21,
MAGE_ARCANE : 22,
MAGE_FIRE : 23,
MAGE_FROST : 24,
WARLOCK_AFFLICTION : 25,
WARLOCK_DEMONOLOGY : 26,
WARLOCK_DESTRUCTION : 27,
DRUID_BALANCE : 28,
DRUID_FERAL : 29,
DRUID_RESTORATION : 30,
DEFAULT : 31,
BEGIN : 1,
END : 31
} as const;
export function talentSpecName(id: number) {
return Object.keys(BotTalentSpecs).find(key => BotTalentSpecs[key] === id);
}
export const BotRoles = {
NONE : 0,
TANK : 1,
TANK_OFF : 2,
DPS : 4,
HEAL : 8,
RANGED : 16,
PARTY : 32, // hidden
GATHERING_MINING : 64,
GATHERING_HERBALISM : 128,
GATHERING_SKINNING : 256,
GATHERING_ENGINEERING : 512,
AUTOLOOT : 1024,
AUTOLOOT_POOR : 2048,
AUTOLOOT_COMMON : 4096,
AUTOLOOT_UNCOMMON : 8192,
AUTOLOOT_RARE : 16384,
AUTOLOOT_EPIC : 32768,
AUTOLOOT_LEGENDARY : 65536,
MASK_MAIN : (1 | 2 | 4 | 8 | 16),
MASK_GATHERING : (64 | 128 | 256 | 512),
MASK_LOOTING : (2048 | 4096 | 8192 | 16384 | 32768 | 65536),
BOT_MAX_ROLE : 131072,
} as const;
/**************** ITEM CONSTANTS *************************/
export const ItemQuality = {
Poor: 0,
Common: 1,
Uncommon: 2,
Rare: 3,
Epic: 4,
Legendary: 5,
Artifact: 6,
Heirlooms: 7,
} as const;
export const ItemStat = {
MANA: 0,
HEALTH: 1,
AGILITY: 3,
STRENGTH: 4,
INTELLECT: 5,
SPIRIT: 6,
STAMINA: 7,
DEFENSE_SKILL_RATING: 12,
DODGE_RATING: 13,
PARRY_RATING: 14,
BLOCK_RATING: 15,
HIT_MELEE_RATING: 16,
HIT_RANGED_RATING: 17,
HIT_SPELL_RATING: 18,
CRIT_MELEE_RATING: 19,
CRIT_RANGED_RATING: 20,
CRIT_SPELL_RATING: 21,
HIT_TAKEN_MELEE_RATING: 22,
HIT_TAKEN_RANGED_RATING: 23,
HIT_TAKEN_SPELL_RATING: 24,
CRIT_TAKEN_MELEE_RATING: 25,
CRIT_TAKEN_RANGED_RATING: 26,
CRIT_TAKEN_SPELL_RATING: 27,
HASTE_MELEE_RATING: 28,
HASTE_RANGED_RATING: 29,
HASTE_SPELL_RATING: 30,
HIT_RATING: 31,
CRIT_RATING: 32,
HIT_TAKEN_RATING: 33,
CRIT_TAKEN_RATING: 34,
RESILIENCE_RATING: 35,
HASTE_RATING: 36,
EXPERTISE_RATING: 37,
ATTACK_POWER: 38,
RANGED_ATTACK_POWER: 39,
FERAL_ATTACK_POWER: 40, // Note: This is not used as of 3.3
SPELL_HEALING_DONE: 41,
SPELL_DAMAGE_DONE: 42,
MANA_REGENERATION: 43,
ARMOR_PENETRATION_RATING: 44,
SPELL_POWER: 45,
HEALTH_REGEN: 46,
SPELL_PENETRATION: 47,
BLOCK_VALUE: 48,
} as const;
export const DamageType = {
Physical: 0,
Holy: 1,
Fire: 2,
Nature: 3,
Frost: 4,
Shadow: 5,
Arcane: 6,
} as const;
export const SocketColor = {
Meta: 1,
Red: 2,
Yellow: 4,
Blue: 8,
} as const;
export const SocketBonus = {
3312: '+8 Strength',
3313: '+8 Agility',
3305: '+12 Stamina',
3: '+8 Intellect',
2872: '+9 Healing',
3753: '+9 Spell Power',
3877: '+16 Attack Power',
} as const;