]> git.lizzy.rs Git - dragonfireclient.git/blob - src/mapnode.cpp
Merge pull request #35 from arydevy/patch-1
[dragonfireclient.git] / src / mapnode.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 "irrlichttypes_extrabloated.h"
21 #include "mapnode.h"
22 #include "porting.h"
23 #include "nodedef.h"
24 #include "map.h"
25 #include "content_mapnode.h" // For mapnode_translate_*_internal
26 #include "serialization.h" // For ser_ver_supported
27 #include "util/serialize.h"
28 #include "log.h"
29 #include "util/directiontables.h"
30 #include "util/numeric.h"
31 #include <string>
32 #include <sstream>
33
34 static const Rotation wallmounted_to_rot[] = {
35         ROTATE_0, ROTATE_180, ROTATE_90, ROTATE_270
36 };
37
38 static const u8 rot_to_wallmounted[] = {
39         2, 4, 3, 5
40 };
41
42
43 /*
44         MapNode
45 */
46
47 void MapNode::getColor(const ContentFeatures &f, video::SColor *color) const
48 {
49         if (f.palette) {
50                 *color = (*f.palette)[param2];
51                 return;
52         }
53         *color = f.color;
54 }
55
56 void MapNode::setLight(LightBank bank, u8 a_light, const ContentFeatures &f) noexcept
57 {
58         // If node doesn't contain light data, ignore this
59         if(f.param_type != CPT_LIGHT)
60                 return;
61         if(bank == LIGHTBANK_DAY)
62         {
63                 param1 &= 0xf0;
64                 param1 |= a_light & 0x0f;
65         }
66         else if(bank == LIGHTBANK_NIGHT)
67         {
68                 param1 &= 0x0f;
69                 param1 |= (a_light & 0x0f)<<4;
70         }
71         else
72                 assert("Invalid light bank" == NULL);
73 }
74
75 void MapNode::setLight(LightBank bank, u8 a_light, const NodeDefManager *nodemgr)
76 {
77         setLight(bank, a_light, nodemgr->get(*this));
78 }
79
80 bool MapNode::isLightDayNightEq(const NodeDefManager *nodemgr) const
81 {
82         const ContentFeatures &f = nodemgr->get(*this);
83         bool isEqual;
84
85         if (f.param_type == CPT_LIGHT) {
86                 u8 day   = MYMAX(f.light_source, param1 & 0x0f);
87                 u8 night = MYMAX(f.light_source, (param1 >> 4) & 0x0f);
88                 isEqual = day == night;
89         } else {
90                 isEqual = true;
91         }
92
93         return isEqual;
94 }
95
96 u8 MapNode::getLight(LightBank bank, const NodeDefManager *nodemgr) const
97 {
98         // Select the brightest of [light source, propagated light]
99         const ContentFeatures &f = nodemgr->get(*this);
100
101         u8 light;
102         if(f.param_type == CPT_LIGHT)
103                 light = bank == LIGHTBANK_DAY ? param1 & 0x0f : (param1 >> 4) & 0x0f;
104         else
105                 light = 0;
106
107         return MYMAX(f.light_source, light);
108 }
109
110 u8 MapNode::getLightRaw(LightBank bank, const ContentFeatures &f) const noexcept
111 {
112         if(f.param_type == CPT_LIGHT)
113                 return bank == LIGHTBANK_DAY ? param1 & 0x0f : (param1 >> 4) & 0x0f;
114         return 0;
115 }
116
117 u8 MapNode::getLightNoChecks(LightBank bank, const ContentFeatures *f) const noexcept
118 {
119         return MYMAX(f->light_source,
120                      bank == LIGHTBANK_DAY ? param1 & 0x0f : (param1 >> 4) & 0x0f);
121 }
122
123 bool MapNode::getLightBanks(u8 &lightday, u8 &lightnight,
124         const NodeDefManager *nodemgr) const
125 {
126         // Select the brightest of [light source, propagated light]
127         const ContentFeatures &f = nodemgr->get(*this);
128         if(f.param_type == CPT_LIGHT)
129         {
130                 lightday = param1 & 0x0f;
131                 lightnight = (param1>>4)&0x0f;
132         }
133         else
134         {
135                 lightday = 0;
136                 lightnight = 0;
137         }
138         if(f.light_source > lightday)
139                 lightday = f.light_source;
140         if(f.light_source > lightnight)
141                 lightnight = f.light_source;
142         return f.param_type == CPT_LIGHT || f.light_source != 0;
143 }
144
145 u8 MapNode::getFaceDir(const NodeDefManager *nodemgr,
146         bool allow_wallmounted) const
147 {
148         const ContentFeatures &f = nodemgr->get(*this);
149         if (f.param_type_2 == CPT2_FACEDIR ||
150                         f.param_type_2 == CPT2_COLORED_FACEDIR)
151                 return (getParam2() & 0x1F) % 24;
152         if (allow_wallmounted && (f.param_type_2 == CPT2_WALLMOUNTED ||
153                         f.param_type_2 == CPT2_COLORED_WALLMOUNTED))
154                 return wallmounted_to_facedir[getParam2() & 0x07];
155         return 0;
156 }
157
158 u8 MapNode::getWallMounted(const NodeDefManager *nodemgr) const
159 {
160         const ContentFeatures &f = nodemgr->get(*this);
161         if (f.param_type_2 == CPT2_WALLMOUNTED ||
162                         f.param_type_2 == CPT2_COLORED_WALLMOUNTED) {
163                 return getParam2() & 0x07;
164         } else if (f.drawtype == NDT_SIGNLIKE || f.drawtype == NDT_TORCHLIKE) {
165                 return 1;
166         }
167         return 0;
168 }
169
170 v3s16 MapNode::getWallMountedDir(const NodeDefManager *nodemgr) const
171 {
172         switch(getWallMounted(nodemgr))
173         {
174         case 0: default: return v3s16(0,1,0);
175         case 1: return v3s16(0,-1,0);
176         case 2: return v3s16(1,0,0);
177         case 3: return v3s16(-1,0,0);
178         case 4: return v3s16(0,0,1);
179         case 5: return v3s16(0,0,-1);
180         }
181 }
182
183 u8 MapNode::getDegRotate(const NodeDefManager *nodemgr) const
184 {
185         const ContentFeatures &f = nodemgr->get(*this);
186         if (f.param_type_2 == CPT2_DEGROTATE)
187                 return getParam2() % 240;
188         if (f.param_type_2 == CPT2_COLORED_DEGROTATE)
189                 return 10 * ((getParam2() & 0x1F) % 24);
190         return 0;
191 }
192
193 void MapNode::rotateAlongYAxis(const NodeDefManager *nodemgr, Rotation rot)
194 {
195         ContentParamType2 cpt2 = nodemgr->get(*this).param_type_2;
196
197         if (cpt2 == CPT2_FACEDIR || cpt2 == CPT2_COLORED_FACEDIR) {
198                 static const u8 rotate_facedir[24 * 4] = {
199                         // Table value = rotated facedir
200                         // Columns: 0, 90, 180, 270 degrees rotation around vertical axis
201                         // Rotation is anticlockwise as seen from above (+Y)
202
203                         0, 1, 2, 3,  // Initial facedir 0 to 3
204                         1, 2, 3, 0,
205                         2, 3, 0, 1,
206                         3, 0, 1, 2,
207
208                         4, 13, 10, 19,  // 4 to 7
209                         5, 14, 11, 16,
210                         6, 15, 8, 17,
211                         7, 12, 9, 18,
212
213                         8, 17, 6, 15,  // 8 to 11
214                         9, 18, 7, 12,
215                         10, 19, 4, 13,
216                         11, 16, 5, 14,
217
218                         12, 9, 18, 7,  // 12 to 15
219                         13, 10, 19, 4,
220                         14, 11, 16, 5,
221                         15, 8, 17, 6,
222
223                         16, 5, 14, 11,  // 16 to 19
224                         17, 6, 15, 8,
225                         18, 7, 12, 9,
226                         19, 4, 13, 10,
227
228                         20, 23, 22, 21,  // 20 to 23
229                         21, 20, 23, 22,
230                         22, 21, 20, 23,
231                         23, 22, 21, 20
232                 };
233                 u8 facedir = (param2 & 31) % 24;
234                 u8 index = facedir * 4 + rot;
235                 param2 &= ~31;
236                 param2 |= rotate_facedir[index];
237         } else if (cpt2 == CPT2_WALLMOUNTED ||
238                         cpt2 == CPT2_COLORED_WALLMOUNTED) {
239                 u8 wmountface = (param2 & 7);
240                 if (wmountface <= 1)
241                         return;
242
243                 Rotation oldrot = wallmounted_to_rot[wmountface - 2];
244                 param2 &= ~7;
245                 param2 |= rot_to_wallmounted[(oldrot - rot) & 3];
246         } else if (cpt2 == CPT2_DEGROTATE) {
247                 int angle = param2; // in 1.5°
248                 angle += 60 * rot; // don’t do that on u8
249                 angle %= 240;
250                 param2 = angle;
251         } else if (cpt2 == CPT2_COLORED_DEGROTATE) {
252                 int angle = param2 & 0x1F; // in 15°
253                 int color = param2 & 0xE0;
254                 angle += 6 * rot;
255                 angle %= 24;
256                 param2 = color | angle;
257         }
258 }
259
260 void transformNodeBox(const MapNode &n, const NodeBox &nodebox,
261         const NodeDefManager *nodemgr, std::vector<aabb3f> *p_boxes,
262         u8 neighbors = 0)
263 {
264         std::vector<aabb3f> &boxes = *p_boxes;
265
266         if (nodebox.type == NODEBOX_FIXED || nodebox.type == NODEBOX_LEVELED) {
267                 const std::vector<aabb3f> &fixed = nodebox.fixed;
268                 int facedir = n.getFaceDir(nodemgr, true);
269                 u8 axisdir = facedir>>2;
270                 facedir&=0x03;
271                 for (aabb3f box : fixed) {
272                         if (nodebox.type == NODEBOX_LEVELED)
273                                 box.MaxEdge.Y = (-0.5f + n.getLevel(nodemgr) / 64.0f) * BS;
274
275                         switch (axisdir) {
276                         case 0:
277                                 if(facedir == 1)
278                                 {
279                                         box.MinEdge.rotateXZBy(-90);
280                                         box.MaxEdge.rotateXZBy(-90);
281                                 }
282                                 else if(facedir == 2)
283                                 {
284                                         box.MinEdge.rotateXZBy(180);
285                                         box.MaxEdge.rotateXZBy(180);
286                                 }
287                                 else if(facedir == 3)
288                                 {
289                                         box.MinEdge.rotateXZBy(90);
290                                         box.MaxEdge.rotateXZBy(90);
291                                 }
292                                 break;
293                         case 1: // z+
294                                 box.MinEdge.rotateYZBy(90);
295                                 box.MaxEdge.rotateYZBy(90);
296                                 if(facedir == 1)
297                                 {
298                                         box.MinEdge.rotateXYBy(90);
299                                         box.MaxEdge.rotateXYBy(90);
300                                 }
301                                 else if(facedir == 2)
302                                 {
303                                         box.MinEdge.rotateXYBy(180);
304                                         box.MaxEdge.rotateXYBy(180);
305                                 }
306                                 else if(facedir == 3)
307                                 {
308                                         box.MinEdge.rotateXYBy(-90);
309                                         box.MaxEdge.rotateXYBy(-90);
310                                 }
311                                 break;
312                         case 2: //z-
313                                 box.MinEdge.rotateYZBy(-90);
314                                 box.MaxEdge.rotateYZBy(-90);
315                                 if(facedir == 1)
316                                 {
317                                         box.MinEdge.rotateXYBy(-90);
318                                         box.MaxEdge.rotateXYBy(-90);
319                                 }
320                                 else if(facedir == 2)
321                                 {
322                                         box.MinEdge.rotateXYBy(180);
323                                         box.MaxEdge.rotateXYBy(180);
324                                 }
325                                 else if(facedir == 3)
326                                 {
327                                         box.MinEdge.rotateXYBy(90);
328                                         box.MaxEdge.rotateXYBy(90);
329                                 }
330                                 break;
331                         case 3:  //x+
332                                 box.MinEdge.rotateXYBy(-90);
333                                 box.MaxEdge.rotateXYBy(-90);
334                                 if(facedir == 1)
335                                 {
336                                         box.MinEdge.rotateYZBy(90);
337                                         box.MaxEdge.rotateYZBy(90);
338                                 }
339                                 else if(facedir == 2)
340                                 {
341                                         box.MinEdge.rotateYZBy(180);
342                                         box.MaxEdge.rotateYZBy(180);
343                                 }
344                                 else if(facedir == 3)
345                                 {
346                                         box.MinEdge.rotateYZBy(-90);
347                                         box.MaxEdge.rotateYZBy(-90);
348                                 }
349                                 break;
350                         case 4:  //x-
351                                 box.MinEdge.rotateXYBy(90);
352                                 box.MaxEdge.rotateXYBy(90);
353                                 if(facedir == 1)
354                                 {
355                                         box.MinEdge.rotateYZBy(-90);
356                                         box.MaxEdge.rotateYZBy(-90);
357                                 }
358                                 else if(facedir == 2)
359                                 {
360                                         box.MinEdge.rotateYZBy(180);
361                                         box.MaxEdge.rotateYZBy(180);
362                                 }
363                                 else if(facedir == 3)
364                                 {
365                                         box.MinEdge.rotateYZBy(90);
366                                         box.MaxEdge.rotateYZBy(90);
367                                 }
368                                 break;
369                         case 5:
370                                 box.MinEdge.rotateXYBy(-180);
371                                 box.MaxEdge.rotateXYBy(-180);
372                                 if(facedir == 1)
373                                 {
374                                         box.MinEdge.rotateXZBy(90);
375                                         box.MaxEdge.rotateXZBy(90);
376                                 }
377                                 else if(facedir == 2)
378                                 {
379                                         box.MinEdge.rotateXZBy(180);
380                                         box.MaxEdge.rotateXZBy(180);
381                                 }
382                                 else if(facedir == 3)
383                                 {
384                                         box.MinEdge.rotateXZBy(-90);
385                                         box.MaxEdge.rotateXZBy(-90);
386                                 }
387                                 break;
388                         default:
389                                 break;
390                         }
391                         box.repair();
392                         boxes.push_back(box);
393                 }
394         }
395         else if(nodebox.type == NODEBOX_WALLMOUNTED)
396         {
397                 v3s16 dir = n.getWallMountedDir(nodemgr);
398
399                 // top
400                 if(dir == v3s16(0,1,0))
401                 {
402                         boxes.push_back(nodebox.wall_top);
403                 }
404                 // bottom
405                 else if(dir == v3s16(0,-1,0))
406                 {
407                         boxes.push_back(nodebox.wall_bottom);
408                 }
409                 // side
410                 else
411                 {
412                         v3f vertices[2] =
413                         {
414                                 nodebox.wall_side.MinEdge,
415                                 nodebox.wall_side.MaxEdge
416                         };
417
418                         for (v3f &vertex : vertices) {
419                                 if(dir == v3s16(-1,0,0))
420                                         vertex.rotateXZBy(0);
421                                 if(dir == v3s16(1,0,0))
422                                         vertex.rotateXZBy(180);
423                                 if(dir == v3s16(0,0,-1))
424                                         vertex.rotateXZBy(90);
425                                 if(dir == v3s16(0,0,1))
426                                         vertex.rotateXZBy(-90);
427                         }
428
429                         aabb3f box = aabb3f(vertices[0]);
430                         box.addInternalPoint(vertices[1]);
431                         boxes.push_back(box);
432                 }
433         }
434         else if (nodebox.type == NODEBOX_CONNECTED)
435         {
436                 size_t boxes_size = boxes.size();
437                 boxes_size += nodebox.fixed.size();
438                 if (neighbors & 1)
439                         boxes_size += nodebox.connect_top.size();
440                 else
441                         boxes_size += nodebox.disconnected_top.size();
442
443                 if (neighbors & 2)
444                         boxes_size += nodebox.connect_bottom.size();
445                 else
446                         boxes_size += nodebox.disconnected_bottom.size();
447
448                 if (neighbors & 4)
449                         boxes_size += nodebox.connect_front.size();
450                 else
451                         boxes_size += nodebox.disconnected_front.size();
452
453                 if (neighbors & 8)
454                         boxes_size += nodebox.connect_left.size();
455                 else
456                         boxes_size += nodebox.disconnected_left.size();
457
458                 if (neighbors & 16)
459                         boxes_size += nodebox.connect_back.size();
460                 else
461                         boxes_size += nodebox.disconnected_back.size();
462
463                 if (neighbors & 32)
464                         boxes_size += nodebox.connect_right.size();
465                 else
466                         boxes_size += nodebox.disconnected_right.size();
467
468                 if (neighbors == 0)
469                         boxes_size += nodebox.disconnected.size();
470
471                 if (neighbors < 4)
472                         boxes_size += nodebox.disconnected_sides.size();
473
474                 boxes.reserve(boxes_size);
475
476 #define BOXESPUSHBACK(c) \
477                 for (std::vector<aabb3f>::const_iterator \
478                                 it = (c).begin(); \
479                                 it != (c).end(); ++it) \
480                         (boxes).push_back(*it);
481
482                 BOXESPUSHBACK(nodebox.fixed);
483
484                 if (neighbors & 1) {
485                         BOXESPUSHBACK(nodebox.connect_top);
486                 } else {
487                         BOXESPUSHBACK(nodebox.disconnected_top);
488                 }
489
490                 if (neighbors & 2) {
491                         BOXESPUSHBACK(nodebox.connect_bottom);
492                 } else {
493                         BOXESPUSHBACK(nodebox.disconnected_bottom);
494                 }
495
496                 if (neighbors & 4) {
497                         BOXESPUSHBACK(nodebox.connect_front);
498                 } else {
499                         BOXESPUSHBACK(nodebox.disconnected_front);
500                 }
501
502                 if (neighbors & 8) {
503                         BOXESPUSHBACK(nodebox.connect_left);
504                 } else {
505                         BOXESPUSHBACK(nodebox.disconnected_left);
506                 }
507
508                 if (neighbors & 16) {
509                         BOXESPUSHBACK(nodebox.connect_back);
510                 } else {
511                         BOXESPUSHBACK(nodebox.disconnected_back);
512                 }
513
514                 if (neighbors & 32) {
515                         BOXESPUSHBACK(nodebox.connect_right);
516                 } else {
517                         BOXESPUSHBACK(nodebox.disconnected_right);
518                 }
519
520                 if (neighbors == 0) {
521                         BOXESPUSHBACK(nodebox.disconnected);
522                 }
523
524                 if (neighbors < 4) {
525                         BOXESPUSHBACK(nodebox.disconnected_sides);
526                 }
527
528         }
529         else // NODEBOX_REGULAR
530         {
531                 boxes.emplace_back(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2);
532         }
533 }
534
535 static inline void getNeighborConnectingFace(
536         const v3s16 &p, const NodeDefManager *nodedef,
537         Map *map, MapNode n, u8 bitmask, u8 *neighbors)
538 {
539         MapNode n2 = map->getNode(p);
540         if (nodedef->nodeboxConnects(n, n2, bitmask))
541                 *neighbors |= bitmask;
542 }
543
544 u8 MapNode::getNeighbors(v3s16 p, Map *map) const
545 {
546         const NodeDefManager *nodedef = map->getNodeDefManager();
547         u8 neighbors = 0;
548         const ContentFeatures &f = nodedef->get(*this);
549         // locate possible neighboring nodes to connect to
550         if (f.drawtype == NDT_NODEBOX && f.node_box.type == NODEBOX_CONNECTED) {
551                 v3s16 p2 = p;
552
553                 p2.Y++;
554                 getNeighborConnectingFace(p2, nodedef, map, *this, 1, &neighbors);
555
556                 p2 = p;
557                 p2.Y--;
558                 getNeighborConnectingFace(p2, nodedef, map, *this, 2, &neighbors);
559
560                 p2 = p;
561                 p2.Z--;
562                 getNeighborConnectingFace(p2, nodedef, map, *this, 4, &neighbors);
563
564                 p2 = p;
565                 p2.X--;
566                 getNeighborConnectingFace(p2, nodedef, map, *this, 8, &neighbors);
567
568                 p2 = p;
569                 p2.Z++;
570                 getNeighborConnectingFace(p2, nodedef, map, *this, 16, &neighbors);
571
572                 p2 = p;
573                 p2.X++;
574                 getNeighborConnectingFace(p2, nodedef, map, *this, 32, &neighbors);
575         }
576
577         return neighbors;
578 }
579
580 void MapNode::getNodeBoxes(const NodeDefManager *nodemgr,
581         std::vector<aabb3f> *boxes, u8 neighbors) const
582 {
583         const ContentFeatures &f = nodemgr->get(*this);
584         transformNodeBox(*this, f.node_box, nodemgr, boxes, neighbors);
585 }
586
587 void MapNode::getCollisionBoxes(const NodeDefManager *nodemgr,
588         std::vector<aabb3f> *boxes, u8 neighbors) const
589 {
590         const ContentFeatures &f = nodemgr->get(*this);
591         if (f.collision_box.fixed.empty())
592                 transformNodeBox(*this, f.node_box, nodemgr, boxes, neighbors);
593         else
594                 transformNodeBox(*this, f.collision_box, nodemgr, boxes, neighbors);
595 }
596
597 void MapNode::getSelectionBoxes(const NodeDefManager *nodemgr,
598         std::vector<aabb3f> *boxes, u8 neighbors) const
599 {
600         const ContentFeatures &f = nodemgr->get(*this);
601         transformNodeBox(*this, f.selection_box, nodemgr, boxes, neighbors);
602 }
603
604 u8 MapNode::getMaxLevel(const NodeDefManager *nodemgr) const
605 {
606         const ContentFeatures &f = nodemgr->get(*this);
607         // todo: after update in all games leave only if (f.param_type_2 ==
608         if( f.liquid_type == LIQUID_FLOWING || f.param_type_2 == CPT2_FLOWINGLIQUID)
609                 return LIQUID_LEVEL_MAX;
610         if(f.leveled || f.param_type_2 == CPT2_LEVELED)
611                 return f.leveled_max;
612         return 0;
613 }
614
615 u8 MapNode::getLevel(const NodeDefManager *nodemgr) const
616 {
617         const ContentFeatures &f = nodemgr->get(*this);
618         // todo: after update in all games leave only if (f.param_type_2 ==
619         if(f.liquid_type == LIQUID_SOURCE)
620                 return LIQUID_LEVEL_SOURCE;
621         if (f.param_type_2 == CPT2_FLOWINGLIQUID)
622                 return getParam2() & LIQUID_LEVEL_MASK;
623         if(f.liquid_type == LIQUID_FLOWING) // can remove if all param_type_2 setted
624                 return getParam2() & LIQUID_LEVEL_MASK;
625         if (f.param_type_2 == CPT2_LEVELED) {
626                 u8 level = getParam2() & LEVELED_MASK;
627                 if (level)
628                         return level;
629         }
630         // Return static value from nodedef if param2 isn't used for level
631         if (f.leveled > f.leveled_max)
632                 return f.leveled_max;
633         return f.leveled;
634 }
635
636 s8 MapNode::setLevel(const NodeDefManager *nodemgr, s16 level)
637 {
638         s8 rest = 0;
639         const ContentFeatures &f = nodemgr->get(*this);
640         if (f.param_type_2 == CPT2_FLOWINGLIQUID
641                         || f.liquid_type == LIQUID_FLOWING
642                         || f.liquid_type == LIQUID_SOURCE) {
643                 if (level <= 0) { // liquid can’t exist with zero level
644                         setContent(CONTENT_AIR);
645                         return 0;
646                 }
647                 if (level >= LIQUID_LEVEL_SOURCE) {
648                         rest = level - LIQUID_LEVEL_SOURCE;
649                         setContent(f.liquid_alternative_source_id);
650                         setParam2(0);
651                 } else {
652                         setContent(f.liquid_alternative_flowing_id);
653                         setParam2((level & LIQUID_LEVEL_MASK) | (getParam2() & ~LIQUID_LEVEL_MASK));
654                 }
655         } else if (f.param_type_2 == CPT2_LEVELED) {
656                 if (level < 0) { // zero means default for a leveled nodebox
657                         rest = level;
658                         level = 0;
659                 } else if (level > f.leveled_max) {
660                         rest = level - f.leveled_max;
661                         level = f.leveled_max;
662                 }
663                 setParam2((level & LEVELED_MASK) | (getParam2() & ~LEVELED_MASK));
664         }
665         return rest;
666 }
667
668 s8 MapNode::addLevel(const NodeDefManager *nodemgr, s16 add)
669 {
670         s16 level = getLevel(nodemgr);
671         level += add;
672         return setLevel(nodemgr, level);
673 }
674
675 u32 MapNode::serializedLength(u8 version)
676 {
677         if(!ser_ver_supported(version))
678                 throw VersionMismatchException("ERROR: MapNode format not supported");
679
680         if (version == 0)
681                 return 1;
682
683         if (version <= 9)
684                 return 2;
685
686         if (version <= 23)
687                 return 3;
688
689         return 4;
690 }
691 void MapNode::serialize(u8 *dest, u8 version) const
692 {
693         if(!ser_ver_supported(version))
694                 throw VersionMismatchException("ERROR: MapNode format not supported");
695
696         // Can't do this anymore; we have 16-bit dynamically allocated node IDs
697         // in memory; conversion just won't work in this direction.
698         if(version < 24)
699                 throw SerializationError("MapNode::serialize: serialization to "
700                                 "version < 24 not possible");
701
702         writeU16(dest+0, param0);
703         writeU8(dest+2, param1);
704         writeU8(dest+3, param2);
705 }
706 void MapNode::deSerialize(u8 *source, u8 version)
707 {
708         if(!ser_ver_supported(version))
709                 throw VersionMismatchException("ERROR: MapNode format not supported");
710
711         if(version <= 21)
712         {
713                 deSerialize_pre22(source, version);
714                 return;
715         }
716
717         if(version >= 24){
718                 param0 = readU16(source+0);
719                 param1 = readU8(source+2);
720                 param2 = readU8(source+3);
721         }else{
722                 param0 = readU8(source+0);
723                 param1 = readU8(source+1);
724                 param2 = readU8(source+2);
725                 if(param0 > 0x7F){
726                         param0 |= ((param2&0xF0)<<4);
727                         param2 &= 0x0F;
728                 }
729         }
730 }
731 void MapNode::serializeBulk(std::ostream &os, int version,
732                 const MapNode *nodes, u32 nodecount,
733                 u8 content_width, u8 params_width, int compression_level)
734 {
735         if (!ser_ver_supported(version))
736                 throw VersionMismatchException("ERROR: MapNode format not supported");
737
738         sanity_check(content_width == 2);
739         sanity_check(params_width == 2);
740
741         // Can't do this anymore; we have 16-bit dynamically allocated node IDs
742         // in memory; conversion just won't work in this direction.
743         if (version < 24)
744                 throw SerializationError("MapNode::serializeBulk: serialization to "
745                                 "version < 24 not possible");
746
747         size_t databuf_size = nodecount * (content_width + params_width);
748         u8 *databuf = new u8[databuf_size];
749
750         u32 start1 = content_width * nodecount;
751         u32 start2 = (content_width + 1) * nodecount;
752
753         // Serialize content
754         for (u32 i = 0; i < nodecount; i++) {
755                 writeU16(&databuf[i * 2], nodes[i].param0);
756                 writeU8(&databuf[start1 + i], nodes[i].param1);
757                 writeU8(&databuf[start2 + i], nodes[i].param2);
758         }
759
760         /*
761                 Compress data to output stream
762         */
763
764         compressZlib(databuf, databuf_size, os, compression_level);
765
766         delete [] databuf;
767 }
768
769 // Deserialize bulk node data
770 void MapNode::deSerializeBulk(std::istream &is, int version,
771                 MapNode *nodes, u32 nodecount,
772                 u8 content_width, u8 params_width)
773 {
774         if(!ser_ver_supported(version))
775                 throw VersionMismatchException("ERROR: MapNode format not supported");
776
777         if (version < 22
778                         || (content_width != 1 && content_width != 2)
779                         || params_width != 2)
780                 FATAL_ERROR("Deserialize bulk node data error");
781
782         // Uncompress or read data
783         u32 len = nodecount * (content_width + params_width);
784         std::ostringstream os(std::ios_base::binary);
785         decompressZlib(is, os);
786         std::string s = os.str();
787         if(s.size() != len)
788                 throw SerializationError("deSerializeBulkNodes: "
789                                 "decompress resulted in invalid size");
790         const u8 *databuf = reinterpret_cast<const u8*>(s.c_str());
791
792         // Deserialize content
793         if(content_width == 1)
794         {
795                 for(u32 i=0; i<nodecount; i++)
796                         nodes[i].param0 = readU8(&databuf[i]);
797         }
798         else if(content_width == 2)
799         {
800                 for(u32 i=0; i<nodecount; i++)
801                         nodes[i].param0 = readU16(&databuf[i*2]);
802         }
803
804         // Deserialize param1
805         u32 start1 = content_width * nodecount;
806         for(u32 i=0; i<nodecount; i++)
807                 nodes[i].param1 = readU8(&databuf[start1 + i]);
808
809         // Deserialize param2
810         u32 start2 = (content_width + 1) * nodecount;
811         if(content_width == 1)
812         {
813                 for(u32 i=0; i<nodecount; i++) {
814                         nodes[i].param2 = readU8(&databuf[start2 + i]);
815                         if(nodes[i].param0 > 0x7F){
816                                 nodes[i].param0 <<= 4;
817                                 nodes[i].param0 |= (nodes[i].param2&0xF0)>>4;
818                                 nodes[i].param2 &= 0x0F;
819                         }
820                 }
821         }
822         else if(content_width == 2)
823         {
824                 for(u32 i=0; i<nodecount; i++)
825                         nodes[i].param2 = readU8(&databuf[start2 + i]);
826         }
827 }
828
829 /*
830         Legacy serialization
831 */
832 void MapNode::deSerialize_pre22(const u8 *source, u8 version)
833 {
834         if(version <= 1)
835         {
836                 param0 = source[0];
837         }
838         else if(version <= 9)
839         {
840                 param0 = source[0];
841                 param1 = source[1];
842         }
843         else
844         {
845                 param0 = source[0];
846                 param1 = source[1];
847                 param2 = source[2];
848                 if(param0 > 0x7f){
849                         param0 <<= 4;
850                         param0 |= (param2&0xf0)>>4;
851                         param2 &= 0x0f;
852                 }
853         }
854
855         // Convert special values from old version to new
856         if(version <= 19)
857         {
858                 // In these versions, CONTENT_IGNORE and CONTENT_AIR
859                 // are 255 and 254
860                 // Version 19 is messed up with sometimes the old values and sometimes not
861                 if(param0 == 255)
862                         param0 = CONTENT_IGNORE;
863                 else if(param0 == 254)
864                         param0 = CONTENT_AIR;
865         }
866
867         // Translate to our known version
868         *this = mapnode_translate_to_internal(*this, version);
869 }