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