Fixed small error in mapnode.cpp (didn't cause any harm though)
[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 "tile.h"
23 #include "porting.h"
24 #include <string>
25 #include "mineral.h"
26 // For g_settings
27 #include "main.h"
28 #include "content_mapnode.h"
29 #include "nodemetadata.h"
30
31 ContentFeatures::~ContentFeatures()
32 {
33         /*if(translate_to)
34                 delete translate_to;*/
35         if(initial_metadata)
36                 delete initial_metadata;
37 }
38
39 void ContentFeatures::setTexture(u16 i, std::string name, u8 alpha)
40 {
41         if(g_texturesource)
42         {
43                 tiles[i].texture = g_texturesource->getTexture(name);
44         }
45         
46         if(alpha != 255)
47         {
48                 tiles[i].alpha = alpha;
49                 tiles[i].material_type = MATERIAL_ALPHA_VERTEX;
50         }
51
52         if(inventory_texture == NULL)
53                 setInventoryTexture(name);
54 }
55
56 void ContentFeatures::setInventoryTexture(std::string imgname)
57 {
58         if(g_texturesource == NULL)
59                 return;
60         
61         imgname += "^[forcesingle";
62         
63         inventory_texture = g_texturesource->getTextureRaw(imgname);
64 }
65
66 void ContentFeatures::setInventoryTextureCube(std::string top,
67                 std::string left, std::string right)
68 {
69         if(g_texturesource == NULL)
70                 return;
71         
72         str_replace_char(top, '^', '&');
73         str_replace_char(left, '^', '&');
74         str_replace_char(right, '^', '&');
75
76         std::string imgname_full;
77         imgname_full += "[inventorycube{";
78         imgname_full += top;
79         imgname_full += "{";
80         imgname_full += left;
81         imgname_full += "{";
82         imgname_full += right;
83         inventory_texture = g_texturesource->getTextureRaw(imgname_full);
84 }
85
86 struct ContentFeatures g_content_features[256];
87
88 ContentFeatures & content_features(u8 i)
89 {
90         return g_content_features[i];
91 }
92
93 /*
94         See mapnode.h for description.
95 */
96 void init_mapnode()
97 {
98         if(g_texturesource == NULL)
99         {
100                 dstream<<"INFO: Initial run of init_mapnode with "
101                                 "g_texturesource=NULL. If this segfaults, "
102                                 "there is a bug with something not checking for "
103                                 "the NULL value."<<std::endl;
104         }
105         else
106         {
107                 dstream<<"INFO: Full run of init_mapnode with "
108                                 "g_texturesource!=NULL"<<std::endl;
109         }
110
111         /*// Read some settings
112         bool new_style_water = g_settings.getBool("new_style_water");
113         bool new_style_leaves = g_settings.getBool("new_style_leaves");*/
114
115         /*
116                 Initialize content feature table
117         */
118         
119         /*
120                 Set initial material type to same in all tiles, so that the
121                 same material can be used in more stuff.
122                 This is set according to the leaves because they are the only
123                 differing material to which all materials can be changed to
124                 get this optimization.
125         */
126         u8 initial_material_type = MATERIAL_ALPHA_SIMPLE;
127         /*if(new_style_leaves)
128                 initial_material_type = MATERIAL_ALPHA_SIMPLE;
129         else
130                 initial_material_type = MATERIAL_ALPHA_NONE;*/
131         for(u16 i=0; i<256; i++)
132         {
133                 ContentFeatures *f = &g_content_features[i];
134                 // Re-initialize
135                 f->reset();
136
137                 for(u16 j=0; j<6; j++)
138                         f->tiles[j].material_type = initial_material_type;
139         }
140
141         /*
142                 Initially set every block to be shown as an unknown block.
143                 Don't touch CONTENT_IGNORE or CONTENT_AIR.
144         */
145         for(u16 i=0; i<256; i++)
146         {
147                 if(i == CONTENT_IGNORE || i == CONTENT_AIR)
148                         continue;
149                 ContentFeatures *f = &g_content_features[i];
150                 f->setAllTextures("unknown_block.png");
151                 f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
152         }
153
154         /*
155                 Initialize mapnode content
156         */
157         content_mapnode_init();
158         
159 }
160
161 v3s16 facedir_rotate(u8 facedir, v3s16 dir)
162 {
163         /*
164                 Face 2 (normally Z-) direction:
165                 facedir=0: Z-
166                 facedir=1: X-
167                 facedir=2: Z+
168                 facedir=3: X+
169         */
170         v3s16 newdir;
171         if(facedir==0) // Same
172                 newdir = v3s16(dir.X, dir.Y, dir.Z);
173         else if(facedir == 1) // Face is taken from rotXZccv(-90)
174                 newdir = v3s16(-dir.Z, dir.Y, dir.X);
175         else if(facedir == 2) // Face is taken from rotXZccv(180)
176                 newdir = v3s16(-dir.X, dir.Y, -dir.Z);
177         else if(facedir == 3) // Face is taken from rotXZccv(90)
178                 newdir = v3s16(dir.Z, dir.Y, -dir.X);
179         else
180                 newdir = dir;
181         return newdir;
182 }
183
184 TileSpec MapNode::getTile(v3s16 dir)
185 {
186         if(content_features(d).param_type == CPT_FACEDIR_SIMPLE)
187                 dir = facedir_rotate(param1, dir);
188         
189         TileSpec spec;
190         
191         s32 dir_i = -1;
192         
193         if(dir == v3s16(0,0,0))
194                 dir_i = -1;
195         else if(dir == v3s16(0,1,0))
196                 dir_i = 0;
197         else if(dir == v3s16(0,-1,0))
198                 dir_i = 1;
199         else if(dir == v3s16(1,0,0))
200                 dir_i = 2;
201         else if(dir == v3s16(-1,0,0))
202                 dir_i = 3;
203         else if(dir == v3s16(0,0,1))
204                 dir_i = 4;
205         else if(dir == v3s16(0,0,-1))
206                 dir_i = 5;
207         
208         if(dir_i == -1)
209                 // Non-directional
210                 spec = content_features(d).tiles[0];
211         else 
212                 spec = content_features(d).tiles[dir_i];
213         
214         /*
215                 If it contains some mineral, change texture id
216         */
217         if(content_features(d).param_type == CPT_MINERAL && g_texturesource)
218         {
219                 u8 mineral = param & 0x1f;
220                 std::string mineral_texture_name = mineral_block_texture(mineral);
221                 if(mineral_texture_name != "")
222                 {
223                         u32 orig_id = spec.texture.id;
224                         std::string texture_name = g_texturesource->getTextureName(orig_id);
225                         //texture_name += "^blit:";
226                         texture_name += "^";
227                         texture_name += mineral_texture_name;
228                         u32 new_id = g_texturesource->getTextureId(texture_name);
229                         spec.texture = g_texturesource->getTexture(new_id);
230                 }
231         }
232
233         return spec;
234 }
235
236 u8 MapNode::getMineral()
237 {
238         if(content_features(d).param_type == CPT_MINERAL)
239         {
240                 return param & 0x1f;
241         }
242
243         return MINERAL_NONE;
244 }
245
246 u32 MapNode::serializedLength(u8 version)
247 {
248         if(!ser_ver_supported(version))
249                 throw VersionMismatchException("ERROR: MapNode format not supported");
250                 
251         if(version == 0)
252                 return 1;
253         else if(version <= 9)
254                 return 2;
255         else
256                 return 3;
257 }
258 void MapNode::serialize(u8 *dest, u8 version)
259 {
260         if(!ser_ver_supported(version))
261                 throw VersionMismatchException("ERROR: MapNode format not supported");
262                 
263         u8 actual_d = d;
264
265         // Convert from new version to old
266         if(version <= 18)
267         {
268                 // In these versions, CONTENT_IGNORE and CONTENT_AIR
269                 // are 255 and 254
270                 if(d == CONTENT_IGNORE)
271                         d = 255;
272                 else if(d == CONTENT_AIR)
273                         d = 254;
274         }
275
276         if(version == 0)
277         {
278                 dest[0] = actual_d;
279         }
280         else if(version <= 9)
281         {
282                 dest[0] = actual_d;
283                 dest[1] = param;
284         }
285         else
286         {
287                 dest[0] = actual_d;
288                 dest[1] = param;
289                 dest[2] = param2;
290         }
291 }
292 void MapNode::deSerialize(u8 *source, u8 version)
293 {
294         if(!ser_ver_supported(version))
295                 throw VersionMismatchException("ERROR: MapNode format not supported");
296                 
297         if(version == 0)
298         {
299                 d = source[0];
300         }
301         else if(version == 1)
302         {
303                 d = source[0];
304                 // This version doesn't support saved lighting
305                 if(light_propagates() || light_source() > 0)
306                         param = 0;
307                 else
308                         param = source[1];
309         }
310         else if(version <= 9)
311         {
312                 d = source[0];
313                 param = source[1];
314         }
315         else
316         {
317                 d = source[0];
318                 param = source[1];
319                 param2 = source[2];
320                 
321                 // Convert from old version to new
322                 if(version <= 18)
323                 {
324                         // In these versions, CONTENT_IGNORE and CONTENT_AIR
325                         // are 255 and 254
326                         if(d == 255)
327                                 d = CONTENT_IGNORE;
328                         else if(d == 254)
329                                 d = CONTENT_AIR;
330                 }
331         }
332 }
333
334 /*
335         Gets lighting value at face of node
336         
337         Parameters must consist of air and !air.
338         Order doesn't matter.
339
340         If either of the nodes doesn't exist, light is 0.
341         
342         parameters:
343                 daynight_ratio: 0...1000
344                 n: getNodeParent(p)
345                 n2: getNodeParent(p + face_dir)
346                 face_dir: axis oriented unit vector from p to p2
347         
348         returns encoded light value.
349 */
350 u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
351                 v3s16 face_dir)
352 {
353         try{
354                 u8 light;
355                 u8 l1 = n.getLightBlend(daynight_ratio);
356                 u8 l2 = n2.getLightBlend(daynight_ratio);
357                 if(l1 > l2)
358                         light = l1;
359                 else
360                         light = l2;
361
362                 // Make some nice difference to different sides
363
364                 // This makes light come from a corner
365                 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
366                         light = diminish_light(diminish_light(light));
367                 else if(face_dir.X == -1 || face_dir.Z == -1)
368                         light = diminish_light(light);*/
369                 
370                 // All neighboring faces have different shade (like in minecraft)
371                 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
372                         light = diminish_light(diminish_light(light));
373                 else if(face_dir.Z == 1 || face_dir.Z == -1)
374                         light = diminish_light(light);
375
376                 return light;
377         }
378         catch(InvalidPositionException &e)
379         {
380                 return 0;
381         }
382 }
383
384