]> git.lizzy.rs Git - dragonfireclient.git/blob - src/mapblockobject.cpp
sitä sun tätä tekeillä, toimii kivasti
[dragonfireclient.git] / src / mapblockobject.cpp
1 /*
2 (c) 2010 Perttu Ahola <celeron55@gmail.com>
3 */
4
5 #include "mapblockobject.h"
6 #include "mapblock.h"
7 // Only for ::getNodeBox, TODO: Get rid of this
8 #include "map.h"
9
10 /*
11         MapBlockObject
12 */
13
14 // This is here because it uses the MapBlock
15 v3f MapBlockObject::getAbsolutePos()
16 {
17         if(m_block == NULL)
18                 return m_pos;
19         
20         // getPosRelative gets nodepos relative to map origin
21         v3f blockpos = intToFloat(m_block->getPosRelative());
22         return blockpos + m_pos;
23 }
24
25 void MapBlockObject::setBlockChanged()
26 {
27         if(m_block)
28                 m_block->setChangedFlag();
29 }
30
31 /*
32         MovingObject
33 */
34 void MovingObject::move(float dtime, v3f acceleration)
35 {
36         //m_pos += dtime * 3.0;
37
38         v3s16 oldpos_i = floatToInt(m_pos);
39         
40         if(m_block->isValidPosition(oldpos_i) == false)
41         {
42                 // Should have wrapped, cancelling further movement.
43                 return;
44         }
45
46         // No collisions if there is no collision box
47         if(m_collision_box == NULL)
48         {
49                 m_speed += dtime * acceleration;
50                 m_pos += m_speed * dtime;
51                 return;
52         }
53
54         v3f position = m_pos;
55         v3f oldpos = position;
56
57         /*std::cout<<"oldpos_i=("<<oldpos_i.X<<","<<oldpos_i.Y<<","
58                         <<oldpos_i.Z<<")"<<std::endl;*/
59
60         // Maximum time increment (for collision detection etc)
61         // Allow 0.1 blocks per increment
62         // time = distance / speed
63         // NOTE: In the loop below collisions are detected at 0.15*BS radius
64         float speedlength = m_speed.getLength();
65         f32 dtime_max_increment;
66         if(fabs(speedlength) > 0.001)
67                 dtime_max_increment = 0.1*BS / speedlength;
68         else
69                 dtime_max_increment = 0.5;
70         
71         m_touching_ground = false;
72                 
73         u32 loopcount = 0;
74         do
75         {
76                 loopcount++;
77
78                 f32 dtime_part;
79                 if(dtime > dtime_max_increment)
80                         dtime_part = dtime_max_increment;
81                 else
82                         dtime_part = dtime;
83                 dtime -= dtime_part;
84
85                 // Begin of dtime limited code
86                 
87                 m_speed += acceleration * dtime_part;
88                 position += m_speed * dtime_part;
89
90                 /*
91                         Collision detection
92                 */
93                 
94                 v3s16 pos_i = floatToInt(position);
95                 
96                 // The loop length is limited to the object moving a distance
97                 f32 d = (float)BS * 0.15;
98
99                 core::aabbox3d<f32> objectbox(
100                                 m_collision_box->MinEdge + position,
101                                 m_collision_box->MaxEdge + position
102                 );
103                 
104                 core::aabbox3d<f32> objectbox_old(
105                                 m_collision_box->MinEdge + oldpos,
106                                 m_collision_box->MaxEdge + oldpos
107                 );
108                 
109                 //TODO: Get these ranges from somewhere
110                 for(s16 y = oldpos_i.Y - 1; y <= oldpos_i.Y + 2; y++)
111                 for(s16 z = oldpos_i.Z - 1; z <= oldpos_i.Z + 1; z++)
112                 for(s16 x = oldpos_i.X - 1; x <= oldpos_i.X + 1; x++)
113                 {
114                         try{
115                                 if(m_block->getNodeParent(v3s16(x,y,z)).d == MATERIAL_AIR){
116                                         continue;
117                                 }
118                         }
119                         catch(InvalidPositionException &e)
120                         {
121                                 // Doing nothing here will block the player from
122                                 // walking over map borders
123                         }
124
125                         core::aabbox3d<f32> nodebox = Map::getNodeBox(
126                                         v3s16(x,y,z));
127                         
128                         // See if the player is touching ground
129                         if(
130                                         fabs(nodebox.MaxEdge.Y-objectbox.MinEdge.Y) < d
131                                         && nodebox.MaxEdge.X-d > objectbox.MinEdge.X
132                                         && nodebox.MinEdge.X+d < objectbox.MaxEdge.X
133                                         && nodebox.MaxEdge.Z-d > objectbox.MinEdge.Z
134                                         && nodebox.MinEdge.Z+d < objectbox.MaxEdge.Z
135                         ){
136                                 m_touching_ground = true;
137                         }
138                         
139                         if(objectbox.intersectsWithBox(nodebox))
140                         {
141                                         
142                 v3f dirs[3] = {
143                         v3f(0,0,1), // back
144                         v3f(0,1,0), // top
145                         v3f(1,0,0), // right
146                 };
147                 for(u16 i=0; i<3; i++)
148                 {
149                         f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[i]);
150                         f32 nodemin = nodebox.MinEdge.dotProduct(dirs[i]);
151                         f32 playermax = objectbox.MaxEdge.dotProduct(dirs[i]);
152                         f32 playermin = objectbox.MinEdge.dotProduct(dirs[i]);
153                         f32 playermax_old = objectbox_old.MaxEdge.dotProduct(dirs[i]);
154                         f32 playermin_old = objectbox_old.MinEdge.dotProduct(dirs[i]);
155
156                         bool main_edge_collides = 
157                                 ((nodemax > playermin && nodemax <= playermin_old + d
158                                         && m_speed.dotProduct(dirs[i]) < 0)
159                                 ||
160                                 (nodemin < playermax && nodemin >= playermax_old - d
161                                         && m_speed.dotProduct(dirs[i]) > 0));
162
163                         bool other_edges_collide = true;
164                         for(u16 j=0; j<3; j++)
165                         {
166                                 if(j == i)
167                                         continue;
168                                 f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[j]);
169                                 f32 nodemin = nodebox.MinEdge.dotProduct(dirs[j]);
170                                 f32 playermax = objectbox.MaxEdge.dotProduct(dirs[j]);
171                                 f32 playermin = objectbox.MinEdge.dotProduct(dirs[j]);
172                                 if(!(nodemax - d > playermin && nodemin + d < playermax))
173                                 {
174                                         other_edges_collide = false;
175                                         break;
176                                 }
177                         }
178                         
179                         if(main_edge_collides && other_edges_collide)
180                         {
181                                 m_speed -= m_speed.dotProduct(dirs[i]) * dirs[i];
182                                 position -= position.dotProduct(dirs[i]) * dirs[i];
183                                 position += oldpos.dotProduct(dirs[i]) * dirs[i];
184                         }
185                 
186                 }
187                 
188                         } // if(objectbox.intersectsWithBox(nodebox))
189                 } // for y
190
191         } // End of dtime limited loop
192         while(dtime > 0.001);
193
194         m_pos = position;
195 }
196
197 /*
198         MapBlockObjectList
199 */
200
201 MapBlockObjectList::MapBlockObjectList(MapBlock *block):
202         m_block(block)
203 {
204         m_mutex.Init();
205 }
206
207 MapBlockObjectList::~MapBlockObjectList()
208 {
209         clear();
210 }
211
212 /*
213         The serialization format:
214         [0] u16 number of entries
215         [2] entries (id, typeId, parameters)
216 */
217
218 void MapBlockObjectList::serialize(std::ostream &os, u8 version)
219 {
220         JMutexAutoLock lock(m_mutex);
221
222         u8 buf[2];
223         writeU16(buf, m_objects.size());
224         os.write((char*)buf, 2);
225
226         for(core::map<s16, MapBlockObject*>::Iterator
227                         i = m_objects.getIterator();
228                         i.atEnd() == false; i++)
229         {
230                 i.getNode()->getValue()->serialize(os, version);
231         }
232 }
233
234 void MapBlockObjectList::update(std::istream &is, u8 version,
235                 scene::ISceneManager *smgr)
236 {
237         JMutexAutoLock lock(m_mutex);
238
239         /*
240                 Collect all existing ids to a set.
241
242                 As things are updated, they are removed from this.
243
244                 All remaining ones are deleted.
245         */
246         core::map<s16, bool> ids_to_delete;
247         for(core::map<s16, MapBlockObject*>::Iterator
248                         i = m_objects.getIterator();
249                         i.atEnd() == false; i++)
250         {
251                 ids_to_delete.insert(i.getNode()->getKey(), true);
252         }
253         
254         u8 buf[6];
255         
256         is.read((char*)buf, 2);
257         u16 count = readU16(buf);
258
259         for(u16 i=0; i<count; i++)
260         {
261                 // Read id
262                 is.read((char*)buf, 2);
263                 s16 id = readS16(buf);
264                 
265                 // Read position
266                 // stored as x1000/BS v3s16
267                 is.read((char*)buf, 6);
268                 v3s16 pos_i = readV3S16(buf);
269                 v3f pos((f32)pos_i.X/1000*BS,
270                                 (f32)pos_i.Y/1000*BS,
271                                 (f32)pos_i.Z/1000*BS);
272
273                 // Read typeId
274                 is.read((char*)buf, 2);
275                 u16 type_id = readU16(buf);
276                 
277                 bool create_new = false;
278
279                 // Find an object with the id
280                 core::map<s16, MapBlockObject*>::Node *n;
281                 n = m_objects.find(id);
282                 // If no entry is found for id
283                 if(n == NULL)
284                 {
285                         // Insert dummy pointer node
286                         m_objects.insert(id, NULL);
287                         // Get node
288                         n = m_objects.find(id);
289                         // A new object will be created at this node
290                         create_new = true;
291                 }
292                 // If type_id differs
293                 else if(n->getValue()->getTypeId() != type_id)
294                 {
295                         // Delete old object
296                         delete n->getValue();
297                         // A new object will be created at this node
298                         create_new = true;
299                 }
300
301                 MapBlockObject *obj = NULL;
302
303                 if(create_new)
304                 {
305                         /*dstream<<"MapBlockObjectList adding new object"
306                                         " id="<<id
307                                         <<std::endl;*/
308
309                         if(type_id == MAPBLOCKOBJECT_TYPE_TEST)
310                         {
311                                 // The constructors of objects shouldn't need
312                                 // any more parameters than this.
313                                 obj = new TestObject(m_block, id, pos);
314                         }
315                         else if(type_id == MAPBLOCKOBJECT_TYPE_TEST2)
316                         {
317                                 obj = new Test2Object(m_block, id, pos);
318                         }
319                         else if(type_id == MAPBLOCKOBJECT_TYPE_SIGN)
320                         {
321                                 obj = new SignObject(m_block, id, pos);
322                         }
323                         else if(type_id == MAPBLOCKOBJECT_TYPE_RAT)
324                         {
325                                 obj = new RatObject(m_block, id, pos);
326                         }
327                         else
328                         {
329                                 throw SerializationError
330                                 ("MapBlockObjectList::update(): Unknown MapBlockObject type");
331                         }
332
333                         if(smgr != NULL)
334                                 obj->addToScene(smgr);
335
336                         n->setValue(obj);
337                 }
338                 else
339                 {
340                         obj = n->getValue();
341                         obj->updatePos(pos);
342                 }
343
344                 // Now there is an object in obj.
345                 // Update it.
346                 
347                 obj->update(is, version);
348                 
349                 // Remove from deletion list
350                 if(ids_to_delete.find(id) != NULL)
351                         ids_to_delete.remove(id);
352         }
353
354         // Delete all objects whose ids_to_delete remain in ids_to_delete
355         for(core::map<s16, bool>::Iterator
356                         i = ids_to_delete.getIterator();
357                         i.atEnd() == false; i++)
358         {
359                 s16 id = i.getNode()->getKey();
360
361                 /*dstream<<"MapBlockObjectList deleting object"
362                                 " id="<<id
363                                 <<std::endl;*/
364
365                 MapBlockObject *obj = m_objects[id];
366                 obj->removeFromScene();
367                 delete obj;
368                 m_objects.remove(id);
369         }
370 }
371
372 s16 MapBlockObjectList::getFreeId() throw(ContainerFullException)
373 {
374         s16 id = 0;
375         for(;;)
376         {
377                 if(m_objects.find(id) == NULL)
378                         return id;
379                 if(id == 32767)
380                         throw ContainerFullException
381                                         ("MapBlockObjectList doesn't fit more objects");
382                 id++;
383         }
384 }
385
386 void MapBlockObjectList::add(MapBlockObject *object)
387                 throw(ContainerFullException, AlreadyExistsException)
388 {
389         if(object == NULL)
390         {
391                 dstream<<"MapBlockObjectList::add(): NULL object"<<std::endl;
392                 return;
393         }
394
395         JMutexAutoLock lock(m_mutex);
396
397         // Create unique id if id==-1
398         if(object->m_id == -1)
399         {
400                 object->m_id = getFreeId();
401         }
402
403         if(m_objects.find(object->m_id) != NULL)
404         {
405                 dstream<<"MapBlockObjectList::add(): "
406                                 "object with same id already exists"<<std::endl;
407                 throw AlreadyExistsException
408                                 ("MapBlockObjectList already has given id");
409         }
410         
411         object->m_block = m_block;
412         
413         /*v3f p = object->m_pos;
414         dstream<<"MapBlockObjectList::add(): "
415                         <<"m_block->getPos()=("
416                         <<m_block->getPos().X<<","
417                         <<m_block->getPos().Y<<","
418                         <<m_block->getPos().Z<<")"
419                         <<" inserting object with id="<<object->m_id
420                         <<" pos="
421                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
422                         <<std::endl;*/
423         
424         m_objects.insert(object->m_id, object);
425 }
426
427 void MapBlockObjectList::clear()
428 {
429         JMutexAutoLock lock(m_mutex);
430
431         for(core::map<s16, MapBlockObject*>::Iterator
432                         i = m_objects.getIterator();
433                         i.atEnd() == false; i++)
434         {
435                 MapBlockObject *obj = i.getNode()->getValue();
436                 //FIXME: This really shouldn't be NULL at any time,
437                 //       but this condition was added because it was.
438                 if(obj != NULL)
439                 {
440                         obj->removeFromScene();
441                         delete obj;
442                 }
443         }
444
445         m_objects.clear();
446 }
447
448 void MapBlockObjectList::remove(s16 id)
449 {
450         JMutexAutoLock lock(m_mutex);
451
452         core::map<s16, MapBlockObject*>::Node *n;
453         n = m_objects.find(id);
454         if(n == NULL)
455                 return;
456         
457         n->getValue()->removeFromScene();
458         delete n->getValue();
459         m_objects.remove(id);
460 }
461
462 MapBlockObject * MapBlockObjectList::get(s16 id)
463 {
464         core::map<s16, MapBlockObject*>::Node *n;
465         n = m_objects.find(id);
466         if(n == NULL)
467                 return NULL;
468         else
469                 return n->getValue();
470 }
471
472 void MapBlockObjectList::step(float dtime, bool server)
473 {
474         JMutexAutoLock lock(m_mutex);
475
476         core::map<s16, bool> ids_to_delete;
477
478         for(core::map<s16, MapBlockObject*>::Iterator
479                         i = m_objects.getIterator();
480                         i.atEnd() == false; i++)
481         {
482                 MapBlockObject *obj = i.getNode()->getValue();
483                 
484                 if(server)
485                 {
486                         bool to_delete = obj->serverStep(dtime);
487
488                         if(to_delete)
489                                 ids_to_delete.insert(obj->m_id, true);
490                 }
491                 else
492                 {
493                         obj->clientStep(dtime);
494                 }
495         }
496
497         // Delete objects in delete queue
498         for(core::map<s16, bool>::Iterator
499                         i = ids_to_delete.getIterator();
500                         i.atEnd() == false; i++)
501         {
502                 s16 id = i.getNode()->getKey();
503
504                 MapBlockObject *obj = m_objects[id];
505                 obj->removeFromScene();
506                 delete obj;
507                 m_objects.remove(id);
508         }
509         
510         /*
511                 Wrap objects on server
512         */
513
514         if(server == false)
515                 return;
516
517         for(core::map<s16, MapBlockObject*>::Iterator
518                         i = m_objects.getIterator();
519                         i.atEnd() == false; i++)
520         {
521                 MapBlockObject *obj = i.getNode()->getValue();
522
523                 v3s16 pos_i = floatToInt(obj->m_pos);
524
525                 if(m_block->isValidPosition(pos_i))
526                 {
527                         // No wrap
528                         continue;
529                 }
530
531                 bool impossible = wrapObject(obj);
532
533                 if(impossible)
534                 {
535                         // No wrap
536                         continue;
537                 }
538
539                 // Restart find
540                 i = m_objects.getIterator();
541         }
542 }
543
544 bool MapBlockObjectList::wrapObject(MapBlockObject *object)
545 {
546         // No lock here; this is called so that the lock is already locked.
547         //JMutexAutoLock lock(m_mutex);
548
549         assert(object->m_block == m_block);
550         assert(m_objects.find(object->m_id) != NULL);
551         assert(m_objects[object->m_id] == object);
552
553         NodeContainer *parentcontainer = m_block->getParent();
554         // This will only work if the parent is the map
555         if(parentcontainer->nodeContainerId() != NODECONTAINER_ID_MAP)
556         {
557                 dstream<<"WARNING: Wrapping object not possible: "
558                                 "MapBlock's parent is not map"<<std::endl;
559                 return true;
560         }
561         // OK, we have the map!
562         Map *map = (Map*)parentcontainer;
563         
564         // Calculate blockpos on map
565         v3s16 oldblock_pos_i_on_map = m_block->getPosRelative();
566         v3f pos_f_on_oldblock = object->m_pos;
567         v3s16 pos_i_on_oldblock = floatToInt(pos_f_on_oldblock);
568         v3s16 pos_i_on_map = pos_i_on_oldblock + oldblock_pos_i_on_map;
569         v3s16 pos_blocks_on_map = getNodeBlockPos(pos_i_on_map);
570
571         // Get new block
572         MapBlock *newblock;
573         try{
574                 newblock = map->getBlockNoCreate(pos_blocks_on_map);
575         }
576         catch(InvalidPositionException &e)
577         {
578                 // Couldn't find block -> not wrapping
579                 /*dstream<<"WARNING: Wrapping object not possible: "
580                                 <<"could not find new block"
581                                 <<"("<<pos_blocks_on_map.X
582                                 <<","<<pos_blocks_on_map.Y
583                                 <<","<<pos_blocks_on_map.Z
584                                 <<")"<<std::endl;*/
585                 /*dstream<<"pos_f_on_oldblock=("
586                                 <<pos_f_on_oldblock.X<<","
587                                 <<pos_f_on_oldblock.Y<<","
588                                 <<pos_f_on_oldblock.Z<<")"
589                                 <<std::endl;*/
590                 return true;
591         }
592
593         if(newblock == m_block)
594         {
595                 dstream<<"WARNING: Wrapping object not possible: "
596                                 "newblock == oldblock"<<std::endl;
597                 return true;
598         }
599         
600         // Calculate position on new block
601         v3f oldblock_pos_f_on_map = intToFloat(oldblock_pos_i_on_map);
602         v3s16 newblock_pos_i_on_map = newblock->getPosRelative();
603         v3f newblock_pos_f_on_map = intToFloat(newblock_pos_i_on_map);
604         v3f pos_f_on_newblock = pos_f_on_oldblock
605                         - newblock_pos_f_on_map + oldblock_pos_f_on_map;
606
607         // Remove object from this block
608         m_objects.remove(object->m_id);
609         
610         // Add object to new block
611         object->m_pos = pos_f_on_newblock;
612         object->m_id = -1;
613         object->m_block = NULL;
614         newblock->addObject(object);
615
616         //dstream<<"NOTE: Wrapped object"<<std::endl;
617
618         return false;
619 }
620
621 void MapBlockObjectList::getObjects(v3f origin, f32 max_d,
622                 core::array<DistanceSortedObject> &dest)
623 {
624         for(core::map<s16, MapBlockObject*>::Iterator
625                         i = m_objects.getIterator();
626                         i.atEnd() == false; i++)
627         {
628                 MapBlockObject *obj = i.getNode()->getValue();
629
630                 f32 d = (obj->m_pos - origin).getLength();
631
632                 if(d > max_d)
633                         continue;
634
635                 DistanceSortedObject dso(obj, d);
636
637                 dest.push_back(dso);
638         }
639 }
640
641 //END