Advfort now supports workshops and siege weapons >:)

develop
Warmist 2012-12-03 21:49:17 +02:00
parent d9a5eefb9a
commit 3bce3838af
5 changed files with 708 additions and 179 deletions

@ -2362,7 +2362,8 @@ keybinding. (e.g. keybinding set Ctrl-T gui/advfort). Possible arguments:
* -c or --cheat - relaxes item requirements for buildings (e.g. walls from bones). * -c or --cheat - relaxes item requirements for buildings (e.g. walls from bones).
implies -a implies -a
* job - selects that job (e.g. Dig or FellTree)
gui/gm-editor gui/gm-editor

@ -334,7 +334,22 @@ local trap_inputs = {
}, },
[df.trap_type.TrackStop] = { { flags2={ building_material=true, non_economic=true } } } [df.trap_type.TrackStop] = { { flags2={ building_material=true, non_economic=true } } }
} }
local siegeengine_input = {
[df.siegeengine_type.Catapult] = {
{
item_type=df.item_type.CATAPULTPARTS,
vector_id=df.job_item_vector_id.CATAPULTPARTS,
quantity=3
}
},
[df.siegeengine_type.Ballista] = {
{
item_type=df.item_type.BALLISTAPARTS,
vector_id=df.job_item_vector_id.BALLISTAPARTS,
quantity=3
}
},
}
--[[ Functions for lookup in tables. ]] --[[ Functions for lookup in tables. ]]
local function get_custom_inputs(custom) local function get_custom_inputs(custom)
@ -359,6 +374,8 @@ local function get_inputs_by_type(type,subtype,custom)
end end
elseif type == df.building_type.Trap then elseif type == df.building_type.Trap then
return trap_inputs[subtype] return trap_inputs[subtype]
elseif type == df.building_type.SiegeEngine then
return siegeengine_input[subtype]
else else
return building_inputs[type] return building_inputs[type]
end end

