-- Shows populations of animals in the region, and allows tweaking them.
local utils = require 'utils'
local FORCE = false
local function sort_keys(tab)
local kt = {}
for k,v in pairs(tab) do table.insert(kt,k) end
table.sort(kt)
return ipairs(kt)
end
local is_plant_map = {
Animal = false, Vermin = false, VerminInnumerable = false,
ColonyInsect = false, Tree = true, Grass = true, Bush = true
}
function enum_populations()
local stat_table = {
plants = {},
creatures = {},
any = {}
}
for i,v in ipairs(df.global.world.populations) do
local typeid = df.world_population_type[v.type]
local is_plant = is_plant_map[typeid]
local id, obj, otable, idtoken
if is_plant then
id = v.plant
obj = df.plant_raw.find(id)
otable = stat_table.plants
idtoken = obj.id
else
id = v.race
obj = df.creature_raw.find(id)
otable = stat_table.creatures
idtoken = obj.creature_id
end
local entry = otable[idtoken]
if not entry then
entry = {
obj = obj, token = idtoken, id = id, records = {},
count = 0, known_count = 0,
known = false, infinite = false
}
otable[idtoken] = entry
stat_table.any[idtoken] = entry
end
table.insert(entry.records, v)
entry.known = entry.known or v.flags.discovered
entry.count = entry.count + v.quantity
if v.flags.discovered then
entry.known_count = entry.known_count + v.quantity
end
if v.quantity > 10000000 then
entry.infinite = true
end
end
return stat_table
end
function list_poptable(entries, all, pattern)
for _,k in sort_keys(entries) do
local entry = entries[k]
if (all or entry.known) and (not pattern or string.match(k,pattern)) then
local count = entry.known_count
if all then
count = entry.count
end
if entry.infinite then
count = 'innumerable'
end
print(string.format('%-40s %s', entry.token, count))
end
end
end
function list_populations(stat_table, all, pattern)
print('Plants:')
list_poptable(stat_table.plants, true, pattern)
print('\nCreatures and vermin:')
list_poptable(stat_table.creatures, all, pattern)
end
function boost_population(entry, factor, boost_count)
for _,v in ipairs(entry.records) do
if v.quantity < 10000001 or FORCE then
boost_count = boost_count + 1
v.quantity = math.floor(v.quantity * factor)
end
end
return boost_count
end
function incr_population(entry, amount, incr_count)
for _,v in ipairs(entry.records) do
if v.quantity < 10000001 or FORCE then
incr_count = incr_count + 1
v.quantity = math.max(0, v.quantity + amount)
end
end
return incr_count
end
function set_population(entry, amount, set_count)
for _,v in ipairs(entry.records) do
if v.quantity < 10000001 or FORCE then
set_count = set_count + 1
v.quantity = amount
end
end
return set_count
end
local args = {...}
local pops = enum_populations()
if args[4] == 'FORCE' then
FORCE = true
end
if args[1] == 'list' or args[1] == 'list-all' then
list_populations(pops, args[1] == 'list-all', args[2])
elseif args[1] == 'boost' or args[1] == 'boost-all' then
local factor = tonumber(args[3])
if not factor or factor < 0 then
qerror('Invalid boost factor.')
end
local count = 0
if args[1] == 'boost' then
local entry = pops.any[args[2]] or qerror('Unknown population token.')
count = boost_population(entry, factor, count)
else
for k,entry in pairs(pops.any) do
if string.match(k, args[2]) then
count = boost_population(entry, factor, count)
end
end
end
print('Boosted '..count..' populations.')
elseif args[1] == 'incr' or args[1] == 'incr-all' then
local amount = tonumber(args[3])
if not amount then
qerror('Invalid increase amount.')
end
local count = 0
if args[1] == 'incr' then
local entry = pops.any[args[2]] or qerror('Unknown population token.')
count = incr_population(entry, amount, count)
else
for k,entry in pairs(pops.any) do
if string.match(k, args[2]) then
count = incr_population(entry, amount, count)
end
end
end
print('Increased '..count..' populations.')
elseif args[1] == 'set' or args[1] == 'set-all' then
local amount = tonumber(args[3])
if not amount then
qerror('Invalid set amount.')
end
local count = 0
if args[1] == 'set' then
local entry = pops.any[args[2]] or qerror('Unknown population token.')
count = set_population(entry, amount, count)
else
for k,entry in pairs(pops.any) do
if string.match(k, args[2]) then
count = set_population(entry, amount, count)
end
end
end
print('Set '..count..' populations.')
else
print([[
Usage:
region-pops list [pattern]
Lists encountered populations of the region, optionally restricted by pattern.
region-pops list-all [pattern]
Lists all populations of the region, optionally restricted by pattern.
region-pops boost <TOKEN> <factor>
Multiply all populations of TOKEN by factor.
If the factor is greater than one, increases the
population, otherwise decreases it.
region-pops boost-all <pattern> <factor>
Same as above, but boost all populations matching pattern.
region-pops incr <TOKEN> <amount>
Increase (or diminish) all populations of TOKEN by amount (additive).
region-pops incr-all <pattern> <amount>
Same as above, but increase all populations matching pattern.
region-pops set <TOKEN> <amount>
Set all populations of TOKEN to amount.
region-pops set-all <pattern> <amount>
Same as above, but set all populations matching pattern.
Adding 'FORCE' at the end of the command will allow changing
"innumerable" populations as well (experimental)
]])
end
To use it, install dfhack (if you don't have it already), copy the code, paste it over the code in "hack/scripts/region-pops.lua" in your Dwarf Fortress directory, then run "region-pops set SAGUARO 0 FORCE" in the dfhack console. Make sure you keep a backup copy of your save because there is some possibility for flakiness. I tested it for several seasons and the game didn't crash, but I'm not sure that setting the region population to zero actually prevents new trees from growing (it doesn't affect already growing trees). But, at least in theory, this should eliminate the elfish encroachment.