function parse_action_packet(act) -- Make a function that returns the action array with additional information -- actor : type, name, is_npc -- target : type, name, is_npc if not Self then Self = windower.ffxi.get_player() if not Self then return act end end act.actor = player_info(act.actor_id) act.action = get_spell(act) -- Pulls the resources line for the action act.actor.name = act.actor and act.actor.name and string.gsub(act.actor.name,'[- ]', {['-'] = string.char(0x81,0x7C), [' '] = string.char(0x81,0x3F)}) --fix for ffxi chat splits on trusts with - and spaces targets_condensed = false if not act.action then return act end for i,v in ipairs(act.targets) do v.target = {} v.target[1] = player_info(v.id) if #v.actions > 1 then for n,m in ipairs(v.actions) do if res.action_messages[m.message] then m.fields = fieldsearch(res.action_messages[m.message][language]) end if res.action_messages[m.add_effect_message] then m.add_effect_fields = fieldsearch(res.action_messages[m.add_effect_message][language]) end if res.action_messages[m.spike_effect_message] then m.spike_effect_fields = fieldsearch(res.action_messages[m.spike_effect_message][language]) end if res.buffs[m.param] then --and m.param ~= 0 then m.status = res.buffs[m.param][language] end if res.buffs[m.add_effect_param] then -- and m.add_effect_param ~= 0 then m.add_effect_status = res.buffs[m.add_effect_param][language] end if res.buffs[m.spike_effect_param] then -- and m.spike_effect_param ~= 0 then m.spike_effect_status = res.buffs[m.spike_effect_param][language] end m.number = 1 if m.has_add_effect then m.add_effect_number = 1 end if m.has_spike_effect then m.spike_effect_number = 1 end if not check_filter(act.actor,v.target[1],act.category,m.message) then m.message = 0 m.add_effect_message = 0 end if m.spike_effect_message ~= 0 and not check_filter(v.target[1],act.actor,act.category,m.message) then m.spike_effect_message = 0 end if condensedamage and n > 1 then -- Damage/Action condensation within one target for q=1,n-1 do local r = v.actions[q] if r.message ~= 0 and m.message ~= 0 then if m.message == r.message or (condensecrits and S{1,67}:contains(m.message) and S{1,67}:contains(r.message)) then if (m.effect == r.effect) or (S{1,67}:contains(m.message) and S{0,1,2,3}:contains(m.effect) and S{0,1,2,3}:contains(r.effect)) then -- combine kicks and crits if m.reaction == r.reaction then --or (S{8,10}:contains(m.reaction) and S{8,10}:contains(r.reaction)) then -- combine hits and guards -- windower.add_to_chat(8, 'Condensed: '..m.message..':'..r.message..' - '..m.effect..':'..r.effect..' - '..m.reaction..':'..r.reaction) r.number = r.number + 1 if not sumdamage then if not r.cparam then r.cparam = r.param if condensecrits and r.message == 67 then r.cparam = r.cparam..'!' end end r.cparam = r.cparam..', '..m.param if condensecrits and m.message == 67 then r.cparam = r.cparam..'!' end end r.param = m.param + r.param if condensecrits and m.message == 67 then r.message = m.message r.effect = m.effect end m.message = 0 else -- windower.add_to_chat(8, 'Didn\'t condense: '..m.message..':'..r.message..' - '..m.effect..':'..r.effect..' - '..m.reaction..':'..r.reaction) end else -- windower.add_to_chat(8, 'Didn\'t condense: '..m.message..':'..r.message..' - '..m.effect..':'..r.effect..' - '..m.reaction..':'..r.reaction) end else -- windower.add_to_chat(8, 'Didn\'t condense: '..m.message..':'..r.message..' - '..m.effect..':'..r.effect..' - '..m.reaction..':'..r.reaction) end end if m.has_add_effect and r.add_effect_message ~= 0 then if m.add_effect_effect == r.add_effect_effect and m.add_effect_message == r.add_effect_message and m.add_effect_message ~= 0 then r.add_effect_number = r.add_effect_number + 1 if not sumdamage then r.cadd_effect_param = (r.cadd_effect_param or r.add_effect_param)..', '..m.add_effect_param end r.add_effect_param = m.add_effect_param + r.add_effect_param m.add_effect_message = 0 end end if m.has_spike_effect and r.spike_effect_message ~= 0 then if r.spike_effect_effect == r.spike_effect_effect and m.spike_effect_message == r.spike_effect_message and m.spike_effect_message ~= 0 then r.spike_effect_number = r.spike_effect_number + 1 if not sumdamage then r.cspike_effect_param = (r.cspike_effect_param or r.spike_effect_param)..', '..m.spike_effect_param end r.spike_effect_param = m.spike_effect_param + r.spike_effect_param m.spike_effect_message = 0 end end end end end else local tempact = v.actions[1] if res.action_messages[tempact.message] then tempact.fields = fieldsearch(res.action_messages[tempact.message][language]) end if res.action_messages[tempact.add_effect_message] then tempact.add_effect_fields = fieldsearch(res.action_messages[tempact.add_effect_message][language]) end if res.action_messages[tempact.spike_effect_message] then tempact.spike_effect_fields = fieldsearch(res.action_messages[tempact.spike_effect_message][language]) end --if tempact.add_effect_fields and tempact.add_effect_fields.status then windower.add_to_chat(8,tostring(tempact.add_effect_fields.status)..' '..res.action_messages[tempact.add_effect_message][language]) end if not check_filter(act.actor,v.target[1],act.category,tempact.message) then tempact.message = 0 tempact.add_effect_message = 0 end if tempact.spike_effect_message ~= 0 and not check_filter(v.target[1],act.actor,act.category,tempact.message) then tempact.spike_effect_message = 0 end tempact.number = 1 if tempact.has_add_effect and tempact.message ~= 674 then tempact.add_effect_number = 1 end if tempact.has_spike_effect then tempact.spike_effect_number = 1 end if res.buffs[tempact.param] then -- and tempact.param ~= 0 then tempact.status = res.buffs[tempact.param][language] end if res.buffs[tempact.add_effect_param] then -- and tempact.add_effect_param ~= 0 then tempact.add_effect_status = res.buffs[tempact.add_effect_param][language] end if res.buffs[tempact.spike_effect_param] then -- and tempact.spike_effect_param ~= 0 then tempact.spike_effect_status = res.buffs[tempact.spike_effect_param][language] end end if condensetargets and i > 1 then for n=1,i-1 do local m = act.targets[n] --windower.add_to_chat(8,m.actions[1].message..' '..v.actions[1].message) if (v.actions[1].message == m.actions[1].message and v.actions[1].param == m.actions[1].param) or (message_map[m.actions[1].message] and message_map[m.actions[1].message]:contains(v.actions[1].message) and v.actions[1].param == m.actions[1].param) or (message_map[m.actions[1].message] and message_map[m.actions[1].message]:contains(v.actions[1].message) and v.actions[1].param == m.actions[1].param) then m.target[#m.target+1] = v.target[1] v.target[1] = nil v.actions[1].message = 0 end end end end for i,v in pairs(act.targets) do for n,m in pairs(v.actions) do if m.message ~= 0 and res.action_messages[m.message] ~= nil then local col = res.action_messages[m.message].color local targ = assemble_targets(act.actor,v.target,act.category,m.message) local color = color_filt(col,v.target[1].id==Self.id) if m.reaction == 11 and act.category == 1 then m.simp_name = 'parried by' --elseif m.reaction == 12 and act.category == 1 then m.simp_name = 'blocked by' elseif m.message == 1 and (act.category == 1 or act.category == 11) then m.simp_name = 'hit' elseif m.message == 15 then m.simp_name = 'missed' elseif m.message == 29 or m.message == 84 then m.simp_name = 'is paralyzed' elseif m.message == 30 then m.simp_name = 'anticipated by' elseif m.message == 31 then m.simp_name = 'absorbed by' elseif m.message == 32 then m.simp_name = 'dodged by' elseif m.message == 67 and (act.category == 1 or act.category == 11) then m.simp_name = 'critical hit' elseif m.message == 106 then m.simp_name = 'intimidated by' elseif m.message == 153 then m.simp_name = act.action.name..' fails' elseif m.message == 244 then m.simp_name = 'Mug fails' elseif m.message == 282 then m.simp_name = 'evaded by' elseif m.message == 373 then m.simp_name = 'absorbed by' elseif m.message == 352 then m.simp_name = 'RA' elseif m.message == 353 then m.simp_name = 'critical RA' elseif m.message == 354 then m.simp_name = 'missed RA' elseif m.message == 576 then m.simp_name = 'RA hit squarely' elseif m.message == 577 then m.simp_name = 'RA struck true' elseif m.message == 157 then m.simp_name = 'Barrage' elseif m.message == 76 then m.simp_name = 'No targets within range' elseif m.message == 77 then m.simp_name = 'Sange' elseif m.message == 360 then m.simp_name = act.action.name..' (JA reset)' elseif m.message == 426 or m.message == 427 then m.simp_name = 'Bust! '..act.action.name elseif m.message == 435 or m.message == 436 then m.simp_name = act.action.name..' (JAs)' elseif m.message == 437 or m.message == 438 then m.simp_name = act.action.name..' (JAs and TP)' elseif m.message == 439 or m.message == 440 then m.simp_name = act.action.name..' (SPs, JAs, TP, and MP)' elseif T{252,265,268,269,271,272,274,275,379,650,747}:contains(m.message) then m.simp_name = 'Magic Burst! '..act.action.name elseif not act.action then m.simp_name = '' act.action = {} else m.simp_name = act.action.name or '' end -- Debuff Application Messages if simplify and message_map[82]:contains(m.message) then if m.status == 'Evasion Down' then m.message = 237 end if m.status == 'addle' then m.status = 'addled' elseif m.status == 'bind' then m.status = 'bound' elseif m.status == 'blindness' then m.status = 'blinded' elseif m.status == 'Inundation' then m.status = 'inundated' elseif m.status == 'paralysis' then m.status = 'paralyzed' elseif m.status == 'petrification' then m.status = 'petrified' elseif m.status == 'poison' then m.status = 'poisoned' elseif m.status == 'silence' then m.status = 'silenced' elseif m.status == 'sleep' then m.status = 'asleep' elseif m.status == 'slow' then m.status = 'slowed' elseif m.status == 'stun' then m.status = 'stunned' elseif m.status == 'weight' then m.status = 'weighed down' end end -- Some messages uses the english log version of the buff if not simplify and log_form_messages:contains(m.message) then m.status = res.buffs[m.param].enl end -- if m.message == 93 or m.message == 273 then m.status=color_it('Vanish',color_arr['statuscol']) end -- Special Message Handling if m.message == 93 or m.message == 273 then m.status=color_it('Vanish',color_arr['statuscol']) elseif m.message == 522 and simplify then targ = targ..' ('..color_it('stunned',color_arr['statuscol'])..')' elseif m.message == 416 and simplify then targ = targ..' ('..color_it('Magic Attack Boost and Magic Defense Boost',color_arr['statuscol'])..')' elseif m.message == 1023 and simplify then targ = targ..' ('..color_it('attacks and defenses enhanced',color_arr['statuscol'])..')' elseif m.message == 762 and simplify then targ = targ..' ('..color_it('all status parameters boosted',color_arr['statuscol'])..')' elseif m.message == 779 and simplify then targ = 'A barrier pulsates around '..targ elseif m.message == 780 and simplify then targ = 'Takes aim on '..targ elseif T{158,188,245,324,592,658}:contains(m.message) and simplify then -- When you miss a WS or JA. Relevant for condensed battle. m.status = 'Miss' --- This probably doesn't work due to the if a==nil statement below. elseif m.message == 653 or m.message == 654 then m.status = color_it('Immunobreak',color_arr['statuscol']) elseif m.message == 655 or m.message == 656 then m.status = color_it('Completely Resists',color_arr['statuscol']) elseif m.message == 85 or m.message == 284 then if m.unknown == 2 then m.status = color_it('Resists!',color_arr['statuscol']) else m.status = color_it('Resists',color_arr['statuscol']) end elseif m.message == 351 then m.status = color_it('status ailments',color_arr['statuscol']) m.simp_name = color_it('remedy',color_arr['itemcol']) elseif T{75,114,156,189,248,283,312,323,336,355,408,422,423,425,659}:contains(m.message) then m.status = color_it('No effect',color_arr['statuscol']) -- The status code for "No Effect" is 255, so it might actually work without this line end if m.message == 188 then m.simp_name = m.simp_name..' (Miss)' -- elseif m.message == 189 then -- m.simp_name = m.simp_name..' (No Effect)' elseif T{78,198,328}:contains(m.message) then m.simp_name = '(Too Far)' end local msg,numb = simplify_message(m.message) if not color_arr[act.actor.owner or act.actor.type] then windower.add_to_chat(123,'Battlemod error, missing filter:'..tostring(act.actor.owner)..' '..tostring(act.actor.type)) end if m.fields.status then numb = m.status else numb = pref_suf((m.message == 674 and m.add_effect_param or m.cparam or m.param),m.message,act.actor.damage,col) end if msg and m.message == 70 and not simplify then -- fix pronoun on parry if v.target[1].race == 0 then msg = msg:gsub(' his ',' its ') elseif female_races:contains(v.target[1].race) then msg = msg:gsub(' his ',' her ') end end local count = '' if m.message == 377 and act.actor_id == Self.id then parse_quantity = true item_quantity.id = act.action.item2_id count = '${count}' end if not simplify then if col == 'D' or grammar_numb_msg:contains(m.message) then msg = grammatical_number_fix(msg, (m.cparam or m.param), m.message) end if act.action.item_id or act.action.item2_id then msg = item_article_fix(act.action.item_id,act.action.item2_id,msg) end if common_nouns:contains(act.actor.id) then msg = actor_noun(msg) end if plural_entities:contains(act.actor.id) then msg = plural_actor(msg, m.message) end if targets_condensed or plural_entities:contains(v.target[1].id) then msg = plural_target(msg, m.message) end end local roll = showrollinfo and act.category == 6 and corsair_rolls[act.param] and corsair_rolls[act.param][m.param] or '' local reaction_lookup = reaction_offsets[act.category] and (m.reaction - reaction_offsets[act.category]) or 0 local has_line_break = string.find(res.action_messages[m.message].en, '${lb}') and true or false local prefix = (not has_line_break or simplify) and get_prefix(act.category, m.effect, m.message, m.unknown, reaction_lookup) or '' local prefix2 = has_line_break and get_prefix(act.category, m.effect, m.message, m.unknown, reaction_lookup) or '' local message = prefix..make_condensedamage_number(m.number)..( clean_msg((msg or tostring(m.message)) :gsub('${spell}',color_it(act.action.spell or 'ERROR 111',color_arr.spellcol)) :gsub('${ability}',color_it(act.action.ability or 'ERROR 112',color_arr.abilcol)) :gsub('${item}',color_it(act.action.item or 'ERROR 113',color_arr.itemcol)) :gsub('${item2}',count..color_it(act.action.item2 or 'ERROR 121',color_arr.itemcol)) :gsub('${weapon_skill}',color_it(act.action.weapon_skill or 'ERROR 114',color_arr.wscol)) :gsub('${abil}',m.simp_name or 'ERROR 115') :gsub('${numb}',numb..roll or 'ERROR 116') :gsub('${actor}\'s',color_it(act.actor.name or 'ERROR 117',color_arr[act.actor.owner or act.actor.type])..'\'s'..act.actor.owner_name) :gsub('${actor}',color_it(act.actor.name or 'ERROR 117',color_arr[act.actor.owner or act.actor.type])..act.actor.owner_name) :gsub('${target}\'s',targ) :gsub('${target}',targ) :gsub('${lb}','\7'..prefix2) :gsub('${number}',(act.action.number or m.param)..roll) :gsub('${status}',m.status or 'ERROR 120') :gsub('${gil}',m.param..' gil'), m.message)) if m.message == 377 and act.actor_id == Self.id then send_delayed_message:schedule(0.5,color,message) else windower.add_to_chat(color,message) end if not non_block_messages:contains(m.message) then m.message = 0 end end if m.has_add_effect and m.add_effect_message ~= 0 and add_effect_valid[act.category] then local targ = assemble_targets(act.actor,v.target,act.category,m.add_effect_message) local col = res.action_messages[m.add_effect_message].color local color = color_filt(col,v.target[1].id==Self.id) if m.add_effect_message > 287 and m.add_effect_message < 303 then m.simp_add_name = skillchain_arr[m.add_effect_message-287] elseif m.add_effect_message > 384 and m.add_effect_message < 399 then m.simp_add_name = skillchain_arr[m.add_effect_message-384] elseif m.add_effect_message > 766 and m.add_effect_message < 769 then m.simp_add_name = skillchain_arr[m.add_effect_message-752] elseif m.add_effect_message > 768 and m.add_effect_message < 771 then m.simp_add_name = skillchain_arr[m.add_effect_message-754] elseif m.add_effect_message == 603 then m.simp_add_name = 'AE: TH' elseif m.add_effect_message == 605 then m.simp_add_name = 'AE: Death' elseif m.add_effect_message == 776 then m.simp_add_name = 'AE: Chainbound' else m.simp_add_name = 'AE' end local msg,numb = simplify_message(m.add_effect_message) if not simplify then if col == 'D' or grammar_numb_msg:contains(m.add_effect_message) then msg = grammatical_number_fix(msg, (m.cparam or m.param), m.add_effect_message) end if common_nouns:contains(act.actor.id) then msg = actor_noun(msg) end if plural_entities:contains(act.actor.id) then msg = plural_actor(msg, m.add_effect_message) end if targets_condensed or plural_entities:contains(v.target[1].id) then msg = plural_target(msg, m.add_effect_message) end end if m.add_effect_fields.status then numb = m.add_effect_status else numb = pref_suf((m.cadd_effect_param or m.add_effect_param),m.add_effect_message,act.actor.damage,col) end if not act.action then -- windower.add_to_chat(color, 'act.action==nil : '..m.message..' - '..m.add_effect_message..' - '..msg) else windower.add_to_chat(color,make_condensedamage_number(m.add_effect_number)..(clean_msg(msg :gsub('${spell}',act.action.spell or 'ERROR 127') :gsub('${ability}',act.action.ability or 'ERROR 128') :gsub('${item}',act.action.item or 'ERROR 129') :gsub('${weapon_skill}',act.action.weapon_skill or 'ERROR 130') :gsub('${abil}',m.simp_add_name or act.action.name or 'ERROR 131') :gsub('${numb}',numb or 'ERROR 132') :gsub('${actor}\'s',color_it(act.actor.name,color_arr[act.actor.owner or act.actor.type])..'\'s'..act.actor.owner_name) :gsub('${actor}',color_it(act.actor.name,color_arr[act.actor.owner or act.actor.type])..act.actor.owner_name) :gsub('${target}\'s',targ) :gsub('${target}',targ) :gsub('${lb}','\7') :gsub('${number}',m.add_effect_param) :gsub('${status}',m.add_effect_status or 'ERROR 178'), m.add_effect_message))) if not non_block_messages:contains(m.add_effect_message) then m.add_effect_message = 0 end end end if m.has_spike_effect and m.spike_effect_message ~= 0 and spike_effect_valid[act.category] then local targ = assemble_targets(act.actor,v.target,act.category,m.spike_effect_message) local col = res.action_messages[m.spike_effect_message].color local color = color_filt(col,act.actor.id==Self.id) local actor = act.actor if m.spike_effect_message == 14 then m.simp_spike_name = 'from counter' elseif T{33,606}:contains(m.spike_effect_message) then m.simp_spike_name = 'counter' actor = v.target[1] --Counter dmg is done by the target, fix for coloring the dmg elseif m.spike_effect_message == 592 then m.simp_spike_name = 'missed counter' elseif m.spike_effect_message == 536 then m.simp_spike_name = 'retaliation' actor = v.target[1] --Retaliation dmg is done by the target, fix for coloring the dmg elseif m.spike_effect_message == 535 then m.simp_spike_name = 'from retaliation' else m.simp_spike_name = 'spikes' actor = v.target[1] --Spikes dmg is done by the target, fix for coloring the dmg end local msg = simplify_message(m.spike_effect_message) if not simplify then if col == 'D' or grammar_numb_msg:contains(m.spike_effect_message) then msg = grammatical_number_fix(msg, (m.cparam or m.param), m.spike_effect_message) end if common_nouns:contains(act.actor.id) then msg = actor_noun(msg) end if plural_entities:contains(act.actor.id) then msg = plural_actor(msg, m.spike_effect_message) end if targets_condensed or plural_entities:contains(v.target[1].id) then msg = plural_target(msg, m.spike_effect_message) end end if m.spike_effect_fields.status then numb = m.spike_effect_status else numb = pref_suf((m.cspike_effect_param or m.spike_effect_param),m.spike_effect_message,actor.damage,col) end windower.add_to_chat(color,make_condensedamage_number(m.spike_effect_number)..(clean_msg(msg :gsub('${spell}',act.action.spell or 'ERROR 142') :gsub('${ability}',act.action.ability or 'ERROR 143') :gsub('${item}',act.action.item or 'ERROR 144') :gsub('${weapon_skill}',act.action.weapon_skill or 'ERROR 145') :gsub('${abil}',m.simp_spike_name or act.action.name or 'ERROR 146') :gsub('${numb}',numb or 'ERROR 147') :gsub('${actor}\'s',color_it(act.actor.name,color_arr[act.actor.owner or act.actor.type])..'\'s'..act.actor.owner_name) :gsub((simplify and '${target}' or '${actor}'),color_it(act.actor.name,color_arr[act.actor.owner or act.actor.type])..act.actor.owner_name) :gsub('${target}\'s',targ) :gsub((simplify and '${actor}' or '${target}'),targ) :gsub('${lb}','\7') :gsub('${number}',m.spike_effect_param) :gsub('${status}',m.spike_effect_status or 'ERROR 150'), m.spike_effect_message))) if not non_block_messages:contains(m.spike_effect_message) then m.spike_effect_message = 0 end end end end return act end function pref_suf(param,msg_ID,actor_dmg,col) local outstr = (col == 'D' or dmg_drain_msg:contains(msg_ID)) and color_it(tostring(param),color_arr[actor_dmg]) or tostring(param) local msg = res.action_messages[msg_ID] or nil if msg then if msg.prefix then outstr = msg.prefix..' '..outstr end if msg.suffix then if msg.suffix == 'shadow' and param ~= 1 then outstr = outstr..' shadows' elseif msg.suffix == 'Petra' and param ~= 1 then outstr = outstr..' Petras' elseif msg.suffix == 'effects disappears' and param ~= 1 then outstr = outstr..' effects disappear' elseif msg_ID == 641 then outstr = outstr..' 1 attribute drained' elseif msg.suffix == 'attributes drained' and param == 1 then outstr = outstr..' attribute drained' elseif msg.suffix == 'status effect drained' and param ~= 1 then outstr = outstr..' status effects drained' elseif msg.suffix == 'status ailments disappears' and param ~= 1 then outstr = outstr..' status ailments disappear' elseif msg.suffix == 'status ailments absorbed' and param == 1 then outstr = outstr..' status ailment absorbed' elseif msg.suffix == 'status ailments healed' and param == 1 then outstr = outstr..' status ailment healed' elseif msg.suffix == 'status benefits absorbed' and param == 1 then outstr = outstr..' status benefit absorbed' elseif msg.suffix == 'status effects removed' and param == 1 then outstr = outstr..' status effect removed' elseif msg.suffix == 'magic effects drained' and param == 1 then outstr = outstr..' magic effect drained' elseif msg.suffix == 'magical effects received' and param == 1 then outstr = outstr..' magical effect received' elseif msg.suffix == 'magical effects copied' and param == 1 then outstr = outstr..' magical effect copied' else outstr = outstr..' '..msg.suffix end end end return outstr end function simplify_message(msg_ID) local msg = res.action_messages[msg_ID][language] local fields = fieldsearch(msg) if simplify and not T{23,64,133,204,210,211,212,213,214,350,442,516,531,557,565,582}:contains(msg_ID) then if T{93,273,522,653,654,655,656,85,284,75,114,156,189,248,283,312,323,336,351,355,408,422,423,425,453,659,158,245,324,658}:contains(msg_ID) then fields.status = true end if msg_ID == 31 or msg_ID == 798 or msg_ID == 799 then fields.actor = true end if (msg_ID > 287 and msg_ID < 303) or (msg_ID > 384 and msg_ID < 399) or (msg_ID > 766 and msg_ID < 771) or T{129,152,161,162,163,165,229,384,453,603,652,798}:contains(msg_ID) then fields.ability = true end if T{125,593,594,595,596,597,598,599}:contains(msg_ID) then fields.ability = true fields.item = true end if T{129,152,153,160,161,162,163,164,165,166,167,168,229,244,652}:contains(msg_ID) then fields.actor = true fields.target = true end if msg_ID == 139 then fields.number = true end local Despoil_msg = {[593] = 'Attack Down', [594] = 'Defense Down', [595] = 'Magic Atk. Down', [596] = 'Magic Def. Down', [597] = 'Evasion Down', [598] = 'Accuracy Down', [599] = 'Slow',} if line_full and fields.number and fields.target and fields.actor then msg = line_full elseif line_aoebuff and fields.status and fields.target then --and fields.actor then -- and (fields.spell or fields.ability or fields.item or fields.weapon_skill) then msg = line_aoebuff elseif line_item and fields.item2 then if fields.number then msg = line_itemnum else msg = line_item end elseif line_steal and fields.item and fields.ability then if T{593,594,595,596,597,598,599}:contains(msg_ID) then msg = line_steal..''..string.char(0x07)..'AE: '..color_it(Despoil_msg[msg_ID],color_arr['statuscol']) else msg = line_steal end elseif line_nonumber and not fields.number then msg = line_nonumber elseif line_aoe and T{264}:contains(msg_ID) then msg = line_aoe elseif line_noactor and not fields.actor and (fields.spell or fields.ability or fields.item or fields.weapon_skill) then msg = line_noactor elseif line_noability and not fields.actor then msg = line_noability elseif line_notarget and fields.actor and fields.number then if msg_ID == 798 then --Maneuver message msg = line_notarget..'%' elseif msg_ID == 799 then --Maneuver message with overload msg = line_notarget..'% (${actor} overloaded)' else msg = line_notarget end end end return msg end function assemble_targets(actor,targs,category,msg) local targets = {} local samename = {} local total = 0 for i,v in pairs(targs) do -- Done in two loops so that the ands and commas don't get out of place. -- This loop filters out unwanted targets. if check_filter(actor,v,category,msg) or check_filter(v,actor,category,msg) then if samename[v.name] and condensetargetname then samename[v.name] = samename[v.name] + 1 else targets[#targets+1] = v samename[v.name] = 1 end total = total + 1 end end local out_str if targetnumber and total > 1 then out_str = '{'..total..'}: ' else out_str = '' end for i,v in pairs(targets) do local name = string.gsub(v.name,' ', string.char(0x81,0x3F)) --fix for ffxi chat splits on space local article = common_nouns:contains(v.id) and (not simplify or msg == 206) and 'The ' or '' local numb = condensetargetname and samename[v.name] > 1 and ' {'..samename[v.name]..'}' or '' if i == 1 then name = color_it(name,color_arr[v.owner or v.type])..v.owner_name if samename[v.name] > 1 then targets_condensed = true else if (not simplify or msg == 206) and #targets == 1 and string.find(res.action_messages[msg][language], '${target}\'s') then name = color_it(name,color_arr[v.owner or v.type])..(plural_entities:contains(v.id) and '\'' or '\'s')..v.owner_name end targets_condensed = false end out_str = out_str..article..name..numb else targets_condensed = true name = color_it(name,color_arr[v.owner or v.type])..v.owner_name out_str = conjunctions(out_str,article..name..numb,#targets,i) end end out_str = string.gsub(out_str,'-', string.char(0x81,0x7C)) --fix for ffxi chat splits on trusts with - return out_str end function make_condensedamage_number(number) if swingnumber and condensedamage and 1 < number then return '['..number..'] ' else return '' end end function player_info(id) local player_table = windower.ffxi.get_mob_by_id(id) local typ,dmg,owner,filt,owner_name if player_table == nil then return {name=nil,id=nil,is_npc=nil,type='debug',owner=nil, owner_name=nil,race=nil} end for i,v in pairs(windower.ffxi.get_party()) do if type(v) == 'table' and v.mob and v.mob.id == player_table.id then typ = i if i == 'p0' then filt = 'me' dmg = 'mydmg' elseif i:sub(1,1) == 'p' then filt = 'party' dmg = 'partydmg' else filt = 'alliance' dmg = 'allydmg' end end end if not filt then if player_table.is_npc then if player_table.index>1791 or player_table.charmed then typ = 'other_pets' filt = 'other_pets' owner = 'other' dmg = 'otherdmg' for i,v in pairs(windower.ffxi.get_party()) do if type(v) == 'table' and v.mob and v.mob.pet_index and v.mob.pet_index == player_table.index then if i == 'p0' then typ = 'my_pet' filt = 'my_pet' dmg = 'mydmg' end owner = i owner_name = showownernames and ' ('..color_it(v.mob.name, color_arr[owner or typ])..')' break elseif type(v) == 'table' and v.mob and v.mob.fellow_index and v.mob.fellow_index == player_table.index then if i == 'p0' then typ = 'my_fellow' filt = 'my_fellow' dmg = 'mydmg' end owner = i owner_name = showownernames and ' ('..color_it(v.mob.name, color_arr[owner or typ])..')' break end end else typ = 'mob' filt = 'monsters' dmg = 'mobdmg' if filter.enemies then for i,v in pairs(Self.buffs) do if domain_buffs:contains(v) then -- If you are in Domain Invasion, or a Reive, or various other places -- then all monsters should be considered enemies. filt = 'enemies' break end end if filt ~= 'enemies' then for i,v in pairs(windower.ffxi.get_party()) do if type(v) == 'table' and nf(v.mob,'id') == player_table.claim_id then filt = 'enemies' break end end end end end else typ = 'other' filt = 'others' dmg = 'otherdmg' end end if not typ then typ = 'debug' end return {name=player_table.monstrosity_name or player_table.name,id=id,is_npc = player_table.is_npc,type=typ,damage=dmg,filter=filt,owner=(owner or nil), owner_name=(owner_name or ''),race = player_table.race} end function get_spell(act) local spell, abil_ID, effect_val = {} local msg_ID = act.targets[1].actions[1].message if T{7,8,9}:contains(act['category']) then abil_ID = act.targets[1].actions[1].param elseif T{3,4,5,6,11,13,14,15}:contains(act.category) then abil_ID = act.param effect_val = act.targets[1].actions[1].param end if act.category == 1 then spell.english = 'hit' spell.german = spell.english spell.japanese = spell.english spell.french = spell.english elseif act.category == 2 and act.category == 12 then if msg_ID == 77 then spell = res.job_abilities[171] -- Sange if spell then spell.name = color_it(spell[language],color_arr.abilcol) end elseif msg_ID == 157 then spell = res.job_abilities[60] -- Barrage if spell then spell.name = color_it(spell[language],color_arr.abilcol) end else spell.english = 'Ranged Attack' spell.german = spell.english spell.japanese = spell.english spell.french = spell.english end else if not res.action_messages[msg_ID] then if T{4,8}:contains(act['category']) then spell = res.spells[abil_ID] elseif T{6,14,15}:contains(act['category']) or T{7,13}:contains(act['category']) and false then spell = res.job_abilities[abil_ID] -- May have to correct for charmed pets some day, but I'm not sure there are any monsters with TP moves that give no message. elseif T{3,7,11}:contains(act['category']) then if abil_ID < 256 then spell = res.weapon_skills[abil_ID] -- May have to correct for charmed pets some day, but I'm not sure there are any monsters with TP moves that give no message. else spell = res.monster_abilities[abil_ID] end elseif T{5,9}:contains(act['category']) then spell = res.items[abil_ID] else spell = {none=tostring(msg_ID)} -- Debugging end return spell end local fields = fieldsearch(res.action_messages[msg_ID][language]) if fields.spell then spell = res.spells[abil_ID] if spell then spell.name = color_it(spell[language],color_arr.spellcol) spell.spell = color_it(spell[language],color_arr.spellcol) end elseif fields.ability then spell = res.job_abilities[abil_ID] if spell then spell.name = color_it(spell[language],color_arr.abilcol) spell.ability = color_it(spell[language],color_arr.abilcol) if msg_ID == 139 then spell.number = 'Nothing' end end elseif fields.weapon_skill then if abil_ID > 256 then -- WZ_RECOVER_ALL is used by chests in Limbus spell = res.monster_abilities[abil_ID] if not spell then spell = {english= 'Special Attack'} end elseif abil_ID <= 256 then spell = res.weapon_skills[abil_ID] end if spell then spell.name = color_it(spell[language],color_arr.wscol) spell.weapon_skill = color_it(spell[language],color_arr.wscol) end elseif msg_ID == 303 then spell = res.job_abilities[74] -- Divine Seal if spell then spell.name = color_it(spell[language],color_arr.abilcol) spell.ability = color_it(spell[language],color_arr.abilcol) end elseif msg_ID == 304 then spell = res.job_abilities[75] -- 'Elemental Seal' if spell then spell.name = color_it(spell[language],color_arr.abilcol) spell.ability = color_it(spell[language],color_arr.abilcol) end elseif msg_ID == 305 then spell = res.job_abilities[76] -- 'Trick Attack' if spell then spell.name = color_it(spell[language],color_arr.abilcol) spell.ability = color_it(spell[language],color_arr.abilcol) end elseif msg_ID == 311 or msg_ID == 312 then spell = res.job_abilities[79] -- 'Cover' if spell then spell.name = color_it(spell[language],color_arr.abilcol) spell.ability = color_it(spell[language],color_arr.abilcol) end elseif msg_ID == 240 or msg_ID == 241 then spell = res.job_abilities[43] -- 'Hide' if spell then spell.name = color_it(spell[language],color_arr.abilcol) spell.ability = color_it(spell[language],color_arr.abilcol) end end if fields.item then if T{125,593,594,595,596,597,598,599}:contains(msg_ID) then local item_article = not simplify and add_item_article(effect_val) or '' spell.item = color_it(item_article..res.items[effect_val]['english_log'], color_arr.itemcol) spell.item_id = res.items[effect_val].id else spell = res.items[abil_ID] local item_article = not simplify and add_item_article(spell.id) or '' if spell then spell.name = color_it(item_article..spell['english_log'],color_arr.itemcol) spell.item = color_it(item_article..spell['english_log'],color_arr.itemcol) spell.item_id = abil_ID end end end if fields.item2 then local item_article = not simplify and add_item_article(effect_val) or '' local tempspell = (msg_ID == 377 or msg_ID == 674) and res.items_grammar[effect_val] and res.items_grammar[effect_val].plural or item_article..res.items[effect_val].english_log spell.item2 = color_it(tempspell,color_arr.itemcol) spell.item2_id = effect_val if fields.number then spell.number = act.targets[1].actions[1].add_effect_param end end end if spell and not spell.name then spell.name = spell[language] end return spell end function color_filt(col,is_me) --Used to convert situational colors from the resources into real colors --Depends on whether or not the target is you, the same as using in-game colors -- Returns a color code for windower.add_to_chat() -- Does not currently support a Debuff/Buff distinction if col == 'D' then -- Damage if is_me then return 28 else return 20 end elseif col == 'M' then -- Misses if is_me then return 29 else return 21 end elseif col == 'H' then -- Healing if is_me then return 30 else return 22 end elseif col == 'B' then -- Beneficial effects if is_me then return 56 else return 60 end elseif col == 'DB' then -- Detrimental effects (I don't know how I'd split these) if is_me then return 57 else return 61 end elseif col == 'R' then -- Resists if is_me then return 59 else return 63 end else return col end end function get_prefix(category, effect, message, unknown, reaction_lookup) local prefix = S{1,3,4,6,11,13,14,15}:contains(category) and (bit.band(unknown,1)==1 and 'Cover! ' or '') ..(bit.band(unknown,4)==4 and 'Magic Burst! ' or '') --Used on Swipe/Lunge MB ..(bit.band(unknown,8)==8 and 'Immunobreak! ' or '') --Unused? Displayed directly on message ..(showcritws and bit.band(effect,2)==2 and S{1,3,11}:contains(category) and message~=67 and 'Critical Hit! ' or '') --Unused? Crits have their own message ..(showblocks and reaction_lookup == 4 and 'Blocked! ' or '') ..(showguards and reaction_lookup == 2 and 'Guarded! ' or '') ..(reaction_lookup == 3 and S{3,4,6,11,13,14,15}:contains(category) and 'Parried! ' or '') --Unused? They are send the same as missed return prefix end function condense_actions(action_array) for i,v in pairs(action_array) do local comb_table = {} for n,m in pairs(v) do if comb_table[m.primary.name] then if m.secondary.name == 'number' then comb_table[m.primary.name].secondary.name = tostring(tonumber(comb_table[m.primary.name].secondary.name)+tonumber(m.secondary.name)) end comb_table[m.primary.name].count = comb_table[m.primary.name].count + 1 else comb_table[m.primary.name] = m comb_table[m.primary.name].count = 1 end m = nil -- Could cause next() error end for n,m in pairs(comb_table) do v[#v+1] = m end end return action_array end function condense_targets(action_array) local comb_table = {} for i,v in pairs(action_array) do local was_created = false for n,m in pairs(comb_table) do if table.equal(v,m,3) then -- Compares 3 levels deep n[#n+1] = i[1] was_created = true end end if not was_created then comb_table[{i[1]}] = v end end return comb_table end