a757149b10612925093837750b52a9a0d43f5599
[oweals/minetest.git] / src / mapnode.cpp
1 /*
2 Minetest-c55
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 "mineral.h"
25 #include "main.h" // For g_settings
26 #include "nodedef.h"
27 #include "content_mapnode.h" // For mapnode_translate_*_internal
28 #include "serialization.h" // For ser_ver_supported
29
30 #ifndef SERVER
31 /*
32         Nodes make a face if contents differ and solidness differs.
33         Return value:
34                 0: No face
35                 1: Face uses m1's content
36                 2: Face uses m2's content
37         equivalent: Whether the blocks share the same face (eg. water and glass)
38
39         TODO: Add 3: Both faces drawn with backface culling, remove equivalent
40 */
41 u8 face_contents(content_t m1, content_t m2, bool *equivalent,
42                 INodeDefManager *nodemgr)
43 {
44         *equivalent = false;
45
46         if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
47                 return 0;
48         
49         bool contents_differ = (m1 != m2);
50         
51         const ContentFeatures &f1 = nodemgr->get(m1);
52         const ContentFeatures &f2 = nodemgr->get(m2);
53
54         // Contents don't differ for different forms of same liquid
55         if(f1.sameLiquid(f2))
56                 contents_differ = false;
57         
58         u8 c1 = f1.solidness;
59         u8 c2 = f2.solidness;
60
61         bool solidness_differs = (c1 != c2);
62         bool makes_face = contents_differ && solidness_differs;
63
64         if(makes_face == false)
65                 return 0;
66         
67         if(c1 == 0)
68                 c1 = f1.visual_solidness;
69         if(c2 == 0)
70                 c2 = f2.visual_solidness;
71         
72         if(c1 == c2){
73                 *equivalent = true;
74                 // If same solidness, liquid takes precense
75                 if(f1.isLiquid())
76                         return 1;
77                 if(f2.isLiquid())
78                         return 2;
79         }
80         
81         if(c1 > c2)
82                 return 1;
83         else
84                 return 2;
85 }
86 #endif
87
88 v3s16 facedir_rotate(u8 facedir, v3s16 dir)
89 {
90         /*
91                 Face 2 (normally Z-) direction:
92                 facedir=0: Z-
93                 facedir=1: X-
94                 facedir=2: Z+
95                 facedir=3: X+
96         */
97         v3s16 newdir;
98         if(facedir==0) // Same
99                 newdir = v3s16(dir.X, dir.Y, dir.Z);
100         else if(facedir == 1) // Face is taken from rotXZccv(-90)
101                 newdir = v3s16(-dir.Z, dir.Y, dir.X);
102         else if(facedir == 2) // Face is taken from rotXZccv(180)
103                 newdir = v3s16(-dir.X, dir.Y, -dir.Z);
104         else if(facedir == 3) // Face is taken from rotXZccv(90)
105                 newdir = v3s16(dir.Z, dir.Y, -dir.X);
106         else
107                 newdir = dir;
108         return newdir;
109 }
110
111 u8 packDir(v3s16 dir)
112 {
113         u8 b = 0;
114
115         if(dir.X > 0)
116                 b |= (1<<0);
117         else if(dir.X < 0)
118                 b |= (1<<1);
119
120         if(dir.Y > 0)
121                 b |= (1<<2);
122         else if(dir.Y < 0)
123                 b |= (1<<3);
124
125         if(dir.Z > 0)
126                 b |= (1<<4);
127         else if(dir.Z < 0)
128                 b |= (1<<5);
129         
130         return b;
131 }
132 v3s16 unpackDir(u8 b)
133 {
134         v3s16 d(0,0,0);
135
136         if(b & (1<<0))
137                 d.X = 1;
138         else if(b & (1<<1))
139                 d.X = -1;
140
141         if(b & (1<<2))
142                 d.Y = 1;
143         else if(b & (1<<3))
144                 d.Y = -1;
145
146         if(b & (1<<4))
147                 d.Z = 1;
148         else if(b & (1<<5))
149                 d.Z = -1;
150         
151         return d;
152 }
153
154 /*
155         MapNode
156 */
157
158 // Create directly from a nodename
159 // If name is unknown, sets CONTENT_IGNORE
160 MapNode::MapNode(INodeDefManager *ndef, const std::string &name,
161                 u8 a_param1, u8 a_param2)
162 {
163         content_t id = CONTENT_IGNORE;
164         ndef->getId(name, id);
165         param1 = a_param1;
166         param2 = a_param2;
167         // Set content (param0 and (param2&0xf0)) after other params
168         // because this needs to override part of param2
169         setContent(id);
170 }
171
172 void MapNode::setLight(enum LightBank bank, u8 a_light, INodeDefManager *nodemgr)
173 {
174         // If node doesn't contain light data, ignore this
175         if(nodemgr->get(*this).param_type != CPT_LIGHT)
176                 return;
177         if(bank == LIGHTBANK_DAY)
178         {
179                 param1 &= 0xf0;
180                 param1 |= a_light & 0x0f;
181         }
182         else if(bank == LIGHTBANK_NIGHT)
183         {
184                 param1 &= 0x0f;
185                 param1 |= (a_light & 0x0f)<<4;
186         }
187         else
188                 assert(0);
189 }
190
191 u8 MapNode::getLight(enum LightBank bank, INodeDefManager *nodemgr) const
192 {
193         // Select the brightest of [light source, propagated light]
194         u8 light = 0;
195         if(nodemgr->get(*this).param_type == CPT_LIGHT)
196         {
197                 if(bank == LIGHTBANK_DAY)
198                         light = param1 & 0x0f;
199                 else if(bank == LIGHTBANK_NIGHT)
200                         light = (param1>>4)&0x0f;
201                 else
202                         assert(0);
203         }
204         if(nodemgr->get(*this).light_source > light)
205                 light = nodemgr->get(*this).light_source;
206         return light;
207 }
208
209 u8 MapNode::getLightBanksWithSource(INodeDefManager *nodemgr) const
210 {
211         // Select the brightest of [light source, propagated light]
212         u8 lightday = 0;
213         u8 lightnight = 0;
214         if(nodemgr->get(*this).param_type == CPT_LIGHT)
215         {
216                 lightday = param1 & 0x0f;
217                 lightnight = (param1>>4)&0x0f;
218         }
219         if(nodemgr->get(*this).light_source > lightday)
220                 lightday = nodemgr->get(*this).light_source;
221         if(nodemgr->get(*this).light_source > lightnight)
222                 lightnight = nodemgr->get(*this).light_source;
223         return (lightday&0x0f) | ((lightnight<<4)&0xf0);
224 }
225
226 u8 MapNode::getMineral(INodeDefManager *nodemgr) const
227 {
228         if(nodemgr->get(*this).param_type == CPT_MINERAL)
229         {
230                 return param1 & 0x0f;
231         }
232
233         return MINERAL_NONE;
234 }
235
236 u32 MapNode::serializedLength(u8 version)
237 {
238         if(!ser_ver_supported(version))
239                 throw VersionMismatchException("ERROR: MapNode format not supported");
240                 
241         if(version == 0)
242                 return 1;
243         else if(version <= 9)
244                 return 2;
245         else
246                 return 3;
247 }
248 void MapNode::serialize(u8 *dest, u8 version)
249 {
250         if(!ser_ver_supported(version))
251                 throw VersionMismatchException("ERROR: MapNode format not supported");
252                 
253         // Translate to wanted version
254         MapNode n_foreign = mapnode_translate_from_internal(*this, version);
255
256         u8 actual_param0 = n_foreign.param0;
257
258         // Convert special values from new version to old
259         if(version <= 18)
260         {
261                 // In these versions, CONTENT_IGNORE and CONTENT_AIR
262                 // are 255 and 254
263                 if(actual_param0 == CONTENT_IGNORE)
264                         actual_param0 = 255;
265                 else if(actual_param0 == CONTENT_AIR)
266                         actual_param0 = 254;
267         }
268
269         if(version == 0)
270         {
271                 dest[0] = actual_param0;
272         }
273         else if(version <= 9)
274         {
275                 dest[0] = actual_param0;
276                 dest[1] = n_foreign.param1;
277         }
278         else
279         {
280                 dest[0] = actual_param0;
281                 dest[1] = n_foreign.param1;
282                 dest[2] = n_foreign.param2;
283         }
284 }
285 void MapNode::deSerialize(u8 *source, u8 version)
286 {
287         if(!ser_ver_supported(version))
288                 throw VersionMismatchException("ERROR: MapNode format not supported");
289                 
290         if(version == 0)
291         {
292                 param0 = source[0];
293         }
294         else if(version == 1)
295         {
296                 param0 = source[0];
297         }
298         else if(version <= 9)
299         {
300                 param0 = source[0];
301                 param1 = source[1];
302         }
303         else
304         {
305                 param0 = source[0];
306                 param1 = source[1];
307                 param2 = source[2];
308         }
309         
310         // Convert special values from old version to new
311         if(version <= 18)
312         {
313                 // In these versions, CONTENT_IGNORE and CONTENT_AIR
314                 // are 255 and 254
315                 if(param0 == 255)
316                         param0 = CONTENT_IGNORE;
317                 else if(param0 == 254)
318                         param0 = CONTENT_AIR;
319         }
320         // version 19 is fucked up with sometimes the old values and sometimes not
321         if(version == 19)
322         {
323                 if(param0 == 255)
324                         param0 = CONTENT_IGNORE;
325                 else if(param0 == 254)
326                         param0 = CONTENT_AIR;
327         }
328
329         // Translate to our known version
330         *this = mapnode_translate_to_internal(*this, version);
331 }
332
333 /*
334         Gets lighting value at face of node
335         
336         Parameters must consist of air and !air.
337         Order doesn't matter.
338
339         If either of the nodes doesn't exist, light is 0.
340         
341         parameters:
342                 daynight_ratio: 0...1000
343                 n: getNode(p) (uses only the lighting value)
344                 n2: getNode(p + face_dir) (uses only the lighting value)
345                 face_dir: axis oriented unit vector from p to p2
346         
347         returns encoded light value.
348 */
349 u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
350                 v3s16 face_dir, INodeDefManager *nodemgr)
351 {
352         try{
353                 u8 light;
354                 u8 l1 = n.getLightBlend(daynight_ratio, nodemgr);
355                 u8 l2 = n2.getLightBlend(daynight_ratio, nodemgr);
356                 if(l1 > l2)
357                         light = l1;
358                 else
359                         light = l2;
360
361                 // Make some nice difference to different sides
362
363                 // This makes light come from a corner
364                 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
365                         light = diminish_light(diminish_light(light));
366                 else if(face_dir.X == -1 || face_dir.Z == -1)
367                         light = diminish_light(light);*/
368                 
369                 // All neighboring faces have different shade (like in minecraft)
370                 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
371                         light = diminish_light(diminish_light(light));
372                 else if(face_dir.Z == 1 || face_dir.Z == -1)
373                         light = diminish_light(light);
374
375                 return light;
376         }
377         catch(InvalidPositionException &e)
378         {
379                 return 0;
380         }
381 }
382
383