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