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