From f41adf2846f9b75229437c99d0f983732e7283c4 Mon Sep 17 00:00:00 2001 From: Petter Jennison Date: Mon, 23 Oct 2023 09:46:00 +0100 Subject: [PATCH 1/6] script for a gui pregnancy tool --- gui/pregnancy.lua | 328 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 328 insertions(+) create mode 100644 gui/pregnancy.lua diff --git a/gui/pregnancy.lua b/gui/pregnancy.lua new file mode 100644 index 0000000000..f7a765a5fa --- /dev/null +++ b/gui/pregnancy.lua @@ -0,0 +1,328 @@ +local gui = require('gui') +local widgets = require('gui.widgets') + +PregnancyGui = defclass(PregnancyGui, widgets.Window) +PregnancyGui.ATTRS { + frame_title='My Window', + frame={w=50, h=45}, + resizable=true, -- if resizing makes sense for your dialog + resize_min={w=50, h=20}, -- try to allow users to shrink your windows +} + +function PregnancyGui:init() + self.mother = false + self.father = false + self.father_historical = false + self.msg = {} + -- self.success = false + self:addviews{ + widgets.ResizingPanel{ + frame={t=0}, + frame_style=gui.FRAME_INTERIOR, + autoarrange_subviews=true, + subviews={ + widgets.WrappedLabel{ + text_to_wrap=self:callback('getMotherLabel') + }, + widgets.HotkeyLabel{ + frame={l=0}, + label="Select Mother", + key='CUSTOM_SHIFT_M', + on_activate=self:callback('selectmother'), + }, + }, + }, + widgets.ResizingPanel{ + frame={t=6}, + frame_style=gui.FRAME_INTERIOR, + autoarrange_subviews=true, + subviews={ + widgets.WrappedLabel{ + text_to_wrap=self:callback('getFatherLabel') + }, + widgets.HotkeyLabel{ + frame={l=0}, + label="Select Father", + key='CUSTOM_SHIFT_F', + on_activate=self:callback('selectfather'), + }, + widgets.HotkeyLabel{ + frame={l=5}, + label="Set Mother's spouse as the Father", + key='CUSTOM_F', + on_activate=self:callback('spouseFather'), + disabled=function() return not self.mother or self.mother.relationship_ids.Spouse == -1 end + }, + }, + }, + widgets.ResizingPanel{ + frame={t=12}, + frame_style=gui.FRAME_INTERIOR, + autoarrange_subviews=1, + subviews={ + widgets.HotkeyLabel{ + frame={l=0}, + key='CUSTOM_SHIFT_P', + label="Create pregnancy", + on_activate=self:callback('CreatePregnancy'), + enabled=function() return self.mother or self.father and self.father_historical end + }, + widgets.TooltipLabel{ + text_to_wrap=self.msg, + show_tooltip=true + }, + + widgets.ToggleHotkeyLabel{ + view_id='Force', + label='Force', + options={{label='On', value=true, pen=COLOR_GREEN}, + {label='Off', value=false, pen=COLOR_RED}}, + initial_option=false + }, + }, + }, + widgets.ResizingPanel{ + frame={t=22}, + frame_style=gui.FRAME_INTERIOR, + autoarrange_subviews=true, + subviews={ + widgets.HotkeyLabel{ + frame={l=1, b=0}, + key='LEAVESCREEN', + label="Return to game", + on_activate=function() + repeat until not self:onInput{LEAVESCREEN=true} + view:dismiss() + end, + }, + }, + }, + } +end + +function PregnancyGui:selectmother() + local unit = dfhack.gui.getSelectedUnit() + if unit then + if unit.sex==0 and dfhack.units.isAdult(unit) then + self.mother = unit + self:updateLayout() + end + end +end + +function PregnancyGui:selectfather() + local unit = dfhack.gui.getSelectedUnit() + if unit and dfhack.units.isAdult(unit) then + self.father = unit + self.father_historical = false + self:updateLayout() + end +end + +function PregnancyGui:spouseFather() + local father = self:findSpouse(self.mother)[3] + if father then + if df.unit.find(father.unit_id) then + self.father = df.unit.find(father.unit_id) + self.father_historical = false + else + self.father_historical = father + self.father = false + end + self:updateLayout() + end +end + +function PregnancyGui:getMotherLabel() + if self.mother then + local motherName = dfhack.TranslateName(self.mother.name) + if self.mother.relationship_ids.Spouse > -1 then + local spouseInfo = self:findSpouse(self.mother) + return ('Selected mother: %s.%sShe is married to %s (%s).'):format( + self:findName(self.mother), + NEWLINE, + spouseInfo[1], + spouseInfo[2] + ) + else + return ('Selected mother: %s.%sShe is unmarried.'):format( + self:findName(self.mother), + NEWLINE + ) + end + else return ('No mother selected - Must be a adult female') + end +end + +function PregnancyGui:getFatherLabel() + if self.father or self.father_historical then + if self.father_historical or self.father.relationship_ids.Spouse > -1 then + local father = self.father or self.father_historical + local spouseInfo = self:findSpouse(father) + return ('Selected father: %s.%s%s is married to %s (%s).'):format( + self:findName(father), + NEWLINE, + df.pronoun_type[father.sex]:gsub("^%l", string.upper), + spouseInfo[1], + spouseInfo[2] + ) + else + return ('Selected father: %s.%s%s is unmarried.'):format( + self:findName(self.father), + NEWLINE, + df.pronoun_type[self.father.sex]:gsub("^%l", string.upper) + ) + end + else return ('No father selected') + end +end + +function PregnancyGui:findName(unit) + if unit.name.has_name then + return dfhack.TranslateName(unit.name) + elseif unit.name.nickname ~= "" then + return unit.name.nickname + else return ('Unnamed %s. (Unit id:%s)'):format( + string.upper(df.global.world.raws.creatures.all[unit.race].name[0]), + unit.id + ) + end +end + +function PregnancyGui:findSpouse(unit) + local historical_spouse, spouse_loc, spouse, spouseid + local culled = false + + --setting variables for if mother or father are local, followed by finding the father's spouse if he is not local + if self.father == unit or self.mother == unit then + spouseid = unit.relationship_ids.Spouse + spouse = df.unit.find(spouseid) + elseif self.father_historical == unit then + for index, relation in pairs(unit.histfig_links) do + if relation._type == df.histfig_hf_link_spousest then + historical_spouse=df.historical_figure.find(relation.target_hf) + if not historical_spouse then culled = true --there was an id, but there wasn't a histfig with that id (due culling) + elseif df.global.plotinfo.site_id==historical_spouse.info.whereabouts.site then + spouse_loc = 'local' + else spouse_loc = 'offsite' + end + end + end + return {dfhack.TranslateName(historical_spouse.name),spouse_loc,historical_spouse} + end + + --if the spouse is local this should identify them: + if spouse then + historical_spouse = df.historical_figure.find(spouse.hist_figure_id) or false + spouse_loc = 'local' + end + + --if spouse is not local (offsite): + if spouseid > -1 and not spouse then --spouse exists but isnt on the map, so search historical units: + local historical_unit = df.historical_figure.find(unit.hist_figure_id) + for index, relation in pairs(historical_unit.histfig_links) do + if relation._type == df.histfig_hf_link_spousest then + historical_spouse=df.historical_figure.find(relation.target_hf) + if not historical_spouse then culled = true --there was an id, but there wasn't a histfig with that id (due culling) + elseif df.global.plotinfo.site_id==historical_spouse.info.whereabouts.site then--i dont think this should ever be true + spouse_loc = 'local' + else spouse_loc = 'offsite' + end + end + end + end + if culled then + return {'Unknown','culled'} + else + return {dfhack.TranslateName(historical_spouse.name),spouse_loc,historical_spouse} + end +end + +function PregnancyGui:CreatePregnancy() + local genes,father_id,father_caste,father_name + local bypass = true + local force = self.subviews.Force:getOptionValue() + + local count = #self.msg + for i=0, count do self.msg[i]=nil end --empty self.msg + + if self.father then + genes=self.father.appearance.genes:new() + father_id=self.father.hist_figure_id + father_caste=self.father.caste + father_name=self:findName(self.father) + else + genes=self.mother.appearance.genes:new()--i dont think historical figures have genes + father_id=self.father_historical.id + father_caste=self.father_historical.caste + father_name=self:findName(self.father_historical) + end + + if self.mother.pregnancy_timer > 0 then + local og_father = df.historical_figure.find(self.mother.pregnancy_spouse) + bypass = false + if force and og_father then + table.insert(self.msg, ('SUCCESS:%sMother:%s%sFather:%s%sPrevious pregnancy with %s aborted'):format( + NEWLINE, + self:findName(self.mother), + NEWLINE, + father_name, + NEWLINE, + dfhack.TranslateName(og_father.name) + )) + elseif force then + table.insert(self.msg, ('SUCCESS:%sMother:%s%sFather:%s%sPrevious pregnancy aborted'):format( + NEWLINE, + self:findName(self.mother), + NEWLINE, + father_name, + NEWLINE + )) + elseif og_father then + table.insert(self.msg, ('FAILED:%s%s already pregnant with %s%s'):format( + NEWLINE, + self:findName(self.mother), + dfhack.TranslateName(og_father.name), + force + )) + else + table.insert(self.msg, ('FAILED:%s%s is already pregnant, no father is recorded'):format( + NEWLINE, + self:findName(self.mother) + )) + end + end + -- self.success = false + if bypass or force then + --TODO add GUI to select the number of months for pregnancy timer + self.mother.pregnancy_timer=math.random(1, 13000) + self.mother.pregnancy_caste=father_caste + self.mother.pregnancy_spouse=father_id + self.mother.pregnancy_genes=genes + -- self.success = true + if not force then + table.insert(self.msg, ('SUCCESS:%sMother:%s%sFather:%s'):format( + NEWLINE, + self:findName(self.mother), + NEWLINE, + father_name + )) + end + end + self:updateLayout() +end + +PregnancyScreen = defclass(PregnancyScreen, gui.ZScreen) +PregnancyScreen.ATTRS { + focus_path='PregnancyScreen', +} + +function PregnancyScreen:init() + self:addviews{PregnancyGui{}} +end + +function PregnancyScreen:onDismiss() + view = nil +end + +view = view and view:raise() or PregnancyScreen{}:show() \ No newline at end of file From 15e675214e1376319ed3469c0b0d0791d0a94f18 Mon Sep 17 00:00:00 2001 From: Petter Jennison Date: Sat, 28 Oct 2023 15:20:33 +0100 Subject: [PATCH 2/6] Implementing myk002's suggestions --- gui/pregnancy.lua | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/gui/pregnancy.lua b/gui/pregnancy.lua index f7a765a5fa..20c64ac563 100644 --- a/gui/pregnancy.lua +++ b/gui/pregnancy.lua @@ -3,7 +3,7 @@ local widgets = require('gui.widgets') PregnancyGui = defclass(PregnancyGui, widgets.Window) PregnancyGui.ATTRS { - frame_title='My Window', + frame_title='Pregnancy manager', frame={w=50, h=45}, resizable=true, -- if resizing makes sense for your dialog resize_min={w=50, h=20}, -- try to allow users to shrink your windows @@ -14,7 +14,6 @@ function PregnancyGui:init() self.father = false self.father_historical = false self.msg = {} - -- self.success = false self:addviews{ widgets.ResizingPanel{ frame={t=0}, @@ -178,10 +177,9 @@ function PregnancyGui:getFatherLabel() end function PregnancyGui:findName(unit) - if unit.name.has_name then - return dfhack.TranslateName(unit.name) - elseif unit.name.nickname ~= "" then - return unit.name.nickname + local name = dfhack.TranslateName(unit.name) + if name ~= "" then + return name else return ('Unnamed %s. (Unit id:%s)'):format( string.upper(df.global.world.raws.creatures.all[unit.race].name[0]), unit.id @@ -262,7 +260,7 @@ function PregnancyGui:CreatePregnancy() local og_father = df.historical_figure.find(self.mother.pregnancy_spouse) bypass = false if force and og_father then - table.insert(self.msg, ('SUCCESS:%sMother:%s%sFather:%s%sPrevious pregnancy with %s aborted'):format( + table.insert(self.msg, ('SUCCESS:%sMother:%s%sFather:%s%sPrevious pregnancy with %s replaced'):format( NEWLINE, self:findName(self.mother), NEWLINE, @@ -314,7 +312,7 @@ end PregnancyScreen = defclass(PregnancyScreen, gui.ZScreen) PregnancyScreen.ATTRS { - focus_path='PregnancyScreen', + focus_path='pregnancy', } function PregnancyScreen:init() From d0d7106945d0f58bbe22f96c2b069ebbf7e7888f Mon Sep 17 00:00:00 2001 From: Petter Jennison Date: Sat, 28 Oct 2023 19:05:05 +0100 Subject: [PATCH 3/6] added Rangeslider widget for pregnancy term --- gui/pregnancy.lua | 81 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 67 insertions(+), 14 deletions(-) diff --git a/gui/pregnancy.lua b/gui/pregnancy.lua index 20c64ac563..6590f5e85a 100644 --- a/gui/pregnancy.lua +++ b/gui/pregnancy.lua @@ -4,7 +4,7 @@ local widgets = require('gui.widgets') PregnancyGui = defclass(PregnancyGui, widgets.Window) PregnancyGui.ATTRS { frame_title='Pregnancy manager', - frame={w=50, h=45}, + frame={w=64, h=35}, resizable=true, -- if resizing makes sense for your dialog resize_min={w=50, h=20}, -- try to allow users to shrink your windows } @@ -14,6 +14,18 @@ function PregnancyGui:init() self.father = false self.father_historical = false self.msg = {} + + local term_options = {} + local term_index = {} + local months + for months=0,10 do + -- table.insert(term_options,{label=('%s months'):format(months),value=months}) --I tried this to add labels, probably doing something wrong, it broke the range widget + table.insert(term_options,months) --this works though + end + for k,v in ipairs(term_options) do + term_index[v] = k + end + self:addviews{ widgets.ResizingPanel{ frame={t=0}, @@ -32,7 +44,7 @@ function PregnancyGui:init() }, }, widgets.ResizingPanel{ - frame={t=6}, + frame={t=5}, frame_style=gui.FRAME_INTERIOR, autoarrange_subviews=true, subviews={ @@ -54,34 +66,69 @@ function PregnancyGui:init() }, }, }, - widgets.ResizingPanel{ - frame={t=12}, + widgets.Panel{ + frame={t=12,h=14}, frame_style=gui.FRAME_INTERIOR, - autoarrange_subviews=1, subviews={ widgets.HotkeyLabel{ - frame={l=0}, + frame={l=0, t=0}, key='CUSTOM_SHIFT_P', label="Create pregnancy", on_activate=self:callback('CreatePregnancy'), enabled=function() return self.mother or self.father and self.father_historical end }, - widgets.TooltipLabel{ - text_to_wrap=self.msg, - show_tooltip=true - }, - widgets.ToggleHotkeyLabel{ + frame={l=1, t=1}, view_id='Force', label='Force', options={{label='On', value=true, pen=COLOR_GREEN}, {label='Off', value=false, pen=COLOR_RED}}, initial_option=false }, + widgets.TooltipLabel{ + frame={l=0, t=3}, + text_to_wrap='Pregnancy term range (months):', + show_tooltip=true, + text_pen=COLOR_WHITE + }, + widgets.CycleHotkeyLabel{ + view_id='min_term', + frame={l=0, t=6, w=SLIDER_LABEL_WIDTH}, + label='Min pregnancy term:', + key_back='CUSTOM_SHIFT_Z', + key='CUSTOM_SHIFT_X', + options=term_options, + initial_option=7 + }, + widgets.CycleHotkeyLabel{ + view_id='max_term', + frame={l=30, t=6, w=SLIDER_LABEL_WIDTH}, + label='Max pregnancy term:', + key_back='CUSTOM_SHIFT_Q', + key='CUSTOM_SHIFT_W', + options=term_options, + initial_option=9 + }, + widgets.RangeSlider{ + frame={l=0, t=4}, + num_stops=#term_options, + get_left_idx_fn=function() + return term_index[self.subviews.min_term:getOptionLabel()] + end, + get_right_idx_fn=function() + return term_index[self.subviews.max_term:getOptionLabel()] + end, + on_left_change=function(idx) self.subviews.min_term:setOption(idx, true) end, + on_right_change=function(idx) self.subviews.max_term:setOption(idx, true) end, + }, + widgets.WrappedLabel{ + frame={t=8},--, h=5}, + text_to_wrap=self.msg + }, }, }, widgets.ResizingPanel{ - frame={t=22}, + frame={t=26}, frame_style=gui.FRAME_INTERIOR, autoarrange_subviews=true, subviews={ @@ -237,13 +284,19 @@ function PregnancyGui:findSpouse(unit) end function PregnancyGui:CreatePregnancy() - local genes,father_id,father_caste,father_name + local genes,father_id,father_caste,father_name local bypass = true local force = self.subviews.Force:getOptionValue() local count = #self.msg for i=0, count do self.msg[i]=nil end --empty self.msg + if self.subviews.min_term:getOptionLabel() > self.subviews.max_term:getOptionLabel() then + table.insert(self.msg,('Min term has to be less then max term')) + self:updateLayout() + return + end + if self.father then genes=self.father.appearance.genes:new() father_id=self.father.hist_figure_id @@ -293,7 +346,7 @@ function PregnancyGui:CreatePregnancy() -- self.success = false if bypass or force then --TODO add GUI to select the number of months for pregnancy timer - self.mother.pregnancy_timer=math.random(1, 13000) + self.mother.pregnancy_timer=math.random(self.subviews.min_term:getOptionLabel()*33600+1, self.subviews.max_term:getOptionLabel()*33600+1) self.mother.pregnancy_caste=father_caste self.mother.pregnancy_spouse=father_id self.mother.pregnancy_genes=genes From 523c58558064d3291be2e93369be2632c4349040 Mon Sep 17 00:00:00 2001 From: Petter Jennison Date: Sat, 28 Oct 2023 19:08:04 +0100 Subject: [PATCH 4/6] Apply suggestions from code review Co-authored-by: Myk --- gui/pregnancy.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gui/pregnancy.lua b/gui/pregnancy.lua index 6590f5e85a..b3fc94eebb 100644 --- a/gui/pregnancy.lua +++ b/gui/pregnancy.lua @@ -147,7 +147,7 @@ function PregnancyGui:init() end function PregnancyGui:selectmother() - local unit = dfhack.gui.getSelectedUnit() + local unit = dfhack.gui.getSelectedUnit(true) if unit then if unit.sex==0 and dfhack.units.isAdult(unit) then self.mother = unit @@ -157,7 +157,7 @@ function PregnancyGui:selectmother() end function PregnancyGui:selectfather() - local unit = dfhack.gui.getSelectedUnit() + local unit = dfhack.gui.getSelectedUnit(true) if unit and dfhack.units.isAdult(unit) then self.father = unit self.father_historical = false @@ -175,7 +175,7 @@ function PregnancyGui:spouseFather() self.father_historical = father self.father = false end - self:updateLayout() + self:updateLayout() end end @@ -196,7 +196,7 @@ function PregnancyGui:getMotherLabel() NEWLINE ) end - else return ('No mother selected - Must be a adult female') + else return ('No mother selected - Must be an adult female') end end From fb43a1e2629f746677bc9fa326004ed53497694c Mon Sep 17 00:00:00 2001 From: Petter Jennison Date: Sat, 28 Oct 2023 19:44:37 +0100 Subject: [PATCH 5/6] More myk002 suggestions/corrections --- gui/pregnancy.lua | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/gui/pregnancy.lua b/gui/pregnancy.lua index b3fc94eebb..7c6739861f 100644 --- a/gui/pregnancy.lua +++ b/gui/pregnancy.lua @@ -10,7 +10,10 @@ PregnancyGui.ATTRS { } function PregnancyGui:init() - self.mother = false + if dfhack.gui.getSelectedUnit(true).sex == df.pronoun_type.she then + self.mother = dfhack.gui.getSelectedUnit(true) + else self.mother = false + end self.father = false self.father_historical = false self.msg = {} @@ -37,7 +40,7 @@ function PregnancyGui:init() }, widgets.HotkeyLabel{ frame={l=0}, - label="Select Mother", + label="Set mother to selected unit", key='CUSTOM_SHIFT_M', on_activate=self:callback('selectmother'), }, @@ -53,13 +56,13 @@ function PregnancyGui:init() }, widgets.HotkeyLabel{ frame={l=0}, - label="Select Father", + label="Set father to selected unit", key='CUSTOM_SHIFT_F', on_activate=self:callback('selectfather'), }, widgets.HotkeyLabel{ frame={l=5}, - label="Set Mother's spouse as the Father", + label="Set mother's spouse as the father", key='CUSTOM_F', on_activate=self:callback('spouseFather'), disabled=function() return not self.mother or self.mother.relationship_ids.Spouse == -1 end @@ -80,7 +83,7 @@ function PregnancyGui:init() widgets.ToggleHotkeyLabel{ frame={l=1, t=1}, view_id='Force', - label='Force', + label='Replace existing pregnancy', options={{label='On', value=true, pen=COLOR_GREEN}, {label='Off', value=false, pen=COLOR_RED}}, initial_option=false @@ -149,7 +152,7 @@ end function PregnancyGui:selectmother() local unit = dfhack.gui.getSelectedUnit(true) if unit then - if unit.sex==0 and dfhack.units.isAdult(unit) then + if unit.sex==df.pronoun_type.she and dfhack.units.isAdult(unit) then self.mother = unit self:updateLayout() end @@ -175,7 +178,7 @@ function PregnancyGui:spouseFather() self.father_historical = father self.father = false end - self:updateLayout() + self:updateLayout() end end @@ -343,14 +346,12 @@ function PregnancyGui:CreatePregnancy() )) end end - -- self.success = false + if bypass or force then - --TODO add GUI to select the number of months for pregnancy timer self.mother.pregnancy_timer=math.random(self.subviews.min_term:getOptionLabel()*33600+1, self.subviews.max_term:getOptionLabel()*33600+1) self.mother.pregnancy_caste=father_caste self.mother.pregnancy_spouse=father_id self.mother.pregnancy_genes=genes - -- self.success = true if not force then table.insert(self.msg, ('SUCCESS:%sMother:%s%sFather:%s'):format( NEWLINE, From 04d2b664e07518ad36e5ac1acb0331e72c2dce0a Mon Sep 17 00:00:00 2001 From: Petter Jennison Date: Thu, 2 Nov 2023 09:17:52 +0000 Subject: [PATCH 6/6] myk002 newline+msg changes pregnancy.lua added Newline and self.msg changes to pregnancy.lua --- gui/pregnancy.lua | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/gui/pregnancy.lua b/gui/pregnancy.lua index 7c6739861f..e759dfa118 100644 --- a/gui/pregnancy.lua +++ b/gui/pregnancy.lua @@ -126,7 +126,7 @@ function PregnancyGui:init() }, widgets.WrappedLabel{ frame={t=8},--, h=5}, - text_to_wrap=self.msg + text_to_wrap=function() return self.msg end }, }, }, @@ -291,8 +291,7 @@ function PregnancyGui:CreatePregnancy() local bypass = true local force = self.subviews.Force:getOptionValue() - local count = #self.msg - for i=0, count do self.msg[i]=nil end --empty self.msg + self.msg = {} if self.subviews.min_term:getOptionLabel() > self.subviews.max_term:getOptionLabel() then table.insert(self.msg,('Min term has to be less then max term')) @@ -377,4 +376,4 @@ function PregnancyScreen:onDismiss() view = nil end -view = view and view:raise() or PregnancyScreen{}:show() \ No newline at end of file +view = view and view:raise() or PregnancyScreen{}:show()