Tornac83
5 years ago
commit
425493b8b8
2 changed files with 450 additions and 0 deletions
@ -0,0 +1,22 @@ |
|||||||
|
# Pupatt |
||||||
|
Ashita pup attachment addon for changing pup attachments per user profiles. Only working on retail and only pup main at the moment. |
||||||
|
|
||||||
|
pupatt commands /pupatt |
||||||
|
|
||||||
|
/pupatt load profileName, - Loads a saved profile from settings. |
||||||
|
|
||||||
|
/pupatt newprofile profileName, - Creates a new profile. |
||||||
|
|
||||||
|
/pupatt save, - Saves the new profile created. |
||||||
|
|
||||||
|
/pupatt list, - Lists the profiles as well as the hex values for each attachment. |
||||||
|
|
||||||
|
/pupatt current, - Lists the current attachments hex values. |
||||||
|
|
||||||
|
/pupatt clear, - Clears the current attachments. |
||||||
|
|
||||||
|
/pupatt save, - Saves the new profile created. |
||||||
|
|
||||||
|
/pupatt spawn, - Spawns the puppet using whatever is off cooldown. |
||||||
|
|
||||||
|
/pupatt despawn, - Despawns the puppet. |
@ -0,0 +1,428 @@ |
|||||||
|
--[[ |
||||||
|
* Ashita - Copyright (c) 2014 - 2020 atom0s [atom0s@live.com] |
||||||
|
* |
||||||
|
* This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. |
||||||
|
* To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/ or send a letter to |
||||||
|
* Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. |
||||||
|
* |
||||||
|
* By using Ashita, you agree to the above license and its terms. |
||||||
|
* |
||||||
|
* Attribution - You must give appropriate credit, provide a link to the license and indicate if changes were |
||||||
|
* made. You must do so in any reasonable manner, but not in any way that suggests the licensor |
||||||
|
* endorses you or your use. |
||||||
|
* |
||||||
|
* Non-Commercial - You may not use the material (Ashita) for commercial purposes. |
||||||
|
* |
||||||
|
* No-Derivatives - If you remix, transform, or build upon the material (Ashita), you may not distribute the |
||||||
|
* modified material. You are, however, allowed to submit the modified works back to the original |
||||||
|
* Ashita project in attempt to have it added to the original project. |
||||||
|
* |
||||||
|
* You may not apply legal terms or technological measures that legally restrict others |
||||||
|
* from doing anything the license permits. |
||||||
|
* |
||||||
|
* No warranties are given. |
||||||
|
]]-- |
||||||
|
|
||||||
|
_addon.author = 'tornac'; |
||||||
|
_addon.name = 'pupatt'; |
||||||
|
_addon.version = '1.05'; |
||||||
|
|
||||||
|
--------------------------------- |
||||||
|
--DO NOT EDIT BELOW THIS LINE |
||||||
|
--------------------------------- |
||||||
|
|
||||||
|
require 'common' |
||||||
|
require 'ffxi.recast' |
||||||
|
require 'logging' |
||||||
|
require 'timer' |
||||||
|
|
||||||
|
-------------------------------------------------------------- |
||||||
|
-- Create a table for holding the current profile to be written |
||||||
|
-------------------------------------------------------------- |
||||||
|
|
||||||
|
objDelay = 0.65; -- The delay to prevent spamming packets. |
||||||
|
objTimer = 0; -- The current time used for delaying packets. |
||||||
|
unequip = 0x00; |
||||||
|
pupSub = 0x00; |
||||||
|
offset = 0x04; |
||||||
|
local PlayerName = nil; |
||||||
|
inProgress = false; -- packet sending is in progress. |
||||||
|
queOffset = 1; -- smoother experience for packets. |
||||||
|
|
||||||
|
currentProfile = { }; |
||||||
|
attachmentQueue = { }; -- Table to hold commands queued for sending |
||||||
|
currentAttachments = { }; -- table for holding current attachments |
||||||
|
pupattProfiles = { }; -- table for holding attachment profiles |
||||||
|
petlessZones = {50,235,234,224,284,233,70,257,251,14,242,250,226,245, |
||||||
|
237,249,131,53,252,231,236,246,232,240,247,243,223,248,230, |
||||||
|
26,71,244,239,238,241,256,257} |
||||||
|
|
||||||
|
--[[ keeping for reference |
||||||
|
|
||||||
|
local player = GetPlayerEntity(); |
||||||
|
local pet = player.PetTargetIndex |
||||||
|
local recastTimerActivate = ashita.ffxi.recast.get_ability_recast_by_id(205); |
||||||
|
local recastTimerDeactivate = ashita.ffxi.recast.get_ability_recast_by_id(208); |
||||||
|
local recastTimerdeusex = ashita.ffxi.recast.get_ability_recast_by_id(115); |
||||||
|
local MainJob = AshitaCore:GetDataManager():GetPlayer():GetMainJob(); |
||||||
|
local SubJob = AshitaCore:GetDataManager():GetPlayer():GetSubJob(); |
||||||
|
local buffs = AshitaCore:GetDataManager():GetPlayer():GetBuffs(); |
||||||
|
local limitpoints = AshitaCore:GetDataManager():GetPlayer():GetLimitPoints(); |
||||||
|
local zone_id = AshitaCore:GetDataManager():GetParty():GetMemberZone(0); |
||||||
|
|
||||||
|
]] |
||||||
|
|
||||||
|
--------------------------------------------------------------- |
||||||
|
--try to load file when addon is loaded |
||||||
|
--------------------------------------------------------------- |
||||||
|
|
||||||
|
ashita.register_event('load', function() |
||||||
|
load_pupattSettings(); |
||||||
|
end); |
||||||
|
|
||||||
|
--------------------------------------------------------------- |
||||||
|
-- sees if any values are in a given table. |
||||||
|
--------------------------------------------------------------- |
||||||
|
|
||||||
|
function contains(table, val) |
||||||
|
for i=1,#table do |
||||||
|
if table[i] == val then |
||||||
|
return true |
||||||
|
end |
||||||
|
end |
||||||
|
return false; |
||||||
|
end; |
||||||
|
|
||||||
|
------------------------------------------------------------------------------------------------ |
||||||
|
-- desc: Getting pup information from packets for subsequent equipment changes. |
||||||
|
---------------------------------------------------------------------------------------------------- |
||||||
|
|
||||||
|
ashita.register_event('incoming_packet', function(id, size, packet) |
||||||
|
-- Party Member's Status |
||||||
|
if (id == 0x044) then |
||||||
|
DiffPack = struct.unpack('B', packet, 0x05 + 1); |
||||||
|
equippedOffset = 1; -- Increase by one byte every loop |
||||||
|
if (DiffPack == 0) then |
||||||
|
-- Unpack 14 bytes and set the slotid:attachmentid into currentAttachments table |
||||||
|
for i = 1, 14 do |
||||||
|
attachmentId = string.format("0x%X" , struct.unpack('B', packet, 0x08 + equippedOffset)); |
||||||
|
currentAttachments[i] = attachmentId; |
||||||
|
equippedOffset = equippedOffset + 1; |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
return false; |
||||||
|
end); |
||||||
|
|
||||||
|
------------------------------------------------------------------------------------------------ |
||||||
|
-- desc: Adds all the attachements from the given profile. |
||||||
|
---------------------------------------------------------------------------------------------------- |
||||||
|
|
||||||
|
--Pass in a slot ID + hex id of the Attachment |
||||||
|
-- Slot ID 1 = Head, 2=frame, 3-14 = attachment slots |
||||||
|
function addAttachment(slot, id) |
||||||
|
slots = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; |
||||||
|
slots[slot] = id; |
||||||
|
local attach = struct.pack('I2I2BBBBBBI2BBBBBBBBBBBBBB', 0x5302, 0x0000, id, 0x00, unequip, 0x00, 0x12, pupSub, 0x0000, slots[1],slots[2],slots[3],slots[4],slots[5],slots[6],slots[7],slots[8],slots[9],slots[10],slots[11],slots[12],slots[13],slots[14]):totable(); |
||||||
|
table.insert(attachmentQueue, { 0x102, attach}); |
||||||
|
end; |
||||||
|
|
||||||
|
------------------------------------------------------------------------------------------------ |
||||||
|
-- desc: Clearing all the attachment out of the puppet. |
||||||
|
---------------------------------------------------------------------------------------------------- |
||||||
|
|
||||||
|
function clearAttachments() |
||||||
|
local player = GetPlayerEntity(); |
||||||
|
local pet = GetEntity(player.PetTargetIndex); |
||||||
|
|
||||||
|
if(pet == nil) then |
||||||
|
print ("Clearing Attachments"); |
||||||
|
slots = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; |
||||||
|
for slot,id in pairs(currentAttachments) do |
||||||
|
slots[slot] = id; |
||||||
|
end |
||||||
|
slots[1] = 0x00; |
||||||
|
slots[2] = 0x00; |
||||||
|
local unattach = struct.pack('I2I2BBBBBBI2BBBBBBBBBBBBBB', 0x5302, 0x0000, 0x00, 0x00, 0x01, 0x00, 0x12, pupSub, 0x0000, slots[1],slots[2],slots[3],slots[4],slots[5],slots[6],slots[7],slots[8],slots[9],slots[10],slots[11],slots[12],slots[13],slots[14]):totable(); |
||||||
|
table.insert(attachmentQueue, { 0x102, unattach}); |
||||||
|
else |
||||||
|
print("Puppet still out please despawn to unequip attachments.") |
||||||
|
end |
||||||
|
end; |
||||||
|
|
||||||
|
---------------------------------------------------------------------------------------------------- |
||||||
|
-- desc: Pup attachment struct.packing. |
||||||
|
---------------------------------------------------------------------------------------------------- |
||||||
|
|
||||||
|
function load_pupatt(attachmentSet) |
||||||
|
local player = GetPlayerEntity(); |
||||||
|
local pet = GetEntity(player.PetTargetIndex); |
||||||
|
local MainJob = AshitaCore:GetDataManager():GetPlayer():GetMainJob(); |
||||||
|
local SubJob = AshitaCore:GetDataManager():GetPlayer():GetSubJob(); |
||||||
|
|
||||||
|
if ((MainJob == 18 or SubJob == 18)and pet == nil) then |
||||||
|
if (SubJob == 18) then |
||||||
|
local pupSub = 0x01; |
||||||
|
end |
||||||
|
for slot,item in ipairs(attachmentSet) do |
||||||
|
addAttachment(slot,item); |
||||||
|
end |
||||||
|
else |
||||||
|
print("Puppet is still out please please despawn pet to make changes to attachments.") |
||||||
|
end |
||||||
|
end; |
||||||
|
|
||||||
|
---------------------------------------------------------------------------------------------------- |
||||||
|
-- desc: Despawns your pet based on cooldowns. |
||||||
|
---------------------------------------------------------------------------------------------------- |
||||||
|
|
||||||
|
function despawn_pet() |
||||||
|
local recastTimerDeactivate = ashita.ffxi.recast.get_ability_recast_by_id(208); |
||||||
|
local player = GetPlayerEntity(); |
||||||
|
local pet = GetEntity(player.PetTargetIndex); |
||||||
|
|
||||||
|
if (recastTimerDeactivate == 0 and pet ~= nil) then |
||||||
|
AshitaCore:GetChatManager():QueueCommand('/ja "Deactivate" <me>' , 1); |
||||||
|
elseif (recastTimerDeactivate > 0 and pet ~= nil) then |
||||||
|
print('<<Pupatt: Deactivate is not ready yet please try again later>>') |
||||||
|
end |
||||||
|
end; |
||||||
|
|
||||||
|
---------------------------------------------------------------------------------------------------- |
||||||
|
-- desc: Summons your pet based on cooldowns and zone_id. |
||||||
|
---------------------------------------------------------------------------------------------------- |
||||||
|
|
||||||
|
function Summon_pet() |
||||||
|
local recastTimerActivate = ashita.ffxi.recast.get_ability_recast_by_id(205); |
||||||
|
local recastTimerdeusex = ashita.ffxi.recast.get_ability_recast_by_id(115); |
||||||
|
local zone_id = AshitaCore:GetDataManager():GetParty():GetMemberZone(0); |
||||||
|
local player = GetPlayerEntity(); |
||||||
|
local pet = GetEntity(player.PetTargetIndex); |
||||||
|
|
||||||
|
if (pet == nil) then |
||||||
|
if contains(petlessZones, zone_id) then |
||||||
|
print("You are in a zone that dose not allow pets.") |
||||||
|
else |
||||||
|
if (recastTimerActivate == 0) then |
||||||
|
print("<<Pupatt: Using Activate>>") |
||||||
|
AshitaCore:GetChatManager():QueueCommand('/ja "Activate" <me>' , 1); |
||||||
|
elseif(recastTimerdeusex == 0) then |
||||||
|
print('<<Pupatt: Activate is not ready using Deus Ex Automata>>') |
||||||
|
AshitaCore:GetChatManager():QueueCommand('/ja "Deus Ex Automata" <me>' , 1); |
||||||
|
elseif(recastTimerdeusex > 0 and recastTimerdeusex > 0) then |
||||||
|
print('<<Pupatt: Activate and Deus Ex Automata is not ready yet please try again later>>') |
||||||
|
end |
||||||
|
end |
||||||
|
else |
||||||
|
print("Your puppet is already out") |
||||||
|
end |
||||||
|
end; |
||||||
|
|
||||||
|
---------------------------------------------------------------------------------------------------- |
||||||
|
-- func: process_queue |
||||||
|
-- desc: Processes the packet queue to be sent. |
||||||
|
---------------------------------------------------------------------------------------------------- |
||||||
|
|
||||||
|
function process_queue() |
||||||
|
if (os.clock() >= (objTimer + objDelay)) then |
||||||
|
objTimer = os.clock(); |
||||||
|
|
||||||
|
-- Ensure the queue has something to process.. |
||||||
|
if (#attachmentQueue > queOffset) then |
||||||
|
queOffset = 0 |
||||||
|
inProgress = true |
||||||
|
-- Obtain the first queue entry.. |
||||||
|
local data = table.remove(attachmentQueue, 1); |
||||||
|
|
||||||
|
-- Send the queued object.. |
||||||
|
--print("Sending packet #"..(#attachmentQueue + 1)) |
||||||
|
AddOutgoingPacket(data[1], data[2]); |
||||||
|
elseif (#attachmentQueue == queOffset and inProgress == true) then |
||||||
|
print("Attachment change completed") |
||||||
|
inProgress = false |
||||||
|
queOffset = 1 |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
---------------------------------------------------------------------------------------------------- |
||||||
|
-- func: render |
||||||
|
-- desc: Event called when the addon is being rendered. |
||||||
|
---------------------------------------------------------------------------------------------------- |
||||||
|
|
||||||
|
ashita.register_event('render', function() |
||||||
|
-- Process the objectives packet queue.. |
||||||
|
process_queue(); |
||||||
|
attFromMemory(); |
||||||
|
end); |
||||||
|
|
||||||
|
--------------------------------------------------------------------------------------------------- |
||||||
|
-- func: load_pupattSettings |
||||||
|
-- desc: load pup attachments from a file and sets currentattachment equip. |
||||||
|
--------------------------------------------------------------------------------------------------- |
||||||
|
|
||||||
|
function load_pupattSettings() |
||||||
|
local tempCommands = ashita.settings.load(_addon.path .. '/settings/pupattProfiles.json'); |
||||||
|
|
||||||
|
if tempCommands ~= nil then |
||||||
|
print('Stored objective profiles found.'); |
||||||
|
pupattProfiles = tempCommands; |
||||||
|
else |
||||||
|
print('pupatt profiles could not be loaded. Creating empty lists.'); |
||||||
|
pupattProfiles = { }; |
||||||
|
end |
||||||
|
end; |
||||||
|
|
||||||
|
--------------------------------------------------------------------------------------------------- |
||||||
|
-- func: new_profile |
||||||
|
-- desc: Creates new profile with current objectives |
||||||
|
--------------------------------------------------------------------------------------------------- |
||||||
|
|
||||||
|
function new_profile(profileName) |
||||||
|
print("Saving current attachments to profile " .. profileName) |
||||||
|
newProfile = {} |
||||||
|
for k,v in pairs (currentAttachments) do |
||||||
|
convv = string.format("0x%X",v) |
||||||
|
if (__debug) then |
||||||
|
print(string.format("slot id and attachmentId: %d, 0x%X", k, v)); |
||||||
|
print(convk) |
||||||
|
print(convv) |
||||||
|
end |
||||||
|
table.insert(newProfile, convv) |
||||||
|
end |
||||||
|
pupattProfiles[profileName] = newProfile; |
||||||
|
end; |
||||||
|
|
||||||
|
--------------------------------------------------------------------------------------------------- |
||||||
|
-- func: list_profiles |
||||||
|
-- desc: Lists saved profiles |
||||||
|
--------------------------------------------------------------------------------------------------- |
||||||
|
|
||||||
|
function list_profiles() |
||||||
|
print("Current Profiles:\n") |
||||||
|
printProfiles = ashita.settings.JSON:encode_pretty(pupattProfiles, nil, {pretty = true, indent = "-> " }); |
||||||
|
print(printProfiles); |
||||||
|
end; |
||||||
|
|
||||||
|
--------------------------------------------------------------- |
||||||
|
-- Prepairs, reads and stores the current attachment for 1st time use. |
||||||
|
--------------------------------------------------------------- |
||||||
|
|
||||||
|
function attFromMemory() |
||||||
|
if (currentAttachments == nil) then |
||||||
|
local pointer1 = ashita.memory.findpattern('FFXiMain.dll', 0, 'C1E1032BC8B0018D????????????B9????????F3A55F5E5B', 10, 0); |
||||||
|
if (pointer1 == 0) then |
||||||
|
err('Failed to locate current attachments, please cycle a attachment to continue.'); |
||||||
|
else |
||||||
|
local offset1 = ashita.memory.read_uint32(pointer1); |
||||||
|
pointer = ashita.memory.read_uint32(AshitaCore:GetPointerManager():GetPointer('inventory')); |
||||||
|
pointer = ashita.memory.read_uint32(pointer); |
||||||
|
currentAttachments = ashita.memory.read_array((pointer + offset1) + offset, 0x0E); |
||||||
|
if (currentAttachments ~= nil) then |
||||||
|
for i = 1, 14 do |
||||||
|
currentAttachments[i] = string.format("0x%X" , currentAttachments[i]); |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end; |
||||||
|
|
||||||
|
--------------------------------------------------------------------------------------------------- |
||||||
|
-- func: save_pupattProfile |
||||||
|
-- desc: saves current pup attachment profiles to a file |
||||||
|
--------------------------------------------------------------------------------------------------- |
||||||
|
|
||||||
|
function save_profiles() |
||||||
|
print("Writing saved profiles to file settings/pupattProfiles.json"); |
||||||
|
-- Save the addon settings to a file (from the addonSettings table) |
||||||
|
ashita.settings.save(_addon.path .. '/settings' .. '/pupattProfiles.json' , pupattProfiles); |
||||||
|
end; |
||||||
|
|
||||||
|
---------------------------------------------------------------------------------------------------- |
||||||
|
-- func: print_help |
||||||
|
-- desc: Displays a help block for proper command usage. |
||||||
|
---------------------------------------------------------------------------------------------------- |
||||||
|
|
||||||
|
local function print_help(cmd, help) |
||||||
|
-- Print the invalid format header.. |
||||||
|
print('\31\200[\31\05' .. _addon.name .. '\31\200]\30\01 ' .. '\30\68Invalid format for command:\30\02 ' .. cmd .. '\30\01'); |
||||||
|
|
||||||
|
-- Loop and print the help commands.. |
||||||
|
for k, v in pairs(help) do |
||||||
|
print('\31\200[\31\05' .. _addon.name .. '\31\200]\30\01 ' .. '\30\68Syntax:\30\02 ' .. v[1] .. '\30\71 ' .. v[2]); |
||||||
|
end |
||||||
|
end; |
||||||
|
|
||||||
|
---------------------------------------------------------------------------------------------------- |
||||||
|
-- func: user_commands |
||||||
|
-- desc: User commands to help interact with the program. |
||||||
|
---------------------------------------------------------------------------------------------------- |
||||||
|
|
||||||
|
ashita.register_event('command', function(command, ntype) |
||||||
|
-- Get the arguments of the command.. |
||||||
|
local args = command:args(); |
||||||
|
|
||||||
|
if (args[1] ~= '/pupatt') then |
||||||
|
return false; |
||||||
|
end |
||||||
|
|
||||||
|
if (#args == 3 and args[2] == 'newprofile') then |
||||||
|
new_profile(args[3]) |
||||||
|
return true; |
||||||
|
end |
||||||
|
|
||||||
|
if (#args >= 2 and args[2] == 'list') then |
||||||
|
list_profiles() |
||||||
|
return true; |
||||||
|
end |
||||||
|
|
||||||
|
if (#args >= 2 and args[2] == 'current') then |
||||||
|
currentAtt = ashita.settings.JSON:encode_pretty(currentAttachments, nil, {pretty = true, indent = "-> " }); |
||||||
|
print(currentAtt); |
||||||
|
return true; |
||||||
|
end |
||||||
|
|
||||||
|
if (#args >= 2 and args[2] == 'save') then |
||||||
|
save_profiles() |
||||||
|
return true; |
||||||
|
end |
||||||
|
|
||||||
|
if (#args >= 2 and args[2] == 'clear') then |
||||||
|
clearAttachments() |
||||||
|
return true; |
||||||
|
end |
||||||
|
|
||||||
|
if (#args >= 2 and args[2] == 'spawn') then |
||||||
|
Summon_pet() |
||||||
|
return true; |
||||||
|
end |
||||||
|
|
||||||
|
if (#args >= 2 and args[2] == 'despawn') then |
||||||
|
despawn_pet() |
||||||
|
return true; |
||||||
|
end |
||||||
|
|
||||||
|
if (#args >= 2 and args[2] == 'load') then |
||||||
|
print("Loading " .. args[3]); |
||||||
|
if pupattProfiles[args[3]] then |
||||||
|
ashita.timer.once(1,clearAttachments); |
||||||
|
ashita.timer.once(2,load_pupatt,pupattProfiles[args[3]]); |
||||||
|
else |
||||||
|
print (args[3] .. " profile not found"); |
||||||
|
end |
||||||
|
return true; |
||||||
|
end |
||||||
|
|
||||||
|
-- Prints the addon help.. |
||||||
|
print_help('/pupatt', { |
||||||
|
{'/pupatt load profileName', ' - Loads a saved profile from settings. '}, |
||||||
|
{'/pupatt newprofile profileName', ' - creates a new profile.'}, |
||||||
|
{'/pupatt save', ' - Saves the new profile created.'}, |
||||||
|
{'/pupatt list', ' - Lists the profiles as well as the hex values for each attachment.'}, |
||||||
|
{'/pupatt current', ' - Lists the current attachments hex values.'}, |
||||||
|
{'/pupatt clear', ' - Clears the current attachments.'}, |
||||||
|
{'/pupatt spawn', ' - Spawns the puppet using whatever is off cooldown.'}, |
||||||
|
{'/pupatt despawn', ' - despawns the puppet.'}, |
||||||
|
}); |
||||||
|
return true; |
||||||
|
end); |
Loading…
Reference in new issue