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