Skip to content

Instantly share code, notes, and snippets.

@Rumrusher
Last active December 17, 2015 00:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Rumrusher/5519984 to your computer and use it in GitHub Desktop.
Save Rumrusher/5519984 to your computer and use it in GitHub Desktop.
modded Companion order that allows you to set up a worker for comfort to do so you need to have the pointer on top of the unit you want to set up.
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)<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 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 getCreatureAtPos(x,y,z) -- gets the creature index @ x,y,z coord
--local x,y,z=getxyz() --get 'X' coords
local vector=df.global.world.units.all -- load all creatures
for i = 0, #vector-1 do -- look into all creatures offsets
local curpos=vector[i].pos --get its coordinates
local cx=curpos.x
local cy=curpos.y
local cz=curpos.z
if cx==x and cy==y and cz==z then --compare them
return vector[i] --return index
end
end
--print("Creature not found!")
return nil
end
function getCreatureAtPointer()
return getCreatureAtPos(getxyz())
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
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 MakeDig(unit,pos,job_type)
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)
nj.general_refs:insert("#",{new=df.general_ref_unit_workerst,unit_id=unit.id})
AddNewJob(nj)
unit.job.current_job=nj
pos.x=pos.x-1
unit.path.dest:assign(pos)
end
function getItemAtPos(x,y,z) -- gets the item index @ x,y,z coord
--local x,y,z=getxyz() --get 'X' coords
local vector=df.global.world.items.all -- load all items
for i = 0, #vector-1 do -- look into all items offsets
local curpos=vector[i].pos --get its coordinates
local cx=curpos.x
local cy=curpos.y
local cz=curpos.z
if cx==x and cy==y and cz==z then --compare them
return vector[i] --return index
end
end
--print("item not found!")
return nil
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="channel",f=function(unit_list,pos)
if not CheckCursor(pos) then
return false
end
for k,unit in pairs(unit_list) do
MakeDig(unit,pos,df.job_type.FellTree)
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="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},
{name="Grab",f=function (unit_list)
for k,v in pairs(unit_list) do
if tofollow==nil then
tofollow=df.global.world.units.active[0]
end
if unit==nil then
print=("no one is selected")
end
v.relations.following=tofollow
v.relations.dragger_id=tofollow.id
v.relations.draggee_id=v.id
v.relations.unk_238=8
tofollow.relations.draggee_id=v.id
--[[unit.relations.following=tofollow
v.relations.unk_258=tofollow.id
v.relations.unk_238=8
tofollow.relations.unk_254=v.id]]--
end
return true
end},
{name="Let go",f=function (unit_list)
for k,v in pairs(unit_list) do
if tofollow==nil then
tofollow=df.global.world.units.active[0]
end
if unit==nil then
print=("no one is selected")
end
v.relations.following=nil
v.relations.dragger_id=-1
v.relations.draggee_id=-1
v.relations.unk_238=-1
tofollow.relations.draggee_id=-1
end
return true
end},
{name="Patch up",f=function (unit_list)
for k,v in pairs(unit_list) do
unit=v
unit.body.wounds:resize(0) -- memory leak here :/
unit.body.blood_count=unit.body.blood_max
--set flags for standing and grasping...
unit.status2.limbs_stand_max=4
unit.status2.limbs_stand_count=4
unit.status2.limbs_grasp_max=4
unit.status2.limbs_grasp_count=4
--should also set temperatures, and flags for breath etc...
unit.flags1.dead=false
unit.flags2.calculated_bodyparts=false
unit.flags2.calculated_nerves=false
unit.flags2.circulatory_spray=false
unit.flags2.vision_good=true
unit.flags2.vision_damaged=false
unit.flags2.vision_missing=false
unit.counters.winded=0
unit.counters.unconscious=0
for k,v in pairs(unit.body.components) do
for kk,vv in pairs(v) do
if k == 'body_part_status' then v[kk].whole = 0 else v[kk] = 0 end
end
end end
return true
end},
{name="Power up",f=function (unit_list)
for k,d in pairs(unit_list) do
if d.civ_id==adv then
utils = require 'utils'
labor_rating = 15
military_rating = 70
skill = { 0,2,3,4,5,6,7,8,9,10,11,12,13,14,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,41,42,43,44,45,46,47,48,49,54,55,57,58,59,60,61,62,63,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,95,96,97,98,99,100,101,102,103,104,105,109,110,111,112,113,114,115 }
military = { 38,39,41,42,43,44,45,46,54,99,100,101,102,103,104,105 }
for sk,sv in ipairs(skill) do
new_rating = labor_rating
for _,v in ipairs(military) do
if v == sv then
new_rating = military_rating
end
end
utils.insert_or_update(d.status.current_soul.skills, { new = true, id = sv, rating = new_rating, experience = (new_rating * 500) + (new_rating * (new_rating - 1)) * 50}, 'id')
end
for sk,sv in ipairs(d.status.current_soul.skills) do
printall(sv)
end
end
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=getItemAtPos(getxyz())
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},
{name="Shove in",f=function (unit_list,pos)
unit=getCreatureAtPos(getxyz())
item=getItemAtPos(getxyz())
for a,b in ipairs(df.global.world.items.all) do for c,d in ipairs(b.general_refs) do end end
local u_ref=df.general_ref_contains_unitst:new()
u_ref.unit_id=unit.id
item.general_refs:insert(#item.general_refs,u_ref)
local u_cr_ref=df.general_ref_contained_in_itemst:new()
unit.flags1.caged=true
u_cr_ref.item_id=item.id
item.general_refs:insert(#general_refs,u_cr_ref)
return true
end},
{name="Gain Party",f=function (unit_list,pos)
if not CheckCursor(pos) then
return false
end
unit=getCreatureAtPos(getxyz())
trgunit=df.global.world.units.active[0]
unit.relations.group_leader_id=trgunit.id
local u_nem=dfhack.units.getNemesis(unit)
local t_nem=dfhack.units.getNemesis(trgunit)
if u_nem then
u_nem.group_leader_id=t_nem.id
end
if t_nem and u_nem then
t_nem.companions:insert(#t_nem.companions,u_nem.id)
end
return true
end},
{name="Travel Party",f=function ()
for k,v in pairs(df.global.world.nemesis.all) do
if v.id==df.global.world.units.active[0].id then
pause=getline()
df.global.world.world_data.travel[0].members:insert("#",{new=true,nemesis_id=v.id})
return true
end
end
end},
{name="Switch",f=function ()
if nemesis==nil then
nemesis=true --default value is nemesis switch too.
end
if unit==nil then
unit= getCreatureAtPointer()
end
if unit==nil then
error("Invalid unit!")
end
local other=df.global.world.units.active
local unit_indx
for k,v in pairs(other) do
if v==unit then
unit_indx=k
break
end
end
if unit_indx==nil then
error("Unit not found in array?!") --should not happen
end
other[unit_indx]=other[0]
other[0]=unit
if nemesis then --basicly copied from advtools plugin...
local nem=dfhack.units.getNemesis(unit)
local other_nem=dfhack.units.getNemesis(other[unit_indx])
if other_nem then
other_nem.flags[0]=false
other_nem.flags[1]=true
end
if nem then
nem.flags[0]=true
nem.flags[2]=true
for k,v in pairs(df.global.world.nemesis.all) do
if v.id==nem.id then
df.global.ui_advmode.player_id=k
end
end
else
qerror("Current unit does not have nemesis record, further working not guaranteed")
end
end
end},
{name="set as worker",f=function ()
if nemesis==nil then
nemesis=true --default value is nemesis switch too.
end
if unit==nil then
unit= getCreatureAtPointer()
end
if unit==nil then
error("Invalid unit!")
end
local other=df.global.world.units.active
local unit_indx
for k,v in pairs(other) do
if v==unit then
unit_indx=k
break
end
end
if unit_indx==nil then
error("Unit not found in array?!") --should not happen
end
other[unit_indx]=other[2]
other[2]=unit
if nemesis then --basicly copied from advtools plugin...
local nem=dfhack.units.getNemesis(unit)
local other_nem=dfhack.units.getNemesis(other[unit_indx])
if other_nem then
other_nem.flags[0]=false
other_nem.flags[1]=true
end
if nem then
nem.flags[0]=true
nem.flags[2]=true
for k,v in pairs(df.global.world.nemesis.all) do
if v.id==nem.id then
df.global.ui_advmode.player_id=k
end
end
else
qerror("Current unit does not have nemesis record, further working not guaranteed")
end
end
end},
{name="Attack",f=function (unit_list,pos)
if not CheckCursor(pos) then
return false
end
local targ=getCreatureAtPos(getxyz())
--unit_list.job.hunt_target=targ
targ.job.hunt_target=unit_list
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()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment