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