Make saplings only grow on dirt or grass, make jungle tree trunks only replace air
[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                 if(ndef->get(n_top).light_propagates &&
55                                 !ndef->get(n_top).isLiquid() &&
56                                 n_top.getLightBlend(env->getDayNightRatio(), ndef) >= 13)
57                 {
58                         n.setContent(ndef->getId("mapgen_dirt_with_grass"));
59                         map->addNodeWithEvent(p, n);
60                 }
61         }
62 };
63
64 class RemoveGrassABM : public ActiveBlockModifier
65 {
66 private:
67 public:
68         virtual std::set<std::string> getTriggerContents()
69         {
70                 std::set<std::string> s;
71                 s.insert("mapgen_dirt_with_grass");
72                 return s;
73         }
74         virtual float getTriggerInterval()
75         { return 2.0; }
76         virtual u32 getTriggerChance()
77         { return 20; }
78         virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n)
79         {
80                 INodeDefManager *ndef = env->getGameDef()->ndef();
81                 ServerMap *map = &env->getServerMap();
82                 
83                 MapNode n_top = map->getNodeNoEx(p+v3s16(0,1,0));
84                 if((!ndef->get(n_top).light_propagates &&
85                                 n_top.getContent() != CONTENT_IGNORE) ||
86                                 ndef->get(n_top).isLiquid())
87                 {
88                         n.setContent(ndef->getId("mapgen_dirt"));
89                         map->addNodeWithEvent(p, n);
90                 }
91         }
92 };
93
94 class MakeTreesFromSaplingsABM : public ActiveBlockModifier
95 {
96 private:
97         content_t c_junglesapling;
98         content_t c_dirt;
99         content_t c_dirt_with_grass;
100         
101 public:
102         MakeTreesFromSaplingsABM(ServerEnvironment *env, INodeDefManager *nodemgr) {
103                 c_junglesapling   = nodemgr->getId("junglesapling");
104                 c_dirt            = nodemgr->getId("mapgen_dirt");
105                 c_dirt_with_grass = nodemgr->getId("mapgen_dirt_with_grass");
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 (n_below.getContent() != c_dirt &&
127                         n_below.getContent() != c_dirt_with_grass)
128                         return;
129                         
130                 bool is_jungle_tree = n.getContent() == c_junglesapling;
131                 
132                 actionstream <<"A " << (is_jungle_tree ? "jungle " : "")
133                                 << "sapling grows into a tree at "
134                                 << PP(p) << std::endl;
135
136                 std::map<v3s16, MapBlock*> modified_blocks;
137                 v3s16 tree_p = p;
138                 ManualMapVoxelManipulator vmanip(map);
139                 v3s16 tree_blockp = getNodeBlockPos(tree_p);
140                 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
141                 
142                 if (is_jungle_tree) {
143                         treegen::make_jungletree(vmanip, tree_p, ndef, myrand());
144                 } else {
145                         bool is_apple_tree = myrand() % 4 == 0;
146                         treegen::make_tree(vmanip, tree_p, is_apple_tree, ndef, myrand());
147                 }
148                 
149                 vmanip.blitBackAll(&modified_blocks);
150
151                 // update lighting
152                 std::map<v3s16, MapBlock*> lighting_modified_blocks;
153                 lighting_modified_blocks.insert(modified_blocks.begin(), modified_blocks.end());
154                 map->updateLighting(lighting_modified_blocks, modified_blocks);
155
156                 // Send a MEET_OTHER event
157                 MapEditEvent event;
158                 event.type = MEET_OTHER;
159 //              event.modified_blocks.insert(modified_blocks.begin(), modified_blocks.end());
160                 for(std::map<v3s16, MapBlock*>::iterator
161                         i = modified_blocks.begin();
162                         i != modified_blocks.end(); ++i)
163                 {
164                         event.modified_blocks.insert(i->first);
165                 }
166                 map->dispatchEvent(&event);
167         }
168 };
169
170 class LiquidFlowABM : public ActiveBlockModifier
171 {
172 private:
173         std::set<std::string> contents;
174
175 public:
176         LiquidFlowABM(ServerEnvironment *env, INodeDefManager *nodemgr) 
177         {
178                 std::set<content_t> liquids;
179                 nodemgr->getIds("group:liquid", liquids);
180                 for(std::set<content_t>::const_iterator k = liquids.begin(); k != liquids.end(); k++)
181                         contents.insert(nodemgr->get(*k).liquid_alternative_flowing);
182                 
183         }
184         virtual std::set<std::string> getTriggerContents()
185         {
186                 return contents;
187         }
188         virtual float getTriggerInterval()
189         { return 10.0; }
190         virtual u32 getTriggerChance()
191         { return 10; }
192         virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n)
193         {
194                 ServerMap *map = &env->getServerMap();
195                 if (map->transforming_liquid_size() < 500)
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 void add_legacy_abms(ServerEnvironment *env, INodeDefManager *nodedef)
202 {
203         env->addActiveBlockModifier(new GrowGrassABM());
204         env->addActiveBlockModifier(new RemoveGrassABM());
205         env->addActiveBlockModifier(new MakeTreesFromSaplingsABM(env, nodedef));
206         if (g_settings->getBool("liquid_finite"))
207                 env->addActiveBlockModifier(new LiquidFlowABM(env, nodedef));
208 }