Revert "Plantlike: Fix visual_scale being applied squared (#5115)"
[oweals/minetest.git] / src / clientenvironment.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2017 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 Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser 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 #include "util/serialize.h"
21 #include "util/pointedthing.h"
22 #include "clientenvironment.h"
23 #include "clientsimpleobject.h"
24 #include "clientmap.h"
25 #include "mapblock_mesh.h"
26 #include "event.h"
27 #include "collision.h"
28 #include "profiler.h"
29 #include "raycast.h"
30 #include "voxelalgorithms.h"
31 #include "settings.h"
32
33 /*
34         ClientEnvironment
35 */
36
37 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
38         ITextureSource *texturesource, Client *client,
39         IrrlichtDevice *irr):
40         m_map(map),
41         m_local_player(NULL),
42         m_smgr(smgr),
43         m_texturesource(texturesource),
44         m_client(client),
45         m_irr(irr)
46 {
47         char zero = 0;
48         memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
49 }
50
51 ClientEnvironment::~ClientEnvironment()
52 {
53         // delete active objects
54         for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin();
55                 i != m_active_objects.end(); ++i) {
56                 delete i->second;
57         }
58
59         for(std::vector<ClientSimpleObject*>::iterator
60                 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
61                 delete *i;
62         }
63
64         // Drop/delete map
65         m_map->drop();
66 }
67
68 Map & ClientEnvironment::getMap()
69 {
70         return *m_map;
71 }
72
73 ClientMap & ClientEnvironment::getClientMap()
74 {
75         return *m_map;
76 }
77
78 void ClientEnvironment::setLocalPlayer(LocalPlayer *player)
79 {
80         DSTACK(FUNCTION_NAME);
81         /*
82                 It is a failure if already is a local player
83         */
84         FATAL_ERROR_IF(m_local_player != NULL,
85                 "Local player already allocated");
86
87         m_local_player = player;
88 }
89
90 void ClientEnvironment::step(float dtime)
91 {
92         DSTACK(FUNCTION_NAME);
93
94         /* Step time of day */
95         stepTimeOfDay(dtime);
96
97         // Get some settings
98         bool fly_allowed = m_client->checkLocalPrivilege("fly");
99         bool free_move = fly_allowed && g_settings->getBool("free_move");
100
101         // Get local player
102         LocalPlayer *lplayer = getLocalPlayer();
103         assert(lplayer);
104         // collision info queue
105         std::vector<CollisionInfo> player_collisions;
106
107         /*
108                 Get the speed the player is going
109         */
110         bool is_climbing = lplayer->is_climbing;
111
112         f32 player_speed = lplayer->getSpeed().getLength();
113
114         /*
115                 Maximum position increment
116         */
117         //f32 position_max_increment = 0.05*BS;
118         f32 position_max_increment = 0.1*BS;
119
120         // Maximum time increment (for collision detection etc)
121         // time = distance / speed
122         f32 dtime_max_increment = 1;
123         if(player_speed > 0.001)
124                 dtime_max_increment = position_max_increment / player_speed;
125
126         // Maximum time increment is 10ms or lower
127         if(dtime_max_increment > 0.01)
128                 dtime_max_increment = 0.01;
129
130         // Don't allow overly huge dtime
131         if(dtime > 0.5)
132                 dtime = 0.5;
133
134         f32 dtime_downcount = dtime;
135
136         /*
137                 Stuff that has a maximum time increment
138         */
139
140         u32 loopcount = 0;
141         do
142         {
143                 loopcount++;
144
145                 f32 dtime_part;
146                 if(dtime_downcount > dtime_max_increment)
147                 {
148                         dtime_part = dtime_max_increment;
149                         dtime_downcount -= dtime_part;
150                 }
151                 else
152                 {
153                         dtime_part = dtime_downcount;
154                         /*
155                                 Setting this to 0 (no -=dtime_part) disables an infinite loop
156                                 when dtime_part is so small that dtime_downcount -= dtime_part
157                                 does nothing
158                         */
159                         dtime_downcount = 0;
160                 }
161
162                 /*
163                         Handle local player
164                 */
165
166                 {
167                         // Apply physics
168                         if(!free_move && !is_climbing)
169                         {
170                                 // Gravity
171                                 v3f speed = lplayer->getSpeed();
172                                 if(!lplayer->in_liquid)
173                                         speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
174
175                                 // Liquid floating / sinking
176                                 if(lplayer->in_liquid && !lplayer->swimming_vertical)
177                                         speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
178
179                                 // Liquid resistance
180                                 if(lplayer->in_liquid_stable || lplayer->in_liquid)
181                                 {
182                                         // How much the node's viscosity blocks movement, ranges between 0 and 1
183                                         // Should match the scale at which viscosity increase affects other liquid attributes
184                                         const f32 viscosity_factor = 0.3;
185
186                                         v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
187                                         f32 dl = d_wanted.getLength();
188                                         if(dl > lplayer->movement_liquid_fluidity_smooth)
189                                                 dl = lplayer->movement_liquid_fluidity_smooth;
190                                         dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
191
192                                         v3f d = d_wanted.normalize() * dl;
193                                         speed += d;
194                                 }
195
196                                 lplayer->setSpeed(speed);
197                         }
198
199                         /*
200                                 Move the lplayer.
201                                 This also does collision detection.
202                         */
203                         lplayer->move(dtime_part, this, position_max_increment,
204                                 &player_collisions);
205                 }
206         }
207         while(dtime_downcount > 0.001);
208
209         //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
210
211         for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
212                 i != player_collisions.end(); ++i) {
213                 CollisionInfo &info = *i;
214                 v3f speed_diff = info.new_speed - info.old_speed;;
215                 // Handle only fall damage
216                 // (because otherwise walking against something in fast_move kills you)
217                 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
218                         continue;
219                 // Get rid of other components
220                 speed_diff.X = 0;
221                 speed_diff.Z = 0;
222                 f32 pre_factor = 1; // 1 hp per node/s
223                 f32 tolerance = BS*14; // 5 without damage
224                 f32 post_factor = 1; // 1 hp per node/s
225                 if(info.type == COLLISION_NODE)
226                 {
227                         const ContentFeatures &f = m_client->ndef()->
228                                 get(m_map->getNodeNoEx(info.node_p));
229                         // Determine fall damage multiplier
230                         int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
231                         pre_factor = 1.0 + (float)addp/100.0;
232                 }
233                 float speed = pre_factor * speed_diff.getLength();
234                 if(speed > tolerance)
235                 {
236                         f32 damage_f = (speed - tolerance)/BS * post_factor;
237                         u16 damage = (u16)(damage_f+0.5);
238                         if(damage != 0){
239                                 damageLocalPlayer(damage, true);
240                                 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
241                                 m_client->event()->put(e);
242                         }
243                 }
244         }
245
246         /*
247                 A quick draft of lava damage
248         */
249         if(m_lava_hurt_interval.step(dtime, 1.0))
250         {
251                 v3f pf = lplayer->getPosition();
252
253                 // Feet, middle and head
254                 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
255                 MapNode n1 = m_map->getNodeNoEx(p1);
256                 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
257                 MapNode n2 = m_map->getNodeNoEx(p2);
258                 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
259                 MapNode n3 = m_map->getNodeNoEx(p3);
260
261                 u32 damage_per_second = 0;
262                 damage_per_second = MYMAX(damage_per_second,
263                         m_client->ndef()->get(n1).damage_per_second);
264                 damage_per_second = MYMAX(damage_per_second,
265                         m_client->ndef()->get(n2).damage_per_second);
266                 damage_per_second = MYMAX(damage_per_second,
267                         m_client->ndef()->get(n3).damage_per_second);
268
269                 if(damage_per_second != 0)
270                 {
271                         damageLocalPlayer(damage_per_second, true);
272                 }
273         }
274
275         // Protocol v29 make this behaviour obsolete
276         if (getGameDef()->getProtoVersion() < 29) {
277                 /*
278                         Drowning
279                 */
280                 if (m_drowning_interval.step(dtime, 2.0)) {
281                         v3f pf = lplayer->getPosition();
282
283                         // head
284                         v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS);
285                         MapNode n = m_map->getNodeNoEx(p);
286                         ContentFeatures c = m_client->ndef()->get(n);
287                         u8 drowning_damage = c.drowning;
288                         if (drowning_damage > 0 && lplayer->hp > 0) {
289                                 u16 breath = lplayer->getBreath();
290                                 if (breath > 10) {
291                                         breath = 11;
292                                 }
293                                 if (breath > 0) {
294                                         breath -= 1;
295                                 }
296                                 lplayer->setBreath(breath);
297                                 updateLocalPlayerBreath(breath);
298                         }
299
300                         if (lplayer->getBreath() == 0 && drowning_damage > 0) {
301                                 damageLocalPlayer(drowning_damage, true);
302                         }
303                 }
304                 if (m_breathing_interval.step(dtime, 0.5)) {
305                         v3f pf = lplayer->getPosition();
306
307                         // head
308                         v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS);
309                         MapNode n = m_map->getNodeNoEx(p);
310                         ContentFeatures c = m_client->ndef()->get(n);
311                         if (!lplayer->hp) {
312                                 lplayer->setBreath(11);
313                         } else if (c.drowning == 0) {
314                                 u16 breath = lplayer->getBreath();
315                                 if (breath <= 10) {
316                                         breath += 1;
317                                         lplayer->setBreath(breath);
318                                         updateLocalPlayerBreath(breath);
319                                 }
320                         }
321                 }
322         }
323
324         // Update lighting on local player (used for wield item)
325         u32 day_night_ratio = getDayNightRatio();
326         {
327                 // Get node at head
328
329                 // On InvalidPositionException, use this as default
330                 // (day: LIGHT_SUN, night: 0)
331                 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
332
333                 v3s16 p = lplayer->getLightPosition();
334                 node_at_lplayer = m_map->getNodeNoEx(p);
335
336                 u16 light = getInteriorLight(node_at_lplayer, 0, m_client->ndef());
337                 final_color_blend(&lplayer->light_color, light, day_night_ratio);
338         }
339
340         /*
341                 Step active objects and update lighting of them
342         */
343
344         g_profiler->avg("CEnv: num of objects", m_active_objects.size());
345         bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
346         for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin();
347                 i != m_active_objects.end(); ++i) {
348                 ClientActiveObject* obj = i->second;
349                 // Step object
350                 obj->step(dtime, this);
351
352                 if(update_lighting)
353                 {
354                         // Update lighting
355                         u8 light = 0;
356                         bool pos_ok;
357
358                         // Get node at head
359                         v3s16 p = obj->getLightPosition();
360                         MapNode n = m_map->getNodeNoEx(p, &pos_ok);
361                         if (pos_ok)
362                                 light = n.getLightBlend(day_night_ratio, m_client->ndef());
363                         else
364                                 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
365
366                         obj->updateLight(light);
367                 }
368         }
369
370         /*
371                 Step and handle simple objects
372         */
373         g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
374         for(std::vector<ClientSimpleObject*>::iterator
375                 i = m_simple_objects.begin(); i != m_simple_objects.end();) {
376                 std::vector<ClientSimpleObject*>::iterator cur = i;
377                 ClientSimpleObject *simple = *cur;
378
379                 simple->step(dtime);
380                 if(simple->m_to_be_removed) {
381                         delete simple;
382                         i = m_simple_objects.erase(cur);
383                 }
384                 else {
385                         ++i;
386                 }
387         }
388 }
389
390 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
391 {
392         m_simple_objects.push_back(simple);
393 }
394
395 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
396 {
397         ClientActiveObject *obj = getActiveObject(id);
398         if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
399                 return (GenericCAO*) obj;
400         else
401                 return NULL;
402 }
403
404 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
405 {
406         UNORDERED_MAP<u16, ClientActiveObject*>::iterator n = m_active_objects.find(id);
407         if (n == m_active_objects.end())
408                 return NULL;
409         return n->second;
410 }
411
412 bool isFreeClientActiveObjectId(const u16 id,
413         UNORDERED_MAP<u16, ClientActiveObject*> &objects)
414 {
415         if(id == 0)
416                 return false;
417
418         return objects.find(id) == objects.end();
419 }
420
421 u16 getFreeClientActiveObjectId(UNORDERED_MAP<u16, ClientActiveObject*> &objects)
422 {
423         //try to reuse id's as late as possible
424         static u16 last_used_id = 0;
425         u16 startid = last_used_id;
426         for(;;) {
427                 last_used_id ++;
428                 if (isFreeClientActiveObjectId(last_used_id, objects))
429                         return last_used_id;
430
431                 if (last_used_id == startid)
432                         return 0;
433         }
434 }
435
436 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
437 {
438         assert(object); // Pre-condition
439         if(object->getId() == 0)
440         {
441                 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
442                 if(new_id == 0)
443                 {
444                         infostream<<"ClientEnvironment::addActiveObject(): "
445                                 <<"no free ids available"<<std::endl;
446                         delete object;
447                         return 0;
448                 }
449                 object->setId(new_id);
450         }
451         if (!isFreeClientActiveObjectId(object->getId(), m_active_objects)) {
452                 infostream<<"ClientEnvironment::addActiveObject(): "
453                         <<"id is not free ("<<object->getId()<<")"<<std::endl;
454                 delete object;
455                 return 0;
456         }
457         infostream<<"ClientEnvironment::addActiveObject(): "
458                 <<"added (id="<<object->getId()<<")"<<std::endl;
459         m_active_objects[object->getId()] = object;
460         object->addToScene(m_smgr, m_texturesource, m_irr);
461         { // Update lighting immediately
462                 u8 light = 0;
463                 bool pos_ok;
464
465                 // Get node at head
466                 v3s16 p = object->getLightPosition();
467                 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
468                 if (pos_ok)
469                         light = n.getLightBlend(getDayNightRatio(), m_client->ndef());
470                 else
471                         light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
472
473                 object->updateLight(light);
474         }
475         return object->getId();
476 }
477
478 void ClientEnvironment::addActiveObject(u16 id, u8 type,
479         const std::string &init_data)
480 {
481         ClientActiveObject* obj =
482                 ClientActiveObject::create((ActiveObjectType) type, m_client, this);
483         if(obj == NULL)
484         {
485                 infostream<<"ClientEnvironment::addActiveObject(): "
486                         <<"id="<<id<<" type="<<type<<": Couldn't create object"
487                         <<std::endl;
488                 return;
489         }
490
491         obj->setId(id);
492
493         try
494         {
495                 obj->initialize(init_data);
496         }
497         catch(SerializationError &e)
498         {
499                 errorstream<<"ClientEnvironment::addActiveObject():"
500                         <<" id="<<id<<" type="<<type
501                         <<": SerializationError in initialize(): "
502                         <<e.what()
503                         <<": init_data="<<serializeJsonString(init_data)
504                         <<std::endl;
505         }
506
507         addActiveObject(obj);
508 }
509
510 void ClientEnvironment::removeActiveObject(u16 id)
511 {
512         verbosestream<<"ClientEnvironment::removeActiveObject(): "
513                 <<"id="<<id<<std::endl;
514         ClientActiveObject* obj = getActiveObject(id);
515         if (obj == NULL) {
516                 infostream<<"ClientEnvironment::removeActiveObject(): "
517                         <<"id="<<id<<" not found"<<std::endl;
518                 return;
519         }
520         obj->removeFromScene(true);
521         delete obj;
522         m_active_objects.erase(id);
523 }
524
525 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
526 {
527         ClientActiveObject *obj = getActiveObject(id);
528         if (obj == NULL) {
529                 infostream << "ClientEnvironment::processActiveObjectMessage():"
530                         << " got message for id=" << id << ", which doesn't exist."
531                         << std::endl;
532                 return;
533         }
534
535         try {
536                 obj->processMessage(data);
537         } catch (SerializationError &e) {
538                 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
539                         << " id=" << id << " type=" << obj->getType()
540                         << " SerializationError in processMessage(): " << e.what()
541                         << std::endl;
542         }
543 }
544
545 /*
546         Callbacks for activeobjects
547 */
548
549 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
550 {
551         LocalPlayer *lplayer = getLocalPlayer();
552         assert(lplayer);
553
554         if (handle_hp) {
555                 if (lplayer->hp > damage)
556                         lplayer->hp -= damage;
557                 else
558                         lplayer->hp = 0;
559         }
560
561         ClientEnvEvent event;
562         event.type = CEE_PLAYER_DAMAGE;
563         event.player_damage.amount = damage;
564         event.player_damage.send_to_server = handle_hp;
565         m_client_event_queue.push(event);
566 }
567
568 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
569 {
570         ClientEnvEvent event;
571         event.type = CEE_PLAYER_BREATH;
572         event.player_breath.amount = breath;
573         m_client_event_queue.push(event);
574 }
575
576 /*
577         Client likes to call these
578 */
579
580 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
581         std::vector<DistanceSortedActiveObject> &dest)
582 {
583         for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin();
584                 i != m_active_objects.end(); ++i) {
585                 ClientActiveObject* obj = i->second;
586
587                 f32 d = (obj->getPosition() - origin).getLength();
588
589                 if(d > max_d)
590                         continue;
591
592                 DistanceSortedActiveObject dso(obj, d);
593
594                 dest.push_back(dso);
595         }
596 }
597
598 ClientEnvEvent ClientEnvironment::getClientEvent()
599 {
600         ClientEnvEvent event;
601         if(m_client_event_queue.empty())
602                 event.type = CEE_NONE;
603         else {
604                 event = m_client_event_queue.front();
605                 m_client_event_queue.pop();
606         }
607         return event;
608 }
609
610 ClientActiveObject * ClientEnvironment::getSelectedActiveObject(
611         const core::line3d<f32> &shootline_on_map, v3f *intersection_point,
612         v3s16 *intersection_normal)
613 {
614         std::vector<DistanceSortedActiveObject> objects;
615         getActiveObjects(shootline_on_map.start,
616                 shootline_on_map.getLength() + 3, objects);
617         const v3f line_vector = shootline_on_map.getVector();
618
619         // Sort them.
620         // After this, the closest object is the first in the array.
621         std::sort(objects.begin(), objects.end());
622
623         /* Because objects can have different nodebox sizes,
624          * the object whose center is the nearest isn't necessarily
625          * the closest one. If an object is found, don't stop
626          * immediately. */
627
628         f32 d_min = shootline_on_map.getLength();
629         ClientActiveObject *nearest_obj = NULL;
630         for (u32 i = 0; i < objects.size(); i++) {
631                 ClientActiveObject *obj = objects[i].obj;
632
633                 aabb3f *selection_box = obj->getSelectionBox();
634                 if (selection_box == NULL)
635                         continue;
636
637                 v3f pos = obj->getPosition();
638
639                 aabb3f offsetted_box(selection_box->MinEdge + pos,
640                         selection_box->MaxEdge + pos);
641
642                 if (offsetted_box.getCenter().getDistanceFrom(
643                         shootline_on_map.start) > d_min + 9.6f*BS) {
644                         // Probably there is no active object that has bigger nodebox than
645                         // (-5.5,-5.5,-5.5,5.5,5.5,5.5)
646                         // 9.6 > 5.5*sqrt(3)
647                         break;
648                 }
649
650                 v3f current_intersection;
651                 v3s16 current_normal;
652                 if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
653                         &current_intersection, &current_normal)) {
654                         f32 d_current = current_intersection.getDistanceFrom(
655                                 shootline_on_map.start);
656                         if (d_current <= d_min) {
657                                 d_min = d_current;
658                                 nearest_obj = obj;
659                                 *intersection_point = current_intersection;
660                                 *intersection_normal = current_normal;
661                         }
662                 }
663         }
664
665         return nearest_obj;
666 }
667
668 /*
669         Check if a node is pointable
670 */
671 static inline bool isPointableNode(const MapNode &n,
672         INodeDefManager *ndef, bool liquids_pointable)
673 {
674         const ContentFeatures &features = ndef->get(n);
675         return features.pointable ||
676                 (liquids_pointable && features.isLiquid());
677 }
678
679 PointedThing ClientEnvironment::getPointedThing(
680         core::line3d<f32> shootline,
681         bool liquids_pointable,
682         bool look_for_object)
683 {
684         PointedThing result;
685
686         INodeDefManager *nodedef = m_map->getNodeDefManager();
687
688         core::aabbox3d<s16> maximal_exceed = nodedef->getSelectionBoxIntUnion();
689         // The code needs to search these nodes
690         core::aabbox3d<s16> search_range(-maximal_exceed.MaxEdge,
691                 -maximal_exceed.MinEdge);
692         // If a node is found, there might be a larger node behind.
693         // To find it, we have to go further.
694         s16 maximal_overcheck =
695                 std::max(abs(search_range.MinEdge.X), abs(search_range.MaxEdge.X))
696                         + std::max(abs(search_range.MinEdge.Y), abs(search_range.MaxEdge.Y))
697                         + std::max(abs(search_range.MinEdge.Z), abs(search_range.MaxEdge.Z));
698
699         const v3f original_vector = shootline.getVector();
700         const f32 original_length = original_vector.getLength();
701
702         f32 min_distance = original_length;
703
704         // First try to find an active object
705         if (look_for_object) {
706                 ClientActiveObject *selected_object = getSelectedActiveObject(
707                         shootline, &result.intersection_point,
708                         &result.intersection_normal);
709
710                 if (selected_object != NULL) {
711                         min_distance =
712                                 (result.intersection_point - shootline.start).getLength();
713
714                         result.type = POINTEDTHING_OBJECT;
715                         result.object_id = selected_object->getId();
716                 }
717         }
718
719         // Reduce shootline
720         if (original_length > 0) {
721                 shootline.end = shootline.start
722                         + shootline.getVector() / original_length * min_distance;
723         }
724
725         // Try to find a node that is closer than the selected active
726         // object (if it exists).
727
728         voxalgo::VoxelLineIterator iterator(shootline.start / BS,
729                 shootline.getVector() / BS);
730         v3s16 oldnode = iterator.m_current_node_pos;
731         // Indicates that a node was found.
732         bool is_node_found = false;
733         // If a node is found, it is possible that there's a node
734         // behind it with a large nodebox, so continue the search.
735         u16 node_foundcounter = 0;
736         // If a node is found, this is the center of the
737         // first nodebox the shootline meets.
738         v3f found_boxcenter(0, 0, 0);
739         // The untested nodes are in this range.
740         core::aabbox3d<s16> new_nodes;
741         while (true) {
742                 // Test the nodes around the current node in search_range.
743                 new_nodes = search_range;
744                 new_nodes.MinEdge += iterator.m_current_node_pos;
745                 new_nodes.MaxEdge += iterator.m_current_node_pos;
746
747                 // Only check new nodes
748                 v3s16 delta = iterator.m_current_node_pos - oldnode;
749                 if (delta.X > 0)
750                         new_nodes.MinEdge.X = new_nodes.MaxEdge.X;
751                 else if (delta.X < 0)
752                         new_nodes.MaxEdge.X = new_nodes.MinEdge.X;
753                 else if (delta.Y > 0)
754                         new_nodes.MinEdge.Y = new_nodes.MaxEdge.Y;
755                 else if (delta.Y < 0)
756                         new_nodes.MaxEdge.Y = new_nodes.MinEdge.Y;
757                 else if (delta.Z > 0)
758                         new_nodes.MinEdge.Z = new_nodes.MaxEdge.Z;
759                 else if (delta.Z < 0)
760                         new_nodes.MaxEdge.Z = new_nodes.MinEdge.Z;
761
762                 // For each untested node
763                 for (s16 x = new_nodes.MinEdge.X; x <= new_nodes.MaxEdge.X; x++) {
764                         for (s16 y = new_nodes.MinEdge.Y; y <= new_nodes.MaxEdge.Y; y++) {
765                                 for (s16 z = new_nodes.MinEdge.Z; z <= new_nodes.MaxEdge.Z; z++) {
766                                         MapNode n;
767                                         v3s16 np(x, y, z);
768                                         bool is_valid_position;
769
770                                         n = m_map->getNodeNoEx(np, &is_valid_position);
771                                         if (!(is_valid_position &&
772                                                 isPointableNode(n, nodedef, liquids_pointable))) {
773                                                 continue;
774                                         }
775                                         std::vector<aabb3f> boxes;
776                                         n.getSelectionBoxes(nodedef, &boxes,
777                                                 n.getNeighbors(np, m_map));
778
779                                         v3f npf = intToFloat(np, BS);
780                                         for (std::vector<aabb3f>::const_iterator i = boxes.begin();
781                                                 i != boxes.end(); ++i) {
782                                                 aabb3f box = *i;
783                                                 box.MinEdge += npf;
784                                                 box.MaxEdge += npf;
785                                                 v3f intersection_point;
786                                                 v3s16 intersection_normal;
787                                                 if (!boxLineCollision(box, shootline.start, shootline.getVector(),
788                                                         &intersection_point, &intersection_normal)) {
789                                                         continue;
790                                                 }
791                                                 f32 distance = (intersection_point - shootline.start).getLength();
792                                                 if (distance >= min_distance) {
793                                                         continue;
794                                                 }
795                                                 result.type = POINTEDTHING_NODE;
796                                                 result.node_undersurface = np;
797                                                 result.intersection_point = intersection_point;
798                                                 result.intersection_normal = intersection_normal;
799                                                 found_boxcenter = box.getCenter();
800                                                 min_distance = distance;
801                                                 is_node_found = true;
802                                         }
803                                 }
804                         }
805                 }
806                 if (is_node_found) {
807                         node_foundcounter++;
808                         if (node_foundcounter > maximal_overcheck) {
809                                 break;
810                         }
811                 }
812                 // Next node
813                 if (iterator.hasNext()) {
814                         oldnode = iterator.m_current_node_pos;
815                         iterator.next();
816                 } else {
817                         break;
818                 }
819         }
820
821         if (is_node_found) {
822                 // Set undersurface and abovesurface nodes
823                 f32 d = 0.002 * BS;
824                 v3f fake_intersection = result.intersection_point;
825                 // Move intersection towards its source block.
826                 if (fake_intersection.X < found_boxcenter.X)
827                         fake_intersection.X += d;
828                 else
829                         fake_intersection.X -= d;
830
831                 if (fake_intersection.Y < found_boxcenter.Y)
832                         fake_intersection.Y += d;
833                 else
834                         fake_intersection.Y -= d;
835
836                 if (fake_intersection.Z < found_boxcenter.Z)
837                         fake_intersection.Z += d;
838                 else
839                         fake_intersection.Z -= d;
840
841                 result.node_real_undersurface = floatToInt(fake_intersection, BS);
842                 result.node_abovesurface = result.node_real_undersurface
843                         + result.intersection_normal;
844         }
845         return result;
846 }