From b6f48e088a3fa251e881a21ef8e8c92c1861fc66 Mon Sep 17 00:00:00 2001 From: warmist Date: Sat, 25 Jul 2015 15:16:30 +0300 Subject: [PATCH 01/19] Create gm-unit.lua A gui util to (eventually) user friendly edit any unit. --- scripts/gui/gm-unit.lua | 170 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 scripts/gui/gm-unit.lua diff --git a/scripts/gui/gm-unit.lua b/scripts/gui/gm-unit.lua new file mode 100644 index 000000000..5a97928fe --- /dev/null +++ b/scripts/gui/gm-unit.lua @@ -0,0 +1,170 @@ +-- 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="",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) +-------------------------------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() From 0e939531ab630176c005cee12d4a84f0fdb1771c Mon Sep 17 00:00:00 2001 From: warmist Date: Sun, 26 Jul 2015 12:53:48 +0300 Subject: [PATCH 02/19] Update gm-unit.lua Add counter editor. --- scripts/gui/gm-unit.lua | 99 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) diff --git a/scripts/gui/gm-unit.lua b/scripts/gui/gm-unit.lua index 5a97928fe..e9b245f3a 100644 --- a/scripts/gui/gm-unit.lua +++ b/scripts/gui/gm-unit.lua @@ -131,10 +131,107 @@ function editor_skills:level_skill(lvl) end self:update_list() end -function editor_skills:remove_rust(skill) +function editor_skills:remove_rust(skill) --TODO end add_editor(editor_skills) +------- 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) -------------------------------main window---------------- unit_editor = defclass(unit_editor, gui.FramedScreen) unit_editor.ATTRS={ From d9da55f6bd4726c06cb764f77250a0a0320389d1 Mon Sep 17 00:00:00 2001 From: warmist Date: Sun, 26 Jul 2015 12:54:23 +0300 Subject: [PATCH 03/19] Update gm-unit.lua tabs to spaces. --- scripts/gui/gm-unit.lua | 300 ++++++++++++++++++++-------------------- 1 file changed, 150 insertions(+), 150 deletions(-) diff --git a/scripts/gui/gm-unit.lua b/scripts/gui/gm-unit.lua index e9b245f3a..2aecd98bf 100644 --- a/scripts/gui/gm-unit.lua +++ b/scripts/gui/gm-unit.lua @@ -10,186 +10,186 @@ 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]) + target=df.units.find(args[1]) else - target=dfhack.gui.getSelectedUnit(true) + target=dfhack.gui.getSelectedUnit(true) end if target==nil then - qerror("No unit to edit") --TODO: better error message + 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}) + 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_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 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="",xp_threshold=0} - end + local rating + if u_skill.rating >=0 then + rating=df.skill_rating.attrs[u_skill.rating] + else + rating={caption="",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 + 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 + 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 + 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) + 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{ + 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)} + 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} + 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 + 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 + 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 + 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() + 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 + --TODO end add_editor(editor_skills) ------- counters editor editor_counters=defclass(editor_counters,gui.FramedScreen) editor_counters.ATTRS={ - frame_style = gui.GREY_LINE_FRAME, + 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? - } + "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 + 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) + 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() + 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, @@ -198,38 +198,38 @@ function editor_counters:choose_cur_counter(index,choice) end) end function editor_counters:init( args ) - if self.target_unit==nil then - qerror("invalid unit") - end + if self.target_unit==nil then + qerror("invalid unit") + end - self.counter_list=self:fill_counters() + 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{ + 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", - } + key = "SEC_SELECT", + }, + {text=": set counter ", + key = "SELECT", + } } }, } - self:update_counters() + self:update_counters() end add_editor(editor_counters) -------------------------------main window---------------- @@ -243,16 +243,16 @@ unit_editor.ATTRS={ 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{ + 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", From 7b917303529779c88a8fb4548c244c0adaf09a81 Mon Sep 17 00:00:00 2001 From: expwnent Date: Mon, 27 Jul 2015 08:46:01 -0400 Subject: [PATCH 04/19] Initial commit for modtools/create-unit.lua. --- scripts/modtools/create-item.lua | 149 +++++----- scripts/modtools/create-unit.lua | 452 +++++++++++++++++++++++++++++++ scripts/modtools/equip-item.lua | 99 +++++++ scripts/teleport.lua | 6 +- 4 files changed, 640 insertions(+), 66 deletions(-) create mode 100644 scripts/modtools/create-unit.lua create mode 100644 scripts/modtools/equip-item.lua diff --git a/scripts/modtools/create-item.lua b/scripts/modtools/create-item.lua index d97e3d6de..ff5740dbb 100644 --- a/scripts/modtools/create-item.lua +++ b/scripts/modtools/create-item.lua @@ -4,17 +4,97 @@ local utils = require 'utils' -validArgs = validArgs or utils.invert({ +validArgs = --[[validArgs or--]] utils.invert({ 'help', 'creator', 'material', 'item', -- 'creature', -- 'caste', - 'matchingGloves', - 'matchingShoes' + 'leftHand', + 'rightHand', + 'quality' }) +organicTypes = organicTypes or utils.invert({ + df.item_type.REMAINS, + df.item_type.FISH, + df.item_type.FISH_RAW, + df.item_type.VERMIN, + df.item_type.PET, + df.item_type.EGG, +}) + +badTypes = badTypes or utils.invert({ + df.item_type.CORPSE, + df.item_type.CORPSEPIECE, + df.item_type.FOOD, +}) + +function createItem(creatorID, item, material, leftHand, rightHand, quality) + local itemQuality = quality and df.item_quality[quality] + print(itemQuality) + + local creator = df.unit.find(creatorID) + if not creator then + error 'Invalid creator.' + end + + if not item then + error 'Invalid item.' + end + local itemType = dfhack.items.findType(item) + if itemType == -1 then + error 'Invalid item.' + end + local itemSubtype = dfhack.items.findSubtype(item) + + if organicTypes[itemType] then + --TODO: look up creature and caste + error 'Not yet supported.' + end + + if badTypes[itemType] then + error 'Not supported.' + end + + if not material then + error 'Invalid material.' + end + local materialInfo = dfhack.matinfo.find(material) + if not materialInfo then + error 'Invalid material.' + end + + local item1 = dfhack.items.createItem(itemType, itemSubtype, materialInfo['type'], materialInfo.index, creator) + local item = df.item.find(item1) + if leftHand then + item:setGloveHandedness(2) + elseif rightHand then + item:setGloveHandedness(1) + end + + if itemQuality then + item:setQuality(itemQuality) + end + --[[if matchingGloves or matchingShoes then + if matchingGloves then + item1 = df.item.find(item1) + item1:setGloveHandedness(1); + end + local item2 = dfhack.items.createItem(itemType, itemSubtype, materialInfo['type'], materialInfo.index, creator) + if matchingGloves then + item2 = df.item.find(item2) + item2:setGloveHandedness(2); + end + end --]] + return item1 +end + +if moduleMode then + return +end + local args = utils.processArgs({...}, validArgs) if args.help then @@ -46,65 +126,4 @@ arguments: return end -if not args.creator or not tonumber(args.creator) or not df.unit.find(tonumber(args.creator)) then - error 'Invalid creator.' -end -args.creator = df.unit.find(tonumber(args.creator)) -if not args.creator then - error 'Invalid creator.' -end - -if not args.item then - error 'Invalid item.' -end -local itemType = dfhack.items.findType(args.item) -if itemType == -1 then - error 'Invalid item.' -end -local itemSubtype = dfhack.items.findSubtype(args.item) - -organicTypes = organicTypes or utils.invert({ - df.item_type.REMAINS, - df.item_type.FISH, - df.item_type.FISH_RAW, - df.item_type.VERMIN, - df.item_type.PET, - df.item_type.EGG, -}) - -if organicTypes[itemType] then - --TODO: look up creature and caste - error 'Not yet supported.' -end - -badTypes = badTypes or utils.invert({ - df.item_type.CORPSE, - df.item_type.CORPSEPIECE, - df.item_type.FOOD, -}) - -if badTypes[itemType] then - error 'Not supported.' -end - -if not args.material then - error 'Invalid material.' -end -args.material = dfhack.matinfo.find(args.material) -if not args.material then - error 'Invalid material.' -end - -local item1 = dfhack.items.createItem(itemType, itemSubtype, args.material['type'], args.material.index, args.creator) -if args.matchingGloves or args.matchingShoes then - if args.matchingGloves then - item1 = df.item.find(item1) - item1:setGloveHandedness(1); - end - local item2 = dfhack.items.createItem(itemType, itemSubtype, args.material['type'], args.material.index, args.creator) - if args.matchingGloves then - item2 = df.item.find(item2) - item2:setGloveHandedness(2); - end -end - +createItem(tonumber(args.creator), args.item, args.material, args.leftHand, args.rightHand, args.quality) diff --git a/scripts/modtools/create-unit.lua b/scripts/modtools/create-unit.lua new file mode 100644 index 000000000..1187c98b8 --- /dev/null +++ b/scripts/modtools/create-unit.lua @@ -0,0 +1,452 @@ +-- 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.4 +-- This is a beta version. Use at your own risk. + +--[[ +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 + -- ... + 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' +}) + +if moduleMode then + return +end + +local args = utils.processArgs({...}, validArgs) +if args.help then + print('help string TODO') + 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 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 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--]] diff --git a/scripts/modtools/equip-item.lua b/scripts/modtools/equip-item.lua new file mode 100644 index 000000000..492fe2b1b --- /dev/null +++ b/scripts/modtools/equip-item.lua @@ -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) + diff --git a/scripts/teleport.lua b/scripts/teleport.lua index 64a456219..cf4253ccf 100644 --- a/scripts/teleport.lua +++ b/scripts/teleport.lua @@ -3,7 +3,7 @@ -- author Putnam -- edited by expwnent -local function teleport(unit,pos) +function teleport(unit,pos) local unitoccupancy = dfhack.maps.getTileBlock(unit.pos).occupancy[unit.pos.x%16][unit.pos.y%16] local newoccupancy = dfhack.maps.getTileBlock(pos).occupancy[pos.x%16][pos.y%16] if newoccupancy.unit then @@ -26,6 +26,10 @@ validArgs = validArgs or utils.invert({ 'showpos' }) +if moduleMode then + return +end + local args = utils.processArgs({...}, validArgs) if args.showunitid or args.showpos then From 418d2831ef8fcf24e33d0889d04449e7dc8ab891 Mon Sep 17 00:00:00 2001 From: lethosor Date: Thu, 6 Aug 2015 21:50:01 -0400 Subject: [PATCH 05/19] Add a framework for including 3rd-party script repos Repos need to include a CMakeLists.txt file with calls to the DFHACK_SCRIPTS macro, which functions similarly to DFHACK_PLUGIN. The `open-legends` script from lethosor/dfhack-scripts is included as an example. --- .gitmodules | 3 +++ CMakeLists.txt | 2 ++ library/CMakeLists.txt | 1 + scripts/3rdparty/lethosor | 1 + scripts/CMakeLists.txt | 2 ++ scripts/Scripts.cmake | 19 +++++++++++++++++++ 6 files changed, 28 insertions(+) create mode 160000 scripts/3rdparty/lethosor create mode 100644 scripts/CMakeLists.txt create mode 100644 scripts/Scripts.cmake diff --git a/.gitmodules b/.gitmodules index b20d189ec..275772e0e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "depends/clsocket"] path = depends/clsocket url = git://github.com/DFHack/clsocket.git +[submodule "scripts/3rdparty/lethosor"] + path = scripts/3rdparty/lethosor + url = https://github.com/lethosor/dfhack-scripts diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a8dabd13..50fca68f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -184,6 +184,8 @@ IF(BUILD_PLUGINS) add_subdirectory (plugins) endif() +add_subdirectory(scripts) + # Packaging with CPack! IF(UNIX) if(APPLE) diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 270948e1a..73d6188a3 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -377,6 +377,7 @@ install(DIRECTORY ${dfhack_SOURCE_DIR}/scripts DESTINATION ${DFHACK_DATA_DESTINATION} FILES_MATCHING PATTERN "*.lua" PATTERN "*.rb" + PATTERN "3rdparty" EXCLUDE ) install(DIRECTORY ${dfhack_SOURCE_DIR}/patches diff --git a/scripts/3rdparty/lethosor b/scripts/3rdparty/lethosor new file mode 160000 index 000000000..213826bd6 --- /dev/null +++ b/scripts/3rdparty/lethosor @@ -0,0 +1 @@ +Subproject commit 213826bd6feba0b16a582e0c4a74c5678ddc3be8 diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt new file mode 100644 index 000000000..ed5cc1ffc --- /dev/null +++ b/scripts/CMakeLists.txt @@ -0,0 +1,2 @@ +include(Scripts.cmake) +DFHACK_3RDPARTY_SCRIPT_REPO(lethosor) diff --git a/scripts/Scripts.cmake b/scripts/Scripts.cmake new file mode 100644 index 000000000..8ef0d1161 --- /dev/null +++ b/scripts/Scripts.cmake @@ -0,0 +1,19 @@ +include(../plugins/Plugins.cmake) + +MACRO(DFHACK_SCRIPTS) + PARSE_ARGUMENTS(SCRIPT + "SUBDIRECTORY" + "SOME_OPT" + ${ARGN} + ) + CAR(SCRIPT_SUBDIRECTORY ${SCRIPT_SUBDIRECTORY}) + install(FILES ${SCRIPT_DEFAULT_ARGS} + DESTINATION ${DFHACK_DATA_DESTINATION}/scripts/${SCRIPT_SUBDIRECTORY}) +ENDMACRO() + +MACRO(DFHACK_3RDPARTY_SCRIPT_REPO repo_path) + if(NOT EXISTS ${dfhack_SOURCE_DIR}/scripts/3rdparty/${repo_path}/CMakeLists.txt) + MESSAGE(FATAL_ERROR "Script submodule scripts/3rdparty/${repo_path} does not exist - run `git submodule update`.") + endif() + add_subdirectory(3rdparty/${repo_path}) +ENDMACRO() From 9a58332aedcb1b1eb655766ca990583e9fa08ef5 Mon Sep 17 00:00:00 2001 From: lethosor Date: Thu, 6 Aug 2015 21:59:33 -0400 Subject: [PATCH 06/19] Update lethosor/dfhack-scripts submodule (whitespace) --- scripts/3rdparty/lethosor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/3rdparty/lethosor b/scripts/3rdparty/lethosor index 213826bd6..ceed207e3 160000 --- a/scripts/3rdparty/lethosor +++ b/scripts/3rdparty/lethosor @@ -1 +1 @@ -Subproject commit 213826bd6feba0b16a582e0c4a74c5678ddc3be8 +Subproject commit ceed207e38220e21067a91b8d6f7b9680a476f69 From a7e1b15db5c4a79679fa680f632aee09405df902 Mon Sep 17 00:00:00 2001 From: warmist Date: Sun, 23 Aug 2015 14:33:14 +0300 Subject: [PATCH 07/19] Add civilization chooser Adds a civilization chooser, also there is race choice dialog and civ choice dialog (with race filtering). --- scripts/gui/gm-unit.lua | 329 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 329 insertions(+) diff --git a/scripts/gui/gm-unit.lua b/scripts/gui/gm-unit.lua index 2aecd98bf..ee021b9df 100644 --- a/scripts/gui/gm-unit.lua +++ b/scripts/gui/gm-unit.lua @@ -135,6 +135,179 @@ 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="",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=":$ID", + name_other="", + 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 "" + 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 "" + 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 ":$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(": ") + 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=": ", + } + } + 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={ @@ -232,6 +405,162 @@ function editor_counters:init( args ) 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 "" +end +function format_wound( list_id,wound, unit) + + local name="" + 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={ From 5e38689a62c780a97ef422ab2121b69dd56d0ab0 Mon Sep 17 00:00:00 2001 From: warmist Date: Sun, 23 Aug 2015 15:03:15 +0300 Subject: [PATCH 08/19] Whitespace removal oh why oh why, my editor does not do this automagically? We are after all in year 2015... --- scripts/gui/gm-unit.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/gui/gm-unit.lua b/scripts/gui/gm-unit.lua index ee021b9df..485552729 100644 --- a/scripts/gui/gm-unit.lua +++ b/scripts/gui/gm-unit.lua @@ -198,7 +198,7 @@ function civ_name(id,format_name,format_no_name,name_other,name_invalid) 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 ":$ID", "%$(%w+)", t) - end + end return string.gsub(format_name or "$NAME ($ENGLISH):$ID", "%$(%w+)", t) end function CivBox:update_choices() @@ -231,7 +231,7 @@ function CivBox:update_race_filter(id) self:update_choices() end function CivBox:choose_race() - showRacePrompt("Choose race","Select new race:",nil,function (id,choice) + showRacePrompt("Choose race","Select new race:",nil,function (id,choice) self:update_race_filter(choice.num) end,nil,nil,true) end @@ -281,7 +281,7 @@ function editor_civ:init( args ) 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 () + 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 @@ -456,8 +456,8 @@ editor_wounds.ATTRS={ --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 + 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("_"," ") From 2fcf751a44ec65c99a6efb67494a2a3a6c1695ae Mon Sep 17 00:00:00 2001 From: lethosor Date: Mon, 14 Sep 2015 16:56:09 -0400 Subject: [PATCH 09/19] Use DFHack fork of lethosor-scripts --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 275772e0e..961850a67 100644 --- a/.gitmodules +++ b/.gitmodules @@ -15,4 +15,4 @@ url = git://github.com/DFHack/clsocket.git [submodule "scripts/3rdparty/lethosor"] path = scripts/3rdparty/lethosor - url = https://github.com/lethosor/dfhack-scripts + url = https://github.com/DFHack/lethosor-scripts From ea74ec713292d034aa3df3d8da65ed504f3b489d Mon Sep 17 00:00:00 2001 From: expwnent Date: Tue, 15 Sep 2015 01:07:31 -0400 Subject: [PATCH 10/19] Made "help" argument to scripts/modtools/create-unit.lua work correctly. --- scripts/modtools/create-unit.lua | 46 +++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/scripts/modtools/create-unit.lua b/scripts/modtools/create-unit.lua index 1187c98b8..5d91287de 100644 --- a/scripts/modtools/create-unit.lua +++ b/scripts/modtools/create-unit.lua @@ -319,7 +319,8 @@ validArgs = --[[validArgs or]]utils.invert({ 'flagSet', 'flagClear', 'name', - 'location' + 'location', + 'age' }) if moduleMode then @@ -328,8 +329,47 @@ end local args = utils.processArgs({...}, validArgs) if args.help then - print('help string TODO') - return + 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 From d4300ab81417fef9cd6d339fc155018e772f22cb Mon Sep 17 00:00:00 2001 From: expwnent Date: Tue, 15 Sep 2015 01:36:29 -0400 Subject: [PATCH 11/19] scripts/modtools/create-unit.lua: now you can set the age (in years) of newly-created units. --- scripts/modtools/create-unit.lua | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/scripts/modtools/create-unit.lua b/scripts/modtools/create-unit.lua index 5d91287de..73bb4acd6 100644 --- a/scripts/modtools/create-unit.lua +++ b/scripts/modtools/create-unit.lua @@ -404,6 +404,14 @@ 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 @@ -424,6 +432,22 @@ if args.domesticate then domesticate(unitId, group_id) end +if age then + --note that if age is 0 then we + 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 = {} From 6730e58bcf95bead2bc107453653251ba821518a Mon Sep 17 00:00:00 2001 From: expwnent Date: Tue, 15 Sep 2015 02:19:09 -0400 Subject: [PATCH 12/19] scripts/modtools/create-unit.lua: fix indentation and add todo list. --- scripts/modtools/create-unit.lua | 305 ++++++++++++++++--------------- 1 file changed, 158 insertions(+), 147 deletions(-) diff --git a/scripts/modtools/create-unit.lua b/scripts/modtools/create-unit.lua index 73bb4acd6..e753ac416 100644 --- a/scripts/modtools/create-unit.lua +++ b/scripts/modtools/create-unit.lua @@ -3,6 +3,16 @@ -- version 0.4 -- 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' @@ -35,7 +45,7 @@ function createUnit(race_id, caste_id) --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') @@ -56,141 +66,142 @@ end -- Picking a caste or gender at random function getRandomCasteId(race_id) - local cr = df.creature_raw.find(race_id) - local caste_id, casteMax + local cr = df.creature_raw.find(race_id) + local caste_id, casteMax - casteMax = #cr.caste - 1 + casteMax = #cr.caste - 1 - if casteMax > 0 then - return math.random(0, casteMax) - end + if casteMax > 0 then + return math.random(0, casteMax) + end - return 0 + 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) + 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 + 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 - -- ... - 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 + 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) + 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) @@ -258,11 +269,11 @@ function nameUnit(id, entityRawName, civ_id) 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 @@ -329,7 +340,7 @@ end local args = utils.processArgs({...}, validArgs) if args.help then - print( + print( [[scripts/modtools/create-unit.lua arguments: -help @@ -344,32 +355,32 @@ arguments: examples: MALE FEMALE - -domesticate - if the unit can't learn or can't speak, then make it a friendly animal + -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 + 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 + 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 + 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 + return end local race From 836fbe86502c52003227bf267d57619c6c891fc6 Mon Sep 17 00:00:00 2001 From: expwnent Date: Tue, 15 Sep 2015 02:20:23 -0400 Subject: [PATCH 13/19] Increment version of create-unit. --- scripts/modtools/create-unit.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/modtools/create-unit.lua b/scripts/modtools/create-unit.lua index e753ac416..4faec4d53 100644 --- a/scripts/modtools/create-unit.lua +++ b/scripts/modtools/create-unit.lua @@ -1,6 +1,6 @@ -- 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.4 +-- version 0.5 -- This is a beta version. Use at your own risk. --[[ From fdca83e17abff71c69b44c52ecaa4b770786855d Mon Sep 17 00:00:00 2001 From: expwnent Date: Tue, 15 Sep 2015 02:32:58 -0400 Subject: [PATCH 14/19] Tweak modtools/create-item (creator = \LAST) --- scripts/modtools/create-item.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/modtools/create-item.lua b/scripts/modtools/create-item.lua index ff5740dbb..b7c88bb45 100644 --- a/scripts/modtools/create-item.lua +++ b/scripts/modtools/create-item.lua @@ -104,10 +104,11 @@ arguments: -help print this help message -creator id - specify the id of the unit who will create the item + specify the id of the unit who will create the item, or \\LAST to indicate the unit with id df.global.unit_next_id-1 examples: 0 2 + \\LAST -material matstring specify the material of the item to be created examples: @@ -126,4 +127,8 @@ arguments: return end +if args.creator == '\\LAST' then + args.creator = tostring(df.global.unit_next_id-1) +end + createItem(tonumber(args.creator), args.item, args.material, args.leftHand, args.rightHand, args.quality) From 5d40b4d2744fa2f8158eb205b8d9fd41809aaee6 Mon Sep 17 00:00:00 2001 From: expwnent Date: Tue, 15 Sep 2015 02:33:11 -0400 Subject: [PATCH 15/19] Update NEWS. --- NEWS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS b/NEWS index 4dd16cada..7a537be30 100644 --- a/NEWS +++ b/NEWS @@ -17,10 +17,13 @@ DFHack Future fix-ster: changes fertility/sterility of animals or dwarves view-item-info: adds information and customisable descriptions to item viewscreens warn-starving: check for starving, thirsty, or very drowsy units and pause with warning if any are found + modtools/create-unit: create new units from nothing + modtools/equip-item: a script to equip items on units New tweaks kitchen-keys: Fixes DF kitchen meal keybindings kitchen-prefs-color: Changes color of enabled items to green in kitchen preferences kitchen-prefs-empty: Fixes a layout issue with empty kitchen tabs + modtools/create-item: arguments are named more clearly, and you can specify the creator to be the unit with id df.global.unit_next_id-1 (useful in conjunction with modtools/create-unit) Fixes Plugins with vmethod hooks can now be reloaded on OS X Lua's os.system() now works on OS X From 301aca7f723670175e4848a3d01be7a3bf858721 Mon Sep 17 00:00:00 2001 From: expwnent Date: Tue, 15 Sep 2015 02:48:28 -0400 Subject: [PATCH 16/19] Update NEWS. --- NEWS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 7a537be30..34e9beb58 100644 --- a/NEWS +++ b/NEWS @@ -23,7 +23,8 @@ DFHack Future kitchen-keys: Fixes DF kitchen meal keybindings kitchen-prefs-color: Changes color of enabled items to green in kitchen preferences kitchen-prefs-empty: Fixes a layout issue with empty kitchen tabs - modtools/create-item: arguments are named more clearly, and you can specify the creator to be the unit with id df.global.unit_next_id-1 (useful in conjunction with modtools/create-unit) + scripts/modtools/create-item: arguments are named more clearly, and you can specify the creator to be the unit with id df.global.unit_next_id-1 (useful in conjunction with modtools/create-unit) + scripts/teleport.lua is now compatible with the script_environment/reqscript system. Fixes Plugins with vmethod hooks can now be reloaded on OS X Lua's os.system() now works on OS X From 8c361b784f548ade90873d7f62647387be673ea2 Mon Sep 17 00:00:00 2001 From: expwnent Date: Tue, 15 Sep 2015 03:09:32 -0400 Subject: [PATCH 17/19] Fix NEWS merge errors. --- NEWS | 47 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index 34e9beb58..f0bb73189 100644 --- a/NEWS +++ b/NEWS @@ -4,22 +4,34 @@ DFHack Future Developer plugins can be ignored on startup by setting the DFHACK_NO_DEV_PLUGINS environment variable The console on Linux and OS X now recognizes keyboard input between prompts JSON libraries available (C++ and Lua) - More build information available in plugins + More DFHack build information used in plugin version checks and available to plugins and lua scripts + Fixed a rare overflow issue that could cause crashes on Linux and OS X + Stopped DF window from receiving input when unfocused on OS X + Fixed issues with keybindings involving Ctrl-A and Ctrl-Z, as well as Alt-E/U/N on OS X + Multiple contexts can now be specified when adding keybindings + Keybindings can now use F10-F12 and 0-9 + Plugin system is no longer restricted to plugins that exist on startup Lua Scripts can be enabled with the built-in enable/disable commands A new function, reqscript(), is available as a safer alternative to script_environment() Lua viewscreens can choose not to intercept the OPTIONS keybinding New internal commands kill-lua: Interrupt running Lua scripts + type: Show where a command is implemented New plugins + confirm: Adds confirmation dialogs for several potentially dangerous actions + fix-unit-occupancy: Fixes issues with unit occupancy, such as faulty "unit blocking tile" messages (bug 3499) + title-version (formerly vshook): Display DFHack version on title screen New scripts burial: sets all unowned coffins to allow burial ("-pets" to allow pets too) fix-ster: changes fertility/sterility of animals or dwarves - view-item-info: adds information and customisable descriptions to item viewscreens + view-item-info: adds information and customisable descriptions to item viewscreens warn-starving: check for starving, thirsty, or very drowsy units and pause with warning if any are found modtools/create-unit: create new units from nothing modtools/equip-item: a script to equip items on units + points: set number of points available at embark screen New tweaks + embark-profile-name: Allows the use of lowercase letters when saving embark profiles kitchen-keys: Fixes DF kitchen meal keybindings kitchen-prefs-color: Changes color of enabled items to green in kitchen preferences kitchen-prefs-empty: Fixes a layout issue with empty kitchen tabs @@ -31,16 +43,25 @@ DFHack Future Fixed default arguments in Lua gametype detection functions Circular lua dependencies (reqscript/script_environment) fixed Prevented crash in Items::createItem() - gui/hack-wish now properly assigns quality to items. + buildingplan: Now supports hatch covers + gui/create-item: + - fixed assigning quality to items + - made "esc" work properly gui/gm-editor handles lua tables properly + help: now recognizes built-in commands, like "help" + manipulator: fixed crash when selecting custom professions when none are found + remotefortressreader: fixed crash when attempting to send map info when no map was loaded search: fixed crash in unit list after cancelling a job + stockpiles: now checks/sanitizes filenames when saving + stocks: fixed a crash when right-clicking steam-engine: - fixed a crash on arena load - number keys (e.g. 2/8) take priority over cursor keys when applicable tweak fps-min fixed + tweak farm-plot-select: Stopped controls from appearing when plots weren't fully built workflow: Fixed some issues with stuck jobs - Note: Existing stuck jobs must be cancelled and re-added - zone: Fixed a crash in the unit list after cancelling a job (and several other potential crashes) + zone: Fixed a crash when using "zone set" (and a few other potential crashes) Misc Improvements autolabor: - Stopped modification of labors that shouldn't be modified for brokers/diplomats @@ -50,22 +71,36 @@ DFHack Future - widgets' positions, formats, etc. are now customizable (see Readme) - weather display now separated from the date display - New mouse cursor widget + dfstatus: Can enable/disable individual categories and customize metal bar list full-heal: "-r" option removes corpses - gui/gm-editor: Pointers can now be displaced + gui/gm-editor + - Pointers can now be displaced + - Added some useful aliases: "item" for the selected item, "screen" for the current screen, etc. + - Now avoids errors with unrecognized types gui/hack-wish: renamed to gui/create-item "keybinding list" accepts a context + lever: + - Lists lever names + - "lever pull" can be used to pull the currently-selected lever memview: Fixed display issue nyan: Can now be stopped with dfhack-run + plug: + - lists all plugins + - shows state and number of commands in plugins quicksave: Restricted to fortress mode remotefortressreader: Exposes more information search: - Supports noble suggestion screen (e.g. suggesting a baron) - Supports fortress mode loo[k] menu - Recognizes ? and ; keys + stocks: can now match beginning and end of item names teleport: Fixed cursor recognition - tweak: debug output now logged to stderr.log instead of console + tweak: + - debug output now logged to stderr.log instead of console - makes DFHack start faster + - farm-plot-select: Fixed issues with selecting undiscovered crops workflow: Improved handling of plant reactions Removed + embark-tools nano: 1x1 embarks are now possible in vanilla 0.40.24 DFHack 0.40.24-r3 Internals From ab749ab0c8ad6355132507717a73d081ebeabe73 Mon Sep 17 00:00:00 2001 From: expwnent Date: Tue, 15 Sep 2015 03:17:57 -0400 Subject: [PATCH 18/19] Tweak create-unit and remove trailing whitespace. --- scripts/modtools/create-unit.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/modtools/create-unit.lua b/scripts/modtools/create-unit.lua index 4faec4d53..948b94486 100644 --- a/scripts/modtools/create-unit.lua +++ b/scripts/modtools/create-unit.lua @@ -443,8 +443,7 @@ if args.domesticate then domesticate(unitId, group_id) end -if age then - --note that if age is 0 then we +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 From f9fce7e6a141fd5bb487e169579e951f26d3b6e4 Mon Sep 17 00:00:00 2001 From: expwnent Date: Tue, 15 Sep 2015 03:21:09 -0400 Subject: [PATCH 19/19] Convert tab to spaces in RemoteFortressReader.proto. --- plugins/proto/RemoteFortressReader.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/proto/RemoteFortressReader.proto b/plugins/proto/RemoteFortressReader.proto index c0d7104a7..1344bb694 100644 --- a/plugins/proto/RemoteFortressReader.proto +++ b/plugins/proto/RemoteFortressReader.proto @@ -182,7 +182,7 @@ message UnitDefinition optional uint32 flags1 = 8; optional uint32 flags2 = 9; optional uint32 flags3 = 10; - optional bool is_soldier = 11; + optional bool is_soldier = 11; } message UnitList