Do they use soap? I'm asking because in my 10-years old fortress with (currently) 370 dwarves they NEVER used as soap to clean themselves. Only to clean patients. I know because I control the number of soap bars carefully (there are 120 bars now), and check periodically with dfhack both they use and emotions related to baths. My dwarves in 0.44.10-12 never actually bath, at least in foamy way.
Since each bath uses a fractional part of a bar, monitoring the total number of soap bars is a very poor barometer. Your dwarves could take over 1000 soapy baths without causing a single one of your 120 bars to disappear.
Oh, I know how this is working. I used dfhack to learn which bars are full, which are partially used and where they currently are. I just gave the number to show both that there is plenty of bars, and that I control exactly how many are there (additionally I also know that the only used ones were used for medical procedures, they still litter the hospital anyway).
I also used dfhack to check if they do have bath-related thoughts, just like I was doing in older fortresses. They don't.
Meanwhile, dwarves now only bathe when they feel it necessary, which is much less often than older versions. My fortress has 30 dwarves and 20 soap bars around the well, and it's probably been at least 5 years since I last cleaned up the soap, so a rough estimate would be 1 bath per dwarf every 5 years. That number changes significantly based on environment and play-style--if your dwarves are regularly caught in freakish weather or cave-in dust clouds then they will bathe more often; less often if they spend all their time sequestered in a library underground.
I have too many dwarves and many of them are citizens for too long (most are there over 5 years) for dirtiness to not kick. I suppose there must be a mechanism which resets it or prevents from occurring. Currently I suspect mist generator, which is present in the tavern, and maybe rain in the case of soldiers, who train under open roof.
You can engineer a small cave-in and watch the dust-cloud-contaminated dwarves march off to bathe, with soap if available.
I'll try it, though they were in dirty incidents already, and somehow never bathed.
Check the 'o'rders' 'z'one 'w'ater setting, if that setting is "only use" and there is no declared water source, then they might not attempt to bath. I'
Hm, I don't have such option under o-z. Only "prefer zone drinking" and "prefer zone fishing".
Drinking is the one they seem to use for baths. I have had to set this standing order when dwarfs were using an above ground pond in preference to an indoor pool for cleaning off bad rain.
I've set it to Zone-only, and will see if they start behaving.
EDIT: Maybe I'll show a script I use for actually checking on baths. You people without problems can run it and say how many bathers you had, if curious. This is quite old script and I probably should remake it, but works for now, more or less.
list-baths.lua-- Show baths and soap usage
local help = [====[
list-baths
=============
Shows how many units do baths, plus soap usage.
]====]
--bathing emotions
local my_baths,my_soapy_baths,bathing_units,bathing_citizens,soapybathing_units,soapybathing_citizens,total_units=0,0,0,0,0,0,0
local my_lackwell,my_lackwellunits,my_lackwellCitizens=0,0,0
for k,v in ipairs(df.global.world.units.all) do
local bather,soapybather,citizen,soapycitizen=false,false,false,false
local lackweller,lackwellerCitizen=false
total_units=#df.global.world.units.all
if v.status.current_soul then
if #v.status.current_soul.personality.emotions>0 then
for kk,vv in ipairs(v.status.current_soul.personality.emotions) do
if vv.thought==130 then
my_baths=my_baths+1
bather=true
--print(string.format("Bath - %s %s",dfhack.units.getProfessionName(v),string.gsub(v.name.first_name,"^%l",string.upper)))
citizen=dfhack.units.isCitizen(v)
elseif vv.thought==131 then
my_soapy_baths=my_soapy_baths+1
soapybather=true
print(string.format("Soapy bath - %s %s",dfhack.units.getProfessionName(v),string.gsub(v.name.first_name,"^%l",string.upper)))
soapycitizen=dfhack.units.isCitizen(v)
elseif vv.thought==144 then
my_lackwell=my_lackwell+1
lackweller=true
print(string.format("Complains about a well - %s %s",dfhack.units.getProfessionName(v),string.gsub(v.name.first_name,"^%l",string.upper)))
lackwellerCitizen=dfhack.units.isCitizen(v)
end
end
if bather then bathing_units=bathing_units+1 if citizen then bathing_citizens=bathing_citizens+1 end end
if soapybather then soapybathing_units=soapybathing_units+1 if soapycitizen then soapybathing_citizens=soapybathing_citizens+1 end end
if lackweller then my_lackwellunits=my_lackwellunits+1 if lackwellerCitizen then my_lackwellCitizens=my_lackwellCitizens+1 end end
end
end
end
print("--Bathers-----------")
print(string.format("Normal baths: %s, soapy baths: %s.",my_baths,my_soapy_baths))
print(string.format("Bathing units: %s (citizens: %s), soapy bathing units: %s (citizens: %s), total units %s.",bathing_units,bathing_citizens,soapybathing_units,soapybathing_citizens,total_units))
print("--Complainers-------")
print(string.format("Complain about a well: %s (citizens: %s), total well complains: %s",my_lackwellunits,my_lackwellCitizens,my_lackwell))
-- soaps
print("--Soaps-------------")
local total_soaps,used_soaps=0,0
for k,v in ipairs(df.global.world.items.all) do
if df.item_barst:is_instance(v) then
local my_smat=string.match(dfhack.matinfo.decode(v.mat_type,v.mat_index):getToken(),".*:SOAP$")
if my_smat then
total_soaps=total_soaps+1
if v.dimension<150 then used_soaps=used_soaps+1 end
--print(my_smat)
end
end
end
print(string.format("Soap bars: %s (unwrapped: %s)",total_soaps,used_soaps))