]> git.lizzy.rs Git - minetest.git/blob - src/content_abm.cpp
110ac1eea919d641591be89ff1635c4266f887bf
[minetest.git] / src / content_abm.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "content_abm.h"
21
22 #include "environment.h"
23 #include "gamedef.h"
24 #include "nodedef.h"
25 #include "content_sao.h"
26 #include "settings.h"
27 #include "mapblock.h" // For getNodeBlockPos
28 #include "treegen.h" // For treegen::make_tree
29 #include "main.h" // for g_settings
30 #include "map.h"
31 #include "cpp_api/scriptapi.h"
32 #include "log.h"
33
34 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
35
36 class GrowGrassABM : public ActiveBlockModifier
37 {
38 private:
39 public:
40         virtual std::set<std::string> getTriggerContents()
41         {
42                 std::set<std::string> s;
43                 s.insert("mapgen_dirt");
44                 return s;
45         }
46         virtual float getTriggerInterval()
47         { return 2.0; }
48         virtual u32 getTriggerChance()
49         { return 200; }
50         virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n)
51         {
52                 INodeDefManager *ndef = env->getGameDef()->ndef();
53                 ServerMap *map = &env->getServerMap();
54                 
55                 MapNode n_top = map->getNodeNoEx(p+v3s16(0,1,0));
56                 content_t c_snow = ndef->getId("snow");
57                 if(ndef->get(n_top).light_propagates &&
58                                 !ndef->get(n_top).isLiquid() &&
59                                 n_top.getLightBlend(env->getDayNightRatio(), ndef) >= 13)
60                 {
61                         if(c_snow != CONTENT_IGNORE && n_top.getContent() == c_snow)
62                                 n.setContent(ndef->getId("dirt_with_snow"));
63                         else
64                                 n.setContent(ndef->getId("mapgen_dirt_with_grass"));
65                         map->addNodeWithEvent(p, n);
66                 }
67         }
68 };
69
70 class RemoveGrassABM : public ActiveBlockModifier
71 {
72 private:
73 public:
74         virtual std::set<std::string> getTriggerContents()
75         {
76                 std::set<std::string> s;
77                 s.insert("mapgen_dirt_with_grass");
78                 return s;
79         }
80         virtual float getTriggerInterval()
81         { return 2.0; }
82         virtual u32 getTriggerChance()
83         { return 20; }
84         virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n)
85         {
86                 INodeDefManager *ndef = env->getGameDef()->ndef();
87                 ServerMap *map = &env->getServerMap();
88                 
89                 MapNode n_top = map->getNodeNoEx(p+v3s16(0,1,0));
90                 if((!ndef->get(n_top).light_propagates &&
91                                 n_top.getContent() != CONTENT_IGNORE) ||
92                                 ndef->get(n_top).isLiquid())
93                 {
94                         n.setContent(ndef->getId("mapgen_dirt"));
95                         map->addNodeWithEvent(p, n);
96                 }
97         }
98 };
99
100 class MakeTreesFromSaplingsABM : public ActiveBlockModifier
101 {
102 private:
103         content_t c_junglesapling;
104         
105 public:
106         MakeTreesFromSaplingsABM(ServerEnvironment *env, INodeDefManager *nodemgr) {
107                 c_junglesapling = nodemgr->getId("junglesapling");
108         }
109
110         virtual std::set<std::string> getTriggerContents()
111         {
112                 std::set<std::string> s;
113                 s.insert("sapling");
114                 s.insert("junglesapling");
115                 return s;
116         }
117         virtual float getTriggerInterval()
118         { return 10.0; }
119         virtual u32 getTriggerChance()
120         { return 50; }
121         virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n,
122                         u32 active_object_count, u32 active_object_count_wider)
123         {
124                 INodeDefManager *ndef = env->getGameDef()->ndef();
125                 ServerMap *map = &env->getServerMap();
126                 
127                 MapNode n_below = map->getNodeNoEx(p - v3s16(0, 1, 0));
128                 if (!((ItemGroupList) ndef->get(n_below).groups)["soil"])
129                         return;
130                         
131                 bool is_jungle_tree = n.getContent() == c_junglesapling;
132                 
133                 actionstream <<"A " << (is_jungle_tree ? "jungle " : "")
134                                 << "sapling grows into a tree at "
135                                 << PP(p) << std::endl;
136
137                 std::map<v3s16, MapBlock*> modified_blocks;
138                 v3s16 tree_p = p;
139                 ManualMapVoxelManipulator vmanip(map);
140                 v3s16 tree_blockp = getNodeBlockPos(tree_p);
141                 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
142                 
143                 if (is_jungle_tree) {
144                         treegen::make_jungletree(vmanip, tree_p, ndef, myrand());
145                 } else {
146                         bool is_apple_tree = myrand() % 4 == 0;
147                         treegen::make_tree(vmanip, tree_p, is_apple_tree, ndef, myrand());
148                 }
149                 
150                 vmanip.blitBackAll(&modified_blocks);
151
152                 // update lighting
153                 std::map<v3s16, MapBlock*> lighting_modified_blocks;
154                 lighting_modified_blocks.insert(modified_blocks.begin(), modified_blocks.end());
155                 map->updateLighting(lighting_modified_blocks, modified_blocks);
156
157                 // Send a MEET_OTHER event
158                 MapEditEvent event;
159                 event.type = MEET_OTHER;
160 //              event.modified_blocks.insert(modified_blocks.begin(), modified_blocks.end());
161                 for(std::map<v3s16, MapBlock*>::iterator
162                         i = modified_blocks.begin();
163                         i != modified_blocks.end(); ++i)
164                 {
165                         event.modified_blocks.insert(i->first);
166                 }
167                 map->dispatchEvent(&event);
168         }
169 };
170
171 class LiquidFlowABM : public ActiveBlockModifier {
172         private:
173                 std::set<std::string> contents;
174
175         public:
176                 LiquidFlowABM(ServerEnvironment *env, INodeDefManager *nodemgr) {
177                         std::set<content_t> liquids;
178                         nodemgr->getIds("group:liquid", liquids);
179                         for(std::set<content_t>::const_iterator k = liquids.begin(); k != liquids.end(); k++)
180                                 contents.insert(nodemgr->get(*k).liquid_alternative_flowing);
181                 }
182                 virtual std::set<std::string> getTriggerContents() {
183                         return contents;
184                 }
185                 virtual float getTriggerInterval()
186                 { return 10.0; }
187                 virtual u32 getTriggerChance()
188                 { return 10; }
189                 virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) {
190                         ServerMap *map = &env->getServerMap();
191                         if (map->transforming_liquid_size() > 500)
192                                 return;
193                         map->transforming_liquid_add(p);
194                 }
195 };
196
197 class LiquidDropABM : public ActiveBlockModifier {
198         private:
199                 std::set<std::string> contents;
200
201         public:
202                 LiquidDropABM(ServerEnvironment *env, INodeDefManager *nodemgr) {
203                         std::set<content_t> liquids;
204                         nodemgr->getIds("group:liquid", liquids);
205                         for(std::set<content_t>::const_iterator k = liquids.begin(); k != liquids.end(); k++)
206                                 contents.insert(nodemgr->get(*k).liquid_alternative_source);
207                 }
208                 virtual std::set<std::string> getTriggerContents()
209                 { return contents; }
210                 virtual std::set<std::string> getRequiredNeighbors() {
211                         std::set<std::string> neighbors;
212                         neighbors.insert("mapgen_air");
213                         return neighbors;
214                 }
215                 virtual float getTriggerInterval()
216                 { return 20.0; }
217                 virtual u32 getTriggerChance()
218                 { return 10; }
219                 virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) {
220                         ServerMap *map = &env->getServerMap();
221                         if (map->transforming_liquid_size() > 500)
222                                 return;
223                         if (   map->getNodeNoEx(p - v3s16(0,  1, 0 )).getContent() != CONTENT_AIR  // below
224                             && map->getNodeNoEx(p - v3s16(1,  0, 0 )).getContent() != CONTENT_AIR  // right
225                             && map->getNodeNoEx(p - v3s16(-1, 0, 0 )).getContent() != CONTENT_AIR  // left
226                             && map->getNodeNoEx(p - v3s16(0,  0, 1 )).getContent() != CONTENT_AIR  // back
227                             && map->getNodeNoEx(p - v3s16(0,  0, -1)).getContent() != CONTENT_AIR  // front
228                            )
229                                 return;
230                         map->transforming_liquid_add(p);
231                 }
232 };
233
234 class LiquidFreeze : public ActiveBlockModifier {
235         public:
236                 LiquidFreeze(ServerEnvironment *env, INodeDefManager *nodemgr) { }
237                 virtual std::set<std::string> getTriggerContents() {
238                         std::set<std::string> s;
239                         s.insert("group:freezes");
240                         return s;
241                 }
242                 virtual std::set<std::string> getRequiredNeighbors() {
243                         std::set<std::string> s;
244                         s.insert("mapgen_air");
245                         s.insert("group:melts");
246                         return s;
247                 }
248                 virtual float getTriggerInterval()
249                 { return 10.0; }
250                 virtual u32 getTriggerChance()
251                 { return 20; }
252                 virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) {
253                         ServerMap *map = &env->getServerMap();
254                         INodeDefManager *ndef = env->getGameDef()->ndef();
255
256                         float heat = map->getHeat(env, p);
257                         //heater = rare
258                         if (heat <= -1 && (heat <= -50 || ((myrand_range(-50, heat)) <= -30))) {
259                                 content_t c_self = n.getContent();
260                                 // making freeze not annoying, do not freeze random blocks in center of ocean
261                                 // todo: any block not water (dont freeze _source near _flowing)
262                                 content_t c;
263                                 bool allow = heat < -40;
264                                 // todo: make for(...)
265                                 if (!allow) {
266                                  c = map->getNodeNoEx(p - v3s16(0,  1, 0 )).getContent(); // below
267                                  if (c == CONTENT_AIR || c == CONTENT_IGNORE)
268                                         return; // do not freeze when falling
269                                  if (c != c_self && c != CONTENT_IGNORE) allow = 1;
270                                  if (!allow) {
271                                   c = map->getNodeNoEx(p - v3s16(1,  0, 0 )).getContent(); // right
272                                   if (c != c_self && c != CONTENT_IGNORE) allow = 1;
273                                   if (!allow) {
274                                    c = map->getNodeNoEx(p - v3s16(-1, 0, 0 )).getContent(); // left
275                                    if (c != c_self && c != CONTENT_IGNORE) allow = 1;
276                                    if (!allow) {
277                                     c = map->getNodeNoEx(p - v3s16(0,  0, 1 )).getContent(); // back
278                                     if (c != c_self && c != CONTENT_IGNORE) allow = 1;
279                                     if (!allow) {
280                                      c = map->getNodeNoEx(p - v3s16(0,  0, -1)).getContent(); // front
281                                      if (c != c_self && c != CONTENT_IGNORE) allow = 1;
282                                     }
283                                    }
284                                   }
285                                  }
286                                 }
287                                 if (allow) {
288                                         n.setContent(ndef->getId(ndef->get(n).freezemelt));
289                                         map->addNodeWithEvent(p, n);
290                                 }
291                         }
292                 }
293 };
294
295 class LiquidMeltWeather : public ActiveBlockModifier {
296         public:
297                 LiquidMeltWeather(ServerEnvironment *env, INodeDefManager *nodemgr) { }
298                 virtual std::set<std::string> getTriggerContents() {
299                         std::set<std::string> s;
300                         s.insert("group:melts");
301                         return s;
302                 }
303                 virtual std::set<std::string> getRequiredNeighbors() {
304                         std::set<std::string> s;
305                         s.insert("mapgen_air");
306                         s.insert("group:freezes");
307                         return s;
308                 }
309                 virtual float getTriggerInterval()
310                 { return 10.0; }
311                 virtual u32 getTriggerChance()
312                 { return 20; }
313                 virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) {
314                         ServerMap *map = &env->getServerMap();
315                         INodeDefManager *ndef = env->getGameDef()->ndef();
316
317                         float heat = map->getHeat(env, p);
318                         if (heat >= 1 && (heat >= 40 || ((myrand_range(heat, 40)) >= 20))) {
319                                 n.setContent(ndef->getId(ndef->get(n).freezemelt));
320                                 if (!n.getLevel(ndef))
321                                         n.addLevel(ndef);
322                                 map->addNodeWithEvent(p, n);
323                                 env->getScriptIface()->node_falling_update(p);
324                         }
325                 }
326 };
327
328 class LiquidMeltHot : public ActiveBlockModifier {
329         public:
330                 LiquidMeltHot(ServerEnvironment *env, INodeDefManager *nodemgr) { }
331                 virtual std::set<std::string> getTriggerContents() {
332                         std::set<std::string> s;
333                         s.insert("group:melts");
334                         return s;
335                 }
336                 virtual std::set<std::string> getRequiredNeighbors() {
337                         std::set<std::string> s;
338                         s.insert("group:igniter");
339                         s.insert("group:hot");
340                         return s;
341                 }
342                 virtual float getTriggerInterval()
343                 { return 2.0; }
344                 virtual u32 getTriggerChance()
345                 { return 4; }
346                 virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) {
347                         ServerMap *map = &env->getServerMap();
348                         INodeDefManager *ndef = env->getGameDef()->ndef();
349                         n.setContent(ndef->getId(ndef->get(n).freezemelt));
350                         if (!n.getLevel(ndef))
351                                 n.addLevel(ndef);
352                         map->addNodeWithEvent(p, n);
353                         env->getScriptIface()->node_falling_update(p);
354                 }
355 };
356
357 class LiquidMeltAround : public LiquidMeltHot {
358         public:
359                 LiquidMeltAround(ServerEnvironment *env, INodeDefManager *nodemgr) 
360                         : LiquidMeltHot(env, nodemgr) { }
361                 virtual std::set<std::string> getRequiredNeighbors() {
362                         std::set<std::string> s;
363                         s.insert("group:melt_around");
364                         return s;
365                 }
366                 virtual float getTriggerInterval()
367                 { return 40.0; }
368                 virtual u32 getTriggerChance()
369                 { return 60; }
370 };
371
372
373 void add_legacy_abms(ServerEnvironment *env, INodeDefManager *nodedef) {
374         env->addActiveBlockModifier(new GrowGrassABM());
375         env->addActiveBlockModifier(new RemoveGrassABM());
376         env->addActiveBlockModifier(new MakeTreesFromSaplingsABM(env, nodedef));
377         if (g_settings->getBool("liquid_finite")) {
378                 env->addActiveBlockModifier(new LiquidFlowABM(env, nodedef));
379                 env->addActiveBlockModifier(new LiquidDropABM(env, nodedef));
380                 env->addActiveBlockModifier(new LiquidMeltHot(env, nodedef));
381                 env->addActiveBlockModifier(new LiquidMeltAround(env, nodedef));
382                 if (g_settings->getBool("weather")) {
383                         env->addActiveBlockModifier(new LiquidFreeze(env, nodedef));
384                         env->addActiveBlockModifier(new LiquidMeltWeather(env, nodedef));
385                 }
386         }
387 }