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