initial steps in doing content type extension
[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<=253; i++)
146         {
147                 ContentFeatures *f = &g_content_features[i];
148                 f->setAllTextures("unknown_block.png");
149                 f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
150         }
151
152         /*
153                 Initialize mapnode content
154         */
155         content_mapnode_init();
156         
157 }
158
159 v3s16 facedir_rotate(u8 facedir, v3s16 dir)
160 {
161         /*
162                 Face 2 (normally Z-) direction:
163                 facedir=0: Z-
164                 facedir=1: X-
165                 facedir=2: Z+
166                 facedir=3: X+
167         */
168         v3s16 newdir;
169         if(facedir==0) // Same
170                 newdir = v3s16(dir.X, dir.Y, dir.Z);
171         else if(facedir == 1) // Face is taken from rotXZccv(-90)
172                 newdir = v3s16(-dir.Z, dir.Y, dir.X);
173         else if(facedir == 2) // Face is taken from rotXZccv(180)
174                 newdir = v3s16(-dir.X, dir.Y, -dir.Z);
175         else if(facedir == 3) // Face is taken from rotXZccv(90)
176                 newdir = v3s16(dir.Z, dir.Y, -dir.X);
177         else
178                 newdir = dir;
179         return newdir;
180 }
181
182 TileSpec MapNode::getTile(v3s16 dir)
183 {
184         if(content_features(d).param_type == CPT_FACEDIR_SIMPLE)
185                 dir = facedir_rotate(param1, dir);
186         
187         TileSpec spec;
188         
189         s32 dir_i = -1;
190         
191         if(dir == v3s16(0,0,0))
192                 dir_i = -1;
193         else if(dir == v3s16(0,1,0))
194                 dir_i = 0;
195         else if(dir == v3s16(0,-1,0))
196                 dir_i = 1;
197         else if(dir == v3s16(1,0,0))
198                 dir_i = 2;
199         else if(dir == v3s16(-1,0,0))
200                 dir_i = 3;
201         else if(dir == v3s16(0,0,1))
202                 dir_i = 4;
203         else if(dir == v3s16(0,0,-1))
204                 dir_i = 5;
205         
206         if(dir_i == -1)
207                 // Non-directional
208                 spec = content_features(d).tiles[0];
209         else 
210                 spec = content_features(d).tiles[dir_i];
211         
212         /*
213                 If it contains some mineral, change texture id
214         */
215         if(content_features(d).param_type == CPT_MINERAL && g_texturesource)
216         {
217                 u8 mineral = param & 0x1f;
218                 std::string mineral_texture_name = mineral_block_texture(mineral);
219                 if(mineral_texture_name != "")
220                 {
221                         u32 orig_id = spec.texture.id;
222                         std::string texture_name = g_texturesource->getTextureName(orig_id);
223                         //texture_name += "^blit:";
224                         texture_name += "^";
225                         texture_name += mineral_texture_name;
226                         u32 new_id = g_texturesource->getTextureId(texture_name);
227                         spec.texture = g_texturesource->getTexture(new_id);
228                 }
229         }
230
231         return spec;
232 }
233
234 u8 MapNode::getMineral()
235 {
236         if(content_features(d).param_type == CPT_MINERAL)
237         {
238                 return param & 0x1f;
239         }
240
241         return MINERAL_NONE;
242 }
243
244 u32 MapNode::serializedLength(u8 version)
245 {
246         if(!ser_ver_supported(version))
247                 throw VersionMismatchException("ERROR: MapNode format not supported");
248                 
249         if(version == 0)
250                 return 1;
251         else if(version <= 9)
252                 return 2;
253         else
254                 return 3;
255 }
256 void MapNode::serialize(u8 *dest, u8 version)
257 {
258         if(!ser_ver_supported(version))
259                 throw VersionMismatchException("ERROR: MapNode format not supported");
260                 
261         u8 actual_d = d;
262
263         // Convert from new version to old
264         if(version <= 18)
265         {
266                 // In these versions, CONTENT_IGNORE and CONTENT_AIR
267                 // are 255 and 254
268                 if(d == CONTENT_IGNORE)
269                         d = 255;
270                 else if(d == CONTENT_AIR)
271                         d = 254;
272         }
273
274         if(version == 0)
275         {
276                 dest[0] = actual_d;
277         }
278         else if(version <= 9)
279         {
280                 dest[0] = actual_d;
281                 dest[1] = param;
282         }
283         else
284         {
285                 dest[0] = actual_d;
286                 dest[1] = param;
287                 dest[2] = param2;
288         }
289 }
290 void MapNode::deSerialize(u8 *source, u8 version)
291 {
292         if(!ser_ver_supported(version))
293                 throw VersionMismatchException("ERROR: MapNode format not supported");
294                 
295         if(version == 0)
296         {
297                 d = source[0];
298         }
299         else if(version == 1)
300         {
301                 d = source[0];
302                 // This version doesn't support saved lighting
303                 if(light_propagates() || light_source() > 0)
304                         param = 0;
305                 else
306                         param = source[1];
307         }
308         else if(version <= 9)
309         {
310                 d = source[0];
311                 param = source[1];
312         }
313         else
314         {
315                 d = source[0];
316                 param = source[1];
317                 param2 = source[2];
318                 
319                 // Convert from old version to new
320                 if(version <= 18)
321                 {
322                         // In these versions, CONTENT_IGNORE and CONTENT_AIR
323                         // are 255 and 254
324                         if(d == 255)
325                                 d = CONTENT_IGNORE;
326                         else if(d == 254)
327                                 d = CONTENT_AIR;
328                 }
329         }
330 }
331
332 /*
333         Gets lighting value at face of node
334         
335         Parameters must consist of air and !air.
336         Order doesn't matter.
337
338         If either of the nodes doesn't exist, light is 0.
339         
340         parameters:
341                 daynight_ratio: 0...1000
342                 n: getNodeParent(p)
343                 n2: getNodeParent(p + face_dir)
344                 face_dir: axis oriented unit vector from p to p2
345         
346         returns encoded light value.
347 */
348 u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
349                 v3s16 face_dir)
350 {
351         try{
352                 u8 light;
353                 u8 l1 = n.getLightBlend(daynight_ratio);
354                 u8 l2 = n2.getLightBlend(daynight_ratio);
355                 if(l1 > l2)
356                         light = l1;
357                 else
358                         light = l2;
359
360                 // Make some nice difference to different sides
361
362                 // This makes light come from a corner
363                 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
364                         light = diminish_light(diminish_light(light));
365                 else if(face_dir.X == -1 || face_dir.Z == -1)
366                         light = diminish_light(light);*/
367                 
368                 // All neighboring faces have different shade (like in minecraft)
369                 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
370                         light = diminish_light(diminish_light(light));
371                 else if(face_dir.Z == 1 || face_dir.Z == -1)
372                         light = diminish_light(light);
373
374                 return light;
375         }
376         catch(InvalidPositionException &e)
377         {
378                 return 0;
379         }
380 }
381
382