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