This thread is about the technical details of how and when temperature values appear in a fortress, but has little immediate practical use for a player.
I've made quite a few one year measurements of fortress temperature changes and their timing in embarks on worlds of different sizes and with different poles (South, Both, and None). Since I've used Lua scripts for this, any "code" notation is using Lua syntax. My conclusions are as per the below:
Freezing point:
Water turns to ice when the temperature changes from 10001 to 10000, and thaws on a change from 10000 to 10001 (it has been suggested the transition was between 0 and -1).
Max Embark Temperature: Approximately 10000 + 3/4 * World Tile Temperature
Detailed Max Embark Temperature:
if temperature >= 0 then
max_temperature = 10000 + math.floor (temperature / 4) * 3 + math.max (0, temperature % 4 - 1)
else
max_temperature = 10000 - math.floor (math.abs (temperature) / 4) * 3 - math.max (0, math.abs (temperature) % 4 - 1)
end
Temperature Variation (i.e. how much lower the min temperature is compared to the max temperature):
Dependent on Latitude, number of Poles, and world size.
Unsurprisingly, worlds without poles do not have any seasonal temperature variation.
The variation is approximately linear from pole (0) to mid latitude (max) and between mid latitude and the equator (0).
The lowest temperature is reached during mid winter and the highest during mid summer.
The max variation is dependent on a Divisor for the mid latitude world tile, and the max variation is
calculated as:
max_variation = math.ceil (Divisor * 3 / 4).
It can be noted that the minimum temperature of an embark is held for only 10 ticks for many embarks (all embarks whose
divisor isn't evenly divisible by 4). This can result in flash freeze/thaws you might not even notice.
So far no formula for determining the max Divisor for different world configurations has been found, so a table is used.
The max variation for a world with at least one pole of a given size:
- 17: 43 (Divisor 57)
- 33: 46 (Divisor 61)
- 65: 48 (Divisor 63)
- 129: 48 (Divisor 64)
- 257: 48 (Divisor 64)
The temperature variation of a given latitude:
The Divisor mentioned above is also the primary driver for temperature variation. Each latitude has a Divisor,
and this divisor controls what the temperature variation is at that latitude, as well as when the temperature
changes (although the algorithm for that isn't wholly straight forward or intuitive).
The Divisor for a latitude is dependent on the Divisor of the mid latitude world tile and the number of world
tiles from it to the pole/equator. I've been unable to find a formula that works for all sizes, resulting in 3
formulae rather than one.
If the world size is 17 the formula is:
Divisor = math.floor (Max_Divisor / Steps * Latitude_Step + 0.4),
while for a world size of 33 it becomes:
Divisor = math.floor (Max_Divisor / Steps * Latitude_Step + 0.1),
and the other sizes:
Divisor = math.floor (Max_Divisor / Steps * Latitude_Step),
where Latitude_Step is the number of world tiles from the pole/equator towards the mid latitude, starting with 0 for the
pole/equator.
The conversion from a Divisor to a temperature variation is the same as above, i.e. math.ceil (Divisor * 3 / 4).
Embark Temperature:
The exact temperature of an embark at a particular point in time should be possible to calculate. As mentioned above, the
logic has become somewhat odd:
The temperature is checked only at discreet times, namely every half_year / Divisor ticks, rounded up to the next
10 tick boundary. In addition to this, the temperature is changed only 3 out of every 4 checks (which is an effect
of the temperature variation being only 3/4 of the Divisor). Note that there are slight exceptions to this general
behavior.
Once the Divisor has been the determined, the interval can be calculated, and based on that the exact time when
the temperature is changed (and to what, when max temperature is available) should be possible to calculate.
However, I've been unable to get an algorithm that provides the exact results, but I've come fairly close.
The following Lua script function produces a temperature and timing that's usually exact, but the measured time
can be 10 ticks later than the calculated one for some points (but not the min temperature ones):
[code]
function make_series (max_temperature, divisor)
local half_year = 28 * 6 * 1200
local year = half_year * 2
local interval = half_year / divisor
local a, b
local mid_summer = 1200 * 28 * 4 + 1200 * 14
local mid_winter = 1200 * 28 * 10 + 1200 * 14
local list = {}
local now = mid_summer
local now_back = mid_summer
local next_time
local temperature = max_temperature
local temp
local amplitude = math.ceil (divisor * 3 / 4)
if interval == math.floor (interval) and
divisor ~= 50 and --###
divisor ~= 25 then --###
now_back = now_back + 10
end
for i = 0, divisor - 1 do
now = now + interval
now_back = now_back - interval
if now_back < 0 then
now_back = now_back + year
end
if i % 4 ~= 3 and
temperature ~= max_temperature - amplitude then
temperature = temperature - 1
temp = math.floor (now + 0.99)
if temp % 10 ~= 0 then
temp = temp + 10 - temp % 10
end
table.insert (list, {temperature, temp, now})
if i == divisor - 1 and
interval ~= math.floor (interval) then
now_back = now_back + 10
end
if divisor == 62 then --###
temp = math.ceil (now_back)
else
temp = math.floor (now_back + 0.99)
end
if temp % 10 ~= 0 then
temp = temp + 10 - temp % 10
end
table.insert (list, {temperature + 1, temp, now_back})
end
end
for i, junk in ipairs (list) do
for k = i, #list do
a = list [i] [2]
if a < 16800 then
a = a + year
end
b = list [k] [2]
if b < 16800 then
b = b + year
end
if a > b then
temp = list [i]
list [i] = list [k]
list [k] = temp
end
end
end
--### To make list match produced measurements
if #list > 1 then
if list [1] [2] == 16800 then
list [1] [2] = 16810
end
end
if divisor == 1 then
table.insert (list, 1, {max_temperature, 16800})
else
table.insert (list, 1, {list [1] [1] - 1, 16800})
end
return list
end
[/code]
PSV -> Generated World Temperature:
PSV temperature values are changed on world gen as DF applies a latitude modifier as well as an elevation modifier
to the PSV value. For the time being this examination will not try to produce formulae for those transformations
(the author has tried in the past, settling for translation tables).