Just companion orders tool
							parent
							
								
									49476818c4
								
							
						
					
					
						commit
						1b5a6616e2
					
				| @ -0,0 +1,398 @@ | |||||||
|  | 
 | ||||||
|  | local gui = require 'gui' | ||||||
|  | local dlg = require 'gui.dialogs' | ||||||
|  | 
 | ||||||
|  | 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_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) 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 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)<trg_layer.size+sb.layer_size then | ||||||
|  |         return true | ||||||
|  |     end | ||||||
|  |     return false | ||||||
|  | end | ||||||
|  | function AddLayering(body_part,item) | ||||||
|  |     if not DoesHaveSubtype(item) then | ||||||
|  |         return | ||||||
|  |     end | ||||||
|  |     local sb=item.subtype.props | ||||||
|  |     local trg_layer=body_part.layers[sb.layer] | ||||||
|  |     trg_layer.permit=math.min(trg_layer.permit,sb.layer_permit) | ||||||
|  |     trg_layer.size=trg_layer.size+sb.layer_size | ||||||
|  | end | ||||||
|  | function AddIfFits(body_equip,unit,item) | ||||||
|  |     --TODO shaped items | ||||||
|  |      | ||||||
|  |     local need_flag | ||||||
|  |     for k,v in pairs(permited_equips) do | ||||||
|  |         if k:is_instance(item) then | ||||||
|  |             need_flag=v | ||||||
|  |             break | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  |     if need_flag==nil then | ||||||
|  |         return false | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     | ||||||
|  |     for k,bp in pairs(body_equip) do | ||||||
|  |         local handedness_ok=true | ||||||
|  |         if df.item_glovesst:is_instance(item) then | ||||||
|  |             if bp.bp.flags["LEFT"]~=item.handedness[1] then | ||||||
|  |                 handedness_ok=false | ||||||
|  |             end | ||||||
|  |         end | ||||||
|  |         if bp.bp.flags[need_flag] and LayeringPermits(bp,item) and handedness_ok then | ||||||
|  |             if dfhack.items.moveToInventory(item,unit,2,k) then | ||||||
|  |                 AddLayering(bp,item) | ||||||
|  |                 return true | ||||||
|  |             end | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  |     return false | ||||||
|  | end | ||||||
|  | function EnumGrasps(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 | ||||||
|  |         if v.flags.GRASP then | ||||||
|  |             --table.insert(ret,{k,v}) | ||||||
|  |             ret[k]={v} | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  |     return ret | ||||||
|  | end | ||||||
|  | function EnumEmptyGrasps(unit) | ||||||
|  |     local grasps=EnumGrasps(unit.race,unit.caste) | ||||||
|  |     local ret={} | ||||||
|  |     for k,v in pairs(unit.inventory) do | ||||||
|  |         if grasps[v.body_part_id] and v.mode==1 then | ||||||
|  |             grasps[v.body_part_id][2]=true | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  |     for k,v in pairs(grasps) do | ||||||
|  |         if not v[2] then | ||||||
|  |             table.insert(ret,k) | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  |     return ret | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | function GetBackpack(unit) | ||||||
|  |     for k,v in pairs(unit.inventory) do | ||||||
|  |         | ||||||
|  |         if df.item_backpackst:is_instance(v.item) then | ||||||
|  |             return v.item | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  | end | ||||||
|  | function AddBackpackItems(backpack,items) | ||||||
|  |     if backpack then | ||||||
|  |         local bitems=dfhack.items.getContainedItems(backpack) | ||||||
|  |         for k,v in pairs(bitems) do | ||||||
|  |             table.insert(items,v) | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  | end | ||||||
|  | function GetItemsAtPos(pos) | ||||||
|  |     local ret={} | ||||||
|  |     for k,v in pairs(df.global.world.items.all) do | ||||||
|  |         if v.flags.on_ground and v.pos.x==pos.x and v.pos.y==pos.y and v.pos.z==pos.z then | ||||||
|  |             table.insert(ret,v) | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  |     return ret | ||||||
|  | end | ||||||
|  | function isAnyOfEquipable(item) | ||||||
|  |     for k,v in pairs(permited_equips) do | ||||||
|  |         if k:is_instance(item) then | ||||||
|  |             return true | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  |     return false | ||||||
|  | end | ||||||
|  | function FilterByEquipable(items) | ||||||
|  |     local ret={} | ||||||
|  |     for k,v in pairs(items) do | ||||||
|  |         if isAnyOfEquipable(v) then | ||||||
|  |             table.insert(ret,v) | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  |     return ret | ||||||
|  | end | ||||||
|  | function FilterBySize(items,race_id) --TODO add logic for compatible races | ||||||
|  |     local ret={} | ||||||
|  |     for k,v in pairs(items) do | ||||||
|  |         if v.maker_race==race_id then | ||||||
|  |             table.insert(ret,v) | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  |     return ret | ||||||
|  | end | ||||||
|  | --local companions=?? | ||||||
|  | local orders={ | ||||||
|  | {name="move",f=function (unit_list,pos) | ||||||
|  |     if not CheckCursor(pos) then | ||||||
|  |         return false | ||||||
|  |     end | ||||||
|  |     for k,v in pairs(unit_list) do | ||||||
|  |         v.path.dest:assign(pos) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     return true | ||||||
|  | end}, | ||||||
|  | {name="equip",f=function (unit_list) | ||||||
|  |     --search in inventory(hands/backpack/ground) and equip everything | ||||||
|  |     --lot's of stuff to think: layering, which item goes where, which goes first, which body parts are missing, body sizes etc... | ||||||
|  |     --dfhack.items.moveToInventory(item,unit,use_mode,body_part) | ||||||
|  |     for k,unit in pairs(unit_list) do | ||||||
|  |         local items=GetItemsAtPos(unit.pos) | ||||||
|  |         --todo make a table join function or sth... submit it to the lua list! | ||||||
|  |         AddBackpackItems(GetBackpack(unit),items) | ||||||
|  |         items=FilterByEquipable(items) | ||||||
|  |         FilterBySize(items,unit.race)  | ||||||
|  |         local body_parts=EnumBodyEquipable(unit.race,unit.caste) | ||||||
|  |         ReadCurrentEquiped(body_parts,unit) | ||||||
|  |         for it_num,item in pairs(items) do | ||||||
|  |             AddIfFits(body_parts,unit,item) | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  | end}, | ||||||
|  | {name="pick-up",f=function (unit_list) | ||||||
|  |     --pick everything up (first into hands (if empty) then backpack (if have and has space?)) | ||||||
|  |     for k,v in pairs(unit_list) do | ||||||
|  |         local items=GetItemsAtPos(v.pos) | ||||||
|  |         local grasps=EnumEmptyGrasps(v) | ||||||
|  |         -- TODO sort with weapon/shield on top of list! | ||||||
|  |         --add to grasps, then add to backpack (sanely? i.e. weapons/shields into hands then stuff) | ||||||
|  |         --or add to backpack if have, only then check grasps (faster equiping) | ||||||
|  |         while #grasps >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="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={} | ||||||
|  | --[[ 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 | ||||||
|  |             --do order | ||||||
|  |         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 | ||||||
|  | end | ||||||
|  | local screen=CompanionUi{unit_list=getCompanions()} | ||||||
|  | screen:show() | ||||||
		Loading…
	
		Reference in New Issue