Quantcast
--[[
-- **************************************************************************
-- * TitanAmmo.lua
-- *
-- * By: Titan Panel Development Team
-- **************************************************************************
-- 2024 Jan - Combined Classic Era and Wrath into one version
-- 2019 Aug - reverted and updated for Classic
--
-- This will track the count of ammo (bows and guns) or thrown (knives) equipped.
-- Ammo is placed in the 'ammo' slot where Blizzard counts ALL of that *type of ammo*
-- regardless of where it is in your bags.
-- Thrown is placed in the actual display.weapon slot where Blizzard counts ALL of that *type of thrown*.
-- This forces a different routine to be used so the ammo must always be checked for type and count.
--
-- Note: Thrown has no durability. Not sure when Blizz implemented this
--]]
-- ******************************** Constants *******************************
local _G = getfenv(0);
local TITAN_AMMO_ID = "Ammo";
local TITAN_BUTTON = "TitanPanel"..TITAN_AMMO_ID.."Button"

local SHOOT_STACK = 200
local ARROW_STACK = 200
local THROW_STACK = 200

local LIM_GOOD = 2
local LIM_OK   = 1.5
local LIM_BAD  = .5

local game_version = select(4, GetBuildInfo())

local TITAN_AMMO_THRESHOLD_TABLE = { -- Use ammo stack and threshold limits above to calc colored text
	["INVTYPE_RANGEDRIGHT"] = {
		 Values = { SHOOT_STACK*LIM_BAD, SHOOT_STACK*LIM_OK, SHOOT_STACK*LIM_GOOD }, -- 100,150,400
		 Colors = { RED_FONT_COLOR, ORANGE_FONT_COLOR, NORMAL_FONT_COLOR, HIGHLIGHT_FONT_COLOR },
	 },
	["INVTYPE_RANGED"] = {
		 Values = { ARROW_STACK*LIM_BAD, ARROW_STACK*LIM_OK, ARROW_STACK*LIM_GOOD }, -- 100,150,400
		 Colors = { RED_FONT_COLOR, ORANGE_FONT_COLOR, NORMAL_FONT_COLOR, HIGHLIGHT_FONT_COLOR },
	 },
	["INVTYPE_THROWN"] = {
		 Values = { THROW_STACK/10, THROW_STACK/4, THROW_STACK/2 }, -- 20, 50, 100
		 Colors = { RED_FONT_COLOR, ORANGE_FONT_COLOR, NORMAL_FONT_COLOR, HIGHLIGHT_FONT_COLOR },
	 },
};

-- ******************************** Variables *******************************
local ammoSlotID = GetInventorySlotInfo("AmmoSlot")
local rangedSlotID = GetInventorySlotInfo("RangedSlot")

-- Info to show on the plugin
local display = {
	ammo_count  = 0,
	ammo_type   = "",
	ammo_name   = "", -- L["TITAN_AMMO_BUTTON_NOAMMO"]
	weapon      = "",
	weapon_type = "",
	mismatch    = false,
	mismatch_text = "",
	}

local L = LibStub("AceLocale-3.0"):GetLocale("TitanClassic", true)

local debug_flow = false

-- ******************************** Functions *******************************
--[[ local
-- **************************************************************************
-- NAME : debug_msg(Message)
-- DESC : Debug function to print message to chat frame
-- VARS : Message = message to print to chat frame
-- **************************************************************************
--]]
local function debug_msg(Message)
	local msg = ""
	local stamp = date("%H:%M:%S") -- date("%m/%d/%y %H:%M:%S")
	local milli = GetTime() -- seconds with millisecond precision (float)
	milli = string.format("%0.2F", milli - math.modf(milli))
	msg = msg..TitanUtils_GetGoldText(stamp..milli.." "..TITAN_AMMO_ID..": ")
	msg = msg..TitanUtils_GetGreenText(Message)
	DEFAULT_CHAT_FRAME:AddMessage(msg)
--		DEFAULT_CHAT_FRAME:AddMessage(TITAN_AMMO_ID..": " .. Message, 1.00, 0.49, 0.04)
end

local function ClrAmmoInfo()
	display.ammo_count = 0;
	display.ammo_type  = L["TITAN_AMMO_BUTTON_NOAMMO"]
	display.ammo_name  = ""
	display.weapon = ""
	display.weapon_type = ""
	display.mismatch = false
	display.mismatch_text = ""
end

