root/trunk/lua/lib.macrolist.lua @ 3214

Revision 3214, 56.4 kB (checked in by ghoulsblade, 8 months ago)
use .iris for config and temp data in user home dir if USE_HOME_DIR exists in iris maindir, centralized a few path variables
Line 
1--[[ ***** see Iris2 COPYING for license info ***** ]]--
2--[[ \brief
3        handles macro list
4]]--
5
6gMacroList = {}
7
8gMacroOpenCommands = {}
9gMacroOpenCommands.Status           = function()    ToggleStatusAos()           end
10gMacroOpenCommands.Skill            = function()    ToggleSkill()               end
11gMacroOpenCommands.Journal          = function()    ToggleJournal()             end
12gMacroOpenCommands.Backpack         = function()    TogglePlayerBackpack()      end
13gMacroOpenCommands.Paperdoll        = function()    TogglePlayerPaperdoll()     end
14gMacroOpenCommands.Compass          = function()    ToggleCompass()             end
15gMacroOpenCommands.Logo             = function()    ToggleLogo()                end
16gMacroOpenCommands.PartyList        = function()    TogglePartyList()           end
17
18gMacroItemSlots = {}
19gMacroReadMobileStats = {}
20gMacroReadMobileStats.curHits       = true
21gMacroReadMobileStats.maxHits       = true
22gMacroReadMobileStats.curMana       = true
23gMacroReadMobileStats.maxMana       = true
24gMacroReadMobileStats.curStamina    = true
25gMacroReadMobileStats.maxStamina    = true
26gMacroReadMobileStats.curWeight     = true
27gMacroReadMobileStats.maxWeight     = true
28gMacroReadMobileStats.maxPet            = true
29gMacroReadMobileStats.curPet            = true
30gMacroReadMobileStats.tithing           = true
31
32function GetMobileStat(mobile,statname) return mobile and mobile.stats and mobile.stats[statname] or 0 end 
33function GetMobileRelHP(serial) local mobile = GetMobile(serial) return mobile and mobile:GetRelHP() end 
34
35function GetPlayerManaCur() return GetMobileStat(GetPlayerMobile(),"curMana") end 
36function GetPlayerManaMax() return GetMobileStat(GetPlayerMobile(),"maxMana") end 
37function GetPlayerStamCur() return GetMobileStat(GetPlayerMobile(),"curStamina") end 
38function GetPlayerStamMax() return GetMobileStat(GetPlayerMobile(),"maxStamina") end 
39function GetPlayerHitsCur() return GetMobileStat(GetPlayerMobile(),"curHits") end 
40function GetPlayerHitsMax() return GetMobileStat(GetPlayerMobile(),"maxHits") end 
41function GetPlayerPetsCur() return GetMobileStat(GetPlayerMobile(),"curPet") end  -- follower slots
42function GetPlayerPetsMax() return GetMobileStat(GetPlayerMobile(),"maxPet") end
43function GetPlayerTithingPoints() return GetMobileStat(GetPlayerMobile(),"tithing") end
44function IsPlayerHidden() local mobile = GetPlayerMobile() return mobile and mobile.flag_hidden end
45
46function MacroCmd_MobilePoisoned (mobile) return mobile and IsMobilePoisoned(mobile) end
47function MacroCmd_MobileMortaled (mobile) return mobile and IsMobileMortaled(mobile) end
48
49function MacroCmd_MiniHealCureSelf () 
50    if (not GetPlayerMobile()) then return end
51    if (MacroCmd_MobilePoisoned(GetPlayerMobile())) then
52        MacroCmd_Spell("Cure"   ,GetPlayerSerial())
53    else
54        MacroCmd_Spell("Heal"   ,GetPlayerSerial())
55    end
56end
57
58kSmartSpellHealSmall    = 4
59kSmartSpellHealBig      = 29
60kSmartSpellCureSmall    = 11
61kSmartSpellCureBig      = 25
62-- returns serial,bIsFriendly
63function MacroCmd_GetSmartTargetForLastSpell () -- smart targets for friendly spells (heal,cure)
64    if (gSmartLastSpellID) then 
65        if (gSmartLastSpellID == kSmartSpellHealSmall or gSmartLastSpellID == kSmartSpellHealBig) then
66            local target = MobileList_GetWeakestFromList(MobileList_HealablePartyMembers(true))
67            --~ print("healtarget",target)
68            return target and target.serial or GetPlayerSerial(),true
69        end
70        if (gSmartLastSpellID == kSmartSpellCureSmall or gSmartLastSpellID == kSmartSpellCureBig) then
71            local target = MobileList_GetWeakestFromList(MobileList_CurablePartyMembers(true))
72            --~ print("curetarget",target)
73            return target and target.serial or GetPlayerSerial(),true
74        end
75    end
76    -- gLastSpellID
77    return MobListGetMainTargetSerial()
78end
79
80function MobileList_GetWeakestFromList      (list) 
81    local found
82    local foundhp
83    for k,mobile in pairs(list) do 
84        local curhp = mobile:GetRelHP() or 1
85        --~ print("MobileList_GetWeakestFromList",curhp)
86        if ((not foundhp) or foundhp > curhp) then foundhp = curhp found = mobile end
87    end
88    return found
89end
90
91-- checking if a mob has equip helps detecting humans/players
92function MobileHasEquip   (mobile) 
93        for index,layer in pairs(gLayerOrder) do
94                -- slime has kLayer_Backpack , dog not
95                if (layer ~= kLayer_Backpack and mobile:GetEquipmentAtLayer(layer)) then return true end
96        end
97        return false
98end
99function MobileIsHuman   (mobile) 
100        return  mobile and
101                        mobile.artid == 400 or mobile.artid == 401 or
102                        mobile.artid == 744 or mobile.artid == 745 or MobileHasEquip(mobile)
103end
104
105function MobileList_GetByFilter (fun)
106        local res = {}
107        for k,mobile in pairs(GetMobileList()) do if (fun(mobile)) then table.insert(res,mobile) end end
108        return res
109end
110
111
112function MobileList_HealablePartyMembers    (bFriendsAlso) 
113        return MobileList_GetByFilter(function (mobile) return  GetUODistToPlayer(mobile.xloc,mobile.yloc) <= gSpellCastRange and 
114                                                                                                                        (not MacroCmd_MobilePoisoned(mobile)) and (not MacroCmd_MobileMortaled(mobile)) and 
115                                                                                                                        (IsMobileInParty(mobile.serial) or (bFriendsAlso and mobile.notoriety == kNotoriety_Friend and MobileIsHuman(mobile))) and
116                                                                                                                        (mobile:GetRelHP() or 0) < 1 and 
117                                                                                                                        (mobile:GetRelHP() or 0) > 0 end) -- not dead
118end
119function MobileList_CurablePartyMembers    (bFriendsAlso) 
120        return MobileList_GetByFilter(function (mobile) return  GetUODistToPlayer(mobile.xloc,mobile.yloc) <= gSpellCastRange and 
121                                                                                                                        (MacroCmd_MobilePoisoned(mobile)) and 
122                                                                                                                        (IsMobileInParty(mobile.serial) or (bFriendsAlso and mobile.notoriety == kNotoriety_Friend and MobileIsHuman(mobile))) and
123                                                                                                                        (mobile:GetRelHP() or 0) < 1 end)
124end
125
126-- GetPlayerMobile
127function MacroRead_PlayerStat           (statname) return MacroReadAux_MobileStat(GetPlayerMobile(),statname,"MacroRead_PlayerStat") end
128function MacroRead_TargetStat           (statname) return MacroReadAux_MobileStat(GetMobile(giSelectedMobile),statname,"MacroRead_TargetStat") end
129
130function MacroRead_SkillDataPart                (skillname,partname) -- internal, used by the others
131    for skillid,name in pairs(glSkillNames) do
132        if skillname == name then return gPlayerSkills and gPlayerSkills[skillid] and gPlayerSkills[skillid][partname] end
133    end
134end
135function MacroRead_SkillBaseSum () 
136        local c = 0
137        for skillid,name in pairs(glSkillNames) do c = c + MacroRead_SkillBase(name) end
138        return c
139end
140function MacroRead_SkillBase                    (skillname) return (MacroRead_SkillDataPart(skillname,"base_value") or -1)  * 0.1 end 
141function MacroRead_SkillCap                             (skillname) return (MacroRead_SkillDataPart(skillname,"skill_cap") or -1)  * 0.1 end 
142function MacroRead_SkillValue                   (skillname) return (MacroRead_SkillDataPart(skillname,"value") or -1)  * 0.1 end
143function MacroRead_SkillLockState               (skillname) return MacroRead_SkillDataPart(skillname,"lockstate") end -- 0=up, 1=down, 2=lock
144function MacroCmd_SetSkillLockState             (skillname,lockstate)
145    for skillid,name in pairs(glSkillNames) do
146        if skillname == name then Send_SkillLockState(skillid-1,tonumber(lockstate) or 0) return true end
147    end
148end -- 0=up, 1=down, 2=lock
149
150
151
152function MacroRead_BuffActive           (buffname)
153    for id,v in pairs(gBuffIcons) do
154        if buffname == v.name then
155            return gBuffs[id] or false
156        end
157    end
158    return false
159end
160
161function MacroCmd_Say                   (text)  if (gInGameStarted) then SendChat(text) end end
162function MacroCmd_NextCamMode           ()      gCurrentRenderer:ChangeCamMode() end
163function MacroCmd_BandageSelf           ()      SendBandageSelf() end
164function MacroCmd_Quit                  ()      Terminate() end
165function MacroCmd_RepeatLastChat        ()      if (gInGameStarted) then IrisChatLine_RepeatLast() end end
166function MacroCmd_RepeatLastDoubleClick ()      if (gInGameStarted) then RepeatLastDoubleClick() end end
167function MacroCmd_SelectNearestMobile   ()      if (gInGameStarted) then SelectNearestMobile() end end
168function MacroCmd_SelectNextMobile      ()      if (gInGameStarted) then SelectNextMobile() end end
169function MacroCmd_AttackSelectedMobile  ()      if (gInGameStarted) then if giSelectedMobile then AttackMobile(giSelectedMobile) end end end
170function MacroCmd_ToggleWarmode         ()      if (gInGameStarted) then Send_CombatMode(IsWarModeActive() and gWarmode_Normal or gWarmode_Combat) end end
171function MacroCmd_OpenDoors             ()      Send_OpenDoors() end
172function MacroCmd_Open                  (dialogtype)
173    if (not gInGameStarted) then return end
174    local f = gMacroOpenCommands[dialogtype] 
175    if (not f) then return MacroErrorNameMismatch("MacroCmd_Open",dialogtype,gMacroOpenCommands) end
176    f() 
177end
178
179function MacroCmd_UseNearbyGate () local gate = MacroCmd_Item_FindFirstNearByArtID(kMoongateGateArtID,nil,1) MacroCmd_Item_Use(gate) return gate ~= nil end
180
181function MacroCmd_ToggleHideGUI () GuiToggleHide() Client_RenderOneFrame() end
182
183
184function MacroCmd_PopupCommandByTag (serial,tag,timeout)
185    local timeout_endt = Client_GetTicks() + (timeout or 1000)
186    Send_PopupRequest(serial)
187    Send_PopupAnswer(serial,tag)
188    RegisterListener("Hook_OpenPopupMenu",function (popupmenu)
189        if (popupmenu.serial == serial or Client_GetTicks() < timeout_endt) then ClosePopUpMenu() end
190        return true
191    end)
192end 
193
194function MacroCmd_PopupCommandByName (serial,name,timeout)
195        if (not serial) then return end
196        if (type(serial) ~= "number") then return end
197    local timeout_endt = Client_GetTicks() + (timeout or 1000)
198        ClosePopUpMenu() -- close old
199    Send_PopupRequest(serial)
200    RegisterListener("Hook_OpenPopupMenu",function (popupmenu)
201        if (popupmenu.serial == serial or Client_GetTicks() < timeout_endt) then 
202            for k,entry in pairs(popupmenu.entries) do 
203                                print("MacroCmd_PopupCommandByName",">"..entry.text.."<")
204                if (StringContains(entry.text,name)) then Send_PopupAnswer(popupmenu.serial,entry.tag) break end
205            end
206            ClosePopUpMenu()
207        end 
208        return true
209    end)
210end
211
212function MacroCmd_SendTargetSerial  (serial,maxrange) 
213    local mobile = GetMobile(serial)
214    if mobile then return CompleteTargetMode({ hittype = kMousePickHitType_Mobile,  mobile = mobile },maxrange) end
215    local dynamic = GetDynamic(serial)
216    if dynamic then return CompleteTargetMode({ hittype = kMousePickHitType_Dynamic,  dynamic = dynamic },maxrange) end
217end
218
219-- timeout : in ms
220-- bFailBySpellInterrupt : if true, then it will be aborted if a message indicating spell interruption is received
221function MacroCmd_QueueTargetSerial     (serial,timeout,callback,bFailBySpellInterrupt)
222    gMacroQueuedTarget_Serial = serial
223    gMacroQueuedTarget_Timeout = Client_GetTicks() + timeout
224    gMacroQueuedTarget_CallBack = callback
225    gMacroQueuedTarget_FailBySpellInterrupt = bFailBySpellInterrupt
226    if (IsTargetModeActive()) then
227        MacroCmd_SendTargetSerial(serial)
228        MacroCmd_QueuedTargetEnd(true)
229    end
230end
231function MacroCmd_QueuedTargetEnd   (bSuccess,sFailureReason) 
232    if (gMacroQueuedTarget_CallBack) then gMacroQueuedTarget_CallBack(bSuccess,sFailureReason) end
233    gMacroQueuedTarget_CallBack = nil
234    gMacroQueuedTarget_Serial = nil
235    gMacroQueuedTarget_Timeout = nil
236    gMacroQueuedTarget_FailBySpellInterrupt = nil
237end
238
239RegisterStepper(function () 
240    local t = Client_GetTicks()
241    if (gMacroQueuedTarget_Timeout and t > gMacroQueuedTarget_Timeout) then MacroCmd_QueuedTargetEnd(false,"timeout") end
242    for data,v in pairs(gMacroGumpWaiters) do
243        if (t > data.timeout) then data.callback() gMacroGumpWaiters[data] = nil end
244    end
245end)
246RegisterListener("Hook_Spell_Interrupt",    function ()
247    if (gMacroQueuedTarget_FailBySpellInterrupt) then MacroCmd_QueuedTargetEnd(false,"spell-interrupt") end
248end)
249gMacroJobsWaitingForTarget = {}
250function MacroCmd_JobWaitForTarget(timeout) -- returns true if IsTargetModeActive
251        if (IsTargetModeActive()) then return true end
252    gMacroJobsWaitingForTarget[job.running_id()] = true
253    job.wait(timeout or 30000)
254    gMacroJobsWaitingForTarget[job.running_id()] = nil
255    return IsTargetModeActive()
256end
257RegisterListener("Hook_TargetMode_Start",   function () 
258    if (gMacroQueuedTarget_Serial) then MacroCmd_SendTargetSerial(gMacroQueuedTarget_Serial) MacroCmd_QueuedTargetEnd(true) end
259    for jobid,v in pairs(gMacroJobsWaitingForTarget) do job.wakeup(jobid,true) end gMacroJobsWaitingForTarget = {}
260end)
261RegisterListener("Hook_TargetMode_Start",   function () 
262    if (gMacroQueuedTarget_Serial) then MacroCmd_SendTargetSerial(gMacroQueuedTarget_Serial) MacroCmd_QueuedTargetEnd(true) end
263end)
264
265gMacroJobsWaitingForGump = {}
266function MacroCmd_JobWaitForGump(callback,timeout) -- callback(dialog,playerid,dialogId) should return true for the gump that it waited for
267    gMacroJobsWaitingForGump[job.running_id()] = callback
268    job.wait(timeout or 30000)
269    gMacroJobsWaitingForGump[job.running_id()] = nil
270end
271function MacroCmd_JobWaitForGumpWithKeyword(keyword,timeout) 
272    local dialog = MacroCmd_FindGumpByKeyword(keyword)
273    if (dialog) then return dialog end
274    MacroCmd_JobWaitForGump(function(dialog) return dialog:Search(keyword) end,timeout) 
275    return MacroCmd_FindGumpByKeyword(keyword)
276end
277function MacroCmd_FindGumpByKeyword(keyword)
278    for k,dialog in pairs(gServerSideGump) do 
279        if (dialog:Search(keyword)) then return dialog end
280    end
281end
282
283RegisterListener("Hook_OpenServersideGump", function (dialog,playerid,dialogId,Length_Data) 
284    for data,v in pairs(gMacroGumpWaiters) do
285        if ((not data.search) or dialog:Search(data.search)) then data.callback(dialog) gMacroGumpWaiters[data] = nil end
286    end
287   
288    for jobid,callback in pairs(gMacroJobsWaitingForGump) do 
289        if ((not callback) or callback(dialog,playerid,dialogId)) then 
290            job.wakeup(jobid,true) 
291            gMacroJobsWaitingForGump[jobid] = nil 
292        end
293    end
294end)
295
296gMacroGumpWaiters = {} 
297-- callback(dialog) or callback(nil) for timeout
298function MacroCmd_NextGumpContaining (search,timeout,callback) 
299    if (not callback) then return end
300    gMacroGumpWaiters[{search=search,timeout=Client_GetTicks()+(timeout or 1000),callback=callback}] = true
301end
302
303
304gMacroJobsWaitingForShop = {}
305function MacroCmd_JobWaitForShop(timeout)
306    gMacroJobsWaitingForShop[job.running_id()] = true
307    job.wait(timeout or 30000)
308    gMacroJobsWaitingForShop[job.running_id()] = nil
309end
310RegisterListener("Hook_Open_Shop_Dialog", function (shop) 
311    for jobid,callback in pairs(gMacroJobsWaitingForShop) do 
312                gMacroJobsWaitingForShop[jobid] = nil
313                job.wakeup(jobid,true) 
314        end
315        end)
316       
317
318-- method:  nil=recall "gate"=gate  "use"=runebookcharge
319function MacroCmd_UseRuneBookPreAOS (runebookid,runeidx,method,timeout) -- for pre-aos (uogamers)   runeidx:0-15     
320    Send_DoubleClick(runebookid)
321    MacroCmd_NextGumpContaining("Charges",timeout or 1000,function (dialog) 
322            if (dialog) then 
323                dialog:ShowPage(floor(runeidx/2)+2)
324                local side = (runeidx%2)
325                if (method == "gate") then
326                    dialog:SendClick((side == 0) and 230 or 390,160) -- gate travel
327                elseif (method == "use") then
328                    dialog:SendClick((side == 0) and 135 or 295,70) -- runebook charge
329                else   
330                    dialog:SendClick((side == 0) and 160 or 320,160) -- recall
331                end
332            end
333        end) 
334end
335
336-- method:  nil=recall "gate"=gate "chivalry"=sacred-journey "use"=runebookcharge  "default":set as runebook default
337function MacroCmd_UseRuneBookPostAOS (runebookid,runeidx,method,timeout,forcew,forceh) -- for post-aos (vetus-mundus)   runeidx:0-15     
338    Send_DoubleClick(runebookid)
339    MacroCmd_NextGumpContaining("Charges",timeout or 1000,function (dialog) 
340            if (dialog) then 
341                dialog:ShowPage(floor(runeidx/2)+2)
342                local side = (runeidx%2)
343                                if (method == "use") then
344                    dialog:SendClick((side == 0) and 135 or 295,70,forcew,forceh) -- runebook charge
345                elseif (method == "gate") then
346                    dialog:SendClick((side == 0) and 140 or 300,160,forcew,forceh) -- gate travel
347                elseif (method == "chivalry") then
348                    dialog:SendClick((side == 0) and 140 or 300,180,forcew,forceh) -- chivalry : sacred journey
349                elseif (method == "default") then
350                    dialog:SendClick((side == 0) and 165 or 305,25,forcew,forceh) -- recall
351                else   
352                    dialog:SendClick((side == 0) and 140 or 300,145,forcew,forceh) -- recall
353                end
354            end
355        end) 
356end
357
358gMacroLastTargetMemory = nil
359gMacroTargetLastRunning = false
360function MacroRememberTarget (hitobject)
361    gMacroLastTargetMemory = {}
362    for k,v in pairs(hitobject) do gMacroLastTargetMemory[k] = v end
363end
364function MacroSetLastTarget (serial)
365    local mobile = GetMobile(serial)
366    if (mobile) then gMacroLastTargetMemory = { hittype=kMousePickHitType_Mobile, mobile=mobile } return end
367    local dynamic = GetDynamic(serial)
368    if (dynamic) then gMacroLastTargetMemory = { hittype=kMousePickHitType_Dynamic, dynamic=mobile } return end
369end
370
371function MacroGetLastTargetSerial ()
372    if (not gMacroLastTargetMemory) then return 0 end
373    if (gMacroLastTargetMemory.hittype == kMousePickHitType_Mobile) then return gMacroLastTargetMemory.mobile.serial end
374    if (gMacroLastTargetMemory.hittype == kMousePickHitType_Dynamic) then return gMacroLastTargetMemory.dynamic.serial end
375end
376
377function MacroCmd_LastTargetVisible ()
378    if (not gMacroLastTargetMemory) then return end
379    if (gMacroLastTargetMemory.hittype == kMousePickHitType_Mobile) then
380        local mobile = GetMobile(gMacroLastTargetMemory.mobile.serial)
381        return mobile ~= nil
382    end
383    if (gMacroLastTargetMemory.hittype == kMousePickHitType_Dynamic) then
384        local dynamic = GetDynamic(gMacroLastTargetMemory.dynamic.serial)
385        return dynamic ~= nil
386    end
387end
388
389
390function MacroCmd_TargetLastNow () if (gMacroLastTargetMemory) then CompleteTargetMode(gMacroLastTargetMemory) end end
391function MacroCmd_TargetSelfNow () CompleteTargetModeWithTargetMobile(GetPlayerMobile()) end
392
393function MacroCmd_WeaponAbilityPrimary      () local a,b = GetWeaponSpecialsForMobile(GetPlayerMobile()) Send_AOSCommand_WeaponAbility(a) end
394function MacroCmd_WeaponAbilitySecondary    () local a,b = GetWeaponSpecialsForMobile(GetPlayerMobile()) Send_AOSCommand_WeaponAbility(b) end
395
396-- timeout in ms, defaults to 30 seconds
397gMacroTargetLastRunningNextIndex = 1
398function MacroCmd_TargetLast    (completefun,timeout)       -- repeat the last target   
399    timeout = timeout or 30000
400    if (gMacroTargetLastRunning) then return end
401    if (not gMacroLastTargetMemory) then return end
402   
403    if (IsTargetModeActive()) then
404        MacroCmd_TargetLastNow()
405        if (completefun) then completefun() end
406        return 
407    end
408   
409    local myindex = gMacroTargetLastRunningNextIndex
410    gMacroTargetLastRunningNextIndex = gMacroTargetLastRunningNextIndex + 1
411    gMacroTargetLastRunning = myindex
412    local listener = RegisterListener("Hook_TargetMode_Start",function ()
413            if (myindex ~= gMacroTargetLastRunning) then return true end -- timeout was reached, or other targetlast started
414            MacroCmd_TargetLastNow()
415            gMacroTargetLastRunning = false
416            if (completefun) then completefun() end
417            return true
418        end)
419    if (timeout) then 
420        InvokeLater(timeout,function ()
421                if (not gMacroTargetLastRunning) then return end
422                UnregisterListener("Hook_TargetMode_Start",listener)
423                gMacroTargetLastRunning = false
424                print("MacroCmd_TargetLast timeout")
425            end)
426    end
427end
428
429function MacroCmd_TargetGround  (xloc,yloc,zloc_or_nil, completefun)
430    if (gMacroWaitForTargetActive) then return end
431    gMacroWaitForTargetActive = true
432    RegisterListener("Hook_TargetMode_Start",function () 
433            print("MacroCmd_TargetGround hook triggered")
434            MacroCmd_TargetGroundNow(xloc,yloc,zloc_or_nil)
435            gMacroWaitForTargetActive = false
436            if (completefun) then completefun() end
437            return true
438        end)
439end
440
441-- zloc_or_nil : determined automatically if nil
442function MacroCmd_TargetGroundNow (xloc,yloc,zloc_or_nil)
443    if (xloc and yloc) then
444        local zloc = zloc_or_nil or GetGroundZAtAbsPos(xloc,yloc) or 0
445        CompleteTargetMode({hittype=kMousePickHitType_Ground,x=xloc,y=yloc,z=zloc}) 
446    end
447end
448
449function MacroCmd_TargetSelf    (completefun)       -- target self
450    if (gMacroWaitForTargetActive) then return end
451    gMacroWaitForTargetActive = true
452    RegisterListener("Hook_TargetMode_Start",function () 
453            local playermobile = GetPlayerMobile()
454            print("MacroCmd_TargetSelf hook triggered")
455            if (playermobile) then 
456                print("#",playermobile.serial)
457                CompleteTargetMode({hittype=kMousePickHitType_Mobile,mobile=playermobile}) 
458            end
459            gMacroWaitForTargetActive = false
460            if (completefun) then completefun() end
461            return true
462        end)
463end
464
465-- currently broken
466--~ function MacroCmd_ShowFallBackTool      ()              if (gInGameStarted) then ShowFallBackTool() end end
467function MacroCmd_ShowDevTool           ()              if (gInGameStarted) then ShowDevTool() end end 
468function MacroCmd_ZoomCompass           (zoomfactor)    if (gInGameStarted) then ZoomCompass(zoomfactor) end end
469function MacroCmd_ActivateNextRenderer  ()              if (gInGameStarted) then ActivateNextRenderer() end end
470function MacroCmd_CamChangeZoom         (zoomadd)       if (gInGameStarted) then gCurrentRenderer:CamChangeZoom(zoomadd) end end
471function MacroCmd_Screenshot            ()              Client_TakeScreenshot(gScreenshotDir) end
472function MacroCmd_GridScreenshot        ()         
473    if (not gInGameStarted) then return end
474    ToggleCompass()
475    Client_TakeGridScreenshot(gScreenshotDir)
476    ToggleCompass()
477end
478
479function MacroCmd_ReloadMap     ()         
480    if (not gInGameStarted) then return end
481    local mapindex = gMapIndex
482    UnloadOldMap(true) -- do not clear objects
483    LoadMap(gMapIndex)
484end
485
486function MacroCmd_Dress (dresslist)
487    for k,serial in pairs(dresslist) do 
488        if (MacroCmd_EquipItem(serial)) then return true end 
489        end
490end
491
492function MacroCmd_EquipItem (serial,bOkIfNotInBackPack)
493    if (type(serial) == "table") then 
494        local cmd,param = unpack(serial)
495        if (cmd == "use") then Send_DoubleClick(param) end 
496        return
497    end
498    local item = GetDynamic(serial)
499    if (not item) then item = MacroCmd_Item_FindFirstByArtID(serial) end
500    if (not item) then return end
501    if (item.iContainerSerial == GetPlayerSerial()) then return end -- already equipped
502    if (item.iContainerSerial ~= GetPlayerBackPackSerial() and (not bOkIfNotInBackPack)) then return end -- not in backpack, other char?
503    local layer = GetPaperdollLayerFromTileType(item.artid)
504        if (not layer) then return end -- unknown layer
505        if (MacroCmd_GetPlayerEquipment(layer)) then return end -- something else already equipped there
506    print("MacroCmd_EquipItem",serial,item.amount,layer)
507    Send_Take_Object(item.serial,item.amount)
508    Send_Equip_Item_Request(item.serial,layer,GetPlayerSerial())
509    return true
510end
511
512   
513
514function MacroCmd_RiseText (r,g,b,text,serial)
515    serial = serial or GetPlayerSerial()
516    if (SpellBarRiseTextOnMob) then SpellBarRiseTextOnMob(serial,r,g,b,text) end
517end
518
519function MacroCmd_Item_Use  (item) if (item) then Send_DoubleClick(item.serial) return item end end
520
521function MacroCmd_Item_UseByName    (itemnamepart)  return MacroCmd_Item_Use(MacroCmd_Item_FindFirstByName(itemnamepart)) end
522function MacroCmd_Item_UseByArtID   (artid,hue)     return MacroCmd_Item_Use(MacroCmd_Item_FindFirstByArtID(artid,hue)) end
523
524function MacroCmd_Item_FindFirstByName  (itemnamepart,container) local list = MacroCmd_Item_FindByName(itemnamepart,container) return list[1] end
525function MacroCmd_Item_FindFirstByArtID (artid,hue,container) local list = MacroCmd_Item_FindByArtID(artid,hue,container) return list[1] end
526function MacroCmd_Item_FindFirstNearByArtID (artid,hue,dist) local list = MacroCmd_Item_FindNearByArtID(artid,hue,dist) return list[1] end
527
528function MacroCmd_GetPlayerEquipment (layer) 
529    local playermobile = GetPlayerMobile()
530    return playermobile and GetMobileEquipmentItem(playermobile,layer)
531end
532function MacroCmd_GetItemInHand () return MacroCmd_GetPlayerEquipment(kLayer_OneHanded) end
533function MacroCmd_IsMounted () return MacroCmd_GetPlayerEquipment(kLayer_Mount) ~= nil end
534function MacroCmd_Dismount ()
535    if (MacroCmd_IsMounted()) then Send_DoubleClick(GetPlayerSerial()) return true end -- todo : check if mounted
536end
537
538function MacroCmd_DragAndEquip (takeserial,mobileserial)
539    local item = GetDynamic(takeserial)
540    local layer = item and GetPaperdollLayerFromTileType(item.artid)
541    if (not layer) then print("MacroCmd_DragEquip not wearable or item not found, artid=",item.artid) return end
542    Send_Take_Object(takeserial,1)
543    Send_Equip_Item_Request(takeserial,layer,mobileserial or GetPlayerSerial())
544end 
545function MacroCmd_DragDrop (takeserial,amount,dropcontainerserial) 
546    Send_Take_Object(takeserial,amount)
547    Send_Drop_Object_AutoStack(takeserial,dropcontainerserial or GetPlayerBackPackSerial())
548end
549
550function MacroCmd_DragDropToGround (takeserial,amount,xloc,yloc,zloc) 
551        if (not takeserial) then print("MacroCmd_DragDropToGround:no takeserial") return end
552        local o = GetDynamic(takeserial)
553        amount = amount or (o and o.amount)
554        if (not amount) then print("MacroCmd_DragDropToGround:no amount") return end
555    xloc = xloc or gPlayerXLoc
556    yloc = yloc or gPlayerYLoc
557    zloc = zloc or gPlayerZLoc
558        job.create(function ()
559                Send_Take_Object(takeserial,amount)
560                job.wait(math.random(200,300))
561                Send_Drop_Object(takeserial,xloc,yloc,zloc,0xFFFFFFFF)
562        end)
563end
564
565-- 0x0c9e : ohii tree not hackable
566function MacroCmd_IsHackableTrees (artid) return 0x0c9e ~= artid and StringContains(string.lower(GetStaticTileTypeName(artid)),"tree") end
567function MacroCmd_FindNearbyTrees (r) return MacroCmd_FindNearbyStatics(r,"tree",{0x0c9e}) end -- ohii tree not hackable
568function MacroCmd_FindNearbyStatics (r,namepart,skipartidlist)
569    local res = {}
570    r = r or 2
571    for ax = -r,r do
572    for ay = -r,r do
573        for k,item in pairs(MapGetStatics(gPlayerXLoc+ax,gPlayerYLoc+ay)) do
574            --~ print("MacroCmd_FindNearbyStatics",GetStaticTileTypeName(item.artid))
575            if (StringContains(string.lower(GetStaticTileTypeName(item.artid)),namepart) and
576                ((not skipartidlist) or (not in_array(item.artid,skipartidlist)))) then 
577                table.insert(res,item) 
578            end
579        end 
580    end
581    end
582    return res
583end
584
585-- doesn't sum stack-amount
586function MacroCmd_Item_CountByArtID (artid,hue,container) local list = MacroCmd_Item_FindByArtID(artid,hue,container) return list and #list or 0 end
587function MacroCmd_Item_SumByArtID   (artid,hue,container) -- does sum stack
588    local list = MacroCmd_Item_FindByArtID(artid,hue,container) 
589    if (not list) then return 0 end
590    local c = 0
591    for k,item in pairs(list) do c = c + item.amount end
592    return c
593end
594
595-- container defaults to player-backpack
596function MacroCmd_Item_FindByName   (itemnamepart,container)
597    container = container or GetPlayerBackPackContainer()
598    if (not container) then return end
599    local res = {}
600    for k,item in pairs(container:GetContent()) do 
601        local name = AosToolTip_GetText(item.serial) or GetStaticTileTypeName(item.artid)
602        if (name and string.find(name,itemnamepart)) then
603            table.insert(res,item)
604        end
605    end
606    return res
607end
608
609function MacroCmd_HideAllCorpses    ()
610    for k,item in pairs(GetDynamicList()) do 
611        if (item.artid_base == kCorpseDynamicArtID and (not Renderer2D:MobileHasVisibleEquip(item.amount))) then item:Destroy() end
612    end
613end         
614       
615function MacroCmd_AutoClickItems    ()
616    for k,item in pairs(GetDynamicList()) do 
617        if (DynamicIsInWorld(item) and (not gItemAutoClickSent[item.serial])) then
618            gItemAutoClickSent[item.serial] = true
619            if ((not GetItemTooltipOrLabel(item.serial)) and (gDynamicAutoClickByArtID[item.artid] or gContainerArtIDs[item.artid])) then
620                Send_SingleClick(item.serial,true)
621            end
622        end
623    end
624end
625
626function MacroCmd_Item_FindNearByArtList    (artlist,dist)
627    local res = {}
628    for k,item in pairs(GetDynamicList()) do 
629        if (DynamicIsInWorld(item) and 
630            (dist == nil or item:GetUODistToPlayer() <= dist) and in_array(item.artid,artlist)) then
631            table.insert(res,item)
632        end
633    end
634    return res
635end
636
637function MacroCmd_Item_FindNearByArtID  (artid,hue,dist) -- equals easyuo type
638    local res = {}
639    local artidlist = (type(artid) == "number") and {artid} or artid
640    for k,item in pairs(GetDynamicList()) do 
641        if (in_array(item.artid,artidlist) and (hue == nil or hue == item.hue) and DynamicIsInWorld(item) and 
642            (dist == nil or item:GetUODistToPlayer() <= dist) ) then
643            table.insert(res,item)
644        end
645    end
646    return res
647end 
648
649function MacroCmd_Item_FindNearCorpses  (dist,corpsetype)
650    local res = {}
651    for k,item in pairs(GetDynamicList()) do 
652        if (DynamicIsInWorld(item) and IsCorpseArtID(item.artid) and ((not corpsetype) or (item.amount == corpsetype)) and
653            (dist == nil or item:GetUODistToPlayer() <= dist) ) then
654            table.insert(res,item)
655        end
656    end
657    return res
658end 
659
660function MacroCmd_IsItemInContainer (serial,container)
661    if (type(container) == "number") then container = GetContainer(container) end -- resolve serial
662    container = container or GetPlayerBackPackContainer()
663    return container and serial and container.content[serial]
664end
665
666-- container defaults to player-backpack
667-- hue can be nil for any
668function MacroCmd_Item_FindByArtID  (artid,hue,container) -- equals easyuo type
669    if (type(container) == "number") then container = GetContainer(container) end -- resolve serial
670    container = container or GetPlayerBackPackContainer()
671    if (not container) then return end
672    local res = {}
673    local artidlist = (type(artid) == "number") and {artid} or artid
674    for k,item in pairs(container:GetContent()) do 
675        if (in_array(item.artid,artidlist) and (hue == nil or hue == item.hue)) then
676            table.insert(res,item)
677        end
678    end
679    return res
680end
681
682function MacroCmd_FindNearestMobByName (namepart)
683    local founddist,foundmob
684    local xloc,yloc = GetPlayerPos()
685    for k,mobile in pairs(GetMobileList()) do 
686        local dist = dist2(xloc,yloc,mobile.xloc,mobile.yloc)
687        if (((not founddist) or dist < founddist) and (not IsPlayerMobile(mobile))) then 
688            if ((not namepart) or StringContains(AosToolTip_GetText(mobile.serial) or mobile.name or "",namepart)) then
689                founddist = dist
690                foundmob = mobile
691            end
692        end
693    end
694    return foundmob
695end
696
697function MacroCmd_FindNearestMobByArtID (artid)
698    local founddist,foundmob
699    local xloc,yloc = GetPlayerPos()
700    for k,mobile in pairs(GetMobileList()) do 
701        local dist = dist2(xloc,yloc,mobile.xloc,mobile.yloc)
702        if (((not founddist) or dist < founddist) and (not IsPlayerMobile(mobile))) then 
703            if ((not artid) or mobile.artid == artid) then
704                founddist = dist
705                foundmob = mobile
706            end
707        end
708    end
709    return foundmob
710end
711
712function MacroCmd_GetNearestMobFromList (list)
713    local founddist,foundmob
714    local xloc,yloc = GetPlayerPos()
715    for k,mobile in pairs(list) do 
716        local dist = dist2(xloc,yloc,mobile.xloc,mobile.yloc)
717        if ((not founddist) or dist < founddist) then 
718            founddist = dist
719            foundmob = mobile
720        end
721    end
722    return foundmob
723end
724
725function MacroCmd_ListNonFriendlyPlayers ()
726    local res = {}
727    for k,mobile in pairs(GetMobileList()) do 
728        if ((mobile.artid == 400 or mobile.artid == 401) and 
729            (not IsMobileInParty(mobile.serial)) and
730            (not IsPlayerMobile(mobile))) then 
731           
732            local labelhue = GetItemLabelHue(mobile.serial)
733            if (labelhue ~= kPlayerVendorLabelHue) then res[mobile.serial] = mobile  end 
734        end
735    end
736    return res
737end
738
739function MacroCmd_FindNearestNonFriendlyPlayer () return MacroCmd_GetNearestMobFromList(MacroCmd_ListNonFriendlyPlayers()) end
740
741function MacroCmd_ListMobilesInRange (filterfun,maxdist)
742    local res = {}
743    for k,mobile in pairs(GetMobileList()) do 
744        if (GetUODistToPlayer(mobile.xloc,mobile.yloc) <= maxdist and filterfun(mobile)) then table.insert(res,mobile) end 
745    end 
746    return res
747end
748
749function MacroCmd_ListMobiles (filterfun)
750    local res = {}
751    for k,mobile in pairs(GetMobileList()) do if (filterfun(mobile)) then table.insert(res,mobile) end end 
752    return res
753end
754
755function MacroCmd_FindNearestMob () return MacroCmd_FindNearestMobByName() end
756
757-- set itemserial = 0 to clear
758-- itemserial defaults to the serial of the item currently under the mouse
759function MacroCmd_ItemSlot_Set  (slotnumber,itemserial)     
760    if (not gInGameStarted) then return end
761    if (not itemserial) then itemserial = GetMouseHitSerial() end -- itemserial_under_mouse
762    if (itemserial == 0) then itemserial = nil end -- set 0 to clear
763    print("MacroCmd_ItemSlot_Set",slotnumber,itemserial)
764    local item = itemserial and GetObjectBySerial(itemserial)
765    local itemname = item and GetStaticTileTypeName(item.artid) or "empty"
766    GuiAddChatLine("ItemSlot "..tostring(slotnumber).." set to "..tostring(itemname))
767    gMacroItemSlots[slotnumber] = itemserial
768end
769
770function MacroCmd_ItemSlot_Use  (slotnumber)
771    local itemserial = gMacroItemSlots[slotnumber]
772    if (not itemserial) then return MacroError("MacroCmd_ItemSlot_Use : no item in slot "..tostring(slotnumber)) end
773    print("MacroCmd_ItemSlot_Use",slotnumber,itemserial)
774    Send_DoubleClick(itemserial)
775end
776
777function MacroCmd_Skill                 (skillname)
778    if (not gInGameStarted) then return end
779    local skillid = gCharCreateSkillIDs[skillname] -- zero based
780    if (not skillid) then return MacroErrorNameMismatch("MacroCmd_Skill",skillname,gCharCreateSkillIDs) end
781    skillid = skillid + 1 -- one based needed below
782    if (glSkillActive[skillid] ~= 1) then return MacroError("MacroCmd_Skill : skill is passive : "..tostring(skillname)) end
783    Send_Request_SkillUse(skillid)
784end
785
786function MacroCmd_RepeatLastSpell ()
787    if (gLastSpellID) then Send_Spell(gLastSpellID) end
788end
789function MacroCmd_Spell                 (spellname,targetserial,targetcallback,targetwaitadd)
790    if (not gInGameStarted) then return end
791    local spellid = GetSpellIDByName(spellname)
792    if (not spellid) then return MacroErrorNameMismatch("MacroCmd_Spell",spellname,gSpellIDByName) end
793    Send_Spell(spellid)
794   
795    if (targetserial) then 
796        local timeout = GetSpellCastTimeForPlayer(spellid) + kSpellTimeLatency + (targetwaitadd or 1000)
797        MacroCmd_QueueTargetSerial(targetserial,timeout,targetcallback,true) 
798    end
799end
800
801
802-- searches the journal for text
803-- if timestamp is not nil, only entries since timestamp are searched
804-- if the text is found it returns the complete line otherwise nil
805function MacroJournal_FindLineContainingSince   (text, timestamp)
806    for k,v in pairs(gJournalExtendedEntries) do
807        if timestamp == nil or v.time >= timestamp then
808            if string.find(v.line, text) then
809                return v.line
810            end
811        end
812    end
813   
814    return nil
815end
816
817-- waits until a given text appears, list = {text=returnvalue, text=returnvalue, ...}
818function MacroJournal_WaitForText   (list,timeout)
819    if not list then return end
820   
821    local lastcheck = Client_GetTicks()
822    local endtime = timeout and lastcheck+timeout or nil
823   
824    while true do
825        for k,v in pairs(list) do
826            if MacroJournal_FindLineContainingSince(k, lastcheck) then
827                return v
828            end
829        end
830        lastcheck = Client_GetTicks()
831       
832        -- timeout check
833        if endtime and lastcheck > endtime then
834            print("TIMEOUT")
835            return nil
836        end
837
838        job.wait(100)
839    end
840end
841
842function MacroRead_GetPlayerPosition    ()
843    local sx,sy,sz
844    local mobile = GetPlayerMobile()
845    if mobile then
846        sx,sy,sz = mobile.xloc,mobile.yloc,mobile.zloc * 0.1
847    else
848        sx,sy,sz = gCurrentRenderer:GetExactLocalPos()
849    end 
850    return sx,sy,sz
851end
852
853
854function MacroReadAux_MobileStat            (mobile,statname,errormsg_funname)
855    if (not gInGameStarted) then return 0 end
856    if (not gMacroReadMobileStats[statname]) then 
857        return MacroErrorNameMismatch("MacroRead_PlayerStat",statname,gMacroReadMobileStats) 
858    end
859    local mobile = GetPlayerMobile()
860    return mobile and mobile.stats and mobile.stats[statname] or 0
861end
862
863function MacroErrorNameMismatch (cmd,name,list_by_key) 
864    local infotext = cmd.." : unknown name "..tostring(name).." available names:\n"
865    for k,v in pairs(list_by_key) do infotext = infotext..k.."\n" end
866    MacroError(infotext)
867end
868
869function MacroError (infotext) 
870    print(infotext)
871    PlainMessageBox(infotext,gGuiDefaultStyleSet,gGuiDefaultStyleSet)
872end
873
874function GetMacroKeyComboName (keycode,char,bCtrl,bAlt,bShift) 
875    local text = (keycode > 0) and GetKeyName(keycode) or ("0"..char)
876    if (bCtrl   ) then text = "ctrl+"..text end
877    if (bAlt    ) then text = "alt+"..text end
878    if (bShift  ) then text = "shift+"..text end
879    return text
880end
881
882function ClearAllMacros () gMacroList = {} end
883
884function SetMacro (keycomboname,fun) gMacroList[string.gsub(string.lower(keycomboname)," ","")] = fun end
885
886function TriggerMacros (keycode,char) 
887    local bCtrl     = gKeyPressed[key_lcontrol] or gKeyPressed[key_rcontrol]
888    local bAlt      = gKeyPressed[key_lalt]     or gKeyPressed[key_ralt]   
889    local bShift    = gKeyPressed[key_lshift]   or gKeyPressed[key_rshift]
890    local name = GetMacroKeyComboName(keycode,char,bCtrl,bAlt,bShift)
891    local macrofun = gMacroList[name]
892    if (gMacroPrintAllKeyCombos) then print('to use this macro keycombo : SetMacro("'..name..'",function() MacroCmd_Say("test") end)') end
893    if (not macrofun) then return end -- no macro mapped to this keycode
894   
895    -- protected macro call
896    local success,errormsg_or_result = lugrepcall(macrofun)
897    if (not success) then
898        local myErrorText = "ERROR executing MACRO for keycombo "..name.." :\n"..tostring(errormsg_or_result)
899        print(myErrorText)
900        PlainMessageBox(myErrorText,gGuiDefaultStyleSet,gGuiDefaultStyleSet)
901    end
902end
903
904RegisterListener("keydown",function (keycode,char,bConsumed) 
905    if (not bConsumed) then TriggerMacros(keycode,char) end
906end)
907
908--[[
909gMacroActionDescriptions = {}
910gMacroActionDescriptions.Say                        = "Open a text window where you can enter a line of dialog that your character will speak when the Macro is used."
911gMacroActionDescriptions.Emote                      = "As Say, but may be a different text color than normal speech (see \"Change Emote Color\" above). Also, any Emote text will be placed between two asterisks, for example *grin* or *Broods darkly*. The traditional function of emote text is to convey actions, attitudes, or emotions rather than simple speech."
912gMacroActionDescriptions.Whisper                    = "As Say, but whispered text (e.g., \"Psst, wanna buy a chicken?\") can only be \"heard\" by characters immediately adjacent to you."
913gMacroActionDescriptions.Yell                       = "As Say, but yelled text (e.g., \"HELP!\") can be \"heard\" by any character up to a screen and a half away."
914gMacroActionDescriptions.Walk                       = "Opens a menu of compass directions from which you choose one. Using this menu causes your character to face and take a step in the selected direction."
915gMacroActionDescriptions.War_Peace                  = "Toggles you between War mode and Peace mode."
916gMacroActionDescriptions.Paste                      = "Pastes text from your Windows clipboard into a book or speech. Text length is limited. Speech can be only a few words, while books can receive a few sentences."
917gMacroActionDescriptions.Open                       = "Opens one of your informational windows. Selecting this option will present you with a list of windows from which to select. Your Character Window is listed as \"Paperdoll\" and your Options screen as \"Configuration\"."
918gMacroActionDescriptions.Close                      = "Closes the window you specify."
919gMacroActionDescriptions.Minimize                   = "Minimizes all open windows."
920gMacroActionDescriptions.Maximize                   = "Fully opens all minimized windows on screen."
921gMacroActionDescriptions.Open_Door                  = "Opens any door within reach."
922gMacroActionDescriptions.Use_Skill                  = "Presents you with a list of all applicable skills, from which to select the specific skill you want to try to Use when you trigger this Macro. This command can only be used to initiate those skills which are normally begun from the skill list in your Character Window. It does not apply to skills initiated by using a specific item or taking a certain action."
923gMacroActionDescriptions.Last_Skill                 = "Attempts to again Use the last skill you Used."
924gMacroActionDescriptions.Cast_Spell                 = "Presents you with a list of all the spells in the game, from which you must select the specific spell you want to cast. It's up to you to ensure the spell you select is, in fact, one that you actually know how to cast."
925gMacroActionDescriptions.Last_Spell                 = "Attempts to recast the last spell you cast."
926gMacroActionDescriptions.Last_Object                = "Attempt to again use the last item you Used."
927gMacroActionDescriptions.Bow                        = "Your character will bow from the waist."
928gMacroActionDescriptions.Salute                     = "Your character will perform a military salute."
929gMacroActionDescriptions.Quit_Game                  = "Disconnects you and closes the game."
930gMacroActionDescriptions.Allnames                   = "Displays the names of every creature and character currently on screen."
931gMacroActionDescriptions.LastTarget                 = "Automatically target the last object, creature, or player that you clicked on with the targeting cursor."
932gMacroActionDescriptions.TargetSelf                 = "Targets you. Used in conjunction with other macros."
933gMacroActionDescriptions.Arm_Disarm                 = "Arms or Disarms your current or chosen weapon. You must specify an arm (right or left)."
934gMacroActionDescriptions.Wait_for_Target            = "Waits for the target cursor to become available."
935gMacroActionDescriptions.Target_Next                = "Moves your target cursor to the next available target."
936gMacroActionDescriptions.Attack_Last                = "Attacks the creature or player your last targeted."
937gMacroActionDescriptions.Delay                      = "Allows you to set a \"wait\" delay with a complex macro."
938gMacroActionDescriptions.CircleTrans                = "Allows you to toggle Circle Transparency with a macro."
939gMacroActionDescriptions.CloseGumps                 = "Closes all open pop-up messages."
940gMacroActionDescriptions.AlwaysRun                  = "Toggles the Always Run setting, which makes you always run whenever you move."
941gMacroActionDescriptions.SaveDesktop                = ""
942gMacroActionDescriptions.KillGumpOpen               = ""
943gMacroActionDescriptions.PrimaryAbility             = "Activates your weapon's primary special ability."
944gMacroActionDescriptions.SecondaryAbility           = "Activates your weapon's secondary special ability."
945gMacroActionDescriptions.EquipLastWeapon            = "Allows you to quickly switch between two weapons. Click here for more information."
946gMacroActionDescriptions.SetUpdateRange             = ""
947gMacroActionDescriptions.ModifyUpdateRange          = ""
948gMacroActionDescriptions.IncreaseUpdateRange        = ""
949gMacroActionDescriptions.DecreaseUpdateRange        = ""
950gMacroActionDescriptions.MaxUpdateRange             = ""
951gMacroActionDescriptions.MinUpdateRange             = ""
952gMacroActionDescriptions.DefaultUpdateRange         = ""
953gMacroActionDescriptions.UpdateRangeInfo            = ""
954gMacroActionDescriptions.EnableRangeColor           = ""
955gMacroActionDescriptions.DisableRangeColor          = ""
956gMacroActionDescriptions.ToggleRangeColor           = ""
957gMacroActionDescriptions.InvokeVirtue               = "Allows you to specify a virtue to be activated."
958]]--
959
960
961-- obsolete...
962gMacroListDialog = nil
963function ToggleMacroList () end
964function ToggleMacroList_OLD () 
965    if (gMacroListDialog) then CloseMacroListDialog() return end
966   
967    local rows = {
968        {   {"MacroList"} },
969        {   {type="EditText",controlname="note",w=400,h=24} },
970        {   {"Apply",function () 
971            local mytext = gMacroListDialog.controls["note"]:GetText() or ""
972            -- todo....
973            end},
974            {"Close",function () CloseMacroListDialog() end},
975        },
976    }
977    gMacroListDialog = guimaker.MakeTableDlg(rows,100,100,false,true)
978end
979
980-- obsolete...
981function CloseMacroListDialog_OLD () 
982    if (gMacroListDialog) then gMacroListDialog:Destroy() gMacroListDialog = nil end
983end
984
985function MacroCmd_WalkInDir     (iDir,bRunFlag) ExecWalkRequestIfPossible(iDir,bRunFlag) end
986
987-- run from a job (uses wait) !!!!
988-- timeout : stop walking if the target wasn't reached after this time.  0 to walk until target is reached
989
990function MacroCmd_PathFindTo    (xloc,yloc,tolerance,timeout,bNoLog)
991        tolerance = tolerance or 0
992        local bLog = not bNoLog
993        if (bLog) then print("MacroCmd_PathFindTo : start",xloc,yloc,tolerance,timeout) end
994        if (GetUODistToPlayer(xloc,yloc) <= tolerance) then 
995                if (bLog) then print("MacroCmd_PathFindTo : already there") end
996                return true 
997        end
998        local iJobWaitInterval = 50
999        timeout = timeout or 0
1000        local endt = (timeout > 0) and (Client_GetTicks() + timeout)
1001        repeat -- repeat the pathfinding calc every few seconds in case dynamics show up
1002                local t = Client_GetTicks()
1003                local res = cPathFind2:CalcRouteFromPlayerToPos(xloc,yloc,tolerance,iJobWaitInterval,endt and (endt-t)) 
1004                local t2 = Client_GetTicks()
1005                local nextstept = t2 + 1000
1006                local dt = t2-t
1007                if (bLog) then print("MacroCmd_PathFindTo: calc took ",dt,"ms",res and ("numsteps:"..#res) or "failed","curpos:"..table.concat({GetPlayerPos()},",")) end
1008                if (not res) then if (bLog) then print("MacroCmd_PathFindTo : failed, no path") end return end
1009                local bRunFlag = true
1010                local bTrySides = true
1011                for k,pos in ipairs(res) do 
1012                        local xloc,yloc,zloc = unpack(pos)
1013                        repeat
1014                                job.wait(max(10,Walk_GetTimeUntilNextStep()))
1015                                WalkStep_WalkToPosSimple(xloc,yloc,bRunFlag,bTrySides) 
1016                                if (endt and gMyTicks > endt) then 
1017                                        if (bLog) then print("MacroCmd_PathFindTo : failed, timeout") end
1018                                        return
1019                                end
1020                                if (gMyTicks > nextstept) then break end
1021                        until GetUODistToPlayer(xloc,yloc) <= 0 -- repeat (turn,walk) until this next tile reached
1022                        if (gMyTicks > nextstept) then break end
1023                end
1024                if (gMyTicks <= nextstept) then 
1025                        if (bLog) then print("MacroCmd_PathFindTo : success, finished") end
1026                        return true -- final destination reached
1027                end
1028        until false
1029end
1030
1031function MacroCmd_WalkToMouse   ()
1032    MainMousePick()
1033    local x,y,z = GetMouseHitTileCoords()
1034    SetAutoWalkTo(x,y)
1035end
1036
1037
1038gAttackRunning = false
1039function StopAttack ()
1040    gAttackRunning = false
1041end
1042
1043gAttackMobile = nil
1044function AttackMobile   (mobileserial)
1045    gAttackMobile = mobileserial
1046    if gAttackRunning then return end
1047    gAttackRunning = true
1048   
1049    job.create(function()
1050        --~ print("START ATTACK")
1051        local reqsend = false
1052        while gAttackRunning and gAttackMobile do
1053            local mobile = gMobiles[gAttackMobile]
1054            if not mobile then 
1055                gAttackRunning = false
1056            else
1057                local tx,ty,tz = mobile.xloc,mobile.yloc,mobile.zloc
1058                local px,py,pz = GetPlayerTilePosition()
1059                local dx,dy,dz = Vector.sub(px,py,pz, tx,ty,tz)
1060                dx,dy,dz = Vector.normalise_to_len(dx,dy,dz, 1)
1061                gCurrentRenderer:SetViewDir(dx,dy)
1062               
1063                --[[
1064                autowalk is not good here, because of distance attacks
1065                dx,dy,dz = Vector.add(tx,ty,tz, dx,dy,dz)
1066                dx = round(dx)
1067                dy = round(dy)
1068                SetAutoWalkTo(dx,dy)
1069                ]]
1070               
1071                if (IsWarModeActive()) then
1072                    if not reqsend then
1073                        --~ print("REQUEST SEND")
1074                        Send_AttackReq(gAttackMobile)
1075                        reqsend = true
1076                    end
1077                else 
1078                    reqsend = false
1079                end
1080               
1081                job.wait(500)
1082            end
1083        end
1084        --~ print("STOP ATTACK")
1085    end)
1086end
1087
1088function MacroGoto(x,y,slow)
1089    SetAutoWalkTo(x,y,slow)
1090    while gWalkPathToGo do
1091        job.wait(500)
1092    end
1093end
1094
1095function MacroGetItemFromBackpackByName(itemnamepart)
1096    local backpack_container = GetPlayerBackPackContainer()
1097    if backpack_container then return MacroGetItemFromContainerByName(itemnamepart, backpack_container.serial) end
1098    return nil
1099end
1100
1101function MacroGetItemFromContainerByArtidHue(artid, hue, container_serial)
1102    MacroEnsureContainerIsOpen(container_serial)
1103   
1104    local container = GetContainer(container_serial)
1105    if (not container) then return nil end
1106    for k,item in pairs(container:GetContent()) do 
1107        if item.artid == artid and (not hue or item.hue == hue) then
1108            return item
1109        end
1110    end
1111
1112    return nil
1113end
1114
1115function MacroGetItemFromContainerByName(itemnamepart, container_serial)
1116    MacroEnsureContainerIsOpen(container_serial)
1117   
1118    local container = GetContainer(container_serial)
1119    if (not container) then return nil end
1120    for k,item in pairs(container:GetContent()) do 
1121        local name = GetStaticTileTypeName(item.artid)
1122        if (name and string.find(name,itemnamepart)) then
1123            return item
1124        end
1125    end
1126
1127    return nil
1128end
1129
1130function MacroDropItemIntoContainer(dropitem, container_serial, x,y)
1131    x = x or 50
1132    y = y or 50
1133
1134    MacroEnsureContainerIsOpen(container_serial)
1135   
1136    local amount = dropitem.amount or 1
1137    local target = container_serial
1138   
1139    local container = GetContainer(container_serial)
1140    if (not container) then return nil end
1141    for k,item in pairs(container:GetContent()) do 
1142        if item.serial ~= dropitem.serial and item.artid == dropitem.artid and (not dropitem.hue or item.hue == dropitem.hue) then
1143            target = item.serial
1144        end
1145    end
1146
1147    Send_Take_Object(dropitem.serial,amount)
1148    job.wait(400)
1149    Send_Drop_Object(dropitem.serial,x,y,0,target)
1150    job.wait(400)
1151end
1152
1153function MacroDropAllIntoContainer(itemnamepart, container_serial, x,y)
1154    MacroEnsureContainerIsOpen(container_serial)
1155   
1156    x = x or 50
1157    y = y or 50
1158    local item = MacroGetItemFromBackpackByName(itemnamepart)
1159    local lastserial = nil
1160    while item do
1161        MacroDropItemIntoContainer(item, container_serial, x,y)
1162        lastserial = item.serial
1163        item = MacroGetItemFromBackpackByName(itemnamepart)
1164        -- something went wrong so stop
1165        if item and lastserial and item.serial == lastserial then return end
1166    end
1167end
1168
1169function MacroEnsureContainerIsOpen (container_serial)
1170    if not IsContainerAlreadyOpen(container_serial) then
1171        Send_DoubleClick(container_serial)
1172    end
1173    while not IsContainerAlreadyOpen(container_serial) do
1174        job.wait(100)
1175    end
1176end
1177
1178function MacroStackEverytingInContainer (container_serial)
1179    MacroEnsureContainerIsOpen(container_serial)
1180
1181    local sx = 20
1182    local sy = 20
1183    local dx = 2
1184    local dy = 2
1185    local x = sx
1186    local y = sy
1187    local limit = 150
1188   
1189    local rowh = 0
1190   
1191    local container = GetContainer(container_serial)
1192    if (not container) then return nil end
1193
1194   
1195    local l = {}
1196   
1197    -- sort content by height
1198    for k,v in pairs(container:GetContent()) do table.insert(l,v) end
1199    table.sort(l, function(a,b)
1200        local minx,miny,maxx,maxy = GetArtVisibleAABB(a.artid + 0x4000)
1201        local ha = maxy-miny
1202        minx,miny,maxx,maxy = GetArtVisibleAABB(b.artid + 0x4000)
1203        local hb = maxy-miny
1204        return ha < hb
1205    end)
1206   
1207    for k,item in ipairs(l) do 
1208        -- print("DROP AT",x,y)
1209       
1210        local minx,miny,maxx,maxy = GetArtVisibleAABB(item.artid + 0x4000)
1211        -- print("AABB",minx,miny,maxx,maxy)
1212        --local w,h = GetArtSize(item.artid + 0x4000)
1213        local w,h = maxx-minx,maxy-miny
1214       
1215        rowh = math.max(h, rowh)
1216       
1217        -- print("w,h,rowh",w,h,rowh,x,y)
1218       
1219        MacroDropItemIntoContainer(item, container_serial, x-minx,y-miny)
1220       
1221        -- goto next position
1222        x = x + w + dx
1223        if x > limit then
1224            x = sx
1225            y = y + rowh + dy
1226        end
1227    end
1228end
1229
1230function MacroGetSerialUnderMouse   ()
1231    return GetMouseHitSerial(true)
1232end
1233
1234
1235-- color is #000000 format, timeout in ms
1236function Macro_ShowTimeout (x,y,w,h,text,color,timeout)
1237    if timeout > 0 then
1238        local params = {
1239            gfxparam_bar = MakeSpritePanelParam_BorderPartMatrix(GetPlainTextureGUIMat("ray_border.png"),32,32, 0,0, 0,0, 1,30,1, 1,30,1, 32,32, 1,1, false, false),
1240            gfxparam_background = MakeSpritePanelParam_BorderPartMatrix(GetPlainTextureGUIMat("ray_border_black.png"),32,32, 0,0, 0,0, 1,30,1, 1,30,1, 32,32, 1,1, false, false),
1241        }
1242       
1243        local progress = GetDesktopWidget():CreateChild("Bar",params)
1244        progress:SetLeftTop(x,y)
1245        progress:SetSize(w,h)
1246        progress:SetProgress(0)
1247        progress:CreateContentChild("UOText",{text="<BASEFONT COLOR="..color..">"..text.."</BASEFONT>",x=5,y=-2,width=w,height=h,background=0,scrollbar=0,bold=false,crop=false,html=true})
1248       
1249        local startt = Client_GetTicks()
1250       
1251        job.create(function()
1252            local p = 0
1253            repeat
1254                p = Clamp((Client_GetTicks() - startt) / timeout, 0, 1)
1255                progress:SetProgress(p)
1256                job.wait(10)
1257            until p == 1
1258            progress:Destroy()
1259        end)
1260    end
1261end
1262
1263
1264-- disconnect from server + login with a different char/acc   (experimental, no error handling)
1265function MacroCmd_ReLogin (shardname,user,pass,charidx)
1266        print("MacroCmd_Relog",shardname,user,charidx)
1267        NetDisconnect()
1268    gHuffmanDecode = false
1269    gInGameStarted = false
1270       
1271        -- close healthbars
1272        for serial,dialog in pairs(gHealthbarDialogs) do dialog:Destroy() end gHealthbarDialogs = {}
1273       
1274        for k,dynamic in pairs(GetDynamicList()) do if (DynamicIsInWorld(dynamic)) then dynamic:Destroy() end end
1275        for k,mobile in pairs(GetMobileList()) do mobile:Destroy() end
1276       
1277        -- ClearDynamicsAndMobiles : not really needed since dynamics and mobiles are destroyed above
1278        if (gCurrentRenderer.ClearDynamicsAndMobiles) then
1279                gCurrentRenderer:ClearDynamicsAndMobiles()
1280        else
1281                gCurrentRenderer:DeInit()
1282        end
1283       
1284        local shard = gShardList[shardname]  assert(shard)
1285        gShardName = shardname
1286    LoadShardfilter(shard.gCustomArtFilterFilePath) -- todo : revert on error or back-button ?
1287       
1288    -- load global config from shard
1289    for k,v in pairs(shard) do _G[k] = v end
1290       
1291        gLoginname = user
1292        gPassword = pass -- MainMenu_GetStoredPassword(shard.gLoginServerIP,shard.gLoginServerPort,gLoginname)
1293       
1294    -- init net
1295    gNet_State = NetConnectWithKey(gLoginServerIP,gLoginServerPort,gServerSeed)
1296        assert(gNet_State)
1297   
1298        gAutoLoginCharID = nil
1299        gAutoLoginCharName = nil
1300        gAutoLoginCharID = charidx
1301    Send_Account_Login_Request(user,pass) -- 0x80 kPacket_Account_Login_Request
1302end
Note: See TracBrowser for help on using the browser.