Advertisement
Guest User

RNGHelper

a guest
Mar 28th, 2019
576
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 35.57 KB | None | 0 0
  1. Alles in einer Datei, am Ende habt ihr 3, 2 LUA´s, 1 XML Datei
  2.  
  3. ------------------------------------------------------------------------------------------------------------------------------------------
  4. ------------------------------------------------------------------------------------------------------------------------------------------
  5. ------------------------------------------------------------------------------------------------------------------------------------------
  6. Format LUA - Name config2 Speicherort: Windower/addons/libs
  7. ------------------------------------------------------------------------------------------------------------------------------------------
  8.  
  9.  
  10. --[[
  11. Functions that facilitate loading, parsing, manipulating and storing of config files.
  12. ]]
  13.  
  14. local config = {}
  15.  
  16. _libs = _libs or {}
  17. _libs.config = config
  18. _libs.tables = _libs.tables or require('tables')
  19. _libs.sets = _libs.sets or require('sets')
  20. _libs.lists = _libs.lists or require('lists')
  21. _libs.strings = _libs.strings or require('strings')
  22. _libs.xml = _libs.xml or require('xml')
  23. _libs.files = _libs.files or require('files')
  24.  
  25. local error = error or print+{'Error:'}
  26. local warning = warning or print+{'Warning:'}
  27. local notice = notice or print+{'Notice:'}
  28. local log = log or print
  29.  
  30. -- Map for different config loads.
  31. local settings_map = T{}
  32.  
  33. --[[ Local functions ]]
  34.  
  35. local parse
  36. local merge
  37. local settings_table
  38. local settings_xml
  39. local nest_xml
  40. local table_diff
  41.  
  42. -- Loads a specified file, or alternatively a file 'settings.xml' in the current addon/data folder.
  43. function config.load(filepath, defaults)
  44. if type(filepath) ~= 'string' then
  45. filepath, defaults = 'data/settings.xml', filepath
  46. end
  47.  
  48. local confdict_mt = getmetatable(defaults) or _meta.T
  49. local settings = setmetatable(table.copy(defaults or {}), {__class = 'Settings', __index = function(t, k)
  50. if config[k] ~= nil then
  51. return config[k]
  52. end
  53.  
  54. return confdict_mt.__index[k]
  55. end})
  56.  
  57. -- Settings member variables, in separate struct
  58. local meta = {}
  59. meta.file = _libs.files.new(filepath, true)
  60. meta.original = T{global = table.copy(settings)}
  61. meta.chars = S{}
  62. meta.comments = {}
  63. meta.refresh = T{}
  64. meta.cdata = S{}
  65.  
  66. settings_map[settings] = meta
  67.  
  68. -- Load addon config file (Windower/addon/<addonname>/data/settings.xml).
  69. if not meta.file:exists() then
  70. config.save(settings, 'all')
  71. end
  72.  
  73. return parse(settings)
  74. end
  75.  
  76. -- Reloads the settings for the provided table. Needs to be the same table that was assigned to with config.load.
  77. function config.reload(settings)
  78. if not settings_map[settings] then
  79. error('Config reload error: unknown settings table.')
  80. return
  81. end
  82.  
  83. parse(settings)
  84.  
  85. for t in settings_map[settings].refresh:it() do
  86. t.fn(settings, unpack(t.args))
  87. end
  88. end
  89.  
  90. -- Resolves to the correct parser and calls the respective subroutine, returns the parsed settings table.
  91. function parse(settings)
  92. local parsed = T{}
  93. local err
  94. local meta = settings_map[settings]
  95.  
  96. if meta.file.path:endswith('.json') then
  97. parsed = _libs.json.read(meta.file)
  98.  
  99. elseif meta.file.path:endswith('.xml') then
  100. parsed, err = _libs.xml.read(meta.file)
  101.  
  102. if not parsed then
  103. error(err or 'XML error: Unknown error.')
  104. return settings
  105. end
  106.  
  107. parsed = settings_table(parsed, settings)
  108. end
  109.  
  110. -- Determine all characters found in the settings file.
  111. meta.chars = parsed:keyset() - S{'global'}
  112. meta.original = T{}
  113.  
  114. if table.empty(settings) then
  115. for char in (meta.chars + S{'global'}):it() do
  116. meta.original[char] = table.update(table.copy(settings), parsed[char], true)
  117. end
  118.  
  119. local full_parsed = parsed.global
  120. local player = windower.ffxi.get_player()
  121. if player then
  122. full_parsed = full_parsed:update(parsed[player.name:lower()], true)
  123. end
  124.  
  125. return settings:update(full_parsed, true)
  126. end
  127.  
  128. -- Update the global settings with the per-player defined settings, if they exist. Save the parsed value for later comparison.
  129. for char in (meta.chars + S{'global'}):it() do
  130. meta.original[char] = merge(table.copy(settings), parsed[char], char)
  131. end
  132. for char in meta.chars:it() do
  133. meta.original[char] = table_diff(meta.original.global, meta.original[char]) or T{}
  134. end
  135.  
  136. local full_parsed = parsed.global
  137.  
  138. local player = windower.ffxi.get_player()
  139. if player then
  140. full_parsed = full_parsed:update(parsed[player.name:lower()], true)
  141. end
  142.  
  143. return merge(settings, full_parsed)
  144. end
  145.  
  146. -- Merges two tables like update would, but retains type-information and tries to work around conflicts.
  147. function merge(t, t_merge, path)
  148. path = type(path) == 'string' and T{path} or path
  149.  
  150. local keys = {}
  151. for key in pairs(t) do
  152. keys[tostring(key):lower()] = key
  153. end
  154.  
  155. for lkey, val in pairs(t_merge) do
  156. local key = keys[lkey:lower()]
  157. if not key then
  158. if type(val) == 'table' then
  159. t[lkey] = setmetatable(table.copy(val), getmetatable(val) or _meta.T)
  160. else
  161. t[lkey] = val
  162. end
  163.  
  164. else
  165. local err = false
  166. local oldval = rawget(t, key)
  167. local oldtype = type(oldval)
  168.  
  169. if oldtype == 'table' and type(val) == 'table' then
  170. t[key] = merge(oldval, val, path and path:copy() + key or nil)
  171.  
  172. elseif oldtype ~= type(val) then
  173. if oldtype == 'table' then
  174. if type(val) == 'string' then
  175. -- Single-line CSV parser, can possible refactor this to tables.lua
  176. local res = {}
  177. local current = ''
  178. local quote = false
  179. local last
  180. for c in val:gmatch('.') do
  181. if c == ',' and not quote then
  182. res[#res + 1] = current
  183. current = ''
  184. last = nil
  185. elseif c == '"' then
  186. if last == '"' then
  187. current = current .. c
  188. last = nil
  189. else
  190. last = '"'
  191. end
  192.  
  193. quote = not quote
  194. else
  195. current = current .. c
  196. last = c
  197. end
  198. end
  199. res[#res + 1] = current
  200.  
  201. -- TODO: Remove this after a while, not standard compliant
  202. -- Currently needed to not mess up existing settings
  203. res = table.map(res, string.trim)
  204.  
  205. if class then
  206. if class(oldval) == 'Set' then
  207. res = S(res)
  208. elseif class(oldval) == 'List' then
  209. res = L(res)
  210. elseif class(oldval) == 'Table' then
  211. res = T(res)
  212. end
  213. end
  214. t[key] = res
  215.  
  216. else
  217. err = true
  218.  
  219. end
  220.  
  221. elseif oldtype == 'number' then
  222. local testdec = tonumber(val)
  223. local testhex = tonumber(val, 16)
  224. if testdec then
  225. t[key] = testdec
  226. elseif testhex then
  227. t[key] = testhex
  228. else
  229. err = true
  230. end
  231.  
  232. elseif oldtype == 'boolean' then
  233. if val == 'true' then
  234. t[key] = true
  235. elseif val == 'false' then
  236. t[key] = false
  237. else
  238. err = true
  239. end
  240.  
  241. elseif oldtype == 'string' then
  242. if type(val) == 'table' and not next(val) then
  243. t[key] = ''
  244. else
  245. t[key] = val
  246. err = true
  247. end
  248.  
  249. else
  250. err = true
  251. end
  252.  
  253. else
  254. t[key] = val
  255. end
  256.  
  257. if err then
  258. if path then
  259. warning('Could not safely merge values for \'%s/%s\', %s expected (default: %s), got %s (%s).':format(path:concat('/'), key, class(oldval), tostring(oldval), class(val), tostring(val)))
  260. end
  261. t[key] = val
  262. end
  263. end
  264. end
  265.  
  266. return t
  267. end
  268.  
  269. -- Parses a settings struct from a DOM tree.
  270. function settings_table(node, settings, key, meta)
  271. settings = settings or T{}
  272. key = key or 'settings'
  273. meta = meta or settings_map[settings]
  274.  
  275. local t = T{}
  276. if node.type ~= 'tag' then
  277. return t
  278. end
  279.  
  280. if not node.children:all(function(n)
  281. return n.type == 'tag' or n.type == 'comment'
  282. end) and not (#node.children == 1 and node.children[1].type == 'text') then
  283. error('Malformatted settings file.')
  284. return t
  285. end
  286.  
  287. -- TODO: Type checking necessary? merge should take care of that.
  288. if #node.children == 1 and node.children[1].type == 'text' then
  289. local val = node.children[1].value
  290. if node.children[1].cdata then
  291. meta.cdata:add(key)
  292. return val
  293. end
  294.  
  295. if val:lower() == 'false' then
  296. return false
  297. elseif val:lower() == 'true' then
  298. return true
  299. end
  300.  
  301. local num = tonumber(val)
  302. if num ~= nil then
  303. return num
  304. end
  305.  
  306. return val
  307. end
  308.  
  309. for child in node.children:it() do
  310. if child.type == 'comment' then
  311. meta.comments[key] = child.value:trim()
  312. elseif child.type == 'tag' then
  313. key = child.name:lower()
  314. local childdict
  315. if table.containskey(settings, key) then
  316. childdict = table.copy(settings)
  317. else
  318. childdict = settings
  319. end
  320. t[child.name:lower()] = settings_table(child, childdict, key, meta)
  321. end
  322. end
  323.  
  324. return t
  325. end
  326.  
  327. -- Writes the passed config table to the spcified file name.
  328. -- char defaults to windower.ffxi.get_player().name. Set to "all" to apply to all characters.
  329. function config.save(t, char)
  330. if char ~= 'all' and not windower.ffxi.get_info().logged_in then
  331. return
  332. end
  333.  
  334. char = (char or windower.ffxi.get_player().name):lower()
  335. local meta = settings_map[t]
  336.  
  337. if char == 'all' then
  338. char = 'global'
  339. elseif char ~= 'global' and not meta.chars:contains(char) then
  340. meta.chars:add(char)
  341. meta.original[char] = T{}
  342. end
  343.  
  344. meta.original[char]:update(t)
  345.  
  346. if char == 'global' then
  347. meta.original = T{global = meta.original.global}
  348. meta.chars = S{}
  349. else
  350. meta.original.global:amend(meta.original[char], true)
  351. meta.original[char] = table_diff(meta.original.global, meta.original[char]) or T{}
  352.  
  353. if meta.original[char]:empty(true) then
  354. meta.original[char] = nil
  355. meta.chars:remove(char)
  356. end
  357. end
  358.  
  359. meta.file:write(settings_xml(meta))
  360. end
  361.  
  362. -- Returns the table containing only elements from t_new that are different from t and not nil.
  363. function table_diff(t, t_new)
  364. local res = T{}
  365. local cmp
  366.  
  367. for key, val in pairs(t_new) do
  368. cmp = t[key]
  369. if cmp ~= nil then
  370. if type(cmp) ~= type(val) then
  371. warning('Mismatched setting types for key \''..key..'\':', type(cmp), type(val))
  372. else
  373. if type(val) == 'table' then
  374. if class(val) == 'Set' or class(val) == 'List' then
  375. if not cmp:equals(val) then
  376. res[key] = val
  377. end
  378. elseif table.isarray(val) and table.isarray(cmp) then
  379. if not table.equals(cmp, val) then
  380. res[key] = val
  381. end
  382. else
  383. res[key] = table_diff(cmp, val)
  384. end
  385. elseif cmp ~= val then
  386. res[key] = val
  387. end
  388. end
  389. end
  390. end
  391.  
  392. return not table.empty(res) and res or nil
  393. end
  394.  
  395. -- Converts a settings table to a XML representation.
  396. function settings_xml(meta)
  397. local lines = L{}
  398. lines:append('<?xml version="1.1" ?>')
  399. lines:append('<settings>')
  400.  
  401. local chars = (meta.original:keyset() - S{'global'}):sort()
  402. for char in (L{'global'} + chars):it() do
  403. if char == 'global' and meta.comments.settings then
  404. lines:append(' <!--')
  405. local comment_lines = meta.comments.settings:split('\n')
  406. for comment in comment_lines:it() do
  407. lines:append(' %s':format(comment:trim()))
  408. end
  409.  
  410. lines:append(' -->')
  411. end
  412.  
  413. lines:append(' <%s>':format(char))
  414. lines:append(nest_xml(meta.original[char], meta))
  415. lines:append(' </%s>':format(char))
  416. end
  417.  
  418. lines:append('</settings>')
  419. lines:append('')
  420. return lines:concat('\n')
  421. end
  422.  
  423. -- Converts a table to XML without headers using appropriate indentation and comment spacing. Used in settings_xml.
  424. function nest_xml(t, meta, indentlevel)
  425. indentlevel = indentlevel or 2
  426. local indent = (' '):rep(4*indentlevel)
  427.  
  428. local inlines = T{}
  429. local fragments = T{}
  430. local maxlength = 0 -- For proper comment indenting
  431. local keys = set.sort(table.keyset(t))
  432. local val
  433. for _, key in ipairs(keys) do
  434. val = t[key]
  435. if type(val) == 'table' and not (class(val) == 'List' or class(val) == 'Set') then
  436. fragments:append('%s<%s>':format(indent, key))
  437. if meta.comments[key] then
  438. local c = '<!-- %s -->':format(meta.comments[key]:trim()):split('\n')
  439. local pre = ''
  440. for cstr in c:it() do
  441. fragments:append('%s%s%s':format(indent, pre, cstr:trim()))
  442. pre = '\t '
  443. end
  444. end
  445. fragments:append(nest_xml(val, meta, indentlevel + 1))
  446. fragments:append('%s</%s>':format(indent, key))
  447.  
  448. else
  449. if class(val) == 'List' then
  450. val = list.format(val, 'csv')
  451. elseif class(val) == 'Set' then
  452. val = set.format(val, 'csv')
  453. elseif type(val) == 'table' then
  454. val = table.format(val, 'csv')
  455. elseif type(val) == 'string' and meta.cdata:contains(tostring(key):lower()) then
  456. val = '<![CDATA[%s]]>':format(val)
  457. else
  458. val = tostring(val)
  459. end
  460.  
  461. if val == '' then
  462. fragments:append('%s<%s />':format(indent, key))
  463. else
  464. fragments:append('%s<%s>%s</%s>':format(indent, key, meta.cdata:contains(tostring(key):lower()) and val or val:xml_escape(), key))
  465. end
  466. local length = fragments:last():length() - indent:length()
  467. if length > maxlength then
  468. maxlength = length
  469. end
  470. inlines[fragments:length()] = key
  471. end
  472. end
  473.  
  474. for frag_key, key in pairs(inlines) do
  475. if meta.comments[key] then
  476. fragments[frag_key] = '%s%s<!-- %s -->':format(fragments[frag_key], ' ':rep(maxlength - fragments[frag_key]:trim():length() + 1), meta.comments[key])
  477. end
  478. end
  479.  
  480. return fragments:concat('\n')
  481. end
  482.  
  483. function config.register(settings, fn, ...)
  484. local args = {...}
  485. local key = tostring(args):sub(8)
  486. settings_map[settings].refresh[key] = {fn=fn, args=args}
  487. return key
  488. end
  489.  
  490. function config.unregister(settings, key)
  491. settings_map[settings].refresh[key] = nil
  492. end
  493.  
  494. local function reload_settings()
  495. for _, settings in settings_map:it() do
  496. config.reload(settings)
  497. end
  498. end
  499.  
  500. windower.raw_register_event('load', reload_settings)
  501. windower.raw_register_event('logout', reload_settings)
  502. windower.raw_register_event('login', reload_settings)
  503.  
  504. return config
  505.  
  506. --[[
  507. Copyright © 2013-2015, Windower
  508. All rights reserved.
  509.  
  510. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
  511.  
  512. * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  513. * 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.
  514. * Neither the name of Windower nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
  515.  
  516. 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 Windower 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.
  517. ]]
  518.  
  519.  
  520.  
  521. ------------------------------------------------------------------------------------------------------------------------------------------
  522. ------------------------------------------------------------------------------------------------------------------------------------------
  523. Format LUA - Name rnghelper Speicherort: Windower/addons/gearswap/libs
  524. ------------------------------------------------------------------------------------------------------------------------------------------
  525. ------------------------------------------------------------------------------------------------------------------------------------------
  526. require('queues')
  527. res = require('resources')
  528. packets = require('packets')
  529. config = require('config2')
  530.  
  531. local self = windower.ffxi.get_player().id
  532. local index = windower.ffxi.get_player().index
  533. local target = nil
  534. local completion = false
  535. local turn = false
  536. local mode = nil
  537. local weaponskill = nil
  538. local assist = nil
  539. local cooldowns = {}
  540. local settings = config.load('libs/rnghelper_settings.xml')
  541. local cooldown = 0
  542. local queue = Q{}
  543. local pending = nil
  544. local enabled = true
  545. local pet = nil
  546. local cancel = false
  547. local timeout = 6.0
  548. local timestamp = os.time()
  549.  
  550. local action_events = {
  551. [2] = 'mid /ra',
  552. [3] = 'mid /ws',
  553. [4] = 'mid /ma',
  554. [5] = 'mid /item',
  555. [6] = 'pre /ja',
  556. [7] = 'pre /ws',
  557. [8] = 'pre /ma',
  558. [9] = 'pre /item',
  559. [12] = 'pre /ra',
  560. [13] = 'mid /petws',
  561. [14] = 'pre /ja',
  562. [15] = 'pre /ja',
  563. }
  564.  
  565. local terminal_action_events = {
  566. [2] = 'mid /ra',
  567. [3] = 'mid /ws',
  568. [4] = 'mid /ma',
  569. [5] = 'mid /item',
  570. [6] = 'pre /ja',
  571. [13] = 'mid /petws',
  572. }
  573.  
  574. local action_interrupted = {
  575. [78] = 78,
  576. [84] = 84,
  577. }
  578.  
  579. local action_message_interrupted = {
  580. [16] = 16,
  581. [62] = 62,
  582. }
  583.  
  584. local action_message_unable = {
  585. [12] = 12,
  586. [17] = 17,
  587. [18] = 18,
  588. [34] = 34,
  589. [35] = 35,
  590. [40] = 40,
  591. [47] = 47,
  592. [48] = 48,
  593. [49] = 49,
  594. [55] = 55,
  595. [56] = 56,
  596. [71] = 71,
  597. [72] = 72,
  598. [76] = 76,
  599. [78] = 78,
  600. [84] = 84,
  601. [87] = 87,
  602. [88] = 88,
  603. [89] = 89,
  604. [90] = 90,
  605. [91] = 91,
  606. [92] = 92,
  607. [94] = 94,
  608. [95] = 95,
  609. [96] = 96,
  610. [104] = 104,
  611. [106] = 106,
  612. [111] = 111,
  613. [128] = 128,
  614. [154] = 154,
  615. [155] = 155,
  616. [190] = 190,
  617. [191] = 191,
  618. [192] = 192,
  619. [193] = 193,
  620. [198] = 198,
  621. [199] = 199,
  622. [215] = 215,
  623. [216] = 216,
  624. [217] = 217,
  625. [218] = 218,
  626. [219] = 219,
  627. [220] = 220,
  628. [233] = 233,
  629. [246] = 246,
  630. [247] = 247,
  631. [307] = 307,
  632. [308] = 308,
  633. [313] = 313,
  634. [315] = 315,
  635. [316] = 316,
  636. [325] = 325,
  637. [328] = 328,
  638. [337] = 337,
  639. [338] = 338,
  640. [346] = 346,
  641. [347] = 347,
  642. [348] = 348,
  643. [349] = 349,
  644. [356] = 356,
  645. [410] = 410,
  646. [411] = 411,
  647. [428] = 428,
  648. [429] = 429,
  649. [443] = 443,
  650. [444] = 444,
  651. [445] = 445,
  652. [446] = 446,
  653. [514] = 514,
  654. [516] = 516,
  655. [517] = 517,
  656. [518] = 518,
  657. [523] = 523,
  658. [524] = 524,
  659. [525] = 525,
  660. [547] = 547,
  661. [561] = 561,
  662. [568] = 568,
  663. [569] = 569,
  664. [574] = 574,
  665. [575] = 575,
  666. [579] = 579,
  667. [580] = 580,
  668. [581] = 581,
  669. [649] = 649,
  670. [660] = 660,
  671. [661] = 661,
  672. [662] = 662,
  673. [665] = 665,
  674. [666] = 666,
  675. [700] = 700,
  676. [701] = 701,
  677. [717] = 717,
  678. }
  679.  
  680. local function load_profile(name, set_to_default)
  681. local profile = settings.profiles[name]
  682. for k, v in pairs(profile.cooldowns) do
  683. cooldowns["\/%s":format(k)] = v
  684. end
  685. weaponskill = profile.weaponskill
  686. mode = profile.mode
  687. turn = profile.turn
  688. if set_to_default then
  689. settings.default = name
  690. settings:save('all')
  691. end
  692. end
  693.  
  694. local function save_profile(name)
  695. local profile = {}
  696. profile.cooldowns = {}
  697. for k, v in pairs(cooldowns) do
  698. profile.cooldowns[k:sub(2)] = v
  699. end
  700. profile.weaponskill = weaponskill
  701. profile.mode = mode
  702. profile.turn = turn
  703. settings.profiles[name] = profile
  704. settings.default = name
  705. settings:save('all')
  706. end
  707.  
  708. local function heading_difference(h, a)
  709. if (h > 0 and a > 0) or (h < 0 and a < 0) then
  710. return math.abs(h - a)
  711. else
  712. local d = math.abs(a - h)
  713. if d > math.pi then
  714. if h > 0 then
  715. d = h - a - 2 * math.pi
  716. else
  717. d = h - a + 2 * math.pi
  718. end
  719. end
  720. return math.abs(d)
  721. end
  722. end
  723.  
  724. local function turn_away(player, mob)
  725. local dx = player.x - mob.x
  726. local dy = player.y - mob.y
  727. local away = math.atan(-dy/dx)
  728. if (dx >= 0) then
  729. windower.ffxi.turn(away)
  730. else
  731. if away >= 0 then
  732. away = away - math.pi
  733. else
  734. away = away + math.pi
  735. end
  736. windower.ffxi.turn(away)
  737. end
  738. local p = packets.parse('outgoing', windower.packets.last_outgoing(0x15))
  739. local h = p['Rotation'] * 2 * math.pi/255
  740. if h > math.pi then
  741. h = h - 2 * math.pi
  742. end
  743. return heading_difference(h, away) >= 0.785398
  744. end
  745.  
  746. local function able_to_use_action()
  747. if pending.action_type == 'Ability' then
  748. return windower.ffxi.get_ability_recasts()[res.job_abilities[pending.id].recast_id] == 0
  749. elseif pending.action_type == 'Magic' then
  750. return windower.ffxi.get_spell_recasts()[res.spells[pending.id].recast_id] == 0
  751. end
  752. return true
  753. end
  754.  
  755. local function able_to_use_weaponskill()
  756. return windower.ffxi.get_player().vitals.tp >= 1000
  757. end
  758.  
  759. local function execute_pending_action()
  760. cooldown = cooldowns[pending.prefix]
  761. if pending.prefix == '/range' then
  762. windower.chat.input("%s %d":format(pending.prefix, pending.target))
  763. else
  764. windower.chat.input("%s \"%s\" %d":format(pending.prefix, pending.english, pending.target))
  765. end
  766. end
  767.  
  768. local function process_pending_action()
  769. if turn and (pending.prefix == '/range' or pending.prefix == '/weaponskill') then
  770. local player = windower.ffxi.get_mob_by_id(self)
  771. local mob = windower.ffxi.get_mob_by_id(pending.target)
  772. cancel = turn_away(mob, player)
  773. end
  774. if pending.prefix == '/weaponskill' then
  775. if not able_to_use_weaponskill() then
  776. queue:insert(1, pending)
  777. pending = {
  778. ['prefix'] = '/range',
  779. ['english'] = 'Ranged',
  780. ['target'] = pending.target,
  781. }
  782. end
  783. execute_pending_action()
  784. elseif not able_to_use_action() then
  785. windower.add_to_chat(200, "Rnghelper : Aborting %s - Ability not ready.":format(pending.english))
  786. completion = true
  787. process_queue()
  788. else
  789. execute_pending_action()
  790. end
  791. end
  792.  
  793. function process_queue()
  794. if completion then
  795. pending = nil
  796. completion = false
  797. end
  798. if pending then
  799. elseif not queue:empty() then
  800. pending = queue:pop()
  801. elseif target then
  802. if weaponskill and able_to_use_weaponskill() then
  803. pending = {
  804. ['prefix'] = '/weaponskill',
  805. ['english'] = weaponskill,
  806. ['target'] = target,
  807. ['action_type'] = 'Ability',
  808. }
  809. else
  810. pending = {
  811. ['prefix'] = '/range',
  812. ['english'] = 'Ranged',
  813. ['target'] = target,
  814. ['action_type'] = 'Ranged Attack',
  815. }
  816. end
  817. end
  818. if pending then
  819. process_pending_action()
  820. end
  821. end
  822.  
  823. local function handle_interrupt()
  824. completion = true
  825. windower.send_command('@wait %f;gs rh process':format(cooldown))
  826. end
  827.  
  828. local function add_spell_to_queue(spell)
  829. queue:push({
  830. ['prefix'] = spell.prefix,
  831. ['english'] = spell.english,
  832. ['target'] = spell.target.id,
  833. ['id'] = spell.id,
  834. ['action_type'] = spell.action_type,
  835. })
  836. end
  837.  
  838. local function check_timeout()
  839. local current_time = os.time()
  840. if pending and (current_time - timestamp) >= timeout then
  841. timestamp = current_time
  842. if pending.english then
  843. windower.add_to_chat(167, "Timed out attempting to %s - Retrying":format(pending.english))
  844. end
  845. process_queue()
  846. end
  847. end
  848.  
  849. function filter_precast(spell, spellMap, eventArgs)
  850. if (pending and
  851. pending.prefix == spell.prefix and
  852. pending.english == spell.english and
  853. pending.target == spell.target.id) or (not enabled) then
  854. if enabled and turn and cancel then
  855. eventArgs.cancel = true
  856. cancel_spell()
  857. windower.send_command('@wait 0.1;gs rh process')
  858. else
  859. timestamp = os.time()
  860. coroutine.schedule(check_timeout, timeout)
  861. end
  862. else
  863. eventArgs.cancel = true
  864. cancel_spell()
  865. if pending then
  866. if spell.english == 'Ranged' then
  867. target = spell.target.id
  868. completion = true
  869. process_queue()
  870. else
  871. add_spell_to_queue(spell)
  872. end
  873. else
  874. add_spell_to_queue(spell)
  875. process_queue()
  876. end
  877. end
  878. end
  879.  
  880. local function monitor_target(id, data, modified, injected, blocked)
  881. if (id == 0xe) and target then
  882. local p = packets.parse('incoming', data)
  883. if (p.NPC == target) and ((p.Mask % 8) > 3) then
  884. if not (p['HP %'] > 0) then
  885. target = nil
  886. pending = nil
  887. completion = false
  888. queue:clear()
  889. end
  890. end
  891. end
  892. end
  893.  
  894. local function handle_incoming_action_packet(id, data, modified, injected, blocked)
  895. if id == 0x28 and enabled then
  896. local p = packets.parse('incoming', data)
  897. if ((p.Actor == self) or (p.Actor == pet)) and action_events[p.Category] then
  898. if action_interrupted[p['Target 1 Action 1 Message']] then
  899. handle_interrupt()
  900. elseif p.Param == 28787 then
  901. elseif terminal_action_events[p.Category] then
  902. if pending and pending.prefix == '/pet' and not (res.job_abilities[p.Param].type == 'PetCommand') then
  903. if p.Actor == pet then
  904. handle_interrupt()
  905. end
  906. else
  907. handle_interrupt()
  908. end
  909. end
  910. elseif assist and (not target) and (p.Category == 2) then
  911. local player = windower.ffxi.get_mob_by_id(p.Actor)
  912. if player.name == assist then
  913. local empty = queue:empty()
  914. queue:push({
  915. ['prefix'] = '/range',
  916. ['english'] = 'Ranged',
  917. ['target'] = p['Target 1 ID'],
  918. ['action_type'] = 'Ranged Attack',
  919. })
  920. if empty then
  921. process_queue()
  922. end
  923. end
  924. end
  925. end
  926. end
  927.  
  928. local function handle_incoming_action_message_packet(id, data, modified, injected, blocked)
  929. if id == 0x29 and enabled then
  930. local p = packets.parse('incoming', data)
  931. if (p.Actor == self) then
  932. if action_message_interrupted[p.Message] then
  933. handle_interrupt()
  934. elseif action_message_unable[p.Message] then
  935. windower.send_command('@wait 0;gs rh process')
  936. end
  937. end
  938. end
  939. end
  940.  
  941. local function handle_outgoing_action_packet(id, data, modified, injected, blocked)
  942. if id == 0x1a and enabled then
  943. local p = packets.parse('outgoing', data)
  944. if p.Category == 16 then
  945. target = p.Target
  946. cooldown = cooldowns['/range']
  947. end
  948. end
  949. end
  950.  
  951. local function acquire_pet(id, data)
  952. if id == 0x68 then
  953. local p = packets.parse('incoming', data)
  954. if p['Owner ID'] == self then
  955. pet = p['Target ID']
  956. end
  957. end
  958. end
  959.  
  960. local function clear()
  961. target = nil
  962. pending = nil
  963. completion = false
  964. queue:clear()
  965. end
  966.  
  967. register_unhandled_command(function (...)
  968. local commands = {...}
  969. if commands[1] and commands[1]:lower() == 'rh' then
  970. if commands[2] and commands[2]:lower() == 'process' then
  971. process_queue()
  972. elseif commands[2] and commands[2]:lower() == 'set' then
  973. if commands[3] then
  974. windower.add_to_chat(200, "Rnghelper : Setting weaponskill to %s":format(commands[3]))
  975. weaponskill = commands[3]
  976. else
  977. windower.add_to_chat(200, "Rnghelper : Clearing weaponskill.")
  978. weaponskill = nil
  979. end
  980. elseif commands[2] and commands[2]:lower() == 'assist' then
  981. if commands[3] then
  982. windower.add_to_chat(200, "Rnghelper : Assisting %s":format(commands[3]))
  983. assist = commands[3]
  984. else
  985. windower.add_to_chat(200, "Rnghelper : Clearing assist.")
  986. assist = nil
  987. end
  988. elseif commands[2] and commands[2]:lower() == 'turn' then
  989. if turn then
  990. windower.add_to_chat(200, "Rnghelper : Disabling auto turn")
  991. turn = false
  992. cancel = false
  993. else
  994. windower.add_to_chat(200, "Rnghelper : Enabling auto turn")
  995. end
  996. elseif commands[2] and commands[2]:lower() == 'print' then
  997. if pending then
  998. windower.add_to_chat(200, pending.prefix .. pending.english .. pending.target)
  999. end
  1000. for k, v in pairs(queue.data) do
  1001. windower.add_to_chat(200, k .. v.prefix .. v.english .. v.target)
  1002. end
  1003. elseif commands[2] and commands[2]:lower() == 'save' then
  1004. save_profile(commands[3])
  1005. elseif commands[2] and commands[2]:lower() == 'load' then
  1006. load_profile(commands[3], true)
  1007. elseif commands[2] and commands[2]:lower() == 'clear' then
  1008. windower.add_to_chat(200, "Rnghelper : Clearing queue")
  1009. clear()
  1010. elseif commands[2] and commands[2]:lower() == 'enable' then
  1011. if enabled then
  1012. windower.add_to_chat(200, "Rnghelper : Already enabled")
  1013. else
  1014. windower.add_to_chat(200, "Rnghelper : Enabling")
  1015. enabled = true
  1016. end
  1017. elseif commands[2] and commands[2]:lower() == 'disable' then
  1018. if not enabled then
  1019. windower.add_to_chat(200, "Rnghelper : Already disabled")
  1020. else
  1021. windower.add_to_chat(200, "Rnghelper : Disabling")
  1022. clear()
  1023. enabled = false
  1024. end
  1025. end
  1026. return true
  1027. end
  1028. return false
  1029. end)
  1030.  
  1031. load_profile(settings.default)
  1032. windower.raw_register_event('incoming chunk', handle_incoming_action_packet)
  1033. windower.raw_register_event('incoming chunk', handle_incoming_action_message_packet)
  1034. windower.raw_register_event('outgoing chunk', handle_outgoing_action_packet)
  1035. windower.raw_register_event('incoming chunk', monitor_target)
  1036. windower.raw_register_event('incoming chunk', acquire_pet)
  1037.  
  1038. ------------------------------------------------------------------------------------------------------------------------------------------
  1039. ------------------------------------------------------------------------------------------------------------------------------------------
  1040. Format XML - Name rnghelper_settings Speicherort: Windower/addons/gearswap/libs
  1041. ------------------------------------------------------------------------------------------------------------------------------------------
  1042. ------------------------------------------------------------------------------------------------------------------------------------------
  1043.  
  1044. <?xml version="1.1" ?>
  1045. <settings>
  1046. <global>
  1047. <default>default</default>
  1048. <profiles>
  1049. <default>
  1050. <cooldowns>
  1051. <bstpet>0</bstpet>
  1052. <item>1.1</item>
  1053. <jobability>0</jobability>
  1054. <magic>2.75</magic>
  1055. <monsterskill>2.25</monsterskill>
  1056. <ninjutsu>2.75</ninjutsu>
  1057. <pet>0</pet>
  1058. <range>1.15</range>
  1059. <song>2.75</song>
  1060. <weaponskill>2.45</weaponskill>
  1061. </cooldowns>
  1062. <mode>pummel</mode>
  1063. <weaponskill>Coronach</weaponskill>
  1064. <turn>true</turn>
  1065. </default>
  1066. <ls>
  1067. <cooldowns>
  1068. <bstpet>0</bstpet>
  1069. <item>1.1</item>
  1070. <jobability>0</jobability>
  1071. <magic>2.75</magic>
  1072. <monsterskill>2.25</monsterskill>
  1073. <ninjutsu>2.75</ninjutsu>
  1074. <pet>0</pet>
  1075. <range>1.15</range>
  1076. <song>2.75</song>
  1077. <weaponskill>2.45</weaponskill>
  1078. </cooldowns>
  1079. <mode>pummel</mode>
  1080. <weaponskill>Coronach</weaponskill>
  1081. <turn>true</turn>
  1082. </ls>
  1083. </profiles>
  1084. </global>
  1085. </settings>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement