summaryrefslogtreecommitdiff
path: root/Data/BuiltIn/Libraries/lua-addons/addons/pointwatch/pointwatch.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Data/BuiltIn/Libraries/lua-addons/addons/pointwatch/pointwatch.lua')
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/pointwatch/pointwatch.lua328
1 files changed, 328 insertions, 0 deletions
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/pointwatch/pointwatch.lua b/Data/BuiltIn/Libraries/lua-addons/addons/pointwatch/pointwatch.lua
new file mode 100644
index 0000000..8eac64d
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/pointwatch/pointwatch.lua
@@ -0,0 +1,328 @@
+--Copyright (c) 2014, Byrthnoth
+--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 <addon name> 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 <your name> 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.
+
+texts = require 'texts'
+config = require 'config'
+require 'sets'
+res = require 'resources'
+require 'statics'
+messages = require 'message_ids'
+require 'pack'
+
+_addon.name = 'PointWatch'
+_addon.author = 'Byrth'
+_addon.version = 0.150811
+_addon.command = 'pw'
+
+settings = config.load('data\\settings.xml',default_settings)
+config.register(settings,initialize)
+
+box = texts.new('${current_string}',settings.text_box_settings,settings)
+box.current_string = ''
+box:show()
+
+initialize()
+
+windower.register_event('incoming chunk',function(id,org,modi,is_injected,is_blocked)
+ if is_injected then return end
+ if id == 0x29 then -- Action Message, used in Abyssea for xp
+ local val = org:unpack('I',0xD)
+ local msg = org:unpack('H',0x19)%1024
+ exp_msg(val,msg)
+ elseif id == 0x2A then -- Resting message
+ local zone = 'z'..windower.ffxi.get_info().zone
+ if settings.options.message_printing then
+ print('Message ID: '..(org:unpack('H',0x1B)%2^14))
+ end
+
+ if messages[zone] then
+ local msg = org:unpack('H',0x1B)%2^14
+ for i,v in pairs(messages[zone]) do
+ if tonumber(v) and v + messages[zone].offset == msg then
+ local param_1 = org:unpack('I',0x9)
+ local param_2 = org:unpack('I',0xD)
+ local param_3 = org:unpack('I',0x11)
+ local param_4 = org:unpack('I',0x15)
+ -- print(param_1,param_2,param_3,param_4) -- DEBUGGING STATEMENT -------------------------
+ if zone_message_functions[i] then
+ zone_message_functions[i](param_1,param_2,param_3,param_4)
+ end
+ if i:contains("visitant_status_") then
+ abyssea.update_time = os.clock()
+ end
+ end
+ end
+ end
+ elseif id == 0x2D then
+ local val = org:unpack('I',0x11)
+ local msg = org:unpack('H',0x19)%1024
+ exp_msg(val,msg)
+ elseif id == 0x55 then
+ if org:byte(0x85) == 3 then
+ local dyna_KIs = math.floor((org:byte(6)%64)/2) -- 5 bits (32, 16, 8, 4, and 2 originally -> shifted to 16, 8, 4, 2, and 1)
+ dynamis._KIs = {
+ ['Crimson'] = dyna_KIs%2 == 1,
+ ['Azure'] = math.floor(dyna_KIs/2)%2 == 1,
+ ['Amber'] = math.floor(dyna_KIs/4)%2 == 1,
+ ['Alabaster'] = math.floor(dyna_KIs/8)%2 == 1,
+ ['Obsidian'] = math.floor(dyna_KIs/16) == 1,
+ }
+ if dynamis_map[dynamis.zone] then
+ dynamis.time_limit = 3600
+ for KI,TE in pairs(dynamis_map[dynamis.zone]) do
+ if dynamis._KIs[KI] then
+ dynamis.time_limit = dynamis.time_limit + TE*60
+ end
+ end
+ update_box()
+ end
+ end
+ elseif id == 0x61 then
+ xp.current = org:unpack('H',0x11)
+ xp.tnl = org:unpack('H',0x13)
+ accolades.current = math.floor(org:byte(0x5A)/4) + org:byte(0x5B)*2^6 + org:byte(0x5C)*2^14
+ elseif id == 0x63 and org:byte(5) == 2 then
+ lp.current = org:unpack('H',9)
+ lp.number_of_merits = org:byte(11)%128
+ lp.maximum_merits = org:byte(0x0D)%128
+ elseif id == 0x63 and org:byte(5) == 5 then
+ local offset = windower.ffxi.get_player().main_job_id*6+13 -- So WAR (ID==1) starts at byte 19
+ cp.current = org:unpack('H',offset)
+ cp.number_of_job_points = org:unpack('H',offset+2)
+ elseif id == 0x110 then
+ sparks.current = org:unpack('I',5)
+ elseif id == 0xB and box:visible() then
+ zoning_bool = true
+ box:hide()
+ elseif id == 0xA and zoning_bool then
+ zoning_bool = nil
+ box:show()
+ end
+end)
+
+windower.register_event('zone change',function(new,old)
+ if res.zones[new].english:sub(1,7) == 'Dynamis' then
+ dynamis.entry_time = os.clock()
+ abyssea.update_time = 0
+ abyssea.time_remaining = 0
+ dynamis.time_limit = 3600
+ dynamis.zone = new
+ cur_func,loadstring_err = loadstring("current_string = "..settings.strings.dynamis)
+ elseif res.zones[new].english:sub(1,7) == 'Abyssea' then
+ abyssea.update_time = os.clock()
+ abyssea.time_remaining = 5
+ dynamis.entry_time = 0
+ dynamis.time_limit = 0
+ dynamis.zone = 0
+ cur_func,loadstring_err = loadstring("current_string = "..settings.strings.abyssea)
+ else
+ abyssea.update_time = 0
+ abyssea.time_remaining = 0
+ dynamis.entry_time = 0
+ dynamis.time_limit = 0
+ dynamis.zone = 0
+ cur_func,loadstring_err = loadstring("current_string = "..settings.strings.default)
+ end
+ if not cur_func or loadstring_err then
+ cur_func = loadstring("current_string = ''")
+ error(loadstring_err)
+ end
+end)
+
+windower.register_event('addon command',function(...)
+ local commands = {...}
+ local first_cmd = table.remove(commands,1):lower()
+ if approved_commands[first_cmd] and #commands >= approved_commands[first_cmd].n then
+ local tab = {}
+ for i,v in ipairs(commands) do
+ tab[i] = tonumber(v) or v
+ if i <= approved_commands[first_cmd].n and type(tab[i]) ~= approved_commands[first_cmd].t then
+ print('Pointwatch: texts library command ('..first_cmd..') requires '..approved_commands[first_cmd].n..' '..approved_commands[first_cmd].t..'-type input'..(approved_commands[first_cmd].n > 1 and 's' or ''))
+ return
+ end
+ end
+ texts[first_cmd](box,unpack(tab))
+ settings.text_box_settings = box._settings
+ config.save(settings)
+ elseif first_cmd == 'reload' then
+ windower.send_command('lua r pointwatch')
+ elseif first_cmd == 'unload' then
+ windower.send_command('lua u pointwatch')
+ elseif first_cmd == 'reset' then
+ initialize()
+ elseif first_cmd == 'message_printing' then
+ settings.options.message_printing = not settings.options.message_printing
+ print('Pointwatch: Message printing is '..tostring(settings.options.message_printing)..'.')
+ elseif first_cmd == 'eval' then
+ assert(loadstring(table.concat(commands, ' ')))()
+ end
+end)
+
+windower.register_event('prerender',function()
+ if frame_count%30 == 0 and box:visible() then
+ update_box()
+ end
+ frame_count = frame_count + 1
+end)
+
+function update_box()
+ if not windower.ffxi.get_info().logged_in or not windower.ffxi.get_player() then
+ box.current_string = ''
+ return
+ end
+ cp.rate = analyze_points_table(cp.registry)
+ xp.rate = analyze_points_table(xp.registry)
+ if dynamis.entry_time ~= 0 and dynamis.entry_time+dynamis.time_limit-os.clock() > 0 then
+ dynamis.time_remaining = os.date('!%H:%M:%S',dynamis.entry_time+dynamis.time_limit-os.clock())
+ dynamis.KIs = X_or_O(dynamis._KIs.Crimson)..X_or_O(dynamis._KIs.Azure)..X_or_O(dynamis._KIs.Amber)..X_or_O(dynamis._KIs.Alabaster)..X_or_O(dynamis._KIs.Obsidian)
+ elseif abyssea.update_time ~= 0 then
+ local time_less_then = math.floor((os.clock() - abyssea.update_time)/60)
+ abyssea.time_remaining = abyssea.time_remaining-time_less_then
+ if time_less_then >= 1 then
+ abyssea.update_time = os.clock()
+ end
+ else
+ dynamis.time_remaining = 0
+ dynamis.KIs = ''
+ end
+ assert(cur_func)()
+
+ if box.current_string ~= current_string then
+ box.current_string = current_string
+ end
+end
+
+function X_or_O(bool)
+ if bool then return 'O' else return 'X' end
+end
+
+function analyze_points_table(tab)
+ local t = os.clock()
+ local running_total = 0
+ local maximum_timestamp = 29
+ for ts,points in pairs(tab) do
+ local time_diff = t - ts
+ if t - ts > 600 then
+ tab[ts] = nil
+ else
+ running_total = running_total + points
+ if time_diff > maximum_timestamp then
+ maximum_timestamp = time_diff
+ end
+ end
+ end
+
+ local rate
+ if maximum_timestamp == 29 then
+ rate = 0
+ else
+ rate = math.floor((running_total/maximum_timestamp)*3600)
+ end
+
+ return rate
+end
+
+zone_message_functions = {
+ amber_light = function(p1,p2,p3,p4)
+ abyssea.amber = math.min(abyssea.amber + 8,255)
+ end,
+ azure_light = function(p1,p2,p3,p4)
+ abyssea.azure = math.min(abyssea.azure + 8,255)
+ end,
+ ruby_light = function(p1,p2,p3,p4)
+ abyssea.ruby = math.min(abyssea.ruby + 8,255)
+ end,
+ pearlescent_light = function(p1,p2,p3,p4)
+ abyssea.pearlescent = math.min(abyssea.pearlescent + 5,230)
+ end,
+ ebon_light = function(p1,p2,p3,p4)
+ abyssea.ebon = math.min(abyssea.ebon + p1+1,200) -- NM kill = 1, faint = 1, mild = 2, strong = 3
+ end,
+ silvery_light = function(p1,p2,p3,p4)
+ abyssea.silvery = math.min(abyssea.silvery + 5*(p1+1),200) -- faint = 5, mild = 10, strong = 15
+ end,
+ golden_light = function(p1,p2,p3,p4)
+ abyssea.golden = math.min(abyssea.golden + 5*(p1+1),200) -- faint = 5, mild = 10, strong = 15
+ end,
+ pearl_ebon_gold_silvery = function(p1,p2,p3,p4)
+ abyssea.pearlescent = p1
+ abyssea.ebon = p2
+ abyssea.golden = p3
+ abyssea.silvery = p4
+ end,
+ azure_ruby_amber = function(p1,p2,p3,p4)
+ abyssea.azure = p1
+ abyssea.ruby = p2
+ abyssea.amber = p3
+ end,
+ visitant_status_gain = function(p1,p2,p3,p4)
+ abyssea.time_remaining = p1
+ end,
+ visitant_status_update = function(p1,p2,p3,p4)
+ abyssea.time_remaining = p1
+ end,
+ visitant_status_wears_off = function(p1,p2,p3,p4)
+ abyssea.time_remaining = p1
+ end,
+ visitant_status_extend = function(p1,p2,p3,p4)
+ abyssea.time_remaining = abyssea.time_remaining + p1
+ end,
+}
+
+function exp_msg(val,msg)
+ local t = os.clock()
+ if msg == 718 or msg == 735 then
+ cp.registry[t] = (cp.registry[t] or 0) + val
+ cp.total = cp.total + val
+ cp.current = cp.current + val
+ if cp.current > cp.tnjp and cp.number_of_job_points ~= cp.maximum_job_points then
+ cp.number_of_job_points = math.min(cp.number_of_job_points + math.floor(cp.current/cp.tnjp),cp.maximum_job_points)
+ cp.current = cp.current%cp.tnjp
+ end
+ elseif msg == 8 or msg == 105 then
+ xp.registry[t] = (xp.registry[t] or 0) + val
+ xp.total = xp.total + val
+ xp.current = math.min(xp.current + val,55999)
+ -- 98 to 99 is 56000 XP, so 55999 is the most you can ever have
+ if xp.current > xp.tnl then
+ -- I have capped all jobs, but I assume that a 0x61 packet is sent after you
+ -- level up, which will update the TNL and make this adjustment meaningless.
+ xp.current = xp.current - xp.tnl
+ end
+ elseif msg == 371 or msg == 372 then
+ lp.registry[t] = (lp.registry[t] or 0) + val
+ lp.current = lp.current + val
+ if lp.current >= lp.tnm and lp.number_of_merits ~= lp.maximum_merits then
+ -- Merit Point gained!
+ lp.number_of_merits = math.min(lp.number_of_merits + math.floor(lp.current/lp.tnm),lp.maximum_merits)
+ lp.current = lp.current%lp.tnm
+ else
+ -- If a merit point was not gained,
+ lp.current = math.min(lp.current,lp.tnm-1)
+ end
+ end
+ update_box()
+end