-- allows to do jobs in adv. mode. local gui = require 'gui' local wid=require 'gui.widgets' local dialog=require 'gui.dialogs' local buildings=require 'dfhack.buildings' mode=mode or 0 keybinds={ key_next={key="CUSTOM_SHIFT_T",desc="Next job in the list"}, key_prev={key="CUSTOM_SHIFT_R",desc="Previous job in the list"}, key_continue={key="A_WAIT",desc="Continue job if available"}, key_down_alt1={key="CUSTOM_CTRL_D",desc="Use job down"},--does not work? key_down_alt2={key="CURSOR_DOWN_Z_AUX",desc="Use job down"}, key_up_alt1={key="CUSTOM_CTRL_E",desc="Use job up"}, --does not work? key_up_alt2={key="CURSOR_UP_Z_AUX",desc="Use job up"}, key_use_same={key="A_MOVE_SAME_SQUARE",desc="Use job at the tile you are standing"}, } function Disclaimer(tlb) local dsc={"The Gathering Against ",{text="Goblin ",pen=dfhack.pen.parse{fg=COLOR_GREEN,bg=0}}, "Oppresion ", "(TGAGO) is not responsible for all ",NEWLINE,"the damage that this tool can (and will) cause to you and your loved worlds",NEWLINE,"and/or sanity.Please use with caution.",NEWLINE,{text="Magma not included.",pen=dfhack.pen.parse{fg=COLOR_LIGHTRED,bg=0}}} if tlb then for _,v in ipairs(dsc) do table.insert(tlb,v) end end return dsc end function showHelp() local helptext={ "This tool allow you to perform jobs as a dwarf would in dwarf mode. When ",NEWLINE, "cursor is available you can press ",{key="SELECT", text="select",key_sep="()"}, " to enqueue a job from",NEWLINE,"pointer location. If job is 'Build' and there is no planed construction",NEWLINE, "at cursor this tool show possible building choices.",NEWLINE,NEWLINE,{text="Keybindings:",pen=dfhack.pen.parse{fg=COLOR_CYAN,bg=0}},NEWLINE } for k,v in pairs(keybinds) do table.insert(helptext,{key=v.key,text=v.desc,key_sep=":"}) table.insert(helptext,NEWLINE) end table.insert(helptext,{text="CAREFULL MOVE",pen=dfhack.pen.parse{fg=COLOR_LIGHTGREEN,bg=0}}) table.insert(helptext,": use job in that direction") table.insert(helptext,NEWLINE) table.insert(helptext,NEWLINE) Disclaimer(helptext) require("gui.dialogs").showMessage("Help!?!",helptext) end function getLastJobLink() local st=df.global.world.job_list while st.next~=nil do st=st.next end return st end function AddNewJob(job) local nn=getLastJobLink() local nl=df.job_list_link:new() nl.prev=nn nn.next=nl nl.item=job job.list_link=nl end function MakeJob(unit,pos,job_type,unit_pos,post_actions) local nj=df.job:new() nj.id=df.global.job_next_id df.global.job_next_id=df.global.job_next_id+1 --nj.flags.special=true nj.job_type=job_type nj.completion_timer=-1 --nj.unk4a=12 --nj.unk4b=0 nj.pos:assign(pos) AssignUnitToJob(nj,unit,unit_pos) for k,v in ipairs(post_actions or {}) do v{job=nj,pos=pos,old_pos=unit_pos,unit=unit} end AddNewJob(nj) return nj end function AssignUnitToJob(job,unit,unit_pos) job.general_refs:insert("#",{new=df.general_ref_unit_workerst,unit_id=unit.id}) unit.job.current_job=job unit_pos=unit_pos or {x=job.pos.x,y=job.pos.y,z=job.pos.z} unit.path.dest:assign(unit_pos) end function SetCreatureRef(args) local job=args.job local pos=args.pos for k,v in pairs(df.global.world.units.active) do if v.pos.x==pos.x and v.pos.y==pos.y and v.pos.z==pos.z then job.general_refs:insert("#",{new=df.general_ref_unit_cageest,unit_id=v.id}) return end end end function SetPatientRef(args) local job=args.job local pos=args.pos for k,v in pairs(df.global.world.units.active) do if v.pos.x==pos.x and v.pos.y==pos.y and v.pos.z==pos.z then job.general_refs:insert("#",{new=df.general_ref_unit_patientst,unit_id=v.id}) return end end end function MakePredicateWieldsItem(item_skill) local pred=function(args) local inv=args.unit.inventory for k,v in pairs(inv) do if v.mode==1 and df.item_weaponst:is_instance(v.item) then if v.item.subtype.skill_melee==item_skill and args.unit.body.weapon_bp==v.body_part_id then return true end end end return false,"Correct tool not equiped" end return pred end function makeset(args) local tbl={} for k,v in pairs(args) do tbl[v]=true end return tbl end function IsConstruct(args) local tt=dfhack.maps.getTileType(args.pos) local cwalls=makeset{ df.tiletype.ConstructedWallRD2, df.tiletype.ConstructedWallR2D, df.tiletype.ConstructedWallR2U, df.tiletype.ConstructedWallRU2, df.tiletype.ConstructedWallL2U, df.tiletype.ConstructedWallLU2, df.tiletype.ConstructedWallL2D, df.tiletype.ConstructedWallLD2, df.tiletype.ConstructedWallLRUD, df.tiletype.ConstructedWallRUD, df.tiletype.ConstructedWallLRD, df.tiletype.ConstructedWallLRU, df.tiletype.ConstructedWallLUD, df.tiletype.ConstructedWallRD, df.tiletype.ConstructedWallRU, df.tiletype.ConstructedWallLU, df.tiletype.ConstructedWallLD, df.tiletype.ConstructedWallUD, df.tiletype.ConstructedWallLR,} if cwalls[tt] or dfhack.buildings.findAtTile(args.pos) then return true else return false, "Can only do it on constructions" end end function IsWall(args) local tt=dfhack.maps.getTileType(args.pos) local walls=makeset{df.tiletype.StoneWallWorn1, df.tiletype.StoneWallWorn2, df.tiletype.StoneWallWorn3, df.tiletype.StoneWall, df.tiletype.SoilWall, df.tiletype.LavaWallSmoothRD2, df.tiletype.LavaWallSmoothR2D, df.tiletype.LavaWallSmoothR2U, df.tiletype.LavaWallSmoothRU2, df.tiletype.LavaWallSmoothL2U, df.tiletype.LavaWallSmoothLU2, df.tiletype.LavaWallSmoothL2D, df.tiletype.LavaWallSmoothLD2, df.tiletype.LavaWallSmoothLRUD, df.tiletype.LavaWallSmoothRUD, df.tiletype.LavaWallSmoothLRD, df.tiletype.LavaWallSmoothLRU, df.tiletype.LavaWallSmoothLUD, df.tiletype.LavaWallSmoothRD, df.tiletype.LavaWallSmoothRU, df.tiletype.LavaWallSmoothLU, df.tiletype.LavaWallSmoothLD, df.tiletype.LavaWallSmoothUD, df.tiletype.LavaWallSmoothLR, df.tiletype.FeatureWallSmoothRD2, df.tiletype.FeatureWallSmoothR2D, df.tiletype.FeatureWallSmoothR2U, df.tiletype.FeatureWallSmoothRU2, df.tiletype.FeatureWallSmoothL2U, df.tiletype.FeatureWallSmoothLU2, df.tiletype.FeatureWallSmoothL2D, df.tiletype.FeatureWallSmoothLD2, df.tiletype.FeatureWallSmoothLRUD, df.tiletype.FeatureWallSmoothRUD, df.tiletype.FeatureWallSmoothLRD, df.tiletype.FeatureWallSmoothLRU, df.tiletype.FeatureWallSmoothLUD, df.tiletype.FeatureWallSmoothRD, df.tiletype.FeatureWallSmoothRU, df.tiletype.FeatureWallSmoothLU, df.tiletype.FeatureWallSmoothLD, df.tiletype.FeatureWallSmoothUD, df.tiletype.FeatureWallSmoothLR, df.tiletype.StoneWallSmoothRD2, df.tiletype.StoneWallSmoothR2D, df.tiletype.StoneWallSmoothR2U, df.tiletype.StoneWallSmoothRU2, df.tiletype.StoneWallSmoothL2U, df.tiletype.StoneWallSmoothLU2, df.tiletype.StoneWallSmoothL2D, df.tiletype.StoneWallSmoothLD2, df.tiletype.StoneWallSmoothLRUD, df.tiletype.StoneWallSmoothRUD, df.tiletype.StoneWallSmoothLRD, df.tiletype.StoneWallSmoothLRU, df.tiletype.StoneWallSmoothLUD, df.tiletype.StoneWallSmoothRD, df.tiletype.StoneWallSmoothRU, df.tiletype.StoneWallSmoothLU, df.tiletype.StoneWallSmoothLD, df.tiletype.StoneWallSmoothUD, df.tiletype.StoneWallSmoothLR, df.tiletype.LavaWallWorn1, df.tiletype.LavaWallWorn2, df.tiletype.LavaWallWorn3, df.tiletype.LavaWall, df.tiletype.FeatureWallWorn1, df.tiletype.FeatureWallWorn2, df.tiletype.FeatureWallWorn3, df.tiletype.FeatureWall, df.tiletype.FrozenWallWorn1, df.tiletype.FrozenWallWorn2, df.tiletype.FrozenWallWorn3, df.tiletype.FrozenWall, df.tiletype.MineralWallSmoothRD2, df.tiletype.MineralWallSmoothR2D, df.tiletype.MineralWallSmoothR2U, df.tiletype.MineralWallSmoothRU2, df.tiletype.MineralWallSmoothL2U, df.tiletype.MineralWallSmoothLU2, df.tiletype.MineralWallSmoothL2D, df.tiletype.MineralWallSmoothLD2, df.tiletype.MineralWallSmoothLRUD, df.tiletype.MineralWallSmoothRUD, df.tiletype.MineralWallSmoothLRD, df.tiletype.MineralWallSmoothLRU, df.tiletype.MineralWallSmoothLUD, df.tiletype.MineralWallSmoothRD, df.tiletype.MineralWallSmoothRU, df.tiletype.MineralWallSmoothLU, df.tiletype.MineralWallSmoothLD, df.tiletype.MineralWallSmoothUD, df.tiletype.MineralWallSmoothLR, df.tiletype.MineralWallWorn1, df.tiletype.MineralWallWorn2, df.tiletype.MineralWallWorn3, df.tiletype.MineralWall, df.tiletype.FrozenWallSmoothRD2, df.tiletype.FrozenWallSmoothR2D, df.tiletype.FrozenWallSmoothR2U, df.tiletype.FrozenWallSmoothRU2, df.tiletype.FrozenWallSmoothL2U, df.tiletype.FrozenWallSmoothLU2, df.tiletype.FrozenWallSmoothL2D, df.tiletype.FrozenWallSmoothLD2, df.tiletype.FrozenWallSmoothLRUD, df.tiletype.FrozenWallSmoothRUD, df.tiletype.FrozenWallSmoothLRD, df.tiletype.FrozenWallSmoothLRU, df.tiletype.FrozenWallSmoothLUD, df.tiletype.FrozenWallSmoothRD, df.tiletype.FrozenWallSmoothRU, df.tiletype.FrozenWallSmoothLU, df.tiletype.FrozenWallSmoothLD, df.tiletype.FrozenWallSmoothUD, df.tiletype.FrozenWallSmoothLR, } if walls[tt] then return true else return false, "Can only do it on walls" end end function IsTree(args) local tt=dfhack.maps.getTileType(args.pos) if tt==24 then return true else return false, "Can only do it on trees" end end function IsWater(args) return true end function IsPlant(args) return true end function IsUnit(args) local pos=args.pos for k,v in pairs(df.global.world.units.active) do if v.pos.x==pos.x and v.pos.y==pos.y and v.pos.z==pos.z then return true end end return false,"Unit must be present" end function itemsAtPos(pos) local ret={} for k,v in pairs(df.global.world.items.all) do if v.pos.x==pos.x and v.pos.y==pos.y and v.pos.z==pos.z and v.flags.on_ground then table.insert(ret,v) end end return ret end function AssignBuildingRef(args) local bld=dfhack.buildings.findAtTile(args.pos) args.job.general_refs:insert("#",{new=df.general_ref_building_holderst,building_id=bld.id}) bld.jobs:insert("#",args.job) end --[[ building submodule... ]]-- function DialogBuildingChoose(on_select, on_cancel) blist={} for i=df.building_type._first_item,df.building_type._last_item do table.insert(blist,df.building_type[i]) end dialog.showListPrompt("Building list", "Choose building:", COLOR_WHITE, blist, on_select, on_cancel, nil, true) end function DialogSubtypeChoose(subtype,on_select, on_cancel) blist={} for i=subtype._first_item,subtype._last_item do table.insert(blist,subtype[i]) end dialog.showListPrompt("Subtype", "Choose subtype:", COLOR_WHITE, blist, on_select, on_cancel, nil, true) end --workshop, furnaces, traps invalid_buildings={} function SubtypeChosen(args,index) args.subtype=index-1 buildings.constructBuilding(args) end function BuildingChosen(st_pos,pos,index) local b_type=index-2 local args={} args.type=b_type args.pos=pos args.items=itemsAtPos(st_pos) if invalid_buildings[b_type] then return elseif b_type==df.building_type.Construction then DialogSubtypeChoose(df.construction_type,dfhack.curry(SubtypeChosen,args)) return elseif b_type==df.building_type.Furnace then DialogSubtypeChoose(df.furnace_type,dfhack.curry(SubtypeChosen,args)) return elseif b_type==df.building_type.Trap then DialogSubtypeChoose(df.trap_type,dfhack.curry(SubtypeChosen,args)) return elseif b_type==df.building_type.Workshop then DialogSubtypeChoose(df.workshop_type,dfhack.curry(SubtypeChosen,args)) return else buildings.constructBuilding(args) end end --[[ end of buildings ]]-- function AssignJobToBuild(args) local bld=dfhack.buildings.findAtTile(args.pos) if bld~=nil then if #bld.jobs>0 then AssignUnitToJob(bld.jobs[0],args.unit,args.old_pos) else local jb=MakeJob(args.unit,args.pos,df.job_type.ConstructBuilding,args.old_pos,{AssignBuildingRef}) local its=itemsAtPos(args.old_pos) for k,v in pairs(its) do jb.items:insert("#",{new=true,item=v,role=2}) end end else DialogBuildingChoose(dfhack.curry(BuildingChosen,args.old_pos,args.pos)) end end function ContinueJob(unit) local c_job=unit.job.current_job if c_job then for k,v in pairs(c_job.items) do if v.is_fetching==1 then unit.path.dest:assign(v.item.pos) return end end unit.path.dest:assign(c_job.pos) end end dig_modes={ {"CarveFortification" ,df.job_type.CarveFortification,{IsWall}}, {"DetailWall" ,df.job_type.DetailWall,{IsWall}}, {"DetailFloor" ,df.job_type.DetailFloor}, --{"CarveTrack" ,df.job_type.CarveTrack}, -- does not work?? {"Dig" ,df.job_type.Dig,{MakePredicateWieldsItem(df.job_skill.MINING),IsWall}}, {"CarveUpwardStaircase" ,df.job_type.CarveUpwardStaircase,{MakePredicateWieldsItem(df.job_skill.MINING),IsWall}}, {"CarveDownwardStaircase",df.job_type.CarveDownwardStaircase,{MakePredicateWieldsItem(df.job_skill.MINING)}}, {"CarveUpDownStaircase" ,df.job_type.CarveUpDownStaircase,{MakePredicateWieldsItem(df.job_skill.MINING)}}, {"CarveRamp" ,df.job_type.CarveRamp,{MakePredicateWieldsItem(df.job_skill.MINING),IsWall}}, {"DigChannel" ,df.job_type.DigChannel,{MakePredicateWieldsItem(df.job_skill.MINING)}}, {"FellTree" ,df.job_type.FellTree,{MakePredicateWieldsItem(df.job_skill.AXE),IsTree}}, {"Fish" ,df.job_type.Fish,{IsWater}}, --{"Diagnose Patient" ,df.job_type.DiagnosePatient,{IsUnit},{SetPatientRef}}, --{"Surgery" ,df.job_type.Surgery,{IsUnit},{SetPatientRef}}, --{"TameAnimal" ,df.job_type.TameAnimal,{IsUnit},{SetCreatureRef}}, {"GatherPlants" ,df.job_type.GatherPlants,{IsPlant}}, {"RemoveConstruction" ,df.job_type.RemoveConstruction,{IsConstruct}}, --{"HandleLargeCreature" ,df.job_type.HandleLargeCreature,{isUnit},{SetCreatureRef}}, {"Build" ,AssignJobToBuild}, } usetool=defclass(usetool,gui.Screen) function usetool:getModeName() local adv=df.global.world.units.active[0] if adv.job.current_job then return string.format("%s working(%d) ",(dig_modes[(mode or 0)+1][1] or ""),adv.job.current_job.completion_timer) else return dig_modes[(mode or 0)+1][1] or " " end end function usetool:init(args) self:addviews{ wid.Label{ frame = {xalign=0,yalign=0}, text={{key=keybinds.key_prev.key},{gap=1,text=dfhack.curry(usetool.getModeName,self)},{gap=1,key=keybinds.key_next.key}} } } end function usetool:onRenderBody(dc) self:renderParent() end function usetool:onIdle() self._native.parent:logic() end MOVEMENT_KEYS = { A_CARE_MOVE_N = { 0, -1, 0 }, A_CARE_MOVE_S = { 0, 1, 0 }, A_CARE_MOVE_W = { -1, 0, 0 }, A_CARE_MOVE_E = { 1, 0, 0 }, A_CARE_MOVE_NW = { -1, -1, 0 }, A_CARE_MOVE_NE = { 1, -1, 0 }, A_CARE_MOVE_SW = { -1, 1, 0 }, A_CARE_MOVE_SE = { 1, 1, 0 }, --[[A_MOVE_N = { 0, -1, 0 }, A_MOVE_S = { 0, 1, 0 }, A_MOVE_W = { -1, 0, 0 }, A_MOVE_E = { 1, 0, 0 }, A_MOVE_NW = { -1, -1, 0 }, A_MOVE_NE = { 1, -1, 0 }, A_MOVE_SW = { -1, 1, 0 }, A_MOVE_SE = { 1, 1, 0 },--]] A_CUSTOM_CTRL_D = { 0, 0, -1 }, A_CUSTOM_CTRL_E = { 0, 0, 1 }, CURSOR_UP_Z_AUX = { 0, 0, 1 }, CURSOR_DOWN_Z_AUX = { 0, 0, -1 }, A_MOVE_SAME_SQUARE={0,0,0}, SELECT={0,0,0}, } ALLOWED_KEYS={ A_MOVE_N=true,A_MOVE_S=true,A_MOVE_W=true,A_MOVE_E=true,A_MOVE_NW=true, A_MOVE_NE=true,A_MOVE_SW=true,A_MOVE_SE=true,A_STANCE=true,SELECT=true,A_MOVE_DOWN_AUX=true, A_MOVE_UP_AUX=true } function moddedpos(pos,delta) return {x=pos.x+delta[1],y=pos.y+delta[2],z=pos.z+delta[3]} end function usetool:onDismiss() local adv=df.global.world.units.active[0] --TODO: cancel job --[[if adv and adv.job.current_job then local cj=adv.job.current_job adv.jobs.current_job=nil cj:delete() end]] end function usetool:onHelp() showHelp() end function usetool:onInput(keys) if keys.LEAVESCREEN then self:dismiss() elseif keys[keybinds.key_next.key] then mode=(mode+1)%#dig_modes elseif keys[keybinds.key_prev.key] then mode=mode-1 if mode<0 then mode=#dig_modes-1 end --elseif keys.A_LOOK then -- self:sendInputToParent("A_LOOK") elseif keys[keybinds.key_continue.key] then ContinueJob(df.global.world.units.active[0]) self:sendInputToParent("A_WAIT") else local adv=df.global.world.units.active[0] local cur_mode=dig_modes[(mode or 0)+1] local failed=false for code,_ in pairs(keys) do print(code) if MOVEMENT_KEYS[code] then local state={unit=adv,pos=moddedpos(adv.pos,MOVEMENT_KEYS[code]),dir=MOVEMENT_KEYS[code], old_pos={x=adv.pos.x,y=adv.pos.y, z=adv.pos.z}} if code=="SELECT" then if df.global.cursor.x~=-30000 then state.pos={x=df.global.cursor.x,y=df.global.cursor.y,z=df.global.cursor.z} else break end end for _,p in pairs(cur_mode[3] or {}) do local t,v=p(state) if t==false then dfhack.gui.showAnnouncement(v,5,1) failed=true end end if not failed then if type(cur_mode[2])=="function" then cur_mode[2](state) else MakeJob(adv,moddedpos(adv.pos,MOVEMENT_KEYS[code]),cur_mode[2],adv.pos,cur_mode[4]) end if code=="SELECT" then self:sendInputToParent("LEAVESCREEN") end self:sendInputToParent("A_WAIT") end return code end if code~="_STRING" and code~="_MOUSE_L" and code~="_MOUSE_R" then if ALLOWED_KEYS[code] then self:sendInputToParent(code) end end end end end usetool():show()