An Ashita v3 addon that allows you to track and edit gathering metrics within a simple GUI in Final Fantasy 11 Online.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3014 lines
148 KiB

--[[
Copyright © 2021, Sjshovan (LoTekkie)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Yield nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL Sjshovan (LoTekkie) BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--]]
_addon.name = 'Yield';
_addon.description = 'Track and edit a variety of metrics related to gathering within a simple GUI.';
_addon.author = 'Sjshovan (LoTekkie) [email protected]';
_addon.version = '1.0.3';
_addon.commands = {'/yield', '/yld'};
require 'templates';
require 'libs.baseprices';
require 'libs.zonenames';
require 'helpers';
require 'common';
require 'ffxi.enums';
require 'ffxi.vanatime';
require 'timer';
require 'd3d8';
--[[ #TODOs & Notes
- cleanup code
- figure out a way to scale better, use table? can we align?
--]]
----------------------------------------------------------------------------------------------------
-- Variables
----------------------------------------------------------------------------------------------------
local settings = table.copy(defaultSettingsTemplate);
local state = table.copy(stateTemplate);
local metrics = {};
local textures = {};
local ashitaResourceManager = AshitaCore:GetResourceManager();
local ashitaChatManager = AshitaCore:GetChatManager();
local ashitaDataManager = AshitaCore:GetDataManager();
local ashitaParty = ashitaDataManager:GetParty();
local ashitaPlayer = ashitaDataManager:GetPlayer();
local ashitaInventory = ashitaDataManager:GetInventory();
local ashitaTarget = ashitaDataManager:GetTarget();
local ashitaEntity = ashitaDataManager:GetEntity();
local gatherTypes =
{
[1] = { name = "harvesting", short = "ha.", target = "Harvesting Point", tool = "sickle", toolId = 1020, action = "harvest" },
[2] = { name = "excavating", short = "ex.", target = "Excavation Point", tool = "pickaxe", toolId = 605, action = "dig up" },
[3] = { name = "logging", short = "lo.", target = "Logging Point", tool = "hatchet", toolId = 1021, action = "cut off" },
[4] = { name = "mining", short = "mi.", target = "Mining Point", tool = "pickaxe", toolId = 605, action = "dig up" },
[5] = { name = "clamming", short = "cl.", target = "Clamming Point", tool = "clamming kit", toolId = 511, action = "find" },
[6] = { name = "fishing", short = "fi.", target = nil, tool = "bait", toolId = 3, action = "caught" },
[7] = { name = "digging", short = "di.", target = nil, tool = "gysahl green", toolId = 4545, action = "dig" }
}
local settingsTypes =
{
[1] = { name = "general" },
[2] = { name = "setPrices" },
[3] = { name = "setColors" },
[4] = { name = "setAlerts" },
[5] = { name = "reports" },
[6] = { name = "feedback" },
[7] = { name = "about" }
}
local helpTypes =
{
[1] = { name = "generalInfo" },
[2] = { name = "commonQuestions" },
}
local metricsTotalsToolTips =
{
lost = "Total number of yields lost.",
breaks = "Total number of broken tools.",
yields = "Total successful gathers.",
attempts = "Total attempts at gathering.",
}
local windowScales =
{
[0] = 1.0;
[1] = 1.15;
[2] = 1.30;
}
local playerStorage = { available_pct = 100 };
local containers =
{
inventory = 0,
satchel = 5,
sack = 6,
case = 7,
wardrobe = 8,
wardrobe2 = 10,
wardrobe3 = 11,
wardrobe4 = 12
}
local helpTable =
{
commands =
{
helpSeparator('=', 26),
helpTitle('Commands'),
helpSeparator('=', 26),
helpCommandEntry('unload', 'Unload Yield.'),
helpCommandEntry('reload', 'Reload Yield.'),
helpCommandEntry('find', 'Move Yield to the top left corner of your screen.');
helpCommandEntry('about', 'Display information about Yield.'),
helpCommandEntry('help', 'Display Yield commands.'),
helpSeparator('=', 26),
},
about =
{
helpSeparator('=', 23),
helpTitle('About'),
helpSeparator('=', 23),
helpTypeEntry('Name', string.format("%s by Lotekkie & Narpt", _addon.name)),
helpTypeEntry('Description', _addon.description),
helpTypeEntry('Author', _addon.author),
helpTypeEntry('Version', _addon.version),
helpTypeEntry('Support/Donate', "https://Paypal.me/Sjshovan OR For Gil donations: I play on Wings private server! (https://www.wingsxi.com/wings/) My in-game name is LoTekkie."),
helpSeparator('=', 23),
}
}
local modalConfirmPromptTemplate = "Are you sure you want to %s?";
local defaultFontSize = imgui.GetFontSize();
local sounds = { [0] = "" };
local reports = {};
----------------------------------------------------------------------------------------------------
-- UI Variables
---------------------------------------------------------------------------------------------------
local uiVariables =
{
-- User Set
["var_WindowOpacity"] = { nil, ImGuiVar_FLOAT, 1.0 },
["var_ShowToolTips"] = { nil, ImGuiVar_BOOLCPP, true },
["var_TargetValue"] = { nil, ImGuiVar_UINT32, 0 },
["var_WindowScaleIndex"] = { nil, ImGuiVar_UINT32, 0 },
["var_ShowDetailedYields"] = { nil, ImGuiVar_BOOLCPP, true },
["var_YieldDetailsColor"] = { nil, ImGuiVar_FLOATARRAY, 4 };
["var_UseImageButtons"] = { nil, ImGuiVar_BOOLCPP, true },
["var_EnableSoundAlerts"] = { nil, ImGuiVar_BOOLCPP, true },
["var_TargetSoundFile"] = { nil, ImGuiVar_CDSTRING, 128},
["var_FishingSkillSoundFile"] = { nil, ImGuiVar_CDSTRING, 128},
["var_ClamBreakSoundFile"] = { nil, ImGuiVar_CDSTRING, 128},
["var_AutoGenReports"] = { nil, ImGuiVar_BOOLCPP, true },
["var_ReportFontScale"] = { nil, ImGuiVar_FLOAT, 1.0 },
["var_WindowLocked"] = { nil, ImGuiVar_BOOLCPP, false },
-- Internal
['var_WindowVisible'] = { nil, ImGuiVar_BOOLCPP, true },
['var_SettingsVisible'] = { nil, ImGuiVar_BOOLCPP, false },
["var_HelpVisible"] = { nil, ImGuiVar_BOOLCPP, false },
['var_AllSoundIndex'] = { nil, ImGuiVar_UINT32, 0 },
['var_AllColors'] = { nil, ImGuiVar_FLOATARRAY, 4 },
["var_TargetSoundIndex"] = { nil, ImGuiVar_UINT32, 0 },
["var_FishingSkillSoundIndex"] = { nil, ImGuiVar_UINT32, 0 },
["var_ClamBreakSoundIndex"] = { nil, ImGuiVar_UINT32, 0 },
["var_IssueTitle"] = { nil, ImGuiVar_CDSTRING, 256 },
["var_IssueBody"] = { nil, ImGuiVar_CDSTRING, 16384 },
['var_ReportSelected'] = { nil, ImGuiVar_INT32, nil },
}
----------------------------------------------------------------------------------------------------
-- func: loadUiVariables
-- desc: Loads the ui variables from the Yield settings file.
----------------------------------------------------------------------------------------------------
function loadUiVariables()
-- Load the UI variables..
imgui.SetVarValue(uiVariables["var_WindowOpacity"][1], settings.general.opacity);
imgui.SetVarValue(uiVariables["var_TargetValue"][1], settings.general.targetValue);
imgui.SetVarValue(uiVariables["var_ShowToolTips"][1], settings.general.showToolTips);
imgui.SetVarValue(uiVariables["var_WindowScaleIndex"][1], settings.general.windowScaleIndex);
imgui.SetVarValue(uiVariables["var_ShowDetailedYields"][1], settings.general.showDetailedYields);
imgui.SetVarValue(uiVariables["var_UseImageButtons"][1], settings.general.useImageButtons);
imgui.SetVarValue(uiVariables["var_EnableSoundAlerts"][1], settings.general.enableSoundAlerts);
imgui.SetVarValue(uiVariables["var_AutoGenReports"][1], settings.general.autoGenReports);
local r, g, b, a = colorToRGBA(settings.general.yieldDetailsColor);
imgui.SetVarValue(uiVariables["var_YieldDetailsColor"][1], r/255, g/255, b/255, a/255);
for gathering, yields in pairs(settings.yields) do -- per yield
for yield, data in pairs(yields) do
imgui.SetVarValue(uiVariables[string.format("var_%s_%s_prices", gathering, yield)][1], data.singlePrice, data.stackPrice);
local r, g, b, a = colorToRGBA(data.color);
imgui.SetVarValue(uiVariables[string.format("var_%s_%s_color", gathering, yield)][1], r/255, g/255, b/255, a/255);
-- re-index for file changes
local soundIndex = getSoundIndex(data.soundFile);
imgui.SetVarValue(uiVariables[string.format("var_%s_%s_soundIndex", gathering, yield)][1], soundIndex);
local soundFile = sounds[soundIndex];
imgui.SetVarValue(uiVariables[string.format("var_%s_%s_soundFile", gathering, yield)][1], soundFile);
end
-- per gathering
imgui.SetVarValue(uiVariables[string.format("var_%s_priceMode", gathering)][1], settings.priceModes[gathering]);
end
for gathering, data in pairs(metrics) do -- per metric
imgui.SetVarValue(uiVariables[string.format("var_%s_estimatedValue", gathering)][1], data.estimatedValue);
end
-- target sound file
local soundIndex = getSoundIndex(settings.general.targetSoundFile);
imgui.SetVarValue(uiVariables["var_TargetSoundIndex"][1], soundIndex);
local soundFile = sounds[soundIndex];
imgui.SetVarValue(uiVariables["var_TargetSoundFile"][1], soundFile);
-- fishing skill sound file
soundIndex = getSoundIndex(settings.general.fishingSkillSoundFile);
imgui.SetVarValue(uiVariables["var_FishingSkillSoundIndex"][1], soundIndex);
soundFile = sounds[soundIndex];
imgui.SetVarValue(uiVariables["var_FishingSkillSoundFile"][1], soundFile);
-- clam break sound file
soundIndex = getSoundIndex(settings.general.clamBreakSoundFile);
imgui.SetVarValue(uiVariables["var_ClamBreakSoundIndex"][1], soundIndex);
soundFile = sounds[soundIndex];
imgui.SetVarValue(uiVariables["var_ClamBreakSoundFile"][1], soundFile);
-- All colors
local r, g, b, a = colorToRGBA(-3877684);
imgui.SetVarValue(uiVariables["var_AllColors"][1], r/255, g/255, b/255, a/255);
end
----------------------------------------------------------------------------------------------------
-- func: updatePlotPoints
-- desc: Update the display of all plots every second.
----------------------------------------------------------------------------------------------------
function updatePlotPoints()
if state.timers[state.gathering] then
totalSecs = metrics[state.gathering].secondsPassed
metrics[state.gathering].secondsPassed = totalSecs + 1
local timeSpan = 3600 -- one hour
local timePassed = metrics[state.gathering].secondsPassed
local pointsWindowMax = 60 -- one min
local yieldsOverTime = metrics[state.gathering].totals.yields * (timeSpan / timePassed)
local valueOverTime = metrics[state.gathering].estimatedValue * (timeSpan / timePassed)
if totalSecs >= pointsWindowMax then
table.remove(metrics[state.gathering].points.yields, 2)
table.remove(metrics[state.gathering].points.values, 2)
end
table.insert(metrics[state.gathering].points.yields, yieldsOverTime)
table.insert(metrics[state.gathering].points.values, valueOverTime)
end
end
----------------------------------------------------------------------------------------------------
-- func: updatePlayerStorage
-- desc: Update the global playerStorage table with gathering tool counts and available inventory space every second.
----------------------------------------------------------------------------------------------------
function updatePlayerStorage()
local storage = {};
for _, data in ipairs(gatherTypes) do
if data.name ~= "clamming" then
local itemId = data.toolId;
if data.name == "fishing" then -- check equipment (for fishing bait)
local item = ashitaInventory:GetEquippedItem(data.toolId);
if item then
itemId = getItemIdFromContainers(item.ItemIndex, containers);
end
end
storage[data.tool] = getItemCountFromContainers(itemId, containers);
else -- clamming (key item)
if (AshitaCore:GetDataManager():GetPlayer():HasKeyItem(data.toolId)) then
storage[data.tool] = 1
else
storage[data.tool] = 0
end
end
end
storage["available"], storage["available_pct"] = getAvailableStorageFromContainers({0});
playerStorage = storage;
end
----------------------------------------------------------------------------------------------------
-- func: getPrice
-- desc: Get the price for the given yield based on user settings.
----------------------------------------------------------------------------------------------------
function getPrice(itemName, gatherType)
if gatherType == nil then gatherType = state.gathering; end
local data = settings.yields[gatherType][itemName];
local price = data.singlePrice or 0;
switch(settings.priceModes[gatherType]) : caseof
{
[0] = function() price = data.stackPrice / data.stackSize or 0 end, -- stackPrice
[2] = function() price = basePrices[data.id] or 0 end, -- NPCPrice
}
return math.floor(price);
end
----------------------------------------------------------------------------------------------------
-- func: adjTotal
-- desc: Modify the "total" metric by the value given.
----------------------------------------------------------------------------------------------------
function adjTotal(metricName, val)
local total = metrics[state.gathering].totals[metricName]
if total == nil then total = 0 end
metrics[state.gathering].totals[metricName] = total + val
end
----------------------------------------------------------------------------------------------------
-- func: adjYield
-- desc: Modify the "yield" metric by the value given.
----------------------------------------------------------------------------------------------------
function adjYield(yieldName, val)
local yield = metrics[state.gathering].yields[yieldName]
if yield == nil then yield = 0 end
metrics[state.gathering].yields[yieldName] = yield + val
return metrics[state.gathering].yields[yieldName];
end
----------------------------------------------------------------------------------------------------
-- func: recordCurrentZone
-- desc: Get the current zone and append it to the zones table.
----------------------------------------------------------------------------------------------------
function recordCurrentZone()
local zoneId = getPlayerZoneId();
if not table.hasvalue(settings.zones[state.gathering], zoneId) then
table.insert(settings.zones[state.gathering], zoneId);
end
end
----------------------------------------------------------------------------------------------------
-- func: calcTargetProgress
-- desc: Calculate and normalize the value of progress towards reaching the target value.
----------------------------------------------------------------------------------------------------
function calcTargetProgress()
local progress = metrics[state.gathering].estimatedValue/settings.general.targetValue
if progress == math.huge or progress ~= progress then progress = 0.0 end
if progress < 0 then progress = 0.0 end
if progress > 1.0 then progress = 1.0 end
return progress
end
----------------------------------------------------------------------------------------------------
-- func: getGatherTypeData
-- desc: Obtain a table of gathering related data.
----------------------------------------------------------------------------------------------------
function getGatherTypeData()
for _, data in ipairs(gatherTypes) do
if data.name == state.gathering then
return data;
end
end
end
----------------------------------------------------------------------------------------------------
-- func: getItemCountFromContainers
-- desc: Obtain a count of the given item within the given container types.
----------------------------------------------------------------------------------------------------
function getItemCountFromContainers(itemId, containers)
itemCount = 0;
for containerName, containerId in pairs(containers) do
for i = 0, ashitaInventory:GetContainerMax(containerId), 1 do -- check containers
local entry = ashitaInventory:GetItem(containerId, i);
if entry then
if entry.Id == itemId and entry.Id ~= 0 and entry.Id ~= 65535 then
local item = ashitaResourceManager:GetItemById(entry.Id);
if item then
local quantity = 1;
if entry.Count and item.StackSize > 1 then
quantity = entry.Count;
end
itemCount = itemCount + quantity;
end
end
end
end
end
return itemCount;
end
----------------------------------------------------------------------------------------------------
-- func: getItemPriceFromContainers
-- desc: Obtain the price of a given item from within the given container types.
----------------------------------------------------------------------------------------------------
function getItemPriceFromContainers(itemId, containers)
for containerName, containerId in pairs(containers) do
for i = 0, ashitaInventory:GetContainerMax(containerId), 1 do -- check containers
local entry = ashitaInventory:GetItem(containerId, i);
if entry then
if entry.Id == itemId and entry.Id ~= 0 and entry.Id ~= 65535 then
return entry.Price;
end
end
end
end
return 0;
end
----------------------------------------------------------------------------------------------------
-- func: getItemIdFromContainers
-- desc: Obtain an item ID from the given item index, checks within given container types.
----------------------------------------------------------------------------------------------------
function getItemIdFromContainers(itemIndex, containers)
itemId = nil;
for containerName, containerId in pairs(containers) do
for i = 0, ashitaInventory:GetContainerMax(containerId), 1 do -- check containers
local entry = ashitaInventory:GetItem(containerId, i);
if entry then
if entry.Index == itemIndex then
return entry.Id;
end
end
end
end
return itemId;
end
----------------------------------------------------------------------------------------------------
-- func: getAvailableStorageFromContainers
-- desc: Obtain the available storage space from within the given container types.
----------------------------------------------------------------------------------------------------
function getAvailableStorageFromContainers(containers)
total = 0;
available = 0;
for _, containerId in pairs(containers) do
local max = ashitaInventory:GetContainerMax(containerId) - 1;
local used = 0;
total = total + max;
for i = 0, max, 1 do
local entry = ashitaInventory:GetItem(containerId, i);
if entry then
if entry.Id > 0 and entry.Id < 65535 then
used = used + 1;
end
end
end
available = available + (max - used);
end
return available, math.floor(available/total*100); -- pct
end
----------------------------------------------------------------------------------------------------
-- func: sortKeysByTotalValue
-- desc: Sort yields based on their total value.
----------------------------------------------------------------------------------------------------
function table.sortKeysByTotalValue(t, desc)
local ret = {}
for k, v in pairs(t) do
table.insert(ret, k)
end
local totalA = function(a, b) return math.floor(getPrice(a) * metrics[state.gathering].yields[a]); end;
local totalB = function(a, b) return math.floor(getPrice(b) * metrics[state.gathering].yields[b]); end;
if (desc) then
table.sort(ret, function(a, b) return totalA(a, b) < totalB(a, b); end);
else
table.sort(ret, function(a, b) return totalA(a, b) > totalB(a, b); end);
end
return ret;
end
----------------------------------------------------------------------------------------------------
-- func: updateAllStates
-- desc: Set all tracked gathering states to the given state.
----------------------------------------------------------------------------------------------------
function updateAllStates(newState)
state.gathering = newState;
state.settings.setPrices.gathering = newState;
state.settings.setColors.gathering = newState;
state.settings.setAlerts.gathering = newState;
state.settings.reports.gathering = newState;
end
----------------------------------------------------------------------------------------------------
-- func: getSoundOptions
-- desc: Obtain a formatted string of sound options used for sound selection drop-downs.
----------------------------------------------------------------------------------------------------
function getSoundOptions()
local options = "None\0";
for i, file in pairs(sounds) do
options = options..file.."\0";
end
return options.."\0";
end
----------------------------------------------------------------------------------------------------
-- func: alertYield
-- desc: Play the user set sound for the given yield if alerts are enabled.
----------------------------------------------------------------------------------------------------
function alertYield(yieldName)
local yieldData = settings.yields[state.gathering][yieldName];
if yieldData.soundFile ~= "" then
return playAlert(yieldData.soundFile);
end
return false;
end
----------------------------------------------------------------------------------------------------
-- func: playAlert
-- desc: Play the given sound file if alerts are enabled.
----------------------------------------------------------------------------------------------------
function playAlert(soundFile)
if settings.general.enableSoundAlerts then
playSound(soundFile);
return true;
end
return false;
end
----------------------------------------------------------------------------------------------------
-- func: playSound
-- desc: Play the given sound file.
----------------------------------------------------------------------------------------------------
function playSound(soundFile)
if soundFile ~= "" then
ashita.misc.play_sound(string.format(_addon.path.."sounds\\%s", soundFile));
end
end
----------------------------------------------------------------------------------------------------
-- func: getSoundIndex
-- desc: Obtain the stored table index of the given sound file name.
----------------------------------------------------------------------------------------------------
function getSoundIndex(fileName)
for i, file in pairs(sounds) do
if fileName == file then
return i;
end
end
return 0;
end
----------------------------------------------------------------------------------------------------
-- func: checkTargetAlertReady
-- desc: Check if we should play the target value alert.
----------------------------------------------------------------------------------------------------
function checkTargetAlertReady()
state.values.targetAlertReady = metrics[state.gathering].estimatedValue < settings.general.targetValue;
end
----------------------------------------------------------------------------------------------------
-- func: sendIssue
-- desc: Send an issue or feedback to github issues.
----------------------------------------------------------------------------------------------------
function sendIssue(title, body)
io.popen(string.format('%s "%s" "%s"', _addon.path .. "tools\\sendissue.exe", title, body));
end
----------------------------------------------------------------------------------------------------
-- func: fileExists
-- desc: Check if the given file exits.
----------------------------------------------------------------------------------------------------
function fileExists(file)
local f = io.open(file, "rb")
if f then f:close() end
return f ~= nil
end
----------------------------------------------------------------------------------------------------
-- func: linesFrom
-- desc: Obtain lines from the given file.
----------------------------------------------------------------------------------------------------
function linesFrom(file)
if not fileExists(file) then return {} end
lines = {}
for line in io.lines(file) do
lines[#lines + 1] = line
end
return lines
end
----------------------------------------------------------------------------------------------------
-- func: getPlayerName
-- desc: Obtain the current players name.
----------------------------------------------------------------------------------------------------
function getPlayerName(lower)
local name = ashitaParty:GetMemberName(0);
if lower then
name = string.lower(name);
end
return name
end
----------------------------------------------------------------------------------------------------
-- func: getPlayerZoneId
-- desc: Obtain the current zone ID.
----------------------------------------------------------------------------------------------------
function getPlayerZoneId()
return ashitaParty:GetMemberZone(0);
end
----------------------------------------------------------------------------------------------------
-- func: generateGatheringReport
-- desc: Generate a report file using tracked metrics.
----------------------------------------------------------------------------------------------------
function generateGatheringReport(gatherType)
if gatherType == nil then gatherType = state.gathering; end
if getPlayerName() == "" then return false; end
local zones = settings.zones[gatherType];
local zonesCount = table.count(zones);
local metrics = metrics[gatherType];
local zoneName = zoneNames[getPlayerZoneId()];
if zonesCount > 0 then -- there has been some activity here.
zoneName = zoneNames[zones[1]];
if zonesCount > 1 then zoneName = "Multiple Zones"; end
end
zoneName = string.gsub(zoneName, " ", "_");
local sep = "------------\n";
local date = os.date('*t');
local dateTimeStamp = string.format("%.4d_%.2d_%.2d__%.2d_%.2d_%.2d", date.year, date.month, date.day, date.hour, date.min, date.sec);
local fname = string.format('%s__%s.log', zoneName, dateTimeStamp);
local fpath = string.format('%s/%s/%s/%s', _addon.path, 'reports', getPlayerName(), gatherType);
if (not ashita.file.dir_exists(fpath)) then
ashita.file.create_dir(fpath);
end
local file = io.open(string.format('%s/%s', fpath, fname), 'w+');
if (file ~= nil) then
local dateTimeStampNice = string.format("%.4d-%.2d-%.2d %.2d:%.2d:%.2d", date.year, date.month, date.day, date.hour, date.min, date.sec);
file:write(string.format("%s YIELD REPORT : [%s]\n", string.upper(gatherType), dateTimeStampNice));
file:write(sep);
file:write("ZONES\n");
file:write(sep);
if zonesCount > 1 then
for i, id in ipairs(settings.zones[gatherType]) do
local zoneName = zoneNames[id];
file:write("\t"..zoneName.."\n");
end
else
file:write("\t"..zoneName.."\n");
end
file:write(sep);
file:write("METRICS\n");
file:write(sep);
for name, val in pairs(metrics.totals) do
file:write(string.format("\t%s: %s\n", name, val));
end
local successRate = metrics.totals.yields/metrics.totals.attempts * 100
if successRate == math.huge or successRate ~= successRate then successRate = 0.0 end
if successRate < 0 then successRate = 0.0 end
file:write(string.format("\tSuccess Rate: %.2f%%\n", successRate, 0, 100));
file:write(string.format("\tTime Passed: %s\n", os.date("!%X", (metrics.secondsPassed))));
file:write(string.format("\tEstimated Value: %s\n", metrics.estimatedValue));
file:write(string.format("\tYields per Hour: %.2f\n", metrics.points.yields[#metrics.points.yields]));
file:write(string.format("\tValue per Hour: %.2f\n", metrics.points.values[#metrics.points.values]));
file:write(string.format("\tTarget Value: %s\n", settings.general.targetValue));
local targetReached = metrics.estimatedValue >= settings.general.targetValue;
local targetReachedAnswer = "No";
if targetReached then targetReachedAnswer = "Yes"; end
file:write(string.format("\tTarget Reached: %s\n", targetReachedAnswer));
file:write(sep);
file:write("YIELDS\n");
file:write(sep);
if table.count(metrics.yields) > 0 then
for name, count in pairs(metrics.yields) do
file:write(string.format("\t%s: %s %s\n", name, count, string.format("@%dea.=(%s)", getPrice(name, gatherType), math.floor(getPrice(name, gatherType) * metrics.yields[name]))));
end
else
file:write("\tNone");
end
file:close();
reports[gatherType][#reports[gatherType] + 1] = fname;
return true;
end
return false;
end
----------------------------------------------------------------------------------------------------
-- func: saveSettings
-- desc: Saves the Yield settings file.
----------------------------------------------------------------------------------------------------
function saveSettings()
-- Obtain the configuration variables..
settings.general.opacity = imgui.GetVarValue(uiVariables["var_WindowOpacity"][1]);
settings.general.targetValue = imgui.GetVarValue(uiVariables["var_TargetValue"][1]);
settings.general.showToolTips = imgui.GetVarValue(uiVariables["var_ShowToolTips"][1]);
settings.general.windowScaleIndex = imgui.GetVarValue(uiVariables["var_WindowScaleIndex"][1]);
settings.general.yieldDetailsColor = colorTableToInt(imgui.GetVarValue(uiVariables["var_YieldDetailsColor"][1]));
settings.general.useImageButtons = imgui.GetVarValue(uiVariables["var_UseImageButtons"][1]);
settings.general.enableSoundAlerts = imgui.GetVarValue(uiVariables["var_EnableSoundAlerts"][1]);
settings.general.targetSoundFile = imgui.GetVarValue(uiVariables["var_TargetSoundFile"][1]);
settings.general.fishingSkillSoundFile = imgui.GetVarValue(uiVariables["var_FishingSkillSoundFile"][1]);
settings.general.clamBreakSoundFile = imgui.GetVarValue(uiVariables["var_ClamBreakSoundFile"][1]);
settings.general.autoGenReports = imgui.GetVarValue(uiVariables["var_AutoGenReports"][1]);
for gathering, yields in pairs(settings.yields) do
for yield, data in pairs(yields) do
local yieldSettings = settings.yields[gathering][yield];
local prices = imgui.GetVarValue(uiVariables[string.format("var_%s_%s_prices", gathering, yield)][1]);
yieldSettings.singlePrice = prices[1];
yieldSettings.stackPrice = prices[2];
yieldSettings.color = colorTableToInt(imgui.GetVarValue(uiVariables[string.format("var_%s_%s_color", gathering, yield)][1]));
yieldSettings.soundFile = imgui.GetVarValue(uiVariables[string.format("var_%s_%s_soundFile", gathering, yield)][1]);
end
settings.priceModes[gathering] = imgui.GetVarValue(uiVariables[string.format("var_%s_priceMode", gathering)][1]);
end
for _, data in ipairs(gatherTypes) do
metrics[data.name].estimatedValue = tonumber(imgui.GetVarValue(uiVariables[string.format("var_%s_estimatedValue", data.name)][1]))
end
-- Obtain the metrics..
settings.metrics = table.copy(metrics);
-- Obtain the state..
settings.state.gathering = state.gathering;
settings.state.lastKnownGathering = state.values.lastKnownGathering;
settings.state.windowPosX = state.window.posX;
settings.state.windowPosY = state.window.posY;
settings.state.clamBucketBroken = state.values.clamBucketBroken;
settings.state.clamConfirmedYields = state.values.clamConfirmedYields;
settings.state.clamBucketTotal = state.values.clamBucketTotal;
settings.state.clamBucketPz = state.values.clamBucketPz;
settings.state.clamBucketPzMax = state.values.clamBucketPzMax;
settings.state.firstLoad = state.firstLoad;
-- Save the configuration variables..
ashita.settings.save(_addon.path .. 'settings/settings.json', settings);
end
----------------------------------------------------------------------------------------------------
-- func: load
-- desc: Called when the addon is loaded.
----------------------------------------------------------------------------------------------------
ashita.register_event('load', function()
state.initializing = true
-- Ensure the settings folder exists..
ashita.file.create_dir(_addon.path .. '/settings/');
ashita.file.create_dir(_addon.path .. '/reports/');
ashita.file.create_dir(_addon.path .. '/reports/' .. getPlayerName());
-- Load and merge the users settings..
settings = ashita.settings.load_merged(
_addon.path .. '/settings/settings.json', settings
)
-- loop through gathering types..
for _, data in ipairs(gatherTypes) do
-- Populate the metrics table..
if table.haskey(settings.metrics, data.name) then
metrics[data.name] = table.copy(settings.metrics[data.name]);
else
metrics[data.name] = table.copy(metricsTemplate);
end
-- Initialize state timers..
state.timers[data.name] = false;
-- Add estimated value ui variables...
uiVariables[string.format("var_%s_estimatedValue", data.name)] = { nil, ImGuiVar_UINT32, 0 }
-- Add textures..
local texturePath = string.format('images\\%s.png', data.name)
local hres, texture = ashita.d3dx.CreateTextureFromFileA(_addon.path .. texturePath);
if texture == nil then
state.values.btnTextureFailure = true;
displayResponse(string.format("Yield: Failed to load texture (%s). Buttons will now default to text display.", texturePath), "\31\167%s");
imgui.SetVarValue(uiVariables["var_UseImageButtons"][1], false);
end
textures[data.name] = texture;
end
-- Update saved gathering state..
updateAllStates(settings.state.gathering);
-- misc state updates..
checkTargetAlertReady();
state.values.lastKnownGathering = settings.state.lastKnownGathering;
state.window.posX = settings.state.windowPosX;
state.window.posY = settings.state.windowPosY;
state.values.clamBucketBroken = settings.state.clamBucketBroken;
state.values.clamConfirmedYields = settings.state.clamConfirmedYields;
state.values.clamBucketTotal = settings.state.clamBucketTotal;
state.values.clamBucketPz = settings.state.clamBucketPz;
state.values.clamBucketPzMax = settings.state.clamBucketPzMax;
state.firstLoad = settings.state.firstLoad;
-- Add price ui variables from settings..
for gathering, yields in pairs(settings.yields) do
for yield, data in pairs(yields) do -- per yield
uiVariables[string.format("var_%s_%s_prices", gathering, yield)] = { nil, ImGuiVar_INT32ARRAY, 2 };
uiVariables[string.format("var_%s_%s_color", gathering, yield)] = { nil, ImGuiVar_FLOATARRAY, 4 };
uiVariables[string.format("var_%s_%s_soundFile", gathering, yield)] = { nil, ImGuiVar_CDSTRING, 128 };
uiVariables[string.format("var_%s_%s_soundIndex", gathering, yield)] = { nil, ImGuiVar_UINT32, 0 };
end
-- per gathering
uiVariables[string.format("var_%s_priceMode", gathering)] = { nil, ImGuiVar_UINT32, 0 };
end
-- Retrieve sounds files..
for f in io.popen(string.format('dir "%s\\sounds" /b', _addon.path)):lines() do
sounds[#sounds + 1] = f;
end
-- Retrieve reports..
for _, data in ipairs(gatherTypes) do
if not table.haskey(reports, data.name) then reports[data.name] = {}; end
if getPlayerName() ~= "" then
local dirName = string.format("%s\\reports\\%s\\%s", _addon.path, getPlayerName(), data.name);
if ashita.file.dir_exists(dirName) then
for f in io.popen(string.format("dir %s /b", dirName)):lines() do
reports[data.name][#reports[data.name] + 1] = f;
end
else
ashita.file.create_dir(dirName);
end
state.reportsLoaded = true;
end
end
-- Create timers..
if ashita.timer.create_timer("updatePlotPoints") then
ashita.timer.adjust_timer("updatePlotPoints", 1, 0, updatePlotPoints)
ashita.timer.start_timer("updatePlotPoints")
end
if ashita.timer.create_timer("updatePlayerStorage") then
ashita.timer.adjust_timer("updatePlayerStorage", 1, 0, updatePlayerStorage)
ashita.timer.start_timer("updatePlayerStorage")
end
if ashita.timer.create_timer("inactivityCheck") then
ashita.timer.adjust_timer("inactivityCheck", 1, 0, function()
if state.timers[state.gathering] then
state.values.inactivitySeconds = state.values.inactivitySeconds + 1;
if state.values.inactivitySeconds == 300 then -- 5min
for _, data in ipairs(gatherTypes) do
state.timers[data.name] = false; -- shutdown timers
end
displayResponse("Yield: Timers halted due to inactivity.", "\31\140%s");
end
else
state.values.inactivitySeconds = 0;
end
if state.attempting then
state.values.inactivitySeconds = 0;
end
end)
ashita.timer.start_timer("inactivityCheck")
end
-- Initialize custom variables..
for varName, data in pairs(uiVariables) do
if (data[2] >= ImGuiVar_CDSTRING) then
uiVariables[varName][1] = imgui.CreateVar(uiVariables[varName][2], uiVariables[varName][3]);
else
uiVariables[varName][1] = imgui.CreateVar(uiVariables[varName][2]);
end
if (#data > 2 and data[2] < ImGuiVar_CDSTRING) then
imgui.SetVarValue(uiVariables[varName][1], uiVariables[varName][3]);
end
end
-- Load ui variables from the settings file..
loadUiVariables();
if state.firstLoad then
imgui.SetVarValue(uiVariables["var_HelpVisible"][1], true);
end
end)
----------------------------------------------------------------------------------------------------
-- func: unload
-- desc: Called when the addon is unloaded.
----------------------------------------------------------------------------------------------------
ashita.register_event('unload', function()
-- Save the settings file..
saveSettings();
-- Remove timers..
ashita.timer.remove_timer("updatePlotPoints");
ashita.timer.remove_timer("updatePlayerStorage");
ashita.timer.remove_timer("inactivityCheck");
-- Cleanup the custom variables..
for varName, data in pairs(uiVariables) do
if (uiVariables[varName][1] ~= nil) then
imgui.DeleteVar(uiVariables[varName][1]);
end
uiVariables[varName][1] = nil;
end
end)
---------------------------------------------------------------------------------------------------
-- func: command
-- desc: Called when the addon is handling a command.
---------------------------------------------------------------------------------------------------
ashita.register_event('command', function(command, ntype)
local commandArgs = command:lower():args();
if not table.hasvalue(_addon.commands, commandArgs[1]) then
return false;
end
local responseMessage = "";
local success = true;
if commandArgs[2] == 'reload' or commandArgs[2] == 'r' then
AshitaCore:GetChatManager():QueueCommand('/addon reload yield', 1);
elseif commandArgs[2] == 'unload' or commandArgs[2] == 'u' then
response_message = 'Thank you for using Yield. Goodbye.';
AshitaCore:GetChatManager():QueueCommand('/addon unload yield', 1);
elseif commandArgs[2] == 'about' or commandArgs[2] == 'a' then
displayHelp(helpTable.about);
elseif commandArgs[2] == 'help' or commandArgs[2] == 'h' then
displayHelp(helpTable.commands);
--[[ test commands
elseif commandArgs[2] == "test" then
settings.general.showToolTips = not settings.general.showToolTips;
elseif commandArgs[2] == "test2" then
settings.general.windowScaleIndex = cycleIndex(settings.general.windowScaleIndex, 0, 2, 1);
--]]
elseif commandArgs[2] == "find" or commandArgs[2] == 'f' then
imgui.SetWindowPos(string.format("%s v%s by Lotekkie & Narpt", _addon.name, _addon.version), 0, 0);
else
displayHelp(helpTable.commands);
end
if responseMessage ~= "" then
displayResponse(
commandResponse(response_message, success)
);
end
return false;
end)
---------------------------------------------------------------------------------------------------
-- func: incoming_text
-- desc: Event called when the addon is asked to handle an incoming chat line.
---------------------------------------------------------------------------------------------------
ashita.register_event('incoming_text', function(mode, message, modifiedmode, modifiedmessage, blocked)
-- Ensure proper chat modes..
if not table.hasvalue({919, 654, 702, 662, 664, 129}, mode) then state.attempting = false; return false; end
if (blocked) then state.attempting = false; return false; end
-- Remove colors form message..
message = string.strip_colors(message);
message = string.lower(message);
-- Ensure we care..
if not state.attempting then
if state.values.lastKnownGathering == "fishing" then -- play alert on skill-up
local skillup = string.contains(message, string.format("%s's fishing skill rises", getPlayerName(true)))
if skillup then
playAlert(imgui.GetVarValue(uiVariables["var_FishingSkillSoundFile"][1]));
end
elseif getPlayerZoneId() == 4 then -- Bibiki Bay
local obtainedBucket = string.contains(message, "obtained key item: clamming kit");
local returnedBucket = string.contains(message, "you return the clamming kit");
local upgraded = string.match(message, "^your clamming capacity has increased to (.*) ponzes!")
if upgraded then
state.values.clamBucketPzMax = tonumber(upgraded);
end
if obtainedBucket or returnedBucket then
state.values.clamConfirmedYields = table.copy(metrics["clamming"].yields);
state.values.clamBucketBroken = false;
state.values.clamBucketPz = 0;
state.values.clamBucketPzMax = 50;
saveSettings();
end
end
return false;
end
-- Ensure correct state..
updateAllStates(state.attemptType);
-- Check the attempt.
if state.attempting then
if not state.timers[state.gathering] then
state.timers[state.gathering] = true
end
local val = 0;
local success = false;
local successBreak = false;
local unable = false;
local broken = false;
local full = false;
local gatherData = getGatherTypeData(state.gathering);
switch(gatherData.name) : caseof
{
["digging"] = function ()
successBreak = false;
success = string.match(message, "obtained: (.*).") or successBreak
unable = string.contains(message, "you dig, but find nothing.");
broken = false;
lost = false;
end,
["fishing"] = function ()
successBreak = false;
success = string.match(message, string.format("%s %s a[n]? (.*)!", getPlayerName(true), gatherData.action)) or successBreak
unable = string.contains(message, "you didn't catch anything.") or string.contains(message, "you give up");
broken = string.contains(message, "your rod breaks.");
lost = string.contains(message, "you lost your catch") or string.contains(message, "your line breaks.") or string.contains(message, "but cannot carry any more items.");
end,
["clamming"] = function ()
successBreak = false;
success = string.match(message, string.format("^you %s a[n]? (.*) and toss it into your bucket.", gatherData.action));
unable = string.contains(message, "with a broken bucket!") --or string.contains(message, "someone has been digging here.");
broken = string.contains(message, "and toss it into your bucket...");
lost = false;
if success then
if state.values.clamBucketTotal == nil then state.values.clamBucketTotal = 0; end
state.values.clamBucketTotal = state.values.clamBucketTotal + 1;
end
if broken then
success = nil;
metrics[state.gathering].yields = table.copy(state.values.clamConfirmedYields);
metrics[state.gathering].totals.yields = table.sumValues(metrics[state.gathering].yields);
metrics[state.gathering].estimatedValue = 0;
state.values.clamBucketTotal = 0;
state.values.clamBucketPz = 0;
state.values.clamBucketPzMax = 50;
for yield, count in pairs(metrics[state.gathering].yields) do
local price = getPrice(yield);
metrics[state.gathering].estimatedValue = metrics[state.gathering].estimatedValue + (price * count);
end
imgui.SetVarValue(uiVariables[string.format("var_%s_estimatedValue", state.gathering)][1], metrics[state.gathering].estimatedValue);
playAlert(imgui.GetVarValue(uiVariables["var_ClamBreakSoundFile"][1]));
end
if broken or unable then
state.values.clamBucketBroken = true;
ashita.timer.once(1, function () -- let plots update a second
state.timers[state.gathering] = false;
end);
saveSettings();
end
end,
["default"] = function ()
successBreak = string.match(message, string.format("^you %s a[n]? (.*), but your %s .*", gatherData.action, gatherData.tool));
success = string.match(message, string.format("^you successfully %s a[n]? (.*)!", gatherData.action)) or successBreak
unable = string.contains(message, "you are unable to");
broken = string.match(message, "^your (.*) breaks!");
lost = false;
end
}
full = string.contains(message, "you cannot carry any more") or string.contains(message, "your inventory is full");
if success then
local of = string.match(success, "of (.*)");
if of then success = of end;
end
if success then
success = string.lowerToTitle(success);
if not table.haskey(settings.yields[state.gathering], success) then
displayResponse(string.format("Yield: The %s yield name (%s) is unrecognized! Please report this to LoTekkie.", state.gathering, success), "\31\167%s");
state.attempting = false;
return false;
end
val = getPrice(success);
adjYield(success, 1);
if state.gathering == "clamming" then
state.values.clamBucketPz = state.values.clamBucketPz + settings.yields[state.gathering][success].pz
end
local soundPlaying = alertYield(success);
if successBreak then adjTotal("breaks", 1); end
adjTotal("yields", 1);
elseif broken then
adjTotal("breaks", 1);
elseif full or lost then
adjTotal("lost", 1);
end
if success or unable or broken or full or lost then
adjTotal("attempts", 1);
recordCurrentZone();
state.values.lastKnownGathering = state.gathering;
state.attempting = false;
end
curVal = metrics[state.gathering].estimatedValue;
metrics[state.gathering].estimatedValue = curVal + val;
imgui.SetVarValue(uiVariables[string.format("var_%s_estimatedValue", state.gathering)][1], metrics[state.gathering].estimatedValue);
local targetReached = metrics[state.gathering].estimatedValue >= settings.general.targetValue;
if state.values.targetAlertReady and targetReached then
local soundFile = imgui.GetVarValue(uiVariables["var_TargetSoundFile"][1]);
playAlert(soundFile);
state.values.targetAlertReady = false;
end
end
return false;
end);
----------------------------------------------------------------------------------------------------
-- func: outgoing_packet
-- desc: Event called when the client is sending a packet to the server.
----------------------------------------------------------------------------------------------------
ashita.register_event('outgoing_packet', function(id, size, packet, packet_modified, blocked)
if id == 0x36 then -- helm
for gathering, data in pairs(gatherTypes) do
if data.target == ashitaTarget:GetTargetName() then
state.attempting = true;
state.attemptType = data.name;
state.gathering = data.name;
end
end
elseif id == 0x01A then -- clam
if ashitaTarget:GetTargetName() == "Clamming Point" and AshitaCore:GetDataManager():GetPlayer():HasKeyItem(511) then
state.attempting = true;
state.attemptType = "clamming";
state.gathering = "clamming";
elseif struct.unpack("H", packet, 0x0A) == 0x1104 then -- digging
state.attempting = true;
state.attemptType = "digging";
state.gathering = "digging";
else
state.attempting = false;
end
elseif id == 0x110 then -- fishing
local action = struct.unpack("H", packet, 0x0E + 1);
if action ~= 4 then
state.attempting = true;
state.attemptType = "fishing";
state.gathering = "fishing";
else
state.attempting = false
end
end
return false;
end);
----------------------------------------------------------------------------------------------------
-- func: incoming_packet
-- desc: Event called when the client is receiving a packet from the server.
----------------------------------------------------------------------------------------------------
ashita.register_event('incoming_packet', function(id, size, packet, packet_modified, blocked)
if id == 0x00B then -- zoning out (11)
state.attempting = false;
state.values.zoning = true;
state.values.preZoneCounts["available"] = playerStorage['available'];
state.values.preZoneCounts["available_pct"] = playerStorage["available_pct"];
for _, data in ipairs(gatherTypes) do
state.timers[data.name] = false; -- shutdown timers
state.values.preZoneCounts[data.tool] = playerStorage[data.tool];
end
if state.values.lastKnownGathering ~= nil then
if settings.general.autoGenReports then
generateGatheringReport(state.values.lastKnownGathering);
end
state.values.lastKnownGathering = nil;
end
elseif (id == 0x01D and state.values.zoning) then -- inventory ready
state.values.zoning = false;
end
return false;
end);
-- The settings window
local SettingsWindow =
{
modalSaveAction = function (self)
imgui.CloseCurrentPopup();
imgui.SetVarValue(uiVariables["var_SettingsVisible"][1], false)
updateAllStates(state.gathering);
saveSettings();
imgui.SetVarValue(uiVariables["var_AllSoundIndex"][1], 0);
local r, g, b, a = colorToRGBA(-3877684);
imgui.SetVarValue(uiVariables["var_AllColors"][1], r/255, g/255, b/255, a/255);
checkTargetAlertReady();
state.values.feedbackSubmitted = false;
state.values.feedbackMissing = false;
imgui.SetVarValue(uiVariables["var_IssueTitle"][1], "");
imgui.SetVarValue(uiVariables["var_IssueBody"][1], "")
imgui.SetVarValue(uiVariables['var_ReportSelected'][1], nil);
state.values.currentReportName = nil;
end,
Draw = function (self, title)
local scaledHeightReduction = 0;
if state.window.scale == 1.15 then scaledHeightReduction = 7 elseif state.window.scale == 1.30 then scaledHeightReduction = 12 end;
imgui.SetNextWindowSize(state.window.widthSettings, state.window.heightSettings - scaledHeightReduction, ImGuiSetCond_Always);
if state.values.centerWindow then
imgui.SetNextWindowPosCenter(1);
state.values.centerWindow = false;
end
if (not imgui.Begin(title, uiVariables["var_SettingsVisible"][1], imgui.bor(ImGuiWindowFlags_MenuBar, ImGuiWindowFlags_NoResize))) then
imgui.End();
return;
end
imgui.SetWindowFontScale(state.window.scale);
-- SETTINGS_MENU
if imgui.BeginMenuBar() then
for i, data in ipairs(settingsTypes) do
local btnName = string.camelToTitle(data.name);
imguiPushActiveBtnColor(state.settings.activeIndex == i);
if imgui.Button(btnName) then
state.settings.activeIndex = i;
state.values.feedbackSubmitted = false;
state.values.feedbackMissing = false;
imgui.SetVarValue(uiVariables["var_IssueTitle"][1], "");
imgui.SetVarValue(uiVariables["var_IssueBody"][1], "")
end
imgui.PopStyleColor();
imgui.SameLine(0.0, state.window.spaceSettingsBtn);
end
imgui.EndMenuBar();
end
-- /SETTINGS_MENU
-- render settings pages..
imgui.BeginGroup();
imgui.Spacing();
switch(state.settings.activeIndex) : caseof
{
[1] = function() renderSettingsGeneral() end,
[2] = function() renderSettingsSetPrices() end,
[3] = function() renderSettingsSetColors() end,
[4] = function() renderSettingsSetAlerts() end,
[5] = function() renderSettingsReports() end,
[6] = function() renderSettingsFeedback() end,
[7] = function() renderSettingsAbout() end,
};
imgui.EndGroup();
imgui.Spacing();
if imgui.Button("Done") then
imgui.SetVarValue(uiVariables["var_SettingsVisible"][1], false);
end
imgui.SameLine();
imgui.Text("OR close window to save.");
-- Recalculate
local yieldsExist = table.count(metrics[state.settings.setPrices.gathering].yields) > 0;
if state.settings.activeIndex == 2 and yieldsExist then -- if we are setting prices
local spaceBtnRecalculate = state.window.spaceBtnRecalculate;
if settings.general.showToolTips then spaceBtnRecalculate = spaceBtnRecalculate - ( imgui.GetFontSize() * 24 / defaultFontSize ) end
if state.window.scale == 1.15 then spaceBtnRecalculate = spaceBtnRecalculate + 3 end;
if state.window.scale == 1.30 then spaceBtnRecalculate = spaceBtnRecalculate + 6 end;
imgui.SameLine(0.0, spaceBtnRecalculate);
if imguiShowToolTip("Recalculate the estimated value with your current price settings.", settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
if imgui.Button("Recalculate Value") then
updateAllStates(state.settings.setPrices.gathering);
metrics[state.gathering].estimatedValue = 0;
for yield, count in pairs(metrics[state.gathering].yields) do
local price = getPrice(yield);
metrics[state.gathering].estimatedValue = metrics[state.gathering].estimatedValue + (price * count);
end
imgui.SetVarValue(uiVariables[string.format("var_%s_estimatedValue", state.gathering)][1], metrics[state.gathering].estimatedValue);
end
end
if state.initializing then
imgui.SetVarValue(uiVariables["var_SettingsVisible"][1], false);
imgui.SetVarValue(uiVariables["var_HelpVisible"][1], false);
imgui.CloseCurrentPopup();
end
imgui.PopStyleVar();
imgui.End();
end
}
-- The help window
local helpWindow =
{
Draw = function (self, title)
local scaledHeightReduction = 0;
if state.window.scale == 1.15 then scaledHeightReduction = 7 elseif state.window.scale == 1.30 then scaledHeightReduction = 12 end;
imgui.SetNextWindowSize(state.window.widthSettings, state.window.heightSettings - scaledHeightReduction, ImGuiSetCond_Always);
if state.values.centerWindow then
imgui.SetNextWindowPosCenter(1);
state.values.centerWindow = false;
end
if (not imgui.Begin(title, uiVariables["var_HelpVisible"][1], imgui.bor(ImGuiWindowFlags_MenuBar, ImGuiWindowFlags_NoResize))) then
imgui.End();
return;
end
imgui.SetWindowFontScale(state.window.scale);
-- HELP_MENU
if imgui.BeginMenuBar() then
for i, data in ipairs(helpTypes) do
local btnName = string.camelToTitle(data.name);
imguiPushActiveBtnColor(state.help.activeIndex == i);
if imgui.Button(btnName) then
state.help.activeIndex = i;
end
imgui.PopStyleColor();
imgui.SameLine(0.0, state.window.spaceSettingsBtn);
end
imgui.EndMenuBar();
end
-- /HELP_MENU
imgui.BeginGroup();
imgui.Spacing();
switch(state.help.activeIndex) : caseof
{
[1] = function() renderHelpGeneral() end,
[2] = function() renderHelpQsAndAs() end
};
imgui.EndGroup();
imgui.Spacing();
if imgui.Button("Done") then
imgui.SetVarValue(uiVariables["var_HelpVisible"][1], false);
end
imgui.SameLine();
imgui.Text("OR close window to exit.");
if state.initializing then
imgui.SetVarValue(uiVariables["var_SettingsVisible"][1], false);
imgui.SetVarValue(uiVariables["var_HelpVisible"][1], false);
imgui.CloseCurrentPopup();
end
imgui.End();
end
}
----------------------------------------------------------------------------------------------------
-- func: render
-- desc: Called when the addon is rendering.
----------------------------------------------------------------------------------------------------
ashita.register_event('render', function()
local windowScale = windowScales[settings.general.windowScaleIndex];
local scaledFontSize = windowScale*defaultFontSize;
local scaledHeightReduction = 0;
if windowScale == 1.15 then scaledHeightReduction = 34 elseif windowScale == 1.30 then scaledHeightReduction = 54 end;
imgui.PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0);
imgui.PushStyleVar(ImGuiStyleVar_FrameRounding, 5.0);
imgui.PushStyleVar(ImGuiStyleVar_ChildWindowRounding, 5.0);
imgui.PushStyleVar(ImGuiStyleVar_Alpha, settings.general.opacity);
imgui.PushStyleVar(ImGuiStyleVar_WindowPadding, scaledFontSize*5/defaultFontSize, scaledFontSize*5/defaultFontSize);
imgui.PushStyleColor(ImGuiCol_Border, 0.21, 0.47, 0.59, 0.5);
imgui.PushStyleColor(ImGuiCol_PlotLines, 0.77, 0.83, 0.80, 0.3);
imgui.PushStyleColor(ImGuiCol_PlotHistogram, 0.77, 0.83, 0.80, 0.3);
imgui.PushStyleColor(ImGuiCol_TitleBgActive, 17/255, 17/255, 30/255, 1.0);
-- MAIN
imgui.SetNextWindowSize(scaledFontSize*250/defaultFontSize, scaledFontSize*500/defaultFontSize - scaledHeightReduction, ImGuiSetCond_Always);
if state.initializing and state.firstLoad then
imgui.SetNextWindowPosCenter(1);
state.values.centerWindow = true;
elseif state.initializing then
imgui.SetNextWindowPos(state.window.posX , state.window.posY);
end
if not imgui.Begin(string.format("%s v%s by Lotekkie & Narpt", _addon.name, _addon.version), imgui.GetVarValue(uiVariables['var_WindowVisible'][1]), imgui.bor(ImGuiWindowFlags_MenuBar, ImGuiWindowFlags_NoResize)) then
imgui.End();
return
end
state.window = -- Calculations based on scaled window sizes
{
scale = windowScale,
height = scaledFontSize * 500.0 / defaultFontSize,
width = scaledFontSize * 250.0 / defaultFontSize,
padX = scaledFontSize * 5.0 / defaultFontSize,
padY = scaledFontSize * 5.0 / defaultFontSize,
spaceGatherBtn = scaledFontSize * 6.5 / defaultFontSize * windowScale + (windowScale - 1.0) * 2,
spaceGatherImg = scaledFontSize * 6.3 / defaultFontSize * windowScale + (windowScale - 1.0) * 4,
heightHeaderMain = scaledFontSize * 15.0 / defaultFontSize,
heightPlot = scaledFontSize * 25.0 / defaultFontSize,
heightYields = scaledFontSize * 130.0 / defaultFontSize,
spaceToolTip = scaledFontSize * 4.0 / defaultFontSize,
spaceFooterBtn = scaledFontSize * 4.0 / defaultFontSize * windowScale + (windowScale - 1.0) * 2,
widthSettings = scaledFontSize * 500.0 / defaultFontSize,
heightSettings = scaledFontSize * 450.0 / defaultFontSize,
heightSettingsContent = scaledFontSize * 367.0 / defaultFontSize,
heightSettingsScroll = scaledFontSize * 343.0 / defaultFontSize,
spacePriceModeRadio = scaledFontSize * 26.0 / defaultFontSize * windowScale + (windowScale - 1.0) * 2,
spacePriceDefaults = scaledFontSize * 177.0 / defaultFontSize,
spaceEstimatedValue = scaledFontSize * 12.0 / defaultFontSize * windowScale + (windowScale - 1.0) * 2,
widthModalConfirm = scaledFontSize * 350.0 / defaultFontSize,
heightModalConfirm = scaledFontSize * 102.0 / defaultFontSize,
spaceColorDefaults = scaledFontSize * 177.0 / defaultFontSize,
widthWidgetDefault = scaledFontSize * 275.0 / defaultFontSize,
spaceSettingsBtn = scaledFontSize * 6.0 / defaultFontSize * windowScale + (windowScale - 1.0) * 2,
spaceSettingsDefaults = scaledFontSize * 377.0 / defaultFontSize,
widthWidgetValue = scaledFontSize * 191.0 / defaultFontSize,
offsetPriceColumns1 = scaledFontSize * 140.0 / defaultFontSize,
offsetPriceColumns2 = scaledFontSize * 270.0 / defaultFontSize,
heightPriceColumns = scaledFontSize * 25.0 / defaultFontSize,
offsetPriceCursorY = scaledFontSize * 2.0 / defaultFontSize,
offsetNameCursorY = scaledFontSize * 5.0 / defaultFontSize,
sizeGatherTexture = scaledFontSize * 20.0 / defaultFontSize,
spaceBtnRecalculate = scaledFontSize * 152.0 / defaultFontSize,
spaceReportsDelete = scaledFontSize * 176.0 / defaultFontSize,
widthReportScale = scaledFontSize * 150.0 / defaultFontSize
}
imgui.SetWindowFontScale(state.window.scale);
if getPlayerName() ~= "" and not state.reportsLoaded then
for _, data in ipairs(gatherTypes) do
local dirName = string.format("%s\\reports\\%s\\%s", _addon.path, getPlayerName(), data.name);
if ashita.file.dir_exists(dirName) then
for f in io.popen(string.format("dir %s /b", dirName)):lines() do
reports[data.name][#reports[data.name] + 1] = f;
end
else
ashita.file.create_dir(dirName);
end
end
state.reportsLoaded = true;
end
-- MAIN_MENU
if imgui.BeginMenuBar() then
local btnAction = function(data)
updateAllStates(data.name);
state.values.inactivitySeconds = 0;
checkTargetAlertReady();
imgui.SetVarValue(uiVariables['var_ReportSelected'][1], nil);
state.values.currentReportName = nil;
state.settings.setColors.gathering = data.name;
local r, g, b, a = colorToRGBA(-3877684);
imgui.SetVarValue(uiVariables["var_AllColors"][1], r/255, g/255, b/255, a/255);
state.settings.setAlerts.gathering = data.name;
imgui.SetVarValue(uiVariables["var_AllSoundIndex"][1], 0);
end
for _, data in ipairs(gatherTypes) do
if state.values.btnTextureFailure or not settings.general.useImageButtons then
imguiPushActiveBtnColor(data.name == state.gathering);
if imgui.SmallButton(string.upperfirst(data.short)) then
btnAction(data);
end
else
local texture = textures[data.name];
imguiPushActiveBtnColor(data.name == state.gathering);
local textureSize = state.window.sizeGatherTexture;
if imgui.ImageButton(texture:Get(), textureSize, textureSize) then
btnAction(data);
end
end
imgui.PopStyleColor();
if imgui.IsItemHovered() then
imgui.SetTooltip(string.upperfirst(data.name));
end
imgui.SameLine(0.0, state.window.spaceGatherBtn);
end
imgui.EndMenuBar();
end
-- /MAIN_MENU
imguiHalfSep();
-- MAIN_HEADER
if imguiShowToolTip(string.format("Progress towards your target value (adjusted within settings)."), settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip)
end
if imgui.BeginChild("Header", -1, state.window.heightHeaderMain) then
imgui.SetWindowFontScale(state.window.scale);
local progress = calcTargetProgress()
if progress < 1 and progress >= 0.5 then
imgui.PushStyleColor(ImGuiCol_Text, 1, 1, 0.54, 1); -- warn
elseif progress < 0.5 then
imgui.PushStyleColor(ImGuiCol_Text, 1, 0.615, 0.615, 1); -- danger
else
imgui.PushStyleColor(ImGuiCol_Text, 0.39, 0.96, 0.13, 1); -- success
end
imgui.ProgressBar(progress, -1, state.window.heightHeaderMain, string.format("%s/%s", metrics[state.gathering].estimatedValue, settings.general.targetValue))
imgui.PopStyleColor();
imgui.EndChild();
end
-- /MAIN_HEADER
imguiHalfSep(true);
-- totals metrics
for total, metric in pairs(table.sortKeysByLength(metrics[state.gathering].totals, true)) do
if state.gathering == "digging" and metric == "breaks" then
if imguiShowToolTip("Current Moon percentage.", settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
imgui.Text(string.format("%s:", string.upperfirst("Moon")));
imgui.SameLine();
local moonPct = tostring(ashita.ffxi.vanatime.get_current_date().moon_percent);
imgui.TextUnformatted(moonPct.."%");
else
if imguiShowToolTip(metricsTotalsToolTips[metric], settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
imgui.Text(string.format("%s:", string.upperfirst(metric)));
imgui.SameLine();
imgui.Text(metrics[state.gathering].totals[metric])
end
if state.gathering == "clamming" and metric == "yields" then
imgui.SameLine();
imgui.Text("~");
imgui.SameLine();
if imguiShowToolTip("Total pz value in current bucket (will turn red when within 5 points of limit). ", settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
local pzDiff = state.values.clamBucketPzMax - state.values.clamBucketPz;
if pzDiff <= 5 then
imgui.PushStyleColor(ImGuiCol_Text, 1, 0.615, 0.615, 1); -- danger
elseif pzDiff <= state.values.clamBucketPzMax/2 then
imgui.PushStyleColor(ImGuiCol_Text, 1, 1, 0.54, 1); -- warn
else
imgui.PushStyleColor(ImGuiCol_Text, 0.77, 0.83, 0.80, 1); -- plain
end
imgui.Text(string.format("Bucket: %spz", state.values.clamBucketPz));
imgui.PopStyleColor();
end
end
-- totals metrics
-- gathering tools
if imguiShowToolTip("Total gathering tools on hand.", settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
local gatherData = getGatherTypeData();
local avail = playerStorage[gatherData.tool] or 0;
if state.values.zoning then avail = state.values.preZoneCounts[gatherData.tool]; end
local targetAvail = 12;
if state.gathering == "clamming" then targetAvail = 1; end
if avail < targetAvail then
imgui.PushStyleColor(ImGuiCol_Text, 1, 0.615, 0.615, 1); -- danger
else
if state.gathering == "clamming" and state.values.clamBucketBroken then
imgui.PushStyleColor(ImGuiCol_Text, 1, 1, 0.54, 1); -- warn
else
imgui.PushStyleColor(ImGuiCol_Text, 0.77, 0.83, 0.80, 1); -- plain
end
end
local toolName = string.lowerToTitle(gatherData.tool)
if not table.hasvalue({"fishing", "clamming"}, gatherData.name) then
toolName = toolName.."s"
end
imgui.Text(toolName..":");
imgui.SameLine();
local value = avail;
if state.gathering == "clamming" then
if not state.values.clamBucketBroken then
if avail == 1 then value = "Ready"; else value = "None"; end
else
value = "Broken";
end
end
imgui.Text(value);
imgui.PopStyleColor();
-- /gathering tools
-- inventory
if imguiShowToolTip("Total inventory slots available (main inventory only).", settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
local availPct = playerStorage['available_pct'];
if state.values.zoning then availPct = state.values.preZoneCounts['available_pct']; end
if availPct < 50 and availPct >= 25 then
imgui.PushStyleColor(ImGuiCol_Text, 1, 1, 0.54, 1); -- warn
elseif availPct < 25 then
imgui.PushStyleColor(ImGuiCol_Text, 1, 0.615, 0.615, 1); -- danger
else
imgui.PushStyleColor(ImGuiCol_Text, 0.77, 0.83, 0.80, 1); -- plain
end
imgui.Text("Inventory:")
imgui.SameLine();
local avail = playerStorage['available'] or 0;
if state.values.zoning then avail = state.values.preZoneCounts['available']; end
imgui.Text(avail);
imgui.PopStyleColor();
-- /inventory
-- time passed
if imguiShowToolTip(string.format("Time passed since your first %s attempt or when the timer was manually started.", string.upperfirst(state.gathering)), settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
imgui.Text("Time Passed:");
imgui.SameLine();
local r, g, b, a = 1, 0.615, 0.615, 1 -- danger
if state.timers[state.gathering] then
r, g, b, a = 0.77, 0.83, 0.80, 1 -- plain
end
imgui.TextColored(r, g, b, a, os.date("!%X", (metrics[state.gathering].secondsPassed)))
-- /time passed
imgui.Spacing();
-- timer
if imguiShowToolTip(string.format("Start, stop, or clear the %s timer.", string.upperfirst(state.gathering)), settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
imgui.Text("Timer:")
imgui.SameLine();
if imgui.SmallButton(state.values.btnStartTimer) then
state.timers[state.gathering] = not state.timers[state.gathering];
end
if state.timers[state.gathering] then
state.values.btnStartTimer = "Stop";
else
state.values.btnStartTimer = "Start";
end
imgui.SameLine();
if imgui.SmallButton("Clear") then
state.timers[state.gathering] = false;
metrics[state.gathering].secondsPassed = 0;
end
-- /timer
imguiHalfSep();
imgui.AlignFirstTextHeightToWidgets();
-- value
imgui.PushStyleColor(ImGuiCol_Text, 0.39, 0.96, 0.13, 1); -- success
if imguiShowToolTip(string.format("Editable estimated value of all %s yields (yield prices adjusted within settings).", string.upperfirst(state.gathering)), settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
imgui.Text("Value:")
if settings.general.showToolTips then
imgui.SameLine(0.0, state.window.spaceToolTip);
else
imgui.SameLine();
end
imgui.PushItemWidth(-1);
imgui.PushAllowKeyboardFocus(false);
if (imgui.InputInt('', uiVariables[string.format("var_%s_estimatedValue", state.gathering)][1])) then
metrics[state.gathering].estimatedValue = imgui.GetVarValue(uiVariables[string.format("var_%s_estimatedValue", state.gathering)][1]);
checkTargetAlertReady();
end
imgui.PopStyleColor();
imgui.PopItemWidth();
imgui.PopAllowKeyboardFocus();
-- /value
imguiHalfSep(true);
-- plot yields
imgui.PushItemWidth(-1);
local plotYields = metrics[state.gathering].points.yields;
local yieldsLabelMap =
{
[1] = string.format("Yields/HR (%.2f)", metrics[state.gathering].points.yields[#metrics[state.gathering].points.yields]),
[2] = string.format("%.2f/HR", metrics[state.gathering].points.yields[#metrics[state.gathering].points.yields]),
[3] = ""
}
imgui.AlignFirstTextHeightToWidgets();
local plotYieldsLabel = yieldsLabelMap[state.values.yieldsLabelIndex];
if imguiShowToolTip(string.format("Plot histogram of %s yields per hour (click the on plot to cycle its label displays).", string.upperfirst(state.gathering)), settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
local yieldsPerHour = metrics[state.gathering].points.yields[#metrics[state.gathering].points.yields];
local targetYields = 120;
if state.gathering == "fishing" then targetYields = 90; end
if yieldsPerHour < targetYields and yieldsPerHour >= targetYields/2 then
imgui.PushStyleColor(ImGuiCol_Text, 1, 1, 0.54, 1); -- warn
elseif yieldsPerHour < targetYields/2 then
imgui.PushStyleColor(ImGuiCol_Text, 1, 0.615, 0.615, 1); -- danger
else
imgui.PushStyleColor(ImGuiCol_Text, 0.39, 0.96, 0.13, 1); -- success
end
imgui.PlotHistogram("", plotYields, #plotYields, 0, plotYieldsLabel, FLT_MIN, FLT_MAX, 0.0, state.window.heightPlot);
imgui.PopStyleColor()
if imgui.IsItemClicked() then
state.values.yieldsLabelIndex = cycleIndex(state.values.yieldsLabelIndex, 1, 3);
end
if imgui.IsItemClicked(1) then
state.values.yieldsLabelIndex = cycleIndex(state.values.yieldsLabelIndex, 1, 3, -1);
end
if imgui.IsItemHovered() then
if plotYieldsLabel == "" then
imgui.SetTooltip(string.format("Yields/HR (%.2f)", yieldsPerHour));
else
imgui.SetTooltip("");
end
end
-- /plot yields
-- plot values
local plotValues = metrics[state.gathering].points.values;
local valuesLabelMap =
{
[1] = string.format("Value/HR (%.2f)", metrics[state.gathering].points.values[#metrics[state.gathering].points.values]),
[2] = string.format("%.2f/HR", metrics[state.gathering].points.values[#metrics[state.gathering].points.values]),
[3] = ""
}
local plotValuesLabel = valuesLabelMap[state.values.valuesLabelIndex];
if imguiShowToolTip("Plot lines of the estimated value per hour (L/R click on the plot to cycle its label displays).", settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
local valuesPerHour = metrics[state.gathering].points.values[#metrics[state.gathering].points.values];
local targetValue = 30000;
if valuesPerHour < targetValue and valuesPerHour >= targetValue/2 then
imgui.PushStyleColor(ImGuiCol_Text, 1, 1, 0.54, 1); -- warn
elseif valuesPerHour < targetValue/2 then
imgui.PushStyleColor(ImGuiCol_Text, 1, 0.615, 0.615, 1); -- danger
else
imgui.PushStyleColor(ImGuiCol_Text, 0.39, 0.96, 0.13, 1); -- success
end
imgui.PlotLines("", plotValues, #plotValues, 0, plotValuesLabel, FLT_MIN, FLT_MAX, 0.0, state.window.heightPlot);
imgui.PopStyleColor()
if imgui.IsItemClicked() then
state.values.valuesLabelIndex = cycleIndex(state.values.valuesLabelIndex, 1, 3);
end
if imgui.IsItemClicked(1) then
state.values.valuesLabelIndex = cycleIndex(state.values.valuesLabelIndex, 1, 3, -1);
end
if imgui.IsItemHovered() then
if plotValuesLabel == "" then
imgui.SetTooltip(string.format("Value/HR (%.2f)", valuesPerHour));
else
imgui.SetTooltip("");
end
end
-- /plot values
imgui.PopItemWidth();
imguiFullSep();
-- MAIN_SCROLLING
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip(string.format("Scrollable List of current %s yields and their amounts (L/R click on the list to cycle its sorting methods).", string.upperfirst(state.gathering)), settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
yieldsSortMap =
{
[1] = { table.sortKeysByAlphabet(metrics[state.gathering].yields, false), "Alphabetical (DESC)" },
[2] = { table.sortKeysByAlphabet(metrics[state.gathering].yields, true), "Alphabetical (ASC)" },
[3] = { table.sortbykey(metrics[state.gathering].yields, false), "Count (DESC)" },
[4] = { table.sortbykey(metrics[state.gathering].yields, true), "Count (ASC)" },
[5] = { table.sortKeysByTotalValue(metrics[state.gathering].yields, false), "Value (DESC)" },
[6] = { table.sortKeysByTotalValue(metrics[state.gathering].yields, true), "Value (ASC)"}
}
if imgui.BeginChild("Scrolling", -1, state.window.heightYields, true) then
imgui.SetWindowFontScale(state.window.scale);
-- yields
imgui.PushTextWrapPos(imgui.GetContentRegionAvailWidth());
for _, item in pairs(yieldsSortMap[state.values.yieldSortIndex][1]) do
if settings.general.showToolTips then
imgui.SetCursorPosX(imgui.GetCursorPosX() - 3.0);
imgui.TextDisabled('(?)');
if imgui.IsItemHovered() then
state.values.yieldListHovered = false;
state.values.yieldListBtnsHovered = true;
imgui.SetTooltip(string.format("Manually Add(+) or subtract(-) %s", item));
elseif state.values.yieldListHovered then
state.values.yieldListBtnsHovered = false
end
imgui.SameLine(0.0, state.window.spaceToolTip - 1.5);
end
imgui.BeginGroup();
imgui.SmallButton("-");
if imgui.IsItemClicked() then
adjYield(item, -1);
adjTotal("yields", -1);
val = getPrice(item);
curVal = metrics[state.gathering].estimatedValue;
metrics[state.gathering].estimatedValue = curVal - val;
imgui.SetVarValue(uiVariables[string.format("var_%s_estimatedValue", state.gathering)][1], metrics[state.gathering].estimatedValue);
end
imgui.SameLine(0.0, 1.0);
imgui.SmallButton("+");
if imgui.IsItemClicked() then
adjYield(item, 1);
adjTotal("yields", 1);
val = getPrice(item);
curVal = metrics[state.gathering].estimatedValue;
metrics[state.gathering].estimatedValue = curVal + val;
imgui.SetVarValue(uiVariables[string.format("var_%s_estimatedValue", state.gathering)][1], metrics[state.gathering].estimatedValue);
end
imgui.EndGroup();
if imgui.IsItemHovered() then
state.values.yieldListHovered = false;
state.values.yieldListBtnsHovered = true;
imgui.SetTooltip("");
elseif state.values.yieldListHovered then
state.values.yieldListBtnsHovered = false
imgui.SetTooltip(string.format("Sort Type: %s", yieldsSortMap[state.values.yieldSortIndex][2]));
end
imgui.SameLine(0.0, state.window.spaceToolTip);
local r, g, b, a = colorToRGBA(settings.yields[state.gathering][item].color);
local shortName = settings.yields[state.gathering][item].short;
local adjItemName = shortName or item;
imgui.TextColored(r/255, g/255, b/255, a/255, adjItemName..":");
imgui.SameLine(0.0, state.window.spaceToolTip);
imgui.Text(metrics[state.gathering].yields[item]);
if settings.general.showDetailedYields then
local pricePer = getPrice(item);
local r, g, b, a = colorToRGBA(settings.general.yieldDetailsColor);
imgui.TextColored(r/255, g/255, b/255, a/255, string.format("@%dea.=(%s)", getPrice(item), math.floor(getPrice(item) * metrics[state.gathering].yields[item])));
end
end
imgui.PopTextWrapPos();
imgui.EndChild();
if imgui.IsItemClicked() then
state.values.yieldListClicked = true;
if not state.values.yieldListBtnsHovered then
state.values.yieldSortIndex = cycleIndex(state.values.yieldSortIndex, 1, 6);
end
end
if imgui.IsItemClicked(1) then
state.values.yieldListClicked = true;
if not state.values.yieldListBtnsHovered then
state.values.yieldSortIndex = cycleIndex(state.values.yieldSortIndex, 1, 6, -1);
end
end
if imgui.IsItemHovered() then
if table.count(metrics[state.gathering].yields) == 0 then
imgui.SetTooltip(string.format("Sort Type: %s", yieldsSortMap[state.values.yieldSortIndex][2]));
else
if not state.values.yieldListClicked then
state.values.yieldListHovered = true;
end
end
else
state.values.yieldListHovered = false;
end
if not imgui.IsMouseDown(1) and not imgui.IsMouseDown(0) then
state.values.yieldListClicked = false;
end
-- /yields
end
-- /MAIN_SCROLLING
imguiFullSep();
if imgui.Button("Exit") then
state.actions.modalConfirmAction = function() AshitaCore:GetChatManager():QueueCommand('/addon unload yield', 1); end
state.values.modalConfirmPrompt = string.format(modalConfirmPromptTemplate, "Exit");
state.values.modalConfirmHelp = "(All gathering data will be saved.)";
imgui.OpenPopup("Yield Confirm")
end
imgui.SameLine(0.0, state.window.spaceFooterBtn);
if imgui.Button("Reload") then
state.actions.modalConfirmAction = function() AshitaCore:GetChatManager():QueueCommand('/addon reload yield', 1); end
state.values.modalConfirmPrompt = string.format(modalConfirmPromptTemplate, "Reload");
state.values.modalConfirmHelp = "(All gathering data will be saved.)";
imgui.OpenPopup("Yield Confirm")
end
imgui.SameLine(0.0, state.window.spaceFooterBtn);
if imgui.Button("Reset") then
state.actions.modalConfirmAction = function()
-- Generate report..
if settings.general.autoGenReports then
generateGatheringReport(state.gathering);
end
-- Reset the metrics..
metrics[state.gathering] = table.copy(metricsTemplate);
-- Reset the timers..
for timerName, running in pairs(state.timers) do
state.timers[timerName] = false
end
-- Reset ui variables..
imgui.SetVarValue(uiVariables[string.format("var_%s_estimatedValue", state.gathering)][1], metrics[state.gathering].estimatedValue);
-- Reset the zones..
settings.zones[state.gathering] = {};
state.values.lastKnownGathering = nil;
end
state.values.modalConfirmPrompt = string.format(modalConfirmPromptTemplate, "Reset");
state.values.modalConfirmHelp = string.format("(Current %s data will be lost.)", string.upperfirst(state.gathering));
state.values.modalConfirmDanger = true;
if state.gathering == "clamming" then
state.values.clamConfirmedYields = {};
state.values.clamBucketPz = 0;
end
saveSettings();
imgui.OpenPopup("Yield Confirm")
end
imgui.SameLine(0.0, state.window.spaceFooterBtn);
if imgui.Button("Settings") then
if imgui.GetVarValue(uiVariables["var_HelpVisible"][1]) then
imgui.SetVarValue(uiVariables["var_HelpVisible"][1], false);
end
imgui.SetVarValue(uiVariables["var_SettingsVisible"][1], true);
state.values.centerWindow = true;
end
imgui.SameLine(0.0, state.window.spaceFooterBtn);
if imgui.Button("Help") then
if imgui.GetVarValue(uiVariables["var_SettingsVisible"][1]) then
imgui.SetVarValue(uiVariables["var_SettingsVisible"][1], false);
end
imgui.SetVarValue(uiVariables["var_HelpVisible"][1], true);
state.values.centerWindow = true;
end
-- CONFIRM
local scaledHeightReduction = 0;
if windowScale == 1.15 then scaledHeightReduction = 10 elseif windowScale == 1.30 then scaledHeightReduction = 16 end;
imgui.SetNextWindowSize(state.window.widthModalConfirm, state.window.heightModalConfirm - scaledHeightReduction, ImGuiSetCond_Always)
if imgui.BeginPopupModal("Yield Confirm", imgui.GetVarValue(uiVariables['var_WindowVisible'][1]), imgui.bor(ImGuiWindowFlags_NoResize)) then
imgui.SetWindowFontScale(state.window.scale);
imgui.Text(state.values.modalConfirmPrompt);
imgui.Spacing();
if state.values.modalConfirmHelp then
local r, g, b, a = 0.39, 0.96, 0.13, 1
if state.values.modalConfirmDanger then
r, g, b, a = 1, 0.615, 0.615, 1
end
imgui.TextColored(r, g, b, a, state.values.modalConfirmHelp);
end
imguiFullSep();
if imgui.Button("Yes") or state.initializing then
imgui.CloseCurrentPopup();
state.actions.modalCancelAction = function() end
state.actions.modalConfirmAction();
end
imgui.SameLine(0.0, 10);
if imgui.Button("No") then
imgui.CloseCurrentPopup();
state.actions.modalConfirmAction = function() end
state.actions.modalCancelAction();
end
imgui.SameLine();
imgui.Text("OR click away to exit.");
if (not imgui.IsMouseHoveringAnyWindow() and imgui.IsMouseClicked()) then
SettingsWindow:modalSaveAction();
end
if state.initializing then
imgui.CloseCurrentPopup();
imgui.SetVarValue(uiVariables["var_SettingsVisible"][1], false);
end
imgui.EndPopup();
else
state.values.modalConfirmPrompt = ""
state.values.modalConfirmHelp = ""
state.values.modalConfirmDanger = false
end
-- /CONFIRM
state.initializing = false
-- /MAIN
state.window.posX, state.window.posY = imgui.GetWindowPos();
imgui.End();
-- SETTINGS
if imgui.GetVarValue(uiVariables["var_SettingsVisible"][1]) then
state.values.settingsWindowOpen = true;
SettingsWindow:Draw("Yield Settings")
elseif state.values.settingsWindowOpen then
state.values.settingsWindowOpen = false;
SettingsWindow:modalSaveAction();
end
-- /SETTINGS
-- HELP
if imgui.GetVarValue(uiVariables["var_HelpVisible"][1]) then
state.values.helpWindowOpen = true;
helpWindow:Draw("Yield Help");
elseif state.values.helpWindowOpen then
state.values.helpWindowOpen = false;
state.firstLoad = false;
end
-- /HELP
end);
----------------------------------------------------------------------------------------------------
-- func: renderSettingsGeneral
-- desc: Renders the General settings.
----------------------------------------------------------------------------------------------------
function renderSettingsGeneral()
if imgui.BeginChild("General", -1, state.window.heightSettingsContent, imgui.GetVarValue(uiVariables['var_WindowVisible'][1])) then
imgui.SetWindowFontScale(state.window.scale);
imgui.PushItemWidth(state.window.widthWidgetDefault);
imgui.AlignFirstTextHeightToWidgets();
imgui.TextColored(1, 1, 0.54, 1, "Window");
local spaceSettingsDefaults = state.window.spaceSettingsDefaults;
if settings.general.showToolTips then spaceSettingsDefaults = spaceSettingsDefaults - ( imgui.GetFontSize() * 24 / defaultFontSize ) end
imgui.SameLine(0.0, spaceSettingsDefaults);
if imguiShowToolTip("Set all general settings to their defaults.", settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
--imgui.PushStyleColor(ImGuiCol_Button, 0.21, 0.47, 0.59, 1); -- info
if imgui.Button("Defaults") then
settings.general = table.copy(defaultSettingsTemplate.general);
imgui.SetVarValue(uiVariables["var_WindowOpacity"][1], settings.general.opacity);
imgui.SetVarValue(uiVariables["var_TargetValue"][1], settings.general.targetValue);
imgui.SetVarValue(uiVariables["var_ShowToolTips"][1], settings.general.showToolTips);
imgui.SetVarValue(uiVariables["var_WindowScaleIndex"][1], settings.general.windowScaleIndex);
imgui.SetVarValue(uiVariables["var_ShowDetailedYields"][1], settings.general.showDetailedYields);
imgui.SetVarValue(uiVariables["var_UseImageButtons"][1], settings.general.useImageButtons);
imgui.SetVarValue(uiVariables["var_EnableSoundAlerts"][1], true);
imgui.SetVarValue(uiVariables["var_AutoGenReports"][1], true);
local r, g, b, a = colorToRGBA(settings.general.yieldDetailsColor);
imgui.SetVarValue(uiVariables["var_YieldDetailsColor"][1], r/255, g/255, b/255, a/255);
end
--imgui.PopStyleColor();
imguiHalfSep(true);
-- Opacity
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip("Current alpha channel value of all Yield windows.", settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
if (imgui.SliderFloat("Window Opacity", uiVariables['var_WindowOpacity'][1], 0.25, 1.0, "%1.2f")) then
settings.general.opacity = imgui.GetVarValue(uiVariables['var_WindowOpacity'][1])
end
-- /Opacity
imgui.Spacing();
-- Scale
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip("Current size for all Yield windows.", settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
if imgui.Combo("Window Size", uiVariables['var_WindowScaleIndex'][1], "Small\0Medium\0Large\0\0") then
settings.general.windowScaleIndex = imgui.GetVarValue(uiVariables['var_WindowScaleIndex'][1]);
end
-- /Scale
imguiFullSep();
imgui.TextColored(1, 1, 0.54, 1, "Gathering")
imguiFullSep();
-- Target Value
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip("Amount you would like to earn this session (affects progress bar).", settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
if (imgui.InputInt("Target Value", uiVariables['var_TargetValue'][1])) then
settings.general.targetValue = imgui.GetVarValue(uiVariables['var_TargetValue'][1]);
end
-- /Target Value
imgui.Spacing();
-- Target Sound
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip("Sound that will be played when you reach your target value (will only play if your target is reached through gathering).", settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
if imgui.Button("Play") then
end
if imgui.IsItemClicked() then
local soundFile = imgui.GetVarValue(uiVariables["var_TargetSoundFile"][1]);
if soundFile ~= "" then
ashita.misc.play_sound(string.format(_addon.path.."sounds\\%s", soundFile));
end
end
imgui.SameLine();
local scaledWidths ={ [0] = 232, [1] = 268, [2] = 304 };
imgui.PushItemWidth(scaledWidths[settings.general.windowScaleIndex]);
if imgui.Combo("Target Alert", uiVariables["var_TargetSoundIndex"][1], getSoundOptions()) then
local soundIndex = imgui.GetVarValue(uiVariables["var_TargetSoundIndex"][1]);
local soundFile = sounds[soundIndex];
imgui.SetVarValue(uiVariables["var_TargetSoundFile"][1], "");
imgui.SetVarValue(uiVariables["var_TargetSoundFile"][1], soundFile);
end
imgui.PopItemWidth();
-- /Target Sound
-- Detailed Yields
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip("Toggles the display of the math breakdown in the scrollable yields list.", settings.general.showToolTips) then
imgui.SameLine(0.0 , state.window.spaceToolTip);
end
if (imgui.Checkbox("Show Detailed Yields", uiVariables['var_ShowDetailedYields'][1])) then
settings.general.showDetailedYields = imgui.GetVarValue(uiVariables['var_ShowDetailedYields'][1]);
end
-- /Detailed Yields
imgui.Spacing();
-- Yield Details Color
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip("Set the color of the math breakdown in the scrollable yields list.", settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
local r, g, b, a = colorToRGBA(settings.general.yieldDetailsColor);
if imgui.ColorEdit4("Yield Details Color", uiVariables["var_YieldDetailsColor"][1]) then
settings.general.yieldDetailsColor = colorTableToInt(imgui.GetVarValue(uiVariables["var_YieldDetailsColor"][1]));
end
-- /Yield Details Color
imgui.Spacing();
-- Sound Alerts
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip("Toggles the set sound alerts for incoming yields.", settings.general.showToolTips) then
imgui.SameLine(0.0 , state.window.spaceToolTip);
end
if (imgui.Checkbox("Enable Sound Alerts", uiVariables['var_EnableSoundAlerts'][1])) then
settings.general.enableSoundAlerts = imgui.GetVarValue(uiVariables['var_EnableSoundAlerts'][1]);
end
-- /Sound Alerts
-- Reports
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip("Toggles automatic report generation when zoning or after a data reset (you may still manually generate a report regardless).", settings.general.showToolTips) then
imgui.SameLine(0.0 , state.window.spaceToolTip);
end
if (imgui.Checkbox("Auto Generate Reports", uiVariables['var_AutoGenReports'][1])) then
settings.general.autoGenReports = imgui.GetVarValue(uiVariables['var_AutoGenReports'][1]);
end
-- /Reports
imguiFullSep();
imgui.TextColored(1, 1, 0.54, 1, "Misc") -- warn
imguiFullSep();
-- Image Buttons
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip("Toggles the display of images used for all gathering buttons. If off, text will be used instead.", settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
if (imgui.Checkbox('Use Image Buttons', uiVariables["var_UseImageButtons"][1])) then
settings.general.useImageButtons = imgui.GetVarValue(uiVariables["var_UseImageButtons"][1]);
end
-- /Image Buttons
-- Tooltips
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip("Toggles the display of (?)s and their tooltips.", settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
if (imgui.Checkbox('Show (?) Tooltips', uiVariables['var_ShowToolTips'][1])) then
settings.general.showToolTips = imgui.GetVarValue(uiVariables['var_ShowToolTips'][1]);
end
-- /Tooltips
imgui.PopItemWidth();
imgui.EndChild()
end
end
----------------------------------------------------------------------------------------------------
-- func: renderSettingsSetPrices
-- desc: Renders the Set Prices settings.
----------------------------------------------------------------------------------------------------
function renderSettingsSetPrices()
local gathering = state.settings.setPrices.gathering
if imgui.BeginChild("Set Prices", -1, state.window.heightSettingsContent, imgui.GetVarValue(uiVariables['var_WindowVisible'][1]), imgui.bor(ImGuiWindowFlags_MenuBar, ImGuiWindowFlags_NoResize)) then
imgui.SetWindowFontScale(state.window.scale);
if imgui.BeginMenuBar() then
local btnAction = function(data)
state.settings.setPrices.gathering = data.name;
end
for _, data in ipairs(gatherTypes) do
if state.values.btnTextureFailure or not settings.general.useImageButtons then
imguiPushActiveBtnColor(data.name == gathering);
if imgui.SmallButton(string.upperfirst(data.short)) then
btnAction(data);
end
else
local texture = textures[data.name];
imguiPushActiveBtnColor(data.name == gathering);
local textureSize = state.window.sizeGatherTexture;
if imgui.ImageButton(texture:Get(), textureSize, textureSize) then
btnAction(data);
end
end
imgui.PopStyleColor();
if imgui.IsItemHovered() then
imgui.SetTooltip(string.upperfirst(data.name));
end
imgui.SameLine(0.0, state.window.spaceGatherBtn);
end
-- Defaults
local spacePriceDefaults = state.window.spacePriceDefaults;
if settings.general.showToolTips then
spacePriceDefaults = spacePriceDefaults - ( imgui.GetFontSize() * 24 / defaultFontSize );
if state.window.scale > 1.0 then spacePriceDefaults = spacePriceDefaults + 2 end;
end
imgui.SameLine(0.0, spacePriceDefaults);
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip(string.format("Set all %s prices to their default values (0).", string.upperfirst(gathering)), settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
if imgui.SmallButton("Defaults", uiVariables[string.format("var_%s_priceMode", gathering)][1], 2) then
for yield, data in pairs(settings.yields[gathering], true) do
settings.yields[gathering][yield].singlePrice = 0;
settings.yields[gathering][yield].stackPrice = 0;
imgui.SetVarValue(uiVariables[string.format("var_%s_%s_prices", gathering, yield)][1], 0, 0);
end
imgui.SetVarValue(uiVariables[string.format("var_%s_priceMode", gathering)][1], 0);
end
-- Defaults
imgui.EndMenuBar();
end
-- Columns
imgui.SetCursorPosX(0);
if imgui.BeginChild("Column Names", imgui.GetWindowWidth(), state.window.heightPriceColumns) then
imgui.SetWindowFontScale(state.window.scale);
imgui.Columns(3, "Price Mode Columns", false);
imgui.SetColumnOffset(1, state.window.offsetPriceColumns1);
--local col2Offset = state.window.offsetPriceColumns2;
imgui.SameLine(0.0, 5.0);
imgui.AlignFirstTextHeightToWidgets();
local cursorOffsetY = state.window.offsetPriceCursorY;
imgui.SetCursorPosY(imgui.GetCursorPosY() + cursorOffsetY);
--imgui.SetCursorPosX(imgui.GetCursorPosX() + 10.0);
if imguiShowToolTip("Use the set single-item prices for calculations.", settings.general.showToolTips) then
imgui.SameLine(0.0, 0.0);
end
if imgui.RadioButton("Single Prices", uiVariables[string.format("var_%s_priceMode", gathering)][1], 1) then
settings.priceModes[gathering] = imgui.GetVarValue(uiVariables[string.format("var_%s_priceMode", gathering)][1]);
end
imgui.NextColumn();
imgui.SetCursorPosY(imgui.GetCursorPosY() + cursorOffsetY);
imgui.SetCursorPosX(imgui.GetCursorPosX() - 5.0);
imgui.SetColumnOffset(2, state.window.offsetPriceColumns2);
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip("Use the set stack prices for calculations (Yield will do the math for you).", settings.general.showToolTips) then
imgui.SameLine(0.0, 0.0);
end
if imgui.RadioButton("Stack Prices", uiVariables[string.format("var_%s_priceMode", gathering)][1], 0) then
settings.priceModes[gathering] = imgui.GetVarValue(uiVariables[string.format("var_%s_priceMode", gathering)][1]);
end
imgui.NextColumn();
-- NPC prices
local spacePriceMode = state.window.spacePriceModeRadio;
if settings.general.showToolTips then spacePriceMode = 6.0 end
imgui.SameLine(0.0, spacePriceMode);
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip(string.format("Use NPC base prices (Yield will automatically fetch the NPC single item prices as you gather). This option will override your set prices without modifying them.", string.upperfirst(gathering)), settings.general.showToolTips) then
imgui.SameLine(0.0, 0.0);
end
if imgui.RadioButton("NPC prices", uiVariables[string.format("var_%s_priceMode", gathering)][1], 2) then
settings.priceModes[gathering] = imgui.GetVarValue(uiVariables[string.format("var_%s_priceMode", gathering)][1]);
end
-- /NPC prices
imgui.EndChild();
end
-- /Columns
imgui.Separator();
if imgui.BeginChild("Scrolling", -1, -1) then
imgui.SetWindowFontScale(state.window.scale);
for i, yield in pairs(table.sortKeysByAlphabet(settings.yields[gathering], true)) do
local data = settings.yields[gathering][yield];
if data.id ~= nil then
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip(string.format("Set the single-item and or stack prices for %s.", yield), settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
imgui.PushItemWidth(state.window.widthWidgetDefault);
local adjItemName = data.short or yield;
local disabled = settings.priceModes[gathering] == 2;
imguiPushDisabled(disabled);
local flags = 0;
if disabled then flags = ImGuiInputTextFlags_ReadOnly; end
if imgui.InputInt2(adjItemName, uiVariables[string.format("var_%s_%s_prices", gathering, yield)][1], imgui.bor(flags)) then
local prices = imgui.GetVarValue(uiVariables[string.format("var_%s_%s_prices", gathering, yield)][1]);
settings.yields[gathering][yield].singlePrice = prices[1];
settings.yields[gathering][yield].stackPrice = prices[2];
end
imgui.PopItemWidth();
imguiPopDisabled(disabled);
end
end
imgui.EndChild()
end
imgui.EndChild()
end
end
----------------------------------------------------------------------------------------------------
-- func: renderSettingsSetColors
-- desc: Renders the Set Colors settings.
----------------------------------------------------------------------------------------------------
function renderSettingsSetColors()
local gathering = state.settings.setColors.gathering;
if imgui.BeginChild("Set Colors", -1, state.window.heightSettingsContent, imgui.GetVarValue(uiVariables['var_WindowVisible'][1]), imgui.bor(ImGuiWindowFlags_MenuBar, ImGuiWindowFlags_NoResize)) then
imgui.SetWindowFontScale(state.window.scale);
if imgui.BeginMenuBar() then
local btnAction = function(data)
state.settings.setColors.gathering = data.name;
local r, g, b, a = colorToRGBA(-3877684);
imgui.SetVarValue(uiVariables["var_AllColors"][1], r/255, g/255, b/255, a/255);
end
for _, data in ipairs(gatherTypes) do
if state.values.btnTextureFailure or not settings.general.useImageButtons then
imguiPushActiveBtnColor(data.name == gathering);
if imgui.SmallButton(string.upperfirst(data.short)) then
btnAction(data);
end
else
local texture = textures[data.name];
imguiPushActiveBtnColor(data.name == gathering);
local textureSize = state.window.sizeGatherTexture;
if imgui.ImageButton(texture:Get(), textureSize, textureSize) then
btnAction(data);
end
end
imgui.PopStyleColor();
if imgui.IsItemHovered() then
imgui.SetTooltip(string.upperfirst(data.name));
end
imgui.SameLine(0.0, state.window.spaceGatherBtn);
end
-- Defaults
local spaceColorDefaults = state.window.spaceColorDefaults;
if settings.general.showToolTips then
spaceColorDefaults = spaceColorDefaults - ( imgui.GetFontSize() * 24 / defaultFontSize );
if state.window.scale > 1.0 then spaceColorDefaults = spaceColorDefaults + 2 end;
end
imgui.SameLine(0.0, spaceColorDefaults);
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip(string.format("Set all %s yield colors to their defaults.", string.upperfirst(gathering)), settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
if imgui.SmallButton("Defaults") then
for yield, data in pairs(settings.yields[gathering]) do
settings.yields[gathering][yield].color = -3877684 -- plain
local r, g, b, a = colorToRGBA(-3877684);
imgui.SetVarValue(uiVariables[string.format("var_%s_%s_color", gathering, yield)][1], r/255, g/255, b/255, a/255);
imgui.SetVarValue(uiVariables["var_AllColors"][1], r/255, g/255, b/255, a/255);
end
end
-- /Defaults
imgui.EndMenuBar();
end
-- All
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip("Set the text color for all yields when they are displayed in the yield list.", settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
local scaledWidths ={ [0] = 275, [1] = 318, [2] = 364 };
imgui.PushItemWidth(state.window.widthWidgetDefault);
if imgui.ColorEdit4("Set All", uiVariables["var_AllColors"][1]) then
local color = imgui.GetVarValue(uiVariables["var_AllColors"][1]);
for yield, data in pairs(settings.yields[gathering]) do
imgui.SetVarValue(uiVariables[string.format("var_%s_%s_color", gathering, yield)][1], color[1], color[2], color[3], color[4]);
settings.yields[gathering][yield].color = colorTableToInt(imgui.GetVarValue(uiVariables[string.format("var_%s_%s_color", gathering, yield)][1]));
end
end
imgui.PopItemWidth();
-- All
imgui.Separator();
for data, yield in pairs(table.sortKeysByAlphabet(settings.yields[gathering], true)) do
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip(string.format("Set the text color for %s when its displayed in the yield list.", yield), settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
local r, g, b, a = colorToRGBA(settings.yields[gathering][yield].color);
imgui.PushStyleColor(ImGuiCol_Text, r/255, g/255, b/255, a/255);
imgui.PushItemWidth(state.window.widthWidgetDefault);
local shortName = settings.yields[gathering][yield].short;
local adjItemName = shortName or yield;
if (imgui.ColorEdit4(adjItemName, uiVariables[string.format("var_%s_%s_color", gathering, yield)][1])) then
settings.yields[gathering][yield].color = colorTableToInt(imgui.GetVarValue(uiVariables[string.format("var_%s_%s_color", gathering, yield)][1]));
end
imgui.PopStyleColor();
imgui.PopItemWidth();
end
imgui.EndChild()
end
end
----------------------------------------------------------------------------------------------------
-- func: renderSettingsSetAlerts
-- desc: Renders the Set Alerts settings.
----------------------------------------------------------------------------------------------------
function renderSettingsSetAlerts()
local gathering = state.settings.setAlerts.gathering;
if imgui.BeginChild("Set Alerts", -1, state.window.heightSettingsContent, imgui.GetVarValue(uiVariables['var_WindowVisible'][1]), imgui.bor(ImGuiWindowFlags_MenuBar, ImGuiWindowFlags_NoResize)) then
imgui.SetWindowFontScale(state.window.scale);
if imgui.BeginMenuBar() then
local btnAction = function(data)
state.settings.setAlerts.gathering = data.name;
imgui.SetVarValue(uiVariables["var_AllSoundIndex"][1], 0);
end
for _, data in ipairs(gatherTypes) do
if state.values.btnTextureFailure or not settings.general.useImageButtons then
imguiPushActiveBtnColor(data.name == gathering);
if imgui.SmallButton(string.upperfirst(data.short)) then
btnAction(data);
end
else
local texture = textures[data.name];
imguiPushActiveBtnColor(data.name == gathering);
local textureSize = state.window.sizeGatherTexture;
if imgui.ImageButton(texture:Get(), textureSize, textureSize) then
btnAction(data);
end
end
imgui.PopStyleColor();
if imgui.IsItemHovered() then
imgui.SetTooltip(string.upperfirst(data.name));
end
imgui.SameLine(0.0, state.window.spaceGatherBtn);
end
-- Defaults
local spaceColorDefaults = state.window.spaceColorDefaults;
if settings.general.showToolTips then
spaceColorDefaults = spaceColorDefaults - ( imgui.GetFontSize() * 24 / defaultFontSize );
if state.window.scale > 1.0 then spaceColorDefaults = spaceColorDefaults + 2 end;
end
imgui.SameLine(0.0, spaceColorDefaults);
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip(string.format("Set all %s yield colors to their defaults.", string.upperfirst(gathering)), settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
if imgui.SmallButton("Defaults") then
for yield, data in pairs(settings.yields[gathering]) do
settings.yields[gathering][yield].soundIndex = 0
imgui.SetVarValue(uiVariables[string.format("var_%s_%s_soundFile", gathering, yield)][1], "");
imgui.SetVarValue(uiVariables[string.format("var_%s_%s_soundIndex", gathering, yield)][1], 0);
imgui.SetVarValue(uiVariables["var_AllSoundIndex"][1], 0);
end
if gathering == "fishing" then
imgui.SetVarValue(uiVariables["var_FishingSkillSoundIndex"][1], 0);
imgui.SetVarValue(uiVariables["var_FishingSkillSoundFile"][1], "");
end
if gathering == "clamming" then
imgui.SetVarValue(uiVariables["var_ClamBreakSoundIndex"][1], 0);
imgui.SetVarValue(uiVariables["var_ClamBreakSoundFile"][1], "");
end
end
-- /Defaults
imgui.EndMenuBar();
end
-- All
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip("Set a sound alert for all yields.", settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
local scaledWidths ={ [0] = 273, [1] = 318, [2] = 364 };
imgui.PushItemWidth(scaledWidths[settings.general.windowScaleIndex]);
if imgui.Combo("Set All", uiVariables["var_AllSoundIndex"][1], getSoundOptions()) then
local soundIndex = imgui.GetVarValue(uiVariables["var_AllSoundIndex"][1]);
local soundFile = sounds[soundIndex];
for yield, data in pairs(settings.yields[gathering]) do
imgui.SetVarValue(uiVariables[string.format("var_%s_%s_soundIndex", gathering, yield)][1], soundIndex);
imgui.SetVarValue(uiVariables[string.format("var_%s_%s_soundFile", gathering, yield)][1], "");
imgui.SetVarValue(uiVariables[string.format("var_%s_%s_soundFile", gathering, yield)][1], soundFile);
end
if gathering == "fishing" then
imgui.SetVarValue(uiVariables["var_FishingSkillSoundIndex"][1], soundIndex);
imgui.SetVarValue(uiVariables["var_FishingSkillSoundFile"][1], "");
imgui.SetVarValue(uiVariables["var_FishingSkillSoundFile"][1], soundFile);
end
if gathering == "clamming" then
imgui.SetVarValue(uiVariables["var_ClamBreakSoundIndex"][1], soundIndex);
imgui.SetVarValue(uiVariables["var_ClamBreakSoundFile"][1], "");
imgui.SetVarValue(uiVariables["var_ClamBreakSoundFile"][1], soundFile);
end
end
imgui.PopItemWidth();
-- All
imgui.Separator();
-- Fishing Skillup
if gathering == "fishing" then
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip("Set a sound alert for when you receive a fishing skill-up.", settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
if imgui.Button("Play") then end
if imgui.IsItemClicked() then
local soundFile = imgui.GetVarValue(uiVariables["var_FishingSkillSoundFile"][1]);
if soundFile ~= "" then
ashita.misc.play_sound(string.format(_addon.path.."sounds\\%s", soundFile));
end
end
imgui.SameLine();
imgui.PushItemWidth(state.window.widthWidgetDefault - 45);
if imgui.Combo("Skill-Up", uiVariables["var_FishingSkillSoundIndex"][1], getSoundOptions()) then
local soundIndex = imgui.GetVarValue(uiVariables["var_FishingSkillSoundIndex"][1]);
local soundFile = sounds[soundIndex];
imgui.SetVarValue(uiVariables["var_FishingSkillSoundFile"][1], "");
imgui.SetVarValue(uiVariables["var_FishingSkillSoundFile"][1], soundFile);
end
imgui.PopItemWidth();
imgui.Separator();
end
-- /Fishing Skillup
-- Clamming break
if gathering == "clamming" then
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip("Set a sound alert for when your clamming bucket breaks.", settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
if imgui.Button("Play") then end
if imgui.IsItemClicked() then
local soundFile = imgui.GetVarValue(uiVariables["var_ClamBreakSoundFile"][1]);
if soundFile ~= "" then
ashita.misc.play_sound(string.format(_addon.path.."sounds\\%s", soundFile));
end
end
imgui.SameLine();
imgui.PushItemWidth(state.window.widthWidgetDefault - 45);
if imgui.Combo("Bucket Break", uiVariables["var_ClamBreakSoundIndex"][1], getSoundOptions()) then
local soundIndex = imgui.GetVarValue(uiVariables["var_ClamBreakSoundIndex"][1]);
local soundFile = sounds[soundIndex];
imgui.SetVarValue(uiVariables["var_ClamBreakSoundFile"][1], "");
imgui.SetVarValue(uiVariables["var_ClamBreakSoundFile"][1], soundFile);
end
imgui.PopItemWidth();
imgui.Separator();
end
-- /Clamming break
for data, yield in pairs(table.sortKeysByAlphabet(settings.yields[gathering], true)) do
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip(string.format("Set a sound alert for %s when it enters the yields list.", yield), settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
local shortName = settings.yields[gathering][yield].short;
local adjItemName = shortName or yield;
if imgui.Button("Play") then end
if imgui.IsItemClicked() then
local soundFile = imgui.GetVarValue(uiVariables[string.format("var_%s_%s_soundFile", gathering, yield)][1]);
if soundFile ~= "" then
ashita.misc.play_sound(string.format(_addon.path.."sounds\\%s", soundFile));
end
end
imgui.SameLine();
imgui.PushItemWidth(state.window.widthWidgetDefault - 45);
if imgui.Combo(adjItemName, uiVariables[string.format("var_%s_%s_soundIndex", gathering, yield)][1], getSoundOptions()) then
local soundIndex = imgui.GetVarValue(uiVariables[string.format("var_%s_%s_soundIndex", gathering, yield)][1]);
local soundFile = sounds[soundIndex];
imgui.SetVarValue(uiVariables[string.format("var_%s_%s_soundFile", gathering, yield)][1], "");
imgui.SetVarValue(uiVariables[string.format("var_%s_%s_soundFile", gathering, yield)][1], soundFile);
end
imgui.PopItemWidth();
end
imgui.EndChild();
end
end
----------------------------------------------------------------------------------------------------
-- func: renderSettingsReports
-- desc: Renders the Reports section in settings.
----------------------------------------------------------------------------------------------------
function renderSettingsReports()
local gathering = state.settings.reports.gathering;
local sortedReports = table.sortReportsByDate(reports[gathering], true);
imgui.PushStyleVar(ImGuiStyleVar_WindowPadding, 5, 5);
if imgui.BeginChild("Reports", -1, state.window.heightSettingsContent, imgui.GetVarValue(uiVariables['var_WindowVisible'][1]), imgui.bor(ImGuiWindowFlags_MenuBar, ImGuiWindowFlags_NoResize)) then
imgui.SetWindowFontScale(state.window.scale);
if imgui.BeginMenuBar() then
local btnAction = function(data)
state.settings.reports.gathering = data.name;
imgui.SetVarValue(uiVariables['var_ReportSelected'][1], nil);
state.values.currentReportName = nil;
end
for _, data in ipairs(gatherTypes) do
if state.values.btnTextureFailure or not settings.general.useImageButtons then
imguiPushActiveBtnColor(data.name == gathering);
if imgui.SmallButton(string.upperfirst(data.short)) then
btnAction(data);
end
else
local texture = textures[data.name];
imguiPushActiveBtnColor(data.name == gathering);
local textureSize = state.window.sizeGatherTexture;
if imgui.ImageButton(texture:Get(), textureSize, textureSize) then
btnAction(data);
end
end
imgui.PopStyleColor();
if imgui.IsItemHovered() then
imgui.SetTooltip(string.upperfirst(data.name));
end
imgui.SameLine(0.0, state.window.spaceGatherBtn);
end
-- Generate
local spaceColorDefaults = state.window.spaceColorDefaults;
if settings.general.showToolTips then
spaceColorDefaults = spaceColorDefaults - ( imgui.GetFontSize() * 24 / defaultFontSize );
if state.window.scale > 1.0 then spaceColorDefaults = spaceColorDefaults + 2 end;
end
imgui.SameLine(0.0, spaceColorDefaults);
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip(string.format("Manually generate a %s report using its current yield data.", string.upperfirst(gathering)), settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
local disabled = imguiPushDisabled(state.values.genReportDisabled);
if imgui.SmallButton("Generate") then
if not disabled then
state.values.currentReportName = nil
if generateGatheringReport(gathering) then
state.values.genReportDisabled = true;
ashita.timer.once(2, function()
state.values.genReportDisabled = false;
end);
end
end
end
imguiPopDisabled(disabled);
-- /Generate
imgui.EndMenuBar();
end
imgui.SetCursorPosX(0);
imgui.PushStyleColor(ImGuiCol_Border, 0, 0, 0, 0);
if imgui.BeginChild("Report List", imgui.GetWindowWidth(), state.window.heightYields-25, true) then
imgui.SetWindowFontScale(state.window.scale);
imgui.PushTextWrapPos(imgui.GetContentRegionAvailWidth());
if table.count(reports[gathering]) > 0 then
for _, file in ipairs(sortedReports, true) do
local name = file
if _ == 1 and #reports[gathering] > 1 then
imgui.PushStyleColor(ImGuiCol_Text, 1, 1, 0.54, 1); -- warn
name = file.." --latest"
else
imgui.PushStyleColor(ImGuiCol_Text, 0.77, 0.83, 0.80, 1); -- plain
end
if imgui.Selectable(name, imgui.GetVarValue(uiVariables["var_ReportSelected"][1]) == _, ImGuiSelectableFlags_AllowDoubleClick) then
imgui.SetVarValue(uiVariables['var_ReportSelected'][1], _);
state.values.readReportDisabled = false;
if (imgui.IsMouseDoubleClicked(0)) then
state.values.currentReportName = sortedReports[imgui.GetVarValue(uiVariables["var_ReportSelected"][1])];
end
end
imgui.PopStyleColor();
end
else
if getPlayerName() ~= "" then
imgui.Text("No reports..")
else
imgui.TextColored(1, 0.615, 0.615, 1, string.format("Unable to manage reports with no character loaded."));
end
end
imgui.EndChild()
end
imgui.PopStyleColor();
imgui.Separator();
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip("Read the selected report within the view window below (or double-click on the file name to perform this action).", settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
local disabled = imguiPushDisabled(imgui.GetVarValue(uiVariables["var_ReportSelected"][1]) == 0);
if imgui.Button("Read") then -- here
local selectedIndex = imgui.GetVarValue(uiVariables["var_ReportSelected"][1]);
local fname = sortedReports[selectedIndex];
if state.values.currentReportName ~= fname then
state.values.currentReportName = fname;
end
end
imguiPopDisabled(disabled);
imgui.SameLine(0.0, state.window.spaceSettingsBtn * 2);
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip("Clear the selection window above and the report view window below.", settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
disabled = imguiPushDisabled(imgui.GetVarValue(uiVariables["var_ReportSelected"][1]) == 0);
if imgui.Button("Clear") then
state.values.currentReportName = nil;
imgui.SetVarValue(uiVariables["var_ReportSelected"][1], nil);
end
imguiPopDisabled(disabled);
imgui.SameLine(0.0, state.window.spaceSettingsBtn * 2);
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip("Adjust the font scale of the report.", settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
imgui.PushItemWidth(state.window.widthReportScale);
imgui.SliderFloat("", uiVariables['var_ReportFontScale'][1], 1.0, 1.5, "%.2f")
imgui.PopItemWidth();
imgui.SameLine();
local spaceReportsDeleteMap = {[0] = state.window.spaceReportsDelete, [1] = state.window.spaceReportsDelete - 5, [2] = state.window.spaceReportsDelete - 8};
local spaceReportsDelete = spaceReportsDeleteMap[settings.general.windowScaleIndex]
if settings.general.showToolTips then
spaceReportsDelete = spaceReportsDelete - ( (imgui.GetFontSize() * 24) * 4 / defaultFontSize );
if state.window.scale > 1.0 then spaceReportsDelete = spaceReportsDelete + 2 end;
end
imgui.SameLine(0.0, spaceReportsDelete);
local disabled = imguiPushDisabled(imgui.GetVarValue(uiVariables["var_ReportSelected"][1]) == 0);
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip("Delete the selected report entry.", settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
if imgui.Button("Delete") then
if not disabled and getPlayerName() ~= "" then
local selectedIndex = imgui.GetVarValue(uiVariables["var_ReportSelected"][1]);
local fname = sortedReports[selectedIndex];
if fname ~= nil then
local fpath = string.format('%s/%s/%s/%s/%s', _addon.path, 'reports', getPlayerName(), gathering, fname);
os.remove(fpath);
for _, fileName in ipairs(reports[gathering]) do
if fileName == fname then
table.remove(reports[gathering], _)
end
end
state.values.currentReportName = nil;
imgui.SetVarValue(uiVariables["var_ReportSelected"][1], #sortedReports-1);
end
end
end
imguiPopDisabled(disabled);
imgui.Separator();
imgui.SetCursorPosX(0);
imgui.PushStyleColor(ImGuiCol_Border, 0, 0, 0, 0);
if imgui.BeginChild("Read Report", imgui.GetWindowWidth(), -1, true) then
imgui.SetWindowFontScale(imgui.GetVarValue(uiVariables['var_ReportFontScale'][1]));
imgui.PushTextWrapPos(imgui.GetContentRegionAvailWidth());
local fname = state.values.currentReportName;
if fname ~= nil then
if getPlayerName() ~= "" then
local fpath = string.format('%s/%s/%s/%s/%s', _addon.path, 'reports', getPlayerName(), gathering, fname);
local lines = linesFrom(fpath);
if table.count(lines) > 0 then
for _, line in pairs(lines) do
imgui.TextUnformatted(line);
end
else
imgui.TextColored(1, 0.615, 0.615, 1, string.format("File (%s) is unable to be read. Either this file has been moved, deleted, or you have changed characters. Reload yield to update this list.", state.values.currentReportName))
end
else
imgui.TextColored(1, 0.615, 0.615, 1, string.format("Unable to manage reports with no character loaded."));
end
elseif getPlayerName() == "" then
imgui.TextColored(1, 0.615, 0.615, 1, string.format("Unable to manage reports with no character loaded."));
end
imgui.EndChild()
end
imgui.PopStyleColor();
imgui.EndChild();
end
imgui.PopStyleVar();
end
----------------------------------------------------------------------------------------------------
-- func: renderSettingsFeedback
-- desc: Renders the Reports section in settings.
----------------------------------------------------------------------------------------------------
function renderSettingsFeedback()
if imgui.BeginChild("Set Alerts", -1, state.window.heightSettingsContent, true) then
imgui.SetWindowFontScale(state.window.scale);
local hasTitle = imgui.GetVarValue(uiVariables["var_IssueTitle"][1]):len() > 0;
local hasBody = imgui.GetVarValue(uiVariables["var_IssueBody"][1]):len() > 0;
local msg = "I hope you are enjoying Yield!"
local widget = imgui.Text
local r, g, b, a = 0.77, 0.83, 0.80, 1 -- plain
if not hasTitle and state.values.feedbackMissing then
msg = "Please enter a title. "
widget = imgui.BulletText;
r, g, b, a = 1, 0.615, 0.615, 1 -- danger
elseif not hasBody and state.values.feedbackMissing then
msg = "Please enter some feedback. "
widget = imgui.BulletText;
r, g, b, a = 1, 0.615, 0.615, 1 -- danger
end
local fontWidth = (msg:len()*imgui.GetFontSize()/2) / 1.75
imgui.SetCursorPosX(imgui.GetContentRegionAvailWidth()/2 - fontWidth);
imgui.PushStyleColor(ImGuiCol_Text, r, g, b, a);
widget(msg);
imgui.PopStyleColor();
imguiFullSep();
imgui.PushTextWrapPos(imgui.GetContentRegionAvailWidth());
imgui.SetCursorPosX(imgui.GetContentRegionAvailWidth()/16);
imgui.Text("If you have discovered a problem or want to provide some feedback you can do so here anonymously!")
local widgetWidth = state.window.widthWidgetDefault+75
local centerWidget = imgui.GetWindowContentRegionWidth()/2 - widgetWidth/2
if settings.general.showToolTips then centerWidget = centerWidget - ( imgui.GetFontSize() * 24 / defaultFontSize ); end
imgui.SetCursorPosY(imgui.GetWindowHeight() / 5);
imgui.SetCursorPosX(centerWidget);
imgui.AlignFirstTextHeightToWidgets();
imgui.PushItemWidth(widgetWidth);
if imguiShowToolTip("Enter a title for your feedback/issue submission.", settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
imgui.InputText('Title', uiVariables['var_IssueTitle'][1], 128, imgui.bor(ImGuiInputTextFlags_EnterReturnsTrue));
imgui.Spacing();
imgui.SetCursorPosX(centerWidget);
imgui.AlignFirstTextHeightToWidgets();
if imguiShowToolTip("Enter your feedback/issue.", settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
imgui.InputTextMultiline('Body', uiVariables['var_IssueBody'][1], 16384, state.window.widthWidgetDefault+75, imgui.GetTextLineHeight() * 16, imgui.bor(ImGuiInputTextFlags_AllowTabInput, ImGuiInputTextFlags_EnterReturnsTrue));
imgui.PopItemWidth();
imgui.Spacing();
local widgetPos = state.window.widthWidgetDefault+75
local centerWidget = imgui.GetWindowContentRegionWidth()/2 - widgetPos/2
imgui.SetCursorPosX(centerWidget);
if not state.values.feedbackSubmitted then
if imgui.Button("Submit") then
if not hasBody or not hasTitle then
state.values.feedbackMissing = true;
else
state.values.feedbackSubmitted = true;
state.values.feedbackMissing = false;
local title = imgui.GetVarValue(uiVariables["var_IssueTitle"][1]);
local body = imgui.GetVarValue(uiVariables["var_IssueBody"][1]);
sendIssue(title, body);
imgui.SetVarValue(uiVariables["var_IssueTitle"][1], "");
imgui.SetVarValue(uiVariables["var_IssueBody"][1], "")
end
end
end
if state.values.feedbackSubmitted then
imgui.SetCursorPosX(centerWidget);
imgui.PushStyleColor(ImGuiCol_Text, 0.39, 0.96, 0.13, 1); -- success
imgui.Text("Thank you for your feedback!");
imgui.PopStyleColor();
imgui.SameLine();
imgui.PushStyleColor(ImGuiCol_Text, 1, 0.615, 0.615, 1); -- danger
imgui.Text("<3");
imgui.PopStyleColor();
end
imgui.PushTextWrapPos(imgui.GetWindowContentRegionWidth());
imgui.SetCursorPosY(imgui.GetWindowHeight()-imgui.GetTextLineHeight()*2);
if imguiShowToolTip("All submissions are anonymous and will go to LoTekkie's github issue tracker for Ashita-Yield.", settings.general.showToolTips) then
imgui.SameLine(0.0, state.window.spaceToolTip);
end
imgui.Text("* To: https://github.com/LoTekkie/Ashita-Yield/issues.");
imgui.PopTextWrapPos();
imgui.EndChild();
end
end
----------------------------------------------------------------------------------------------------
-- func: renderSettingsAbout
-- desc: Renders the About section in settings.
---------------------------------------------------------------------------------------------------
function renderSettingsAbout()
if imgui.BeginChild("About", -1, state.window.heightSettingsContent, true) then
imgui.SetWindowFontScale(state.window.scale);
imgui.Spacing();
imgui.PushTextWrapPos(imgui.GetContentRegionAvailWidth());
imgui.TextColored(1, 1, 0.54, 1, "Name:"); imgui.Text(string.format("%s by Lotekkie & Narpt", _addon.name));
imgui.Spacing();
imgui.TextColored(1, 1, 0.54, 1, "Description:"); imgui.Text(_addon.description); imgui.Text("https://github.com/LoTekkie/Ashita-Yield");
imgui.Spacing();
imgui.TextColored(1, 1, 0.54, 1, "Author:"); imgui.Text(_addon.author);
imgui.Spacing();
imgui.TextColored(1, 1, 0.54, 1, "Version:"); imgui.Text(_addon.version);
imgui.Spacing();
imgui.TextColored(1, 1, 0.54, 1, "Support/Donate:"); imgui.Text("https://Paypal.me/Sjshovan\nOR\nFor Gil donations: I play on Wings private server! (https://www.wingsxi.com/wings/) My in-game name is LoTekkie.");
imgui.Spacing();
imgui.PushStyleColor(ImGuiCol_Button, 0.21, 0.47, 0.59, 1); -- info
if imgui.Button("Go to Paypal") then
ashita.misc.open_url("https://Paypal.me/Sjshovan");
end
imgui.PopStyleColor();
imguiFullSep();
imgui.TextColored(1, 1, 0.54, 1, "Special Thanks:");
imguiFullSep();
imgui.Text("To Narpt (https://www.twitch.tv/narpt): For his awesome streams, invaluable feedback/ideas/testing, and the inspiration to make this!");
imgui.Spacing();
imgui.Text("To Hughesyourdaddy (https://www.omega-ffxi.com): For granting me the freedom to create this within the FFXI Omega private server.");
imgui.Spacing();
imgui.Text("To the Ashita team (https://www.ashitaxi.com/): For making this possible.");
imgui.Spacing();
imgui.Text("To Ashita Discord members (https://discord.gg/3FbepVGh): For their feedback and knowledge.");
imgui.Spacing();
imgui.Text("To everyone who reported bugs and submitted feedback, thanks for helping make Yield great!");
imgui.EndChild();
end
end
----------------------------------------------------------------------------------------------------
-- func: renderHelpGeneral
-- desc: Renders the general help section with the help window.
---------------------------------------------------------------------------------------------------
function renderHelpGeneral()
if imgui.BeginChild("HelpGeneral", -1, state.window.heightSettingsContent, true) then
imgui.SetWindowFontScale(state.window.scale);
imgui.Spacing();
imgui.PushTextWrapPos(imgui.GetContentRegionAvailWidth());
if state.firstLoad then
imgui.TextColored(1, 1, 0.54, 1, "Welcome to Yield!"); imgui.Separator();
imgui.Text("Before you begin, please take a moment to read through the following general information and common questions to familiarize yourself.");
imgui.Text("If you would like to read this later you can open this window anytime by click the 'Help' button located at the bottom of the app.");
imguiHalfSep(true);
end
imgui.TextColored(1, 1, 0.54, 1, "Navigating Yield"); imgui.Separator();
imgui.Text("Your main tool for navigating Yield is the mouse. You will find that if you take your time and hover over the (?) tooltips as well as other items within the interface that Yield will give you an explanation of each item, don't be afraid to take the time to explore!");
imgui.Text("The real power of Yield comes from within its Settings window. There are a variety of features and customization options there to accommodate almost every gatherer's need.");
imguiHalfSep(true);
imgui.TextColored(1, 1, 0.54, 1, "Gathering"); imgui.Separator();
imgui.Text("Yield supports every gathering type in the game and switching between them here is a breeze, simply click on the icons at the top of the Main window. If you hover your mouse over them, Yield will tell you which gathering type you are switching to. If you start gathering and forget to switch, don't worry, Yield will automatically switch to the correct type and begin working to keep track of your stats!");
imgui.Spacing();
imgui.Text("Don't forget to set those prices! Before heading out to begin gathering, it is recommended that you set your prices for yields within the Settings/Set Prices window. If you forget for some reason, that's ok, you can always update the prices later and recalculate your Estimated Value from within the same window.");
imgui.Spacing();
imgui.Text("Yield is intelligent and will begin tracking and recording for you without the need for you to do anything first. After you load Yield, simply start gathering and watch the magic happen!");
imguiHalfSep(true);
imgui.TextColored(1, 1, 0.54, 1, "Settings"); imgui.Separator();
imgui.Text("Yield automatically saves all the changes you make in your Settings window each time the Settings window is closed. You do not need to worry about reloading and losing your Prices/Colors/Alerts or any of your current metrics. When you exit the game and come back, everything will be right where you left it.");
imguiHalfSep(true);
imgui.TextColored(1, 1, 0.54, 1, "Alerts"); imgui.Separator();
imgui.Text("Yield ships with a variety of sounds, used for alerts, out of the box. If you find yourself wanting to add custom sounds, it couldn't be easier. All sounds used for Yield alerts can be found within the /sounds folder. To add a new sound, ensure the sound file is in .wav format (e.g. my_new_sound.wav), and drop it into /sounds. After that, reload Yield and your new sound should be available in all sound selection drop-downs.")
imguiHalfSep(true);
imgui.TextColored(1, 1, 0.54, 1, "Reports"); imgui.Separator();
imgui.Text("Yield allows you to generate detailed reports using the metrics it has tracked while you gathered. You do not need to use Yield to manage these files but these reports can be read and deleted from within the Settings/Reports window. These files are stored locally with the /reports folder of the Yield addon. It is safe to remove these files even while Yield is loaded. Yield will inform you that the files no longer exist if you attempt to read them.");
imgui.Text("Generation of reports can occur both manually and automatically. If you enable automatic generation of reports Yield will generate a report both when you zone and when you reset the data for a particular gathering type.");
imgui.Text("While automatic report generation can happen when you zone, it won't always happen when you zone. Yield will attempt to determine when it should generate on zone change based on your activity.");
imguiHalfSep(true);
imgui.TextColored(1, 1, 0.54, 1, "Tips/Tricks"); imgui.Separator();
imgui.Text("1. Double-click on the title bar of any window to minimize it.");
imgui.Text("2. left-click or right-click on your plots within the Main window to cycle the display of their labels.");
imgui.Text("3. left-click or right-click on the yields list within the Main window to cycle the sorting methods of the list.");
imgui.Text("4. If you forget to shut off your timer when you walk away from Yield, it will automatically shut them off for you after approx. 5 minutes.");
imgui.Text("5. You can Double-click on a file name in Reports to view its contents rather than using the Read button.");
imgui.Text("6. You can left-click drag on the R: G: B: A: color boxes with your mouse to change their values quickly. You can also left-click on the main color box to change color input methods.");
imgui.Text("7. You can view the moon percentage by switching to the digging gathering type.");
imguiHalfSep(true);
imgui.TextColored(1, 1, 0.54, 1, "Bugs/Errors"); imgui.Separator();
imgui.Text("Unfortunately, nothing is perfect, not even Yield. You may come across a problem while using Yield to help you become the ultimate gatherer. I understand the frustration of these occurrences and that is why I added an easy in-app way to report these problems directly to me so I can quickly get the issues resolved.");
imgui.Text("To report an issue directly to me, simply head on over to Settings/Feedback. Enter a title, an explanation, and hit submit.");
imgui.Text("By taking a mere moment to send a report, you are effectively taking part in the active development of Yield and helping it become even better. This time you take to do so is greatly appreciated!");
imguiHalfSep(true);
imgui.TextColored(1, 1, 0.54, 1, "Text Commands"); imgui.Separator();
imgui.Text("Yield has a few text commands to quickly load/reload/unload. To see a full list of the available commands type: '/yield help' in your chat while Yield is loaded.");
imgui.Spacing();
imgui.EndChild();
end
end
----------------------------------------------------------------------------------------------------
-- func: renderHelpQsAndAs
-- desc: Renders the Q's and A's section with the help window.
---------------------------------------------------------------------------------------------------
function renderHelpQsAndAs()
if imgui.BeginChild("HelpQnA", -1, state.window.heightSettingsContent, true) then
imgui.SetWindowFontScale(state.window.scale);
imgui.Spacing();
imgui.PushTextWrapPos(imgui.GetContentRegionAvailWidth());
imgui.Separator();
imgui.TextColored(1, 1, 0.54, 1, "Q: Is this addon available for Windower?"); imgui.Separator();
imgui.Text("A: Unfortunately, No. Windower does not currently offer the technology used to create this addon. If they ever do, I will absolutely port it over. ")
imguiFullSep();
imgui.TextColored(1, 1, 0.54, 1, "Q: Why isn't feature X/Y/Z implemented?"); imgui.Separator();
imgui.Text("A: I'm positive many of you out there have some amazing ideas on how to make Yield better. I'd love to hear them! You can contact me through Feedback in Settings, by email ([email protected]), or on discord (LoTekkie #6070).")
imguiFullSep();
imgui.TextColored(1, 1, 0.54, 1, "Q: How can I donate/support?"); imgui.Separator();
imgui.Text("A: Head on over to the About section in Settings. There you can see some ways that I am able to receive your support. Thank you!");
imguiFullSep();
imgui.TextColored(1, 1, 0.54, 1, "Q: I have upgraded from a previous version now everything went bonkers! What do I do?"); imgui.Separator();
imgui.Text("A: If you reach a scenario where Yield wont display correctly or is acting strange, first try reloading the addon. If you are still experiencing issues try the following steps:")
imgui.Text("1. Exit out of Final Fantasy 11.");
imgui.Text("2. Navigate to the Yield addon and delete your settings/ folder.");
imgui.Text("3. Start Final Fantasy 11 and load Yield.")
imgui.Text("If you are still experiencing issues, reach out to me and I will attempt to solve them.");
imguiFullSep();
imgui.TextColored(1, 1, 0.54, 1, "Q: I cannot find the Yield window! What do I do?"); imgui.Separator();
imgui.Text("A: Type /yield find or /yld f in your chat bar. This will force the Yield window to return to the top left of your screen.");
imguiFullSep();
imgui.TextColored(1, 1, 0.54, 1, "Q: Which server do you play on?"); imgui.Separator();
imgui.Text("A: I am currently playing on Wings private server (https://www.wingsxi.com/). My in-game name is LoTekkie. Hope to see you around!");
imguiFullSep();
imgui.TextColored(1, 1, 0.54, 1, "Q: Have you created any other FFXI addons?"); imgui.Separator();
imgui.Text("A: Yes, I have also authored Mount Muzzle(Windower+Ashita) and Battle Stations(Windower). You can obtain these through their respective launchers.");
imguiFullSep();
imgui.TextColored(1, 1, 0.54, 1, "Q: I have a question that I don't see here. How do I contact you?"); imgui.Separator();
imgui.Text("A: You can contact me through Feedback in Settings, by email ([email protected]), or on discord (LoTekkie #6070).");
imgui.Spacing();
imgui.EndChild();
end
end