3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
20 #include "content_abm.h"
22 #include "environment.h"
25 #include "content_sao.h"
27 #include "mapblock.h" // For getNodeBlockPos
28 #include "treegen.h" // For treegen::make_tree
29 #include "main.h" // for g_settings
31 #include "scripting_game.h"
34 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
36 class GrowGrassABM : public ActiveBlockModifier
40 virtual std::set<std::string> getTriggerContents()
42 std::set<std::string> s;
43 s.insert("mapgen_dirt");
46 virtual float getTriggerInterval()
48 virtual u32 getTriggerChance()
50 virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n)
52 INodeDefManager *ndef = env->getGameDef()->ndef();
53 ServerMap *map = &env->getServerMap();
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)
61 if(c_snow != CONTENT_IGNORE && n_top.getContent() == c_snow)
62 n.setContent(ndef->getId("dirt_with_snow"));
64 n.setContent(ndef->getId("mapgen_dirt_with_grass"));
65 map->addNodeWithEvent(p, n);
70 class RemoveGrassABM : public ActiveBlockModifier
74 virtual std::set<std::string> getTriggerContents()
76 std::set<std::string> s;
77 s.insert("mapgen_dirt_with_grass");
80 virtual float getTriggerInterval()
82 virtual u32 getTriggerChance()
84 virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n)
86 INodeDefManager *ndef = env->getGameDef()->ndef();
87 ServerMap *map = &env->getServerMap();
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())
94 n.setContent(ndef->getId("mapgen_dirt"));
95 map->addNodeWithEvent(p, n);
100 class MakeTreesFromSaplingsABM : public ActiveBlockModifier
103 content_t c_junglesapling;
106 MakeTreesFromSaplingsABM(ServerEnvironment *env, INodeDefManager *nodemgr) {
107 c_junglesapling = nodemgr->getId("junglesapling");
110 virtual std::set<std::string> getTriggerContents()
112 std::set<std::string> s;
114 s.insert("junglesapling");
117 virtual float getTriggerInterval()
119 virtual u32 getTriggerChance()
121 virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n,
122 u32 active_object_count, u32 active_object_count_wider)
124 INodeDefManager *ndef = env->getGameDef()->ndef();
125 ServerMap *map = &env->getServerMap();
127 MapNode n_below = map->getNodeNoEx(p - v3s16(0, 1, 0));
128 if (!((ItemGroupList) ndef->get(n_below).groups)["soil"])
131 bool is_jungle_tree = n.getContent() == c_junglesapling;
133 actionstream <<"A " << (is_jungle_tree ? "jungle " : "")
134 << "sapling grows into a tree at "
135 << PP(p) << std::endl;
137 std::map<v3s16, MapBlock*> modified_blocks;
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));
143 if (is_jungle_tree) {
144 treegen::make_jungletree(vmanip, tree_p, ndef, myrand());
146 bool is_apple_tree = myrand() % 4 == 0;
147 treegen::make_tree(vmanip, tree_p, is_apple_tree, ndef, myrand());
150 vmanip.blitBackAll(&modified_blocks);
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);
157 // Send a MEET_OTHER 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)
165 event.modified_blocks.insert(i->first);
167 map->dispatchEvent(&event);
171 class LiquidFlowABM : public ActiveBlockModifier {
173 std::set<std::string> contents;
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);
182 virtual std::set<std::string> getTriggerContents() {
185 virtual float getTriggerInterval()
187 virtual u32 getTriggerChance()
189 virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) {
190 ServerMap *map = &env->getServerMap();
191 if (map->transforming_liquid_size() > 500)
193 map->transforming_liquid_add(p);
197 class LiquidDropABM : public ActiveBlockModifier {
199 std::set<std::string> contents;
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);
208 virtual std::set<std::string> getTriggerContents()
210 virtual std::set<std::string> getRequiredNeighbors() {
211 std::set<std::string> neighbors;
212 neighbors.insert("mapgen_air");
215 virtual float getTriggerInterval()
217 virtual u32 getTriggerChance()
219 virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) {
220 ServerMap *map = &env->getServerMap();
221 if (map->transforming_liquid_size() > 500)
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
230 map->transforming_liquid_add(p);
234 class LiquidFreeze : public ActiveBlockModifier {
236 LiquidFreeze(ServerEnvironment *env, INodeDefManager *nodemgr) { }
237 virtual std::set<std::string> getTriggerContents() {
238 std::set<std::string> s;
239 s.insert("group:freezes");
242 virtual std::set<std::string> getRequiredNeighbors() {
243 std::set<std::string> s;
244 s.insert("mapgen_air");
245 s.insert("group:melts");
248 virtual float getTriggerInterval()
250 virtual u32 getTriggerChance()
252 virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) {
253 ServerMap *map = &env->getServerMap();
254 INodeDefManager *ndef = env->getGameDef()->ndef();
256 float heat = map->updateBlockHeat(env, p);
258 content_t c = map->getNodeNoEx(p - v3s16(0, -1, 0 )).getContent(); // top
259 //more chance to freeze if air at top
260 if (heat <= -1 && (heat <= -50 || (myrand_range(-50, heat) <= (c == CONTENT_AIR ? -10 : -40)))) {
261 content_t c_self = n.getContent();
262 // making freeze not annoying, do not freeze random blocks in center of ocean
263 // todo: any block not water (dont freeze _source near _flowing)
264 bool allow = heat < -40;
265 // todo: make for(...)
267 c = map->getNodeNoEx(p - v3s16(0, 1, 0 )).getContent(); // below
268 if (c == CONTENT_AIR || c == CONTENT_IGNORE)
269 return; // do not freeze when falling
270 if (c != c_self && c != CONTENT_IGNORE) allow = 1;
272 c = map->getNodeNoEx(p - v3s16(1, 0, 0 )).getContent(); // right
273 if (c != c_self && c != CONTENT_IGNORE) allow = 1;
275 c = map->getNodeNoEx(p - v3s16(-1, 0, 0 )).getContent(); // left
276 if (c != c_self && c != CONTENT_IGNORE) allow = 1;
278 c = map->getNodeNoEx(p - v3s16(0, 0, 1 )).getContent(); // back
279 if (c != c_self && c != CONTENT_IGNORE) allow = 1;
281 c = map->getNodeNoEx(p - v3s16(0, 0, -1)).getContent(); // front
282 if (c != c_self && c != CONTENT_IGNORE) allow = 1;
290 map->addNodeWithEvent(p, n);
296 class LiquidMeltWeather : public ActiveBlockModifier {
298 LiquidMeltWeather(ServerEnvironment *env, INodeDefManager *nodemgr) { }
299 virtual std::set<std::string> getTriggerContents() {
300 std::set<std::string> s;
301 s.insert("group:melts");
304 virtual std::set<std::string> getRequiredNeighbors() {
305 std::set<std::string> s;
306 s.insert("mapgen_air");
307 s.insert("group:freezes");
310 virtual float getTriggerInterval()
312 virtual u32 getTriggerChance()
314 virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) {
315 ServerMap *map = &env->getServerMap();
316 INodeDefManager *ndef = env->getGameDef()->ndef();
318 float heat = map->updateBlockHeat(env, p);
319 content_t c = map->getNodeNoEx(p - v3s16(0, -1, 0 )).getContent(); // top
320 if (heat >= 1 && (heat >= 40 || ((myrand_range(heat, 40)) >= (c == CONTENT_AIR ? 10 : 20)))) {
322 map->addNodeWithEvent(p, n);
323 env->getScriptIface()->node_falling_update(p);
328 class LiquidMeltHot : public ActiveBlockModifier {
330 LiquidMeltHot(ServerEnvironment *env, INodeDefManager *nodemgr) { }
331 virtual std::set<std::string> getTriggerContents() {
332 std::set<std::string> s;
333 s.insert("group:melts");
336 virtual std::set<std::string> getRequiredNeighbors() {
337 std::set<std::string> s;
338 s.insert("group:igniter");
339 s.insert("group:hot");
342 virtual float getTriggerInterval()
344 virtual u32 getTriggerChance()
346 virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) {
347 ServerMap *map = &env->getServerMap();
348 INodeDefManager *ndef = env->getGameDef()->ndef();
350 map->addNodeWithEvent(p, n);
351 env->getScriptIface()->node_falling_update(p);
355 /* too buggy, later via liquid flow code
356 class LiquidMeltAround : public LiquidMeltHot {
358 LiquidMeltAround(ServerEnvironment *env, INodeDefManager *nodemgr)
359 : LiquidMeltHot(env, nodemgr) { }
360 virtual std::set<std::string> getRequiredNeighbors() {
361 std::set<std::string> s;
362 s.insert("group:melt_around");
365 virtual float getTriggerInterval()
367 virtual u32 getTriggerChance()
372 void add_legacy_abms(ServerEnvironment *env, INodeDefManager *nodedef) {
373 env->addActiveBlockModifier(new GrowGrassABM());
374 env->addActiveBlockModifier(new RemoveGrassABM());
375 env->addActiveBlockModifier(new MakeTreesFromSaplingsABM(env, nodedef));
376 if (g_settings->getBool("liquid_finite")) {
377 env->addActiveBlockModifier(new LiquidFlowABM(env, nodedef));
378 env->addActiveBlockModifier(new LiquidDropABM(env, nodedef));
379 env->addActiveBlockModifier(new LiquidMeltHot(env, nodedef));
380 //env->addActiveBlockModifier(new LiquidMeltAround(env, nodedef));
381 if (env->m_use_weather) {
382 env->addActiveBlockModifier(new LiquidFreeze(env, nodedef));
383 env->addActiveBlockModifier(new LiquidMeltWeather(env, nodedef));