1-- ======================================================================= 2-- Water rising Functionality 3-- ======================================================================= 4 5-- ================ 6-- Helper function 7-- ================ 8function _format_time(t) 9 local mins = math.floor(t/1000 / 60) 10 t = t - mins * 1000 * 60 11 local secs = math.floor(t/1000) 12 13 if mins > 0 then 14 return ("%im%isec"):format(mins,secs) 15 else 16 return ("%isec"):format(secs) 17 end 18end 19 20 21function _fully_flooded(f) 22 if f.terd == "summer_water" and f.terr == "summer_water" and 23 f.tln.terr == "summer_water" and f.tln.terd == "summer_water" and 24 f.ln.terr == "summer_water" and f.trn.terd == "summer_water" then 25 return true 26 end 27 return false 28end 29 30-- ================= 31-- A Field Triangle 32-- ================= 33Triangle = {} 34function Triangle:new(f, which) 35 local rv = { 36 _f = f, 37 _d = which, 38 __hash = ("%i_%i_%s"):format(f.x, f.y, which), 39 } 40 41 setmetatable(rv,self) 42 self.__index = self 43 44 return rv 45end 46 47function Triangle:__tostring() 48 return self.__hash 49end 50 51function Triangle:get_ter() 52 if self._d == "d" then 53 return self._f.terd 54 end 55 return self._f.terr 56end 57 58function Triangle:set_ter(t) 59 if self._d == "d" then 60 self._f.terd = t 61 else 62 self._f.terr = t 63 end 64end 65 66function Triangle:get_height() 67 if self._d == "d" then 68 return (self._f.height + self._f.bln.height + self._f.brn.height) / 3 69 else 70 return (self._f.height + self._f.brn.height + self._f.rn.height) / 3 71 end 72end 73 74function Triangle:fields() 75 if self._d == "d" then 76 return {self._f, self._f.bln, self._f.brn} 77 else 78 return {self._f, self._f.brn, self._f.rn} 79 end 80end 81 82function Triangle:neighbours() 83 if self._d == "d" then 84 return { 85 Triangle:new(self._f, "r"), 86 Triangle:new(self._f.ln, "r"), 87 Triangle:new(self._f.bln, "r"), 88 } 89 else 90 return { 91 Triangle:new(self._f.rn, "d"), 92 Triangle:new(self._f.trn, "d"), 93 Triangle:new(self._f, "d"), 94 } 95 end 96end 97 98-- ========== 99-- WaterRiser 100-- ========== 101WaterRiser = {} 102function WaterRiser:new(ocean_seed) 103 local rv = { 104 _water_level = ocean_seed.height, 105 _ocean = Set:new(), 106 _shore = Set:new(), 107 _to_flood = Set:new(), 108 } 109 110 setmetatable(rv, self) 111 self.__index = self 112 113 local triangles_to_check = Set:new{ 114 Triangle:new(ocean_seed, "r"), 115 Triangle:new(ocean_seed, "d"), 116 } 117 118 while triangles_to_check.size > 0 do 119 local tr = triangles_to_check:pop_at(1) 120 if tr:get_ter() == "summer_water" and tr:get_height() <= rv._water_level then 121 rv._ocean:add(tr) 122 for idx, ntr in ipairs(tr:neighbours()) do 123 if not (rv._ocean:contains(ntr) or rv._shore:contains(ntr)) then 124 triangles_to_check:add(ntr) 125 end 126 end 127 else 128 rv._shore:add(tr) 129 end 130 end 131 132 return rv 133end 134 135function WaterRiser:rise(level) 136 run(function() 137 while self._water_level < level do 138 self._water_level = self._water_level + 1 139 140 print(("Beginning rise to: %i"):format(self._water_level)) 141 local st = game.time 142 143 self:_relevel_ocean() 144 145 local delta = game.time - st 146 print(("Done with normalization, took %s"):format(_format_time(delta))) 147 148 self:_reevaluate_shore() 149 150 self:_rise_water() 151 152 local delta = game.time - st 153 print(("Raising to %i took %s"):format(self._water_level, 154 _format_time(delta))) 155 end 156 end) 157end 158 159function WaterRiser:_relevel_ocean() 160 -- Relevels the ocean over 7.5 mins 161 local scnt = math.floor(self._ocean.size / 450) 162 local cnt = scnt 163 for tr in self._ocean:items() do 164 for idx,f in ipairs(tr:fields()) do 165 if _fully_flooded(f) then 166 f.raw_height = self._water_level 167 end 168 end 169 cnt = cnt - 1 170 if cnt == 0 then 171 cnt = scnt 172 sleep(1000) 173 end 174 end 175 map:recalculate() 176end 177 178function WaterRiser:_reevaluate_shore() 179 -- Check for all shore fields if they remain shore or are going to be 180 -- reflooded 181 for tr in self._shore:items() do 182 if tr:get_height() <= self._water_level then 183 self._shore:discard(tr) 184 self._to_flood:add(tr) 185 end 186 end 187end 188 189function WaterRiser:_rise_water() 190 -- Rises the water, floods the land up to the current water level 191 while self._to_flood.size > 0 do 192 local tr = self._to_flood:pop_at(math.random(self._to_flood.size)) 193 194 tr:set_ter("summer_water") 195 for idx,f in ipairs(tr:fields()) do 196 if _fully_flooded(f) then 197 if self.field_flooded_callback then 198 self.field_flooded_callback(f) 199 end 200 201 f.height = self._water_level 202 if f.immovable then f.immovable:remove() end 203 for idx,b in ipairs(f.bobs) do 204 if not b:has_caps("swims") then b:remove() end 205 end 206 end 207 end 208 209 self._ocean:add(tr) 210 211 -- Check the neighbours 212 for idx, ntr in ipairs(tr:neighbours()) do 213 if not (self._ocean:contains(ntr) or self._shore:contains(ntr)) then 214 if ntr:get_height() <= self._water_level then 215 self._to_flood:add(ntr) 216 else 217 self._shore:add(ntr) 218 end 219 end 220 end 221 sleep(300) 222 end 223end 224