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