local function GetItemLink(rangedSlotID)
	return GetInventoryItemLink("player", rangedSlotID)
end

local function IsAmmoClass()
	local class = select(2, UnitClass("player"))
	local res = false
	if class == "ROGUE"
	or class == "WARRIOR"
	or class == "HUNTER"
	then
		res = true
	else
		res = false
	end
	return res
end

local function GetAmmoCount()
	local ammo = ""
	local mis = false
	local mist = ""
	local ammo_name = ""
	local ammo_count = 0
	local label = ""
	local text = ""
	local tool_tip = ""

	ClrAmmoInfo()
	-- weapon info
	local itemName, itemLink, itemQuality, itemLevel, itemMinLevel, itemType, itemSubType, itemStackCount,
	itemEquipLoc, itemTexture, sellPrice, classID, subclassID, bindType, expacID, setID, isCraftingReagent
	-- ammo info
	local ammoName, ammoLink, ammoQuality, ammoLevel, ammoMinLevel, ammoType, ammoSubType, ammoStackCount,
	ammoEquipLoc, ammoTexture, _, ammoID, subammoID

	local weap = GetInventoryItemID("player", rangedSlotID)
	if weap == nil then
		-- nothing in slot
		ammo = L["TITAN_AMMO_BUTTON_NOAMMO"]
	else
		-- get weapon info (thrown) or ammo info (guns & bows)
		itemName, itemLink, itemQuality, itemLevel, itemMinLevel, itemType, itemSubType, itemStackCount,
		itemEquipLoc, itemTexture, sellPrice, classID, subclassID, bindType, expacID, setID, isCraftingReagent
		   = GetItemInfo(weap)
		ammo = itemEquipLoc

		-- set ammo name and count
		if (subclassID == 16) -- Thrown
		then
			-- treat thrown as ther ammo with 0 count
			ammo_name = itemName or ""
			ammo_count = 0

			ammoID = classID
			subammoID = subclassID

			label = L["TITAN_AMMO_BUTTON_LABEL_THROWN"]

			ammo_name = select(1, GetItemInfo(GetInventoryItemID("player", rangedSlotID))) or _G["UNKNOWN"]
			if ammo_name == _G["UNKNOWN"] then
				ammo_count = 0
			else
				if game_version < 30000 then
					-- CE thrown has count
					ammo_count = GetInventoryItemCount("player", rangedSlotID) or ammo_count
					text = format(L["TITAN_AMMO_FORMAT"], ammo_count);
				else
					-- Wrath thrown has no count or durability
					text = TitanUtils_GetGoldText("*")
				end
			end
			if TitanGetVar(TITAN_AMMO_ID, "ShowAmmoName") and ammo_name ~= "" then
				text = text.."|cffffff9a".." ("..ammo_name..")".."|r"
			end
			tool_tip = itemName
			-- no mismatch
		elseif (subclassID == 3)  -- Gun
			or (subclassID == 2)  -- Bow
			or (subclassID == 18) -- Crossbow
		then
			ammoName, ammoLink, ammoQuality, ammoLevel, ammoMinLevel, ammoType, ammoSubType, ammoStackCount,
			ammoEquipLoc, ammoTexture, _, ammoID, subammoID, _, _, _, _
			   = GetItemInfo(GetInventoryItemID("player", ammoSlotID))
--			ammo_name = select(1, GetItemInfo(GetInventoryItemID("player", ammoSlotID))) or UNKNOWN
			ammo_name = ammoName or ""
			if ammoName == nil then
				ammo_count = 0
			else
				ammo_count = GetInventoryItemCount("player", ammoSlotID) or display.ammo_count
			end
			text = format(L["TITAN_AMMO_FORMAT"], ammo_count);
			if TitanGetVar(TITAN_AMMO_ID, "ShowAmmoName") and ammo_name ~= "" then
				text = text.."|cffffff9a".." ("..ammo_name..")".."|r"
			end
			label = L["TITAN_AMMO_BUTTON_LABEL_AMMO"]
			tool_tip = ""
				..tostring(itemName)
				.."\n"..tostring(ammo_name)..""


			-- check for mismatch
			if (subclassID == 3) -- Bullet
			and (subammoID == 2) -- Arrow
			then
				mis = true
				mist = ""
					..tostring(itemName)
					.." <> "
					..tostring(ammoName)
				mist = TitanUtils_GetRedText(mist)
			elseif ((subclassID == 2) -- Bow
			or (subclassID == 18)) -- Crossbow
			and (subammoID == 3) -- Bullets
			then
				mis = true
				mist = ""
					..tostring(itemName)
					.." <> "
					..tostring(ammoName)
				mist = TitanUtils_GetRedText(mist)
			else
			end
		else
			ammo_name = UNKNOWN
			ammo_count = 0

			-- no mismatch
		end