@ -0,0 +1,469 @@
local _ENV = mkmodule('dfhack.workshops')
local utils = require 'utils'
input_filter_defaults = {
item_type = -1,
item_subtype = -1,
mat_type = -1,
mat_index = -1,
flags1 = {},
-- Instead of noting those that allow artifacts, mark those that forbid them.
-- Leaves actually enabling artifacts to the discretion of the API user,
-- which is the right thing because unlike the game UI these filters are
-- used in a way that does not give the user a chance to choose manually.
flags2 = { allow_artifact = true },
flags3 = {},
flags4 = 0,
flags5 = 0,
reaction_class = '',
has_material_reaction_product = '',
metal_ore = -1,
min_dimension = -1,
has_tool_use = -1,
quantity = 1
}
jobs_workshop={
[df.workshop_type.Jewelers]={
{
name="cut gems",
items={{item_type=df.item_type.ROUGH,flags1={unrotten=true}}},
job_fields={job_type=df.job_type.CutGems}
},
{
name="encrust finished goods with gems",
items={{item_type=df.item_type.SMALLGEM},{flags1={improvable=true,finished_goods=true}}},
job_fields={job_type=df.job_type.EncrustWithGems}
},
{
name="encrust ammo with gems",
items={{item_type=df.item_type.SMALLGEM},{flags1={improvable=true,ammo=true}}},
job_fields={job_type=df.job_type.EncrustWithGems}
},
{
name="encrust furniture with gems",
items={{item_type=df.item_type.SMALLGEM},{flags1={improvable=true,furniture=true}}},
job_fields={job_type=df.job_type.EncrustWithGems}
},
},
[df.workshop_type.Fishery]={
{
name="prepare raw fish",
items={{item_type=df.item_type.FISH_RAW,flags1={unrotten=true}}},
job_fields={job_type=df.job_type.PrepareRawFish}
},
{
name="extract from raw fish",
items={{flags1={unrotten=true,extract_bearing_fish=true}},{item_type=df.item_type.FLASK,flags1={empty=true,glass=true}}},
job_fields={job_type=df.job_type.ExtractFromRawFish}
},
{
name="catch live fish",
items={},
job_fields={job_type=df.job_type.CatchLiveFish}
}, -- no items?
},
[df.workshop_type.Still]={
{
name="brew drink",
items={{flags1={distillable=true},vector_id=22},{flags1={empty=true},flags3={food_storage=true}}},
job_fields={job_type=df.job_type.BrewDrink}
},
{
name="extract from plants",
items={{item_type=df.item_type.PLANT,flags1={unrotten=true,extract_bearing_plant=true}},{item_type=df.item_type.FLASK,flags1={empty=true}}},
job_fields={job_type=df.job_type.ExtractFromPlants}
},
--mead from raws?
},
[df.workshop_type.Masons]={
defaults={item_type=df.item_type.BOULDER,item_subtype=-1,vector_id=df.job_item_vector_id.BOULDER, mat_type=0,mat_index=-1,flags3={hard=true}},--flags2={non_economic=true},
{
name="construct armor stand",
items={{}},
job_fields={job_type=df.job_type.ConstructArmorStand}
},
{
name="construct blocks",
items={{}},
job_fields={job_type=df.job_type.ConstructBlocks}
},
{
name="construct throne",
items={{}},
job_fields={job_type=df.job_type.ConstructThrone}
},
{
name="construct coffin",
items={{}},
job_fields={job_type=df.job_type.ConstructCoffin}
},
{
name="construct door",
items={{}},
job_fields={job_type=df.job_type.ConstructDoor}
},
{
name="construct floodgate",
items={{}},
job_fields={job_type=df.job_type.ConstructFloodgate}
},
{
name="construct hatch cover",
items={{}},
job_fields={job_type=df.job_type.ConstructHatchCover}
},
{
name="construct grate",
items={{}},
job_fields={job_type=df.job_type.ConstructGrate}
},
{
name="construct cabinet",
items={{}},
job_fields={job_type=df.job_type.ConstructCabinet}
},
{
name="construct chest",
items={{}},
job_fields={job_type=df.job_type.ConstructChest}
},
{
name="construct statue",
items={{}},
job_fields={job_type=df.job_type.ConstructStatue}
},
{
name="construct slab",
items={{}},
job_fields={job_type=df.job_type.ConstructSlab}
},
{
name="construct table",
items={{}},
job_fields={job_type=df.job_type.ConstructTable}
},
{
name="construct weapon rack",
items={{}},
job_fields={job_type=df.job_type.ConstructWeaponRack}
},
{
name="construct quern",
items={{}},
job_fields={job_type=df.job_type.ConstructQuern}
},
{
name="construct millstone",
items={{}},
job_fields={job_type=df.job_type.ConstructMillstone}
},
},
[df.workshop_type.Carpenters]={
defaults={item_type=df.item_type.WOOD,vector_id=df.job_item_vector_id.WOOD},
{
name="make barrel",
items={{}},
job_fields={job_type=df.job_type.MakeBarrel}
},
{
name="make bucket",
items={{}},
job_fields={job_type=df.job_type.MakeBucket}
},
{
name="make animal trap",
items={{}},
job_fields={job_type=df.job_type.MakeAnimalTrap}
},
{
name="make cage",
items={{}},
job_fields={job_type=df.job_type.MakeCage}
},
{
name="construct bed",
items={{}},
job_fields={job_type=df.job_type.ConstructBed}
},
{
name="construct bin",
items={{}},
job_fields={job_type=df.job_type.ConstructBin}
},
{
name="construct armor stand",
items={{}},
job_fields={job_type=df.job_type.ConstructArmorStand}
},
{
name="construct blocks",
items={{}},
job_fields={job_type=df.job_type.ConstructBlocks}
},
{
name="construct throne",
items={{}},
job_fields={job_type=df.job_type.ConstructThrone}
},
{
name="construct coffin",
items={{}},
job_fields={job_type=df.job_type.ConstructCoffin}
},
{
name="construct door",
items={{}},
job_fields={job_type=df.job_type.ConstructDoor}
},
{
name="construct floodgate",
items={{}},
job_fields={job_type=df.job_type.ConstructFloodgate}
},
{
name="construct hatch cover",
items={{}},
job_fields={job_type=df.job_type.ConstructHatchCover}
},
{
name="construct grate",
items={{}},
job_fields={job_type=df.job_type.ConstructGrate}
},
{
name="construct cabinet",
items={{}},
job_fields={job_type=df.job_type.ConstructCabinet}
},
{
name="construct chest",
items={{}},
job_fields={job_type=df.job_type.ConstructChest}
},
{
name="construct statue",
items={{}},
job_fields={job_type=df.job_type.ConstructStatue}
},
{
name="construct table",
items={{}},
job_fields={job_type=df.job_type.ConstructTable}
},
{
name="construct weapon rack",
items={{}},
job_fields={job_type=df.job_type.ConstructWeaponRack}
},
{
name="construct splint",
items={{}},
job_fields={job_type=df.job_type.ConstructSplint}
},
{
name="construct crutch",
items={{}},
job_fields={job_type=df.job_type.ConstructCrutch}
},
},
[df.workshop_type.Kitchen]={
--mat_type=2,3,4
defaults={flags1={unrotten=true,cookable=true}},
{
name="prepare easy meal",
items={{flags1={solid=true}},{}},
job_fields={job_type=df.job_type.PrepareMeal,mat_type=2}
},
{
name="prepare fine meal",
items={{flags1={solid=true}},{},{}},
job_fields={job_type=df.job_type.PrepareMeal,mat_type=3}
},
{
name="prepare lavish meal",
items={{flags1={solid=true}},{},{},{}},
job_fields={job_type=df.job_type.PrepareMeal,mat_type=4}
},
},
[df.workshop_type.Butchers]={
{
name="butcher an animal",
items={{flags1={butcherable=true,unrotten=true,nearby=true}}},
job_fields={job_type=df.job_type.ButcherAnimal}
},
{
name="extract from land animal",
items={{flags1={extract_bearing_vermin=true,unrotten=true}},{item_type=df.item_type.FLASK,flags1={empty=true,glass=true}}},
job_fields={job_type=df.job_type.ExtractFromLandAnimal}
},
{
name="catch live land animal",
items={},
job_fields={job_type=df.job_type.CatchLiveLandAnimal}
},
},
[df.workshop_type.Mechanics]={
{
name="construct mechanisms",
items={{item_type=df.item_type.BOULDER,item_subtype=-1,vector_id=df.job_item_vector_id.BOULDER, mat_type=0,mat_index=-1,quantity=1,
flags3={hard=true}}},
job_fields={job_type=df.job_type.ConstructMechanisms}
},
{
name="construct traction bench",
items={{item_type=df.item_type.TABLE},{item_type=df.item_type.MECHANISM},{item_type=df.item_type.CHAIN}},
job_fields={job_type=df.job_type.ConstructTractionBench}
},
},
[df.workshop_type.Loom]={
{
name="weave plant thread cloth",
items={{item_type=df.item_type.THREAD,quantity=15000,min_dimension=15000,flags1={collected=true},flags2={plant=true}}},
job_fields={job_type=df.job_type.WeaveCloth}
},
{
name="weave silk thread cloth",
items={{item_type=df.item_type.THREAD,quantity=15000,min_dimension=15000,flags1={collected=true},flags2={silk=true}}},
job_fields={job_type=df.job_type.WeaveCloth}
},
{
name="weave yarn cloth",
items={{item_type=df.item_type.THREAD,quantity=15000,min_dimension=15000,flags1={collected=true},flags2={yarn=true}}},
job_fields={job_type=df.job_type.WeaveCloth}
},
{
name="weave inorganic cloth",
items={{item_type=df.item_type.THREAD,quantity=15000,min_dimension=15000,flags1={collected=true},mat_type=0}},
job_fields={job_type=df.job_type.WeaveCloth}
},
{
name="collect webs",
items={{item_type=df.item_type.THREAD,quantity=10,min_dimension=10,flags1={undisturbed=true}}},
job_fields={job_type=df.job_type.CollectWebs}
},
},
[df.workshop_type.Leatherworks]={
defaults={item_type=SKIN_TANNED},
{
name="construct leather bag",
items={{}},
job_fields={job_type=df.job_type.ConstructChest}
},
{
name="construct waterskin",
items={{}},
job_fields={job_type=df.job_type.MakeFlask}
},
{
name="construct backpack",
items={{}},
job_fields={job_type=df.job_type.MakeBackpack}
},
{
name="construct quiver",
items={{}},
job_fields={job_type=df.job_type.MakeQuiver}
},
{
name="sew leather image",
items={{item_type=-1,flags1={empty=true},flags2={sewn_imageless=true}},{}},
job_fields={job_type=df.job_type.SewImage}
},
},
[df.workshop_type.Dyers]={
{
name="dye thread",
items={{item_type=df.item_type.THREAD,quantity=15000,min_dimension=15000,flags1={collected=true},flags2={dyeable=true}},
{flags1={unrotten=true},flags2={dye=true}}},
job_fields={job_type=df.job_type.DyeThread}
},
{
name="dye cloth",
items={{item_type=df.item_type.CLOTH,quantity=10000,min_dimension=10000,flags2={dyeable=true}},
{flags1={unrotten=true},flags2={dye=true}}},
job_fields={job_type=df.job_type.DyeThread}
},
},
[df.workshop_type.Siege]={
{
name="construct balista parts",
items={{item_type=df.item_type.WOOD}},
job_fields={job_type=df.job_type.ConstructBallistaParts}
},
{
name="construct catapult parts",
items={{item_type=df.item_type.WOOD}},
job_fields={job_type=df.job_type.ConstructCatapultParts}
},
{
name="assemble balista arrow",
items={{item_type=df.item_type.WOOD}},
job_fields={job_type=df.job_type.AssembleSiegeAmmo}
},
{
name="assemble tipped balista arrow",
items={{item_type=df.item_type.WOOD},{item_type=df.item_type.BALLISTAARROWHEAD}},
job_fields={job_type=df.job_type.AssembleSiegeAmmo}
},
},
}
local function matchIds(bid1,wid1,cid1,bid2,wid2,cid2)
if bid1~=-1 and bid2~=-1 and bid1~=bid2 then
return false
end
if wid1~=-1 and wid2~=-1 and wid1~=wid2 then
return false
end
if cid1~=-1 and cid2~=-1 and cid1~=cid2 then
return false
end
return true
end
local function scanRawsReaction(buildingId,workshopId,customId)
local ret={}
for idx,reaction in ipairs(df.global.world.raws.reactions) do
for k,v in pairs(reaction.building.type) do
if matchIds(buildingId,workshopId,customId,v,reaction.building.subtype[k],reaction.building.custom[k]) then
table.insert(ret,reaction)
end
end
end
return ret
end
function getJobs(building_id,workshopId,customId)
local ret={}
local c_jobs
if building_id==df.building_type.Workshop then
c_jobs=jobs_workshop[workshopId]
else
return nil
end
for jobId,contents in pairs(c_jobs) do
if jobId~="defaults" then
local entry={}
entry.name=contents.name
local lclDefaults=utils.clone(input_filter_defaults,true)
if c_jobs.defaults ~=nil then
utils.assign(lclDefaults,c_jobs.defaults)
end
entry.items={}
for k,item in pairs(contents.items) do
entry.items[k]=utils.clone(lclDefaults,true)
utils.assign(entry.items[k],item)
end
if contents.job_fields~=nil then
entry.job_fields={}
utils.assign(entry.job_fields,contents.job_fields)
end
ret[jobId]=entry
end
end
--get jobs, add in from raws
return ret
end
return _ENV

@ -14,7 +14,7 @@ WORKSHOP_ABSTRACT={
} }
WORKSHOP_SPECIAL={ WORKSHOP_SPECIAL={
[df.building_type.Workshop]=true,[df.building_type.Furnace]=true,[df.building_type.Trap]=true, [df.building_type.Workshop]=true,[df.building_type.Furnace]=true,[df.building_type.Trap]=true,
[df.building_type.Construction]=true [df.building_type.Construction]=true,[df.building_type.SiegeEngine]=true
} }
BuildingDialog = defclass(BuildingDialog, gui.FramedScreen) BuildingDialog = defclass(BuildingDialog, gui.FramedScreen)
@ -33,6 +33,7 @@ BuildingDialog.ATTRS{
use_tool_workshop=true, use_tool_workshop=true,
use_furnace = true, use_furnace = true,
use_construction = true, use_construction = true,
use_siege = true,
use_trap = true, use_trap = true,
use_custom = true, use_custom = true,
building_filter = DEFAULT_NIL, building_filter = DEFAULT_NIL,
@ -116,13 +117,20 @@ function BuildingDialog:initBuiltinMode()
cb = self:callback('initConstructionMode') cb = self:callback('initConstructionMode')
}) })
end end
if self.use_siege then
table.insert(choices, {
icon = ARROW, text = 'siege engine', key = 'CUSTOM_SHIFT_S',
cb = self:callback('initSiegeMode')
})
end
if self.use_custom then if self.use_custom then
table.insert(choices, { table.insert(choices, {
icon = ARROW, text = 'custom workshop', key = 'CUSTOM_SHIFT_U', icon = ARROW, text = 'custom workshop', key = 'CUSTOM_SHIFT_U',
cb = self:callback('initCustomMode') cb = self:callback('initCustomMode')
}) })
end end
for i=0,df.building_type._last_item do for i=0,df.building_type._last_item do
if (not WORKSHOP_ABSTRACT[i] or self.use_abstract)and not WORKSHOP_SPECIAL[i] then if (not WORKSHOP_ABSTRACT[i] or self.use_abstract)and not WORKSHOP_SPECIAL[i] then
@ -174,6 +182,15 @@ function BuildingDialog:initFurnaceMode()
self:pushContext('Furnaces', choices) self:pushContext('Furnaces', choices)
end end
function BuildingDialog:initSiegeMode()
local choices = {}
for i=0,df.siegeengine_type._last_item do
self:addBuilding(choices, df.siegeengine_type[i], df.building_type.SiegeEngine, i,-1,nil)
end
self:pushContext('Siege weapons', choices)
end
function BuildingDialog:initCustomMode() function BuildingDialog:initCustomMode()
local choices = {} local choices = {}
local raws=df.global.world.raws.buildings.all local raws=df.global.world.raws.buildings.all

