commit
0397912353
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,591 @@
|
||||
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
|
||||
}
|
||||
local fuel={item_type=df.item_type.BAR,mat_type=df.builtin_mats.COAL}
|
||||
jobs_furnace={
|
||||
[df.furnace_type.Smelter]={
|
||||
{
|
||||
name="Melt metal object",
|
||||
items={fuel,{flags2={allow_melt_dump=true}}},--also maybe melt_designated
|
||||
job_fields={job_type=df.job_type.MeltMetalObject}
|
||||
}
|
||||
},
|
||||
[df.furnace_type.MagmaSmelter]={
|
||||
{
|
||||
name="Melt metal object",
|
||||
items={{flags2={allow_melt_dump=true}}},--also maybe melt_designated
|
||||
job_fields={job_type=df.job_type.MeltMetalObject}
|
||||
}
|
||||
},
|
||||
--[[ [df.furnace_type.MetalsmithsForge]={
|
||||
unpack(concat(furnaces,mechanism,anvil,crafts,coins,flask))
|
||||
|
||||
},
|
||||
]]
|
||||
--MetalsmithsForge,
|
||||
--MagmaForge
|
||||
--[[
|
||||
forges:
|
||||
weapons and ammo-> from raws...
|
||||
armor -> raws
|
||||
furniture -> builtins?
|
||||
siege eq-> builtin (only balista head)
|
||||
trap eq -> from raws+ mechanisms
|
||||
other object-> anvil, crafts, goblets,toys,instruments,nestbox... (raws?) flask, coins,stud with iron
|
||||
metal clothing-> raws???
|
||||
]]
|
||||
[df.furnace_type.GlassFurnace]={
|
||||
{
|
||||
name="collect sand",
|
||||
items={},
|
||||
job_fields={job_type=df.job_type.CollectSand}
|
||||
},
|
||||
--glass crafts x3
|
||||
},
|
||||
[df.furnace_type.WoodFurnace]={
|
||||
defaults={item_type=df.item_type.WOOD,vector_id=df.job_item_vector_id.WOOD},
|
||||
{
|
||||
name="make charcoal",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.MakeCharcoal}
|
||||
},
|
||||
{
|
||||
name="make ash",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.MakeAsh}
|
||||
}
|
||||
},
|
||||
[df.furnace_type.Kiln]={
|
||||
{
|
||||
name="collect clay",
|
||||
items={},
|
||||
job_fields={job_type=df.job_type.CollectClay}
|
||||
}
|
||||
},
|
||||
}
|
||||
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]={
|
||||
--training weapons, wooden shields
|
||||
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
|
||||
local function reagentToJobItem(reagent,react_id,reagentId)
|
||||
local ret_item
|
||||
ret_item=utils.clone_with_default(reagent, input_filter_defaults)
|
||||
ret_item.reaction_id=react_id
|
||||
ret_item.reagent_index=reagentId
|
||||
return ret_item
|
||||
end
|
||||
local function addReactionJobs(ret,bid,wid,cid)
|
||||
local reactions=scanRawsReaction(bid,wid or -1,cid or -1)
|
||||
for idx,react in pairs(reactions) do
|
||||
local job={name=react.name,
|
||||
items={},job_fields={job_type=df.job_type.CustomReaction,reaction_name=react.code}
|
||||
}
|
||||
for reagentId,reagent in pairs(react.reagents) do
|
||||
table.insert(job.items,reagentToJobItem(reagent,idx,reagentId))
|
||||
end
|
||||
if react.flags.FUEL then
|
||||
table.insert(job.items,fuel)
|
||||
end
|
||||
table.insert(ret,job)
|
||||
end
|
||||
end
|
||||
local function scanRawsOres()
|
||||
local ret={}
|
||||
for idx,ore in ipairs(df.global.world.raws.inorganics) do
|
||||
if #ore.metal_ore.mat_index~=0 then
|
||||
ret[idx]=ore
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
local function addSmeltJobs(ret,use_fuel)
|
||||
local ores=scanRawsOres()
|
||||
for idx,ore in pairs(ores) do
|
||||
print("adding:",ore.material.state_name.Solid)
|
||||
printall(ore)
|
||||
local job={name="smelt "..ore.material.state_name.Solid,job_fields={job_type=df.job_type.SmeltOre,mat_type=df.builtin_mats.INORGANIC,mat_index=idx},items={
|
||||
{item_type=df.item_type.BOULDER,mat_type=df.builtin_mats.INORGANIC,mat_index=idx,vector_id=df.job_item_vector_id.BOULDER}}}
|
||||
if use_fuel then
|
||||
table.insert(job.items,fuel)
|
||||
end
|
||||
table.insert(ret,job)
|
||||
end
|
||||
return ret
|
||||
end
|
||||
function getJobs(buildingId,workshopId,customId)
|
||||
local ret={}
|
||||
local c_jobs
|
||||
if buildingId==df.building_type.Workshop then
|
||||
c_jobs=jobs_workshop[workshopId]
|
||||
elseif buildingId==df.building_type.Furnace then
|
||||
c_jobs=jobs_furnace[workshopId]
|
||||
|
||||
if workshopId == df.furnace_type.Smelter or workshopId == df.furnace_type.MagmaSmelter then
|
||||
c_jobs=utils.clone(c_jobs,true)
|
||||
addSmeltJobs(c_jobs,workshopId == df.furnace_type.Smelter)
|
||||
end
|
||||
else
|
||||
return nil
|
||||
end
|
||||
if c_jobs==nil then
|
||||
c_jobs={}
|
||||
else
|
||||
c_jobs=utils.clone(c_jobs,true)
|
||||
end
|
||||
|
||||
addReactionJobs(c_jobs,buildingId,workshopId,customId)
|
||||
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
|
@ -0,0 +1,285 @@
|
||||
-- Stock dialog for selecting buildings
|
||||
|
||||
local _ENV = mkmodule('gui.buildings')
|
||||
|
||||
local gui = require('gui')
|
||||
local widgets = require('gui.widgets')
|
||||
local dlg = require('gui.dialogs')
|
||||
local utils = require('utils')
|
||||
|
||||
ARROW = string.char(26)
|
||||
|
||||
WORKSHOP_ABSTRACT={
|
||||
[df.building_type.Civzone]=true,[df.building_type.Stockpile]=true,
|
||||
}
|
||||
WORKSHOP_SPECIAL={
|
||||
[df.building_type.Workshop]=true,[df.building_type.Furnace]=true,[df.building_type.Trap]=true,
|
||||
[df.building_type.Construction]=true,[df.building_type.SiegeEngine]=true
|
||||
}
|
||||
BuildingDialog = defclass(BuildingDialog, gui.FramedScreen)
|
||||
|
||||
BuildingDialog.focus_path = 'BuildingDialog'
|
||||
|
||||
BuildingDialog.ATTRS{
|
||||
prompt = 'Type or select a building from this list',
|
||||
frame_style = gui.GREY_LINE_FRAME,
|
||||
frame_inset = 1,
|
||||
frame_title = 'Select Building',
|
||||
-- new attrs
|
||||
none_caption = 'none',
|
||||
hide_none = false,
|
||||
use_abstract = true,
|
||||
use_workshops = true,
|
||||
use_tool_workshop=true,
|
||||
use_furnace = true,
|
||||
use_construction = true,
|
||||
use_siege = true,
|
||||
use_trap = true,
|
||||
use_custom = true,
|
||||
building_filter = DEFAULT_NIL,
|
||||
on_select = DEFAULT_NIL,
|
||||
on_cancel = DEFAULT_NIL,
|
||||
on_close = DEFAULT_NIL,
|
||||
}
|
||||
|
||||
function BuildingDialog:init(info)
|
||||
self:addviews{
|
||||
widgets.Label{
|
||||
text = {
|
||||
self.prompt, '\n\n',
|
||||
'Category: ', { text = self:cb_getfield('context_str'), pen = COLOR_CYAN }
|
||||
},
|
||||
text_pen = COLOR_WHITE,
|
||||
frame = { l = 0, t = 0 },
|
||||
},
|
||||
widgets.Label{
|
||||
view_id = 'back',
|
||||
visible = false,
|
||||
text = { { key = 'LEAVESCREEN', text = ': Back' } },
|
||||
frame = { r = 0, b = 0 },
|
||||
auto_width = true,
|
||||
},
|
||||
widgets.FilteredList{
|
||||
view_id = 'list',
|
||||
not_found_label = 'No matching buildings',
|
||||
frame = { l = 0, r = 0, t = 4, b = 2 },
|
||||
icon_width = 2,
|
||||
on_submit = self:callback('onSubmitItem'),
|
||||
},
|
||||
widgets.Label{
|
||||
text = { {
|
||||
key = 'SELECT', text = ': Select',
|
||||
disabled = function() return not self.subviews.list:canSubmit() end
|
||||
} },
|
||||
frame = { l = 0, b = 0 },
|
||||
}
|
||||
}
|
||||
self:initBuiltinMode()
|
||||
end
|
||||
|
||||
function BuildingDialog:getWantedFrameSize(rect)
|
||||
return math.max(self.frame_width or 40, #self.prompt), math.min(28, rect.height-8)
|
||||
end
|
||||
|
||||
function BuildingDialog:onDestroy()
|
||||
if self.on_close then
|
||||
self.on_close()
|
||||
end
|
||||
end
|
||||
|
||||
function BuildingDialog:initBuiltinMode()
|
||||
local choices = {}
|
||||
if not self.hide_none then
|
||||
table.insert(choices, { text = self.none_caption, type_id = -1, subtype_id = -1, custom_id=-1})
|
||||
end
|
||||
|
||||
if self.use_workshops then
|
||||
table.insert(choices, {
|
||||
icon = ARROW, text = 'workshop', key = 'CUSTOM_SHIFT_W',
|
||||
cb = self:callback('initWorkshopMode')
|
||||
})
|
||||
end
|
||||
if self.use_furnace then
|
||||
table.insert(choices, {
|
||||
icon = ARROW, text = 'furnaces', key = 'CUSTOM_SHIFT_F',
|
||||
cb = self:callback('initFurnaceMode')
|
||||
})
|
||||
end
|
||||
if self.use_trap then
|
||||
table.insert(choices, {
|
||||
icon = ARROW, text = 'traps', key = 'CUSTOM_SHIFT_T',
|
||||
cb = self:callback('initTrapMode')
|
||||
})
|
||||
end
|
||||
if self.use_construction then
|
||||
table.insert(choices, {
|
||||
icon = ARROW, text = 'constructions', key = 'CUSTOM_SHIFT_C',
|
||||
cb = self:callback('initConstructionMode')
|
||||
})
|
||||
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
|
||||
table.insert(choices, {
|
||||
icon = ARROW, text = 'custom workshop', key = 'CUSTOM_SHIFT_U',
|
||||
cb = self:callback('initCustomMode')
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
|
||||
for i=0,df.building_type._last_item do
|
||||
if (not WORKSHOP_ABSTRACT[i] or self.use_abstract)and not WORKSHOP_SPECIAL[i] then
|
||||
self:addBuilding(choices, df.building_type[i], i, -1,-1,nil)
|
||||
end
|
||||
end
|
||||
|
||||
self:pushContext('Any building', choices)
|
||||
end
|
||||
|
||||
function BuildingDialog:initWorkshopMode()
|
||||
local choices = {}
|
||||
|
||||
for i=0,df.workshop_type._last_item do
|
||||
if i~=df.workshop_type.Custom and (i~=df.workshop_type.Tool or self.use_tool_workshop) then
|
||||
self:addBuilding(choices, df.workshop_type[i], df.building_type.Workshop, i,-1,nil)
|
||||
end
|
||||
end
|
||||
|
||||
self:pushContext('Workshops', choices)
|
||||
end
|
||||
function BuildingDialog:initTrapMode()
|
||||
local choices = {}
|
||||
|
||||
for i=0,df.trap_type._last_item do
|
||||
self:addBuilding(choices, df.trap_type[i], df.building_type.Trap, i,-1,nil)
|
||||
end
|
||||
|
||||
self:pushContext('Traps', choices)
|
||||
end
|
||||
|
||||
function BuildingDialog:initConstructionMode()
|
||||
local choices = {}
|
||||
|
||||
for i=0,df.construction_type._last_item do
|
||||
self:addBuilding(choices, df.construction_type[i], df.building_type.Construction, i,-1,nil)
|
||||
end
|
||||
|
||||
self:pushContext('Constructions', choices)
|
||||
end
|
||||
|
||||
function BuildingDialog:initFurnaceMode()
|
||||
local choices = {}
|
||||
|
||||
for i=0,df.furnace_type._last_item do
|
||||
self:addBuilding(choices, df.furnace_type[i], df.building_type.Furnace, i,-1,nil)
|
||||
end
|
||||
|
||||
self:pushContext('Furnaces', choices)
|
||||
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()
|
||||
local choices = {}
|
||||
local raws=df.global.world.raws.buildings.all
|
||||
for k,v in pairs(raws) do
|
||||
self:addBuilding(choices, v.name, df.building_type.Workshop,df.workshop_type.Custom,v.id,v)
|
||||
end
|
||||
|
||||
self:pushContext('Custom workshops', choices)
|
||||
end
|
||||
|
||||
function BuildingDialog:addBuilding(choices, name,type_id, subtype_id, custom_id, parent)
|
||||
-- Check the filter
|
||||
if self.building_filter and not self.building_filter(name,type_id,subtype_id,custom_id, parent) then
|
||||
return
|
||||
end
|
||||
|
||||
table.insert(choices, {
|
||||
text = name:lower(),
|
||||
customshop = parent,
|
||||
type_id = type_id, subtype_id = subtype_id, custom_id=custom_id
|
||||
})
|
||||
end
|
||||
|
||||
function BuildingDialog:pushContext(name, choices)
|
||||
if not self.back_stack then
|
||||
self.back_stack = {}
|
||||
self.subviews.back.visible = false
|
||||
else
|
||||
table.insert(self.back_stack, {
|
||||
context_str = self.context_str,
|
||||
all_choices = self.subviews.list:getChoices(),
|
||||
edit_text = self.subviews.list:getFilter(),
|
||||
selected = self.subviews.list:getSelected(),
|
||||
})
|
||||
self.subviews.back.visible = true
|
||||
end
|
||||
|
||||
self.context_str = name
|
||||
self.subviews.list:setChoices(choices, 1)
|
||||
end
|
||||
|
||||
function BuildingDialog:onGoBack()
|
||||
local save = table.remove(self.back_stack)
|
||||
self.subviews.back.visible = (#self.back_stack > 0)
|
||||
|
||||
self.context_str = save.context_str
|
||||
self.subviews.list:setChoices(save.all_choices)
|
||||
self.subviews.list:setFilter(save.edit_text, save.selected)
|
||||
end
|
||||
|
||||
function BuildingDialog:submitBuilding(type_id,subtype_id,custom_id,choice,index)
|
||||
self:dismiss()
|
||||
|
||||
if self.on_select then
|
||||
self.on_select(type_id,subtype_id,custom_id,choice,index)
|
||||
end
|
||||
end
|
||||
|
||||
function BuildingDialog:onSubmitItem(idx, item)
|
||||
if item.cb then
|
||||
item:cb(idx)
|
||||
else
|
||||
self:submitBuilding(item.type_id, item.subtype_id,item.custom_id,item,idx)
|
||||
end
|
||||
end
|
||||
|
||||
function BuildingDialog:onInput(keys)
|
||||
if keys.LEAVESCREEN or keys.LEAVESCREEN_ALL then
|
||||
if self.subviews.back.visible and not keys.LEAVESCREEN_ALL then
|
||||
self:onGoBack()
|
||||
else
|
||||
self:dismiss()
|
||||
if self.on_cancel then
|
||||
self.on_cancel()
|
||||
end
|
||||
end
|
||||
else
|
||||
self:inputToSubviews(keys)
|
||||
end
|
||||
end
|
||||
|
||||
function showBuildingPrompt(title, prompt, on_select, on_cancel, build_filter)
|
||||
BuildingDialog{
|
||||
frame_title = title,
|
||||
prompt = prompt,
|
||||
building_filter = build_filter,
|
||||
on_select = on_select,
|
||||
on_cancel = on_cancel,
|
||||
}:show()
|
||||
end
|
||||
|
||||
return _ENV
|
@ -1 +1 @@
|
||||
Subproject commit fbf671a7d5aacb41cb44059eb16a1ee9cad419be
|
||||
Subproject commit 4d2afc3a0bcebdb17415dc2827b44fd35986a368
|
@ -0,0 +1,257 @@
|
||||
// Create arbitrary items
|
||||
|
||||
#include "Core.h"
|
||||
#include "Console.h"
|
||||
#include "Export.h"
|
||||
#include "PluginManager.h"
|
||||
#include "MiscUtils.h"
|
||||
|
||||
#include "modules/Maps.h"
|
||||
#include "modules/Gui.h"
|
||||
#include "modules/Items.h"
|
||||
#include "modules/Materials.h"
|
||||
|
||||
#include "DataDefs.h"
|
||||
#include "df/game_type.h"
|
||||
#include "df/world.h"
|
||||
#include "df/ui.h"
|
||||
#include "df/unit.h"
|
||||
#include "df/historical_entity.h"
|
||||
#include "df/world_site.h"
|
||||
#include "df/item.h"
|
||||
#include "df/creature_raw.h"
|
||||
#include "df/caste_raw.h"
|
||||
#include "df/reaction_reagent.h"
|
||||
#include "df/reaction_product_itemst.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace DFHack;
|
||||
|
||||
using df::global::world;
|
||||
using df::global::ui;
|
||||
using df::global::gametype;
|
||||
|
||||
DFHACK_PLUGIN("createitem");
|
||||
|
||||
command_result df_createitem (color_ostream &out, vector <string> & parameters);
|
||||
|
||||
DFhackCExport command_result plugin_init (color_ostream &out, std::vector<PluginCommand> &commands)
|
||||
{
|
||||
commands.push_back(PluginCommand("createitem", "Create arbitrary item at the selected unit's feet.", df_createitem));
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
|
||||
{
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
bool makeItem (df::reaction_product_itemst *prod, df::unit *unit, bool glove2 = false)
|
||||
{
|
||||
vector<df::item *> out_items;
|
||||
vector<df::reaction_reagent *> in_reag;
|
||||
vector<df::item *> in_items;
|
||||
bool is_gloves = (prod->item_type == df::item_type::GLOVES);
|
||||
|
||||
prod->produce(unit, &out_items, &in_reag, &in_items, 1, df::job_skill::NONE,
|
||||
df::historical_entity::find(unit->civ_id),
|
||||
((*gametype == df::game_type::DWARF_MAIN) || (*gametype == df::game_type::DWARF_RECLAIM)) ? df::world_site::find(ui->site_id) : NULL);
|
||||
if (!out_items.size())
|
||||
return false;
|
||||
for (size_t i = 0; i < out_items.size(); i++)
|
||||
{
|
||||
out_items[i]->moveToGround(unit->pos.x, unit->pos.y, unit->pos.z);
|
||||
if (is_gloves)
|
||||
{
|
||||
// if the reaction creates gloves without handedness, then create 2 sets (left and right)
|
||||
if (out_items[i]->getGloveHandedness() > 0)
|
||||
is_gloves = false;
|
||||
else
|
||||
out_items[i]->setGloveHandedness(glove2 ? 2 : 1);
|
||||
}
|
||||
}
|
||||
if (is_gloves && !glove2)
|
||||
return makeItem(prod, unit, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
command_result df_createitem (color_ostream &out, vector <string> & parameters)
|
||||
{
|
||||
string item_str, material_str;
|
||||
df::item_type item_type = df::item_type::NONE;
|
||||
int16_t item_subtype = -1;
|
||||
int16_t mat_type = -1;
|
||||
int32_t mat_index = -1;
|
||||
int count = 1;
|
||||
|
||||
if ((parameters.size() < 2) || (parameters.size() > 3))
|
||||
{
|
||||
out.print("Syntax: createitem <item> <material> [count]\n"
|
||||
" <item> - Item token for what you wish to create, as specified in custom\n"
|
||||
" reactions. If the item has no subtype, omit the :NONE.\n"
|
||||
" <material> - The material you want the item to be made of, as specified\n"
|
||||
" in custom reactions. For REMAINS, FISH, FISH_RAW, VERMIN,\n"
|
||||
" PET, and EGG, replace this with a creature ID and caste.\n"
|
||||
" [count] - How many of the item you wish to create.\n"
|
||||
);
|
||||
return CR_WRONG_USAGE;
|
||||
}
|
||||
item_str = parameters[0];
|
||||
material_str = parameters[1];
|
||||
|
||||
if (parameters.size() == 3)
|
||||
{
|
||||
stringstream ss(parameters[2]);
|
||||
ss >> count;
|
||||
if (count < 1)
|
||||
{
|
||||
out.printerr("You cannot produce less than one item!\n");
|
||||
return CR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
ItemTypeInfo item;
|
||||
MaterialInfo material;
|
||||
vector<string> tokens;
|
||||
|
||||
if (!item.find(item_str))
|
||||
{
|
||||
out.printerr("Unrecognized item type!\n");
|
||||
return CR_FAILURE;
|
||||
}
|
||||
item_type = item.type;
|
||||
item_subtype = item.subtype;
|
||||
switch (item.type)
|
||||
{
|
||||
case df::item_type::INSTRUMENT:
|
||||
case df::item_type::TOY:
|
||||
case df::item_type::WEAPON:
|
||||
case df::item_type::ARMOR:
|
||||
case df::item_type::SHOES:
|
||||
case df::item_type::SHIELD:
|
||||
case df::item_type::HELM:
|
||||
case df::item_type::GLOVES:
|
||||
case df::item_type::AMMO:
|
||||
case df::item_type::PANTS:
|
||||
case df::item_type::SIEGEAMMO:
|
||||
case df::item_type::TRAPCOMP:
|
||||
case df::item_type::TOOL:
|
||||
if (item_subtype == -1)
|
||||
{
|
||||
out.printerr("You must specify a subtype!\n");
|
||||
return CR_FAILURE;
|
||||
}
|
||||
default:
|
||||
if (!material.find(material_str))
|
||||
{
|
||||
out.printerr("Unrecognized material!\n");
|
||||
return CR_FAILURE;
|
||||
}
|
||||
mat_type = material.type;
|
||||
mat_index = material.index;
|
||||
break;
|
||||
|
||||
case df::item_type::REMAINS:
|
||||
case df::item_type::FISH:
|
||||
case df::item_type::FISH_RAW:
|
||||
case df::item_type::VERMIN:
|
||||
case df::item_type::PET:
|
||||
case df::item_type::EGG:
|
||||
split_string(&tokens, material_str, ":");
|
||||
if (tokens.size() != 2)
|
||||
{
|
||||
out.printerr("You must specify a creature ID and caste for this item type!\n");
|
||||
return CR_FAILURE;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < world->raws.creatures.all.size(); i++)
|
||||
{
|
||||
df::creature_raw *creature = world->raws.creatures.all[i];
|
||||
if (creature->creature_id == tokens[0])
|
||||
{
|
||||
for (size_t j = 0; j < creature->caste.size(); j++)
|
||||
{
|
||||
df::caste_raw *caste = creature->caste[j];
|
||||
if (creature->caste[j]->caste_id == tokens[1])
|
||||
{
|
||||
mat_type = i;
|
||||
mat_index = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mat_type == -1)
|
||||
{
|
||||
out.printerr("The creature you specified has no such caste!\n");
|
||||
return CR_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mat_type == -1)
|
||||
{
|
||||
out.printerr("Unrecognized creature ID!\n");
|
||||
return CR_FAILURE;
|
||||
}
|
||||
break;
|
||||
|
||||
case df::item_type::CORPSE:
|
||||
case df::item_type::CORPSEPIECE:
|
||||
case df::item_type::FOOD:
|
||||
out.printerr("Cannot create that type of item!\n");
|
||||
return CR_FAILURE;
|
||||
break;
|
||||
}
|
||||
|
||||
CoreSuspender suspend;
|
||||
|
||||
df::unit *unit = Gui::getSelectedUnit(out, true);
|
||||
if (!unit)
|
||||
{
|
||||
out.printerr("No unit selected!\n");
|
||||
return CR_FAILURE;
|
||||
}
|
||||
if (!Maps::IsValid())
|
||||
{
|
||||
out.printerr("Map is not available.\n");
|
||||
return CR_FAILURE;
|
||||
}
|
||||
df::map_block *block = Maps::getTileBlock(unit->pos.x, unit->pos.y, unit->pos.z);
|
||||
if (block == NULL)
|
||||
{
|
||||
out.printerr("Unit does not reside in a valid map block, somehow?\n");
|
||||
return CR_FAILURE;
|
||||
}
|
||||
|
||||
df::reaction_product_itemst *prod = df::allocate<df::reaction_product_itemst>();
|
||||
prod->item_type = item_type;
|
||||
prod->item_subtype = item_subtype;
|
||||
prod->mat_type = mat_type;
|
||||
prod->mat_index = mat_index;
|
||||
prod->probability = 100;
|
||||
prod->count = count;
|
||||
switch (item_type)
|
||||
{
|
||||
case df::item_type::BAR:
|
||||
case df::item_type::POWDER_MISC:
|
||||
case df::item_type::LIQUID_MISC:
|
||||
case df::item_type::DRINK:
|
||||
prod->product_dimension = 150;
|
||||
break;
|
||||
case df::item_type::THREAD:
|
||||
prod->product_dimension = 15000;
|
||||
break;
|
||||
case df::item_type::CLOTH:
|
||||
prod->product_dimension = 10000;
|
||||
break;
|
||||
default:
|
||||
prod->product_dimension = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!makeItem(prod, unit))
|
||||
{
|
||||
out.printerr("Failed to create item!\n");
|
||||
return CR_FAILURE;
|
||||
}
|
||||
return CR_OK;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,327 @@
|
||||
#include "Core.h"
|
||||
#include <Console.h>
|
||||
#include <Export.h>
|
||||
#include <PluginManager.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <VTableInterpose.h>
|
||||
|
||||
#include "df/building_workshopst.h"
|
||||
|
||||
#include "df/unit.h"
|
||||
#include "df/item.h"
|
||||
#include "df/item_actual.h"
|
||||
#include "df/unit_wound.h"
|
||||
#include "df/world.h"
|
||||
#include "df/reaction.h"
|
||||
#include "df/reaction_reagent_itemst.h"
|
||||
#include "df/reaction_product_itemst.h"
|
||||
|
||||
#include "df/proj_itemst.h"
|
||||
#include "df/proj_unitst.h"
|
||||
|
||||
#include "MiscUtils.h"
|
||||
#include "LuaTools.h"
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
using std::stack;
|
||||
using namespace DFHack;
|
||||
using namespace df::enums;
|
||||
|
||||
using df::global::gps;
|
||||
using df::global::world;
|
||||
using df::global::ui;
|
||||
|
||||
typedef df::reaction_product_itemst item_product;
|
||||
|
||||
DFHACK_PLUGIN("eventful");
|
||||
|
||||
struct ReagentSource {
|
||||
int idx;
|
||||
df::reaction_reagent *reagent;
|
||||
|
||||
ReagentSource() : idx(-1), reagent(NULL) {}
|
||||
};
|
||||
|
||||
struct MaterialSource : ReagentSource {
|
||||
bool product;
|
||||
std::string product_name;
|
||||
|
||||
int mat_type, mat_index;
|
||||
|
||||
MaterialSource() : product(false), mat_type(-1), mat_index(-1) {}
|
||||
};
|
||||
|
||||
struct ProductInfo {
|
||||
df::reaction *react;
|
||||
item_product *product;
|
||||
|
||||
MaterialSource material;
|
||||
|
||||
bool isValid() {
|
||||
return true;//due to mat_type being -1 = any
|
||||
}
|
||||
};
|
||||
|
||||
struct ReactionInfo {
|
||||
df::reaction *react;
|
||||
|
||||
std::vector<ProductInfo> products;
|
||||
};
|
||||
|
||||
static std::map<std::string, ReactionInfo> reactions;
|
||||
static std::map<df::reaction_product*, ProductInfo*> products;
|
||||
|
||||
static ReactionInfo *find_reaction(const std::string &name)
|
||||
{
|
||||
auto it = reactions.find(name);
|
||||
return (it != reactions.end()) ? &it->second : NULL;
|
||||
}
|
||||
|
||||
static bool is_lua_hook(const std::string &name)
|
||||
{
|
||||
return name.size() > 9 && memcmp(name.data(), "LUA_HOOK_", 9) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Hooks
|
||||
*/
|
||||
static void handle_fillsidebar(color_ostream &out,df::building_workshopst*,bool *call_native){};
|
||||
static void handle_postfillsidebar(color_ostream &out,df::building_workshopst*){};
|
||||
|
||||
static void handle_reaction_done(color_ostream &out,df::reaction*, df::unit *unit, std::vector<df::item*> *in_items,std::vector<df::reaction_reagent*> *in_reag
|
||||
, std::vector<df::item*> *out_items,bool *call_native){};
|
||||
static void handle_contaminate_wound(color_ostream &out,df::item_actual*,df::unit* unit, df::unit_wound* wound, uint8_t a1, int16_t a2){};
|
||||
static void handle_projitem_ci(color_ostream &out,df::proj_itemst*,bool){};
|
||||
static void handle_projitem_cm(color_ostream &out,df::proj_itemst*){};
|
||||
static void handle_projunit_ci(color_ostream &out,df::proj_unitst*,bool){};
|
||||
static void handle_projunit_cm(color_ostream &out,df::proj_unitst*){};
|
||||
|
||||
DEFINE_LUA_EVENT_2(onWorkshopFillSidebarMenu, handle_fillsidebar, df::building_workshopst*,bool* );
|
||||
DEFINE_LUA_EVENT_1(postWorkshopFillSidebarMenu, handle_postfillsidebar, df::building_workshopst*);
|
||||
|
||||
DEFINE_LUA_EVENT_6(onReactionComplete, handle_reaction_done,df::reaction*, df::unit *, std::vector<df::item*> *,std::vector<df::reaction_reagent*> *,std::vector<df::item*> *,bool *);
|
||||
DEFINE_LUA_EVENT_5(onItemContaminateWound, handle_contaminate_wound, df::item_actual*,df::unit* , df::unit_wound* , uint8_t , int16_t );
|
||||
//projectiles
|
||||
DEFINE_LUA_EVENT_2(onProjItemCheckImpact, handle_projitem_ci, df::proj_itemst*,bool );
|
||||
DEFINE_LUA_EVENT_1(onProjItemCheckMovement, handle_projitem_cm, df::proj_itemst*);
|
||||
DEFINE_LUA_EVENT_2(onProjUnitCheckImpact, handle_projunit_ci, df::proj_unitst*,bool );
|
||||
DEFINE_LUA_EVENT_1(onProjUnitCheckMovement, handle_projunit_cm, df::proj_unitst* );
|
||||
|
||||
DFHACK_PLUGIN_LUA_EVENTS {
|
||||
DFHACK_LUA_EVENT(onWorkshopFillSidebarMenu),
|
||||
DFHACK_LUA_EVENT(postWorkshopFillSidebarMenu),
|
||||
DFHACK_LUA_EVENT(onReactionComplete),
|
||||
DFHACK_LUA_EVENT(onItemContaminateWound),
|
||||
DFHACK_LUA_EVENT(onProjItemCheckImpact),
|
||||
DFHACK_LUA_EVENT(onProjItemCheckMovement),
|
||||
DFHACK_LUA_EVENT(onProjUnitCheckImpact),
|
||||
DFHACK_LUA_EVENT(onProjUnitCheckMovement),
|
||||
DFHACK_LUA_END
|
||||
};
|
||||
struct workshop_hook : df::building_workshopst{
|
||||
typedef df::building_workshopst interpose_base;
|
||||
DEFINE_VMETHOD_INTERPOSE(void,fillSidebarMenu,())
|
||||
{
|
||||
CoreSuspendClaimer suspend;
|
||||
color_ostream_proxy out(Core::getInstance().getConsole());
|
||||
bool call_native=true;
|
||||
onWorkshopFillSidebarMenu(out,this,&call_native);
|
||||
if(call_native)
|
||||
INTERPOSE_NEXT(fillSidebarMenu)();
|
||||
postWorkshopFillSidebarMenu(out,this);
|
||||
}
|
||||
};
|
||||
IMPLEMENT_VMETHOD_INTERPOSE(workshop_hook, fillSidebarMenu);
|
||||
struct product_hook : item_product {
|
||||
typedef item_product interpose_base;
|
||||
|
||||
DEFINE_VMETHOD_INTERPOSE(
|
||||
void, produce,
|
||||
(df::unit *unit, std::vector<df::item*> *out_items,
|
||||
std::vector<df::reaction_reagent*> *in_reag,
|
||||
std::vector<df::item*> *in_items,
|
||||
int32_t quantity, df::job_skill skill,
|
||||
df::historical_entity *entity, df::world_site *site)
|
||||
) {
|
||||
if (auto product = products[this])
|
||||
{
|
||||
df::reaction* this_reaction=product->react;
|
||||
CoreSuspendClaimer suspend;
|
||||
color_ostream_proxy out(Core::getInstance().getConsole());
|
||||
bool call_native=true;
|
||||
onReactionComplete(out,this_reaction,unit,in_items,in_reag,out_items,&call_native);
|
||||
if(!call_native)
|
||||
return;
|
||||
}
|
||||
|
||||
INTERPOSE_NEXT(produce)(unit, out_items, in_reag, in_items, quantity, skill, entity, site);
|
||||
}
|
||||
};
|
||||
|
||||
IMPLEMENT_VMETHOD_INTERPOSE(product_hook, produce);
|
||||
|
||||
|
||||
struct item_hooks :df::item_actual {
|
||||
typedef df::item_actual interpose_base;
|
||||
|
||||
DEFINE_VMETHOD_INTERPOSE(void, contaminateWound,(df::unit* unit, df::unit_wound* wound, uint8_t a1, int16_t a2))
|
||||
{
|
||||
CoreSuspendClaimer suspend;
|
||||
color_ostream_proxy out(Core::getInstance().getConsole());
|
||||
onItemContaminateWound(out,this,unit,wound,a1,a2);
|
||||
INTERPOSE_NEXT(contaminateWound)(unit,wound,a1,a2);
|
||||
}
|
||||
|
||||
};
|
||||
IMPLEMENT_VMETHOD_INTERPOSE(item_hooks, contaminateWound);
|
||||
|
||||
struct proj_item_hook: df::proj_itemst{
|
||||
typedef df::proj_itemst interpose_base;
|
||||
DEFINE_VMETHOD_INTERPOSE(bool,checkImpact,(bool mode))
|
||||
{
|
||||
CoreSuspendClaimer suspend;
|
||||
color_ostream_proxy out(Core::getInstance().getConsole());
|
||||
onProjItemCheckImpact(out,this,mode);
|
||||
return INTERPOSE_NEXT(checkImpact)(mode); //returns destroy item or not?
|
||||
}
|
||||
DEFINE_VMETHOD_INTERPOSE(bool,checkMovement,())
|
||||
{
|
||||
CoreSuspendClaimer suspend;
|
||||
color_ostream_proxy out(Core::getInstance().getConsole());
|
||||
onProjItemCheckMovement(out,this);
|
||||
return INTERPOSE_NEXT(checkMovement)();
|
||||
}
|
||||
};
|
||||
IMPLEMENT_VMETHOD_INTERPOSE(proj_item_hook,checkImpact);
|
||||
IMPLEMENT_VMETHOD_INTERPOSE(proj_item_hook,checkMovement);
|
||||
|
||||
struct proj_unit_hook: df::proj_unitst{
|
||||
typedef df::proj_unitst interpose_base;
|
||||
DEFINE_VMETHOD_INTERPOSE(bool,checkImpact,(bool mode))
|
||||
{
|
||||
CoreSuspendClaimer suspend;
|
||||
color_ostream_proxy out(Core::getInstance().getConsole());
|
||||
onProjUnitCheckImpact(out,this,mode);
|
||||
return INTERPOSE_NEXT(checkImpact)(mode); //returns destroy item or not?
|
||||
}
|
||||
DEFINE_VMETHOD_INTERPOSE(bool,checkMovement,())
|
||||
{
|
||||
CoreSuspendClaimer suspend;
|
||||
color_ostream_proxy out(Core::getInstance().getConsole());
|
||||
onProjUnitCheckMovement(out,this);
|
||||
return INTERPOSE_NEXT(checkMovement)();
|
||||
}
|
||||
};
|
||||
IMPLEMENT_VMETHOD_INTERPOSE(proj_unit_hook,checkImpact);
|
||||
IMPLEMENT_VMETHOD_INTERPOSE(proj_unit_hook,checkMovement);
|
||||
/*
|
||||
* Scan raws for matching reactions.
|
||||
*/
|
||||
|
||||
|
||||
static void parse_product(
|
||||
color_ostream &out, ProductInfo &info, df::reaction *react, item_product *prod
|
||||
) {
|
||||
info.react = react;
|
||||
info.product = prod;
|
||||
info.material.mat_type = prod->mat_type;
|
||||
info.material.mat_index = prod->mat_index;
|
||||
}
|
||||
|
||||
static bool find_reactions(color_ostream &out)
|
||||
{
|
||||
reactions.clear();
|
||||
|
||||
auto &rlist = world->raws.reactions;
|
||||
|
||||
for (size_t i = 0; i < rlist.size(); i++)
|
||||
{
|
||||
if (!is_lua_hook(rlist[i]->code))
|
||||
continue;
|
||||
reactions[rlist[i]->code].react = rlist[i];
|
||||
}
|
||||
|
||||
for (auto it = reactions.begin(); it != reactions.end(); ++it)
|
||||
{
|
||||
auto &prod = it->second.react->products;
|
||||
auto &out_prod = it->second.products;
|
||||
|
||||
for (size_t i = 0; i < prod.size(); i++)
|
||||
{
|
||||
auto itprod = strict_virtual_cast<item_product>(prod[i]);
|
||||
if (!itprod) continue;
|
||||
|
||||
out_prod.push_back(ProductInfo());
|
||||
parse_product(out, out_prod.back(), it->second.react, itprod);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < prod.size(); i++)
|
||||
{
|
||||
if (out_prod[i].isValid())
|
||||
products[out_prod[i].product] = &out_prod[i];
|
||||
}
|
||||
}
|
||||
|
||||
return !products.empty();
|
||||
}
|
||||
|
||||
static void enable_hooks(bool enable)
|
||||
{
|
||||
INTERPOSE_HOOK(workshop_hook,fillSidebarMenu).apply(enable);
|
||||
INTERPOSE_HOOK(item_hooks,contaminateWound).apply(enable);
|
||||
INTERPOSE_HOOK(proj_unit_hook,checkImpact).apply(enable);
|
||||
INTERPOSE_HOOK(proj_unit_hook,checkMovement).apply(enable);
|
||||
INTERPOSE_HOOK(proj_item_hook,checkImpact).apply(enable);
|
||||
INTERPOSE_HOOK(proj_item_hook,checkMovement).apply(enable);
|
||||
}
|
||||
static void world_specific_hooks(color_ostream &out,bool enable)
|
||||
{
|
||||
if(enable && find_reactions(out))
|
||||
{
|
||||
out.print("Detected reaction hooks - enabling plugin.\n");
|
||||
INTERPOSE_HOOK(product_hook, produce).apply(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
INTERPOSE_HOOK(product_hook, produce).apply(false);
|
||||
reactions.clear();
|
||||
products.clear();
|
||||
}
|
||||
}
|
||||
void disable_all_hooks(color_ostream &out)
|
||||
{
|
||||
world_specific_hooks(out,false);
|
||||
enable_hooks(false);
|
||||
}
|
||||
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
|
||||
{
|
||||
switch (event) {
|
||||
case SC_WORLD_LOADED:
|
||||
world_specific_hooks(out,true);
|
||||
break;
|
||||
case SC_WORLD_UNLOADED:
|
||||
world_specific_hooks(out,false);
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
|
||||
{
|
||||
if (Core::getInstance().isWorldLoaded())
|
||||
plugin_onstatechange(out, SC_WORLD_LOADED);
|
||||
enable_hooks(true);
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
|
||||
{
|
||||
disable_all_hooks(out);
|
||||
return CR_OK;
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
local _ENV = mkmodule('plugins.eventful')
|
||||
--[[
|
||||
|
||||
|
||||
--]]
|
||||
local function getShopName(btype,bsubtype,bcustom)
|
||||
local typenames_shop={[df.workshop_type.Carpenters]="CARPENTERS",[df.workshop_type.Farmers]="FARMERS",
|
||||
[df.workshop_type.Masons]="MASONS",[df.workshop_type.Craftsdwarfs]="CRAFTSDWARFS",
|
||||
[df.workshop_type.Jewelers]="JEWELERS",[df.workshop_type.MetalsmithsForge]="METALSMITHSFORGE",
|
||||
[df.workshop_type.MagmaForge]="MAGMAFORGE",[df.workshop_type.Bowyers]="BOWYERS",
|
||||
[df.workshop_type.Mechanics]="MECHANICS",[df.workshop_type.Siege]="SIEGE",
|
||||
[df.workshop_type.Butchers]="BUTCHERS",[df.workshop_type.Leatherworks]="LEATHERWORKS",
|
||||
[df.workshop_type.Tanners]="TANNERS",[df.workshop_type.Clothiers]="CLOTHIERS",
|
||||
[df.workshop_type.Fishery]="FISHERY",[df.workshop_type.Still]="STILL",
|
||||
[df.workshop_type.Loom]="LOOM",[df.workshop_type.Quern]="QUERN",
|
||||
[df.workshop_type.Kennels]="KENNELS",[df.workshop_type.Ashery]="ASHERY",
|
||||
[df.workshop_type.Kitchen]="KITCHEN",[df.workshop_type.Dyers]="DYERS",
|
||||
[df.workshop_type.Tool]="TOOL",[df.workshop_type.Millstone]="MILLSTONE",
|
||||
}
|
||||
local typenames_furnace={[df.furnace_type.WoodFurnace]="WOOD_FURNACE",[df.furnace_type.Smelter]="SMELTER",
|
||||
[df.furnace_type.GlassFurnace]="GLASS_FURNACE",[df.furnace_type.MagmaSmelter]="MAGMA_SMELTER",
|
||||
[df.furnace_type.MagmaGlassFurnace]="MAGMA_GLASS_FURNACE",[df.furnace_type.MagmaKiln]="MAGMA_KILN",
|
||||
[df.furnace_type.Kiln]="KILN"}
|
||||
if btype==df.building_type.Workshop then
|
||||
if typenames_shop[bsubtype]~=nil then
|
||||
return typenames_shop[bsubtype]
|
||||
else
|
||||
return nil --todo add custom (not very useful)
|
||||
end
|
||||
elseif btype==df.building_type.Furnace then
|
||||
if typenames_furnace[bsubtype]~=nil then
|
||||
return typenames_furnace[bsubtype]
|
||||
else
|
||||
return nil --todo add custom (not very useful)
|
||||
end
|
||||
end
|
||||
end
|
||||
_registeredStuff={}
|
||||
local function unregall(state)
|
||||
if state==SC_WORLD_UNLOADED then
|
||||
onReactionComplete._library=nil
|
||||
postWorkshopFillSidebarMenu._library=nil
|
||||
dfhack.onStateChange.eventful= nil
|
||||
_registeredStuff={}
|
||||
end
|
||||
end
|
||||
local function onReact(reaction,unit,input_items,input_reagents,output_items,call_native)
|
||||
if _registeredStuff.reactionCallbacks and _registeredStuff.reactionCallbacks[reaction.code] then
|
||||
_registeredStuff.reactionCallbacks[reaction.code](reaction,unit,input_items,input_reagents,output_items,call_native)
|
||||
end
|
||||
end
|
||||
local function onPostSidebar(workshop)
|
||||
local shop_id=getShopName(workshop:getType(),workshop:getSubtype(),workshop:getCustomType())
|
||||
if shop_id then
|
||||
if _registeredStuff.shopNonNative and _registeredStuff.shopNonNative[shop_id] then
|
||||
if _registeredStuff.shopNonNative[shop_id].all then
|
||||
--[[for _,button in ipairs(df.global.ui_sidebar_menus.workshop_job.choices_all) do
|
||||
button.is_hidden=true
|
||||
end]]
|
||||
df.global.ui_sidebar_menus.workshop_job.choices_visible:resize(0)
|
||||
else
|
||||
--todo by name
|
||||
end
|
||||
end
|
||||
|
||||
if _registeredStuff.reactionToShop and _registeredStuff.reactionToShop[shop_id] then
|
||||
for _,reaction_name in ipairs(_registeredStuff.reactionToShop[shop_id]) do
|
||||
local new_button=df.interface_button_building_new_jobst:new()
|
||||
--new_button.hotkey_id=--todo get hotkey
|
||||
new_button.is_hidden=false
|
||||
new_button.building=workshop
|
||||
new_button.job_type=df.job_type.CustomReaction --could be used for other stuff too i guess...
|
||||
new_button.reaction_name=reaction_name
|
||||
new_button.is_custom=true
|
||||
local wjob=df.global.ui_sidebar_menus.workshop_job
|
||||
wjob.choices_all:insert("#",new_button)
|
||||
wjob.choices_visible:insert("#",new_button)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function registerReaction(reaction_name,callback)
|
||||
_registeredStuff.reactionCallbacks=_registeredStuff.reactionCallbacks or {}
|
||||
_registeredStuff.reactionCallbacks[reaction_name]=callback
|
||||
onReactionComplete._library=onReact
|
||||
dfhack.onStateChange.eventful=unregall
|
||||
end
|
||||
|
||||
function removeNative(shop_name,name)
|
||||
_registeredStuff.shopNonNative=_registeredStuff.shopNonNative or {}
|
||||
local shops=_registeredStuff.shopNonNative
|
||||
shops[shop_name]=shops[shop_name] or {}
|
||||
if name~=nil then
|
||||
table.insert(shops[shop_name],name)
|
||||
else
|
||||
shops[shop_name].all=true
|
||||
end
|
||||
postWorkshopFillSidebarMenu._library=onPostSidebar
|
||||
dfhack.onStateChange.eventful=unregall
|
||||
end
|
||||
|
||||
function addReactionToShop(reaction_name,shop_name)
|
||||
_registeredStuff.reactionToShop=_registeredStuff.reactionToShop or {}
|
||||
local shops=_registeredStuff.reactionToShop
|
||||
shops[shop_name]=shops[shop_name] or {}
|
||||
table.insert(shops[shop_name],reaction_name)
|
||||
postWorkshopFillSidebarMenu._library=onPostSidebar
|
||||
dfhack.onStateChange.eventful=unregall
|
||||
end
|
||||
|
||||
return _ENV
|
@ -1,13 +0,0 @@
|
||||
local _ENV = mkmodule('plugins.reactionhooks')
|
||||
|
||||
--[[
|
||||
|
||||
Native events:
|
||||
|
||||
* onReactionComplete(burrow)
|
||||
|
||||
--]]
|
||||
|
||||
rawset_default(_ENV, dfhack.reactionhooks)
|
||||
|
||||
return _ENV
|
@ -1,322 +0,0 @@
|
||||
#include "Core.h"
|
||||
#include <Console.h>
|
||||
#include <Export.h>
|
||||
#include <PluginManager.h>
|
||||
#include <modules/Gui.h>
|
||||
#include <modules/Screen.h>
|
||||
#include <modules/Maps.h>
|
||||
#include <modules/Job.h>
|
||||
#include <modules/Items.h>
|
||||
#include <modules/Units.h>
|
||||
#include <TileTypes.h>
|
||||
#include <vector>
|
||||
#include <cstdio>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <cmath>
|
||||
#include <string.h>
|
||||
|
||||
#include <VTableInterpose.h>
|
||||
#include "df/item_liquid_miscst.h"
|
||||
#include "df/item_constructed.h"
|
||||
#include "df/builtin_mats.h"
|
||||
#include "df/world.h"
|
||||
#include "df/job.h"
|
||||
#include "df/job_item.h"
|
||||
#include "df/job_item_ref.h"
|
||||
#include "df/ui.h"
|
||||
#include "df/report.h"
|
||||
#include "df/reaction.h"
|
||||
#include "df/reaction_reagent_itemst.h"
|
||||
#include "df/reaction_product_itemst.h"
|
||||
#include "df/matter_state.h"
|
||||
#include "df/contaminant.h"
|
||||
|
||||
#include "MiscUtils.h"
|
||||
#include "LuaTools.h"
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
using std::stack;
|
||||
using namespace DFHack;
|
||||
using namespace df::enums;
|
||||
|
||||
using df::global::gps;
|
||||
using df::global::world;
|
||||
using df::global::ui;
|
||||
|
||||
typedef df::reaction_product_itemst item_product;
|
||||
|
||||
DFHACK_PLUGIN("reactionhooks");
|
||||
|
||||
struct ReagentSource {
|
||||
int idx;
|
||||
df::reaction_reagent *reagent;
|
||||
|
||||
ReagentSource() : idx(-1), reagent(NULL) {}
|
||||
};
|
||||
|
||||
struct MaterialSource : ReagentSource {
|
||||
bool product;
|
||||
std::string product_name;
|
||||
|
||||
int mat_type, mat_index;
|
||||
|
||||
MaterialSource() : product(false), mat_type(-1), mat_index(-1) {}
|
||||
};
|
||||
|
||||
struct ProductInfo {
|
||||
df::reaction *react;
|
||||
item_product *product;
|
||||
|
||||
MaterialSource material;
|
||||
|
||||
bool isValid() {
|
||||
return (material.mat_type >= 0 || material.reagent);
|
||||
}
|
||||
};
|
||||
|
||||
struct ReactionInfo {
|
||||
df::reaction *react;
|
||||
|
||||
std::vector<ProductInfo> products;
|
||||
};
|
||||
|
||||
static std::map<std::string, ReactionInfo> reactions;
|
||||
static std::map<df::reaction_product*, ProductInfo*> products;
|
||||
|
||||
static ReactionInfo *find_reaction(const std::string &name)
|
||||
{
|
||||
auto it = reactions.find(name);
|
||||
return (it != reactions.end()) ? &it->second : NULL;
|
||||
}
|
||||
|
||||
static bool is_lua_hook(const std::string &name)
|
||||
{
|
||||
return name.size() > 9 && memcmp(name.data(), "LUA_HOOK_", 9) == 0;
|
||||
}
|
||||
|
||||
static void find_material(int *type, int *index, df::item *input, MaterialSource &mat)
|
||||
{
|
||||
if (input && mat.reagent)
|
||||
{
|
||||
MaterialInfo info(input);
|
||||
|
||||
if (mat.product)
|
||||
{
|
||||
if (!info.findProduct(info, mat.product_name))
|
||||
{
|
||||
color_ostream_proxy out(Core::getInstance().getConsole());
|
||||
out.printerr("Cannot find product '%s'\n", mat.product_name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
*type = info.type;
|
||||
*index = info.index;
|
||||
}
|
||||
else
|
||||
{
|
||||
*type = mat.mat_type;
|
||||
*index = mat.mat_index;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Hooks
|
||||
*/
|
||||
|
||||
typedef std::map<int, std::vector<df::item*> > item_table;
|
||||
|
||||
static void index_items(item_table &table, df::job *job, ReactionInfo *info)
|
||||
{
|
||||
for (int i = job->items.size()-1; i >= 0; i--)
|
||||
{
|
||||
auto iref = job->items[i];
|
||||
if (iref->job_item_idx < 0) continue;
|
||||
auto iitem = job->job_items[iref->job_item_idx];
|
||||
|
||||
if (iitem->contains.empty())
|
||||
{
|
||||
table[iitem->reagent_index].push_back(iref->item);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<df::item*> contents;
|
||||
Items::getContainedItems(iref->item, &contents);
|
||||
|
||||
for (int j = contents.size()-1; j >= 0; j--)
|
||||
{
|
||||
for (int k = iitem->contains.size()-1; k >= 0; k--)
|
||||
{
|
||||
int ridx = iitem->contains[k];
|
||||
auto reag = info->react->reagents[ridx];
|
||||
|
||||
if (reag->matchesChild(contents[j], info->react, iitem->reaction_id))
|
||||
table[ridx].push_back(contents[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
df::item* find_item(ReagentSource &info, item_table &table)
|
||||
{
|
||||
if (!info.reagent)
|
||||
return NULL;
|
||||
if (table[info.idx].empty())
|
||||
return NULL;
|
||||
return table[info.idx].back();
|
||||
}
|
||||
|
||||
|
||||
|
||||
df::item* find_item(
|
||||
ReagentSource &info,
|
||||
std::vector<df::reaction_reagent*> *in_reag,
|
||||
std::vector<df::item*> *in_items
|
||||
) {
|
||||
if (!info.reagent)
|
||||
return NULL;
|
||||
for (int i = in_items->size(); i >= 0; i--)
|
||||
if ((*in_reag)[i] == info.reagent)
|
||||
return (*in_items)[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void handle_reaction_done(color_ostream &out,df::reaction*, df::unit *unit, std::vector<df::item*> *in_items,std::vector<df::reaction_reagent*> *in_reag
|
||||
, std::vector<df::item*> *out_items,bool *call_native){};
|
||||
|
||||
DEFINE_LUA_EVENT_6(onReactionComplete, handle_reaction_done,df::reaction*, df::unit *, std::vector<df::item*> *,std::vector<df::reaction_reagent*> *,std::vector<df::item*> *,bool *);
|
||||
|
||||
|
||||
DFHACK_PLUGIN_LUA_EVENTS {
|
||||
DFHACK_LUA_EVENT(onReactionComplete),
|
||||
DFHACK_LUA_END
|
||||
};
|
||||
|
||||
struct product_hook : item_product {
|
||||
typedef item_product interpose_base;
|
||||
|
||||
DEFINE_VMETHOD_INTERPOSE(
|
||||
void, produce,
|
||||
(df::unit *unit, std::vector<df::item*> *out_items,
|
||||
std::vector<df::reaction_reagent*> *in_reag,
|
||||
std::vector<df::item*> *in_items,
|
||||
int32_t quantity, df::job_skill skill,
|
||||
df::historical_entity *entity, df::world_site *site)
|
||||
) {
|
||||
if (auto product = products[this])
|
||||
{
|
||||
df::reaction* this_reaction=product->react;
|
||||
CoreSuspendClaimer suspend;
|
||||
color_ostream_proxy out(Core::getInstance().getConsole());
|
||||
bool call_native=true;
|
||||
onReactionComplete(out,this_reaction,unit,in_items,in_reag,out_items,&call_native);
|
||||
if(!call_native)
|
||||
return;
|
||||
}
|
||||
|
||||
INTERPOSE_NEXT(produce)(unit, out_items, in_reag, in_items, quantity, skill, entity, site);
|
||||
}
|
||||
};
|
||||
|
||||
IMPLEMENT_VMETHOD_INTERPOSE(product_hook, produce);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Scan raws for matching reactions.
|
||||
*/
|
||||
|
||||
|
||||
static void parse_product(
|
||||
color_ostream &out, ProductInfo &info, df::reaction *react, item_product *prod
|
||||
) {
|
||||
info.react = react;
|
||||
info.product = prod;
|
||||
info.material.mat_type = prod->mat_type;
|
||||
info.material.mat_index = prod->mat_index;
|
||||
}
|
||||
|
||||
static bool find_reactions(color_ostream &out)
|
||||
{
|
||||
reactions.clear();
|
||||
|
||||
auto &rlist = world->raws.reactions;
|
||||
|
||||
for (size_t i = 0; i < rlist.size(); i++)
|
||||
{
|
||||
if (!is_lua_hook(rlist[i]->code))
|
||||
continue;
|
||||
reactions[rlist[i]->code].react = rlist[i];
|
||||
}
|
||||
|
||||
for (auto it = reactions.begin(); it != reactions.end(); ++it)
|
||||
{
|
||||
auto &prod = it->second.react->products;
|
||||
auto &out_prod = it->second.products;
|
||||
|
||||
for (size_t i = 0; i < prod.size(); i++)
|
||||
{
|
||||
auto itprod = strict_virtual_cast<item_product>(prod[i]);
|
||||
if (!itprod) continue;
|
||||
|
||||
out_prod.push_back(ProductInfo());
|
||||
parse_product(out, out_prod.back(), it->second.react, itprod);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < prod.size(); i++)
|
||||
{
|
||||
if (out_prod[i].isValid())
|
||||
products[out_prod[i].product] = &out_prod[i];
|
||||
}
|
||||
}
|
||||
|
||||
return !products.empty();
|
||||
}
|
||||
|
||||
static void enable_hooks(bool enable)
|
||||
{
|
||||
INTERPOSE_HOOK(product_hook, produce).apply(enable);
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
|
||||
{
|
||||
switch (event) {
|
||||
case SC_WORLD_LOADED:
|
||||
if (find_reactions(out))
|
||||
{
|
||||
out.print("Detected reaction hooks - enabling plugin.\n");
|
||||
enable_hooks(true);
|
||||
}
|
||||
else
|
||||
enable_hooks(false);
|
||||
break;
|
||||
case SC_WORLD_UNLOADED:
|
||||
enable_hooks(false);
|
||||
reactions.clear();
|
||||
products.clear();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
|
||||
{
|
||||
if (Core::getInstance().isWorldLoaded())
|
||||
plugin_onstatechange(out, SC_WORLD_LOADED);
|
||||
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
|
||||
{
|
||||
enable_hooks(false);
|
||||
return CR_OK;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1 +1 @@
|
||||
Subproject commit cb97cf308c6e09638c0de94894473c9bd0f561fd
|
||||
Subproject commit 83e2b8a3754e2feb0d5bf106d2b43ff71ff63935
|
@ -0,0 +1,88 @@
|
||||
-- Fixes cloth/thread stockpiles by correcting material object data.
|
||||
|
||||
local raws = df.global.world.raws
|
||||
|
||||
-- Cache references to vectors in lua tables for a speed-up
|
||||
local organic_types = {}
|
||||
for i,v in ipairs(raws.mat_table.organic_types) do
|
||||
organic_types[i] = v
|
||||
end
|
||||
local organic_indexes = {}
|
||||
for i,v in ipairs(raws.mat_table.organic_indexes) do
|
||||
organic_indexes[i] = v
|
||||
end
|
||||
|
||||
local function verify(category,idx,vtype,vidx)
|
||||
if idx == -1 then
|
||||
-- Purely for reporting reasons
|
||||
return true
|
||||
end
|
||||
local tvec = organic_types[category]
|
||||
if idx < 0 or idx >= #tvec or tvec[idx] ~= vtype then
|
||||
return false
|
||||
end
|
||||
return organic_indexes[category][idx] == vidx
|
||||
end
|
||||
|
||||
local patched_cnt = 0
|
||||
local mat_cnt = 0
|
||||
|
||||
function patch_material(mat,mat_type,mat_index)
|
||||
local idxarr = mat.food_mat_index
|
||||
|
||||
-- These refer to fish/eggs, i.e. castes and not materials
|
||||
idxarr[1] = -1
|
||||
idxarr[2] = -1
|
||||
idxarr[3] = -1
|
||||
|
||||
for i = 0,#idxarr-1 do
|
||||
if not verify(i,idxarr[i],mat_type,mat_index) then
|
||||
idxarr[i] = -1
|
||||
patched_cnt = patched_cnt+1
|
||||
end
|
||||
end
|
||||
|
||||
mat_cnt = mat_cnt + 1
|
||||
end
|
||||
|
||||
function patch_materials()
|
||||
patched_cnt = 0
|
||||
mat_cnt = 0
|
||||
|
||||
print('Fixing cloth stockpile handling (bug 5739)...')
|
||||
|
||||
for i,v in ipairs(raws.inorganics) do
|
||||
patch_material(v.material, 0, i)
|
||||
end
|
||||
|
||||
for i,v in ipairs(raws.creatures.all) do
|
||||
for j,m in ipairs(v.material) do
|
||||
patch_material(m, 19+j, i)
|
||||
end
|
||||
end
|
||||
|
||||
for i,v in ipairs(raws.plants.all) do
|
||||
for j,m in ipairs(v.material) do
|
||||
patch_material(m, 419+j, i)
|
||||
end
|
||||
end
|
||||
|
||||
print('Patched '..patched_cnt..' bad references in '..mat_cnt..' materials.')
|
||||
end
|
||||
|
||||
local args = {...}
|
||||
|
||||
if args[1] == 'enable' then
|
||||
dfhack.onStateChange[_ENV] = function(sc)
|
||||
if sc == SC_WORLD_LOADED then
|
||||
patch_materials()
|
||||
end
|
||||
end
|
||||
if dfhack.isWorldLoaded() then
|
||||
patch_materials()
|
||||
end
|
||||
elseif args[1] == 'disable' then
|
||||
dfhack.onStateChange[_ENV] = nil
|
||||
else
|
||||
patch_materials()
|
||||
end
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,31 @@
|
||||
--set target unit as king/queen
|
||||
local unit=dfhack.gui.getSelectedUnit()
|
||||
if not unit then qerror("No unit selected") end
|
||||
local newfig=dfhack.units.getNemesis(unit).figure
|
||||
local my_entity=df.historical_entity.find(df.global.ui.civ_id)
|
||||
local monarch_id
|
||||
for k,v in pairs(my_entity.positions.own) do
|
||||
if v.code=="MONARCH" then
|
||||
monarch_id=v.id
|
||||
break
|
||||
end
|
||||
end
|
||||
if not monarch_id then qerror("No monarch found!") end
|
||||
local old_id
|
||||
for pos_id,v in pairs(my_entity.positions.assignments) do
|
||||
if v.position_id==monarch_id then
|
||||
old_id=v.histfig
|
||||
v.histfig=newfig.id
|
||||
local oldfig=df.historical_figure.find(old_id)
|
||||
|
||||
for k,v in pairs(oldfig.entity_links) do
|
||||
if df.histfig_entity_link_positionst:is_instance(v) and v.assignment_id==pos_id and v.entity_id==df.global.ui.civ_id then
|
||||
oldfig.entity_links:erase(k)
|
||||
break
|
||||
end
|
||||
end
|
||||
newfig.entity_links:insert("#",{new=df.histfig_entity_link_positionst,entity_id=df.global.ui.civ_id,
|
||||
link_strength=100,assignment_id=pos_id,start_year=df.global.cur_year})
|
||||
break
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue