diff --git a/docs/images/family-affairs.png b/docs/images/family-affairs.png new file mode 100644 index 000000000..faf59390e Binary files /dev/null and b/docs/images/family-affairs.png differ diff --git a/scripts/gui/family-affairs.lua b/scripts/gui/family-affairs.lua new file mode 100644 index 000000000..51525f36e --- /dev/null +++ b/scripts/gui/family-affairs.lua @@ -0,0 +1,296 @@ +-- gui/family-affairs +-- derived from v1.2 @ http://www.bay12forums.com/smf/index.php?topic=147779 +local help = [[=begin + +gui/family-affairs +================== +A user-friendly interface to view romantic relationships, +with the ability to add, remove, or otherwise change them at +your whim - fantastic for depressed dwarves with a dead spouse +(or matchmaking players...). + +The target/s must be alive, sane, and in fortress mode. + +.. image:: /docs/images/family-affairs.png + :align: center + +``gui/family-affairs [unitID]`` + shows GUI for the selected unit, or the specified unit ID + +``gui/family-affairs divorce [unitID]`` + removes all spouse and lover information from the unit + and it's partner, bypassing almost all checks. + +``gui/family-affairs [unitID] [unitID]`` + divorces the two specificed units and their partners, + then arranges for the two units to marry, bypassing + almost all checks. Use with caution. + +=end]] + +local dlg = require ('gui.dialogs') + +function ErrorPopup (msg,color) + if not tostring(msg) then msg = "Error" end + if not color then color = COLOR_LIGHTRED end + dlg.showMessage("Dwarven Family Affairs", msg, color, nil) +end + +function AnnounceAndGamelog (text,l) + if not l then l = true end + dfhack.gui.showAnnouncement(text, _G["COLOR_LIGHTMAGENTA"]) + if l then + local log = io.open('gamelog.txt', 'a') + log:write(text.."\n") + log:close() + end +end + +function ListPrompt (msg, choicelist, bool, yes_func) +dlg.showListPrompt( + "Dwarven Family Affairs", + msg, + COLOR_WHITE, + choicelist, + --called if choice is yes + yes_func, + --called on cancel + function() end, + 15, + bool + ) +end + +function GetMarriageSummary (source) + local familystate = "" + + if source.relations.spouse_id ~= -1 then + if dfhack.units.isSane(df.unit.find(source.relations.spouse_id)) then + familystate = dfhack.TranslateName(source.name).." has a spouse ("..dfhack.TranslateName(df.unit.find(source.relations.spouse_id).name)..")" + end + if dfhack.units.isSane(df.unit.find(source.relations.spouse_id)) == false then + familystate = dfhack.TranslateName(source.name).."'s spouse is dead or not sane, would you like to choose a new one?" + end + end + + if source.relations.spouse_id == -1 and source.relations.lover_id ~= -1 then + if dfhack.units.isSane(df.unit.find(source.relations.lover_id)) then + familystate = dfhack.TranslateName(source.name).." already has a lover ("..dfhack.TranslateName(df.unit.find(source.relations.spouse_id).name)..")" + end + if dfhack.units.isSane(df.unit.find(source.relations.lover_id)) == false then + familystate = dfhack.TranslateName(source.name).."'s lover is dead or not sane, would you like that love forgotten?" + end + end + + if source.relations.spouse_id == -1 and source.relations.lover_id == -1 then + familystate = dfhack.TranslateName(source.name).." is not involved in romantic relationships with anyone" + end + + if source.relations.pregnancy_timer > 0 then + familystate = familystate.."\nShe is pregnant." + local father = df.historical_figure.find(source.relations.pregnancy_spouse) + if father then + familystate = familystate.." The father is "..dfhack.TranslateName(father.name).."." + end + end + + return familystate +end + +function GetSpouseData (source) + local spouse = df.unit.find(source.relations.spouse_id) + local spouse_hf + if spouse then + spouse_hf = df.historical_figure.find (spouse.hist_figure_id) + end + return spouse,spouse_hf +end + +function GetLoverData (source) + local lover = df.unit.find(source.relations.spouse_id) + local lover_hf + if lover then + lover_hf = df.historical_figure.find (lover.hist_figure_id) + end + return lover,lover_hf +end + +function EraseHFLinksLoverSpouse (hf) + for i = #hf.histfig_links-1,0,-1 do + if hf.histfig_links[i]._type == df.histfig_hf_link_spousest or hf.histfig_links[i]._type == df.histfig_hf_link_loverst then + local todelete = hf.histfig_links[i] + hf.histfig_links:erase(i) + todelete:delete() + end + end +end + +function Divorce (source) + local source_hf = df.historical_figure.find(source.hist_figure_id) + local spouse,spouse_hf = GetSpouseData (source) + local lover,lover_hf = GetLoverData (source) + + source.relations.spouse_id = -1 + source.relations.lover_id = -1 + + if source_hf then + EraseHFLinksLoverSpouse (source_hf) + end + if spouse then + spouse.relations.spouse_id = -1 + spouse.relations.lover_id = -1 + end + if lover then + spouse.relations.spouse_id = -1 + spouse.relations.lover_id = -1 + end + if spouse_hf then + EraseHFLinksLoverSpouse (spouse_hf) + end + if lover_hf then + EraseHFLinksLoverSpouse (lover_hf) + end + + local partner = spouse or lover + if not partner then + AnnounceAndGamelog(dfhack.TranslateName(source.name).." is now single") + else + AnnounceAndGamelog(dfhack.TranslateName(source.name).." and "..dfhack.TranslateName(partner.name).." are now single") + end +end + +function Marriage (source,target) + local source_hf = df.historical_figure.find(source.hist_figure_id) + local target_hf = df.historical_figure.find(target.hist_figure_id) + source.relations.spouse_id = target.id + target.relations.spouse_id = source.id + + local new_link = df.histfig_hf_link_spousest:new() -- adding hf link to source + new_link.target_hf = target_hf.id + new_link.link_strength = 100 + source_hf.histfig_links:insert('#',new_link) + + new_link = df.histfig_hf_link_spousest:new() -- adding hf link to target + new_link.target_hf = source_hf.id + new_link.link_strength = 100 + target_hf.histfig_links:insert('#',new_link) +end + +function ChooseNewSpouse (source) + + if not source then + qerror("no unit") return + end + if (source.profession == 103 or source.profession == 104) then + ErrorPopup("target is too young") return + end + if not (source.relations.spouse_id == -1 and source.relations.lover_id == -1) then + ErrorPopup("target already has a spouse or a lover") + qerror("source already has a spouse or a lover") + return + end + + local choicelist = {} + targetlist = {} + + for k,v in pairs (df.global.world.units.active) do + if dfhack.units.isCitizen(v) + and v.race == source.race + and v.sex ~= source.sex + and v.relations.spouse_id == -1 + and v.relations.lover_id == -1 + and not (v.profession == 103 or v.profession == 104) + then + table.insert(choicelist,dfhack.TranslateName(v.name)..', '..dfhack.units.getProfessionName(v)) + table.insert(targetlist,v) + end + end + + if #choicelist > 0 then + ListPrompt( + "Assign new spouse for "..dfhack.TranslateName(source.name), + choicelist, + true, + function(a,b) + local target = targetlist[a] + Marriage (source,target) + AnnounceAndGamelog(dfhack.TranslateName(source.name).." and "..dfhack.TranslateName(target.name).." have married!") + end) + else + ErrorPopup("No suitable candidates") + end +end + +function MainDialog (source) + + local familystate = GetMarriageSummary(source) + + familystate = familystate.."\nSelect action:" + local choicelist = {} + local on_select = {} + + local child = (source.profession == 103 or source.profession == 104) + local is_single = source.relations.spouse_id == -1 and source.relations.lover_id == -1 + local ready_for_marriage = single and not child + + if not child then + table.insert(choicelist,"Remove romantic relationships (if any)") + table.insert(on_select, Divorce) + if ready_for_marriage then + table.insert(choicelist,"Assign a new spouse") + table.insert(on_select,ChooseNewSpouse) + end + if not ready_for_marriage then + table.insert(choicelist,"[Assign a new spouse]") + table.insert(on_select,function () ErrorPopup ("Existing relationships must be removed if you wish to assign a new spouse.") end) + end + else + table.insert(choicelist,"Leave this child alone") + table.insert(on_select,nil) + end + + ListPrompt(familystate, choicelist, false, + function(a,b) if on_select[a] then on_select[a](source) end end) +end + + +local args = {...} + +if args[1] == "help" or args[1] == "?" then print(helpstr) return end + +if not df.global.gamemode == 0 then + print (helpstr) qerror ("invalid gamemode") return +end + +if args[1] == "divorce" and tonumber(args[2]) then + local unit = df.unit.find(args[2]) + if unit then Divorce (unit) return end +end + +if tonumber(args[1]) and tonumber(args[2]) then + local unit1 = df.unit.find(args[1]) + local unit2 = df.unit.find(args[2]) + if unit1 and unit2 then + Divorce (unit1) + Divorce (unit2) + Marriage (unit1,unit2) + return + end +end + +local selected = dfhack.gui.getSelectedUnit(true) +if tonumber(args[1]) then + selected = df.unit.find(tonumber(args[1])) or selected +end + +if selected then + if dfhack.units.isCitizen(selected) and dfhack.units.isSane(selected) then + MainDialog(selected) + else + qerror("You must select sane fortress citizen.") + return + end +else + print (helpstr) + qerror("select a sane fortress dwarf") +end