@ -16,11 +16,16 @@ local wid=require 'gui.widgets'
local dialog=require 'gui.dialogs' local dialog=require 'gui.dialogs'
local buildings=require 'dfhack.buildings' local buildings=require 'dfhack.buildings'
local bdialog=require 'gui.buildings' local bdialog=require 'gui.buildings'
local workshopJobs=require 'dfhack.workshops'
local tile_attrs = df.tiletype.attrs local tile_attrs = df.tiletype.attrs
settings={build_by_items=false,check_inv=false,df_assign=true} settings={build_by_items=false,check_inv=false,df_assign=true}
for k,v in ipairs({...}) do
local mode_name
for k,v in ipairs({...}) do --setting parsing
if v=="-c" or v=="--cheat" then if v=="-c" or v=="--cheat" then
settings.build_by_items=true settings.build_by_items=true
settings.df_assign=false settings.df_assign=false
@ -29,6 +34,9 @@ for k,v in ipairs({...}) do
settings.df_assign=false settings.df_assign=false
elseif v=="-a" or v=="--nodfassign" then elseif v=="-a" or v=="--nodfassign" then
settings.df_assign=false settings.df_assign=false
else
mode_name=v
end end
end end
@ -63,7 +71,7 @@ function showHelp()
Disclaimer(helptext) Disclaimer(helptext)
require("gui.dialogs").showMessage("Help!?!",helptext) require("gui.dialogs").showMessage("Help!?!",helptext)
end end
--[[ low level job management ]]--
function getLastJobLink() function getLastJobLink()
local st=df.global.world.job_list local st=df.global.world.job_list
while st.next~=nil do while st.next~=nil do
@ -79,7 +87,7 @@ function addNewJob(job)
newLink.item=job newLink.item=job
job.list_link=newLink job.list_link=newLink
end end
function MakeJob(args) function makeJob(args)
local newJob=df.job:new() local newJob=df.job:new()
newJob.id=df.global.job_next_id newJob.id=df.global.job_next_id
df.global.job_next_id=df.global.job_next_id+1 df.global.job_next_id=df.global.job_next_id+1
@ -89,29 +97,46 @@ function MakeJob(args)
newJob.pos:assign(args.pos) newJob.pos:assign(args.pos)
args.job=newJob args.job=newJob
local failed=false local failed
for k,v in ipairs(args.pre_actions or {}) do for k,v in ipairs(args.pre_actions or {}) do
local ok,msg=v(args) local ok,msg=v(args)
if not ok then if not ok then
failed=true failed=msg
return false,msg break
end end
end end
if not failed then if failed==nil then
AssignUnitToJob(newJob,args.unit,args.from_pos) AssignUnitToJob(newJob,args.unit,args.from_pos)
for k,v in ipairs(args.post_actions or {}) do
local ok,msg=v(args)
if not ok then
failed=msg
break
end
end
if failed then
UnassignJob(newJob,args.unit)
end
end end
for k,v in ipairs(args.post_actionss or {}) do if failed==nil then
v(args) addNewJob(newJob)
return newJob
else
newJob:delete()
return false,failed
end end
addNewJob(newJob)
return newJob
end end
function UnassignJob(job,unit,unit_pos)
unit.job.current_job=nil
end
function AssignUnitToJob(job,unit,unit_pos) function AssignUnitToJob(job,unit,unit_pos)
job.general_refs:insert("#",{new=df.general_ref_unit_workerst,unit_id=unit.id}) job.general_refs:insert("#",{new=df.general_ref_unit_workerst,unit_id=unit.id})
unit.job.current_job=job unit.job.current_job=job
unit_pos=unit_pos or {x=job.pos.x,y=job.pos.y,z=job.pos.z} unit_pos=unit_pos or {x=job.pos.x,y=job.pos.y,z=job.pos.z}
unit.path.dest:assign(unit_pos) unit.path.dest:assign(unit_pos)
return true
end end
function SetCreatureRef(args) function SetCreatureRef(args)
local job=args.job local job=args.job
@ -258,6 +283,7 @@ function AssignBuildingRef(args)
local bld=dfhack.buildings.findAtTile(args.pos) local bld=dfhack.buildings.findAtTile(args.pos)
args.job.general_refs:insert("#",{new=df.general_ref_building_holderst,building_id=bld.id}) args.job.general_refs:insert("#",{new=df.general_ref_building_holderst,building_id=bld.id})
bld.jobs:insert("#",args.job) bld.jobs:insert("#",args.job)
return true
end end
function BuildingChosen(inp_args,type_id,subtype_id,custom_id) function BuildingChosen(inp_args,type_id,subtype_id,custom_id)
@ -289,8 +315,8 @@ function RemoveBuilding(args)
return false,"No building to remove" return false,"No building to remove"
end end
end end
function isSuitableItem(job_item,item)
function isSuitableItem(job_item,item)
if job_item.item_type~=-1 then if job_item.item_type~=-1 then
if item:getType()~= job_item.item_type then if item:getType()~= job_item.item_type then
return false, "type" return false, "type"
@ -312,9 +338,16 @@ function isSuitableItem(job_item,item)
end end
local matinfo=dfhack.matinfo.decode(item) local matinfo=dfhack.matinfo.decode(item)
--print(matinfo:getCraftClass()) --print(matinfo:getCraftClass())
--print("Matching ",item," vs ",job_item)
if not matinfo:matches(job_item) then if not matinfo:matches(job_item) then
return false,"matinfo" return false,"matinfo"
end end
-- some bonus checks:
if job_item.flags2.building_material and not item:isBuildMat() then
return false,"non-build mat"
end
-- *****************
--print("--Matched")
--reagen_index?? reaction_id?? --reagen_index?? reaction_id??
if job_item.metal_ore~=-1 and not item:isMetalOre(job_item.metal_ore) then if job_item.metal_ore~=-1 and not item:isMetalOre(job_item.metal_ore) then
return false,"metal ore" return false,"metal ore"
@ -336,15 +369,25 @@ function isSuitableItem(job_item,item)
end end
return true return true
end end
function AssignJobItems(args,is_workshop_job) function getItemsUncollected(job)
if settings.df_assign and not is_workshop_job then local ret={}
for id,jitem in pairs(job.items) do
local x,y,z=dfhack.items.getPosition(jitem.item)
if x~=job.pos.x or y~=job.pos.y or z~=job.pos.z then
table.insert(ret,jitem)
end
end
return ret
end
function AssignJobItems(args)
if settings.df_assign then --use df default logic and hope that it would work
return true return true
end end
--print("Assign") -- first find items that you want to use for the job
--printall(args)
local job=args.job local job=args.job
local its=itemsAtPos(args.from_pos) local its=itemsAtPos(args.from_pos)
if settings.check_inv then if settings.check_inv then --check inventory and contained items
for k,v in pairs(args.unit.inventory) do for k,v in pairs(args.unit.inventory) do
table.insert(its,v.item) table.insert(its,v.item)
end end
@ -363,19 +406,27 @@ function AssignJobItems(args,is_workshop_job)
job.items[#job.items-1]:delete() job.items[#job.items-1]:delete()
job.items:erase(#job.items-1) job.items:erase(#job.items-1)
end]] end]]
local item_counts={}
for job_id, trg_job_item in ipairs(job.job_items) do
item_counts[job_id]=trg_job_item.quantity
printall(trg_job_item)
printall(trg_job_item.flags2)
end
local used_item_id={} local used_item_id={}
for job_id, trg_job_item in ipairs(job.job_items) do for job_id, trg_job_item in ipairs(job.job_items) do
for _,cur_item in pairs(its) do for _,cur_item in pairs(its) do
if not used_item_id[cur_item.id] then if not used_item_id[cur_item.id] then
local item_suitable,msg=isSuitableItem(trg_job_item,cur_item) local item_suitable,msg=isSuitableItem(trg_job_item,cur_item)
--if msg then --if msg then
-- print(cur_item,msg) -- print(cur_item,msg)
--end --end
if (trg_job_item.quantity>0 and item_suitable) or settings.build_by_items then
job.items:insert("#",{new=true,item=cur_item,role=2,job_item_idx=job_id}) if (item_counts[job_id]>0 and item_suitable) or settings.build_by_items then
trg_job_item.quantity=trg_job_item.quantity-1 cur_item.flags.in_job=true
--print(string.format("item added, job_item_id=%d, item %s, quantity left=%d",job_id,tostring(cur_item),trg_job_item.quantity)) job.items:insert("#",{new=true,item=cur_item,role=df.job_item_ref.T_role.Reagent,job_item_idx=job_id})
item_counts[job_id]=item_counts[job_id]-1
print(string.format("item added, job_item_id=%d, item %s, quantity left=%d",job_id,tostring(cur_item),item_counts[job_id]))
used_item_id[cur_item.id]=true used_item_id[cur_item.id]=true
end end
end end
@ -384,28 +435,44 @@ function AssignJobItems(args,is_workshop_job)
if not settings.build_by_items then if not settings.build_by_items then
for job_id, trg_job_item in ipairs(job.job_items) do for job_id, trg_job_item in ipairs(job.job_items) do
if trg_job_item.quantity>0 then if item_counts[job_id]>0 then
return false, "Not enough items for this job" return false, "Not enough items for this job"
end end
end end
end end
local uncollected = getItemsUncollected(job)
if #uncollected == 0 then
job.flags.working=true
else
uncollected[1].is_fetching=1
job.flags.fetching=true
end
--todo set working for workshops if items are present, else set fetching (and at least one item to is_fetching=1)
--job.flags.working=true
return true return true
end end
function AssignJobToBuild(args) function AssignJobToBuild(args)
local bld=dfhack.buildings.findAtTile(args.pos) local bld=dfhack.buildings.findAtTile(args.pos)
args.job_type=df.job_type.ConstructBuilding args.job_type=df.job_type.ConstructBuilding
if bld~=nil then if bld~=nil then
if #bld.jobs>0 then for idx,job in pairs(bld.jobs) do
args.job=bld.jobs[0] if job.job_type==df.job_type.ConstructBuilding then
args.job=job
break
end
end
if args.job~=nil then
local ok,msg=AssignJobItems(args) local ok,msg=AssignJobItems(args)
if not ok then if not ok then
return false,msg return false,msg
else else
AssignUnitToJob(bld.jobs[0],args.unit,args.from_pos) --todo check if correct job type AssignUnitToJob(args.job,args.unit,args.from_pos)
end end
else else
args.pre_actions={AssignJobItems} local t={items=buildings.getFiltersByType({},bld:getType(),bld:getSubtype(),bld:getCustomType())}
local ok,msg=MakeJob(args) args.pre_actions={dfhack.curry(setFiltersUp,t),AssignJobItems,AssignBuildingRef}
local ok,msg=makeJob(args)
return ok,msg return ok,msg
end end
else else
@ -439,94 +506,8 @@ function ContinueJob(unit)
unit.path.dest:assign(c_job.pos) unit.path.dest:assign(c_job.pos)
end end
end end
workshops={
--[=[
[df.workshop_type.Jewelers]={
--TODO add material.IS_GEM
[df.job_type.CutGems]={{test={isMaterialGem},item_type=df.item_type.BOULDER},
[df.job_type.EncrustWithGems]={{item_type=df.item_type.SMALLGEM},{flags1={improvable=true,finished_goods=true}},
[df.job_type.EncrustWithGems]={{item_type=df.item_type.SMALLGEM},{flags1={improvable=true,ammo=true}},
[df.job_type.EncrustWithGems]={{item_type=df.item_type.SMALLGEM},{flags1={improvable=true,furniture=true}},
}
]=]
[df.workshop_type.Fishery]={
common={quantity=1},
[df.job_type.PrepareRawFish]={{item_type=df.item_type.FISH_RAW,flags1={unrotten=true}}},
[df.job_type.ExtractFromRawFish]={{flags1={unrotten=true,extract_bearing_fish=true}},{item_type=df.item_type.FLASK,flags1={empty=true,glass=true}}},
[df.job_type.CatchLiveFish]={}, -- no items?
},
[df.workshop_type.Still]={
common={quantity=1},
[df.job_type.BrewDrink]={{flags1={distillable=true},vector_id=22},{flags1={empty=true},flags3={food_storage=true}}},
[df.job_type.ExtractFromPlants]={{item_type=df.item_type.PLANT,flags1={unrotten=true,extract_bearing_plant=true}},{item_type=df.item_type.FLASK,flags1={empty=true}}},
},
[df.workshop_type.Masons]={
common={item_type=df.item_type.BOULDER,item_subtype=-1,vector_id=df.job_item_vector_id.BOULDER, mat_type=0,mat_index=-1,quantity=1,flags3={hard=true}},--flags2={non_economic=true},
[df.job_type.ConstructArmorStand]={{}},
[df.job_type.ConstructBlocks]={{}},
[df.job_type.ConstructThrone]={{}},
[df.job_type.ConstructCoffin]={{}},
[df.job_type.ConstructDoor]={{}},
[df.job_type.ConstructFloodgate]={{}},
[df.job_type.ConstructHatchCover]={{}},
[df.job_type.ConstructGrate]={{}},
[df.job_type.ConstructCabinet]={{}},
[df.job_type.ConstructChest]={{}},
[df.job_type.ConstructStatue]={{}},
[df.job_type.ConstructSlab]={{}},
[df.job_type.ConstructTable]={{}},
[df.job_type.ConstructWeaponRack]={{}},
[df.job_type.ConstructQuern]={{}},
[df.job_type.ConstructMillstone]={{}},
},
[df.workshop_type.Carpenters]={
common={item_type=df.item_type.WOOD,vector_id=df.job_item_vector_id.WOOD,quantity=1},
[df.job_type.MakeBarrel]={{}},
--[[ from raws
[df.job_type.MakeShield]={{}},
[df.job_type.MakeShield]={item_subtype=1,{}}, --buckler
[df.job_type.MakeWeapon]={item_subtype=23,{}}, --training spear -> from raws...
[df.job_type.MakeWeapon]={item_subtype=22,{}}, --training sword
[df.job_type.MakeWeapon]={item_subtype=21,{}}, --training axe
--]]
[df.job_type.ConstructBlocks]={{}},
[df.job_type.MakeBucket]={{}},
[df.job_type.MakeAnimalTrap]={{}},
[df.job_type.MakeCage]={{}},
[df.job_type.ConstructArmorStand]={{}},
[df.job_type.ConstructBed]={{}},
[df.job_type.ConstructThrone]={{}},
[df.job_type.ConstructCoffin]={{}},
[df.job_type.ConstructDoor]={{}},
[df.job_type.ConstructFloodgate]={{}},
[df.job_type.ConstructHatchCover]={{}},
[df.job_type.ConstructGrate]={{}},
[df.job_type.ConstructCabinet]={{}},
[df.job_type.ConstructBin]={{}},
[df.job_type.ConstructChest]={{}},
[df.job_type.ConstructStatue]={{}},
[df.job_type.ConstructSlab]={{}},
[df.job_type.ConstructTable]={{}},
[df.job_type.ConstructWeaponRack]={{}},
--[[ from raws
[df.job_type.MakeTrapComponent]={item_subtype=1,{}}, --corkscrew
[df.job_type.MakeTrapComponent]={item_subtype=2,{}}, --ball
[df.job_type.MakeTrapComponent]={item_subtype=4,{}}, --spike
[df.job_type.MakeTool]={item_subtype=16,{}}, --minecart (?? maybe from raws?)
--]]
[df.job_type.ConstructSplint]={{}},
[df.job_type.ConstructCrutch]={{}},
},
[df.workshop_type.Mechanics]={
common={quantity=1},
[df.job_type.ConstructMechanisms]={{item_type=df.item_type.BOULDER,item_subtype=-1,vector_id=df.job_item_vector_id.BOULDER, mat_type=0,mat_index=-1,quantity=1,
flags3={hard=true}}}, -- flags2={non_economic=true}
[df.job_type.ConstructTractionBench]={{item_type=df.item_type.TABLE},{item_type=df.item_type.MECHANISM},{item_type=df.item_type.CHAIN}}
},
}
dig_modes={ actions={
{"CarveFortification" ,df.job_type.CarveFortification,{IsWall,IsHardMat}}, {"CarveFortification" ,df.job_type.CarveFortification,{IsWall,IsHardMat}},
{"DetailWall" ,df.job_type.DetailWall,{IsWall,IsHardMat}}, {"DetailWall" ,df.job_type.DetailWall,{IsWall,IsHardMat}},
{"DetailFloor" ,df.job_type.DetailFloor,{IsFloor,IsHardMat}}, {"DetailFloor" ,df.job_type.DetailFloor,{IsFloor,IsHardMat}},
@ -541,39 +522,34 @@ dig_modes={
{"Fish" ,df.job_type.Fish,{IsWater}}, {"Fish" ,df.job_type.Fish,{IsWater}},
--{"Diagnose Patient" ,df.job_type.DiagnosePatient,{IsUnit},{SetPatientRef}}, --{"Diagnose Patient" ,df.job_type.DiagnosePatient,{IsUnit},{SetPatientRef}},
--{"Surgery" ,df.job_type.Surgery,{IsUnit},{SetPatientRef}}, --{"Surgery" ,df.job_type.Surgery,{IsUnit},{SetPatientRef}},
--{"TameAnimal" ,df.job_type.TameAnimal,{IsUnit},{SetCreatureRef}}, {"TameAnimal" ,df.job_type.TameAnimal,{IsUnit},{SetCreatureRef}},
{"GatherPlants" ,df.job_type.GatherPlants,{IsPlant}}, {"GatherPlants" ,df.job_type.GatherPlants,{IsPlant}},
{"RemoveConstruction" ,df.job_type.RemoveConstruction,{IsConstruct}}, {"RemoveConstruction" ,df.job_type.RemoveConstruction,{IsConstruct}},
{"RemoveBuilding" ,RemoveBuilding,{IsBuilding}}, {"RemoveBuilding" ,RemoveBuilding,{IsBuilding}},
{"RemoveStairs" ,df.job_type.RemoveStairs,{IsStairs,NotConstruct}},
--{"HandleLargeCreature" ,df.job_type.HandleLargeCreature,{isUnit},{SetCreatureRef}}, --{"HandleLargeCreature" ,df.job_type.HandleLargeCreature,{isUnit},{SetCreatureRef}},
{"Build" ,AssignJobToBuild}, {"Build" ,AssignJobToBuild},
--{"ConstructBlocks" ,df.job_type.ConstructBlocks,{},{AssignBuildingRef},{AddItemRefMason,AssignJobItems}},
{"RemoveStairs" ,df.job_type.RemoveStairs,{IsStairs,NotConstruct}},
--{"LoadCageTrap" ,df.job_type.LoadCageTrap,{},{SetBuildingRef},{AssignJobItems}},
} }
for id,action in pairs(actions) do
if action[1]==mode_name then
mode=id-1
break
end
end
usetool=defclass(usetool,gui.Screen) usetool=defclass(usetool,gui.Screen)
usetool.focus_path = 'advfort' usetool.focus_path = 'advfort'
function usetool:getModeName() function usetool:getModeName()
local adv=df.global.world.units.active[0] local adv=df.global.world.units.active[0]
if adv.job.current_job then 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) return string.format("%s working(%d) ",(actions[(mode or 0)+1][1] or ""),adv.job.current_job.completion_timer)
else else
return dig_modes[(mode or 0)+1][1] or " " return actions[(mode or 0)+1][1] or " "
end end
end end
function usetool:isOnWorkshop()
local adv=df.global.world.units.active[0]
local bld=dfhack.buildings.findAtTile(adv.pos)
if bld and bld:getType()==df.building_type.Workshop and bld.construction_stage==3 then
return true,bld
else
return false
end
end
function usetool:init(args) function usetool:init(args)
self:addviews{ self:addviews{
wid.Label{ wid.Label{
@ -589,14 +565,11 @@ function usetool:init(args)
frame = {l=35,xalign=0,yalign=0}, frame = {l=35,xalign=0,yalign=0},
visible=false, visible=false,
text={ text={
{gap=1,key=keybinds.workshop.key,key_sep="()", text="Workshop Mode",pen=dfhack.pen.parse{fg=COLOR_YELLOW,bg=0}}} {id="text1",gap=1,key=keybinds.workshop.key,key_sep="()", text="Workshop menu",pen=dfhack.pen.parse{fg=COLOR_YELLOW,bg=0}}}
} }
} }
end end
function usetool:onRenderBody(dc)
self:shopMode(self:isOnWorkshop())
self:renderParent()
end
function usetool:onIdle() function usetool:onIdle()
self._native.parent:logic() self._native.parent:logic()
@ -626,31 +599,19 @@ ALLOWED_KEYS={
function moddedpos(pos,delta) function moddedpos(pos,delta)
return {x=pos.x+delta[1],y=pos.y+delta[2],z=pos.z+delta[3]} return {x=pos.x+delta[1],y=pos.y+delta[2],z=pos.z+delta[3]}
end 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() function usetool:onHelp()
showHelp() showHelp()
end end
function setFiltersUp(common,specific,args) function setFiltersUp(specific,args)
local job=args.job local job=args.job
for k,v in pairs(specific) do if specific.job_fields~=nil then
if type(k)=="string" then job:assign(specific.job_fields)
job[k]=v
end
end end
for _,v in ipairs(specific) do --printall(specific)
local filter for _,v in ipairs(specific.items) do
filter=require("utils").clone(common or {}) --printall(v)
local filter=v
filter.new=true filter.new=true
require("utils").assign(filter,v)
--printall(filter)
job.job_items:insert("#",filter) job.job_items:insert("#",filter)
end end
return true return true
@ -659,8 +620,8 @@ function onWorkShopJobChosen(args,idx,choice)
args.pos=args.from_pos args.pos=args.from_pos
args.job_type=choice.job_id args.job_type=choice.job_id
args.post_actions={AssignBuildingRef} args.post_actions={AssignBuildingRef}
args.pre_actions={dfhack.curry(setFiltersUp,args.common,choice.filter),function(args)return AssignJobItems(args,true) end} args.pre_actions={dfhack.curry(setFiltersUp,choice.filter),AssignJobItems}
local job,msg=MakeJob(args) local job,msg=makeJob(args)
if not job then if not job then
dfhack.gui.showAnnouncement(msg,5,1) dfhack.gui.showAnnouncement(msg,5,1)
end end
@ -676,30 +637,79 @@ function onWorkShopJobChosen(args,idx,choice)
--local ok,msg=AssignJobItems(args) --local ok,msg=AssignJobItems(args)
--print(ok,msg) --print(ok,msg)
end end
function siegeWeaponActionChosen(building,actionid)
local args
if actionid==1 then
building.facing=(building.facing+1)%4
elseif actionid==2 then
local action=df.job_type.LoadBallista
if building:getSubtype()==df.siegeengine_type.Catapult then
action=df.job_type.LoadCatapult
end
args={}
args.job_type=action
args.unit=df.global.world.units.active[0]
local from_pos={x=args.unit.pos.x,y=args.unit.pos.y, z=args.unit.pos.z}
args.from_pos=from_pos
args.pos=from_pos
args.pre_actions={dfhack.curry(setFiltersUp,{items={{}}})}
--issue a job...
elseif actionid==3 then
local action=df.job_type.FireBallista
if building:getSubtype()==df.siegeengine_type.Catapult then
action=df.job_type.FireCatapult
end
args={}
args.job_type=action
args.unit=df.global.world.units.active[0]
local from_pos={x=args.unit.pos.x,y=args.unit.pos.y, z=args.unit.pos.z}
args.from_pos=from_pos
args.pos=from_pos
--another job?
end
if args~=nil then
args.post_actions={AssignBuildingRef}
local ok,msg=makeJob(args)
if not ok then
dfhack.gui.showAnnouncement(msg,5,1)
end
end
end
function usetool:openSiegeWindow(building)
require("gui.dialogs").showListPrompt("Engine job choice", "Choose what to do:",COLOR_WHITE,{"Turn","Load","Fire"},
dfhack.curry(siegeWeaponActionChosen,building))
end
function usetool:openShopWindow(building) function usetool:openShopWindow(building)
local adv=df.global.world.units.active[0] local adv=df.global.world.units.active[0]
local filter_pile=workshopJobs.getJobs(building:getType(),building:getSubtype(),building:getCustomType())
local shop_type=building:getSubtype()
local filter_pile=workshops[shop_type]
if filter_pile then if filter_pile then
local state={unit=adv,from_pos={x=adv.pos.x,y=adv.pos.y, z=adv.pos.z} local state={unit=adv,from_pos={x=adv.pos.x,y=adv.pos.y, z=adv.pos.z}
,screen=self,bld=building,common=filter_pile.common} ,screen=self,bld=building,common=filter_pile.common}
choices={} choices={}
for k,v in pairs(filter_pile) do for k,v in pairs(filter_pile) do
if k~= "common" then table.insert(choices,{job_id=0,text=v.name:lower(),filter=v})
table.insert(choices,{job_id=k,text=df.job_type[k]:lower(),filter=v})
end
end end
require("gui.dialogs").showListPrompt("Workshop job choice", "Choose what to make",COLOR_WHITE,choices,dfhack.curry(onWorkShopJobChosen,state) require("gui.dialogs").showListPrompt("Workshop job choice", "Choose what to make",COLOR_WHITE,choices,dfhack.curry(onWorkShopJobChosen,state)
,nil, nil,true) ,nil, nil,true)
end end
end end
function usetool:shopMode(enable,wshop) MODES={
[df.building_type.Workshop]={
name="Workshop menu",
input=usetool.openShopWindow,
},
[df.building_type.SiegeEngine]={
name="Siege menu",
input=usetool.openSiegeWindow,
},
}
function usetool:shopMode(enable,mode,building)
self.subviews.shopLabel.visible=enable self.subviews.shopLabel.visible=enable
self.in_shop=wshop if mode then
self.subviews.shopLabel:itemById("text1").text=mode.name
self.building=building
end
self.mode=mode
end end
function usetool:shopInput(keys) function usetool:shopInput(keys)
if keys[keybinds.workshop.key] then if keys[keybinds.workshop.key] then
@ -708,7 +718,7 @@ function usetool:shopInput(keys)
end end
function usetool:fieldInput(keys) function usetool:fieldInput(keys)
local adv=df.global.world.units.active[0] local adv=df.global.world.units.active[0]
local cur_mode=dig_modes[(mode or 0)+1] local cur_mode=actions[(mode or 0)+1]
local failed=false local failed=false
for code,_ in pairs(keys) do for code,_ in pairs(keys) do
--print(code) --print(code)
@ -736,7 +746,7 @@ function usetool:fieldInput(keys)
if type(cur_mode[2])=="function" then if type(cur_mode[2])=="function" then
ok,msg=cur_mode[2](state) ok,msg=cur_mode[2](state)
else else
ok,msg=MakeJob(state) ok,msg=makeJob(state)
--(adv,moddedpos(adv.pos,MOVEMENT_KEYS[code]),cur_mode[2],adv.pos,cur_mode[4]) --(adv,moddedpos(adv.pos,MOVEMENT_KEYS[code]),cur_mode[2],adv.pos,cur_mode[4])
end end
@ -770,24 +780,39 @@ function usetool:onInput(keys)
CancelJob(adv) CancelJob(adv)
end end
elseif keys[keybinds.nextJob.key] then elseif keys[keybinds.nextJob.key] then
mode=(mode+1)%#dig_modes mode=(mode+1)%#actions
elseif keys[keybinds.prevJob.key] then elseif keys[keybinds.prevJob.key] then
mode=mode-1 mode=mode-1
if mode<0 then mode=#dig_modes-1 end if mode<0 then mode=#actions-1 end
--elseif keys.A_LOOK then --elseif keys.A_LOOK then
-- self:sendInputToParent("A_LOOK") -- self:sendInputToParent("A_LOOK")
elseif keys[keybinds.continue.key] then elseif keys[keybinds.continue.key] then
ContinueJob(adv) ContinueJob(adv)
self:sendInputToParent("A_WAIT") self:sendInputToParent("A_WAIT")
else else
if self.in_shop then if self.mode~=nil then
self:shopInput(keys) if keys[keybinds.workshop.key] then
self:fieldInput(keys) self.mode.input(self,self.building)
end
self:fieldInput(keys)
else else
self:fieldInput(keys) self:fieldInput(keys)
end end
end end
end end
function usetool:isOnBuilding()
local adv=df.global.world.units.active[0]
local bld=dfhack.buildings.findAtTile(adv.pos)
if bld and MODES[bld:getType()]~=nil and bld.construction_stage==3 then
return true,MODES[bld:getType()],bld
else
return false
end
end
function usetool:onRenderBody(dc)
self:shopMode(self:isOnBuilding())
self:renderParent()
end
if not (dfhack.gui.getCurFocus()=="dungeonmode/Look" or dfhack.gui.getCurFocus()=="dungeonmode/Default") then if not (dfhack.gui.getCurFocus()=="dungeonmode/Look" or dfhack.gui.getCurFocus()=="dungeonmode/Default") then
qerror("This script requires an adventurer mode with (l)ook or default mode.") qerror("This script requires an adventurer mode with (l)ook or default mode.")
end end