Merge branch 'develop' of https://github.com/dfhack/dfhack into develop
commit
7f575a8fd1
@ -0,0 +1,596 @@
|
|||||||
|
-- Interface powered (somewhat user friendly) unit editor.
|
||||||
|
local gui = require 'gui'
|
||||||
|
local dialog = require 'gui.dialogs'
|
||||||
|
local widgets =require 'gui.widgets'
|
||||||
|
local guiScript = require 'gui.script'
|
||||||
|
local utils = require 'utils'
|
||||||
|
local args={...}
|
||||||
|
|
||||||
|
|
||||||
|
local target
|
||||||
|
--TODO: add more ways to guess what unit you want to edit
|
||||||
|
if args[1]~= nil then
|
||||||
|
target=df.units.find(args[1])
|
||||||
|
else
|
||||||
|
target=dfhack.gui.getSelectedUnit(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
if target==nil then
|
||||||
|
qerror("No unit to edit") --TODO: better error message
|
||||||
|
end
|
||||||
|
local editors={}
|
||||||
|
function add_editor(editor_class)
|
||||||
|
table.insert(editors,{text=editor_class.ATTRS.frame_title,on_submit=function ( unit )
|
||||||
|
editor_class{target_unit=unit}:show()
|
||||||
|
end})
|
||||||
|
end
|
||||||
|
-------------------------------various subeditors---------
|
||||||
|
--TODO set local sould or better yet skills vector to reduce long skill list access typing
|
||||||
|
editor_skills=defclass(editor_skills,gui.FramedScreen)
|
||||||
|
editor_skills.ATTRS={
|
||||||
|
frame_style = gui.GREY_LINE_FRAME,
|
||||||
|
frame_title = "Skill editor",
|
||||||
|
target_unit = DEFAULT_NIL,
|
||||||
|
learned_only= false,
|
||||||
|
}
|
||||||
|
function list_skills(unit,learned_only)
|
||||||
|
local s_=df.job_skill
|
||||||
|
local u_skills=unit.status.current_soul.skills
|
||||||
|
local ret={}
|
||||||
|
for i,v in ipairs(s_) do
|
||||||
|
if i>0 then
|
||||||
|
local u_skill=utils.binsearch(u_skills,i,"id")
|
||||||
|
if u_skill or not learned_only then
|
||||||
|
if not u_skill then
|
||||||
|
u_skill={rating=-1,experience=0}
|
||||||
|
end
|
||||||
|
|
||||||
|
local rating
|
||||||
|
if u_skill.rating >=0 then
|
||||||
|
rating=df.skill_rating.attrs[u_skill.rating]
|
||||||
|
else
|
||||||
|
rating={caption="<unlearned>",xp_threshold=0}
|
||||||
|
end
|
||||||
|
|
||||||
|
local text=string.format("%s: %s %d %d/%d",df.job_skill.attrs[i].caption,rating.caption,u_skill.rating,u_skill.experience,rating.xp_threshold)
|
||||||
|
table.insert(ret,{text=text,id=i})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
function editor_skills:update_list(no_save_place)
|
||||||
|
local skill_list=list_skills(self.target_unit,self.learned_only)
|
||||||
|
if no_save_place then
|
||||||
|
self.subviews.skills:setChoices(skill_list)
|
||||||
|
else
|
||||||
|
self.subviews.skills:setChoices(skill_list,self.subviews.skills:getSelected())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function editor_skills:init( args )
|
||||||
|
if self.target_unit.status.current_soul==nil then
|
||||||
|
qerror("Unit does not have soul, can't edit skills")
|
||||||
|
end
|
||||||
|
|
||||||
|
local skill_list=list_skills(self.target_unit,self.learned_only)
|
||||||
|
|
||||||
|
self:addviews{
|
||||||
|
widgets.FilteredList{
|
||||||
|
choices=skill_list,
|
||||||
|
frame = {t=0, b=1,l=1},
|
||||||
|
view_id="skills",
|
||||||
|
},
|
||||||
|
widgets.Label{
|
||||||
|
frame = { b=0,l=1},
|
||||||
|
text ={{text= ": exit editor ",
|
||||||
|
key = "LEAVESCREEN",
|
||||||
|
on_activate= self:callback("dismiss")
|
||||||
|
},
|
||||||
|
{text=": remove level ",
|
||||||
|
key = "SECONDSCROLL_UP",
|
||||||
|
on_activate=self:callback("level_skill",-1)},
|
||||||
|
{text=": add level ",
|
||||||
|
key = "SECONDSCROLL_DOWN",
|
||||||
|
on_activate=self:callback("level_skill",1)}
|
||||||
|
,
|
||||||
|
{text=": show learned only ",
|
||||||
|
key = "CHANGETAB",
|
||||||
|
on_activate=function ()
|
||||||
|
self.learned_only=not self.learned_only
|
||||||
|
self:update_list(true)
|
||||||
|
end}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
end
|
||||||
|
function editor_skills:get_cur_skill()
|
||||||
|
local list_wid=self.subviews.skills
|
||||||
|
local _,choice=list_wid:getSelected()
|
||||||
|
if choice==nil then
|
||||||
|
qerror("Nothing selected")
|
||||||
|
end
|
||||||
|
local u_skill=utils.binsearch(self.target_unit.status.current_soul.skills,choice.id,"id")
|
||||||
|
return choice,u_skill
|
||||||
|
end
|
||||||
|
function editor_skills:level_skill(lvl)
|
||||||
|
local sk_en,sk=self:get_cur_skill()
|
||||||
|
if lvl >0 then
|
||||||
|
local rating
|
||||||
|
|
||||||
|
if sk then
|
||||||
|
rating=sk.rating+lvl
|
||||||
|
else
|
||||||
|
rating=lvl-1
|
||||||
|
end
|
||||||
|
|
||||||
|
utils.insert_or_update(self.target_unit.status.current_soul.skills, {new=true, id=sk_en.id, rating=rating}, 'id') --TODO set exp?
|
||||||
|
elseif sk and sk.rating==0 and lvl<0 then
|
||||||
|
utils.erase_sorted_key(self.target_unit.status.current_soul.skills,sk_en.id,"id")
|
||||||
|
elseif sk and lvl<0 then
|
||||||
|
utils.insert_or_update(self.target_unit.status.current_soul.skills, {new=true, id=sk_en.id, rating=sk.rating+lvl}, 'id') --TODO set exp?
|
||||||
|
end
|
||||||
|
self:update_list()
|
||||||
|
end
|
||||||
|
function editor_skills:remove_rust(skill)
|
||||||
|
--TODO
|
||||||
|
end
|
||||||
|
add_editor(editor_skills)
|
||||||
|
------- civ editor
|
||||||
|
RaceBox = defclass(RaceBox, dialog.ListBox)
|
||||||
|
RaceBox.focus_path = 'RaceBox'
|
||||||
|
|
||||||
|
RaceBox.ATTRS{
|
||||||
|
format_name="$NAME ($TOKEN)",
|
||||||
|
with_filter=true,
|
||||||
|
allow_none=false,
|
||||||
|
}
|
||||||
|
function RaceBox:format_creature(creature_raw)
|
||||||
|
local t = {NAME=creature_raw.name[0],TOKEN=creature_raw.creature_id}
|
||||||
|
return string.gsub(self.format_name, "%$(%w+)", t)
|
||||||
|
end
|
||||||
|
function RaceBox:preinit(info)
|
||||||
|
self.format_name=RaceBox.ATTRS.format_name or info.format_name -- preinit does not have ATTRS set yet
|
||||||
|
local choices={}
|
||||||
|
if RaceBox.ATTRS.allow_none or info.allow_none then
|
||||||
|
table.insert(choices,{text="<none>",num=-1})
|
||||||
|
end
|
||||||
|
for i,v in ipairs(df.global.world.raws.creatures.all) do
|
||||||
|
local text=self:format_creature(v)
|
||||||
|
table.insert(choices,{text=text,raw=v,num=i,search_key=text:lower()})
|
||||||
|
end
|
||||||
|
info.choices=choices
|
||||||
|
end
|
||||||
|
function showRacePrompt(title, text, tcolor, on_select, on_cancel, min_width,allow_none)
|
||||||
|
RaceBox{
|
||||||
|
frame_title = title,
|
||||||
|
text = text,
|
||||||
|
text_pen = tcolor,
|
||||||
|
on_select = on_select,
|
||||||
|
on_cancel = on_cancel,
|
||||||
|
frame_width = min_width,
|
||||||
|
allow_none = allow_none,
|
||||||
|
}:show()
|
||||||
|
end
|
||||||
|
CivBox = defclass(CivBox,dialog.ListBox)
|
||||||
|
CivBox.focus_path = "CivBox"
|
||||||
|
|
||||||
|
CivBox.ATTRS={
|
||||||
|
format_name="$NAME ($ENGLISH):$ID",
|
||||||
|
format_no_name="<unnamed>:$ID",
|
||||||
|
name_other="<other(-1)>",
|
||||||
|
with_filter=true,
|
||||||
|
allow_other=false,
|
||||||
|
}
|
||||||
|
|
||||||
|
function civ_name(id,format_name,format_no_name,name_other,name_invalid)
|
||||||
|
if id==-1 then
|
||||||
|
return name_other or "<other (-1)>"
|
||||||
|
end
|
||||||
|
local civ
|
||||||
|
if type(id)=='userdata' then
|
||||||
|
civ=id
|
||||||
|
else
|
||||||
|
civ=df.historical_entity.find(id)
|
||||||
|
if civ==nil then
|
||||||
|
return name_invalid or "<invalid>"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local t={NAME=dfhack.TranslateName(civ.name),ENGLISH=dfhack.TranslateName(civ.name,true),ID=civ.id} --TODO race?, maybe something from raws?
|
||||||
|
if t.NAME=="" then
|
||||||
|
return string.gsub(format_no_name or "<unnamed>:$ID", "%$(%w+)", t)
|
||||||
|
end
|
||||||
|
return string.gsub(format_name or "$NAME ($ENGLISH):$ID", "%$(%w+)", t)
|
||||||
|
end
|
||||||
|
function CivBox:update_choices()
|
||||||
|
local choices={}
|
||||||
|
if self.allow_other then
|
||||||
|
table.insert(choices,{text=self.name_other,num=-1})
|
||||||
|
end
|
||||||
|
|
||||||
|
for i,v in ipairs(df.global.world.entities.all) do
|
||||||
|
if not self.race_filter or (v.race==self.race_filter) then --TODO filter type
|
||||||
|
local text=civ_name(v,self.format_name,self.format_no_name,self.name_other,self.name_invalid)
|
||||||
|
table.insert(choices,{text=text,raw=v,num=i})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.choices=choices
|
||||||
|
if self.subviews.list then
|
||||||
|
self.subviews.list:setChoices(self.choices)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function CivBox:update_race_filter(id)
|
||||||
|
local raw=df.creature_raw.find(id)
|
||||||
|
if raw then
|
||||||
|
self.subviews.race_label:setText(": "..raw.name[0])
|
||||||
|
self.race_filter=id
|
||||||
|
else
|
||||||
|
self.subviews.race_label:setText(": <none>")
|
||||||
|
self.race_filter=nil
|
||||||
|
end
|
||||||
|
|
||||||
|
self:update_choices()
|
||||||
|
end
|
||||||
|
function CivBox:choose_race()
|
||||||
|
showRacePrompt("Choose race","Select new race:",nil,function (id,choice)
|
||||||
|
self:update_race_filter(choice.num)
|
||||||
|
end,nil,nil,true)
|
||||||
|
end
|
||||||
|
function CivBox:init(info)
|
||||||
|
self.subviews.list.frame={t=3,r=0,l=0}
|
||||||
|
self:addviews{
|
||||||
|
widgets.Label{frame={t=1,l=0},text={
|
||||||
|
{text="Filter race ",key="CUSTOM_CTRL_A",key_sep="()",on_activate=self:callback("choose_race")},
|
||||||
|
}},
|
||||||
|
widgets.Label{frame={t=1,l=21},view_id="race_label",
|
||||||
|
text=": <none>",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self:update_choices()
|
||||||
|
end
|
||||||
|
function showCivPrompt(title, text, tcolor, on_select, on_cancel, min_width,allow_other)
|
||||||
|
CivBox{
|
||||||
|
frame_title = title,
|
||||||
|
text = text,
|
||||||
|
text_pen = tcolor,
|
||||||
|
on_select = on_select,
|
||||||
|
on_cancel = on_cancel,
|
||||||
|
frame_width = min_width,
|
||||||
|
allow_other = allow_other,
|
||||||
|
}:show()
|
||||||
|
end
|
||||||
|
|
||||||
|
editor_civ=defclass(editor_civ,gui.FramedScreen)
|
||||||
|
editor_civ.ATTRS={
|
||||||
|
frame_style = gui.GREY_LINE_FRAME,
|
||||||
|
frame_title = "Civilization editor",
|
||||||
|
target_unit = DEFAULT_NIL,
|
||||||
|
}
|
||||||
|
|
||||||
|
function editor_civ:update_curren_civ()
|
||||||
|
self.subviews.civ_name:setText("Currently: "..civ_name(self.target_unit.civ_id))
|
||||||
|
end
|
||||||
|
function editor_civ:init( args )
|
||||||
|
if self.target_unit==nil then
|
||||||
|
qerror("invalid unit")
|
||||||
|
end
|
||||||
|
|
||||||
|
self:addviews{
|
||||||
|
widgets.Label{view_id="civ_name",frame = { t=1,l=1}, text="Currently: "..civ_name(self.target_unit.civ_id)},
|
||||||
|
widgets.Label{frame = { t=2,l=1}, text={{text=": set to other (-1, usually enemy)",key="CUSTOM_N",
|
||||||
|
on_activate= function() self.target_unit.civ_id=-1;self:update_curren_civ() end}}},
|
||||||
|
widgets.Label{frame = { t=3,l=1}, text={{text=": set to current civ("..df.global.ui.civ_id..")",key="CUSTOM_C",
|
||||||
|
on_activate= function() self.target_unit.civ_id=df.global.ui.civ_id;self:update_curren_civ() end}}},
|
||||||
|
widgets.Label{frame = { t=4,l=1}, text={{text=": manually enter",key="CUSTOM_E",
|
||||||
|
on_activate=function ()
|
||||||
|
dialog.showInputPrompt("Civ id","Enter new civ id:",COLOR_WHITE,
|
||||||
|
tostring(self.target_unit.civ_id),function(new_value)
|
||||||
|
self.target_unit.civ_id=new_value
|
||||||
|
self:update_curren_civ()
|
||||||
|
end)
|
||||||
|
end}}
|
||||||
|
},
|
||||||
|
widgets.Label{frame= {t=5,l=1}, text={{text=": select from list",key="CUSTOM_L",
|
||||||
|
on_activate=function ( )
|
||||||
|
showCivPrompt("Choose civilization", "Select units civilization",nil,function ( id,choice )
|
||||||
|
self.target_unit.civ_id=choice.num
|
||||||
|
self:update_curren_civ()
|
||||||
|
end,nil,nil,true)
|
||||||
|
end
|
||||||
|
}}},
|
||||||
|
widgets.Label{
|
||||||
|
frame = { b=0,l=1},
|
||||||
|
text ={{text= ": exit editor ",
|
||||||
|
key = "LEAVESCREEN",
|
||||||
|
on_activate= self:callback("dismiss")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
end
|
||||||
|
add_editor(editor_civ)
|
||||||
|
------- counters editor
|
||||||
|
editor_counters=defclass(editor_counters,gui.FramedScreen)
|
||||||
|
editor_counters.ATTRS={
|
||||||
|
frame_style = gui.GREY_LINE_FRAME,
|
||||||
|
frame_title = "Counters editor",
|
||||||
|
target_unit = DEFAULT_NIL,
|
||||||
|
counters1={
|
||||||
|
"think_counter",
|
||||||
|
"job_counter",
|
||||||
|
"swap_counter",
|
||||||
|
"winded",
|
||||||
|
"stunned",
|
||||||
|
"unconscious",
|
||||||
|
"suffocation",
|
||||||
|
"webbed",
|
||||||
|
"soldier_mood_countdown",
|
||||||
|
"soldier_mood", --todo enum,
|
||||||
|
"pain",
|
||||||
|
"nausea",
|
||||||
|
"dizziness",
|
||||||
|
},
|
||||||
|
counters2={
|
||||||
|
"paralysis",
|
||||||
|
"numbness",
|
||||||
|
"fever",
|
||||||
|
"exhaustion",
|
||||||
|
"hunger_timer",
|
||||||
|
"thirst_timer",
|
||||||
|
"sleepiness_timer",
|
||||||
|
"stomach_content",
|
||||||
|
"stomach_food",
|
||||||
|
"vomit_timeout",
|
||||||
|
"stored_fat" --TODO what to reset to?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function editor_counters:fill_counters()
|
||||||
|
local ret={}
|
||||||
|
local u=self.target_unit
|
||||||
|
for i,v in ipairs(self.counters1) do
|
||||||
|
table.insert(ret,{f=u.counters:_field(v),name=v})
|
||||||
|
end
|
||||||
|
for i,v in ipairs(self.counters2) do
|
||||||
|
table.insert(ret,{f=u.counters2:_field(v),name=v})
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
function editor_counters:update_counters()
|
||||||
|
for i,v in ipairs(self.counter_list) do
|
||||||
|
v.text=string.format("%s:%d",v.name,v.f.value)
|
||||||
|
end
|
||||||
|
self.subviews.counters:setChoices(self.counter_list)
|
||||||
|
end
|
||||||
|
function editor_counters:set_cur_counter(value,index,choice)
|
||||||
|
choice.f.value=value
|
||||||
|
self:update_counters()
|
||||||
|
end
|
||||||
|
function editor_counters:choose_cur_counter(index,choice)
|
||||||
|
dialog.showInputPrompt(choice.name,"Enter new value:",COLOR_WHITE,
|
||||||
|
tostring(choice.f.value),function(new_value)
|
||||||
|
self:set_cur_counter(new_value,index,choice)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
function editor_counters:init( args )
|
||||||
|
if self.target_unit==nil then
|
||||||
|
qerror("invalid unit")
|
||||||
|
end
|
||||||
|
|
||||||
|
self.counter_list=self:fill_counters()
|
||||||
|
|
||||||
|
|
||||||
|
self:addviews{
|
||||||
|
widgets.FilteredList{
|
||||||
|
choices=self.counter_list,
|
||||||
|
frame = {t=0, b=1,l=1},
|
||||||
|
view_id="counters",
|
||||||
|
on_submit=self:callback("choose_cur_counter"),
|
||||||
|
on_submit2=self:callback("set_cur_counter",0),--TODO some things need to be set to different defaults
|
||||||
|
},
|
||||||
|
widgets.Label{
|
||||||
|
frame = { b=0,l=1},
|
||||||
|
text ={{text= ": exit editor ",
|
||||||
|
key = "LEAVESCREEN",
|
||||||
|
on_activate= self:callback("dismiss")
|
||||||
|
},
|
||||||
|
{text=": reset counter ",
|
||||||
|
key = "SEC_SELECT",
|
||||||
|
},
|
||||||
|
{text=": set counter ",
|
||||||
|
key = "SELECT",
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
self:update_counters()
|
||||||
|
end
|
||||||
|
add_editor(editor_counters)
|
||||||
|
|
||||||
|
wound_creator=defclass(wound_creator,gui.FramedScreen)
|
||||||
|
wound_creator.ATTRS={
|
||||||
|
frame_style = gui.GREY_LINE_FRAME,
|
||||||
|
frame_title = "Wound creator",
|
||||||
|
target_wound = DEFAULT_NIL,
|
||||||
|
--filter
|
||||||
|
}
|
||||||
|
function wound_creator:init( args )
|
||||||
|
if self.target_wound==nil then
|
||||||
|
qerror("invalid wound")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
self:addviews{
|
||||||
|
widgets.List{
|
||||||
|
|
||||||
|
frame = {t=0, b=1,l=1},
|
||||||
|
view_id="fields",
|
||||||
|
on_submit=self:callback("edit_cur_wound"),
|
||||||
|
on_submit2=self:callback("delete_current_wound")
|
||||||
|
},
|
||||||
|
widgets.Label{
|
||||||
|
frame = { b=0,l=1},
|
||||||
|
text ={{text= ": exit editor ",
|
||||||
|
key = "LEAVESCREEN",
|
||||||
|
on_activate= self:callback("dismiss")},
|
||||||
|
|
||||||
|
{text=": edit wound ",
|
||||||
|
key = "SELECT"},
|
||||||
|
|
||||||
|
{text=": delete wound ",
|
||||||
|
key = "SEC_SELECT"},
|
||||||
|
{text=": create wound ",
|
||||||
|
key = "CUSTOM_CTRL_I",
|
||||||
|
on_activate= self:callback("create_new_wound")},
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
self:update_wounds()
|
||||||
|
end
|
||||||
|
-------------------
|
||||||
|
editor_wounds=defclass(editor_wounds,gui.FramedScreen)
|
||||||
|
editor_wounds.ATTRS={
|
||||||
|
frame_style = gui.GREY_LINE_FRAME,
|
||||||
|
frame_title = "Wound editor",
|
||||||
|
target_unit = DEFAULT_NIL,
|
||||||
|
--filter
|
||||||
|
}
|
||||||
|
function is_scar( wound_part )
|
||||||
|
return wound_part.flags1.scar_cut or wound_part.flags1.scar_smashed or
|
||||||
|
wound_part.flags1.scar_edged_shake1 or wound_part.flags1.scar_blunt_shake1
|
||||||
|
end
|
||||||
|
function format_flag_name( fname )
|
||||||
|
return fname:sub(1,1):upper()..fname:sub(2):gsub("_"," ")
|
||||||
|
end
|
||||||
|
function name_from_flags( wp )
|
||||||
|
for i,v in ipairs(wp.flags1) do
|
||||||
|
if v then
|
||||||
|
return format_flag_name(df.wound_damage_flags1[i])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for i,v in ipairs(wp.flags2) do
|
||||||
|
if v then
|
||||||
|
return format_flag_name(df.wound_damage_flags2[i])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return "<unnamed wound>"
|
||||||
|
end
|
||||||
|
function format_wound( list_id,wound, unit)
|
||||||
|
|
||||||
|
local name="<unnamed wound>"
|
||||||
|
if #wound.parts>0 and #wound.parts[0].effect_type>0 then --try to make wound name by effect...
|
||||||
|
name=tostring(df.wound_effect_type[wound.parts[0].effect_type[0]])
|
||||||
|
if #wound.parts>1 then --cheap and probably incorrect...
|
||||||
|
name=name.."s"
|
||||||
|
end
|
||||||
|
elseif #wound.parts>0 and is_scar(wound.parts[0]) then
|
||||||
|
name="Scar"
|
||||||
|
elseif #wound.parts>0 then
|
||||||
|
local wp=wound.parts[0]
|
||||||
|
name=name_from_flags(wp)
|
||||||
|
end
|
||||||
|
|
||||||
|
return string.format("%d. %s id=%d",list_id,name,wound.id)
|
||||||
|
end
|
||||||
|
function editor_wounds:update_wounds()
|
||||||
|
local ret={}
|
||||||
|
for i,v in ipairs(self.trg_wounds) do
|
||||||
|
table.insert(ret,{text=format_wound(i,v,self.target_unit),wound=v})
|
||||||
|
end
|
||||||
|
self.subviews.wounds:setChoices(ret)
|
||||||
|
self.wound_list=ret
|
||||||
|
end
|
||||||
|
function editor_wounds:dirty_unit()
|
||||||
|
print("todo: implement unit status recalculation")
|
||||||
|
end
|
||||||
|
function editor_wounds:get_cur_wound()
|
||||||
|
local list_wid=self.subviews.wounds
|
||||||
|
local _,choice=list_wid:getSelected()
|
||||||
|
if choice==nil then
|
||||||
|
qerror("Nothing selected")
|
||||||
|
end
|
||||||
|
local ret_wound=utils.binsearch(self.trg_wounds,choice.id,"id")
|
||||||
|
return choice,ret_wound
|
||||||
|
end
|
||||||
|
function editor_wounds:delete_current_wound(index,choice)
|
||||||
|
|
||||||
|
utils.erase_sorted(self.trg_wounds,choice.wound,"id")
|
||||||
|
choice.wound:delete()
|
||||||
|
self:dirty_unit()
|
||||||
|
self:update_wounds()
|
||||||
|
end
|
||||||
|
function editor_wounds:create_new_wound()
|
||||||
|
print("Creating")
|
||||||
|
end
|
||||||
|
function editor_wounds:edit_cur_wound(index,choice)
|
||||||
|
|
||||||
|
end
|
||||||
|
function editor_wounds:init( args )
|
||||||
|
if self.target_unit==nil then
|
||||||
|
qerror("invalid unit")
|
||||||
|
end
|
||||||
|
self.trg_wounds=self.target_unit.body.wounds
|
||||||
|
|
||||||
|
self:addviews{
|
||||||
|
widgets.List{
|
||||||
|
|
||||||
|
frame = {t=0, b=1,l=1},
|
||||||
|
view_id="wounds",
|
||||||
|
on_submit=self:callback("edit_cur_wound"),
|
||||||
|
on_submit2=self:callback("delete_current_wound")
|
||||||
|
},
|
||||||
|
widgets.Label{
|
||||||
|
frame = { b=0,l=1},
|
||||||
|
text ={{text= ": exit editor ",
|
||||||
|
key = "LEAVESCREEN",
|
||||||
|
on_activate= self:callback("dismiss")},
|
||||||
|
|
||||||
|
{text=": edit wound ",
|
||||||
|
key = "SELECT"},
|
||||||
|
|
||||||
|
{text=": delete wound ",
|
||||||
|
key = "SEC_SELECT"},
|
||||||
|
{text=": create wound ",
|
||||||
|
key = "CUSTOM_CTRL_I",
|
||||||
|
on_activate= self:callback("create_new_wound")},
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
self:update_wounds()
|
||||||
|
end
|
||||||
|
add_editor(editor_wounds)
|
||||||
|
|
||||||
|
-------------------------------main window----------------
|
||||||
|
unit_editor = defclass(unit_editor, gui.FramedScreen)
|
||||||
|
unit_editor.ATTRS={
|
||||||
|
frame_style = gui.GREY_LINE_FRAME,
|
||||||
|
frame_title = "GameMaster's unit editor",
|
||||||
|
target_unit = DEFAULT_NIL,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function unit_editor:init(args)
|
||||||
|
|
||||||
|
self:addviews{
|
||||||
|
widgets.FilteredList{
|
||||||
|
choices=editors,
|
||||||
|
on_submit=function (idx,choice)
|
||||||
|
if choice.on_submit then
|
||||||
|
choice.on_submit(self.target_unit)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
},
|
||||||
|
widgets.Label{
|
||||||
|
frame = { b=0,l=1},
|
||||||
|
text ={{text= ": exit editor",
|
||||||
|
key = "LEAVESCREEN",
|
||||||
|
on_activate= self:callback("dismiss")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
unit_editor{target_unit=target}:show()
|
@ -0,0 +1,526 @@
|
|||||||
|
-- create-unit.lua
|
||||||
|
-- Originally created by warmist, edited by Putnam for the dragon ball mod to be used in reactions, modified by Dirst for use in The Earth Strikes Back mod, incorporating fixes discovered by Boltgun then Mifiki wrote the bit where it switches to arena mode briefly to do some of the messy work, then Expwnent combined that with the old script to make it function for histfigs
|
||||||
|
-- version 0.5
|
||||||
|
-- This is a beta version. Use at your own risk.
|
||||||
|
|
||||||
|
--[[
|
||||||
|
TODO
|
||||||
|
children and babies: set child/baby job
|
||||||
|
confirm body size is computed appropriately for different ages / life stages
|
||||||
|
incarnate pre-existing historical figures
|
||||||
|
some sort of invasion helper script
|
||||||
|
set invasion_id, etc
|
||||||
|
announcement for fake natural birth if appropriate
|
||||||
|
]]
|
||||||
|
|
||||||
|
--[[
|
||||||
|
if dfhack.gui.getCurViewscreen()._type ~= df.viewscreen_dwarfmodest or df.global.ui.main.mode ~= df.ui_sidebar_mode.LookAround then
|
||||||
|
print 'activate loo[k] mode'
|
||||||
|
return
|
||||||
|
end
|
||||||
|
--]]
|
||||||
|
|
||||||
|
local utils=require 'utils'
|
||||||
|
|
||||||
|
function createUnit(race_id, caste_id)
|
||||||
|
local curViewscreen = dfhack.gui.getCurViewscreen()
|
||||||
|
local dwarfmodeScreen = df.viewscreen_dwarfmodest:new()
|
||||||
|
curViewscreen.child = dwarfmodeScreen
|
||||||
|
dwarfmodeScreen.parent = curViewscreen
|
||||||
|
local oldMode = df.global.ui.main.mode
|
||||||
|
df.global.ui.main.mode = df.ui_sidebar_mode.LookAround
|
||||||
|
|
||||||
|
local gui = require 'gui'
|
||||||
|
|
||||||
|
df.global.world.arena_spawn.race:resize(0)
|
||||||
|
df.global.world.arena_spawn.race:insert(0,race_id) --df.global.ui.race_id)
|
||||||
|
|
||||||
|
df.global.world.arena_spawn.caste:resize(0)
|
||||||
|
df.global.world.arena_spawn.caste:insert(0,caste_id)
|
||||||
|
|
||||||
|
df.global.world.arena_spawn.creature_cnt:resize(0)
|
||||||
|
df.global.world.arena_spawn.creature_cnt:insert(0,0)
|
||||||
|
|
||||||
|
--df.global.world.arena_spawn.equipment.skills:insert(0,99)
|
||||||
|
--df.global.world.arena_spawn.equipment.skill_levels:insert(0,0)
|
||||||
|
|
||||||
|
df.global.gametype = 4
|
||||||
|
|
||||||
|
gui.simulateInput(dfhack.gui.getCurViewscreen(), 'D_LOOK_ARENA_CREATURE')
|
||||||
|
gui.simulateInput(dfhack.gui.getCurViewscreen(), 'SELECT')
|
||||||
|
|
||||||
|
df.global.gametype = 0
|
||||||
|
|
||||||
|
curViewscreen.child = nil
|
||||||
|
dwarfmodeScreen:delete()
|
||||||
|
df.global.ui.main.mode = oldMode
|
||||||
|
|
||||||
|
local id = df.global.unit_next_id-1
|
||||||
|
return id
|
||||||
|
end
|
||||||
|
|
||||||
|
--local u = df.unit.find(df.global.unit_next_id-1)
|
||||||
|
--u.civ_id = df.global.ui.civ_id
|
||||||
|
--u.population_id = df.historical_entity.find(df.global.ui.civ_id).populations[0]
|
||||||
|
--local group = df.global.ui.group_id
|
||||||
|
|
||||||
|
-- Picking a caste or gender at random
|
||||||
|
function getRandomCasteId(race_id)
|
||||||
|
local cr = df.creature_raw.find(race_id)
|
||||||
|
local caste_id, casteMax
|
||||||
|
|
||||||
|
casteMax = #cr.caste - 1
|
||||||
|
|
||||||
|
if casteMax > 0 then
|
||||||
|
return math.random(0, casteMax)
|
||||||
|
end
|
||||||
|
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local function allocateNewChunk(hist_entity)
|
||||||
|
hist_entity.save_file_id=df.global.unit_chunk_next_id
|
||||||
|
df.global.unit_chunk_next_id=df.global.unit_chunk_next_id+1
|
||||||
|
hist_entity.next_member_idx=0
|
||||||
|
print("allocating chunk:",hist_entity.save_file_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function allocateIds(nemesis_record,hist_entity)
|
||||||
|
if hist_entity.next_member_idx==100 then
|
||||||
|
allocateNewChunk(hist_entity)
|
||||||
|
end
|
||||||
|
nemesis_record.save_file_id=hist_entity.save_file_id
|
||||||
|
nemesis_record.member_idx=hist_entity.next_member_idx
|
||||||
|
hist_entity.next_member_idx=hist_entity.next_member_idx+1
|
||||||
|
end
|
||||||
|
|
||||||
|
function createFigure(trgunit,he,he_group)
|
||||||
|
local hf=df.historical_figure:new()
|
||||||
|
hf.id=df.global.hist_figure_next_id
|
||||||
|
hf.race=trgunit.race
|
||||||
|
hf.caste=trgunit.caste
|
||||||
|
hf.profession = trgunit.profession
|
||||||
|
hf.sex = trgunit.sex
|
||||||
|
df.global.hist_figure_next_id=df.global.hist_figure_next_id+1
|
||||||
|
hf.appeared_year = df.global.cur_year
|
||||||
|
|
||||||
|
hf.born_year = trgunit.relations.birth_year
|
||||||
|
hf.born_seconds = trgunit.relations.birth_time
|
||||||
|
hf.curse_year = trgunit.relations.curse_year
|
||||||
|
hf.curse_seconds = trgunit.relations.curse_time
|
||||||
|
hf.birth_year_bias = trgunit.relations.birth_year_bias
|
||||||
|
hf.birth_time_bias = trgunit.relations.birth_time_bias
|
||||||
|
hf.old_year = trgunit.relations.old_year
|
||||||
|
hf.old_seconds = trgunit.relations.old_time
|
||||||
|
hf.died_year = -1
|
||||||
|
hf.died_seconds = -1
|
||||||
|
hf.name:assign(trgunit.name)
|
||||||
|
hf.civ_id = trgunit.civ_id
|
||||||
|
hf.population_id = trgunit.population_id
|
||||||
|
hf.breed_id = -1
|
||||||
|
hf.unit_id = trgunit.id
|
||||||
|
|
||||||
|
df.global.world.history.figures:insert("#",hf)
|
||||||
|
|
||||||
|
hf.info = df.historical_figure_info:new()
|
||||||
|
hf.info.unk_14 = df.historical_figure_info.T_unk_14:new() -- hf state?
|
||||||
|
--unk_14.region_id = -1; unk_14.beast_id = -1; unk_14.unk_14 = 0
|
||||||
|
hf.info.unk_14.unk_18 = -1; hf.info.unk_14.unk_1c = -1
|
||||||
|
-- set values that seem related to state and do event
|
||||||
|
--change_state(hf, dfg.ui.site_id, region_pos)
|
||||||
|
|
||||||
|
|
||||||
|
--lets skip skills for now
|
||||||
|
--local skills = df.historical_figure_info.T_skills:new() -- skills snap shot
|
||||||
|
-- ...
|
||||||
|
-- note that innate skills are automaticaly set by DF
|
||||||
|
hf.info.skills = {new=true}
|
||||||
|
|
||||||
|
|
||||||
|
he.histfig_ids:insert('#', hf.id)
|
||||||
|
he.hist_figures:insert('#', hf)
|
||||||
|
if he_group then
|
||||||
|
he_group.histfig_ids:insert('#', hf.id)
|
||||||
|
he_group.hist_figures:insert('#', hf)
|
||||||
|
hf.entity_links:insert("#",{new=df.histfig_entity_link_memberst,entity_id=he_group.id,link_strength=100})
|
||||||
|
end
|
||||||
|
trgunit.flags1.important_historical_figure = true
|
||||||
|
trgunit.flags2.important_historical_figure = true
|
||||||
|
trgunit.hist_figure_id = hf.id
|
||||||
|
trgunit.hist_figure_id2 = hf.id
|
||||||
|
|
||||||
|
hf.entity_links:insert("#",{new=df.histfig_entity_link_memberst,entity_id=trgunit.civ_id,link_strength=100})
|
||||||
|
|
||||||
|
--add entity event
|
||||||
|
local hf_event_id=df.global.hist_event_next_id
|
||||||
|
df.global.hist_event_next_id=df.global.hist_event_next_id+1
|
||||||
|
df.global.world.history.events:insert("#",{new=df.history_event_add_hf_entity_linkst,year=trgunit.relations.birth_year,
|
||||||
|
seconds=trgunit.relations.birth_time,id=hf_event_id,civ=hf.civ_id,histfig=hf.id,link_type=0})
|
||||||
|
return hf
|
||||||
|
end
|
||||||
|
|
||||||
|
function createNemesis(trgunit,civ_id,group_id)
|
||||||
|
local id=df.global.nemesis_next_id
|
||||||
|
local nem=df.nemesis_record:new()
|
||||||
|
|
||||||
|
nem.id=id
|
||||||
|
nem.unit_id=trgunit.id
|
||||||
|
nem.unit=trgunit
|
||||||
|
nem.flags:resize(4)
|
||||||
|
--not sure about these flags...
|
||||||
|
-- [[
|
||||||
|
nem.flags[4]=true
|
||||||
|
nem.flags[5]=true
|
||||||
|
nem.flags[6]=true
|
||||||
|
nem.flags[7]=true
|
||||||
|
nem.flags[8]=true
|
||||||
|
nem.flags[9]=true
|
||||||
|
--]]
|
||||||
|
--[[for k=4,8 do
|
||||||
|
nem.flags[k]=true
|
||||||
|
end]]
|
||||||
|
nem.unk10=-1
|
||||||
|
nem.unk11=-1
|
||||||
|
nem.unk12=-1
|
||||||
|
df.global.world.nemesis.all:insert("#",nem)
|
||||||
|
df.global.nemesis_next_id=id+1
|
||||||
|
trgunit.general_refs:insert("#",{new=df.general_ref_is_nemesisst,nemesis_id=id})
|
||||||
|
trgunit.flags1.important_historical_figure=true
|
||||||
|
|
||||||
|
nem.save_file_id=-1
|
||||||
|
|
||||||
|
local he=df.historical_entity.find(civ_id)
|
||||||
|
he.nemesis_ids:insert("#",id)
|
||||||
|
he.nemesis:insert("#",nem)
|
||||||
|
local he_group
|
||||||
|
if group_id and group_id~=-1 then
|
||||||
|
he_group=df.historical_entity.find(group_id)
|
||||||
|
end
|
||||||
|
if he_group then
|
||||||
|
he_group.nemesis_ids:insert("#",id)
|
||||||
|
he_group.nemesis:insert("#",nem)
|
||||||
|
end
|
||||||
|
allocateIds(nem,he)
|
||||||
|
nem.figure=createFigure(trgunit,he,he_group)
|
||||||
|
end
|
||||||
|
|
||||||
|
--createNemesis(u, u.civ_id,group)
|
||||||
|
function createUnitInCiv(race_id, caste_id, civ_id, group_id)
|
||||||
|
local uid = createUnit(race_id, caste_id)
|
||||||
|
local unit = df.unit.find(uid)
|
||||||
|
if ( civ_id ) then
|
||||||
|
createNemesis(unit, civ_id, group_id)
|
||||||
|
end
|
||||||
|
return uid
|
||||||
|
end
|
||||||
|
|
||||||
|
function createUnitInFortCiv(race_id, caste_id)
|
||||||
|
return createUnitInCiv(race_id, caste_id, df.global.ui.civ_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
function createUnitInFortCivAndGroup(race_id, caste_id)
|
||||||
|
return createUnitInCiv(race_id, caste_id, df.global.ui.civ_id, df.global.ui.group_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
function domesticate(uid, group_id)
|
||||||
|
local u = df.unit.find(uid)
|
||||||
|
group_id = group_id or df.global.ui.group_id
|
||||||
|
-- If a friendly animal, make it domesticated. From Boltgun & Dirst
|
||||||
|
local caste=df.creature_raw.find(u.race).caste[u.caste]
|
||||||
|
if not(caste.flags.CAN_SPEAK and caste.flags.CAN_LEARN) then
|
||||||
|
-- Fix friendly animals (from Boltgun)
|
||||||
|
u.flags2.resident = false;
|
||||||
|
u.flags3.body_temp_in_range = true;
|
||||||
|
u.population_id = -1
|
||||||
|
u.status.current_soul.unit_id = u.id
|
||||||
|
|
||||||
|
u.animal.population.region_x = -1
|
||||||
|
u.animal.population.region_y = -1
|
||||||
|
u.animal.population.unk_28 = -1
|
||||||
|
u.animal.population.population_idx = -1
|
||||||
|
u.animal.population.depth = -1
|
||||||
|
|
||||||
|
u.counters.soldier_mood_countdown = -1
|
||||||
|
u.counters.death_cause = -1
|
||||||
|
|
||||||
|
u.enemy.anon_4 = -1
|
||||||
|
u.enemy.anon_5 = -1
|
||||||
|
u.enemy.anon_6 = -1
|
||||||
|
|
||||||
|
-- And make them tame (from Dirst)
|
||||||
|
u.flags1.tame = true
|
||||||
|
u.training_level = 7
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function nameUnit(id, entityRawName, civ_id)
|
||||||
|
--pick a random appropriate name
|
||||||
|
--choose three random words in the appropriate things
|
||||||
|
local unit = df.unit.find(id)
|
||||||
|
local entity_raw
|
||||||
|
if entityRawName then
|
||||||
|
for k,v in ipairs(df.global.world.raws.entities) do
|
||||||
|
if v.code == entityRawName then
|
||||||
|
entity_raw = v
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local entity = df.historical_entity.find(civ_id)
|
||||||
|
entity_raw = entity.entity_raw
|
||||||
|
end
|
||||||
|
|
||||||
|
if not entity_raw then
|
||||||
|
error('entity raw = nil: ', id, entityRawName, civ_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
local translation = entity_raw.translation
|
||||||
|
local translationIndex
|
||||||
|
for k,v in ipairs(df.global.world.raws.language.translations) do
|
||||||
|
if v.name == translation then
|
||||||
|
translationIndex = k
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
--translation = df.language_translation.find(translation)
|
||||||
|
local language_word_table = entity_raw.symbols.symbols1[0] --educated guess
|
||||||
|
function randomWord()
|
||||||
|
local index = math.random(0, #language_word_table.words[0] - 1)
|
||||||
|
return index
|
||||||
|
end
|
||||||
|
local firstName = randomWord()
|
||||||
|
local lastName1 = randomWord()
|
||||||
|
local lastName2 = randomWord()
|
||||||
|
local name = unit.status.current_soul.name
|
||||||
|
name.words[0] = language_word_table.words[0][lastName1]
|
||||||
|
name.parts_of_speech[0] = language_word_table.parts[0][lastName1]
|
||||||
|
name.words[1] = language_word_table.words[0][lastName2]
|
||||||
|
name.parts_of_speech[1] = language_word_table.parts[0][lastName2]
|
||||||
|
name.first_name = df.language_word.find(language_word_table.words[0][firstName]).forms[language_word_table.parts[0][firstName]]
|
||||||
|
name.has_name = true
|
||||||
|
name.language = translationIndex
|
||||||
|
name = unit.name
|
||||||
|
name.words[0] = language_word_table.words[0][lastName1]
|
||||||
|
name.parts_of_speech[0] = language_word_table.parts[0][lastName1]
|
||||||
|
name.words[1] = language_word_table.words[0][lastName2]
|
||||||
|
name.parts_of_speech[1] = language_word_table.parts[0][lastName2]
|
||||||
|
name.first_name = df.language_word.find(language_word_table.words[0][firstName]).forms[language_word_table.parts[0][firstName]]
|
||||||
|
name.has_name = true
|
||||||
|
name.language = translationIndex
|
||||||
|
if unit.hist_figure_id ~= -1 then
|
||||||
|
local histfig = df.historical_figure.find(unit.hist_figure_id)
|
||||||
|
name = histfig.name
|
||||||
|
name.words[0] = language_word_table.words[0][lastName1]
|
||||||
|
name.parts_of_speech[0] = language_word_table.parts[0][lastName1]
|
||||||
|
name.words[1] = language_word_table.words[0][lastName2]
|
||||||
|
name.parts_of_speech[1] = language_word_table.parts[0][lastName2]
|
||||||
|
name.first_name = df.language_word.find(language_word_table.words[0][firstName]).forms[language_word_table.parts[0][firstName]]
|
||||||
|
name.has_name = true
|
||||||
|
name.language = translationIndex
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
validArgs = --[[validArgs or]]utils.invert({
|
||||||
|
'help',
|
||||||
|
'race',
|
||||||
|
'caste',
|
||||||
|
'domesticate',
|
||||||
|
'civId',
|
||||||
|
'groupId',
|
||||||
|
'flagSet',
|
||||||
|
'flagClear',
|
||||||
|
'name',
|
||||||
|
'location',
|
||||||
|
'age'
|
||||||
|
})
|
||||||
|
|
||||||
|
if moduleMode then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local args = utils.processArgs({...}, validArgs)
|
||||||
|
if args.help then
|
||||||
|
print(
|
||||||
|
[[scripts/modtools/create-unit.lua
|
||||||
|
arguments:
|
||||||
|
-help
|
||||||
|
print this help message
|
||||||
|
-race raceName
|
||||||
|
specify the race of the unit to be created
|
||||||
|
examples:
|
||||||
|
DWARF
|
||||||
|
HUMAN
|
||||||
|
-caste casteName
|
||||||
|
specify the caste of the unit to be created
|
||||||
|
examples:
|
||||||
|
MALE
|
||||||
|
FEMALE
|
||||||
|
-domesticate
|
||||||
|
if the unit can't learn or can't speak, then make it a friendly animal
|
||||||
|
-civId id
|
||||||
|
make the created unit a member of the specified civ (or none if id = -1)
|
||||||
|
if id is \\LOCAL, then make it a member of the civ associated with the current fort
|
||||||
|
otherwise id must be an integer
|
||||||
|
-groupId id
|
||||||
|
make the created unit a member of the specified group (or none if id = -1)
|
||||||
|
if id is \\LOCAL, then make it a member of the group associated with the current fort
|
||||||
|
otherwise id must be an integer
|
||||||
|
-name entityRawName
|
||||||
|
set the unit's name to be a random name appropriate for the given entity
|
||||||
|
examples:
|
||||||
|
MOUNTAIN
|
||||||
|
-location [ x y z ]
|
||||||
|
create the unit at the specified coordinates
|
||||||
|
-age howOld
|
||||||
|
set the birth date of the unit to the specified number of years ago
|
||||||
|
-flagSet [ flag1 flag2 ... ]
|
||||||
|
set the specified unit flags in the new unit to true
|
||||||
|
flags may be selected from df.unit_flags1, df.unit_flags2, or df.unit_flags3
|
||||||
|
-flagClear [ flag1 flag2 ... ]
|
||||||
|
set the specified unit flags in the new unit to false
|
||||||
|
flags may be selected from df.unit_flags1, df.unit_flags2, or df.unit_flags3
|
||||||
|
]])
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local race
|
||||||
|
local raceIndex
|
||||||
|
local casteIndex
|
||||||
|
|
||||||
|
if not args.race or not args.caste then
|
||||||
|
error 'Specfiy a race and caste for the new unit.'
|
||||||
|
end
|
||||||
|
|
||||||
|
--find race
|
||||||
|
for i,v in ipairs(df.global.world.raws.creatures.all) do
|
||||||
|
if v.creature_id == args.race then
|
||||||
|
raceIndex = i
|
||||||
|
race = v
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not race then
|
||||||
|
error 'Invalid race.'
|
||||||
|
end
|
||||||
|
|
||||||
|
for i,v in ipairs(race.caste) do
|
||||||
|
if v.caste_id == args.caste then
|
||||||
|
casteIndex = i
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not casteIndex then
|
||||||
|
error 'Invalid caste.'
|
||||||
|
end
|
||||||
|
|
||||||
|
local age
|
||||||
|
if args.age then
|
||||||
|
age = tonumber(args.age)
|
||||||
|
if not age and not age == 0 then
|
||||||
|
error('Invalid age: ' .. args.age)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local civ_id
|
||||||
|
if args.civId == '\\LOCAL' then
|
||||||
|
civ_id = df.global.ui.civ_id
|
||||||
|
elseif args.civId and tonumber(args.civId) then
|
||||||
|
civ_id = tonumber(args.civId)
|
||||||
|
end
|
||||||
|
|
||||||
|
local group_id
|
||||||
|
if args.groupId == '\\LOCAL' then
|
||||||
|
group_id = df.global.ui.group_id
|
||||||
|
elseif args.groupId and tonumber(args.groupId) then
|
||||||
|
group_id = tonumber(args.groupId)
|
||||||
|
end
|
||||||
|
|
||||||
|
local unitId = createUnitInCiv(raceIndex, casteIndex, civ_id, group_id)
|
||||||
|
|
||||||
|
if args.domesticate then
|
||||||
|
domesticate(unitId, group_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
if age or age == 0 then
|
||||||
|
local u = df.unit.find(unitId)
|
||||||
|
local oldYearDelta = u.relations.old_year - u.relations.birth_year
|
||||||
|
u.relations.birth_year = df.global.cur_year - age
|
||||||
|
u.relations.old_year = u.relations.birth_year + oldYearDelta
|
||||||
|
--these flags are an educated guess of how to get the game to compute sizes correctly: use -flagSet and -flagClear arguments to override or supplement
|
||||||
|
u.flags2.calculated_nerves = false
|
||||||
|
u.flags2.calculated_bodyparts = false
|
||||||
|
u.flags3.body_part_relsize_computed = false
|
||||||
|
u.flags3.size_modifier_computed = false
|
||||||
|
u.flags3.compute_health = true
|
||||||
|
u.flags3.weight_computed = false
|
||||||
|
--TODO: if the unit is a child or baby it will still behave like an adult
|
||||||
|
end
|
||||||
|
|
||||||
|
if args.flagSet or args.flagClear then
|
||||||
|
local u = df.unit.find(unitId)
|
||||||
|
local flagsToSet = {}
|
||||||
|
local flagsToClear = {}
|
||||||
|
for _,v in ipairs(args.flagSet or {}) do
|
||||||
|
flagsToSet[v] = true
|
||||||
|
end
|
||||||
|
for _,v in ipairs(args.flagClear or {}) do
|
||||||
|
flagsToClear[v] = true
|
||||||
|
end
|
||||||
|
for _,k in ipairs(df.unit_flags1) do
|
||||||
|
if flagsToSet[k] then
|
||||||
|
u.flags1[k] = true;
|
||||||
|
elseif flagsToClear[k] then
|
||||||
|
u.flags1[k] = false;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for _,k in ipairs(df.unit_flags2) do
|
||||||
|
if flagsToSet[k] then
|
||||||
|
u.flags2[k] = true;
|
||||||
|
elseif flagsToClear[k] then
|
||||||
|
u.flags2[k] = false;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for _,k in ipairs(df.unit_flags3) do
|
||||||
|
if flagsToSet[k] then
|
||||||
|
u.flags3[k] = true;
|
||||||
|
elseif flagsToClear[k] then
|
||||||
|
u.flags3[k] = false;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if args.name then
|
||||||
|
nameUnit(unitId, args.name, civ_id)
|
||||||
|
else
|
||||||
|
local unit = df.unit.find(unitId)
|
||||||
|
unit.name.has_name = false
|
||||||
|
if unit.status.current_soul then
|
||||||
|
unit.status.current_soul.name.has_name = false
|
||||||
|
end
|
||||||
|
--[[if unit.hist_figure_id ~= -1 then
|
||||||
|
local histfig = df.historical_figure.find(unit.hist_figure_id)
|
||||||
|
histfig.name.has_name = false
|
||||||
|
end--]]
|
||||||
|
end
|
||||||
|
|
||||||
|
if civ_id then
|
||||||
|
local u = df.unit.find(unitId)
|
||||||
|
u.civ_id = civ_id
|
||||||
|
end
|
||||||
|
|
||||||
|
if args.location then
|
||||||
|
local u = df.unit.find(unitId)
|
||||||
|
local pos = df.coord:new()
|
||||||
|
pos.x = tonumber(args.location[1])
|
||||||
|
pos.y = tonumber(args.location[2])
|
||||||
|
pos.z = tonumber(args.location[3])
|
||||||
|
local teleport = dfhack.script_environment('teleport')
|
||||||
|
teleport.teleport(u, pos)
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[if group_id then
|
||||||
|
local u = df.unit.find(unitId)
|
||||||
|
u.group_id = group_id
|
||||||
|
end--]]
|
@ -0,0 +1,99 @@
|
|||||||
|
-- modtools/equip-item.lua
|
||||||
|
-- equip an item on a unit with a particular body part
|
||||||
|
|
||||||
|
local utils = require 'utils'
|
||||||
|
|
||||||
|
function equipItem(unit, item, bodyPart, mode)
|
||||||
|
--it is assumed that the item is on the ground
|
||||||
|
item.flags.on_ground = false
|
||||||
|
item.flags.in_inventory = true
|
||||||
|
local block = dfhack.maps.getTileBlock(item.pos)
|
||||||
|
local occupancy = block.occupancy[item.pos.x%16][item.pos.y%16]
|
||||||
|
for k,v in ipairs(block.items) do
|
||||||
|
--local blockItem = df.item.find(v)
|
||||||
|
if v == item.id then
|
||||||
|
block.items:erase(k)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local foundItem = false
|
||||||
|
for k,v in ipairs(block.items) do
|
||||||
|
local blockItem = df.item.find(v)
|
||||||
|
if blockItem.pos.x == item.pos.x and blockItem.pos.y == item.pos.y then
|
||||||
|
foundItem = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not foundItem then
|
||||||
|
occupancy.item = false
|
||||||
|
end
|
||||||
|
|
||||||
|
local inventoryItem = df.unit_inventory_item:new()
|
||||||
|
inventoryItem.item = item
|
||||||
|
inventoryItem.mode = mode
|
||||||
|
inventoryItem.body_part_id = bodyPart
|
||||||
|
unit.inventory:insert(#unit.inventory,inventoryItem)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
validArgs = --[[validArgs or--]] utils.invert({
|
||||||
|
'help',
|
||||||
|
'unit',
|
||||||
|
'item',
|
||||||
|
'bodyPart',
|
||||||
|
'mode'
|
||||||
|
})
|
||||||
|
|
||||||
|
if moduleMode then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local args = utils.processArgs({...}, validArgs)
|
||||||
|
|
||||||
|
if args.help then
|
||||||
|
print(
|
||||||
|
[[scripts/modtools/equip-item.lua
|
||||||
|
arguments:
|
||||||
|
-help
|
||||||
|
print this help message
|
||||||
|
]])
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local unitId = tonumber(args.unit) or ((args.unit == '\\LAST') and (df.global.unit_next_id-1))
|
||||||
|
local unit = df.unit.find(unitId)
|
||||||
|
if not unit then
|
||||||
|
error('invalid unit!', args.unit)
|
||||||
|
end
|
||||||
|
|
||||||
|
local itemId = tonumber(args.item) or ((args.item == '\\LAST') and (df.global.item_next_id-1))
|
||||||
|
local item = df.item.find(itemId)
|
||||||
|
if not item then
|
||||||
|
error('invalid item!', args.item)
|
||||||
|
end
|
||||||
|
|
||||||
|
local bodyPartName = args.bodyPart
|
||||||
|
local creature_raw = df.global.world.raws.creatures.all[unit.race]
|
||||||
|
local caste_raw = creature_raw.caste[unit.caste]
|
||||||
|
local body_info = caste_raw.body_info
|
||||||
|
|
||||||
|
local partId
|
||||||
|
local part
|
||||||
|
for k,v in ipairs(body_info.body_parts) do
|
||||||
|
if v.token == bodyPartName then
|
||||||
|
partId = k
|
||||||
|
part = v
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not part then
|
||||||
|
error('invalid body part name: ', bodyPartName)
|
||||||
|
end
|
||||||
|
|
||||||
|
local mode = args.mode
|
||||||
|
mode = df.unit_inventory_item.T_mode[mode]
|
||||||
|
|
||||||
|
equipItem(unit, item, partId, mode)
|
||||||
|
|
Loading…
Reference in New Issue