Enable client-side attachments, add detachment code
[oweals/minetest.git] / src / content_cao.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 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 "content_cao.h"
21 #include "tile.h"
22 #include "environment.h"
23 #include "collision.h"
24 #include "settings.h"
25 #include <ICameraSceneNode.h>
26 #include <ITextSceneNode.h>
27 #include <IBillboardSceneNode.h>
28 #include "serialization.h" // For decompressZlib
29 #include "gamedef.h"
30 #include "clientobject.h"
31 #include "content_object.h"
32 #include "mesh.h"
33 #include "itemdef.h"
34 #include "tool.h"
35 #include "content_cso.h"
36 #include "sound.h"
37 #include "nodedef.h"
38 #include "localplayer.h"
39 #include "util/numeric.h" // For IntervalLimiter
40 #include "util/serialize.h"
41 #include "util/mathconstants.h"
42 #include "map.h"
43 #include <IMeshManipulator.h>
44 #include <IAnimatedMeshSceneNode.h>
45 #include <IBoneSceneNode.h>
46
47 class Settings;
48 struct ToolCapabilities;
49
50 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
51
52 core::map<u16, ClientActiveObject::Factory> ClientActiveObject::m_types;
53
54 /*
55         SmoothTranslator
56 */
57
58 struct SmoothTranslator
59 {
60         v3f vect_old;
61         v3f vect_show;
62         v3f vect_aim;
63         f32 anim_counter;
64         f32 anim_time;
65         f32 anim_time_counter;
66         bool aim_is_end;
67
68         SmoothTranslator():
69                 vect_old(0,0,0),
70                 vect_show(0,0,0),
71                 vect_aim(0,0,0),
72                 anim_counter(0),
73                 anim_time(0),
74                 anim_time_counter(0),
75                 aim_is_end(true)
76         {}
77
78         void init(v3f vect)
79         {
80                 vect_old = vect;
81                 vect_show = vect;
82                 vect_aim = vect;
83                 anim_counter = 0;
84                 anim_time = 0;
85                 anim_time_counter = 0;
86                 aim_is_end = true;
87         }
88
89         void sharpen()
90         {
91                 init(vect_show);
92         }
93
94         void update(v3f vect_new, bool is_end_position=false, float update_interval=-1)
95         {
96                 aim_is_end = is_end_position;
97                 vect_old = vect_show;
98                 vect_aim = vect_new;
99                 if(update_interval > 0){
100                         anim_time = update_interval;
101                 } else {
102                         if(anim_time < 0.001 || anim_time > 1.0)
103                                 anim_time = anim_time_counter;
104                         else
105                                 anim_time = anim_time * 0.9 + anim_time_counter * 0.1;
106                 }
107                 anim_time_counter = 0;
108                 anim_counter = 0;
109         }
110
111         void translate(f32 dtime)
112         {
113                 anim_time_counter = anim_time_counter + dtime;
114                 anim_counter = anim_counter + dtime;
115                 v3f vect_move = vect_aim - vect_old;
116                 f32 moveratio = 1.0;
117                 if(anim_time > 0.001)
118                         moveratio = anim_time_counter / anim_time;
119                 // Move a bit less than should, to avoid oscillation
120                 moveratio = moveratio * 0.8;
121                 float move_end = 1.5;
122                 if(aim_is_end)
123                         move_end = 1.0;
124                 if(moveratio > move_end)
125                         moveratio = move_end;
126                 vect_show = vect_old + vect_move * moveratio;
127         }
128
129         bool is_moving()
130         {
131                 return ((anim_time_counter / anim_time) < 1.4);
132         }
133 };
134
135 /*
136         Other stuff
137 */
138
139 static void setBillboardTextureMatrix(scene::IBillboardSceneNode *bill,
140                 float txs, float tys, int col, int row)
141 {
142         video::SMaterial& material = bill->getMaterial(0);
143         core::matrix4& matrix = material.getTextureMatrix(0);
144         matrix.setTextureTranslate(txs*col, tys*row);
145         matrix.setTextureScale(txs, tys);
146 }
147
148 /*
149         TestCAO
150 */
151
152 class TestCAO : public ClientActiveObject
153 {
154 public:
155         TestCAO(IGameDef *gamedef, ClientEnvironment *env);
156         virtual ~TestCAO();
157         
158         u8 getType() const
159         {
160                 return ACTIVEOBJECT_TYPE_TEST;
161         }
162         
163         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env);
164
165         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
166                         IrrlichtDevice *irr);
167         void removeFromScene();
168         void updateLight(u8 light_at_pos);
169         v3s16 getLightPosition();
170         void updateNodePos();
171
172         void step(float dtime, ClientEnvironment *env);
173
174         void processMessage(const std::string &data);
175
176 private:
177         scene::IMeshSceneNode *m_node;
178         v3f m_position;
179 };
180
181 // Prototype
182 TestCAO proto_TestCAO(NULL, NULL);
183
184 TestCAO::TestCAO(IGameDef *gamedef, ClientEnvironment *env):
185         ClientActiveObject(0, gamedef, env),
186         m_node(NULL),
187         m_position(v3f(0,10*BS,0))
188 {
189         ClientActiveObject::registerType(getType(), create);
190 }
191
192 TestCAO::~TestCAO()
193 {
194 }
195
196 ClientActiveObject* TestCAO::create(IGameDef *gamedef, ClientEnvironment *env)
197 {
198         return new TestCAO(gamedef, env);
199 }
200
201 void TestCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
202                         IrrlichtDevice *irr)
203 {
204         if(m_node != NULL)
205                 return;
206         
207         //video::IVideoDriver* driver = smgr->getVideoDriver();
208         
209         scene::SMesh *mesh = new scene::SMesh();
210         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
211         video::SColor c(255,255,255,255);
212         video::S3DVertex vertices[4] =
213         {
214                 video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1),
215                 video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1),
216                 video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0),
217                 video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),
218         };
219         u16 indices[] = {0,1,2,2,3,0};
220         buf->append(vertices, 4, indices, 6);
221         // Set material
222         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
223         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
224         buf->getMaterial().setTexture(0, tsrc->getTextureRaw("rat.png"));
225         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
226         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
227         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
228         // Add to mesh
229         mesh->addMeshBuffer(buf);
230         buf->drop();
231         m_node = smgr->addMeshSceneNode(mesh, NULL);
232         mesh->drop();
233         updateNodePos();
234 }
235
236 void TestCAO::removeFromScene()
237 {
238         if(m_node == NULL)
239                 return;
240
241         m_node->remove();
242         m_node = NULL;
243 }
244
245 void TestCAO::updateLight(u8 light_at_pos)
246 {
247 }
248
249 v3s16 TestCAO::getLightPosition()
250 {
251         return floatToInt(m_position, BS);
252 }
253
254 void TestCAO::updateNodePos()
255 {
256         if(m_node == NULL)
257                 return;
258
259         m_node->setPosition(m_position);
260         //m_node->setRotation(v3f(0, 45, 0));
261 }
262
263 void TestCAO::step(float dtime, ClientEnvironment *env)
264 {
265         if(m_node)
266         {
267                 v3f rot = m_node->getRotation();
268                 //infostream<<"dtime="<<dtime<<", rot.Y="<<rot.Y<<std::endl;
269                 rot.Y += dtime * 180;
270                 m_node->setRotation(rot);
271         }
272 }
273
274 void TestCAO::processMessage(const std::string &data)
275 {
276         infostream<<"TestCAO: Got data: "<<data<<std::endl;
277         std::istringstream is(data, std::ios::binary);
278         u16 cmd;
279         is>>cmd;
280         if(cmd == 0)
281         {
282                 v3f newpos;
283                 is>>newpos.X;
284                 is>>newpos.Y;
285                 is>>newpos.Z;
286                 m_position = newpos;
287                 updateNodePos();
288         }
289 }
290
291 /*
292         ItemCAO
293 */
294
295 class ItemCAO : public ClientActiveObject
296 {
297 public:
298         ItemCAO(IGameDef *gamedef, ClientEnvironment *env);
299         virtual ~ItemCAO();
300         
301         u8 getType() const
302         {
303                 return ACTIVEOBJECT_TYPE_ITEM;
304         }
305         
306         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env);
307
308         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
309                         IrrlichtDevice *irr);
310         void removeFromScene();
311         void updateLight(u8 light_at_pos);
312         v3s16 getLightPosition();
313         void updateNodePos();
314         void updateInfoText();
315         void updateTexture();
316
317         void step(float dtime, ClientEnvironment *env);
318
319         void processMessage(const std::string &data);
320
321         void initialize(const std::string &data);
322         
323         core::aabbox3d<f32>* getSelectionBox()
324                 {return &m_selection_box;}
325         v3f getPosition()
326                 {return m_position;}
327         
328         std::string infoText()
329                 {return m_infotext;}
330
331 private:
332         core::aabbox3d<f32> m_selection_box;
333         scene::IMeshSceneNode *m_node;
334         v3f m_position;
335         std::string m_itemstring;
336         std::string m_infotext;
337 };
338
339 #include "inventory.h"
340
341 // Prototype
342 ItemCAO proto_ItemCAO(NULL, NULL);
343
344 ItemCAO::ItemCAO(IGameDef *gamedef, ClientEnvironment *env):
345         ClientActiveObject(0, gamedef, env),
346         m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.),
347         m_node(NULL),
348         m_position(v3f(0,10*BS,0))
349 {
350         if(!gamedef && !env)
351         {
352                 ClientActiveObject::registerType(getType(), create);
353         }
354 }
355
356 ItemCAO::~ItemCAO()
357 {
358 }
359
360 ClientActiveObject* ItemCAO::create(IGameDef *gamedef, ClientEnvironment *env)
361 {
362         return new ItemCAO(gamedef, env);
363 }
364
365 void ItemCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
366                         IrrlichtDevice *irr)
367 {
368         if(m_node != NULL)
369                 return;
370         
371         //video::IVideoDriver* driver = smgr->getVideoDriver();
372         
373         scene::SMesh *mesh = new scene::SMesh();
374         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
375         video::SColor c(255,255,255,255);
376         video::S3DVertex vertices[4] =
377         {
378                 /*video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1),
379                 video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1),
380                 video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0),
381                 video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),*/
382                 video::S3DVertex(BS/3.,0,0, 0,0,0, c, 0,1),
383                 video::S3DVertex(-BS/3.,0,0, 0,0,0, c, 1,1),
384                 video::S3DVertex(-BS/3.,0+BS*2./3.,0, 0,0,0, c, 1,0),
385                 video::S3DVertex(BS/3.,0+BS*2./3.,0, 0,0,0, c, 0,0),
386         };
387         u16 indices[] = {0,1,2,2,3,0};
388         buf->append(vertices, 4, indices, 6);
389         // Set material
390         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
391         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
392         // Initialize with a generated placeholder texture
393         buf->getMaterial().setTexture(0, tsrc->getTextureRaw(""));
394         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
395         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
396         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
397         // Add to mesh
398         mesh->addMeshBuffer(buf);
399         buf->drop();
400         m_node = smgr->addMeshSceneNode(mesh, NULL);
401         mesh->drop();
402         updateNodePos();
403
404         /*
405                 Update image of node
406         */
407
408         updateTexture();
409 }
410
411 void ItemCAO::removeFromScene()
412 {
413         if(m_node == NULL)
414                 return;
415
416         m_node->remove();
417         m_node = NULL;
418 }
419
420 void ItemCAO::updateLight(u8 light_at_pos)
421 {
422         if(m_node == NULL)
423                 return;
424
425         u8 li = decode_light(light_at_pos);
426         video::SColor color(255,li,li,li);
427         setMeshColor(m_node->getMesh(), color);
428 }
429
430 v3s16 ItemCAO::getLightPosition()
431 {
432         return floatToInt(m_position + v3f(0,0.5*BS,0), BS);
433 }
434
435 void ItemCAO::updateNodePos()
436 {
437         if(m_node == NULL)
438                 return;
439
440         m_node->setPosition(m_position);
441 }
442
443 void ItemCAO::updateInfoText()
444 {
445         try{
446                 IItemDefManager *idef = m_gamedef->idef();
447                 ItemStack item;
448                 item.deSerialize(m_itemstring, idef);
449                 if(item.isKnown(idef))
450                         m_infotext = item.getDefinition(idef).description;
451                 else
452                         m_infotext = "Unknown item: '" + m_itemstring + "'";
453                 if(item.count >= 2)
454                         m_infotext += " (" + itos(item.count) + ")";
455         }
456         catch(SerializationError &e)
457         {
458                 m_infotext = "Unknown item: '" + m_itemstring + "'";
459         }
460 }
461
462 void ItemCAO::updateTexture()
463 {
464         if(m_node == NULL)
465                 return;
466
467         // Create an inventory item to see what is its image
468         std::istringstream is(m_itemstring, std::ios_base::binary);
469         video::ITexture *texture = NULL;
470         try{
471                 IItemDefManager *idef = m_gamedef->idef();
472                 ItemStack item;
473                 item.deSerialize(is, idef);
474                 texture = item.getDefinition(idef).inventory_texture;
475         }
476         catch(SerializationError &e)
477         {
478                 infostream<<"WARNING: "<<__FUNCTION_NAME
479                                 <<": error deSerializing itemstring \""
480                                 <<m_itemstring<<std::endl;
481         }
482         
483         // Set meshbuffer texture
484         m_node->getMaterial(0).setTexture(0, texture);
485 }
486
487
488 void ItemCAO::step(float dtime, ClientEnvironment *env)
489 {
490         if(m_node)
491         {
492                 /*v3f rot = m_node->getRotation();
493                 rot.Y += dtime * 120;
494                 m_node->setRotation(rot);*/
495                 LocalPlayer *player = env->getLocalPlayer();
496                 assert(player);
497                 v3f rot = m_node->getRotation();
498                 rot.Y = 180.0 - (player->getYaw());
499                 m_node->setRotation(rot);
500         }
501 }
502
503 void ItemCAO::processMessage(const std::string &data)
504 {
505         //infostream<<"ItemCAO: Got message"<<std::endl;
506         std::istringstream is(data, std::ios::binary);
507         // command
508         u8 cmd = readU8(is);
509         if(cmd == 0)
510         {
511                 // pos
512                 m_position = readV3F1000(is);
513                 updateNodePos();
514         }
515         if(cmd == 1)
516         {
517                 // itemstring
518                 m_itemstring = deSerializeString(is);
519                 updateInfoText();
520                 updateTexture();
521         }
522 }
523
524 void ItemCAO::initialize(const std::string &data)
525 {
526         infostream<<"ItemCAO: Got init data"<<std::endl;
527         
528         {
529                 std::istringstream is(data, std::ios::binary);
530                 // version
531                 u8 version = readU8(is);
532                 // check version
533                 if(version != 0)
534                         return;
535                 // pos
536                 m_position = readV3F1000(is);
537                 // itemstring
538                 m_itemstring = deSerializeString(is);
539         }
540         
541         updateNodePos();
542         updateInfoText();
543 }
544
545 /*
546         GenericCAO
547 */
548
549 #include "genericobject.h"
550
551 class GenericCAO : public ClientActiveObject
552 {
553 private:
554         // Only set at initialization
555         std::string m_name;
556         bool m_is_player;
557         bool m_is_local_player;
558         int m_id;
559         // Property-ish things
560         ObjectProperties m_prop;
561         //
562         scene::ISceneManager *m_smgr;
563         IrrlichtDevice *m_irr;
564         core::aabbox3d<f32> m_selection_box;
565         scene::IMeshSceneNode *m_meshnode;
566         scene::IAnimatedMeshSceneNode *m_animated_meshnode;
567         scene::IBillboardSceneNode *m_spritenode;
568         scene::ITextSceneNode* m_textnode;
569         v3f m_position;
570         v3f m_velocity;
571         v3f m_acceleration;
572         float m_yaw;
573         s16 m_hp;
574         SmoothTranslator pos_translator;
575         // Spritesheet/animation stuff
576         v2f m_tx_size;
577         v2s16 m_tx_basepos;
578         bool m_initial_tx_basepos_set;
579         bool m_tx_select_horiz_by_yawpitch;
580         v2f m_frames;
581         int m_frame_speed;
582         int m_frame_blend;
583         std::map<std::string, core::vector2d<v3f> > m_bone_posrot;
584         ClientActiveObject* m_attachment_parent;
585         std::string m_attachment_bone;
586         v3f m_attachment_position;
587         v3f m_attachment_rotation;
588         int m_anim_frame;
589         int m_anim_num_frames;
590         float m_anim_framelength;
591         float m_anim_timer;
592         ItemGroupList m_armor_groups;
593         float m_reset_textures_timer;
594         bool m_visuals_expired;
595         float m_step_distance_counter;
596         u8 m_last_light;
597         bool m_is_visible;
598
599 public:
600         GenericCAO(IGameDef *gamedef, ClientEnvironment *env):
601                 ClientActiveObject(0, gamedef, env),
602                 //
603                 m_is_player(false),
604                 m_is_local_player(false),
605                 m_id(0),
606                 //
607                 m_smgr(NULL),
608                 m_irr(NULL),
609                 m_selection_box(-BS/3.,-BS/3.,-BS/3., BS/3.,BS/3.,BS/3.),
610                 m_meshnode(NULL),
611                 m_animated_meshnode(NULL),
612                 m_spritenode(NULL),
613                 m_textnode(NULL),
614                 m_position(v3f(0,10*BS,0)),
615                 m_velocity(v3f(0,0,0)),
616                 m_acceleration(v3f(0,0,0)),
617                 m_yaw(0),
618                 m_hp(1),
619                 m_tx_size(1,1),
620                 m_tx_basepos(0,0),
621                 m_initial_tx_basepos_set(false),
622                 m_tx_select_horiz_by_yawpitch(false),
623                 m_frames(v2f(0,0)),
624                 m_frame_speed(15),
625                 m_frame_blend(0),
626                 // Nothing to do for m_bone_posrot
627                 m_attachment_parent(NULL),
628                 m_attachment_bone(""),
629                 m_attachment_position(v3f(0,0,0)),
630                 m_attachment_rotation(v3f(0,0,0)),
631                 m_anim_frame(0),
632                 m_anim_num_frames(1),
633                 m_anim_framelength(0.2),
634                 m_anim_timer(0),
635                 m_reset_textures_timer(-1),
636                 m_visuals_expired(false),
637                 m_step_distance_counter(0),
638                 m_last_light(255),
639                 m_is_visible(false)
640         {
641                 if(gamedef == NULL)
642                         ClientActiveObject::registerType(getType(), create);
643         }
644
645         void initialize(const std::string &data)
646         {
647                 infostream<<"GenericCAO: Got init data"<<std::endl;
648                 std::istringstream is(data, std::ios::binary);
649                 // version
650                 u8 version = readU8(is);
651                 // check version
652                 if(version != 0){
653                         errorstream<<"GenericCAO: Unsupported init data version"
654                                         <<std::endl;
655                         return;
656                 }
657                 m_name = deSerializeString(is);
658                 m_is_player = readU8(is);
659                 m_id = readS16(is);
660                 m_position = readV3F1000(is);
661                 m_yaw = readF1000(is);
662                 m_hp = readS16(is);
663                 
664                 int num_messages = readU8(is);
665                 for(int i=0; i<num_messages; i++){
666                         std::string message = deSerializeLongString(is);
667                         processMessage(message);
668                 }
669
670                 pos_translator.init(m_position);
671                 updateNodePos();
672                 
673                 if(m_is_player){
674                         Player *player = m_env->getPlayer(m_name.c_str());
675                         if(player && player->isLocal()){
676                                 m_is_local_player = true;
677                         }
678                 }
679         }
680
681         ~GenericCAO()
682         {
683         }
684
685         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env)
686         {
687                 return new GenericCAO(gamedef, env);
688         }
689
690         u8 getType() const
691         {
692                 return ACTIVEOBJECT_TYPE_GENERIC;
693         }
694         core::aabbox3d<f32>* getSelectionBox()
695         {
696                 if(!m_prop.is_visible || !m_is_visible || m_is_local_player)
697                         return NULL;
698                 return &m_selection_box;
699         }
700         v3f getPosition()
701         {
702                 return pos_translator.vect_show;
703         }
704
705         scene::IMeshSceneNode *getMeshSceneNode()
706         {
707                 return m_meshnode;
708         }
709
710         scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode()
711         {
712                 return m_animated_meshnode;
713         }
714
715         scene::IBillboardSceneNode *getSpriteSceneNode()
716         {
717                 return m_spritenode;
718         }
719
720         bool isPlayer()
721         {
722                 return m_is_player;
723         }
724
725         bool isLocalPlayer()
726         {
727                 return m_is_local_player;
728         }
729
730         void removeFromScene()
731         {
732                 if(m_meshnode){
733                         m_meshnode->remove();
734                         m_meshnode = NULL;
735                 }
736                 if(m_animated_meshnode){
737                         m_animated_meshnode->remove();
738                         m_animated_meshnode = NULL;
739                 }
740                 if(m_spritenode){
741                         m_spritenode->remove();
742                         m_spritenode = NULL;
743                 }
744         }
745
746         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
747                         IrrlichtDevice *irr)
748         {
749                 m_smgr = smgr;
750                 m_irr = irr;
751
752                 if(m_meshnode != NULL || m_animated_meshnode != NULL || m_spritenode != NULL)
753                         return;
754                 
755                 m_visuals_expired = false;
756
757                 if(!m_prop.is_visible || m_is_local_player)
758                         return;
759         
760                 //video::IVideoDriver* driver = smgr->getVideoDriver();
761
762                 if(m_prop.visual == "sprite"){
763                         infostream<<"GenericCAO::addToScene(): single_sprite"<<std::endl;
764                         m_spritenode = smgr->addBillboardSceneNode(
765                                         NULL, v2f(1, 1), v3f(0,0,0), -1);
766                         m_spritenode->setMaterialTexture(0,
767                                         tsrc->getTextureRaw("unknown_block.png"));
768                         m_spritenode->setMaterialFlag(video::EMF_LIGHTING, false);
769                         m_spritenode->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
770                         m_spritenode->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF);
771                         m_spritenode->setMaterialFlag(video::EMF_FOG_ENABLE, true);
772                         u8 li = m_last_light;
773                         m_spritenode->setColor(video::SColor(255,li,li,li));
774                         m_spritenode->setSize(m_prop.visual_size*BS);
775                         {
776                                 const float txs = 1.0 / 1;
777                                 const float tys = 1.0 / 1;
778                                 setBillboardTextureMatrix(m_spritenode,
779                                                 txs, tys, 0, 0);
780                         }
781                 }
782                 else if(m_prop.visual == "upright_sprite")
783                 {
784                         scene::SMesh *mesh = new scene::SMesh();
785                         double dx = BS*m_prop.visual_size.X/2;
786                         double dy = BS*m_prop.visual_size.Y/2;
787                         { // Front
788                         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
789                         u8 li = m_last_light;
790                         video::SColor c(255,li,li,li);
791                         video::S3DVertex vertices[4] =
792                         {
793                                 video::S3DVertex(-dx,-dy,0, 0,0,0, c, 0,1),
794                                 video::S3DVertex(dx,-dy,0, 0,0,0, c, 1,1),
795                                 video::S3DVertex(dx,dy,0, 0,0,0, c, 1,0),
796                                 video::S3DVertex(-dx,dy,0, 0,0,0, c, 0,0),
797                         };
798                         u16 indices[] = {0,1,2,2,3,0};
799                         buf->append(vertices, 4, indices, 6);
800                         // Set material
801                         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
802                         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
803                         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
804                         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
805                         // Add to mesh
806                         mesh->addMeshBuffer(buf);
807                         buf->drop();
808                         }
809                         { // Back
810                         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
811                         u8 li = m_last_light;
812                         video::SColor c(255,li,li,li);
813                         video::S3DVertex vertices[4] =
814                         {
815                                 video::S3DVertex(dx,-dy,0, 0,0,0, c, 1,1),
816                                 video::S3DVertex(-dx,-dy,0, 0,0,0, c, 0,1),
817                                 video::S3DVertex(-dx,dy,0, 0,0,0, c, 0,0),
818                                 video::S3DVertex(dx,dy,0, 0,0,0, c, 1,0),
819                         };
820                         u16 indices[] = {0,1,2,2,3,0};
821                         buf->append(vertices, 4, indices, 6);
822                         // Set material
823                         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
824                         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
825                         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
826                         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
827                         // Add to mesh
828                         mesh->addMeshBuffer(buf);
829                         buf->drop();
830                         }
831                         m_meshnode = smgr->addMeshSceneNode(mesh, NULL);
832                         mesh->drop();
833                         // Set it to use the materials of the meshbuffers directly.
834                         // This is needed for changing the texture in the future
835                         m_meshnode->setReadOnlyMaterials(true);
836                 }
837                 else if(m_prop.visual == "cube"){
838                         infostream<<"GenericCAO::addToScene(): cube"<<std::endl;
839                         scene::IMesh *mesh = createCubeMesh(v3f(BS,BS,BS));
840                         m_meshnode = smgr->addMeshSceneNode(mesh, NULL);
841                         mesh->drop();
842                         
843                         m_meshnode->setScale(v3f(m_prop.visual_size.X,
844                                         m_prop.visual_size.Y,
845                                         m_prop.visual_size.X));
846                         u8 li = m_last_light;
847                         setMeshColor(m_meshnode->getMesh(), video::SColor(255,li,li,li));
848                 }
849                 else if(m_prop.visual == "mesh"){
850                         infostream<<"GenericCAO::addToScene(): mesh"<<std::endl;
851                         scene::IAnimatedMesh *mesh = smgr->getMesh(m_prop.mesh.c_str());
852                         if(mesh)
853                         {
854                                 m_animated_meshnode = smgr->addAnimatedMeshSceneNode(mesh, NULL);
855                                 m_animated_meshnode->animateJoints(); // Needed for some animations
856                                 m_animated_meshnode->setScale(v3f(m_prop.visual_size.X,
857                                                 m_prop.visual_size.Y,
858                                                 m_prop.visual_size.X));
859                                 u8 li = m_last_light;
860                                 setMeshColor(m_animated_meshnode->getMesh(), video::SColor(255,li,li,li));
861                         }
862                         else
863                                 errorstream<<"GenericCAO::addToScene(): Could not load mesh "<<m_prop.mesh<<std::endl;
864                 }
865                 else if(m_prop.visual == "wielditem"){
866                         infostream<<"GenericCAO::addToScene(): node"<<std::endl;
867                         infostream<<"textures: "<<m_prop.textures.size()<<std::endl;
868                         if(m_prop.textures.size() >= 1){
869                                 infostream<<"textures[0]: "<<m_prop.textures[0]<<std::endl;
870                                 IItemDefManager *idef = m_gamedef->idef();
871                                 ItemStack item(m_prop.textures[0], 1, 0, "", idef);
872                                 scene::IMesh *item_mesh = item.getDefinition(idef).wield_mesh;
873                                 
874                                 // Copy mesh to be able to set unique vertex colors
875                                 scene::IMeshManipulator *manip =
876                                                 irr->getVideoDriver()->getMeshManipulator();
877                                 scene::IMesh *mesh = manip->createMeshUniquePrimitives(item_mesh);
878
879                                 m_meshnode = smgr->addMeshSceneNode(mesh, NULL);
880                                 mesh->drop();
881                                 
882                                 m_meshnode->setScale(v3f(m_prop.visual_size.X/2,
883                                                 m_prop.visual_size.Y/2,
884                                                 m_prop.visual_size.X/2));
885                                 u8 li = m_last_light;
886                                 setMeshColor(m_meshnode->getMesh(), video::SColor(255,li,li,li));
887                         }
888                 } else {
889                         infostream<<"GenericCAO::addToScene(): \""<<m_prop.visual
890                                         <<"\" not supported"<<std::endl;
891                 }
892                 updateTextures("");
893                 
894                 scene::ISceneNode *node = NULL;
895                 if(m_spritenode)
896                         node = m_spritenode;
897                 else if(m_animated_meshnode)
898                         node = m_animated_meshnode;
899                 else if(m_meshnode)
900                         node = m_meshnode;
901                 if(node && m_is_player && !m_is_local_player){
902                         // Add a text node for showing the name
903                         gui::IGUIEnvironment* gui = irr->getGUIEnvironment();
904                         std::wstring wname = narrow_to_wide(m_name);
905                         m_textnode = smgr->addTextSceneNode(gui->getBuiltInFont(),
906                                         wname.c_str(), video::SColor(255,255,255,255), node);
907                         m_textnode->setPosition(v3f(0, BS*1.1, 0));
908                 }
909                 
910                 updateNodePos();
911         }
912
913         void expireVisuals()
914         {
915                 m_visuals_expired = true;
916         }
917                 
918         void updateLight(u8 light_at_pos)
919         {
920                 // Objects attached to the local player should always be hidden
921                 if(m_attachment_parent != NULL && m_attachment_parent->isLocalPlayer())
922                         m_is_visible = false;
923                 else
924                         m_is_visible = (m_hp != 0);
925                 u8 li = decode_light(light_at_pos);
926
927                 if(li != m_last_light){
928                         m_last_light = li;
929                         video::SColor color(255,li,li,li);
930                         if(m_meshnode){
931                                 setMeshColor(m_meshnode->getMesh(), color);
932                                 m_meshnode->setVisible(m_is_visible);
933                         }
934                         if(m_animated_meshnode){
935                                 setMeshColor(m_animated_meshnode->getMesh(), color);
936                                 m_animated_meshnode->setVisible(m_is_visible);
937                         }
938                         if(m_spritenode){
939                                 m_spritenode->setColor(color);
940                                 m_spritenode->setVisible(m_is_visible);
941                         }
942                 }
943         }
944
945         v3s16 getLightPosition()
946         {
947                 return floatToInt(m_position, BS);
948         }
949
950         void updateNodePos()
951         {
952                 if(m_attachment_parent != NULL)
953                         return;
954
955                 if(m_meshnode){
956                         m_meshnode->setPosition(pos_translator.vect_show);
957                         v3f rot = m_meshnode->getRotation();
958                         rot.Y = -m_yaw;
959                         m_meshnode->setRotation(rot);
960                 }
961                 if(m_animated_meshnode){
962                         m_animated_meshnode->setPosition(pos_translator.vect_show);
963                         v3f rot = m_animated_meshnode->getRotation();
964                         rot.Y = -m_yaw;
965                         m_animated_meshnode->setRotation(rot);
966                 }
967                 if(m_spritenode){
968                         m_spritenode->setPosition(pos_translator.vect_show);
969                 }
970         }
971
972         void step(float dtime, ClientEnvironment *env)
973         {
974                 v3f lastpos = pos_translator.vect_show;
975
976                 if(m_visuals_expired && m_smgr && m_irr){
977                         m_visuals_expired = false;
978                         removeFromScene();
979                         addToScene(m_smgr, m_gamedef->tsrc(), m_irr);
980                         updateAnimations();
981                         updateBonePosRot();
982                         updateAttachments();
983                 }
984
985                 if(m_attachment_parent == NULL) // Attachments should be glued to their parent by Irrlicht
986                 {
987                         if(m_prop.physical){
988                                 core::aabbox3d<f32> box = m_prop.collisionbox;
989                                 box.MinEdge *= BS;
990                                 box.MaxEdge *= BS;
991                                 collisionMoveResult moveresult;
992                                 f32 pos_max_d = BS*0.125; // Distance per iteration
993                                 f32 stepheight = 0;
994                                 v3f p_pos = m_position;
995                                 v3f p_velocity = m_velocity;
996                                 v3f p_acceleration = m_acceleration;
997                                 IGameDef *gamedef = env->getGameDef();
998                                 moveresult = collisionMoveSimple(&env->getMap(), gamedef,
999                                                 pos_max_d, box, stepheight, dtime,
1000                                                 p_pos, p_velocity, p_acceleration);
1001                                 // Apply results
1002                                 m_position = p_pos;
1003                                 m_velocity = p_velocity;
1004                                 m_acceleration = p_acceleration;
1005                                 
1006                                 bool is_end_position = moveresult.collides;
1007                                 pos_translator.update(m_position, is_end_position, dtime);
1008                                 pos_translator.translate(dtime);
1009                                 updateNodePos();
1010                         } else {
1011                                 m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration;
1012                                 m_velocity += dtime * m_acceleration;
1013                                 pos_translator.update(m_position, pos_translator.aim_is_end, pos_translator.anim_time);
1014                                 pos_translator.translate(dtime);
1015                                 updateNodePos();
1016                         }
1017
1018                         float moved = lastpos.getDistanceFrom(pos_translator.vect_show);
1019                         m_step_distance_counter += moved;
1020                         if(m_step_distance_counter > 1.5*BS){
1021                                 m_step_distance_counter = 0;
1022                                 if(!m_is_local_player && m_prop.makes_footstep_sound){
1023                                         INodeDefManager *ndef = m_gamedef->ndef();
1024                                         v3s16 p = floatToInt(getPosition() + v3f(0,
1025                                                         (m_prop.collisionbox.MinEdge.Y-0.5)*BS, 0), BS);
1026                                         MapNode n = m_env->getMap().getNodeNoEx(p);
1027                                         SimpleSoundSpec spec = ndef->get(n).sound_footstep;
1028                                         m_gamedef->sound()->playSoundAt(spec, false, getPosition());
1029                                 }
1030                         }
1031                 }
1032
1033                 m_anim_timer += dtime;
1034                 if(m_anim_timer >= m_anim_framelength){
1035                         m_anim_timer -= m_anim_framelength;
1036                         m_anim_frame++;
1037                         if(m_anim_frame >= m_anim_num_frames)
1038                                 m_anim_frame = 0;
1039                 }
1040
1041                 updateTexturePos();
1042
1043                 if(m_reset_textures_timer >= 0){
1044                         m_reset_textures_timer -= dtime;
1045                         if(m_reset_textures_timer <= 0){
1046                                 m_reset_textures_timer = -1;
1047                                 updateTextures("");
1048                         }
1049                 }
1050                 if(fabs(m_prop.automatic_rotate) > 0.001){
1051                         m_yaw += dtime * m_prop.automatic_rotate * 180 / M_PI;
1052                         updateNodePos();
1053                 }
1054
1055                 // REMAINING ATTACHMENT ISSUES:
1056                 // Absolute Position of attachments is printed differently here than what it's set to in the SetAttachment function.
1057                 // Apparently here it prints the origin of the parent, but ignores the offset it was actually set to.
1058
1059                 //if(m_animated_meshnode != NULL && m_attachment_parent != NULL)
1060                 //      errorstream<<"Attachment position, step: "<<m_animated_meshnode->getAbsolutePosition().X<<","<<m_animated_meshnode->getAbsolutePosition().Y<<","<<m_animated_meshnode->getAbsolutePosition().Z<<std::endl;
1061         }
1062
1063         void updateTexturePos()
1064         {
1065                 if(m_spritenode){
1066                         scene::ICameraSceneNode* camera =
1067                                         m_spritenode->getSceneManager()->getActiveCamera();
1068                         if(!camera)
1069                                 return;
1070                         v3f cam_to_entity = m_spritenode->getAbsolutePosition()
1071                                         - camera->getAbsolutePosition();
1072                         cam_to_entity.normalize();
1073
1074                         int row = m_tx_basepos.Y;
1075                         int col = m_tx_basepos.X;
1076                         
1077                         if(m_tx_select_horiz_by_yawpitch)
1078                         {
1079                                 if(cam_to_entity.Y > 0.75)
1080                                         col += 5;
1081                                 else if(cam_to_entity.Y < -0.75)
1082                                         col += 4;
1083                                 else{
1084                                         float mob_dir = atan2(cam_to_entity.Z, cam_to_entity.X) / M_PI * 180.;
1085                                         float dir = mob_dir - m_yaw;
1086                                         dir = wrapDegrees_180(dir);
1087                                         //infostream<<"id="<<m_id<<" dir="<<dir<<std::endl;
1088                                         if(fabs(wrapDegrees_180(dir - 0)) <= 45.1)
1089                                                 col += 2;
1090                                         else if(fabs(wrapDegrees_180(dir - 90)) <= 45.1)
1091                                                 col += 3;
1092                                         else if(fabs(wrapDegrees_180(dir - 180)) <= 45.1)
1093                                                 col += 0;
1094                                         else if(fabs(wrapDegrees_180(dir + 90)) <= 45.1)
1095                                                 col += 1;
1096                                         else
1097                                                 col += 4;
1098                                 }
1099                         }
1100                         
1101                         // Animation goes downwards
1102                         row += m_anim_frame;
1103
1104                         float txs = m_tx_size.X;
1105                         float tys = m_tx_size.Y;
1106                         setBillboardTextureMatrix(m_spritenode,
1107                                         txs, tys, col, row);
1108                 }
1109         }
1110
1111         void updateTextures(const std::string &mod)
1112         {
1113                 ITextureSource *tsrc = m_gamedef->tsrc();
1114
1115                 if(m_spritenode)
1116                 {
1117                         if(m_prop.visual == "sprite")
1118                         {
1119                                 std::string texturestring = "unknown_block.png";
1120                                 if(m_prop.textures.size() >= 1)
1121                                         texturestring = m_prop.textures[0];
1122                                 texturestring += mod;
1123                                 m_spritenode->setMaterialTexture(0,
1124                                                 tsrc->getTextureRaw(texturestring));
1125
1126                                 // Does not work yet with the current lighting settings
1127                                 m_meshnode->getMaterial(0).AmbientColor = m_prop.colors[0];
1128                                 m_meshnode->getMaterial(0).DiffuseColor = m_prop.colors[0];
1129                         }
1130                 }
1131                 if(m_animated_meshnode)
1132                 {
1133                         if(m_prop.visual == "mesh")
1134                         {
1135                                 for (u32 i = 0; i < m_prop.textures.size(); ++i)
1136                                 {
1137                                         std::string texturestring = m_prop.textures[i];
1138                                         if(texturestring == "")
1139                                                 continue; // Empty texture string means don't modify that material
1140                                         texturestring += mod;
1141                                         video::ITexture* texture = tsrc->getTextureRaw(texturestring);
1142                                         if(!texture)
1143                                         {
1144                                                 errorstream<<"GenericCAO::updateTextures(): Could not load texture "<<texturestring<<std::endl;
1145                                                 continue;
1146                                         }
1147
1148                                         // Set material flags and texture
1149                                         m_animated_meshnode->setMaterialTexture(i, texture);
1150                                         video::SMaterial& material = m_animated_meshnode->getMaterial(i);
1151                                         material.setFlag(video::EMF_LIGHTING, false);
1152                                         material.setFlag(video::EMF_BILINEAR_FILTER, false);
1153                                 }
1154                                 for (u32 i = 0; i < m_prop.colors.size(); ++i)
1155                                 {
1156                                         // Does not work yet with the current lighting settings
1157                                         m_animated_meshnode->getMaterial(i).AmbientColor = m_prop.colors[i];
1158                                         m_animated_meshnode->getMaterial(i).DiffuseColor = m_prop.colors[i];
1159                                 }
1160                         }
1161                 }
1162                 if(m_meshnode)
1163                 {
1164                         if(m_prop.visual == "cube")
1165                         {
1166                                 for (u32 i = 0; i < 6; ++i)
1167                                 {
1168                                         std::string texturestring = "unknown_block.png";
1169                                         if(m_prop.textures.size() > i)
1170                                                 texturestring = m_prop.textures[i];
1171                                         texturestring += mod;
1172                                         AtlasPointer ap = tsrc->getTexture(texturestring);
1173
1174                                         // Get the tile texture and atlas transformation
1175                                         video::ITexture* atlas = ap.atlas;
1176                                         v2f pos = ap.pos;
1177                                         v2f size = ap.size;
1178
1179                                         // Set material flags and texture
1180                                         video::SMaterial& material = m_meshnode->getMaterial(i);
1181                                         material.setFlag(video::EMF_LIGHTING, false);
1182                                         material.setFlag(video::EMF_BILINEAR_FILTER, false);
1183                                         material.setTexture(0, atlas);
1184                                         material.getTextureMatrix(0).setTextureTranslate(pos.X, pos.Y);
1185                                         material.getTextureMatrix(0).setTextureScale(size.X, size.Y);
1186
1187                                         // Does not work yet with the current lighting settings
1188                                         m_meshnode->getMaterial(i).AmbientColor = m_prop.colors[i];
1189                                         m_meshnode->getMaterial(i).DiffuseColor = m_prop.colors[i];
1190                                 }
1191                         }
1192                         else if(m_prop.visual == "upright_sprite")
1193                         {
1194                                 scene::IMesh *mesh = m_meshnode->getMesh();
1195                                 {
1196                                         std::string tname = "unknown_object.png";
1197                                         if(m_prop.textures.size() >= 1)
1198                                                 tname = m_prop.textures[0];
1199                                         tname += mod;
1200                                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(0);
1201                                         buf->getMaterial().setTexture(0,
1202                                                         tsrc->getTextureRaw(tname));
1203                                         
1204                                         // Does not work yet with the current lighting settings
1205                                         m_meshnode->getMaterial(0).AmbientColor = m_prop.colors[0];
1206                                         m_meshnode->getMaterial(0).DiffuseColor = m_prop.colors[0];
1207                                 }
1208                                 {
1209                                         std::string tname = "unknown_object.png";
1210                                         if(m_prop.textures.size() >= 2)
1211                                                 tname = m_prop.textures[1];
1212                                         else if(m_prop.textures.size() >= 1)
1213                                                 tname = m_prop.textures[0];
1214                                         tname += mod;
1215                                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(1);
1216                                         buf->getMaterial().setTexture(0,
1217                                                         tsrc->getTextureRaw(tname));
1218
1219                                         // Does not work yet with the current lighting settings
1220                                         m_meshnode->getMaterial(1).AmbientColor = m_prop.colors[1]; 
1221                                         m_meshnode->getMaterial(1).DiffuseColor = m_prop.colors[1]; 
1222                                 }
1223                         }
1224                 }
1225         }
1226
1227         void updateAnimations()
1228         {
1229                 if(!m_animated_meshnode)
1230                         return;
1231
1232                 m_animated_meshnode->setFrameLoop((int)m_frames.X, (int)m_frames.Y);
1233                 m_animated_meshnode->setAnimationSpeed(m_frame_speed);
1234                 m_animated_meshnode->setTransitionTime(m_frame_blend);
1235         }
1236
1237         void updateBonePosRot()
1238         {
1239                 if(m_bone_posrot.size() > 0)
1240                 {
1241                         m_animated_meshnode->setJointMode(irr::scene::EJUOR_CONTROL); // To write positions to the mesh on render
1242                         for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_posrot.begin(); ii != m_bone_posrot.end(); ++ii){
1243                                 std::string bone_name = (*ii).first;
1244                                 v3f bone_pos = (*ii).second.X;
1245                                 v3f bone_rot = (*ii).second.Y;
1246                                 irr::scene::IBoneSceneNode* bone = m_animated_meshnode->getJointNode(bone_name.c_str());
1247                                 bone->setPosition(bone_pos);
1248                                 bone->setRotation(bone_rot);
1249                         }
1250                 }
1251         }
1252         
1253         void updateAttachments()
1254         {
1255                 // REMAINING ATTACHMENT ISSUES:
1256                 // We get to this function when the object is an attachment that needs to
1257                 // be attached to its parent. If a bone is set we attach it to that skeletal
1258                 // bone, otherwise just to the object's origin. Attachments should not copy parent
1259                 // position as that's laggy... instead the Irrlicht function(s) to attach should
1260                 // be used. If the parent object is NULL that means this object should be detached.
1261                 // This function is only called whenever a GENERIC_CMD_SET_ATTACHMENT message is received.
1262                 
1263                 // We already attach our entity on the server too (copy position). Reason we attach
1264                 // to the client as well is first of all lag. The server sends the position
1265                 // of the child separately than that of the parent, so even on localhost
1266                 // you'd see the child lagging behind. Models are also client-side, so this is
1267                 // needed to read bone data and attach to joints.
1268                 
1269                 // Functions:
1270                 // - m_attachment_parent is ClientActiveObject* for the parent entity.
1271                 // - m_attachment_bone is std::string of the bone, "" means none.
1272                 // - m_attachment_position is v3f and represents the position offset of the attachment.
1273                 // - m_attachment_rotation is v3f and represents the rotation offset of the attachment.
1274                 
1275                 // Implementation information:
1276                 // From what I know, we need to get the AnimatedMeshSceneNode of m_attachment_parent then
1277                 // use parent_node->addChild(m_animated_meshnode) for position attachment. For skeletal
1278                 // attachment I don't know yet. Same must be used to detach when a NULL parent is received.
1279
1280                 //Useful links:
1281                 // http://irrlicht.sourceforge.net/forum/viewtopic.php?t=7514
1282                 // http://www.irrlicht3d.org/wiki/index.php?n=Main.HowToUseTheNewAnimationSystem
1283                 // http://gamedev.stackexchange.com/questions/27363/finding-the-endpoint-of-a-named-bone-in-irrlicht
1284                 // Irrlicht documentation: http://irrlicht.sourceforge.net/docu/
1285
1286                 if(m_attachment_parent == NULL || m_attachment_parent->isLocalPlayer()) // Detach
1287                 {
1288                         if(m_meshnode)
1289                         {
1290                                 v3f old_position = m_meshnode->getAbsolutePosition();
1291                                 v3f old_rotation = m_meshnode->getRotation();
1292                                 m_meshnode->setParent(m_smgr->getRootSceneNode());
1293                                 m_meshnode->setPosition(old_position);
1294                                 m_meshnode->setRotation(old_rotation);
1295                                 m_meshnode->updateAbsolutePosition();
1296                         }
1297                         if(m_animated_meshnode)
1298                         {
1299                                 v3f old_position = m_animated_meshnode->getAbsolutePosition();
1300                                 v3f old_rotation = m_animated_meshnode->getRotation();
1301                                 m_animated_meshnode->setParent(m_smgr->getRootSceneNode());
1302                                 m_animated_meshnode->setPosition(old_position);
1303                                 m_animated_meshnode->setRotation(old_rotation);
1304                                 m_animated_meshnode->updateAbsolutePosition();
1305                         }
1306                         if(m_spritenode)
1307                         {
1308                                 v3f old_position = m_spritenode->getAbsolutePosition();
1309                                 v3f old_rotation = m_spritenode->getRotation();
1310                                 m_spritenode->setParent(m_smgr->getRootSceneNode());
1311                                 m_spritenode->setPosition(old_position);
1312                                 m_spritenode->setRotation(old_rotation);
1313                                 m_spritenode->updateAbsolutePosition();
1314                         }
1315                 }
1316                 else // Attach
1317                 {
1318                         // REMAINING ATTACHMENT ISSUES:
1319                         // The code below should cause the child to get attached, but for some reason it's not working
1320                         // A debug print confirms both position and absolute position are set accordingly, but the object still doesn't show
1321                         // Position and Absolute Position were tested to be set properly here
1322
1323                         scene::IMeshSceneNode *parent_mesh = NULL;
1324                         if(m_attachment_parent->getMeshSceneNode())
1325                                 parent_mesh = m_attachment_parent->getMeshSceneNode();
1326                         scene::IAnimatedMeshSceneNode *parent_animated_mesh = NULL;
1327                         if(m_attachment_parent->getAnimatedMeshSceneNode())
1328                                 parent_animated_mesh = m_attachment_parent->getAnimatedMeshSceneNode();
1329                         scene::IBillboardSceneNode *parent_sprite = NULL;
1330                         if(m_attachment_parent->getSpriteSceneNode())
1331                                 parent_sprite = m_attachment_parent->getSpriteSceneNode();
1332
1333                         scene::IBoneSceneNode *parent_bone = NULL;
1334                         if(parent_animated_mesh && m_attachment_bone != "")
1335                                 parent_bone = parent_animated_mesh->getJointNode(m_attachment_bone.c_str());
1336
1337                         // TODO: Perhaps use polymorphism here to save code duplication
1338                         if(m_meshnode){
1339                                 if(parent_bone){
1340                                         m_meshnode->setParent(parent_bone);
1341                                         m_meshnode->setPosition(m_attachment_position);
1342                                         m_meshnode->setRotation(m_attachment_rotation);
1343                                         m_meshnode->updateAbsolutePosition();
1344                                 }
1345                                 else
1346                                 {
1347                                         if(parent_mesh){
1348                                                 m_meshnode->setParent(parent_mesh);
1349                                                 m_meshnode->setPosition(m_attachment_position);
1350                                                 m_meshnode->setRotation(m_attachment_rotation);
1351                                                 m_meshnode->updateAbsolutePosition();
1352                                         }
1353                                         else if(parent_animated_mesh){
1354                                                 m_meshnode->setParent(parent_animated_mesh);
1355                                                 m_meshnode->setPosition(m_attachment_position);
1356                                                 m_meshnode->setRotation(m_attachment_rotation);
1357                                                 m_meshnode->updateAbsolutePosition();
1358                                         }
1359                                         else if(parent_sprite){
1360                                                 m_meshnode->setParent(parent_sprite);
1361                                                 m_meshnode->setPosition(m_attachment_position);
1362                                                 m_meshnode->setRotation(m_attachment_rotation);
1363                                                 m_meshnode->updateAbsolutePosition();
1364                                         }
1365                                 }
1366                         }
1367                         if(m_animated_meshnode){
1368                                 if(parent_bone){
1369                                         m_animated_meshnode->setParent(parent_bone);
1370                                         m_animated_meshnode->setPosition(m_attachment_position);
1371                                         m_animated_meshnode->setRotation(m_attachment_rotation);
1372                                         m_animated_meshnode->updateAbsolutePosition();
1373                                 }
1374                                 else
1375                                 {
1376                                         if(parent_mesh){
1377                                                 m_animated_meshnode->setParent(parent_mesh);
1378                                                 m_animated_meshnode->setPosition(m_attachment_position);
1379                                                 m_animated_meshnode->setRotation(m_attachment_rotation);
1380                                                 m_animated_meshnode->updateAbsolutePosition();
1381                                         }
1382                                         else if(parent_animated_mesh){
1383                                                 m_animated_meshnode->setParent(parent_animated_mesh);
1384                                                 m_animated_meshnode->setPosition(m_attachment_position);
1385                                                 m_animated_meshnode->setRotation(m_attachment_rotation);
1386                                                 m_animated_meshnode->updateAbsolutePosition();
1387                                         }
1388                                         else if(parent_sprite){
1389                                                 m_animated_meshnode->setParent(parent_sprite);
1390                                                 m_animated_meshnode->setPosition(m_attachment_position);
1391                                                 m_animated_meshnode->setRotation(m_attachment_rotation);
1392                                                 m_animated_meshnode->updateAbsolutePosition();
1393                                         }
1394                                 }
1395                         }
1396                         if(m_spritenode){
1397                                 if(parent_bone){
1398                                         m_spritenode->setParent(parent_bone);
1399                                         m_spritenode->setPosition(m_attachment_position);
1400                                         m_spritenode->setRotation(m_attachment_rotation);
1401                                         m_spritenode->updateAbsolutePosition();
1402                                 }
1403                                 else
1404                                 {
1405                                         if(parent_mesh){
1406                                                 m_spritenode->setParent(parent_mesh);
1407                                                 m_spritenode->setPosition(m_attachment_position);
1408                                                 m_spritenode->setRotation(m_attachment_rotation);
1409                                                 m_spritenode->updateAbsolutePosition();
1410                                         }
1411                                         else if(parent_animated_mesh){
1412                                                 m_spritenode->setParent(parent_animated_mesh);
1413                                                 m_spritenode->setPosition(m_attachment_position);
1414                                                 m_spritenode->setRotation(m_attachment_rotation);
1415                                                 m_spritenode->updateAbsolutePosition();
1416                                         }
1417                                         else if(parent_sprite){
1418                                                 m_spritenode->setParent(parent_sprite);
1419                                                 m_spritenode->setPosition(m_attachment_position);
1420                                                 m_spritenode->setRotation(m_attachment_rotation);
1421                                                 m_spritenode->updateAbsolutePosition();
1422                                         }
1423                                 }
1424                         }
1425                 }
1426         }
1427
1428         void processMessage(const std::string &data)
1429         {
1430                 //infostream<<"GenericCAO: Got message"<<std::endl;
1431                 std::istringstream is(data, std::ios::binary);
1432                 // command
1433                 u8 cmd = readU8(is);
1434                 if(cmd == GENERIC_CMD_SET_PROPERTIES)
1435                 {
1436                         m_prop = gob_read_set_properties(is);
1437
1438                         m_selection_box = m_prop.collisionbox;
1439                         m_selection_box.MinEdge *= BS;
1440                         m_selection_box.MaxEdge *= BS;
1441                                 
1442                         m_tx_size.X = 1.0 / m_prop.spritediv.X;
1443                         m_tx_size.Y = 1.0 / m_prop.spritediv.Y;
1444
1445                         if(!m_initial_tx_basepos_set){
1446                                 m_initial_tx_basepos_set = true;
1447                                 m_tx_basepos = m_prop.initial_sprite_basepos;
1448                         }
1449                         
1450                         expireVisuals();
1451                 }
1452                 else if(cmd == GENERIC_CMD_UPDATE_POSITION)
1453                 {
1454                         // Not sent by the server if the object is an attachment
1455                         
1456                         m_position = readV3F1000(is);
1457                         m_velocity = readV3F1000(is);
1458                         m_acceleration = readV3F1000(is);
1459                         if(fabs(m_prop.automatic_rotate) < 0.001)
1460                                 m_yaw = readF1000(is);
1461                         bool do_interpolate = readU8(is);
1462                         bool is_end_position = readU8(is);
1463                         float update_interval = readF1000(is);
1464
1465                         // Place us a bit higher if we're physical, to not sink into
1466                         // the ground due to sucky collision detection...
1467                         if(m_prop.physical)
1468                                 m_position += v3f(0,0.002,0);
1469                                 
1470                         if(do_interpolate){
1471                                 if(!m_prop.physical)
1472                                         pos_translator.update(m_position, is_end_position, update_interval);
1473                         } else {
1474                                 pos_translator.init(m_position);
1475                         }
1476                         updateNodePos();
1477                 }
1478                 else if(cmd == GENERIC_CMD_SET_TEXTURE_MOD)
1479                 {
1480                         std::string mod = deSerializeString(is);
1481                         updateTextures(mod);
1482                 }
1483                 else if(cmd == GENERIC_CMD_SET_SPRITE)
1484                 {
1485                         v2s16 p = readV2S16(is);
1486                         int num_frames = readU16(is);
1487                         float framelength = readF1000(is);
1488                         bool select_horiz_by_yawpitch = readU8(is);
1489                         
1490                         m_tx_basepos = p;
1491                         m_anim_num_frames = num_frames;
1492                         m_anim_framelength = framelength;
1493                         m_tx_select_horiz_by_yawpitch = select_horiz_by_yawpitch;
1494
1495                         updateTexturePos();
1496                 }
1497                 else if(cmd == GENERIC_CMD_SET_ANIMATIONS)
1498                 {
1499                         m_frames = readV2F1000(is);
1500                         m_frame_speed = readF1000(is);
1501                         m_frame_blend = readF1000(is);
1502
1503                         expireVisuals(); // Automatically calls the proper function next
1504                 }
1505                 else if(cmd == GENERIC_CMD_SET_BONE_POSROT)
1506                 {
1507                         std::string bone = deSerializeString(is);
1508                         v3f position = readV3F1000(is);
1509                         v3f rotation = readV3F1000(is);
1510                         m_bone_posrot[bone] = core::vector2d<v3f>(position, rotation);
1511
1512                         expireVisuals(); // Automatically calls the proper function next
1513                 }
1514                 else if(cmd == GENERIC_CMD_SET_ATTACHMENT)
1515                 {
1516                         int parent_id = readS16(is);
1517                         ClientActiveObject *obj = m_env->getActiveObject(parent_id);
1518                         if(!parent_id || !obj)
1519                                 obj = NULL;
1520                         m_attachment_parent = obj;
1521                         m_attachment_bone = deSerializeString(is);
1522                         m_attachment_position = readV3F1000(is);
1523                         m_attachment_rotation = readV3F1000(is);
1524
1525                         expireVisuals(); // Automatically calls the proper function next
1526                 }
1527                 else if(cmd == GENERIC_CMD_PUNCHED)
1528                 {
1529                         /*s16 damage =*/ readS16(is);
1530                         s16 result_hp = readS16(is);
1531                         
1532                         m_hp = result_hp;
1533                 }
1534                 else if(cmd == GENERIC_CMD_UPDATE_ARMOR_GROUPS)
1535                 {
1536                         m_armor_groups.clear();
1537                         int armor_groups_size = readU16(is);
1538                         for(int i=0; i<armor_groups_size; i++){
1539                                 std::string name = deSerializeString(is);
1540                                 int rating = readS16(is);
1541                                 m_armor_groups[name] = rating;
1542                         }
1543                 }
1544         }
1545         
1546         bool directReportPunch(v3f dir, const ItemStack *punchitem=NULL,
1547                         float time_from_last_punch=1000000)
1548         {
1549                 assert(punchitem);
1550                 const ToolCapabilities *toolcap =
1551                                 &punchitem->getToolCapabilities(m_gamedef->idef());
1552                 PunchDamageResult result = getPunchDamage(
1553                                 m_armor_groups,
1554                                 toolcap,
1555                                 punchitem,
1556                                 time_from_last_punch);
1557
1558                 if(result.did_punch && result.damage != 0)
1559                 {
1560                         if(result.damage < m_hp){
1561                                 m_hp -= result.damage;
1562                         } else {
1563                                 m_hp = 0;
1564                                 // TODO: Execute defined fast response
1565                                 // As there is no definition, make a smoke puff
1566                                 ClientSimpleObject *simple = createSmokePuff(
1567                                                 m_smgr, m_env, m_position,
1568                                                 m_prop.visual_size * BS);
1569                                 m_env->addSimpleObject(simple);
1570                         }
1571                         // TODO: Execute defined fast response
1572                         // Flashing shall suffice as there is no definition
1573                         m_reset_textures_timer = 0.05;
1574                         if(result.damage >= 2)
1575                                 m_reset_textures_timer += 0.05 * result.damage;
1576                         updateTextures("^[brighten");
1577                 }
1578                 
1579                 return false;
1580         }
1581         
1582         std::string debugInfoText()
1583         {
1584                 std::ostringstream os(std::ios::binary);
1585                 os<<"GenericCAO hp="<<m_hp<<"\n";
1586                 os<<"armor={";
1587                 for(ItemGroupList::const_iterator i = m_armor_groups.begin();
1588                                 i != m_armor_groups.end(); i++){
1589                         os<<i->first<<"="<<i->second<<", ";
1590                 }
1591                 os<<"}";
1592                 return os.str();
1593         }
1594 };
1595
1596 // Prototype
1597 GenericCAO proto_GenericCAO(NULL, NULL);
1598
1599