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