]> git.lizzy.rs Git - dragonfireclient.git/blob - src/mapnode.h
Now texture handling is fast. Also now players are saved on disk.
[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 #include "irrlichtwrapper.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(IrrlichtWrapper *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         void setAllTextures(const TextureSpec &spec, u8 alpha=255)
171         {
172                 for(u16 i=0; i<6; i++)
173                 {
174                         tiles[i].spec = spec;
175                         tiles[i].alpha = alpha;
176                 }
177                 
178                 // Set this too so it can be left as is most times
179                 /*if(inventory_image_path == "")
180                         inventory_image_path = porting::getDataPath(imgname.c_str());*/
181
182                 if(inventory_texture.empty())
183                         inventory_texture = spec;
184         }
185         void setTexture(u16 i, const TextureSpec &spec, u8 alpha=255)
186         {
187                 tiles[i].spec = spec;
188                 tiles[i].alpha = alpha;
189         }
190
191         void setInventoryTexture(const TextureSpec &spec)
192         {
193                 inventory_texture = spec;
194         }
195
196         /*void setInventoryImage(std::string imgname)
197         {
198                 inventory_image_path = porting::getDataPath(imgname.c_str());
199         }*/
200 };
201
202 /*
203         Call this to access the ContentFeature list
204 */
205 ContentFeatures & content_features(u8 i);
206
207 /*
208         If true, the material allows light propagation and brightness is stored
209         in param.
210         NOTE: Don't use, use "content_features(m).whatever" instead
211 */
212 inline bool light_propagates_content(u8 m)
213 {
214         return content_features(m).light_propagates;
215         //return (m == CONTENT_AIR || m == CONTENT_TORCH || m == CONTENT_WATER || m == CONTENT_WATERSOURCE);
216 }
217
218 /*
219         If true, the material allows lossless sunlight propagation.
220         NOTE: It doesn't seem to go through torches regardlessly of this
221         NOTE: Don't use, use "content_features(m).whatever" instead
222 */
223 inline bool sunlight_propagates_content(u8 m)
224 {
225         return content_features(m).sunlight_propagates;
226         //return (m == CONTENT_AIR || m == CONTENT_TORCH);
227 }
228
229 /*
230         On a node-node surface, the material of the node with higher solidness
231         is used for drawing.
232         0: Invisible
233         1: Transparent
234         2: Opaque
235         NOTE: Don't use, use "content_features(m).whatever" instead
236 */
237 inline u8 content_solidness(u8 m)
238 {
239         return content_features(m).solidness;
240         /*// As of now, every pseudo node like torches are added to this
241         if(m == CONTENT_AIR || m == CONTENT_TORCH || m == CONTENT_WATER)
242                 return 0;
243         if(m == CONTENT_WATER || m == CONTENT_WATERSOURCE)
244                 return 1;
245         return 2;*/
246 }
247
248 // Objects collide with walkable contents
249 // NOTE: Don't use, use "content_features(m).whatever" instead
250 inline bool content_walkable(u8 m)
251 {
252         return content_features(m).walkable;
253         //return (m != CONTENT_AIR && m != CONTENT_WATER && m != CONTENT_WATERSOURCE && m != CONTENT_TORCH);
254 }
255
256 // NOTE: Don't use, use "content_features(m).whatever" instead
257 inline bool content_liquid(u8 m)
258 {
259         return content_features(m).liquid_type != LIQUID_NONE;
260         //return (m == CONTENT_WATER || m == CONTENT_WATERSOURCE);
261 }
262
263 // NOTE: Don't use, use "content_features(m).whatever" instead
264 inline bool content_flowing_liquid(u8 m)
265 {
266         return content_features(m).liquid_type == LIQUID_FLOWING;
267         //return (m == CONTENT_WATER);
268 }
269
270 // NOTE: Don't use, use "content_features(m).whatever" instead
271 inline bool content_liquid_source(u8 m)
272 {
273         return content_features(m).liquid_type == LIQUID_SOURCE;
274         //return (m == CONTENT_WATERSOURCE);
275 }
276
277 // CONTENT_WATER || CONTENT_WATERSOURCE -> CONTENT_WATER
278 // CONTENT_LAVA || CONTENT_LAVASOURCE -> CONTENT_LAVA
279 inline u8 make_liquid_flowing(u8 m)
280 {
281         if(m == CONTENT_WATER || m == CONTENT_WATERSOURCE)
282                 return CONTENT_WATER;
283         assert(0);
284 }
285
286 // Pointable contents can be pointed to in the map
287 // NOTE: Don't use, use "content_features(m).whatever" instead
288 inline bool content_pointable(u8 m)
289 {
290         return content_features(m).pointable;
291         //return (m != CONTENT_AIR && m != CONTENT_WATER && m != CONTENT_WATERSOURCE);
292 }
293
294 // NOTE: Don't use, use "content_features(m).whatever" instead
295 inline bool content_diggable(u8 m)
296 {
297         return content_features(m).diggable;
298         //return (m != CONTENT_AIR && m != CONTENT_WATER && m != CONTENT_WATERSOURCE);
299 }
300
301 // NOTE: Don't use, use "content_features(m).whatever" instead
302 inline bool content_buildable_to(u8 m)
303 {
304         return content_features(m).buildable_to;
305         //return (m == CONTENT_AIR || m == CONTENT_WATER || m == CONTENT_WATERSOURCE);
306 }
307
308 /*
309         Returns true for contents that form the base ground that
310         follows the main heightmap
311 */
312 /*inline bool is_ground_content(u8 m)
313 {
314         return content_features(m).is_ground_content;
315 }*/
316
317 /*
318         Nodes make a face if contents differ and solidness differs.
319         Return value:
320                 0: No face
321                 1: Face uses m1's content
322                 2: Face uses m2's content
323 */
324 inline u8 face_contents(u8 m1, u8 m2)
325 {
326         if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
327                 return 0;
328         
329         bool contents_differ = (m1 != m2);
330         
331         // Contents don't differ for different forms of same liquid
332         if(content_liquid(m1) && content_liquid(m2)
333                         && make_liquid_flowing(m1) == make_liquid_flowing(m2))
334                 contents_differ = false;
335         
336         bool solidness_differs = (content_solidness(m1) != content_solidness(m2));
337         bool makes_face = contents_differ && solidness_differs;
338
339         if(makes_face == false)
340                 return 0;
341
342         if(content_solidness(m1) > content_solidness(m2))
343                 return 1;
344         else
345                 return 2;
346 }
347
348 /*
349         Packs directions like (1,0,0), (1,-1,0)
350 */
351 inline u8 packDir(v3s16 dir)
352 {
353         u8 b = 0;
354
355         if(dir.X > 0)
356                 b |= (1<<0);
357         else if(dir.X < 0)
358                 b |= (1<<1);
359
360         if(dir.Y > 0)
361                 b |= (1<<2);
362         else if(dir.Y < 0)
363                 b |= (1<<3);
364
365         if(dir.Z > 0)
366                 b |= (1<<4);
367         else if(dir.Z < 0)
368                 b |= (1<<5);
369         
370         return b;
371 }
372 inline v3s16 unpackDir(u8 b)
373 {
374         v3s16 d(0,0,0);
375
376         if(b & (1<<0))
377                 d.X = 1;
378         else if(b & (1<<1))
379                 d.X = -1;
380
381         if(b & (1<<2))
382                 d.Y = 1;
383         else if(b & (1<<3))
384                 d.Y = -1;
385
386         if(b & (1<<4))
387                 d.Z = 1;
388         else if(b & (1<<5))
389                 d.Z = -1;
390         
391         return d;
392 }
393
394 enum LightBank
395 {
396         LIGHTBANK_DAY,
397         LIGHTBANK_NIGHT
398 };
399
400 /*
401         This is the stuff what the whole world consists of.
402 */
403
404 struct MapNode
405 {
406         // Content
407         u8 d;
408
409         /*
410                 Misc parameter. Initialized to 0.
411                 - For light_propagates() blocks, this is light intensity,
412                   stored logarithmically from 0 to LIGHT_MAX.
413                   Sunlight is LIGHT_SUN, which is LIGHT_MAX+1.
414                 - Contains 2 values, day- and night lighting. Each takes 4 bits.
415         */
416         s8 param;
417         
418         union
419         {
420                 u8 param2;
421
422                 /*
423                         Direction for torches and other stuff.
424                         Format is freeform. e.g. packDir or encode_dirs can be used.
425                 */
426                 u8 dir;
427         };
428
429         MapNode(const MapNode & n)
430         {
431                 *this = n;
432         }
433         
434         MapNode(u8 data=CONTENT_AIR, u8 a_param=0, u8 a_param2=0)
435         {
436                 d = data;
437                 param = a_param;
438                 param2 = a_param2;
439         }
440
441         bool operator==(const MapNode &other)
442         {
443                 return (d == other.d
444                                 && param == other.param
445                                 && param2 == other.param2);
446         }
447
448         bool light_propagates()
449         {
450                 return light_propagates_content(d);
451         }
452         
453         bool sunlight_propagates()
454         {
455                 return sunlight_propagates_content(d);
456         }
457         
458         u8 solidness()
459         {
460                 return content_solidness(d);
461         }
462
463         u8 light_source()
464         {
465                 /*
466                         Note that a block that isn't light_propagates() can be a light source.
467                 */
468                 if(d == CONTENT_TORCH)
469                         return LIGHT_MAX;
470                 
471                 return 0;
472         }
473
474         u8 getLightBanksWithSource()
475         {
476                 // Select the brightest of [light source, propagated light]
477                 u8 lightday = 0;
478                 u8 lightnight = 0;
479                 if(light_propagates())
480                 {
481                         lightday = param & 0x0f;
482                         lightnight = (param>>4)&0x0f;
483                 }
484                 if(light_source() > lightday)
485                         lightday = light_source();
486                 if(light_source() > lightnight)
487                         lightnight = light_source();
488                 return (lightday&0x0f) | ((lightnight<<4)&0xf0);
489         }
490
491         void setLightBanks(u8 a_light)
492         {
493                 param = a_light;
494         }
495
496         u8 getLight(enum LightBank bank)
497         {
498                 // Select the brightest of [light source, propagated light]
499                 u8 light = 0;
500                 if(light_propagates())
501                 {
502                         if(bank == LIGHTBANK_DAY)
503                                 light = param & 0x0f;
504                         else if(bank == LIGHTBANK_NIGHT)
505                                 light = (param>>4)&0x0f;
506                         else
507                                 assert(0);
508                 }
509                 if(light_source() > light)
510                         light = light_source();
511                 return light;
512         }
513         
514         // 0 <= daylight_factor <= 1000
515         // 0 <= return value <= LIGHT_SUN
516         u8 getLightBlend(u32 daylight_factor)
517         {
518                 u8 l = ((daylight_factor * getLight(LIGHTBANK_DAY)
519                         + (1000-daylight_factor) * getLight(LIGHTBANK_NIGHT))
520                         )/1000;
521                 u8 max = LIGHT_MAX;
522                 if(getLight(LIGHTBANK_DAY) == LIGHT_SUN)
523                         max = LIGHT_SUN;
524                 if(l > max)
525                         l = max;
526                 return l;
527         }
528         /*// 0 <= daylight_factor <= 1000
529         // 0 <= return value <= 255
530         u8 getLightBlend(u32 daylight_factor)
531         {
532                 u8 daylight = decode_light(getLight(LIGHTBANK_DAY));
533                 u8 nightlight = decode_light(getLight(LIGHTBANK_NIGHT));
534                 u8 mix = ((daylight_factor * daylight
535                         + (1000-daylight_factor) * nightlight)
536                         )/1000;
537                 return mix;
538         }*/
539
540         void setLight(enum LightBank bank, u8 a_light)
541         {
542                 // If not transparent, can't set light
543                 if(light_propagates() == false)
544                         return;
545                 if(bank == LIGHTBANK_DAY)
546                 {
547                         param &= 0xf0;
548                         param |= a_light & 0x0f;
549                 }
550                 else if(bank == LIGHTBANK_NIGHT)
551                 {
552                         param &= 0x0f;
553                         param |= (a_light & 0x0f)<<4;
554                 }
555                 else
556                         assert(0);
557         }
558         
559         // In mapnode.cpp
560         TileSpec getTile(v3s16 dir);
561
562         u8 getMineral();
563
564         /*
565                 These serialization functions are used when informing client
566                 of a single node add
567         */
568
569         static u32 serializedLength(u8 version)
570         {
571                 if(!ser_ver_supported(version))
572                         throw VersionMismatchException("ERROR: MapNode format not supported");
573                         
574                 if(version == 0)
575                         return 1;
576                 else if(version <= 9)
577                         return 2;
578                 else
579                         return 3;
580         }
581         void serialize(u8 *dest, u8 version)
582         {
583                 if(!ser_ver_supported(version))
584                         throw VersionMismatchException("ERROR: MapNode format not supported");
585                         
586                 if(version == 0)
587                 {
588                         dest[0] = d;
589                 }
590                 else if(version <= 9)
591                 {
592                         dest[0] = d;
593                         dest[1] = param;
594                 }
595                 else
596                 {
597                         dest[0] = d;
598                         dest[1] = param;
599                         dest[2] = param2;
600                 }
601         }
602         void deSerialize(u8 *source, u8 version)
603         {
604                 if(!ser_ver_supported(version))
605                         throw VersionMismatchException("ERROR: MapNode format not supported");
606                         
607                 if(version == 0)
608                 {
609                         d = source[0];
610                 }
611                 else if(version == 1)
612                 {
613                         d = source[0];
614                         // This version doesn't support saved lighting
615                         if(light_propagates() || light_source() > 0)
616                                 param = 0;
617                         else
618                                 param = source[1];
619                 }
620                 else if(version <= 9)
621                 {
622                         d = source[0];
623                         param = source[1];
624                 }
625                 else
626                 {
627                         d = source[0];
628                         param = source[1];
629                         param2 = source[2];
630                 }
631
632                 // Translate deprecated stuff
633                 MapNode *translate_to = content_features(d).translate_to;
634                 if(translate_to)
635                 {
636                         dstream<<"MapNode: WARNING: Translating "<<d<<" to "
637                                         <<translate_to->d<<std::endl;
638                         *this = *translate_to;
639                 }
640         }
641 };
642
643 /*
644         Returns integer position of the node in given
645         floating point position.
646 */
647 inline v3s16 floatToInt(v3f p)
648 {
649         v3s16 p2(
650                 (p.X + (p.X>0 ? BS/2 : -BS/2))/BS,
651                 (p.Y + (p.Y>0 ? BS/2 : -BS/2))/BS,
652                 (p.Z + (p.Z>0 ? BS/2 : -BS/2))/BS);
653         return p2;
654 }
655
656 /*
657         The same thing backwards
658 */
659 inline v3f intToFloat(v3s16 p)
660 {
661         v3f p2(
662                 p.X * BS,
663                 p.Y * BS,
664                 p.Z * BS
665         );
666         return p2;
667 }
668
669
670
671 #endif
672