Merge pull request #730 from PeridexisErrant/add-scripts
Add updated emigration script, gui/family-affairsdevelop
						commit
						2f8f5b071a
					
				
											
												Binary file not shown.
											
										
									
								| 
		 After Width: | Height: | Size: 3.9 KiB  | 
@ -0,0 +1,130 @@
 | 
			
		||||
--Allow stressed dwarves to emigrate from the fortress
 | 
			
		||||
-- For 34.11 by IndigoFenix; update and cleanup by PeridexisErrant
 | 
			
		||||
-- old version:  http://dffd.bay12games.com/file.php?id=8404
 | 
			
		||||
--[[=begin
 | 
			
		||||
 | 
			
		||||
emigration
 | 
			
		||||
==========
 | 
			
		||||
Allows dwarves to emigrate from the fortress when stressed,
 | 
			
		||||
in proportion to how badly stressed they are and adjusted
 | 
			
		||||
for who they would have to leave with - a dwarven merchant
 | 
			
		||||
being more attractive than leaving alone (or with an elf).
 | 
			
		||||
The check is made monthly.
 | 
			
		||||
 | 
			
		||||
A happy dwarf (ie with negative stress) will never emigrate.
 | 
			
		||||
 | 
			
		||||
Usage:  ``emigration enable|disable``
 | 
			
		||||
 | 
			
		||||
=end]]
 | 
			
		||||
 | 
			
		||||
local args = {...}
 | 
			
		||||
if args[1] == "enable" then
 | 
			
		||||
    enabled = true
 | 
			
		||||
elseif args[1] == "disable" then
 | 
			
		||||
    enabled = false
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function desireToStay(unit,method,civ_id)
 | 
			
		||||
    -- on a percentage scale
 | 
			
		||||
    value = 100 - unit.status.current_soul.personality.stress_level / 5000
 | 
			
		||||
    if method == 'merchant' or method == 'diplomat' then
 | 
			
		||||
        if civ_id ~= unit.civ_id then value = value*2 end end
 | 
			
		||||
    if method == 'wild' then
 | 
			
		||||
        value = value*5 end
 | 
			
		||||
    return value
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function desert(u,method,civ)
 | 
			
		||||
    u.relations.following = nil
 | 
			
		||||
    local line = dfhack.TranslateName(dfhack.units.getVisibleName(u)) .. " has "
 | 
			
		||||
    if method == 'merchant' then
 | 
			
		||||
        line = line.."joined the merchants"
 | 
			
		||||
        u.flags1.merchant = true
 | 
			
		||||
        u.civ_id = civ
 | 
			
		||||
    elseif method == 'diplomat' then
 | 
			
		||||
        line = line.."followed the diplomat"
 | 
			
		||||
        u.flags1.diplomat = true
 | 
			
		||||
        u.civ_id = civ
 | 
			
		||||
    else
 | 
			
		||||
        line = line.."abandoned the settlement in search of a better life."
 | 
			
		||||
        u.civ_id = -1
 | 
			
		||||
        u.flags1.forest = true
 | 
			
		||||
        u.animal.leave_countdown = 2
 | 
			
		||||
    end
 | 
			
		||||
    print(line)
 | 
			
		||||
    dfhack.gui.showAnnouncement(line, COLOR_WHITE)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function canLeave(unit)
 | 
			
		||||
    for _, skill in pairs(unit.status.current_soul.skills) do
 | 
			
		||||
        if skill.rating > 14 then return false end
 | 
			
		||||
    end
 | 
			
		||||
    if unit.flags1.caged
 | 
			
		||||
        or u.race ~= df.global.ui.race_id
 | 
			
		||||
        or u.civ_id ~= df.global.ui.civ_id
 | 
			
		||||
        or dfhack.units.isDead(u)
 | 
			
		||||
        or dfhack.units.isOpposedToLife(u)
 | 
			
		||||
        or u.flags1.merchant
 | 
			
		||||
        or u.flags1.diplomat
 | 
			
		||||
        or unit.flags1.chained
 | 
			
		||||
        or dfhack.units.getNoblePositions(unit) ~= nil
 | 
			
		||||
        or unit.military.squad_id ~= -1
 | 
			
		||||
        or dfhack.units.isCitizen(unit)
 | 
			
		||||
        or dfhack.units.isSane(unit)
 | 
			
		||||
        or unit.profession ~= 103
 | 
			
		||||
        or not dfhack.units.isDead(unit)
 | 
			
		||||
            then return false end
 | 
			
		||||
    return true
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function checkForDeserters(method,civ_id)
 | 
			
		||||
    local allUnits = df.global.world.units.active
 | 
			
		||||
    for i=#allUnits-1,0,-1 do   -- search list in reverse
 | 
			
		||||
        local u = allUnits[i]
 | 
			
		||||
        if canLeave(u) and math.random(100) < desireToStay(u,method,civ_id) then
 | 
			
		||||
            desert(u,method,civ_id)
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function checkmigrationnow()
 | 
			
		||||
    local merchant_civ_ids = {}
 | 
			
		||||
    local diplomat_civ_ids = {}
 | 
			
		||||
    local allUnits = df.global.world.units.active
 | 
			
		||||
    for i=0, #allUnits-1 do
 | 
			
		||||
        local unit = allUnits[i]
 | 
			
		||||
        if dfhack.units.isSane(unit)
 | 
			
		||||
        and not dfhack.units.isDead(unit)
 | 
			
		||||
        and not dfhack.units.isOpposedToLife(unit)
 | 
			
		||||
        and not unit.flags1.tame
 | 
			
		||||
        then
 | 
			
		||||
            if unit.flags1.merchant then table.insert(merchant_civ_ids, unit.civ_id) end
 | 
			
		||||
            if unit.flags1.diplomat then table.insert(diplomat_civ_ids, unit.civ_id) end
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    for _, civ_id in pairs(merchant_civ_ids) do checkForDeserters('merchant', civ_id) end
 | 
			
		||||
    for _, civ_id in pairs(diplomat_civ_ids) do checkForDeserters('diplomat', civ_id) end
 | 
			
		||||
    checkForDeserters('wild', -1)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function event_loop()
 | 
			
		||||
    checkmigrationnow()
 | 
			
		||||
    dfhack.timeout(1, 'months', event_loop)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
dfhack.onStateChange.loadEmigration = function(code)
 | 
			
		||||
    if code==SC_MAP_LOADED then
 | 
			
		||||
        if enabled then
 | 
			
		||||
            print("Emigration enabled.")
 | 
			
		||||
            event_loop()
 | 
			
		||||
        else
 | 
			
		||||
            print("Emigration disabled.")
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
if dfhack.isMapLoaded() then
 | 
			
		||||
    dfhack.onStateChange.loadEmigration(SC_MAP_LOADED)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
		Loading…
	
		Reference in New Issue