It's for v0.44.12.
list-gods.lua-- lists gods, number of worshipers and temple status
---------------CONFIGURATION---------------
SHOW_CURSE = false --whether to show real name, or assumed one in case of vampires or spies
MULTICOLOR = true --change to false for monochrome output (much less useful)
ACC_LEVEL = {COLOR_DARKGREY,COLOR_BLUE,COLOR_YELLOW,COLOR_WHITE,COLOR_LIGHTGREEN} --colors of access level for temples (doesn't exist, no zones, citizens, citizens+residents, all visitors)
FOCUS_LEVEL = {COLOR_LIGHTGREEN,COLOR_GREEN,COLOR_WHITE,COLOR_GREY,COLOR_BROWN,COLOR_YELLOW,COLOR_LIGHTRED} --colors for focus levels
FOCUS_STR = {"Unfettered","Level-headed","Untroubled","Not distracted","Unfocused","Distracted","Badly distracted"} --names of focus levels
LIST_ROOMS = true --whether to list rooms associated to temples, in addition to zones which are always listed
LIST_DOMAINS = false --whether to show domains (a.k.a. spheres) in the main list, can also be temporarily enabled by "-domain" option
------------END-OF-CONFIGURATION-----------
local help = [====[
list-gods
=============
Shows information about gods and temples, including number of worshippers.
Works only in fortress game mode. Displays in DFhack's console.
``list-gods -all`` also counts gods of visitors
``list-gods -noroom`` doesn't consider rooms, only zones
``list-gods -domain`` shows domains (spheres) in the main list
``list-gods -help`` shows this help
Example of use: list-gods
Has three modes of action, depending on cursor position in DF window:
1. unit - info about unit's gods
2. temple zone/list - info about temple and its god
3. default - info about all gods and their best temples
]====]
local utils = require 'utils'
local cit_stat_txt={"citizen","long-term resident","visitor"}
local glist = {}
local tlist = {}
local map_x,map_y = dfhack.maps.getTileSize()
map_x = map_x-1
map_y = map_y-1
kon = dfhack.df2console
kol = dfhack.color
validArgs = validArgs or utils.invert({
'help',
'all',
'noroom',
'norooms',
'domain',
'domains'
})
function get_critter_name (unit)
if #unit.name.nickname>0 then return dfhack.units.getVisibleName(unit).nickname
elseif SHOW_CURSE then return string.gsub(unit.name.first_name,"^%l",string.upper)
else return string.gsub(dfhack.units.getVisibleName(unit).first_name,"^%l",string.upper)
end return ""
end
function round(n)
return n % 1 >= 0.5 and math.ceil(n) or math.floor(n)
end
function list_table (input_table)
local my_str=table.concat(input_table,", ")
local my_tmp=nil
if my_str then
my_tmp=string.find(my_str,",[^,]*$")
if my_tmp then
my_str=table.concat{my_str:sub(1,my_tmp-1), " and", my_str:sub(my_tmp+1)}
end
return my_str
end
return nil
end
function get_master_race() --master race and master civ
local master_race,master_civ,entity_k=nil,nil,nil
if df.global.gamemode==0 then
if #df.global.world.world_data.active_site>0 then
if #df.global.world.world_data.active_site[0].entity_links>0 then
master_civ=df.global.world.world_data.active_site[0].entity_links[0].entity_id
-- master_race=df.global.world.entities.all[master_civ].race --OK for 0.44 worlds, but may fail for older ones
for k,v in ipairs(df.global.world.entities.all) do --workaround for older worlds
if v.id == master_civ then master_race=v.race entity_k=k end
end
end
else
for k,v in ipairs(df.global.world.raws.creatures.all) do
if string.find(v.creature_id,"^DWARF$") then master_race=k end
end
end
end
return master_race,master_civ,entity_k
end
local _,master_civ,civ_k=get_master_race()
function get_citizen_status(unit) --whether the person is a citizen, long-term resident or a visitor (only alive)
if not unit.flags1.inactive then
if dfhack.units.isCitizen(unit) then return 1
elseif unit.flags2.visitor and not unit.flags1.marauder then return 3
elseif unit.civ_id==master_civ then
if not unit.flags1.marauder and not unit.flags1.tame and not unit.flags2.visitor and not unit.flags1.merchant and not unit.flags1.forest then return 2
end
else return nil
end
else return nil
end
end
function get_location_zone(lid,all) --returns name of first associated zone (or all ones, if all=true), and a table of zone pointers; if LIST_ROOM also lists rooms
if lid then
local f_site=df.global.world.world_data.active_site[0]
for k,v in ipairs(f_site.buildings) do
local fzone_l,fzones,froom_l,frooms={},{},{},{}
if v.id==lid and #v.contents.building_ids>0 then
for kk,vv in ipairs(v.contents.building_ids) do
local f_building=df.building.find(vv)
if f_building and df.building_civzonest:is_instance(f_building) then
if #f_building.name>0 then table.insert(fzone_l,f_building.name) fzones[#fzones+1]=f_building
elseif f_building.zone_num then table.insert(fzone_l,string.format("Activity Zone #%s",f_building.zone_num)) fzones[#fzones+1]=f_building
else table.insert(fzone_l,"Unknown name") fzones[#fzones+1]=f_building
end
if not all and #fzone_l>0 then return list_table(fzone_l),fzones end
elseif LIST_ROOMS and f_building and f_building.is_room then
if #f_building.name>0 then table.insert(froom_l,f_building.name) frooms[#frooms+1]=f_building
else
local my_room=dfhack.buildings.getRoomDescription(f_building)
if my_room and #my_room>0 then table.insert(froom_l,my_room) frooms[#frooms+1]=f_building
else table.insert(froom_l,string.format("UNKNOWN [ID %s]",f_building.id)) frooms[#frooms+1]=f_building end
end
end
end
for kk,vv in ipairs(froom_l) do
fzone_l[#fzone_l+1]=froom_l[kk]
fzones[#fzones+1]=frooms[kk]
end
if #fzone_l>0 then if not all then return fzone_l[1],fzones[1] else return list_table(fzone_l),fzones end end
end
end
if LIST_ROOMS then return "no zone or room",nil else return "no zone",nil end
end
return "N/A",nil
end
function do_nothing (col)
end
-- Main program: ------------------------------------------------------------
local args = utils.processArgs({...}, validArgs)
if args.help or df.global.gamemode~=0 then
print(help)
return
end
if args.noroom or args.norooms then
LIST_ROOMS=false
end
if args.domain or args.domains then
LIST_DOMAINS=true
end
if not MULTICOLOR then kol = do_nothing end
local inid=nil
for k,v in ipairs(df.global.world.units.active) do
inid=dfhack.units.getNemesis(v)
if inid then
if v.hist_figure_id==inid.figure.id then
css=get_citizen_status(v)
if css then
for kk,vv in ipairs(inid.figure.histfig_links) do
if df.histfig_hf_link_deityst:is_instance(vv) then
if glist[vv.target_hf] then
glist[vv.target_hf][css]=glist[vv.target_hf][css]+1
glist[vv.target_hf][4]=glist[vv.target_hf][4]+1
if css~=3 then glist[vv.target_hf][5]=glist[vv.target_hf][5]+1 end
else
glist[vv.target_hf]={0,0,0,1,0,vv.target_hf,-1,{}}
if css~=3 then glist[vv.target_hf][5]=1 end
glist[vv.target_hf][css]=glist[vv.target_hf][css]+1
for kkk,vvv in ipairs(df.historical_figure.find(vv.target_hf).info.spheres) do
glist[vv.target_hf][8][#glist[vv.target_hf][8]+1]=df.sphere_type[vvv]
end
end
end
end
end
end
end
end
for k,v in ipairs(df.global.world.entities.all[civ_k].unknown1b.deities) do --add civ gods, if missing
if not glist[v] then
glist[v]={0,0,0,0,0,v,-1,{}}
for kk,vv in ipairs(df.historical_figure.find(v).info.spheres) do
glist[v][8][#glist[v][8]+1]=df.sphere_type[vv]
end
end
end
local keysG={}
for k,v in pairs(glist) do
keysG[#keysG+1] = {k, v}
end
if args.all then
table.sort(keysG, function(a, b) return a[2][4] > b[2][4] end)
else
table.sort(keysG, function(a, b) return a[2][5] > b[2][5] end)
end
local my_site1=df.global.world.world_data.active_site[0]
for k,v in ipairs(my_site1.buildings) do
local acc=0
if df.abstract_building_templest:is_instance(v) then
if not v.flags[1] or #v.contents.building_ids>0 then
if v.flags.AllowVisitors then acc=3
elseif v.flags.AllowResidents then acc=2
else acc=1 end
local is_zone=false
for kk,vv in ipairs(v.contents.building_ids) do
if df.building.find(vv) and df.building_civzonest:is_instance(df.building.find(vv)) then is_zone=true end
if LIST_ROOMS and df.building.find(vv) and df.building.find(vv).is_room then is_zone=true end
end
if not is_zone then acc=0 end
if tlist[k] then acc=math.max(acc,tlist[k][4]) end
tlist[k]={v.id,v.deity,v.name,acc,false}
end
end
end
local keysT={}
for k,v in pairs(tlist) do
keysT[#keysT+1] = v
end
for i,v in ipairs(keysT) do
for ii,vv in ipairs(keysG) do
if v[2]==vv[1] then
if vv[2][5]>0 then v[5]=true end
if v[4]>vv[2][7] then vv[2][7]=v[4] end --highest inclusivity level
end
end
end
if not dfhack.gui.getSelectedUnit() then
local my_build={}
local my_zone,my_screenL=nil,nil
local my_site=df.global.world.world_data.active_site[0]
my_zone=dfhack.buildings.findCivzonesAt(df.global.cursor.x,df.global.cursor.y,df.global.window_z)
if my_zone then for i,v in ipairs(my_zone) do my_build[i]={v.location_id,v} end end
my_screenL=dfhack.gui.getViewscreenByType(df.viewscreen_locationsst)
if my_screenL then if my_screenL.locations[my_screenL.location_idx] then my_build[1]={my_screenL.locations[my_screenL.location_idx].id,nil} end end
if my_build and #my_build>0 then
for i,v in ipairs(my_build) do
local zone_name=""
if v[2] then
if #v[2].name>0 then zone_name=v[2].name
elseif v[2].zone_num then zone_name=string.format("Activity Zone #%s",v[2].zone_num)
else zone_name="Unknown zone" end
end
if #zone_name>0 then print(string.format("Zone: %s",zone_name)) end
if my_site then
for ii,vv in ipairs(my_site.buildings) do
if vv.id==v[1] then
if df.abstract_building_templest:is_instance(vv) then
for iii,vvv in ipairs(keysT) do
if vvv[1]==v[1] then
kol(ACC_LEVEL[vvv[4]+2])
print(string.format("Temple: %s",kon(dfhack.TranslateName(vvv[3],true))))
local my_god="" if vvv[2]==-1 then my_god="no particular deity" else my_god=dfhack.TranslateName(df.historical_figure.find(vvv[2]).name,true) end kol(nil)
print(string.format(" Dedicated to %s.",my_god))
if vvv[2]>-1 then
for kkkk,vvvv in ipairs(keysG) do
if vvvv[1]==vvv[2] then
if #vvvv[2][8]>0 then print(string.format(" Associated with %s.",string.lower(list_table(vvvv[2][8])))) end
print(string.format(" Worshipping citizens: %s, long-term residents: %s, visitors: %s.",vvvv[2][1],vvvv[2][2],vvvv[2][3]))
end
end
end
end
end
elseif df.abstract_building_inn_tavernst:is_instance(vv) then
print(string.format("Tavern: %s",kon(dfhack.TranslateName(vv.name,true))))
elseif df.abstract_building_libraryst:is_instance(vv) then
print(string.format("Library: %s",kon(dfhack.TranslateName(vv.name,true))))
else
print(string.format("Unknown location type"))
end
local tmp_str=" and rooms:" if not LIST_ROOMS then tmp_str=":" end
print(string.format(" Assigned zones%s %s.",tmp_str,get_location_zone(v[1],true)))
print()
end
end
end
end
else
local glabel,god_n,form_str,strF,strG,strG0,strGA,strH,strV,formH="",1,"","",""," Domains (Spheres)"," Domains","#","",""
if keysG then god_n=#keysG end
local gdn=tostring(god_n) gdn=#gdn gdn=tostring(gdn) formH="%"..gdn.."s" gdn="%"..gdn.."d"
if args.all then strF="+ %3d " strV="+ Vis " strG=strGA else strF="" strV="" strG=strG0 end
if not LIST_DOMAINS then strG="" end
form_str=gdn.." %3d + %3d "..strF.."= %3d %-40s %s"
formH=string.format(formH,"#")
glabel=formH.." Cit + LTR "..strV.."= All God"..strG
kol(nil)
print(glabel)
for i,v in pairs(keysG) do
kol(ACC_LEVEL[v[2][7]+2])
local tmp_str=""
if LIST_DOMAINS then tmp_str=list_table(v[2][8]) else tmp_str="" end
if args.all then
print(string.format(form_str,i,v[2][1],v[2][2],v[2][3],v[2][4],kon(dfhack.TranslateName(df.historical_figure.find(v[1]).name,true)),tmp_str))
else if v[2][5]>0 or v[2][4]==0 then
print(string.format(form_str,i,v[2][1],v[2][2],v[2][5],kon(dfhack.TranslateName(df.historical_figure.find(v[1]).name,true)),tmp_str)) end end
end
kol(nil)
print("Universal temples (all gods):")
local univT=false
for i,v in ipairs(keysT) do
if v[2]==-1 then
univT=true
kol(ACC_LEVEL[v[4]+2])
print(string.format(" %s (%s)",kon(dfhack.TranslateName(v[3],true)),get_location_zone(v[1],true))) end
end
kol(nil)
if not univT then print(" -none-") end
print("Abandoned temples (no worshippers):")
local orphT=false
for i,v in ipairs(keysT) do
if v[2]>-1 and not v[5] then
orphT=true
kol(ACC_LEVEL[v[4]+2])
print(string.format(" %s (dedicated to %s, %s)",kon(dfhack.TranslateName(v[3],true)),kon(dfhack.TranslateName(df.historical_figure.find(v[2]).name,true)),get_location_zone(v[1],true))) end
end
if not orphT then print(" -none-") end
kol(nil)
end
else
local unit = dfhack.gui.getSelectedUnit()
if unit then
local inid=dfhack.units.getNemesis(unit)
if inid then
local css=get_citizen_status(unit)
local godsfollowed=false
if css then
print(string.format("%s is a %s, following these gods:\n",get_critter_name(unit),cit_stat_txt[css]))
for k,v in ipairs(inid.figure.histfig_links) do
if df.histfig_hf_link_deityst:is_instance(v) then
godsfollowed=true spheres={}
for kk,vv in ipairs(keysG) do
if v.target_hf==vv[1] then
kol(ACC_LEVEL[vv[2][7]+2])
print(string.format("%s/%s (Cit: %s, LTR: %s, Vis: %s)",kon(dfhack.TranslateName(df.historical_figure.find(vv[1]).name,true)),kon(dfhack.TranslateName(df.historical_figure.find(vv[1]).name,false)),vv[2][1],vv[2][2],vv[2][3]))
kol(nil)
if #vv[2][8]>0 then print(string.format(" Associated with %s.", string.lower(list_table(vv[2][8])))) end
if unit.status.current_soul then
local godneed=false
for kkk,vvv in ipairs(unit.status.current_soul.personality.needs) do
if vvv.id==2 and vvv.deity_id==vv[1] then
godneed=true
if vvv.focus_level>299 then flev=1
elseif vvv.focus_level>199 then flev=2
elseif vvv.focus_level>99 then flev=3
elseif vvv.focus_level>-1000 then flev=4
elseif vvv.focus_level>-10000 then flev=5
elseif vvv.focus_level>-100000 then flev=6
else flev=7
end
dfhack.print(string.format(" Worship/meditation focus: ")) kol(FOCUS_LEVEL[flev])
dfhack.print(string.format("%s (%s) ",FOCUS_STR[flev],vvv.focus_level)) kol(nil)
print(string.format("Need level: %s",vvv.need_level))
end
end
if not godneed then kol(nil) print(string.format(" Worship/meditation focus: No need")) end
end
print()
end
end
end
end
if not godsfollowed then print(" - none -") end
end
end
end
end
This is a script to show all gods available, their number of worshippers (divided into three categories: citizens, long-term residents and short-term visitors), as well as status of the best temple for each god, plus assigned zones. I had to make it when my fortress reached about 100 gods and started to have problems with temple management. It allowed me to discover popular gods with no temples, gods who lost popularity, or find zones/rooms assigned to temples and forgotten, which is not that easy with DF's interface.
The script otputs to DFhack's console, but the mode of action depends on the cursor or highlight in the main DF window.
- gods available for creating temples are shown, ordered according to the number of worshippers (most popular on top), and colored depending on status/inclusivity of their best temple. Additionally all universal temples are listed (dedicated to no particular deity), as well as orphaned temples (dedicated to deity which has no longer worshippers).
Without additional option only gods avaialble are listed, i.e. ones worshipped by your civ or citizens and long-term residents on map. But with option "
" also gods worshipped by visitors are listed. The game doesn't count visitors when displaying gods available and number of worshippers, that's why this is optional here. There is also option "
") which will display domains (spheres) associated with that god.
- when either the cursor is on a zone in main window, or the location is highlighted in the list of locations. Shows name of the zone (or zones, if overlapping) under the cursor, color-coded name of the temple, spheres associated with the god, number of worshippers of each category, and zones and rooms.
", which prevents counting the rooms, only zones are shown. This is mostly useful with taverns actually, unless you assign rooms to the temples too.
- when the unit is selected in any way (even in list of deads). Shows citizenship status of the unit, all gods worshipped by it (color-coded according to the best temple), associated spheres, need for worship (not all worshippers need to actully worship the god), as well a focus level associated with worshipping the particular god.