--[[
local msg =
	"GII-ammo"
	.." ("..tostring(itemType)..""
	.." "..tostring(classID)..""
	.." "..tostring(subclassID) ..")"
	.." ? ("..tostring(ammoID)..""
	.." "..tostring(subammoID)..")"
	.." "..tostring(mis)..""
debug_msg(msg)
local msg =
	"GII-ammo > "
	.." '"..tostring(label).."'"
	.." '"..tostring(text).."'"
debug_msg(msg)
--]]
	end

	-- Set variables
	display.label = label
	display.text = text
	display.tool_tip = tool_tip
	display.weapon = itemName
	display.weapon_type = itemSubType
	display.ammo_name = ammo_name
	display.ammo_count = ammo_count
	display.ammo_type = ammo
	display.ammo_type_id = subclassID
	display.mismatch = mis
	display.mismatch_text = mist

	if debug_flow then
		local msg =
			"Count"
			.." '"..tostring(itemSubType).."'"
			.." '"..tostring(itemName).."'"
		debug_msg(msg)
	else
		-- not requested
	end
end

local function Events(action, reason)
--[[
- Thrown has no durability so do not register for that event
- Remove ACTIONBAR_HIDEGRID; this is triggered when dragging an item to actionbar
  Not sure why this was implemented - use of event changed?
--]]
	if action == "register" then
		TitanPanelAmmoButton:RegisterEvent("UNIT_INVENTORY_CHANGED")
		TitanPanelAmmoButton:RegisterEvent("MERCHANT_CLOSED")
	elseif action == "unregister" then
		TitanPanelAmmoButton:UnregisterEvent("UNIT_INVENTORY_CHANGED")
		TitanPanelAmmoButton:UnregisterEvent("MERCHANT_CLOSED")
	else
		-- action unknown ???
	end

	if debug_flow then
		local msg =
			"Events"
			.." "..tostring(reason)..""
		debug_msg(msg)
	else
		-- not requested
	end
end

--[[
-- **************************************************************************
-- NAME : TitanPanelAmmoButton_OnLoad()
-- DESC : Registers the plugin upon it loading
-- **************************************************************************
--]]
function TitanPanelAmmoButton_OnLoad(self)
	self.registry = {
			id = TITAN_AMMO_ID,
			--builtIn = 1,
			category = "Built-ins",
			version = TITAN_VERSION,
			menuText = L["TITAN_AMMO_MENU_TEXT"],
			buttonTextFunction = "TitanPanelAmmoButton_GetButtonText",
			tooltipTitle = L["TITAN_AMMO_TOOLTIP"],
            tooltipTextFunction = "TitanPanelAmmoButton_GetTooltipText",
			icon = "Interface\\AddOns\\TitanAmmo\\TitanClassicThrown",
			iconWidth = 16,
			controlVariables = {
			ShowIcon = true,
			ShowLabelText = true,
			ShowRegularText = false,
			ShowColoredText = true,
			DisplayOnRightSide = true
		},
		savedVariables = {
			ShowIcon = 1,
			ShowLabelText = 1,
			ShowColoredText = 1,
			ShowAmmoName = false,
			DisplayOnRightSide = false,
		}
	}
end

--[[
-- **************************************************************************
-- NAME : TitanPanelAmmoButton_GetButtonText(id)
-- DESC : Calculate ammo/thrown logic then put on button
-- VARS : id = button ID
-- **************************************************************************
--]]
function TitanPanelAmmoButton_GetButtonText(id)

	local labelText, ammoText, ammoRichText, color;

	labelText = display.label
	ammoText = display.text

	if display.mismatch then
		ammoRichText = tostring(display.mismatch_text)
	elseif (TitanGetVar(TITAN_AMMO_ID, "ShowColoredText")) then
		color = TitanUtils_GetThresholdColor(TITAN_AMMO_THRESHOLD_TABLE[display.ammo_type], display.ammo_count);
		ammoRichText = TitanUtils_GetColoredText(ammoText, color);
	else
		ammoRichText = TitanUtils_GetHighlightText(ammoText);
	end

	if debug_flow then
		local msg =
			"Btn_Text"
			.." '"..tostring(display.weapon_type).."'"
			.." '"..tostring(ammoRichText).."'"
		debug_msg(msg)
	else
		-- not requested
	end

	return labelText, ammoRichText;
