--[[=begin gui/companion-order =================== A script to issue orders for companions. Select companions with lower case chars, issue orders with upper case. Must be in look or talk mode to issue command on tile. .. image:: /docs/images/companion-order.png * move - orders selected companions to move to location. If companions are following they will move no more than 3 tiles from you. * equip - try to equip items on the ground. * pick-up - try to take items into hand (also wield) * unequip - remove and drop equipment * unwield - drop held items * wait - temporarily remove from party * follow - rejoin the party after "wait" * leave - remove from party (can be rejoined by talking) =end]] local gui = require 'gui' local dlg = require 'gui.dialogs' local args={...} local is_cheat=(#args>0 and args[1]=="-c") local cursor=xyz2pos(df.global.cursor.x,df.global.cursor.y,df.global.cursor.z) local permited_equips={} permited_equips[df.item_backpackst]="UPPERBODY" permited_equips[df.item_quiverst]="UPPERBODY" permited_equips[df.item_flaskst]="UPPERBODY" permited_equips[df.item_armorst]="UPPERBODY" permited_equips[df.item_shoesst]="STANCE" permited_equips[df.item_glovesst]="GRASP" permited_equips[df.item_helmst]="HEAD" permited_equips[df.item_pantsst]="LOWERBODY" function DoesHaveSubtype(item) if df.item_backpackst:is_instance(item) or df.item_flaskst:is_instance(item) or df.item_quiverst:is_instance(item) then return false end return true end function CheckCursor(p) if p.x==-30000 then dlg.showMessage( 'Companion orders', 'You must have a cursor on some tile!', COLOR_LIGHTRED ) return false end return true end function getxyz() -- this will return pointers x,y and z coordinates. local x=df.global.cursor.x local y=df.global.cursor.y local z=df.global.cursor.z return x,y,z -- return the coords end function GetCaste(race_id,caste_id) local race=df.creature_raw.find(race_id) return race.caste[caste_id] end function EnumBodyEquipable(race_id,caste_id) local caste=GetCaste(race_id,caste_id) local bps=caste.body_info.body_parts local ret={} for k,v in pairs(bps) do ret[k]={bp=v,layers={[0]={size=0,permit=0},[1]={size=0,permit=0},[2]={size=0,permit=0},[3]={size=0,permit=0} } } end return ret end function ReadCurrentEquiped(body_equip,unit) for k,v in pairs(unit.inventory) do if v.mode==2 then local bpid=v.body_part_id if DoesHaveSubtype(v.item) then local sb=v.item.subtype.props local trg=body_equip[bpid] local trg_layer=trg.layers[sb.layer] if trg_layer.permit==0 then trg_layer.permit=sb.layer_permit else if trg_layer.permit>sb.layer_permit then trg_layer.permit=sb.layer_permit end end trg_layer.size=trg_layer.size+sb.layer_size end end end end function LayeringPermits(body_part,item) if not DoesHaveSubtype(item) then return true end local sb=item.subtype.props local trg_layer=body_part.layers[sb.layer] if math.min(trg_layer.permit ,sb.layer_permit)0 and #items>0 do if(dfhack.items.moveToInventory(items[#items],v,1,grasps[#grasps])) then table.remove(grasps) end table.remove(items) end local backpack=GetBackpack(v) if backpack then while #items>0 do dfhack.items.moveToContainer(items[#items],backpack) table.remove(items) end end end return true end}, {name="unequip",f=function (unit_list) --remove and drop all the stuff (todo maybe a gui too?) for k,v in pairs(unit_list) do while #v.inventory ~=0 do dfhack.items.moveToGround(v.inventory[0].item,v.pos) end end return true end}, {name="unwield",f=function (unit_list) for k,v in pairs(unit_list) do local wep_count=0 for _,it in pairs(v.inventory) do if it.mode==1 then wep_count=wep_count+1 end end for i=1,wep_count do for _,it in pairs(v.inventory) do if it.mode==1 then dfhack.items.moveToGround(it.item,v.pos) break end end end end return true end}, --[=[ {name="roam not working :<",f=function (unit_list,pos,dist) --does not work if not CheckCursor(pos) then return false end dist=dist or 5 for k,v in pairs(unit_list) do v.idle_area:assign(pos) v.idle_area_threshold=dist end return true end}, --]=] {name="wait",f=function (unit_list) for k,v in pairs(unit_list) do v.relations.group_leader_id=-1 end return true end}, {name="follow",f=function (unit_list) local adv=df.global.world.units.active[0] for k,v in pairs(unit_list) do v.relations.group_leader_id=adv.id end return true end}, {name="leave",f=function (unit_list) local adv=df.global.world.units.active[0] local t_nem=dfhack.units.getNemesis(adv) for k,v in pairs(unit_list) do v.relations.group_leader_id=-1 local u_nem=dfhack.units.getNemesis(v) if u_nem then u_nem.group_leader_id=-1 end if t_nem and u_nem then for k,v in pairs(t_nem.companions) do if v==u_nem.id then t_nem.companions:erase(k) break end end end end return true end}, } local cheats={ {name="Patch up",f=function (unit_list) local dft=require("plugins.dfusion.tools") for k,v in pairs(unit_list) do dft.healunit(v) end return true end}, {name="Power up",f=function (unit_list) local dft=require("plugins.dfusion.tools") for k,d in pairs(unit_list) do dft.powerup(d) end return true end}, {name="get in",f=function (unit_list,pos) if not CheckCursor(pos) then return false end adv=df.global.world.units.active[0] item=getItemsAtPos(getxyz())[1] print(item.id) for k,v in pairs(unit_list) do v.riding_item_id=item.id local ref=df.general_ref_unit_riderst:new() ref.unit_id=v.id item.general_refs:insert("#",ref) end return true end}, } --[[ todo: add cheats...]]-- function getCompanions(unit) unit=unit or df.global.world.units.active[0] local t_nem=dfhack.units.getNemesis(unit) if t_nem==nil then qerror("Invalid unit! No nemesis record") end local ret={} for k,v in pairs(t_nem.companions) do local u=df.nemesis_record.find(v) if u.unit then table.insert(ret,u.unit) end end return ret end CompanionUi=defclass(CompanionUi,gui.FramedScreen) CompanionUi.ATTRS{ frame_title = "Companions", } function CompanionUi:init(args) self.unit_list=args.unit_list self.selected={} for i=0,26 do self.selected[i]=true end end function CompanionUi:GetSelectedUnits() local ret={} for k,v in pairs(self.unit_list) do if self.selected[k] then table.insert(ret,v) end end return ret end function CompanionUi:onInput(keys) if keys.LEAVESCREEN then self:dismiss() elseif keys._STRING then local s=keys._STRING if s==string.byte('*') then local v=self.selected[1] or false for i=0,26 do self.selected[i]=not v end end if s>=string.byte('a') and s<=string.byte('z') then local idx=s-string.byte('a')+1 if self.selected[idx] then self.selected[idx]=false else self.selected[idx]=true end end if s>=string.byte('A') and s<=string.byte('Z') then local idx=s-string.byte('A')+1 if orders[idx] and orders[idx].f then if orders[idx].f(self:GetSelectedUnits(),cursor) then self:dismiss() end end if is_cheat then idx=idx-#orders if cheats[idx] and cheats[idx].f then if cheats[idx].f(self:GetSelectedUnits(),cursor) then self:dismiss() end end end end end end function CompanionUi:onRenderBody( dc) --list widget goes here... local char_a=string.byte('a')-1 dc:newline(1):string("*. All") for k,v in ipairs(self.unit_list) do if self.selected[k] then dc:pen(COLOR_GREEN) else dc:pen(COLOR_GREY) end dc:newline(1):string(string.char(k+char_a)..". "):string(dfhack.TranslateName(v.name)); end dc:pen(COLOR_GREY) local w,h=self:getWindowSize() local char_A=string.byte('A')-1 for k,v in ipairs(orders) do dc:seek(w/2,k):string(string.char(k+char_A)..". "):string(v.name); end if is_cheat then for k,v in ipairs(cheats) do dc:seek(w/2,k+#orders):string(string.char(k+#orders+char_A)..". "):string(v.name); end end end local screen=CompanionUi{unit_list=getCompanions()} screen:show()