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