end

--[[
-- **************************************************************************
-- NAME : TitanPanelRightClickMenu_PrepareAmmoMenu()
-- DESC : Display rightclick menu options
-- **************************************************************************
--]]
function TitanPanelRightClickMenu_PrepareAmmoMenu()
	TitanPanelRightClickMenu_AddTitle(TitanPlugins[TITAN_AMMO_ID].menuText);

	local info = {};
	info.text = L["TITAN_AMMO_BULLET_NAME"];
	info.func = function() TitanPanelRightClickMenu_ToggleVar({TITAN_AMMO_ID, "ShowAmmoName"})
		GetAmmoCount()
		TitanPanelButton_UpdateButton(TITAN_AMMO_ID);
	end
	info.checked = TitanUtils_Ternary(TitanGetVar(TITAN_AMMO_ID, "ShowAmmoName"), 1, nil);
	TitanPanelRightClickMenu_AddButton(info, TitanPanelRightClickMenu_GetDropdownLevel())
	TitanPanelRightClickMenu_AddSpacer();

	info = {};
	TitanPanelRightClickMenu_AddToggleIcon(TITAN_AMMO_ID);
	TitanPanelRightClickMenu_AddToggleLabelText(TITAN_AMMO_ID);
	TitanPanelRightClickMenu_AddToggleColoredText(TITAN_AMMO_ID);

	TitanPanelRightClickMenu_AddToggleRightSide(TITAN_AMMO_ID);
	TitanPanelRightClickMenu_AddSpacer();
	TitanPanelRightClickMenu_AddCommand(L["TITAN_PANEL_MENU_HIDE"], TITAN_AMMO_ID, TITAN_PANEL_MENU_FUNC_HIDE);
end

function TitanPanelAmmoButton_GetTooltipText()
	local txt = display.tool_tip
	if display.mismatch then
		txt = txt
			.."\n\n"
			..tostring(display.mismatch_text)..""
	else
		-- weapon and projectile match
	end
	return txt
end

function OnShow()
	ClrAmmoInfo()

	if IsAmmoClass() then
		-- No need to start events and consume cycles if no ammo
		GetAmmoCount()

		Events("register", "OnShow")
	else
		-- Just set the default text on button
		-- for a class w/o ammo
	end

	TitanPanelButton_UpdateButton(TITAN_AMMO_ID);
end

function OnHide()
	ClrAmmoInfo()

	Events("unregister", "OnHide")
end

local function UpdateDisplay()
	GetAmmoCount()

	TitanPanelButton_UpdateButton(TITAN_AMMO_ID);
end

--[[
-- **************************************************************************
-- NAME : TitanPanelAmmoButton_OnEvent()
-- DESC : React to any registered Events
-- **************************************************************************
--]]
function TitanPanelAmmoButton_OnEvent(self, event, arg1, arg2, ...)
	if event == "PLAYER_ENTERING_WORLD" then
		if arg1 == true then -- login
			EnterWorld()
		end
		if arg2 == true then -- reload / zoning
			UpdateDisplay();
		end
	elseif event == "UNIT_INVENTORY_CHANGED" then
		if arg1 == "player" then
			UpdateDisplay();
		end
	elseif event == "MERCHANT_CLOSED" then
		UpdateDisplay();
	end
end

-- ====== Create needed frames
local function Create_Frames()
	if _G[TITAN_BUTTON] then
		return -- if already created
	end

	-- general container frame
	local f = CreateFrame("Frame", nil, UIParent)
--	f:Hide()

	-- Titan plugin button
	local window = CreateFrame("Button", TITAN_BUTTON, f, "TitanPanelComboTemplate")
	window:SetFrameStrata("FULLSCREEN")
	-- Using SetScript("OnLoad",   does not work
	TitanPanelAmmoButton_OnLoad(window);
--	TitanPanelButton_OnLoad(window); -- Titan XML template calls this...

	window:SetScript("OnEvent", function(self, event, ...)
		TitanPanelAmmoButton_OnEvent(self, event, ...)
	end)
	window:SetScript("OnShow", function(self, button)
		OnShow(self)
	end)
	window:SetScript("OnHide", function(self, button)
		OnHide(self)
	end)

end

Create_Frames() -- do the work