]> git.lizzy.rs Git - minetest-m13.git/blob - src/mapnode.cpp
369a2af6a51d28bf2d9383f8fc4ac60f6a0a32e1
[minetest-m13.git] / src / mapnode.cpp
1 /*
2 Minetest-m13
3 Copyright (C) 2010 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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
14
15 You should have received a copy of the GNU 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 "common_irrlicht.h"
21 #include "mapnode.h"
22 #include "porting.h"
23 #include <string>
24 #include "main.h" // For g_settings
25 #include "nodedef.h"
26 #include "content_mapnode.h" // For mapnode_translate_*_internal
27 #include "serialization.h" // For ser_ver_supported
28
29 /*
30         MapNode
31 */
32
33 // Create directly from a nodename
34 // If name is unknown, sets CONTENT_IGNORE
35 MapNode::MapNode(INodeDefManager *ndef, const std::string &name,
36                 u8 a_param1, u8 a_param2)
37 {
38         content_t id = CONTENT_IGNORE;
39         ndef->getId(name, id);
40         param1 = a_param1;
41         param2 = a_param2;
42         // Set content (param0 and (param2&0xf0)) after other params
43         // because this needs to override part of param2
44         setContent(id);
45 }
46
47 void MapNode::setLight(enum LightBank bank, u8 a_light, INodeDefManager *nodemgr)
48 {
49         // If node doesn't contain light data, ignore this
50         if(nodemgr->get(*this).param_type != CPT_LIGHT)
51                 return;
52         if(bank == LIGHTBANK_DAY)
53         {
54                 param1 &= 0xf0;
55                 param1 |= a_light & 0x0f;
56         }
57         else if(bank == LIGHTBANK_NIGHT)
58         {
59                 param1 &= 0x0f;
60                 param1 |= (a_light & 0x0f)<<4;
61         }
62         else
63                 assert(0);
64 }
65
66 u8 MapNode::getLight(enum LightBank bank, INodeDefManager *nodemgr) const
67 {
68         // Select the brightest of [light source, propagated light]
69         const ContentFeatures &f = nodemgr->get(*this);
70         u8 light = 0;
71         if(f.param_type == CPT_LIGHT)
72         {
73                 if(bank == LIGHTBANK_DAY)
74                         light = param1 & 0x0f;
75                 else if(bank == LIGHTBANK_NIGHT)
76                         light = (param1>>4)&0x0f;
77                 else
78                         assert(0);
79         }
80         if(f.light_source > light)
81                 light = f.light_source;
82         return light;
83 }
84
85 bool MapNode::getLightBanks(u8 &lightday, u8 &lightnight, INodeDefManager *nodemgr) const
86 {
87         // Select the brightest of [light source, propagated light]
88         const ContentFeatures &f = nodemgr->get(*this);
89         if(f.param_type == CPT_LIGHT)
90         {
91                 lightday = param1 & 0x0f;
92                 lightnight = (param1>>4)&0x0f;
93         }
94         else
95         {
96                 lightday = 0;
97                 lightnight = 0;
98         }
99         if(f.light_source > lightday)
100                 lightday = f.light_source;
101         if(f.light_source > lightnight)
102                 lightnight = f.light_source;
103         return f.param_type == CPT_LIGHT || f.light_source != 0;
104 }
105
106 u8 MapNode::getFaceDir(INodeDefManager *nodemgr) const
107 {
108         const ContentFeatures &f = nodemgr->get(*this);
109         if(f.param_type_2 == CPT2_FACEDIR)
110                 return getParam2() & 0x03;
111         return 0;
112 }
113
114 u8 MapNode::getWallMounted(INodeDefManager *nodemgr) const
115 {
116         const ContentFeatures &f = nodemgr->get(*this);
117         if(f.param_type_2 == CPT2_WALLMOUNTED)
118                 return getParam2() & 0x07;
119         return 0;
120 }
121
122 v3s16 MapNode::getWallMountedDir(INodeDefManager *nodemgr) const
123 {
124         switch(getWallMounted(nodemgr))
125         {
126         case 0: default: return v3s16(0,1,0);
127         case 1: return v3s16(0,-1,0);
128         case 2: return v3s16(1,0,0);
129         case 3: return v3s16(-1,0,0);
130         case 4: return v3s16(0,0,1);
131         case 5: return v3s16(0,0,-1);
132         }
133 }
134
135
136
137 u32 MapNode::serializedLength(u8 version)
138 {
139         if(!ser_ver_supported(version))
140                 throw VersionMismatchException("ERROR: MapNode format not supported");
141                 
142         if(version == 0)
143                 return 1;
144         else if(version <= 9)
145                 return 2;
146         else
147                 return 3;
148 }
149 void MapNode::serialize(u8 *dest, u8 version)
150 {
151         if(!ser_ver_supported(version))
152                 throw VersionMismatchException("ERROR: MapNode format not supported");
153
154         if(version <= 21)
155         {
156                 serialize_pre22(dest, version);
157                 return;
158         }
159
160         writeU8(dest+0, param0);
161         writeU8(dest+1, param1);
162         writeU8(dest+2, param2);
163 }
164 void MapNode::deSerialize(u8 *source, u8 version)
165 {
166         if(!ser_ver_supported(version))
167                 throw VersionMismatchException("ERROR: MapNode format not supported");
168                 
169         if(version <= 21)
170         {
171                 deSerialize_pre22(source, version);
172                 return;
173         }
174
175         param0 = readU8(source+0);
176         param1 = readU8(source+1);
177         param2 = readU8(source+2);
178 }
179 void MapNode::serializeBulk(std::ostream &os, int version,
180                 const MapNode *nodes, u32 nodecount,
181                 u8 content_width, u8 params_width, bool compressed)
182 {
183         if(!ser_ver_supported(version))
184                 throw VersionMismatchException("ERROR: MapNode format not supported");
185
186         assert(version >= 22);
187         assert(content_width == 1);
188         assert(params_width == 2);
189
190         SharedBuffer<u8> databuf(nodecount * (content_width + params_width));
191
192         // Serialize content
193         if(content_width == 1)
194         {
195                 for(u32 i=0; i<nodecount; i++)
196                         writeU8(&databuf[i], nodes[i].param0);
197         }
198         /* If param0 is extended to two bytes, use something like this: */
199         /*else if(content_width == 2)
200         {
201                 for(u32 i=0; i<nodecount; i++)
202                         writeU16(&databuf[i*2], nodes[i].param0);
203         }*/
204
205         // Serialize param1
206         u32 start1 = content_width * nodecount;
207         for(u32 i=0; i<nodecount; i++)
208                 writeU8(&databuf[start1 + i], nodes[i].param1);
209
210         // Serialize param2
211         u32 start2 = (content_width + 1) * nodecount;
212         for(u32 i=0; i<nodecount; i++)
213                 writeU8(&databuf[start2 + i], nodes[i].param2);
214
215         /*
216                 Compress data to output stream
217         */
218
219         if(compressed)
220         {
221                 compressZlib(databuf, os);
222         }
223         else
224         {
225                 os.write((const char*) &databuf[0], databuf.getSize());
226         }
227 }
228
229 // Deserialize bulk node data
230 void MapNode::deSerializeBulk(std::istream &is, int version,
231                 MapNode *nodes, u32 nodecount,
232                 u8 content_width, u8 params_width, bool compressed)
233 {
234         if(!ser_ver_supported(version))
235                 throw VersionMismatchException("ERROR: MapNode format not supported");
236
237         assert(version >= 22);
238         assert(content_width == 1);
239         assert(params_width == 2);
240
241         // Uncompress or read data
242         u32 len = nodecount * (content_width + params_width);
243         SharedBuffer<u8> databuf(len);
244         if(compressed)
245         {
246                 std::ostringstream os(std::ios_base::binary);
247                 decompressZlib(is, os);
248                 std::string s = os.str();
249                 if(s.size() != len)
250                         throw SerializationError("deSerializeBulkNodes: "
251                                         "decompress resulted in invalid size");
252                 memcpy(&databuf[0], s.c_str(), len);
253         }
254         else
255         {
256                 is.read((char*) &databuf[0], len);
257                 if(is.eof() || is.fail())
258                         throw SerializationError("deSerializeBulkNodes: "
259                                         "failed to read bulk node data");
260         }
261
262         // Deserialize content
263         if(content_width == 1)
264         {
265                 for(u32 i=0; i<nodecount; i++)
266                         nodes[i].param0 = readU8(&databuf[i]);
267         }
268         /* If param0 is extended to two bytes, use something like this: */
269         /*else if(content_width == 2)
270         {
271                 for(u32 i=0; i<nodecount; i++)
272                         nodes[i].param0 = readU16(&databuf[i*2]);
273         }*/
274
275         // Deserialize param1
276         u32 start1 = content_width * nodecount;
277         for(u32 i=0; i<nodecount; i++)
278                 nodes[i].param1 = readU8(&databuf[start1 + i]);
279
280         // Deserialize param2
281         u32 start2 = (content_width + 1) * nodecount;
282         for(u32 i=0; i<nodecount; i++)
283                 nodes[i].param2 = readU8(&databuf[start2 + i]);
284 }
285
286 /*
287         Legacy serialization
288 */
289 void MapNode::serialize_pre22(u8 *dest, u8 version)
290 {
291         // Translate to wanted version
292         MapNode n_foreign = mapnode_translate_from_internal(*this, version);
293
294         u8 actual_param0 = n_foreign.param0;
295
296         // Convert special values from new version to old
297         if(version <= 18)
298         {
299                 // In these versions, CONTENT_IGNORE and CONTENT_AIR
300                 // are 255 and 254
301                 if(actual_param0 == CONTENT_IGNORE)
302                         actual_param0 = 255;
303                 else if(actual_param0 == CONTENT_AIR)
304                         actual_param0 = 254;
305         }
306
307         if(version == 0)
308         {
309                 dest[0] = actual_param0;
310         }
311         else if(version <= 9)
312         {
313                 dest[0] = actual_param0;
314                 dest[1] = n_foreign.param1;
315         }
316         else
317         {
318                 dest[0] = actual_param0;
319                 dest[1] = n_foreign.param1;
320                 dest[2] = n_foreign.param2;
321         }
322 }
323 void MapNode::deSerialize_pre22(u8 *source, u8 version)
324 {
325         if(version <= 1)
326         {
327                 param0 = source[0];
328         }
329         else if(version <= 9)
330         {
331                 param0 = source[0];
332                 param1 = source[1];
333         }
334         else
335         {
336                 param0 = source[0];
337                 param1 = source[1];
338                 param2 = source[2];
339         }
340         
341         // Convert special values from old version to new
342         if(version <= 19)
343         {
344                 // In these versions, CONTENT_IGNORE and CONTENT_AIR
345                 // are 255 and 254
346                 // Version 19 is fucked up with sometimes the old values and sometimes not
347                 if(param0 == 255)
348                         param0 = CONTENT_IGNORE;
349                 else if(param0 == 254)
350                         param0 = CONTENT_AIR;
351         }
352
353         // Translate to our known version
354         *this = mapnode_translate_to_internal(*this, version);
355 }
356
357
358 #ifndef SERVER
359
360 /*
361         Nodes make a face if contents differ and solidness differs.
362         Return value:
363                 0: No face
364                 1: Face uses m1's content
365                 2: Face uses m2's content
366         equivalent: Whether the blocks share the same face (eg. water and glass)
367
368         TODO: Add 3: Both faces drawn with backface culling, remove equivalent
369 */
370 u8 face_contents(content_t m1, content_t m2, bool *equivalent,
371                 INodeDefManager *nodemgr)
372 {
373         *equivalent = false;
374
375         if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
376                 return 0;
377         
378         bool contents_differ = (m1 != m2);
379         
380         const ContentFeatures &f1 = nodemgr->get(m1);
381         const ContentFeatures &f2 = nodemgr->get(m2);
382
383         // Contents don't differ for different forms of same liquid
384         if(f1.sameLiquid(f2))
385                 contents_differ = false;
386         
387         u8 c1 = f1.solidness;
388         u8 c2 = f2.solidness;
389
390         bool solidness_differs = (c1 != c2);
391         bool makes_face = contents_differ && solidness_differs;
392
393         if(makes_face == false)
394                 return 0;
395         
396         if(c1 == 0)
397                 c1 = f1.visual_solidness;
398         if(c2 == 0)
399                 c2 = f2.visual_solidness;
400         
401         if(c1 == c2){
402                 *equivalent = true;
403                 // If same solidness, liquid takes precense
404                 if(f1.isLiquid())
405                         return 1;
406                 if(f2.isLiquid())
407                         return 2;
408         }
409         
410         if(c1 > c2)
411                 return 1;
412         else
413                 return 2;
414 }
415
416 /*
417         Gets lighting value at face of node
418         
419         Parameters must consist of air and !air.
420         Order doesn't matter.
421
422         If either of the nodes doesn't exist, light is 0.
423         
424         parameters:
425                 daynight_ratio: 0...1000
426                 n: getNode(p) (uses only the lighting value)
427                 n2: getNode(p + face_dir) (uses only the lighting value)
428                 face_dir: axis oriented unit vector from p to p2
429         
430         returns encoded light value.
431 */
432 u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
433                 v3s16 face_dir, INodeDefManager *nodemgr)
434 {
435         try{
436                 u8 light;
437                 u8 l1 = n.getLightBlend(daynight_ratio, nodemgr);
438                 u8 l2 = n2.getLightBlend(daynight_ratio, nodemgr);
439                 if(l1 > l2)
440                         light = l1;
441                 else
442                         light = l2;
443
444                 // Make some nice difference to different sides
445
446                 // This makes light come from a corner
447                 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
448                         light = diminish_light(diminish_light(light));
449                 else if(face_dir.X == -1 || face_dir.Z == -1)
450                         light = diminish_light(light);*/
451                 
452                 // All neighboring faces have different shade (like in minecraft)
453                 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
454                         light = diminish_light(diminish_light(light));
455                 else if(face_dir.Z == 1 || face_dir.Z == -1)
456                         light = diminish_light(light);
457
458                 return light;
459         }
460         catch(InvalidPositionException &e)
461         {
462                 return 0;
463         }
464 }
465
466 #endif
467