diff options
Diffstat (limited to 'Data/DefaultContent/Libraries/addons/addons/GearSwap/refresh.lua')
-rw-r--r-- | Data/DefaultContent/Libraries/addons/addons/GearSwap/refresh.lua | 727 |
1 files changed, 727 insertions, 0 deletions
diff --git a/Data/DefaultContent/Libraries/addons/addons/GearSwap/refresh.lua b/Data/DefaultContent/Libraries/addons/addons/GearSwap/refresh.lua new file mode 100644 index 0000000..9fb5e95 --- /dev/null +++ b/Data/DefaultContent/Libraries/addons/addons/GearSwap/refresh.lua @@ -0,0 +1,727 @@ +--Copyright (c) 2013~2016, 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. + + +-- Deals with refreshing player information and loading user settings -- + + + +----------------------------------------------------------------------------------- +--Name: refresh_globals() +--Args: +---- None +----------------------------------------------------------------------------------- +--Returns: +---- None +---- Updates all global variables to reflect the player's status. Generally run +---- before calling a player function. +----------------------------------------------------------------------------------- +function refresh_globals(user_event_flag) + local current = os.clock() + local dt = current - last_refresh + if not user_event_flag or dt > 0.05 then + refresh_player(dt,user_event_flag) + refresh_ffxi_info(dt,user_event_flag) + refresh_group_info(dt,user_event_flag) + last_refresh = current + end +end + +----------------------------------------------------------------------------------- +--Name: load_user_files() +--Args: +---- None +----------------------------------------------------------------------------------- +--Returns: +---- user_env, a table of all of the player defined functions and their current +---- variables. +----------------------------------------------------------------------------------- +function load_user_files(job_id,user_file) + job_id = tonumber(job_id) + + if current_file then + user_pcall('file_unload',current_file) + end + + for i in pairs(registered_user_events) do + unregister_event_user(i) + end + + for i in pairs(__raw.text.registry) do + windower.text.delete(i) + end + + for i in pairs(__raw.prim.registry) do + windower.prim.delete(i) + end + + current_file = nil + sets = nil + user_env = nil + unhandled_command_events = {} + --registered_user_events = {} + include_user_path = nil + + language = 'english' -- Reset language to english when changing job files. + refresh_globals() + + if job_id and res.jobs[job_id] then + player.main_job_id = job_id + update_job_names() + end + + + local path,base_dir,filename + path,base_dir,filename = pathsearch({user_file}) + if not path then + local long_job = res.jobs[job_id].english + local short_job = res.jobs[job_id].english_short + local tab = {player.name..'_'..short_job..'.lua',player.name..'-'..short_job..'.lua', + player.name..'_'..long_job..'.lua',player.name..'-'..long_job..'.lua', + player.name..'.lua',short_job..'.lua',long_job..'.lua','default.lua'} + path,base_dir,filename = pathsearch(tab) + end + + if not path then + current_file = nil + sets = nil + return + end + + user_env = {gearswap = _G, _global = _global, _settings = _settings,_addon=_addon, + -- Player functions + equip = equip, cancel_spell=cancel_spell, change_target=change_target, cast_delay=cast_delay, + print_set=print_set,set_combine=set_combine,disable=disable,enable=user_enable, + send_command=send_cmd_user,windower=user_windower,include=include_user, + midaction=user_midaction,pet_midaction=user_pet_midaction,set_language=set_language, + show_swaps = show_swaps,debug_mode=debug_mode,include_path=user_include_path, + register_unhandled_command=user_unhandled_command,move_spell_target=move_spell_target, + language=language, + + -- Library functions + string=string,math=math,table=table,set=set,list=list,T=T,S=S,L=L,pack=pack,functions=functions, + os=os,texts=texts,bit=bit,type=type,tostring=tostring,tonumber=tonumber,pairs=pairs, + ipairs=ipairs, print=print, add_to_chat=add_to_chat_user,unpack=unpack,next=next, + select=select,lua_base_path=windower.addon_path,empty=empty,file=file, + loadstring=loadstring,assert=assert,error=error,pcall=pcall,io=io,dofile=dofile, + + debug=debug,coroutine=coroutine,setmetatable=setmetatable,getmetatable=getmetatable, + rawset=rawset,rawget=rawget,require=include_user, + _libs=_libs, + + -- Player environment things + buffactive=buffactive, + player=player, + world=world, + pet=pet, + fellow=fellow, + alliance=alliance, + party=alliance[1], + sets={naked = {main=empty,sub=empty,range=empty,ammo=empty, + head=empty,neck=empty,ear1=empty,ear2=empty, + body=empty,hands=empty,ring1=empty,ring2=empty, + back=empty,waist=empty,legs=empty,feet=empty}} + } + + user_env['_G'] = user_env + + -- Try to load data/<name>_<main job>.lua + local funct, err = loadfile(path) + + -- If the file cannot be loaded, print the error and load the default. + if funct == nil then + print('User file problem: '..err) + current_file = nil + sets = nil + return + else + current_file = filename + print('GearSwap: Loaded your '..current_file..' file!') + end + + setfenv(funct, user_env) + + -- Verify that funct contains functions. + local status, plugin = pcall(funct) + + if not status then + error('GearSwap: File failed to load: \n'..plugin) + sets = nil + return nil + end + + _global.pretarget_cast_delay = 0 + _global.precast_cast_delay = 0 + _global.cancel_spell = false + _global.current_event = 'get_sets' + user_pcall('get_sets') + + gearswap_disabled = false + sets = user_env.sets +end + + +----------------------------------------------------------------------------------- +--Name: refresh_player() +--Args: +---- None +----------------------------------------------------------------------------------- +--Returns: +---- None +---- +---- Loads player from windower.ffxi.get_player(). +---- Adds in a "job", "race", "equipment", "target", and "subtarget" field +---- Also updates "pet" and assigns isvalid and element fields. +---- Further converts player.buffs to buffactive. +-------- Indexes buffs by their buff name and assigns a value equal to the number +-------- of buffs with that name active. +----------------------------------------------------------------------------------- +function refresh_player(dt,user_event_flag) + local pl, player_mob_table + if not user_event_flag or dt > 0.5 then + pl = windower.ffxi.get_player() + if not pl or not pl.vitals then return end + + player_mob_table = windower.ffxi.get_mob_by_index(pl.index) + if not player_mob_table then return end + + table.reassign(player,pl) + for i,v in pairs(player.vitals) do + player[i]=v + end + update_job_names() + player.status_id = player.status + if res.statuses[player.status] then + player.status = res.statuses[player.status].english + else + print(player.status_id) + end + player.nation_id = player.nation + player.nation = res.regions[player.nation_id][language] or 'None' + + for i,v in pairs(player_mob_table) do + if i == 'name' then + player.mob_name = v + elseif i~= 'is_npc' and i~='tp' and i~='mpp' and i~='claim_id' and i~='status' then + player[i] = v + end + end + + if player_mob_table.race ~= nil then + player.race_id = player.race + player.race = res.races[player.race][language] + end + + -- If we have a pet, create or update the table info. + if player_mob_table and player_mob_table.pet_index then + local player_pet_table = windower.ffxi.get_mob_by_index(player_mob_table.pet_index) + if player_pet_table then + table.reassign(pet, target_complete(player_pet_table)) + pet.claim_id = nil + pet.is_npc = nil + pet.isvalid = true + if pet.tp then pet.tp = pet.tp/10 end + + if avatar_element[pet.name] then + pet.element = res.elements[avatar_element[pet.name]][language] + else + pet.element = res.elements[-1][language] -- Physical + end + else + table.reassign(pet, {isvalid=false}) + end + else + table.reassign(pet, {isvalid=false}) + end + + if player.main_job_id == 18 or player.sub_job_id == 18 then + local auto_tab + if player.main_job_id == 18 then auto_tab = windower.ffxi.get_mjob_data() + else auto_tab = windower.ffxi.get_sjob_data() end + + if auto_tab.name then + for i,v in pairs(auto_tab) do + if not T{'available_heads','attachments','available_frames','available_attachments','frame','head'}:contains(i) then + pet[i] = v + end + end + pet.available_heads = make_user_table() + pet.attachments = make_user_table() + pet.available_frames = make_user_table() + pet.available_attachments = make_user_table() + + -- available parts + for i,id in pairs(auto_tab.available_heads) do + if res.items[id] and type(res.items[id]) == 'table' then + pet.available_heads[res.items[id][language]] = true + end + end + for i,id in pairs(auto_tab.available_frames) do + if res.items[id] and type(res.items[id]) == 'table' then + pet.available_frames[res.items[id][language]] = true + end + end + for i,id in pairs(auto_tab.available_attachments) do + if res.items[id] and type(res.items[id]) == 'table' then + pet.available_attachments[res.items[id][language]] = true + end + end + + -- actual parts + pet.head = res.items[auto_tab.head][language] + pet.frame = res.items[auto_tab.frame][language] + for i,id in pairs(auto_tab.attachments) do + if res.items[id] and type(res.items[id]) == 'table' then + pet.attachments[res.items[id][language]] = true + end + end + + if pet.max_mp ~= 0 then + pet.mpp = math.floor(pet.mp/pet.max_mp*100) + else + pet.mpp = 0 + end + end + elseif player.main_job_id == 23 then + local species_id = windower.ffxi.get_mjob_data().species + -- Should add instincts when they become available + + if species_id then + player.species = {} + for i,v in pairs(res.monstrosity[species_id]) do + player.species[i] = v + end + player.species.name = player.species[language] + player.species.tp_moves = copy_entry(res.monstrosity[species_id].tp_moves) + for i,v in pairs(player.species.tp_moves) do + if v > player.main_job_level then + player.species.tp_moves[i] = nil + end + end + end + else + player.species = nil + end + end + + -- This being nil does not cause a return, but items should not really be changing when zoning. + local cur_equip = table.reassign({},items.equipment) + + -- Assign player.equipment to be the gear that has been sent out and the server currently thinks + -- you are wearing. (the sent_out_equip for loop above). + player.equipment = make_user_table() + table.reassign(player.equipment,to_names_set(cur_equip)) + + -- Assign player.inventory to be keyed to item.inventory[i][language] and to have a value of count, similar to buffactive + for i,bag in pairs(res.bags) do + local bag_name = to_windower_bag_api(bag.en) + if items[bag_name] then player[bag_name] = refresh_item_list(items[bag_name]) end + end + + -- Monster tables for the target and subtarget. + player.target = target_complete(windower.ffxi.get_mob_by_target('t')) + player.subtarget = target_complete(windower.ffxi.get_mob_by_target('st')) + player.last_subtarget = target_complete(windower.ffxi.get_mob_by_target('lastst')) + + + + table.reassign(fellow,target_complete(windower.ffxi.get_mob_by_target('<ft>'))) + if fellow.name then + fellow.isvalid = true + else + fellow.isvalid=false + end + + table.reassign(buffactive,convert_buff_list(player.buffs)) + + for global_variable_name,extradatatable in pairs(_ExtraData) do + if _G[global_variable_name] then + for sub_variable_name,value in pairs(extradatatable) do + _G[global_variable_name][sub_variable_name] = value + end + end + end +end + +----------------------------------------------------------------------------------- +--Name: refresh_ffxi_info() +--Args: +---- None +----------------------------------------------------------------------------------- +--Returns: +---- None +---- +---- Updates the global "world" with windower.ffxi.get_info (ignores the target field). +---- Also sets windower.ffxi.get_info()['zone'] to be world.area for consistency with spellcast +----------------------------------------------------------------------------------- +function refresh_ffxi_info(dt,user_event_flag) + local info = windower.ffxi.get_info() + for i,v in pairs(info) do + if i == 'zone' and res.zones[v] then + world.zone = res.zones[v][language] + world.area = world.zone + elseif i == 'weather' and res.weather[v] then + weather_update(v) + elseif i == 'day' and res.days[v] then + world.day = res.days[v][language] + world.day_element = res.elements[res.days[v].element][language] + elseif i == 'moon' then + world.moon_pct = v + elseif i == 'moon_phase' and res.moon_phases[v] then + world.moon = res.moon_phases[v][language] + elseif i ~= 'target' then + world[i] = v + end + end + + for global_variable_name,extradatatable in pairs(_ExtraData) do + if _G[global_variable_name] then + for sub_variable_name,value in pairs(extradatatable) do + _G[global_variable_name][sub_variable_name] = value + end + end + end +end + + +----------------------------------------------------------------------------------- +--Name: weather_update(id) +--Args: +---- id Current weather ID +----------------------------------------------------------------------------------- +--Returns: +---- None, updates the table. +----------------------------------------------------------------------------------- +function weather_update(id) + world.weather_id = id + world.real_weather_id = id + world.real_weather = res.weather[id][language] + world.real_weather_element = res.elements[res.weather[id].element][language] + world.real_weather_intensity = res.weather[world.real_weather_id].intensity + if buffactive[178] then + world.weather_id = 4 + elseif buffactive[179] then + world.weather_id = 12 + elseif buffactive[180] then + world.weather_id = 10 + elseif buffactive[181] then + world.weather_id = 8 + elseif buffactive[182] then + world.weather_id = 14 + elseif buffactive[183] then + world.weather_id = 6 + elseif buffactive[184] then + world.weather_id = 16 + elseif buffactive[185] then + world.weather_id = 18 + elseif buffactive[589] then + world.weather_id = 5 + elseif buffactive[590] then + world.weather_id = 13 + elseif buffactive[591] then + world.weather_id = 11 + elseif buffactive[592] then + world.weather_id = 9 + elseif buffactive[593] then + world.weather_id = 15 + elseif buffactive[594] then + world.weather_id = 7 + elseif buffactive[595] then + world.weather_id = 17 + elseif buffactive[596] then + world.weather_id = 19 + end + world.weather = res.weather[world.weather_id][language] + world.weather_element = res.elements[res.weather[world.weather_id].element][language] + world.weather_intensity = res.weather[world.weather_id].intensity +end + + +----------------------------------------------------------------------------------- +--Name: refresh_group_info() +--Args: +---- None +----------------------------------------------------------------------------------- +--Returns: +---- None +---- +---- Takes the mob arrays from windower.ffxi.get_party() and splits them from p0~5, a10~15, a20~25 +---- into alliance[1][1~6], alliance[2][1~6], alliance[3][1~6], respectively. +---- Also adds a "count" field to alliance (total number of people in alliance) and +---- to the individual subtables (total number of people in each party. +----------------------------------------------------------------------------------- +function refresh_group_info(dt,user_event_flag) + if not alliance or #alliance == 0 then + alliance = make_alliance() + end + + local c_alliance = make_alliance() + + local j = windower.ffxi.get_party() or {} + + c_alliance.leader = j.alliance_leader -- Test whether this works + c_alliance[1].leader = j.party1_leader + c_alliance[2].leader = j.party2_leader + c_alliance[3].leader = j.party3_leader + + for i,v in pairs(j) do + if type(v) == 'table' and v.mob and v.mob.race then + v.mob.race_id = v.mob.race + v.mob.race = res.races[v.mob.race][language] + end + + local allyIndex + local partyIndex + + -- For 'p#', ally index is 1, party index is the second char + if i:sub(1,1) == 'p' and tonumber(i:sub(2)) then + allyIndex = 1 + partyIndex = tonumber(i:sub(2))+1 + -- For 'a##', ally index is the second char, party index is the third char + elseif tonumber(i:sub(2,2)) and tonumber(i:sub(3)) then + allyIndex = tonumber(i:sub(2,2))+1 + partyIndex = tonumber(i:sub(3))+1 + end + + if allyIndex and partyIndex then + if v.mob and partybuffs[v.mob.index] then + v.buffactive = convert_buff_list(partybuffs[v.mob.index].buffs) + elseif v.mob and v.mob.index == player.index then + v.buffactive = buffactive + end + c_alliance[allyIndex][partyIndex] = v + c_alliance[allyIndex].count = c_alliance[allyIndex].count + 1 + c_alliance.count = c_alliance.count + 1 + + if v.mob then + if v.mob.id == c_alliance[1].leader then + c_alliance[1].leader = v + elseif v.mob.id == c_alliance[2].leader then + c_alliance[2].leader = v + elseif v.mob.id == c_alliance[3].leader then + c_alliance[3].leader = v + end + + if v.mob.id == c_alliance.leader then + c_alliance.leader = v + end + end + end + end + + + -- Clear the old structure while maintaining the party references: + for ally_party = 1,3 do + for i,v in pairs(alliance[ally_party]) do + alliance[ally_party][i] = nil + end + alliance[ally_party].count = 0 + end + alliance.count = 0 + alliance.leader = nil + + -- Reassign to the new structure + table.reassign(alliance[1],c_alliance[1]) + table.reassign(alliance[2],c_alliance[2]) + table.reassign(alliance[3],c_alliance[3]) + alliance.count = c_alliance.count + alliance.leader = c_alliance.leader +end + +----------------------------------------------------------------------------------- +--Name: make_alliance() +--Args: +---- none +----------------------------------------------------------------------------------- +--Returns: +---- one blank alliance structure +----------------------------------------------------------------------------------- +function make_alliance() + local all = make_user_table() + all[1]={count=0,leader=nil} + all[2]={count=0,leader=nil} + all[3]={count=0,leader=nil} + all.count=0 + all.leader=nil + return all +end + +----------------------------------------------------------------------------------- +--Name: convert_buff_list(bufflist) +--Args: +---- bufflist (table): List of buffs from windower.ffxi.get_player()['buffs'] +----------------------------------------------------------------------------------- +--Returns: +---- buffarr (table) +---- buffarr is indexed by the string buff name and has a value equal to the number +---- of that string present in the buff array. So two marches would give +---- buffarr.march==2. +----------------------------------------------------------------------------------- +function convert_buff_list(bufflist) + local buffarr = {} + for i,id in pairs(bufflist) do + if res.buffs[id] then -- For some reason we always have buff 255 active, which doesn't have an entry. + local buff = res.buffs[id][language]:lower() + if buffarr[buff] then + buffarr[buff] = buffarr[buff] +1 + else + buffarr[buff] = 1 + end + + if buffarr[id] then + buffarr[id] = buffarr[id] +1 + else + buffarr[id] = 1 + end + end + end + return buffarr +end + +----------------------------------------------------------------------------------- +--Name: refresh_item_list(itemlist) +--Args: +---- itemlist (table): List of items from windower.ffxi.get_items().something +----------------------------------------------------------------------------------- +--Returns: +---- retarr (table) +---- retarr is indexed by the item name, and contains a table with the +---- item id, count, and short name. If the long name for the item is +---- different than the short name, an additional entry is added for +---- that. +---- +---- Overall, this allows doing simple existance checks for both short +---- and long name (eg: player.inventory["Theo. Cap +1"] and +---- player.inventory["Theopany Cap +1"] both return the same table, +---- and both would be 'true' in a conditional check)), and get the item +---- count (player.inventory["Orichalc. Bullet"].count) +---- It also allows one to check for the alternate spelling of an item. +----------------------------------------------------------------------------------- +function refresh_item_list(itemlist) + retarr = make_user_table() + for i,v in pairs(itemlist) do + if type(v) == 'table' and v.id and v.id ~= 0 then + -- If we don't already have the primary item name in the table, add it. + if res.items[v.id] and res.items[v.id][language] and not retarr[res.items[v.id][language]] then + retarr[res.items[v.id][language]] = table.copy(v) + retarr[res.items[v.id][language]].shortname=res.items[v.id][language]:lower() + -- If a long version of the name exists, and is different from the short version, + -- add the long name to the info table and point the long name's key at that table. + if res.items[v.id][language..'_log'] and res.items[v.id][language..'_log']:lower() ~= res.items[v.id][language]:lower() then + retarr[res.items[v.id][language]].longname = res.items[v.id][language..'_log']:lower() + retarr[res.items[v.id][language..'_log']] = retarr[res.items[v.id][language]] + end + elseif res.items[v.id] and res.items[v.id][language] then + -- If there's already an entry for this item, all the hard work has already + -- been done. Just update the count on the subtable of the main item, and + -- everything else will link together. + retarr[res.items[v.id][language]].count = retarr[res.items[v.id][language]].count + v.count + end + end + end + return retarr +end + +----------------------------------------------------------------------------------- +--Name: refresh_user_env() +--Args: +---- none +----------------------------------------------------------------------------------- +--Returns: +---- none, but loads user files if they exist. +----------------------------------------------------------------------------------- +function refresh_user_env(job_id) + refresh_globals() + if not job_id then job_id = windower.ffxi.get_player().main_job_id end + + if not job_id then + windower.send_command('@wait 1;lua i '.._addon.name..' refresh_user_env') + else + load_user_files(job_id) + end +end + + +----------------------------------------------------------------------------------- +--Name: pathsearch() +--Args: +---- files_list - table of strings of the file name to search. +----------------------------------------------------------------------------------- +--Returns: +---- path of a valid file, if it exists. False if it doesn't. +----------------------------------------------------------------------------------- +function pathsearch(files_list) + + -- base directory search order: + -- windower + -- %appdata%/Windower/GearSwap + + -- sub directory search order: + -- libs-dev (only in windower addon path) + -- libs (only in windower addon path) + -- data/player.name + -- data/common + -- data + + local gearswap_data = windower.addon_path .. 'data/' + local gearswap_appdata = (os.getenv('APPDATA') or '') .. '/Windower/GearSwap/' + + local search_path = { + [1] = windower.addon_path .. 'libs-dev/', + [2] = windower.addon_path .. 'libs/', + [3] = gearswap_data .. player.name .. '/', + [4] = gearswap_data .. 'common/', + [5] = gearswap_data, + [6] = gearswap_appdata .. player.name .. '/', + [7] = gearswap_appdata .. 'common/', + [8] = gearswap_appdata, + [9] = windower.windower_path .. 'addons/libs/' + } + + local user_path + local normal_path + + for _,basepath in ipairs(search_path) do + if windower.dir_exists(basepath) then + for i,v in ipairs(files_list) do + if v ~= '' then + if include_user_path then + user_path = basepath .. include_user_path .. '/' .. v + end + normal_path = basepath .. v + + if user_path and windower.file_exists(user_path) then + return user_path,basepath,v + elseif normal_path and windower.file_exists(normal_path) then + return normal_path,basepath,v + end + end + end + end + end + + return false +end |