6adcbf708ef94c328957654216c2fb6fb3da6526
[oweals/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
32 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
33
34 class GrowGrassABM : public ActiveBlockModifier
35 {
36 private:
37 public:
38         virtual std::set<std::string> getTriggerContents()
39         {
40                 std::set<std::string> s;
41                 s.insert("mapgen_dirt");
42                 return s;
43         }
44         virtual float getTriggerInterval()
45         { return 2.0; }
46         virtual u32 getTriggerChance()
47         { return 200; }
48         virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n)
49         {
50                 INodeDefManager *ndef = env->getGameDef()->ndef();
51                 ServerMap *map = &env->getServerMap();
52                 
53                 MapNode n_top = map->getNodeNoEx(p+v3s16(0,1,0));
54                 content_t c_snow = ndef->getId("snow");
55                 if(ndef->get(n_top).light_propagates &&
56                                 !ndef->get(n_top).isLiquid() &&
57                                 n_top.getLightBlend(env->getDayNightRatio(), ndef) >= 13)
58                 {
59                         if(c_snow != CONTENT_IGNORE && n_top.getContent() == c_snow)
60                                 n.setContent(ndef->getId("dirt_with_snow"));
61                         else
62                                 n.setContent(ndef->getId("mapgen_dirt_with_grass"));
63                         map->addNodeWithEvent(p, n);
64                 }
65         }
66 };
67
68 class RemoveGrassABM : public ActiveBlockModifier
69 {
70 private:
71 public:
72         virtual std::set<std::string> getTriggerContents()
73         {
74                 std::set<std::string> s;
75                 s.insert("mapgen_dirt_with_grass");
76                 return s;
77         }
78         virtual float getTriggerInterval()
79         { return 2.0; }
80         virtual u32 getTriggerChance()
81         { return 20; }
82         virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n)
83         {
84                 INodeDefManager *ndef = env->getGameDef()->ndef();
85                 ServerMap *map = &env->getServerMap();
86                 
87                 MapNode n_top = map->getNodeNoEx(p+v3s16(0,1,0));
88                 if((!ndef->get(n_top).light_propagates &&
89                                 n_top.getContent() != CONTENT_IGNORE) ||
90                                 ndef->get(n_top).isLiquid())
91                 {
92                         n.setContent(ndef->getId("mapgen_dirt"));
93                         map->addNodeWithEvent(p, n);
94                 }
95         }
96 };
97
98 class MakeTreesFromSaplingsABM : public ActiveBlockModifier
99 {
100 private:
101         content_t c_junglesapling;
102         
103 public:
104         MakeTreesFromSaplingsABM(ServerEnvironment *env, INodeDefManager *nodemgr) {
105                 c_junglesapling = nodemgr->getId("junglesapling");
106         }
107
108         virtual std::set<std::string> getTriggerContents()
109         {
110                 std::set<std::string> s;
111                 s.insert("sapling");
112                 s.insert("junglesapling");
113                 return s;
114         }
115         virtual float getTriggerInterval()
116         { return 10.0; }
117         virtual u32 getTriggerChance()
118         { return 50; }
119         virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n,
120                         u32 active_object_count, u32 active_object_count_wider)
121         {
122                 INodeDefManager *ndef = env->getGameDef()->ndef();
123                 ServerMap *map = &env->getServerMap();
124                 
125                 MapNode n_below = map->getNodeNoEx(p - v3s16(0, 1, 0));
126                 if (!((ItemGroupList) ndef->get(n_below).groups)["soil"])
127                         return;
128                         
129                 bool is_jungle_tree = n.getContent() == c_junglesapling;
130                 
131                 actionstream <<"A " << (is_jungle_tree ? "jungle " : "")
132                                 << "sapling grows into a tree at "
133                                 << PP(p) << std::endl;
134
135                 std::map<v3s16, MapBlock*> modified_blocks;
136                 v3s16 tree_p = p;
137                 ManualMapVoxelManipulator vmanip(map);
138                 v3s16 tree_blockp = getNodeBlockPos(tree_p);
139                 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
140                 
141                 if (is_jungle_tree) {
142                         treegen::make_jungletree(vmanip, tree_p, ndef, myrand());
143                 } else {
144                         bool is_apple_tree = myrand() % 4 == 0;
145                         treegen::make_tree(vmanip, tree_p, is_apple_tree, ndef, myrand());
146                 }
147                 
148                 vmanip.blitBackAll(&modified_blocks);
149
150                 // update lighting
151                 std::map<v3s16, MapBlock*> lighting_modified_blocks;
152                 lighting_modified_blocks.insert(modified_blocks.begin(), modified_blocks.end());
153                 map->updateLighting(lighting_modified_blocks, modified_blocks);
154
155                 // Send a MEET_OTHER event
156                 MapEditEvent event;
157                 event.type = MEET_OTHER;
158 //              event.modified_blocks.insert(modified_blocks.begin(), modified_blocks.end());
159                 for(std::map<v3s16, MapBlock*>::iterator
160                         i = modified_blocks.begin();
161                         i != modified_blocks.end(); ++i)
162                 {
163                         event.modified_blocks.insert(i->first);
164                 }
165                 map->dispatchEvent(&event);
166         }
167 };
168
169 class LiquidFlowABM : public ActiveBlockModifier
170 {
171 private:
172         std::set<std::string> contents;
173
174 public:
175         LiquidFlowABM(ServerEnvironment *env, INodeDefManager *nodemgr) 
176         {
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         }
183         virtual std::set<std::string> getTriggerContents()
184         {
185                 return contents;
186         }
187         virtual float getTriggerInterval()
188         { return 10.0; }
189         virtual u32 getTriggerChance()
190         { return 10; }
191         virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n)
192         {
193                 ServerMap *map = &env->getServerMap();
194                 if (map->transforming_liquid_size() > 500)
195                         return;
196                 map->transforming_liquid_add(p);
197                 //if ((*map).m_transforming_liquid.size() < 500) (*map).m_transforming_liquid.push_back(p);
198         }
199 };
200
201 class LiquidDropABM : public ActiveBlockModifier
202 {
203 private:
204         std::set<std::string> contents;
205
206 public:
207         LiquidDropABM(ServerEnvironment *env, INodeDefManager *nodemgr) 
208         {
209                 std::set<content_t> liquids;
210                 nodemgr->getIds("group:liquid", liquids);
211                 for(std::set<content_t>::const_iterator k = liquids.begin(); k != liquids.end(); k++)
212                         contents.insert(nodemgr->get(*k).liquid_alternative_source);
213         }
214         virtual std::set<std::string> getTriggerContents()
215         { return contents; }
216         virtual std::set<std::string> getRequiredNeighbors()
217         {
218                 std::set<std::string> neighbors;
219                 neighbors.insert("mapgen_air");
220                 return neighbors; 
221         }
222         virtual float getTriggerInterval()
223         { return 20.0; }
224         virtual u32 getTriggerChance()
225         { return 10; }
226         virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) 
227         {
228                 ServerMap *map = &env->getServerMap();
229                 if (map->transforming_liquid_size() > 500)
230                         return;
231                 if (       map->getNodeNoEx(p - v3s16(0,  1, 0 )).getContent() != CONTENT_AIR  // below
232                         && map->getNodeNoEx(p - v3s16(1,  0, 0 )).getContent() != CONTENT_AIR  // right
233                         && map->getNodeNoEx(p - v3s16(-1, 0, 0 )).getContent() != CONTENT_AIR  // left
234                         && map->getNodeNoEx(p - v3s16(0,  0, 1 )).getContent() != CONTENT_AIR  // back 
235                         && map->getNodeNoEx(p - v3s16(0,  0, -1)).getContent() != CONTENT_AIR  // front
236                 )
237                         return;
238                 map->transforming_liquid_add(p);
239         }
240 };
241
242 void add_legacy_abms(ServerEnvironment *env, INodeDefManager *nodedef)
243 {
244         env->addActiveBlockModifier(new GrowGrassABM());
245         env->addActiveBlockModifier(new RemoveGrassABM());
246         env->addActiveBlockModifier(new MakeTreesFromSaplingsABM(env, nodedef));
247         if (g_settings->getBool("liquid_finite")) {
248                 env->addActiveBlockModifier(new LiquidFlowABM(env, nodedef));
249                 env->addActiveBlockModifier(new LiquidDropABM(env, nodedef));
250         }
251 }