]> git.lizzy.rs Git - minetest.git/blob - src/mapnode.cpp
Create framework for getting rid of global definitions of node/tool/item/whatever...
[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 "mapnode_contentfeatures.h"
27 #include "content_mapnode.h" // For mapnode_translate_*_internal
28
29 /*
30         Nodes make a face if contents differ and solidness differs.
31         Return value:
32                 0: No face
33                 1: Face uses m1's content
34                 2: Face uses m2's content
35         equivalent: Whether the blocks share the same face (eg. water and glass)
36 */
37 u8 face_contents(content_t m1, content_t m2, bool *equivalent)
38 {
39         *equivalent = false;
40
41         if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
42                 return 0;
43         
44         bool contents_differ = (m1 != m2);
45         
46         // Contents don't differ for different forms of same liquid
47         if(content_liquid(m1) && content_liquid(m2)
48                         && make_liquid_flowing(m1) == make_liquid_flowing(m2))
49                 contents_differ = false;
50         
51         u8 c1 = content_solidness(m1);
52         u8 c2 = content_solidness(m2);
53
54         bool solidness_differs = (c1 != c2);
55         bool makes_face = contents_differ && solidness_differs;
56
57         if(makes_face == false)
58                 return 0;
59         
60         if(c1 == 0)
61                 c1 = content_features(m1).visual_solidness;
62         if(c2 == 0)
63                 c2 = content_features(m2).visual_solidness;
64         
65         if(c1 == c2){
66                 *equivalent = true;
67                 // If same solidness, liquid takes precense
68                 if(content_features(m1).liquid_type != LIQUID_NONE)
69                         return 1;
70                 if(content_features(m2).liquid_type != LIQUID_NONE)
71                         return 2;
72         }
73         
74         if(c1 > c2)
75                 return 1;
76         else
77                 return 2;
78 }
79
80 v3s16 facedir_rotate(u8 facedir, v3s16 dir)
81 {
82         /*
83                 Face 2 (normally Z-) direction:
84                 facedir=0: Z-
85                 facedir=1: X-
86                 facedir=2: Z+
87                 facedir=3: X+
88         */
89         v3s16 newdir;
90         if(facedir==0) // Same
91                 newdir = v3s16(dir.X, dir.Y, dir.Z);
92         else if(facedir == 1) // Face is taken from rotXZccv(-90)
93                 newdir = v3s16(-dir.Z, dir.Y, dir.X);
94         else if(facedir == 2) // Face is taken from rotXZccv(180)
95                 newdir = v3s16(-dir.X, dir.Y, -dir.Z);
96         else if(facedir == 3) // Face is taken from rotXZccv(90)
97                 newdir = v3s16(dir.Z, dir.Y, -dir.X);
98         else
99                 newdir = dir;
100         return newdir;
101 }
102
103 u8 packDir(v3s16 dir)
104 {
105         u8 b = 0;
106
107         if(dir.X > 0)
108                 b |= (1<<0);
109         else if(dir.X < 0)
110                 b |= (1<<1);
111
112         if(dir.Y > 0)
113                 b |= (1<<2);
114         else if(dir.Y < 0)
115                 b |= (1<<3);
116
117         if(dir.Z > 0)
118                 b |= (1<<4);
119         else if(dir.Z < 0)
120                 b |= (1<<5);
121         
122         return b;
123 }
124 v3s16 unpackDir(u8 b)
125 {
126         v3s16 d(0,0,0);
127
128         if(b & (1<<0))
129                 d.X = 1;
130         else if(b & (1<<1))
131                 d.X = -1;
132
133         if(b & (1<<2))
134                 d.Y = 1;
135         else if(b & (1<<3))
136                 d.Y = -1;
137
138         if(b & (1<<4))
139                 d.Z = 1;
140         else if(b & (1<<5))
141                 d.Z = -1;
142         
143         return d;
144 }
145
146 /*
147         MapNode
148 */
149
150 // These four are DEPRECATED.
151 bool MapNode::light_propagates()
152 {
153         return light_propagates_content(getContent());
154 }
155 bool MapNode::sunlight_propagates()
156 {
157         return sunlight_propagates_content(getContent());
158 }
159 u8 MapNode::solidness()
160 {
161         return content_solidness(getContent());
162 }
163 u8 MapNode::light_source()
164 {
165         return content_features(*this).light_source;
166 }
167
168 void MapNode::setLight(enum LightBank bank, u8 a_light)
169 {
170         // If node doesn't contain light data, ignore this
171         if(content_features(*this).param_type != CPT_LIGHT)
172                 return;
173         if(bank == LIGHTBANK_DAY)
174         {
175                 param1 &= 0xf0;
176                 param1 |= a_light & 0x0f;
177         }
178         else if(bank == LIGHTBANK_NIGHT)
179         {
180                 param1 &= 0x0f;
181                 param1 |= (a_light & 0x0f)<<4;
182         }
183         else
184                 assert(0);
185 }
186
187 u8 MapNode::getLight(enum LightBank bank)
188 {
189         // Select the brightest of [light source, propagated light]
190         u8 light = 0;
191         if(content_features(*this).param_type == CPT_LIGHT)
192         {
193                 if(bank == LIGHTBANK_DAY)
194                         light = param1 & 0x0f;
195                 else if(bank == LIGHTBANK_NIGHT)
196                         light = (param1>>4)&0x0f;
197                 else
198                         assert(0);
199         }
200         if(light_source() > light)
201                 light = light_source();
202         return light;
203 }
204
205 u8 MapNode::getLightBanksWithSource()
206 {
207         // Select the brightest of [light source, propagated light]
208         u8 lightday = 0;
209         u8 lightnight = 0;
210         if(content_features(*this).param_type == CPT_LIGHT)
211         {
212                 lightday = param1 & 0x0f;
213                 lightnight = (param1>>4)&0x0f;
214         }
215         if(light_source() > lightday)
216                 lightday = light_source();
217         if(light_source() > lightnight)
218                 lightnight = light_source();
219         return (lightday&0x0f) | ((lightnight<<4)&0xf0);
220 }
221
222 #ifndef SERVER
223 TileSpec MapNode::getTile(v3s16 dir, ITextureSource *tsrc)
224 {
225         if(content_features(*this).param_type == CPT_FACEDIR_SIMPLE)
226                 dir = facedir_rotate(param1, dir);
227         
228         TileSpec spec;
229         
230         s32 dir_i = -1;
231         
232         if(dir == v3s16(0,0,0))
233                 dir_i = -1;
234         else if(dir == v3s16(0,1,0))
235                 dir_i = 0;
236         else if(dir == v3s16(0,-1,0))
237                 dir_i = 1;
238         else if(dir == v3s16(1,0,0))
239                 dir_i = 2;
240         else if(dir == v3s16(-1,0,0))
241                 dir_i = 3;
242         else if(dir == v3s16(0,0,1))
243                 dir_i = 4;
244         else if(dir == v3s16(0,0,-1))
245                 dir_i = 5;
246         
247         if(dir_i == -1)
248                 // Non-directional
249                 spec = content_features(*this).tiles[0];
250         else 
251                 spec = content_features(*this).tiles[dir_i];
252         
253         /*
254                 If it contains some mineral, change texture id
255         */
256         if(content_features(*this).param_type == CPT_MINERAL && tsrc)
257         {
258                 u8 mineral = getMineral();
259                 std::string mineral_texture_name = mineral_block_texture(mineral);
260                 if(mineral_texture_name != "")
261                 {
262                         u32 orig_id = spec.texture.id;
263                         std::string texture_name = tsrc->getTextureName(orig_id);
264                         //texture_name += "^blit:";
265                         texture_name += "^";
266                         texture_name += mineral_texture_name;
267                         u32 new_id = tsrc->getTextureId(texture_name);
268                         spec.texture = tsrc->getTexture(new_id);
269                 }
270         }
271
272         return spec;
273 }
274 #endif
275
276 u8 MapNode::getMineral()
277 {
278         if(content_features(*this).param_type == CPT_MINERAL)
279         {
280                 return param1 & 0x0f;
281         }
282
283         return MINERAL_NONE;
284 }
285
286 u32 MapNode::serializedLength(u8 version)
287 {
288         if(!ser_ver_supported(version))
289                 throw VersionMismatchException("ERROR: MapNode format not supported");
290                 
291         if(version == 0)
292                 return 1;
293         else if(version <= 9)
294                 return 2;
295         else
296                 return 3;
297 }
298 void MapNode::serialize(u8 *dest, u8 version)
299 {
300         if(!ser_ver_supported(version))
301                 throw VersionMismatchException("ERROR: MapNode format not supported");
302                 
303         // Translate to wanted version
304         MapNode n_foreign = mapnode_translate_from_internal(*this, version);
305
306         u8 actual_param0 = n_foreign.param0;
307
308         // Convert special values from new version to old
309         if(version <= 18)
310         {
311                 // In these versions, CONTENT_IGNORE and CONTENT_AIR
312                 // are 255 and 254
313                 if(actual_param0 == CONTENT_IGNORE)
314                         actual_param0 = 255;
315                 else if(actual_param0 == CONTENT_AIR)
316                         actual_param0 = 254;
317         }
318
319         if(version == 0)
320         {
321                 dest[0] = actual_param0;
322         }
323         else if(version <= 9)
324         {
325                 dest[0] = actual_param0;
326                 dest[1] = n_foreign.param1;
327         }
328         else
329         {
330                 dest[0] = actual_param0;
331                 dest[1] = n_foreign.param1;
332                 dest[2] = n_foreign.param2;
333         }
334 }
335 void MapNode::deSerialize(u8 *source, u8 version)
336 {
337         if(!ser_ver_supported(version))
338                 throw VersionMismatchException("ERROR: MapNode format not supported");
339                 
340         if(version == 0)
341         {
342                 param0 = source[0];
343         }
344         else if(version == 1)
345         {
346                 param0 = source[0];
347                 // This version doesn't support saved lighting
348                 if(light_propagates() || light_source() > 0)
349                         param1 = 0;
350                 else
351                         param1 = source[1];
352         }
353         else if(version <= 9)
354         {
355                 param0 = source[0];
356                 param1 = source[1];
357         }
358         else
359         {
360                 param0 = source[0];
361                 param1 = source[1];
362                 param2 = source[2];
363         }
364         
365         // Convert special values from old version to new
366         if(version <= 18)
367         {
368                 // In these versions, CONTENT_IGNORE and CONTENT_AIR
369                 // are 255 and 254
370                 if(param0 == 255)
371                         param0 = CONTENT_IGNORE;
372                 else if(param0 == 254)
373                         param0 = CONTENT_AIR;
374         }
375         // version 19 is fucked up with sometimes the old values and sometimes not
376         if(version == 19)
377         {
378                 if(param0 == 255)
379                         param0 = CONTENT_IGNORE;
380                 else if(param0 == 254)
381                         param0 = CONTENT_AIR;
382         }
383
384         // Translate to our known version
385         *this = mapnode_translate_to_internal(*this, version);
386 }
387
388 /*
389         Gets lighting value at face of node
390         
391         Parameters must consist of air and !air.
392         Order doesn't matter.
393
394         If either of the nodes doesn't exist, light is 0.
395         
396         parameters:
397                 daynight_ratio: 0...1000
398                 n: getNode(p) (uses only the lighting value)
399                 n2: getNode(p + face_dir) (uses only the lighting value)
400                 face_dir: axis oriented unit vector from p to p2
401         
402         returns encoded light value.
403 */
404 u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
405                 v3s16 face_dir)
406 {
407         try{
408                 u8 light;
409                 u8 l1 = n.getLightBlend(daynight_ratio);
410                 u8 l2 = n2.getLightBlend(daynight_ratio);
411                 if(l1 > l2)
412                         light = l1;
413                 else
414                         light = l2;
415
416                 // Make some nice difference to different sides
417
418                 // This makes light come from a corner
419                 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
420                         light = diminish_light(diminish_light(light));
421                 else if(face_dir.X == -1 || face_dir.Z == -1)
422                         light = diminish_light(light);*/
423                 
424                 // All neighboring faces have different shade (like in minecraft)
425                 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
426                         light = diminish_light(diminish_light(light));
427                 else if(face_dir.Z == 1 || face_dir.Z == -1)
428                         light = diminish_light(light);
429
430                 return light;
431         }
432         catch(InvalidPositionException &e)
433         {
434                 return 0;
435         }
436 }
437
438