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