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