Skip to content

Instantly share code, notes, and snippets.

@merlight
Last active November 26, 2015 13:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save merlight/e1255719d82e79be3995 to your computer and use it in GitHub Desktop.
Save merlight/e1255719d82e79be3995 to your computer and use it in GitHub Desktop.
MultiCraft modified using ZO_Spinner
--[[
File : MultiCraft.lua
Author: Ayantir
Version : 3
]]--
-- MultiCraft totally rewrited by Ayantir
local ADDON_NAME = "MultiCraft"
-- Inits
local sliderValue = 1
local isWorking = false
-- Tables
local provisioner = {}
local enchanting = {}
local alchemy = {}
local smithing = {}
local g_spinner = nil -- will be ZO_Spinner instance
local g_lastCraftCount = 1
local selectedCraft = nil -- This will hold a pointer to the currently open crafting station
local callDelay = 500
local GENERAL_MODE_CREATION = 1 -- Alchemy, Provisionner
local debugEnabled = false
local maxCraftable = 1
-- Will output to chat a debug message if MyPrint is enabled
function MyPrint(message)
if debugEnabled then
d(message)
end
end
-- Triggers when EVENT_CRAFT_STARTED and Called at Cleanup
local function HideUI()
-- Hide XML
MultiCraft:SetHidden(true)
end
local function EnableOrDisableUI()
MyPrint(".EnableOrDisableUI()")
-- Init
local hidden = true
local mode = selectedCraft:GetMode()
-- Provisioning & Alchemy
if selectedCraft == provisioner or
selectedCraft == alchemy then
if selectedCraft:IsCraftable() then
MyPrint(".EnableOrDisableUI->false()")
hidden = false
end
-- Enchanting
elseif selectedCraft == enchanting then
if (mode == ENCHANTING_MODE_CREATION and selectedCraft:IsCraftable()) or
(mode == ENCHANTING_MODE_EXTRACTION and selectedCraft:IsExtractable()) then
MyPrint(".EnableOrDisableUI->false()")
hidden = false
end
-- Smithing (Smithing, Wood, Clothing)
elseif selectedCraft == smithing then
-- there is a game bug where this returns erroneously true in refinement after completing an extract that results in having less
-- than 10 items but still having the item selected
-- TODO: fix it
if (mode == SMITHING_MODE_REFINMENT and selectedCraft:IsExtractable()) or
--Aya: Buggy with UI_SHORTCUT_SECONDARY, it will bug the UI ->
(mode == SMITHING_MODE_CREATION and selectedCraft:IsCraftable()) or
(mode == SMITHING_MODE_CREATION and selectedCraft:IsCraftable()) or
(mode == SMITHING_MODE_DECONSTRUCTION and selectedCraft:IsDeconstructable()) then
MyPrint(".EnableOrDisableUI->false()")
hidden = false
end
end
-- Alchemy ?
MyPrint(".EnableOrDisableUI->hidden = " .. tostring(hidden))
-- Hide or Show XML
MultiCraft:SetHidden(hidden)
end
-- This function reset the slider to the correct values (min = 1, max = qty craftable, and maybe the default value)
local function ResetSpinner(defaultValue)
MyPrint(".ResetSpinner()")
-- Can really occurs ?
if not selectedCraft then return end
-- DisableSlider or show ?
EnableOrDisableUI()
local numCraftable = 1
-- On which tab are we ? selectedCraft is wrapped by OverrideXXX
local mode = selectedCraft:GetMode()
-- Get qty craftable
-- For Provisionner
if selectedCraft == provisioner then
if selectedCraft:IsCraftable() then
local data = PROVISIONER.recipeTree:GetSelectedData()
numCraftable = data.numCreatable
else
-- Only a try to hide when we cannot do anything, need to be reworked, can occurs if we can do 1 food and 0 drink
numCraftable = 0
end
-- For Enchanting
elseif selectedCraft == enchanting then
-- 1st tab, making glyphs
if mode == ENCHANTING_MODE_CREATION then
if selectedCraft:IsCraftable() then
-- We look in craftingInventory (Bagpack+Bank) how much items we got, and we select the min, cause enchanting use 1 rune per glyph
for k, v in pairs(ENCHANTING.runeSlots) do
if k == 1 then
numCraftable = v.craftingInventory.itemCounts[v.itemInstanceId]
else
numCraftable = zo_min(numCraftable, v.craftingInventory.itemCounts[v.itemInstanceId])
end
MyPrint("in for numCraftable = " .. tostring(zo_floor(numCraftable)))
end
else
numCraftable = 0
end
-- 2nd tab, deconstruct Glyphs
elseif mode == ENCHANTING_MODE_EXTRACTION then
if selectedCraft:IsExtractable() then
-- We count how many Glyphs we got in craftingInventory (Bagpack+Bank)
numCraftable = ENCHANTING.extractionSlot.craftingInventory.itemCounts[ENCHANTING.extractionSlot.itemInstanceId]
else
numCraftable = 0
end
end
-- Alchemy
elseif selectedCraft == alchemy then
if selectedCraft:IsCraftable() then
-- Same as enchanting, our Qty will be the min between each solvant and reagents
-- Solvant
numCraftable = ALCHEMY.solventSlot.craftingInventory.itemCounts[ALCHEMY.solventSlot.itemInstanceId]
if numCraftable then
-- Reagents
for k, data in pairs(ALCHEMY.reagentSlots) do
if data:MeetsUsabilityRequirement() then
if data.craftingInventory.itemCounts[data.itemInstanceId] ~= nil then
numCraftable = zo_min(numCraftable, data.craftingInventory.itemCounts[data.itemInstanceId])
MyPrint("in for numCraftable = " .. tostring(zo_floor(numCraftable)))
end
end
end
else
-- No solvants in slot
numCraftable = 0
end
else
numCraftable = 0
end
-- Smithing (Wood/Smith/Clothing)
elseif selectedCraft == smithing then
-- 1st tab, refinement
if mode == SMITHING_MODE_REFINMENT then
-- Count how many Items we got in craftingInventory and divide per stack size needed to refine
if selectedCraft:IsExtractable() then
numCraftable = SMITHING.refinementPanel.extractionSlot.craftingInventory.itemCounts[SMITHING.refinementPanel.extractionSlot.itemInstanceId]
numCraftable = numCraftable / GetRequiredSmithingRefinementStackSize()
else
numCraftable = 0
end
-- 2nd tab, creation
elseif mode == SMITHING_MODE_CREATION then
if selectedCraft:IsCraftable() then
MyPrint("SMITHING Creation")
-- Determine qty to craft
-- patternIndex is ID of item to do (glove, chest, sword..)
-- materialIndex is ID of material to use (galatite, iron, etc)
-- materialQuantity is Qty of material to use (10, 12, etc..)
-- styleIndex is ID of style to use, qty is always 1
-- traitIndex is ID of trait to use, qty is always 1
local patternIndex, materialIndex, materialQuantity, styleIndex, traitIndex = SMITHING.creationPanel:GetAllCraftingParameters()
-- How many material do we got for this item ?
local materialCount = GetCurrentSmithingMaterialItemCount(patternIndex, materialIndex) / materialQuantity
-- How many stones ?
local styleItemCount = GetCurrentSmithingStyleItemCount(styleIndex)
-- How many trait stones ?
local traitCount = GetCurrentSmithingTraitItemCount(traitIndex)
-- Because trait is optional, start with the min of material and style which is always needed
numCraftable = zo_min(materialCount, styleItemCount)
-- A trait has been selected, 1 is No trait, and if no trait min is already known
if traitIndex ~= 1 then
-- Protection
numCraftable = 1
end
else
numCraftable = 0
end
-- 3rd tab, deconstruction
elseif mode == SMITHING_MODE_DECONSTRUCTION then
if selectedCraft:IsDeconstructable() then
-- Count how many Items we got in craftingInventory
numCraftable = SMITHING.deconstructionPanel.extractionSlot.craftingInventory.itemCounts[SMITHING.deconstructionPanel.extractionSlot.itemInstanceId]
else
numCraftable = 0
end
end
end
-- Protection against divisions
numCraftable = zo_floor(numCraftable)
if numCraftable > 0 then
-- Disable spinner or show ?
MyPrint(".ResetSpinner->numCraftable = " .. numCraftable)
EnableOrDisableUI()
end
-- Don't show spinner if Qty = 1
-- MultiCraft is handled by XML
if numCraftable <= 1 then
MyPrint(".ResetSpinner->Hide spinner")
MultiCraft:SetHidden(true)
g_spinner:SetMinMax(1, 1)
else
MyPrint(".ResetSpinner->Show spinner")
MultiCraft:SetHidden(false)
maxCraftable = numCraftable
g_spinner:SetMinMax(1, maxCraftable)
g_spinner:SetEnabled(not isWorking)
end
if defaultValue then
g_spinner:SetValue(defaultValue)
end
end
-- Triggers when EVENT_CRAFTING_STATION_INTERACT
local function SelectCraftingSkill(eventCode, craftingType)
MyPrint("SelectCraftingSkill")
-- Provisionner
if craftingType == CRAFTING_TYPE_PROVISIONING then
selectedCraft = provisioner
MyPrint(".SelectCraftingSkill->CRAFTING_TYPE_PROVISIONING")
-- Enchanting
elseif craftingType == CRAFTING_TYPE_ENCHANTING then
selectedCraft = enchanting
MyPrint(".SelectCraftingSkill->CRAFTING_TYPE_ENCHANTING")
-- Alchemy
elseif craftingType == CRAFTING_TYPE_ALCHEMY then
selectedCraft = alchemy
MyPrint(".SelectCraftingSkill->CRAFTING_TYPE_ALCHEMY")
-- Blacksmithing
elseif craftingType == CRAFTING_TYPE_BLACKSMITHING then
selectedCraft = smithing
MyPrint(".SelectCraftingSkill->CRAFTING_TYPE_BLACKSMITHING")
-- Clothier
elseif craftingType == CRAFTING_TYPE_CLOTHIER then
selectedCraft = smithing
MyPrint(".SelectCraftingSkill->CRAFTING_TYPE_CLOTHIER")
-- Woodworking
elseif craftingType == CRAFTING_TYPE_WOODWORKING then
selectedCraft = smithing
MyPrint(".SelectCraftingSkill->CRAFTING_TYPE_WOODWORKING")
end
ResetSpinner()
-- Prevent UI bug due to fast Esc
CALLBACK_MANAGER:FireCallbacks("CraftingAnimationsStopped")
end
-- Triggers when EVENT_END_CRAFTING_STATION_INTERACT
local function Cleanup()
-- Hide UI
MyPrint(".Cleanup")
-- Remove button
HideUI()
isWorking = false
selectedCraft = nil
MyPrint(".Cleanup->Cleaned")
end
-- Executed when EVENT_CRAFT_COMPLETED is triggered, set just before craft has been started
local function ContinueWork(workFunc)
MyPrint(".ContinueWork")
-- Need to do somework ?
if sliderValue > 1 and sliderValue <= maxCraftable then
-- Let's do another one
g_spinner:SetValue(sliderValue - 1)
MyPrint("Multicraft will craft : " .. sliderValue)
MyPrint("ContinueWork.In.sliderValue " .. sliderValue)
-- It will call itself with a delay of XX ms
zo_callLater(workFunc, callDelay)
else
MyPrint(".ContinueWork->finished")
-- Work is finished, unregisters itselfs
EVENT_MANAGER:UnregisterForEvent(ADDON_NAME, EVENT_CRAFT_COMPLETED)
-- Reset spinner to last value, and change work flag
isWorking = false
ResetSpinner(g_lastCraftCount)
end
end
-- Executed when user start to craft (press the keybind)
-- Only for : PROVISIONER:Create(), ENCHANTING:Create(), ALCHEMY:Create(), SMITHING.creationPanel:Create(), SMITHING.deconstructionPanel:Extract(), SMITHING.refinementPanel:Extract()
-- When this function is executed, result is always a success, qties and skill have already been verified
local function Work(workFunc)
MyPrint(".Work")
-- selectedCraft is set when entering the craft station, isWorking is for prevent MultiCraft loops, this function is called only at 1st launch
if selectedCraft and not isWorking then
if MultiCraft:IsHidden() == false then
MyPrint("Craft will work 1 item in few ms")
MyPrint(".Work:sliderValuee->" .. sliderValue)
g_lastCraftCount = sliderValue
if sliderValue > 1 and sliderValue <= maxCraftable then
-- We're working
isWorking = true
g_spinner:SetEnabled(false)
-- When a craft is completed, execute :ContinueWork to find if we need to continue work
EVENT_MANAGER:RegisterForEvent(ADDON_NAME, EVENT_CRAFT_COMPLETED, function() ContinueWork(workFunc) end)
else
MyPrint(".Work:sliderValue->" .. sliderValue)
ResetSpinner()
-- If user set value to 0, craft will be done and sliderValue set to 1. But if we still can craft more than 1 craft, UI should be displayed again after craft
EVENT_MANAGER:RegisterForEvent(ADDON_NAME, EVENT_CRAFT_COMPLETED, function() ContinueWork(workFunc) end)
end
end
end
end
local function OverrideProvisionner()
-- Provisioner
provisioner.RefreshRecipeList = PROVISIONER.RefreshRecipeList
PROVISIONER.RefreshRecipeList = function(...)
provisioner.RefreshRecipeList(...)
MyPrint("RefreshRecipeList->ResetSpinner")
ResetSpinner()
end
provisioner.SelectNode = PROVISIONER.recipeTree.SelectNode
PROVISIONER.recipeTree.SelectNode = function(...)
provisioner.SelectNode(...)
MyPrint("SelectNode->ResetSpinner")
ResetSpinner()
end
-- Create function
provisioner.Create = function()
PROVISIONER:Create()
end
provisioner.GetMode = function()
return GENERAL_MODE_CREATION
end
-- Wrapper to check if an item is craftable
provisioner.IsCraftable = function()
return PROVISIONER:IsCraftable()
end
-- wrapper to get the keybind descriptor for the craft button
-- Aya: 1.5 dropped KeybindStripDescriptor for PROVISIONER: Replaced by mainKeybindStripDescriptor
--provisioner.GetSecondaryKeybindStripDescriptor = function()
-- return PROVISIONER.mainKeybindStripDescriptor[1]
--end
end
local function OverrideEnchanting()
-- Enchanting
-- Tab change
enchanting.SetEnchantingMode = ENCHANTING.SetEnchantingMode
ENCHANTING.SetEnchantingMode = function(...)
enchanting.SetEnchantingMode(...)
end
-- For polymorphism
enchanting.GetMode = function()
return ENCHANTING:GetEnchantingMode()
end
-- Rune slot change
enchanting.SetRuneSlotItem = ENCHANTING.SetRuneSlotItem
ENCHANTING.SetRuneSlotItem = function(...)
enchanting.SetRuneSlotItem(...)
-- Reset Slider each time a Runeslot is changed
MyPrint("SetRuneSlotItem->ResetSpinner")
ResetSpinner()
end
-- Extraction selection change
enchanting.OnSlotChanged = ENCHANTING.OnSlotChanged
ENCHANTING.OnSlotChanged = function(...)
enchanting.OnSlotChanged(...)
-- Reset Slider each time Glyph is changed
MyPrint("OnSlotChanged->ResetSpinner")
ResetSpinner()
end
-- Create and extract function
enchanting.Create = function()
ENCHANTING:Create()
end
-- wrapper to check if an item is craftable
enchanting.IsCraftable = function()
return ENCHANTING:IsCraftable()
end
-- ?
enchanting.IsExtractable = enchanting.IsCraftable
-- Wrapper to get the keybind descriptor for the craft button
--enchanting.GetSecondaryKeybindStripDescriptor = function()
-- return ENCHANTING.keybindStripDescriptor[2]
--end
end
local function OverrideAlchemy()
-- Alchemy
-- Selection Change
alchemy.OnSlotChanged = ALCHEMY.OnSlotChanged
ALCHEMY.OnSlotChanged = function(...)
alchemy.OnSlotChanged(...)
-- Reset slider each time a solvant or a plant is changed
MyPrint("OnSlotChanged->ResetSpinner")
ResetSpinner()
end
-- Create function
alchemy.Create = function()
ALCHEMY:Create()
end
-- For polymorphism
alchemy.GetMode = function()
return GENERAL_MODE_CREATION
end
-- Wrapper to check if an item is craftable
alchemy.IsCraftable = function()
return ALCHEMY:IsCraftable()
end
end
function OverrideSmithing()
-- Smithing
-- tab change
smithing.SetMode = SMITHING.SetMode
SMITHING.SetMode = function(...)
smithing.SetMode(...)
ResetSpinner()
end
smithing.GetMode = function()
return SMITHING.mode
end
-- Pattern selection in creation
smithing.OnSelectedPatternChanged = SMITHING.OnSelectedPatternChanged
SMITHING.OnSelectedPatternChanged = function(...)
smithing.OnSelectedPatternChanged(...)
MyPrint("OnSelectedPatternChanged->ResetSpinner")
ResetSpinner()
end
-- Item selection in deconstruction
smithing.OnExtractionSlotChanged = SMITHING.OnExtractionSlotChanged
SMITHING.OnExtractionSlotChanged = function(...)
smithing.OnExtractionSlotChanged(...)
MyPrint("OnExtractionSlotChanged->ResetSpinner")
ResetSpinner()
end
-- Create function
smithing.Create = function()
SMITHING.creationPanel:Create()
end
-- Wrapper to check if an item is craftable
smithing.IsCraftable = function()
return SMITHING.creationPanel:IsCraftable()
end
-- Deconstruction extract function
smithing.Deconstruct = function()
SMITHING.deconstructionPanel:Extract()
end
-- Wrapper to check if an item is deconstructable
smithing.IsDeconstructable = function()
return SMITHING.deconstructionPanel:IsExtractable()
end
-- Refinement extract function
smithing.Extract = function()
SMITHING.refinementPanel:Extract()
end
-- Wrapper to check if an item is refinable
smithing.IsExtractable = function()
return SMITHING.refinementPanel:IsExtractable()
end
end
local function onAddonLoaded(_, addonName)
-- Protect
if addonName == ADDON_NAME then
g_spinner = ZO_Spinner:New(MultiCraft:GetNamedChild("Spinner"), 1, 1)
g_spinner:RegisterCallback("OnValueChanged", function(value) sliderValue = value end)
-- Set up function overrides
OverrideProvisionner()
OverrideEnchanting()
OverrideAlchemy()
OverrideSmithing()
-- Hook everything up
-- Will Hook Real function with MultiCraft ones (MultiCraft function will be executed before real ones)
ZO_PreHook(PROVISIONER, 'Create', function() Work(provisioner.Create) end)
ZO_PreHook(ENCHANTING, 'Create', function() Work(enchanting.Create) end)
ZO_PreHook(ALCHEMY, 'Create', function() Work(alchemy.Create) end)
ZO_PreHook(SMITHING.creationPanel, 'Create', function() Work(smithing.Create) end)
ZO_PreHook(SMITHING.deconstructionPanel, 'Extract', function() Work(smithing.Deconstruct) end)
ZO_PreHook(SMITHING.refinementPanel, 'Extract', function() Work(smithing.Extract) end)
-- Register events
-- Show UI and set it if needed (slider, #number of crafts)
EVENT_MANAGER:RegisterForEvent(ADDON_NAME, EVENT_CRAFTING_STATION_INTERACT, SelectCraftingSkill)
-- Restore UID when leaving craft station
EVENT_MANAGER:RegisterForEvent(ADDON_NAME, EVENT_END_CRAFTING_STATION_INTERACT, Cleanup)
EVENT_MANAGER:UnregisterForEvent(ADDON_NAME, EVENT_ADD_ON_LOADED)
end
end
-- Initialize Addon
EVENT_MANAGER:RegisterForEvent(ADDON_NAME, EVENT_ADD_ON_LOADED, onAddonLoaded)
<GuiXml>
<Controls>
<Control name="MultiCraftSpinnerTemplate" mouseEnabled="true" virtual="true" >
<Dimensions x="110" y="33" />
<Anchor point="BOTTOM" offsetY="-78" />
<Controls>
<Button name="$(parent)Decrease" >
<Dimensions x="32" y="32" />
<Anchor point="LEFT" offsetX="-15" />
<Textures normal="EsoUI/Art/Buttons/pointsMinus_up.dds"
pressed="EsoUI/Art/Buttons/pointsMinus_down.dds"
mouseOver="EsoUI/Art/Buttons/pointsMinus_over.dds"
disabled="EsoUI/Art/Buttons/pointsMinus_disabled.dds"/>
</Button>
<EditBox name="$(parent)Display" inherits="ZO_DefaultEdit" font="ZoFontHeader3"
maxInputCharacters="3" textType="NUMERIC_UNSIGNED_INT" >
<Anchor point="TOPLEFT" offsetX="40" />
<Anchor point="BOTTOMRIGHT" offsetX="-30" />
</EditBox>
<Button name="$(parent)Increase" >
<Dimensions x="32" y="32" />
<Anchor point="RIGHT" offsetX="2" />
<Textures normal="EsoUI/Art/Buttons/pointsPlus_up.dds"
pressed="EsoUI/Art/Buttons/pointsPlus_down.dds"
mouseOver="EsoUI/Art/Buttons/pointsPlus_over.dds"
disabled="EsoUI/Art/Buttons/pointsPlus_disabled.dds"/>
</Button>
</Controls>
</Control>
<TopLevelControl name="MultiCraft" hidden="true" level="999" >
<Anchor point="BOTTOM" />
<Controls>
<Control name="$(parent)Spinner" inherits="MultiCraftSpinnerTemplate" >
</Control>
</Controls>
</TopLevelControl>
</Controls>
</GuiXml>
@merlight
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment