enUS Localization Update Announcement / localization-update-announcement (push) Canceled after 0s
387 lines
16 KiB
Lua
387 lines
16 KiB
Lua
-- Bagshui WoW Game Information Loader
|
|
-- Exposes: BsGameInfo (and Bagshui.components.GameInfo)
|
|
-- Raises: BAGSHUI_GAME_UPDATE
|
|
|
|
Bagshui:AddComponent(function()
|
|
-- Information constructed here is used for localization and generation of default categories.
|
|
|
|
Bagshui:AddConstants({
|
|
BS_INVENTORY_SLOT_NAMES = {
|
|
HeadSlot = _G.INVTYPE_HEAD,
|
|
NeckSlot = _G.INVTYPE_NECK,
|
|
ShoulderSlot = _G.INVTYPE_SHOULDER,
|
|
BackSlot = _G.INVTYPE_CLOAK,
|
|
ChestSlot = _G.INVTYPE_CHEST,
|
|
ShirtSlot = _G.INVTYPE_SHIELD,
|
|
TabardSlot = _G.INVTYPE_TABARD,
|
|
WristSlot = _G.INVTYPE_WRIST,
|
|
HandsSlot = _G.INVTYPE_HAND,
|
|
WaistSlot = _G.INVTYPE_WAIST,
|
|
LegsSlot = _G.INVTYPE_LEGS,
|
|
FeetSlot = _G.INVTYPE_FEET,
|
|
Finger0Slot = _G.INVTYPE_FINGER,
|
|
Finger1Slot = _G.INVTYPE_FINGER,
|
|
Trinket0Slot = _G.INVTYPE_TRINKET,
|
|
Trinket1Slot = _G.INVTYPE_TRINKET,
|
|
MainHandSlot = _G.INVTYPE_WEAPONMAINHAND,
|
|
SecondaryHandSlot = _G.INVTYPE_WEAPONOFFHAND,
|
|
RangedSlot = _G.INVTYPE_RANGED,
|
|
},
|
|
|
|
-- Map item subclasses to the skills they require.
|
|
-- Only needed when the subclass doesn't match the skill name.
|
|
BS_ITEM_SUBCLASS_TO_SKILL = {
|
|
["Fishing Pole"] = "Fishing",
|
|
["One-Handed Axes"] = "Axes",
|
|
["One-Handed Maces"] = "Maces",
|
|
["One-Handed Swords"] = "Swords",
|
|
["Plate"] = "Plate Mail",
|
|
["Shields"] = "Shield",
|
|
},
|
|
})
|
|
|
|
-- Prepare storage of game information.
|
|
local GameInfo = {
|
|
|
|
-- Character classes.
|
|
-- Keys: English UPPERCASE names
|
|
-- Values: English Proper Noun Case names
|
|
characterClasses = {
|
|
["DRUID"] = "Druid",
|
|
["HUNTER"] = "Hunter",
|
|
["MAGE"] = "Mage",
|
|
["PALADIN"] = "Paladin",
|
|
["PRIEST"] = "Priest",
|
|
["ROGUE"] = "Rogue",
|
|
["SHAMAN"] = "Shaman",
|
|
["WARLOCK"] = "Warlock",
|
|
["WARRIOR"] = "Warrior",
|
|
},
|
|
|
|
-- Item classes added here are not covered by GetAuctionItemClasses and
|
|
-- need to be localized manually in the locale file.
|
|
-- Key case must match the case of the item class as returned by the game.
|
|
itemClasses = {
|
|
["Key"] = "Key",
|
|
["Miscellaneous"] = "Miscellaneous",
|
|
["Quest"] = "Quest",
|
|
["Trade Goods"] = "Trade Goods",
|
|
},
|
|
|
|
-- Item subclasses added here are not covered by GetAuctionItemSubClasses.
|
|
itemSubclasses = {},
|
|
|
|
-- Need these mappings to call GetAuctionItemSubClasses.
|
|
itemSubClassIds = {
|
|
Weapon = 1,
|
|
Armor = 2,
|
|
Container = 3,
|
|
Projectile = 6,
|
|
Quiver = 7,
|
|
Recipe = 8,
|
|
},
|
|
|
|
-- List of inventory slot locations, used to generate rule function templates.
|
|
inventorySlots = {},
|
|
|
|
-- Class translation helper tables.
|
|
reverseTranslatedCharacterClasses = {},
|
|
lowercaseReverseTranslatedCharacterClasses = {},
|
|
lowercaseToNormalCaseReverseTranslatedCharacterClasses = {},
|
|
lowercaseLocalizedCharacterClasses = {},
|
|
lowercaseToNormalCaseLocalizedCharacterClasses = {},
|
|
|
|
-- Game state tracking.
|
|
currentZone = "",
|
|
currentRealZone = "",
|
|
currentSubZone = "",
|
|
currentMinimapZone = "",
|
|
playerGroupType = BS_GAME_PLAYER_GROUP_TYPE.SOLO,
|
|
isInInstance = nil, -- Game returns 1/nil so we're just going with it here.
|
|
instanceType = "none",
|
|
lootMethod = "freeforall",
|
|
masterLooterPartyId = nil,
|
|
}
|
|
Bagshui.environment.BsGameInfo = GameInfo
|
|
Bagshui.components.GameInfo = GameInfo
|
|
|
|
-- Populate item classes and subclasses.
|
|
|
|
GameInfo.itemClasses["Weapon"], GameInfo.itemClasses["Armor"], GameInfo.itemClasses["Container"], GameInfo.itemClasses["Consumable"], GameInfo.itemClasses["Trade Goods"], GameInfo.itemClasses["Projectile"], GameInfo.itemClasses["Quiver"], GameInfo.itemClasses["Recipe"], GameInfo.itemClasses["Reagent"], GameInfo.itemClasses["Miscellaneous"] =
|
|
_G.GetAuctionItemClasses()
|
|
|
|
for itemClass, localizedItemClass in pairs(GameInfo.itemClasses) do
|
|
GameInfo.itemSubclasses[itemClass] = {}
|
|
end
|
|
|
|
GameInfo.itemSubclasses["Weapon"]["One-Handed Axes"], GameInfo.itemSubclasses["Weapon"]["Two-Handed Axes"], GameInfo.itemSubclasses["Weapon"]["Bows"], GameInfo.itemSubclasses["Weapon"]["Guns"], GameInfo.itemSubclasses["Weapon"]["One-Handed Maces"], GameInfo.itemSubclasses["Weapon"]["Two-Handed Maces"], GameInfo.itemSubclasses["Weapon"]["Polearms"], GameInfo.itemSubclasses["Weapon"]["One-Handed Swords"], GameInfo.itemSubclasses["Weapon"]["Two-Handed Swords"], GameInfo.itemSubclasses["Weapon"]["Staves"], GameInfo.itemSubclasses["Weapon"]["Fist Weapons"], GameInfo.itemSubclasses["Weapon"]["Miscellaneous"], GameInfo.itemSubclasses["Weapon"]["Daggers"], GameInfo.itemSubclasses["Weapon"]["Thrown"], GameInfo.itemSubclasses["Weapon"]["Crossbows"], GameInfo.itemSubclasses["Weapon"]["Wands"], GameInfo.itemSubclasses["Weapon"]["Fishing Pole"] =
|
|
_G.GetAuctionItemSubClasses(GameInfo.itemSubClassIds.Weapon)
|
|
|
|
GameInfo.itemSubclasses["Armor"]["Miscellaneous"], GameInfo.itemSubclasses["Armor"]["Cloth"], GameInfo.itemSubclasses["Armor"]["Leather"], GameInfo.itemSubclasses["Armor"]["Mail"], GameInfo.itemSubclasses["Armor"]["Plate"], GameInfo.itemSubclasses["Armor"]["Shields"], GameInfo.itemSubclasses["Armor"]["Librams"], GameInfo.itemSubclasses["Armor"]["Idols"], GameInfo.itemSubclasses["Armor"]["Totems"] =
|
|
_G.GetAuctionItemSubClasses(GameInfo.itemSubClassIds.Armor)
|
|
|
|
GameInfo.itemSubclasses["Container"]["Bag"], GameInfo.itemSubclasses["Container"]["Soul Bag"], GameInfo.itemSubclasses["Container"]["Herb Bag"], GameInfo.itemSubclasses["Container"]["Enchanting Bag"] =
|
|
_G.GetAuctionItemSubClasses(GameInfo.itemSubClassIds.Container)
|
|
|
|
GameInfo.itemSubclasses["Projectile"]["Arrow"], GameInfo.itemSubclasses["Projectile"]["Bullet"] =
|
|
_G.GetAuctionItemSubClasses(GameInfo.itemSubClassIds.Projectile)
|
|
|
|
GameInfo.itemSubclasses["Quiver"]["Quiver"], GameInfo.itemSubclasses["Quiver"]["Ammo Pouch"] =
|
|
_G.GetAuctionItemSubClasses(GameInfo.itemSubClassIds.Quiver)
|
|
|
|
GameInfo.itemSubclasses["Recipe"]["Book"], GameInfo.itemSubclasses["Recipe"]["Leatherworking"], GameInfo.itemSubclasses["Recipe"]["Tailoring"], GameInfo.itemSubclasses["Recipe"]["Engineering"], GameInfo.itemSubclasses["Recipe"]["Blacksmithing"], GameInfo.itemSubclasses["Recipe"]["Cooking"], GameInfo.itemSubclasses["Recipe"]["Alchemy"], GameInfo.itemSubclasses["Recipe"]["First Aid"], GameInfo.itemSubclasses["Recipe"]["Enchanting"], GameInfo.itemSubclasses["Recipe"]["Fishing"] =
|
|
_G.GetAuctionItemSubClasses(GameInfo.itemSubClassIds.Recipe)
|
|
|
|
-- Item subclasses not provided by GetAuctionItemClasses which need to be localized manually in the locale file.
|
|
GameInfo.itemSubclasses["Key"]["Key"] = "Key"
|
|
GameInfo.itemSubclasses["Miscellaneous"]["Junk"] = "Junk"
|
|
GameInfo.itemSubclasses["Quest"]["Quest"] = "Quest"
|
|
GameInfo.itemSubclasses["Trade Goods"]["Devices"] = "Devices"
|
|
GameInfo.itemSubclasses["Trade Goods"]["Explosives"] = "Explosives"
|
|
GameInfo.itemSubclasses["Trade Goods"]["Parts"] = "Parts"
|
|
GameInfo.itemSubclasses["Trade Goods"]["Trade Goods"] = "Trade Goods"
|
|
|
|
-- Populate Inventory Slots.
|
|
-- Automatically build the English -> localized translations of inventory slot names.
|
|
-- (INVTYPE_X -> localized doesn't need to be stored because it's always available in global variables).
|
|
for globalVariable, englishName in pairs({
|
|
-- Armor.
|
|
INVTYPE_BODY = "Shirt",
|
|
INVTYPE_CHEST = "Chest",
|
|
INVTYPE_CLOAK = "Back",
|
|
INVTYPE_FEET = "Feet",
|
|
INVTYPE_FINGER = "Finger",
|
|
INVTYPE_HAND = "Hands",
|
|
INVTYPE_HEAD = "Head",
|
|
INVTYPE_LEGS = "Legs",
|
|
INVTYPE_NECK = "Neck",
|
|
INVTYPE_ROBE = "Chest",
|
|
INVTYPE_SHOULDER = "Shoulder",
|
|
INVTYPE_WAIST = "Waist",
|
|
INVTYPE_WRIST = "Wrist",
|
|
|
|
-- Weapons.
|
|
INVTYPE_RANGED = "Ranged",
|
|
INVTYPE_WEAPON = "One-Hand",
|
|
INVTYPE_2HWEAPON = "Two-Hand",
|
|
INVTYPE_WEAPONMAINHAND = "Main Hand",
|
|
INVTYPE_WEAPONOFFHAND = "Off Hand",
|
|
INVTYPE_SHIELD = "Off Hand",
|
|
INVTYPE_HOLDABLE = "IHeld In Off-hand",
|
|
INVTYPE_RELIC = "Relic",
|
|
|
|
-- Other.
|
|
INVTYPE_BAG = "Bag",
|
|
INVTYPE_TABARD = "Tabard",
|
|
INVTYPE_TRINKET = "Trinket",
|
|
}) do
|
|
GameInfo.inventorySlots[englishName] = _G[globalVariable]
|
|
end
|
|
|
|
--- Some game information can't be built until the Bagshui localization has been loaded.
|
|
function GameInfo:PopulatePostLocalizationInfo()
|
|
-- Professions to profession bags.
|
|
self.professionsToBags = {
|
|
[L.Enchanting] = L["Enchanting Bag"],
|
|
[L.Herbalism] = L["Herb Bag"],
|
|
}
|
|
|
|
-- Alphabetically sorted list of player classes.
|
|
self.sortedCharacterClasses = {}
|
|
for uppercaseClass, nounCaseClass in pairs(self.characterClasses) do
|
|
self.reverseTranslatedCharacterClasses[L[nounCaseClass]] = nounCaseClass
|
|
table.insert(self.sortedCharacterClasses, uppercaseClass)
|
|
end
|
|
table.sort(self.sortedCharacterClasses, function(a, b)
|
|
return L[self.characterClasses[a]] < L[self.characterClasses[b]]
|
|
end)
|
|
|
|
-- Provide a menu template for player classes.
|
|
-- Used by Category Editor and Inventory Edit Mode Direct Assignment menu.
|
|
self.characterClassMenu = {}
|
|
for _, class in ipairs(BsGameInfo.sortedCharacterClasses) do
|
|
table.insert(self.characterClassMenu, {
|
|
text = L[BsGameInfo.characterClasses[class]],
|
|
value = class,
|
|
})
|
|
end
|
|
|
|
-- Used by RequiresClass.
|
|
for _, nounCaseClass in pairs(self.characterClasses) do
|
|
self.lowercaseReverseTranslatedCharacterClasses[string.lower(L[nounCaseClass])] = string.lower(nounCaseClass)
|
|
self.lowercaseToNormalCaseReverseTranslatedCharacterClasses[string.lower(L[nounCaseClass])] = nounCaseClass
|
|
end
|
|
for _, nounCaseClass in pairs(self.characterClasses) do
|
|
self.lowercaseLocalizedCharacterClasses[string.lower(nounCaseClass)] = string.lower(L[nounCaseClass])
|
|
self.lowercaseToNormalCaseLocalizedCharacterClasses[string.lower(nounCaseClass)] = L[nounCaseClass]
|
|
end
|
|
end
|
|
|
|
--- Storage helper for `GameInfo:UpdateLocation()`.
|
|
--- Returns nil if the string is empty.
|
|
---@param str any
|
|
---@return any
|
|
local function nilForEmptyString(str)
|
|
return string.len(str or "") > 0 and str or nil
|
|
end
|
|
|
|
-- Tracking variables used to determine whether UpdateLocation() should raise BAGSHUI_GAME_UPDATE.
|
|
|
|
local oldZone, oldRealZone, oldSubZone, oldMinimapZone, oldInInstance, oldInstanceType
|
|
|
|
--- Store current location data.
|
|
function GameInfo:UpdateLocation()
|
|
oldZone = self.currentZone
|
|
oldRealZone = self.currentRealZone
|
|
oldSubZone = self.currentSubZone
|
|
oldMinimapZone = self.currentMinimapZone
|
|
oldInInstance = self.isInInstance
|
|
oldInstanceType = self.instanceType
|
|
|
|
self.currentZone = nilForEmptyString(_G.GetZoneText())
|
|
self.currentRealZone = nilForEmptyString(_G.GetRealZoneText())
|
|
self.currentSubZone = nilForEmptyString(_G.GetSubZoneText())
|
|
self.currentMinimapZone = nilForEmptyString(_G.GetMinimapZoneText())
|
|
self.isInInstance, self.instanceType = _G.IsInInstance()
|
|
|
|
if
|
|
oldZone ~= self.currentZone
|
|
or oldRealZone ~= self.currentRealZone
|
|
or oldSubZone ~= self.currentSubZone
|
|
or oldMinimapZone ~= self.currentMinimapZone
|
|
or oldInInstance ~= self.isInInstance
|
|
or oldInstanceType ~= self.instanceType
|
|
then
|
|
Bagshui:RaiseEvent("BAGSHUI_GAME_UPDATE")
|
|
end
|
|
end
|
|
|
|
-- Tracking variables used to determine whether UpdateLocation() should raise BAGSHUI_GAME_UPDATE.
|
|
|
|
local oldPlayerGroupType, oldLootMethod, oldMasterLooter
|
|
|
|
-- Reusable variable for `GetLootMethod()` return value.
|
|
local masterLooterPartyId
|
|
|
|
--- Store current group (as in party/raid) data.
|
|
function GameInfo:UpdateGroup()
|
|
oldPlayerGroupType = self.playerGroupType
|
|
oldLootMethod = self.lootMethod
|
|
oldMasterLooter = self.isMasterLooter
|
|
|
|
-- There's no GetGroupType() but we can infer it.=
|
|
self.playerGroupType = (
|
|
(_G.GetNumRaidMembers() or 0) > 0 and BS_GAME_PLAYER_GROUP_TYPE.RAID
|
|
or (_G.GetNumPartyMembers() or 0) > 0 and BS_GAME_PLAYER_GROUP_TYPE.PARTY
|
|
or BS_GAME_PLAYER_GROUP_TYPE.SOLO
|
|
)
|
|
|
|
-- Don't need the third return value from GetLootMethod() because
|
|
-- the second is 0 if the current player is the master looter.
|
|
self.lootMethod, masterLooterPartyId = _G.GetLootMethod()
|
|
self.isMasterLooter = (self.lootMethod == "master" and masterLooterPartyId == 0)
|
|
|
|
if
|
|
oldPlayerGroupType ~= self.playerGroupType
|
|
or oldLootMethod ~= self.lootMethod
|
|
or oldMasterLooter ~= self.isMasterLooter
|
|
then
|
|
Bagshui:RaiseEvent("BAGSHUI_GAME_UPDATE")
|
|
end
|
|
end
|
|
|
|
--- Zone output helper for the GameInfo slash handler.
|
|
--- Only prints both zones if they're different and non-nil.
|
|
---@param z1 string? First zone.
|
|
---@param z2 string? Second zone.
|
|
local function printNonNilZones(z1, z2)
|
|
local mainZone = z1 or z2
|
|
if not mainZone then
|
|
return
|
|
end
|
|
Bs:PrintBare(BS_INDENT .. mainZone)
|
|
if mainZone == z2 or not z2 then
|
|
return
|
|
end
|
|
Bs:PrintBare(BS_INDENT .. z2)
|
|
end
|
|
|
|
--- Event handling.
|
|
---@param event string Event identifier.
|
|
---@param arg1 any? Argument 1 from the event.
|
|
function GameInfo:OnEvent(event, arg1)
|
|
if event == "PLAYER_ENTERING_WORLD" then
|
|
self:UpdateLocation()
|
|
self:UpdateGroup()
|
|
return
|
|
end
|
|
|
|
if string.find(event, "ZONE") then
|
|
self:UpdateLocation()
|
|
return
|
|
end
|
|
|
|
if string.find(event, "PARTY") or string.find(event, "RAID") then
|
|
self:UpdateGroup()
|
|
return
|
|
end
|
|
|
|
if event == "BAGSHUI_LOCALIZATION_LOADED" then
|
|
self:PopulatePostLocalizationInfo()
|
|
return
|
|
end
|
|
end
|
|
|
|
--- Slash command helper called from `ItemInfo`'s slash handler.
|
|
--- This lets us have `/Bagshui Info Location` etc.
|
|
---@param tokens table Tokenized input from slash command.
|
|
---@return boolean handled `true` if ItemInfo shouldn't continue processing.
|
|
function GameInfo:HandleInfoSlash(tokens)
|
|
if
|
|
BsUtil.MatchLocalizedOrNon(tokens[2], "location")
|
|
or BsUtil.MatchLocalizedOrNon(tokens[2], "instance")
|
|
or BsUtil.MatchLocalizedOrNon(tokens[2], "zone")
|
|
then
|
|
Bs:Print(string.format(L.Symbol_Colon, L.Location))
|
|
|
|
Bs:PrintBare(string.format(L.Symbol_Colon, L.Zone))
|
|
printNonNilZones(self.currentZone, self.currentSubZone)
|
|
Bs:PrintBare(string.format(L.Symbol_Colon, L.Subzone))
|
|
printNonNilZones(self.currentSubZone, self.currentMinimapZone)
|
|
|
|
Bs:PrintBare(string.format(L.Symbol_Colon, L.Instance))
|
|
if not self.isInInstance or self.instanceType == "none" then
|
|
Bs:PrintBare(BS_INDENT .. L.NoneParenthesis)
|
|
else
|
|
Bs:PrintBare(BS_INDENT .. self.instanceType)
|
|
end
|
|
return true
|
|
elseif
|
|
BsUtil.MatchLocalizedOrNon(tokens[2], "group")
|
|
or BsUtil.MatchLocalizedOrNon(tokens[2], "party")
|
|
or BsUtil.MatchLocalizedOrNon(tokens[2], "loot")
|
|
or BsUtil.MatchLocalizedOrNon(tokens[2], "raid")
|
|
then
|
|
Bs:Print(string.format(L.Symbol_Colon, _G.GROUP))
|
|
Bs:PrintBare(BS_INDENT .. self.playerGroupType)
|
|
|
|
Bs:PrintBare(string.format(L.Symbol_Colon, _G.LOOT))
|
|
Bs:PrintBare(BS_INDENT .. string.format(L.Symbol_Colon, _G.LOOT_METHOD) .. " " .. self.lootMethod)
|
|
Bs:PrintBare(BS_INDENT .. "IsLootMaster? " .. tostring(self.isMasterLooter))
|
|
return true
|
|
end
|
|
end
|
|
|
|
Bagshui:RegisterEvent("BAGSHUI_LOCALIZATION_LOADED", GameInfo)
|
|
-- Location events.
|
|
Bagshui:RegisterEvent("PLAYER_ENTERING_WORLD", GameInfo)
|
|
Bagshui:RegisterEvent("MINIMAP_ZONE_CHANGED", GameInfo)
|
|
Bagshui:RegisterEvent("ZONE_CHANGED", GameInfo)
|
|
Bagshui:RegisterEvent("ZONE_CHANGED_NEW_AREA", GameInfo)
|
|
Bagshui:RegisterEvent("ZONE_CHANGED_INDOORS", GameInfo)
|
|
-- Group events.
|
|
Bagshui:RegisterEvent("PARTY_LOOT_METHOD_CHANGED", GameInfo)
|
|
Bagshui:RegisterEvent("PARTY_MEMBERS_CHANGED", GameInfo)
|
|
Bagshui:RegisterEvent("RAID_ROSTER_UPDATE", GameInfo)
|
|
end)
|