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