]> git.lizzy.rs Git - minetest.git/blob - src/mapnode.h
bug-fixin'
[minetest.git] / src / mapnode.h
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 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 #ifndef MAPNODE_HEADER
21 #define MAPNODE_HEADER
22
23 #include <iostream>
24 #include "common_irrlicht.h"
25 #include "light.h"
26 #include "utility.h"
27 #include "exceptions.h"
28 #include "serialization.h"
29 #include "tile.h"
30 #include "iirrlichtwrapper.h"
31
32 /*
33         Initializes all kind of stuff in here.
34         Many things depend on this.
35
36         irrlicht: Used for getting texture ids.
37 */
38 void init_mapnode(IIrrlichtWrapper *irrlicht);
39
40 // Initializes g_content_inventory_texture_paths
41 void init_content_inventory_texture_paths();
42
43
44 // NOTE: This is not used appropriately everywhere.
45 #define MATERIALS_COUNT 256
46
47 /*
48         Ignored node.
49
50         Anything that stores MapNodes doesn't have to preserve parameters
51         associated with this material.
52         
53         Doesn't create faces with anything and is considered being
54         out-of-map in the game map.
55 */
56 #define CONTENT_IGNORE 255
57 #define CONTENT_IGNORE_DEFAULT_PARAM 0
58
59 /*
60         The common material through which the player can walk and which
61         is transparent to light
62 */
63 #define CONTENT_AIR 254
64
65 /*
66         Suggested materials:
67         - Gravel
68         - Sand
69         
70         New naming scheme:
71         - Material = irrlicht's Material class
72         - Content = (u8) content of a node
73         - Tile = (u16) Material ID at some side of a node
74 */
75
76 #define CONTENT_STONE 0
77 #define CONTENT_GRASS 1
78 #define CONTENT_WATER 2
79 #define CONTENT_TORCH 3
80 #define CONTENT_TREE 4
81 #define CONTENT_LEAVES 5
82 #define CONTENT_GRASS_FOOTSTEPS 6
83 #define CONTENT_MESE 7
84 #define CONTENT_MUD 8
85 #define CONTENT_WATERSOURCE 9
86 // Pretty much useless, clouds won't be drawn this way
87 #define CONTENT_CLOUD 10
88 #define CONTENT_COALSTONE 11
89 #define CONTENT_WOOD 12
90 #define CONTENT_SAND 13
91
92 /*
93         This is used by all kinds of things to allocate memory for all
94         contents except CONTENT_AIR and CONTENT_IGNORE
95 */
96 #define USEFUL_CONTENT_COUNT 14
97
98 /*
99         Content feature list
100 */
101
102 enum ContentParamType
103 {
104         CPT_NONE,
105         CPT_LIGHT,
106         CPT_MINERAL
107 };
108
109 enum LiquidType
110 {
111         LIQUID_NONE,
112         LIQUID_FLOWING,
113         LIQUID_SOURCE
114 };
115
116 class MapNode;
117
118 struct ContentFeatures
119 {
120         // If non-NULL, content is translated to this when deserialized
121         MapNode *translate_to;
122
123         // Type of MapNode::param
124         ContentParamType param_type;
125
126         /*
127                 0: up
128                 1: down
129                 2: right
130                 3: left
131                 4: back
132                 5: front
133         */
134         TileSpec tiles[6];
135
136         //std::string inventory_image_path;
137         TextureSpec inventory_texture;
138
139         bool is_ground_content; //TODO: Remove, use walkable instead
140         bool light_propagates;
141         bool sunlight_propagates;
142         u8 solidness; // Used when choosing which face is drawn
143         bool walkable;
144         bool pointable;
145         bool diggable;
146         bool buildable_to;
147         enum LiquidType liquid_type;
148         bool wall_mounted; // If true, param2 is set to direction when placed
149
150         //TODO: Move more properties here
151
152         ContentFeatures()
153         {
154                 translate_to = NULL;
155                 param_type = CPT_NONE;
156                 is_ground_content = false;
157                 light_propagates = false;
158                 sunlight_propagates = false;
159                 solidness = 2;
160                 walkable = true;
161                 pointable = true;
162                 diggable = true;
163                 buildable_to = false;
164                 liquid_type = LIQUID_NONE;
165                 wall_mounted = false;
166         }
167
168         ~ContentFeatures();
169         
170         // Quickhands for simple materials
171         void setTexture(u16 i, const TextureSpec &spec, u8 alpha=255)
172         {
173                 tiles[i].spec = spec;
174                 if(alpha != 255)
175                 {
176                         tiles[i].alpha = alpha;
177                         tiles[i].material_type = MATERIAL_ALPHA_VERTEX;
178                 }
179         }
180         void setAllTextures(const TextureSpec &spec, u8 alpha=255)
181         {
182                 for(u16 i=0; i<6; i++)
183                 {
184                         setTexture(i, spec, alpha);
185                 }
186                 
187                 // Set this too so it can be left as is most times
188                 if(inventory_texture.empty())
189                         inventory_texture = spec;
190         }
191
192         void setTile(u16 i, const TileSpec &tile)
193         {
194                 tiles[i] = tile;
195         }
196         void setAllTiles(const TileSpec &tile)
197         {
198                 for(u16 i=0; i<6; i++)
199                 {
200                         setTile(i, tile);
201                 }
202         }
203
204         void setInventoryTexture(const TextureSpec &spec)
205         {
206                 inventory_texture = spec;
207         }
208
209         /*void setInventoryImage(std::string imgname)
210         {
211                 inventory_image_path = porting::getDataPath(imgname.c_str());
212         }*/
213 };
214
215 /*
216         Call this to access the ContentFeature list
217 */
218 ContentFeatures & content_features(u8 i);
219
220 /*
221         If true, the material allows light propagation and brightness is stored
222         in param.
223         NOTE: Don't use, use "content_features(m).whatever" instead
224 */
225 inline bool light_propagates_content(u8 m)
226 {
227         return content_features(m).light_propagates;
228         //return (m == CONTENT_AIR || m == CONTENT_TORCH || m == CONTENT_WATER || m == CONTENT_WATERSOURCE);
229 }
230
231 /*
232         If true, the material allows lossless sunlight propagation.
233         NOTE: It doesn't seem to go through torches regardlessly of this
234         NOTE: Don't use, use "content_features(m).whatever" instead
235 */
236 inline bool sunlight_propagates_content(u8 m)
237 {
238         return content_features(m).sunlight_propagates;
239         //return (m == CONTENT_AIR || m == CONTENT_TORCH);
240 }
241
242 /*
243         On a node-node surface, the material of the node with higher solidness
244         is used for drawing.
245         0: Invisible
246         1: Transparent
247         2: Opaque
248         NOTE: Don't use, use "content_features(m).whatever" instead
249 */
250 inline u8 content_solidness(u8 m)
251 {
252         return content_features(m).solidness;
253         /*// As of now, every pseudo node like torches are added to this
254         if(m == CONTENT_AIR || m == CONTENT_TORCH || m == CONTENT_WATER)
255                 return 0;
256         if(m == CONTENT_WATER || m == CONTENT_WATERSOURCE)
257                 return 1;
258         return 2;*/
259 }
260
261 // Objects collide with walkable contents
262 // NOTE: Don't use, use "content_features(m).whatever" instead
263 inline bool content_walkable(u8 m)
264 {
265         return content_features(m).walkable;
266         //return (m != CONTENT_AIR && m != CONTENT_WATER && m != CONTENT_WATERSOURCE && m != CONTENT_TORCH);
267 }
268
269 // NOTE: Don't use, use "content_features(m).whatever" instead
270 inline bool content_liquid(u8 m)
271 {
272         return content_features(m).liquid_type != LIQUID_NONE;
273         //return (m == CONTENT_WATER || m == CONTENT_WATERSOURCE);
274 }
275
276 // NOTE: Don't use, use "content_features(m).whatever" instead
277 inline bool content_flowing_liquid(u8 m)
278 {
279         return content_features(m).liquid_type == LIQUID_FLOWING;
280         //return (m == CONTENT_WATER);
281 }
282
283 // NOTE: Don't use, use "content_features(m).whatever" instead
284 inline bool content_liquid_source(u8 m)
285 {
286         return content_features(m).liquid_type == LIQUID_SOURCE;
287         //return (m == CONTENT_WATERSOURCE);
288 }
289
290 // CONTENT_WATER || CONTENT_WATERSOURCE -> CONTENT_WATER
291 // CONTENT_LAVA || CONTENT_LAVASOURCE -> CONTENT_LAVA
292 inline u8 make_liquid_flowing(u8 m)
293 {
294         if(m == CONTENT_WATER || m == CONTENT_WATERSOURCE)
295                 return CONTENT_WATER;
296         assert(0);
297 }
298
299 // Pointable contents can be pointed to in the map
300 // NOTE: Don't use, use "content_features(m).whatever" instead
301 inline bool content_pointable(u8 m)
302 {
303         return content_features(m).pointable;
304         //return (m != CONTENT_AIR && m != CONTENT_WATER && m != CONTENT_WATERSOURCE);
305 }
306
307 // NOTE: Don't use, use "content_features(m).whatever" instead
308 inline bool content_diggable(u8 m)
309 {
310         return content_features(m).diggable;
311         //return (m != CONTENT_AIR && m != CONTENT_WATER && m != CONTENT_WATERSOURCE);
312 }
313
314 // NOTE: Don't use, use "content_features(m).whatever" instead
315 inline bool content_buildable_to(u8 m)
316 {
317         return content_features(m).buildable_to;
318         //return (m == CONTENT_AIR || m == CONTENT_WATER || m == CONTENT_WATERSOURCE);
319 }
320
321 /*
322         Returns true for contents that form the base ground that
323         follows the main heightmap
324 */
325 /*inline bool is_ground_content(u8 m)
326 {
327         return content_features(m).is_ground_content;
328 }*/
329
330 /*
331         Nodes make a face if contents differ and solidness differs.
332         Return value:
333                 0: No face
334                 1: Face uses m1's content
335                 2: Face uses m2's content
336 */
337 inline u8 face_contents(u8 m1, u8 m2)
338 {
339         if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
340                 return 0;
341         
342         bool contents_differ = (m1 != m2);
343         
344         // Contents don't differ for different forms of same liquid
345         if(content_liquid(m1) && content_liquid(m2)
346                         && make_liquid_flowing(m1) == make_liquid_flowing(m2))
347                 contents_differ = false;
348         
349         bool solidness_differs = (content_solidness(m1) != content_solidness(m2));
350         bool makes_face = contents_differ && solidness_differs;
351
352         if(makes_face == false)
353                 return 0;
354
355         if(content_solidness(m1) > content_solidness(m2))
356                 return 1;
357         else
358                 return 2;
359 }
360
361 /*
362         Packs directions like (1,0,0), (1,-1,0)
363 */
364 inline u8 packDir(v3s16 dir)
365 {
366         u8 b = 0;
367
368         if(dir.X > 0)
369                 b |= (1<<0);
370         else if(dir.X < 0)
371                 b |= (1<<1);
372
373         if(dir.Y > 0)
374                 b |= (1<<2);
375         else if(dir.Y < 0)
376                 b |= (1<<3);
377
378         if(dir.Z > 0)
379                 b |= (1<<4);
380         else if(dir.Z < 0)
381                 b |= (1<<5);
382         
383         return b;
384 }
385 inline v3s16 unpackDir(u8 b)
386 {
387         v3s16 d(0,0,0);
388
389         if(b & (1<<0))
390                 d.X = 1;
391         else if(b & (1<<1))
392                 d.X = -1;
393
394         if(b & (1<<2))
395                 d.Y = 1;
396         else if(b & (1<<3))
397                 d.Y = -1;
398
399         if(b & (1<<4))
400                 d.Z = 1;
401         else if(b & (1<<5))
402                 d.Z = -1;
403         
404         return d;
405 }
406
407 enum LightBank
408 {
409         LIGHTBANK_DAY,
410         LIGHTBANK_NIGHT
411 };
412
413 /*
414         This is the stuff what the whole world consists of.
415 */
416
417 struct MapNode
418 {
419         // Content
420         u8 d;
421
422         /*
423                 Misc parameter. Initialized to 0.
424                 - For light_propagates() blocks, this is light intensity,
425                   stored logarithmically from 0 to LIGHT_MAX.
426                   Sunlight is LIGHT_SUN, which is LIGHT_MAX+1.
427                 - Contains 2 values, day- and night lighting. Each takes 4 bits.
428         */
429         s8 param;
430         
431         union
432         {
433                 /*
434                         The second parameter. Initialized to 0.
435                         Direction for torches and flowing water.
436                 */
437                 u8 param2;
438                 u8 dir;
439         };
440
441         MapNode(const MapNode & n)
442         {
443                 *this = n;
444         }
445         
446         MapNode(u8 data=CONTENT_AIR, u8 a_param=0, u8 a_param2=0)
447         {
448                 d = data;
449                 param = a_param;
450                 param2 = a_param2;
451         }
452
453         /*MapNode & operator=(const MapNode &other)
454         {
455                 d = other.d;
456                 param = other.param;
457                 param2 = other.param2;
458                 return *this;
459         }*/
460
461         bool operator==(const MapNode &other)
462         {
463                 return (d == other.d
464                                 && param == other.param
465                                 && param2 == other.param2);
466         }
467
468         bool light_propagates()
469         {
470                 return light_propagates_content(d);
471         }
472         
473         bool sunlight_propagates()
474         {
475                 return sunlight_propagates_content(d);
476         }
477         
478         u8 solidness()
479         {
480                 return content_solidness(d);
481         }
482
483         u8 light_source()
484         {
485                 /*
486                         Note that a block that isn't light_propagates() can be a light source.
487                 */
488                 if(d == CONTENT_TORCH)
489                         return LIGHT_MAX;
490                 
491                 return 0;
492         }
493
494         u8 getLightBanksWithSource()
495         {
496                 // Select the brightest of [light source, propagated light]
497                 u8 lightday = 0;
498                 u8 lightnight = 0;
499                 if(light_propagates())
500                 {
501                         lightday = param & 0x0f;
502                         lightnight = (param>>4)&0x0f;
503                 }
504                 if(light_source() > lightday)
505                         lightday = light_source();
506                 if(light_source() > lightnight)
507                         lightnight = light_source();
508                 return (lightday&0x0f) | ((lightnight<<4)&0xf0);
509         }
510
511         void setLightBanks(u8 a_light)
512         {
513                 param = a_light;
514         }
515
516         u8 getLight(enum LightBank bank)
517         {
518                 // Select the brightest of [light source, propagated light]
519                 u8 light = 0;
520                 if(light_propagates())
521                 {
522                         if(bank == LIGHTBANK_DAY)
523                                 light = param & 0x0f;
524                         else if(bank == LIGHTBANK_NIGHT)
525                                 light = (param>>4)&0x0f;
526                         else
527                                 assert(0);
528                 }
529                 if(light_source() > light)
530                         light = light_source();
531                 return light;
532         }
533         
534         // 0 <= daylight_factor <= 1000
535         // 0 <= return value <= LIGHT_SUN
536         u8 getLightBlend(u32 daylight_factor)
537         {
538                 u8 l = ((daylight_factor * getLight(LIGHTBANK_DAY)
539                         + (1000-daylight_factor) * getLight(LIGHTBANK_NIGHT))
540                         )/1000;
541                 u8 max = LIGHT_MAX;
542                 if(getLight(LIGHTBANK_DAY) == LIGHT_SUN)
543                         max = LIGHT_SUN;
544                 if(l > max)
545                         l = max;
546                 return l;
547         }
548         /*// 0 <= daylight_factor <= 1000
549         // 0 <= return value <= 255
550         u8 getLightBlend(u32 daylight_factor)
551         {
552                 u8 daylight = decode_light(getLight(LIGHTBANK_DAY));
553                 u8 nightlight = decode_light(getLight(LIGHTBANK_NIGHT));
554                 u8 mix = ((daylight_factor * daylight
555                         + (1000-daylight_factor) * nightlight)
556                         )/1000;
557                 return mix;
558         }*/
559
560         void setLight(enum LightBank bank, u8 a_light)
561         {
562                 // If not transparent, can't set light
563                 if(light_propagates() == false)
564                         return;
565                 if(bank == LIGHTBANK_DAY)
566                 {
567                         param &= 0xf0;
568                         param |= a_light & 0x0f;
569                 }
570                 else if(bank == LIGHTBANK_NIGHT)
571                 {
572                         param &= 0x0f;
573                         param |= (a_light & 0x0f)<<4;
574                 }
575                 else
576                         assert(0);
577         }
578         
579         // In mapnode.cpp
580         TileSpec getTile(v3s16 dir);
581
582         u8 getMineral();
583
584         /*
585                 These serialization functions are used when informing client
586                 of a single node add
587         */
588
589         static u32 serializedLength(u8 version)
590         {
591                 if(!ser_ver_supported(version))
592                         throw VersionMismatchException("ERROR: MapNode format not supported");
593                         
594                 if(version == 0)
595                         return 1;
596                 else if(version <= 9)
597                         return 2;
598                 else
599                         return 3;
600         }
601         void serialize(u8 *dest, u8 version)
602         {
603                 if(!ser_ver_supported(version))
604                         throw VersionMismatchException("ERROR: MapNode format not supported");
605                         
606                 if(version == 0)
607                 {
608                         dest[0] = d;
609                 }
610                 else if(version <= 9)
611                 {
612                         dest[0] = d;
613                         dest[1] = param;
614                 }
615                 else
616                 {
617                         dest[0] = d;
618                         dest[1] = param;
619                         dest[2] = param2;
620                 }
621         }
622         void deSerialize(u8 *source, u8 version)
623         {
624                 if(!ser_ver_supported(version))
625                         throw VersionMismatchException("ERROR: MapNode format not supported");
626                         
627                 if(version == 0)
628                 {
629                         d = source[0];
630                 }
631                 else if(version == 1)
632                 {
633                         d = source[0];
634                         // This version doesn't support saved lighting
635                         if(light_propagates() || light_source() > 0)
636                                 param = 0;
637                         else
638                                 param = source[1];
639                 }
640                 else if(version <= 9)
641                 {
642                         d = source[0];
643                         param = source[1];
644                 }
645                 else
646                 {
647                         d = source[0];
648                         param = source[1];
649                         param2 = source[2];
650                 }
651
652                 // Translate deprecated stuff
653                 // NOTE: This doesn't get used because MapBlock handles node
654                 // parameters directly
655                 MapNode *translate_to = content_features(d).translate_to;
656                 if(translate_to)
657                 {
658                         dstream<<"MapNode: WARNING: Translating "<<d<<" to "
659                                         <<translate_to->d<<std::endl;
660                         *this = *translate_to;
661                 }
662         }
663 };
664
665 /*
666         Returns integer position of the node in given
667         floating point position.
668 */
669 inline v3s16 floatToInt(v3f p)
670 {
671         v3s16 p2(
672                 (p.X + (p.X>0 ? BS/2 : -BS/2))/BS,
673                 (p.Y + (p.Y>0 ? BS/2 : -BS/2))/BS,
674                 (p.Z + (p.Z>0 ? BS/2 : -BS/2))/BS);
675         return p2;
676 }
677
678 /*
679         The same thing backwards
680 */
681 inline v3f intToFloat(v3s16 p)
682 {
683         v3f p2(
684                 p.X * BS,
685                 p.Y * BS,
686                 p.Z * BS
687         );
688         return p2;
689 }
690
691
692
693 #endif
694