]> git.lizzy.rs Git - dragonfireclient.git/blob - src/content_cao.cpp
821862c9be40340ba7e2f5b36e936b7562e57618
[dragonfireclient.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                 if(permanent) // Should be true when removing the object permanently and false when refreshing (eg: updating visuals)
772                 {
773                         // Detach this object's children
774                         for(std::vector<core::vector2d<int> >::iterator ii = attachment_list.begin(); ii != attachment_list.end(); ii++)
775                         {
776                                 if(ii->Y == this->getId()) // Is a child of our object
777                                 {
778                                         ii->Y = 0;
779                                         ClientActiveObject *obj = m_env->getActiveObject(ii->X); // Get the object of the child
780                                         if(obj)
781                                                 obj->updateParent();
782                                 }
783                         }
784                         // Delete this object from the attachments list
785                         for(std::vector<core::vector2d<int> >::iterator ii = attachment_list.begin(); ii != attachment_list.end(); ii++)
786                         {
787                                 if(ii->X == this->getId()) // Is our object
788                                 {
789                                         attachment_list.erase(ii);
790                                         break;
791                                 }
792                         }
793                 }
794
795                 if(m_meshnode){
796                         m_meshnode->remove();
797                         m_meshnode = NULL;
798                 }
799                 if(m_animated_meshnode){
800                         m_animated_meshnode->remove();
801                         m_animated_meshnode = NULL;
802                 }
803                 if(m_spritenode){
804                         m_spritenode->remove();
805                         m_spritenode = NULL;
806                 }
807         }
808
809         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
810                         IrrlichtDevice *irr)
811         {
812                 m_smgr = smgr;
813                 m_irr = irr;
814
815                 if(m_meshnode != NULL || m_animated_meshnode != NULL || m_spritenode != NULL)
816                         return;
817                 
818                 m_visuals_expired = false;
819
820                 if(!m_prop.is_visible || m_is_local_player)
821                         return;
822         
823                 //video::IVideoDriver* driver = smgr->getVideoDriver();
824
825                 if(m_prop.visual == "sprite"){
826                         infostream<<"GenericCAO::addToScene(): single_sprite"<<std::endl;
827                         m_spritenode = smgr->addBillboardSceneNode(
828                                         NULL, v2f(1, 1), v3f(0,0,0), -1);
829                         m_spritenode->setMaterialTexture(0,
830                                         tsrc->getTextureRaw("unknown_block.png"));
831                         m_spritenode->setMaterialFlag(video::EMF_LIGHTING, false);
832                         m_spritenode->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
833                         m_spritenode->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF);
834                         m_spritenode->setMaterialFlag(video::EMF_FOG_ENABLE, true);
835                         u8 li = m_last_light;
836                         m_spritenode->setColor(video::SColor(255,li,li,li));
837                         m_spritenode->setSize(m_prop.visual_size*BS);
838                         {
839                                 const float txs = 1.0 / 1;
840                                 const float tys = 1.0 / 1;
841                                 setBillboardTextureMatrix(m_spritenode,
842                                                 txs, tys, 0, 0);
843                         }
844                 }
845                 else if(m_prop.visual == "upright_sprite")
846                 {
847                         scene::SMesh *mesh = new scene::SMesh();
848                         double dx = BS*m_prop.visual_size.X/2;
849                         double dy = BS*m_prop.visual_size.Y/2;
850                         { // Front
851                         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
852                         u8 li = m_last_light;
853                         video::SColor c(255,li,li,li);
854                         video::S3DVertex vertices[4] =
855                         {
856                                 video::S3DVertex(-dx,-dy,0, 0,0,0, c, 0,1),
857                                 video::S3DVertex(dx,-dy,0, 0,0,0, c, 1,1),
858                                 video::S3DVertex(dx,dy,0, 0,0,0, c, 1,0),
859                                 video::S3DVertex(-dx,dy,0, 0,0,0, c, 0,0),
860                         };
861                         u16 indices[] = {0,1,2,2,3,0};
862                         buf->append(vertices, 4, indices, 6);
863                         // Set material
864                         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
865                         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
866                         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
867                         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
868                         // Add to mesh
869                         mesh->addMeshBuffer(buf);
870                         buf->drop();
871                         }
872                         { // Back
873                         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
874                         u8 li = m_last_light;
875                         video::SColor c(255,li,li,li);
876                         video::S3DVertex vertices[4] =
877                         {
878                                 video::S3DVertex(dx,-dy,0, 0,0,0, c, 1,1),
879                                 video::S3DVertex(-dx,-dy,0, 0,0,0, c, 0,1),
880                                 video::S3DVertex(-dx,dy,0, 0,0,0, c, 0,0),
881                                 video::S3DVertex(dx,dy,0, 0,0,0, c, 1,0),
882                         };
883                         u16 indices[] = {0,1,2,2,3,0};
884                         buf->append(vertices, 4, indices, 6);
885                         // Set material
886                         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
887                         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
888                         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
889                         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
890                         // Add to mesh
891                         mesh->addMeshBuffer(buf);
892                         buf->drop();
893                         }
894                         m_meshnode = smgr->addMeshSceneNode(mesh, m_smgr->getRootSceneNode());
895                         mesh->drop();
896                         // Set it to use the materials of the meshbuffers directly.
897                         // This is needed for changing the texture in the future
898                         m_meshnode->setReadOnlyMaterials(true);
899                 }
900                 else if(m_prop.visual == "cube"){
901                         infostream<<"GenericCAO::addToScene(): cube"<<std::endl;
902                         scene::IMesh *mesh = createCubeMesh(v3f(BS,BS,BS));
903                         m_meshnode = smgr->addMeshSceneNode(mesh, m_smgr->getRootSceneNode());
904                         mesh->drop();
905                         
906                         m_meshnode->setScale(v3f(m_prop.visual_size.X,
907                                         m_prop.visual_size.Y,
908                                         m_prop.visual_size.X));
909                         u8 li = m_last_light;
910                         setMeshColor(m_meshnode->getMesh(), video::SColor(255,li,li,li));
911                 }
912                 else if(m_prop.visual == "mesh"){
913                         infostream<<"GenericCAO::addToScene(): mesh"<<std::endl;
914                         scene::IAnimatedMesh *mesh = smgr->getMesh(m_prop.mesh.c_str());
915                         if(mesh)
916                         {
917                                 m_animated_meshnode = smgr->addAnimatedMeshSceneNode(mesh, m_smgr->getRootSceneNode());
918                                 m_animated_meshnode->animateJoints(); // Needed for some animations
919                                 m_animated_meshnode->setScale(v3f(m_prop.visual_size.X,
920                                                 m_prop.visual_size.Y,
921                                                 m_prop.visual_size.X));
922                                 u8 li = m_last_light;
923                                 setMeshColor(m_animated_meshnode->getMesh(), video::SColor(255,li,li,li));
924                         }
925                         else
926                                 errorstream<<"GenericCAO::addToScene(): Could not load mesh "<<m_prop.mesh<<std::endl;
927                 }
928                 else if(m_prop.visual == "wielditem"){
929                         infostream<<"GenericCAO::addToScene(): node"<<std::endl;
930                         infostream<<"textures: "<<m_prop.textures.size()<<std::endl;
931                         if(m_prop.textures.size() >= 1){
932                                 infostream<<"textures[0]: "<<m_prop.textures[0]<<std::endl;
933                                 IItemDefManager *idef = m_gamedef->idef();
934                                 ItemStack item(m_prop.textures[0], 1, 0, "", idef);
935                                 scene::IMesh *item_mesh = item.getDefinition(idef).wield_mesh;
936                                 
937                                 // Copy mesh to be able to set unique vertex colors
938                                 scene::IMeshManipulator *manip =
939                                                 irr->getVideoDriver()->getMeshManipulator();
940                                 scene::IMesh *mesh = manip->createMeshUniquePrimitives(item_mesh);
941
942                                 m_meshnode = smgr->addMeshSceneNode(mesh, m_smgr->getRootSceneNode());
943                                 mesh->drop();
944                                 
945                                 m_meshnode->setScale(v3f(m_prop.visual_size.X/2,
946                                                 m_prop.visual_size.Y/2,
947                                                 m_prop.visual_size.X/2));
948                                 u8 li = m_last_light;
949                                 setMeshColor(m_meshnode->getMesh(), video::SColor(255,li,li,li));
950                         }
951                 } else {
952                         infostream<<"GenericCAO::addToScene(): \""<<m_prop.visual
953                                         <<"\" not supported"<<std::endl;
954                 }
955                 updateTextures("");
956                 
957                 scene::ISceneNode *node = NULL;
958                 if(m_spritenode)
959                         node = m_spritenode;
960                 else if(m_animated_meshnode)
961                         node = m_animated_meshnode;
962                 else if(m_meshnode)
963                         node = m_meshnode;
964                 if(node && m_is_player && !m_is_local_player){
965                         // Add a text node for showing the name
966                         gui::IGUIEnvironment* gui = irr->getGUIEnvironment();
967                         std::wstring wname = narrow_to_wide(m_name);
968                         m_textnode = smgr->addTextSceneNode(gui->getBuiltInFont(),
969                                         wname.c_str(), video::SColor(255,255,255,255), node);
970                         m_textnode->setPosition(v3f(0, BS*1.1, 0));
971                 }
972                 
973                 updateNodePos();
974         }
975
976         void expireVisuals()
977         {
978                 m_visuals_expired = true;
979         }
980                 
981         void updateLight(u8 light_at_pos)
982         {
983                 // Objects attached to the local player should always be hidden
984                 if(getParent() != NULL && getParent()->isLocalPlayer())
985                         m_is_visible = false;
986                 else
987                         m_is_visible = (m_hp != 0);
988                 u8 li = decode_light(light_at_pos);
989
990                 if(li != m_last_light){
991                         m_last_light = li;
992                         video::SColor color(255,li,li,li);
993                         if(m_meshnode){
994                                 setMeshColor(m_meshnode->getMesh(), color);
995                                 m_meshnode->setVisible(m_is_visible);
996                         }
997                         if(m_animated_meshnode){
998                                 setMeshColor(m_animated_meshnode->getMesh(), color);
999                                 m_animated_meshnode->setVisible(m_is_visible);
1000                         }
1001                         if(m_spritenode){
1002                                 m_spritenode->setColor(color);
1003                                 m_spritenode->setVisible(m_is_visible);
1004                         }
1005                 }
1006         }
1007
1008         v3s16 getLightPosition()
1009         {
1010                 return floatToInt(m_position, BS);
1011         }
1012
1013         void updateNodePos()
1014         {
1015                 if(getParent() != NULL)
1016                         return;
1017
1018                 if(m_meshnode){
1019                         m_meshnode->setPosition(pos_translator.vect_show);
1020                         v3f rot = m_meshnode->getRotation();
1021                         rot.Y = -m_yaw;
1022                         m_meshnode->setRotation(rot);
1023                 }
1024                 if(m_animated_meshnode){
1025                         m_animated_meshnode->setPosition(pos_translator.vect_show);
1026                         v3f rot = m_animated_meshnode->getRotation();
1027                         rot.Y = -m_yaw;
1028                         m_animated_meshnode->setRotation(rot);
1029                 }
1030                 if(m_spritenode){
1031                         m_spritenode->setPosition(pos_translator.vect_show);
1032                 }
1033         }
1034
1035         void step(float dtime, ClientEnvironment *env)
1036         {
1037                 v3f lastpos = pos_translator.vect_show;
1038
1039                 if(m_visuals_expired && m_smgr && m_irr){
1040                         m_visuals_expired = false;
1041
1042                         // Attachments, part 1: All attached objects must be unparented first, or Irrlicht causes a segmentation fault
1043                         for(std::vector<core::vector2d<int> >::iterator ii = attachment_list.begin(); ii != attachment_list.end(); ii++)
1044                         {
1045                                 if(ii->Y == this->getId()) // This is a child of our parent
1046                                 {
1047                                         ClientActiveObject *obj = m_env->getActiveObject(ii->X); // Get the object of the child
1048                                         if(obj)
1049                                         {
1050                                                 scene::IMeshSceneNode *m_child_meshnode = obj->getMeshSceneNode();
1051                                                 scene::IAnimatedMeshSceneNode *m_child_animated_meshnode = obj->getAnimatedMeshSceneNode();
1052                                                 scene::IBillboardSceneNode *m_child_spritenode = obj->getSpriteSceneNode();
1053                                                 if(m_child_meshnode)
1054                                                         m_child_meshnode->setParent(m_smgr->getRootSceneNode());
1055                                                 if(m_child_animated_meshnode)
1056                                                         m_child_animated_meshnode->setParent(m_smgr->getRootSceneNode());
1057                                                 if(m_child_spritenode)
1058                                                         m_child_spritenode->setParent(m_smgr->getRootSceneNode());
1059                                         }
1060                                 }
1061                         }
1062
1063                         removeFromScene(false);
1064                         addToScene(m_smgr, m_gamedef->tsrc(), m_irr);
1065                         updateAnimations();
1066                         updateBonePosRot();
1067                         updateAttachments();
1068
1069                         // Attachments, part 2: Now that the parent has been refreshed, put its attachments back
1070                         for(std::vector<core::vector2d<int> >::iterator ii = attachment_list.begin(); ii != attachment_list.end(); ii++)
1071                         {
1072                                 if(ii->Y == this->getId()) // This is a child of our parent
1073                                 {
1074                                         ClientActiveObject *obj = m_env->getActiveObject(ii->X); // Get the object of the child
1075                                         if(obj)
1076                                                 obj->updateParent();
1077                                 }
1078                         }
1079                 }
1080                 if(getParent() != NULL) // Attachments should be glued to their parent by Irrlicht
1081                 {
1082                         // Set these for later
1083                         if(m_meshnode)
1084                                 m_position = m_meshnode->getAbsolutePosition();
1085                         if(m_animated_meshnode)
1086                                 m_position = m_animated_meshnode->getAbsolutePosition();
1087                         if(m_spritenode)
1088                                 m_position = m_spritenode->getAbsolutePosition();
1089                         m_velocity = v3f(0,0,0);
1090                         m_acceleration = v3f(0,0,0);
1091
1092                         if(m_is_local_player) // Update local player attachment position
1093                         {
1094                                 LocalPlayer *player = m_env->getLocalPlayer();
1095                                 player->overridePosition = getParent()->getPosition();
1096                         }
1097                 }
1098                 else
1099                 {
1100                         if(m_prop.physical){
1101                                 core::aabbox3d<f32> box = m_prop.collisionbox;
1102                                 box.MinEdge *= BS;
1103                                 box.MaxEdge *= BS;
1104                                 collisionMoveResult moveresult;
1105                                 f32 pos_max_d = BS*0.125; // Distance per iteration
1106                                 f32 stepheight = 0;
1107                                 v3f p_pos = m_position;
1108                                 v3f p_velocity = m_velocity;
1109                                 v3f p_acceleration = m_acceleration;
1110                                 IGameDef *gamedef = env->getGameDef();
1111                                 moveresult = collisionMoveSimple(&env->getMap(), gamedef,
1112                                                 pos_max_d, box, stepheight, dtime,
1113                                                 p_pos, p_velocity, p_acceleration);
1114                                 // Apply results
1115                                 m_position = p_pos;
1116                                 m_velocity = p_velocity;
1117                                 m_acceleration = p_acceleration;
1118                                 
1119                                 bool is_end_position = moveresult.collides;
1120                                 pos_translator.update(m_position, is_end_position, dtime);
1121                                 pos_translator.translate(dtime);
1122                                 updateNodePos();
1123                         } else {
1124                                 m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration;
1125                                 m_velocity += dtime * m_acceleration;
1126                                 pos_translator.update(m_position, pos_translator.aim_is_end, pos_translator.anim_time);
1127                                 pos_translator.translate(dtime);
1128                                 updateNodePos();
1129                         }
1130
1131                         float moved = lastpos.getDistanceFrom(pos_translator.vect_show);
1132                         m_step_distance_counter += moved;
1133                         if(m_step_distance_counter > 1.5*BS){
1134                                 m_step_distance_counter = 0;
1135                                 if(!m_is_local_player && m_prop.makes_footstep_sound){
1136                                         INodeDefManager *ndef = m_gamedef->ndef();
1137                                         v3s16 p = floatToInt(getPosition() + v3f(0,
1138                                                         (m_prop.collisionbox.MinEdge.Y-0.5)*BS, 0), BS);
1139                                         MapNode n = m_env->getMap().getNodeNoEx(p);
1140                                         SimpleSoundSpec spec = ndef->get(n).sound_footstep;
1141                                         m_gamedef->sound()->playSoundAt(spec, false, getPosition());
1142                                 }
1143                         }
1144                 }
1145
1146                 m_anim_timer += dtime;
1147                 if(m_anim_timer >= m_anim_framelength){
1148                         m_anim_timer -= m_anim_framelength;
1149                         m_anim_frame++;
1150                         if(m_anim_frame >= m_anim_num_frames)
1151                                 m_anim_frame = 0;
1152                 }
1153
1154                 updateTexturePos();
1155
1156                 if(m_reset_textures_timer >= 0){
1157                         m_reset_textures_timer -= dtime;
1158                         if(m_reset_textures_timer <= 0){
1159                                 m_reset_textures_timer = -1;
1160                                 updateTextures("");
1161                         }
1162                 }
1163                 if(getParent() == NULL && fabs(m_prop.automatic_rotate) > 0.001){
1164                         m_yaw += dtime * m_prop.automatic_rotate * 180 / M_PI;
1165                         updateNodePos();
1166                 }
1167         }
1168
1169         void updateTexturePos()
1170         {
1171                 if(m_spritenode){
1172                         scene::ICameraSceneNode* camera =
1173                                         m_spritenode->getSceneManager()->getActiveCamera();
1174                         if(!camera)
1175                                 return;
1176                         v3f cam_to_entity = m_spritenode->getAbsolutePosition()
1177                                         - camera->getAbsolutePosition();
1178                         cam_to_entity.normalize();
1179
1180                         int row = m_tx_basepos.Y;
1181                         int col = m_tx_basepos.X;
1182                         
1183                         if(m_tx_select_horiz_by_yawpitch)
1184                         {
1185                                 if(cam_to_entity.Y > 0.75)
1186                                         col += 5;
1187                                 else if(cam_to_entity.Y < -0.75)
1188                                         col += 4;
1189                                 else{
1190                                         float mob_dir = atan2(cam_to_entity.Z, cam_to_entity.X) / M_PI * 180.;
1191                                         float dir = mob_dir - m_yaw;
1192                                         dir = wrapDegrees_180(dir);
1193                                         //infostream<<"id="<<m_id<<" dir="<<dir<<std::endl;
1194                                         if(fabs(wrapDegrees_180(dir - 0)) <= 45.1)
1195                                                 col += 2;
1196                                         else if(fabs(wrapDegrees_180(dir - 90)) <= 45.1)
1197                                                 col += 3;
1198                                         else if(fabs(wrapDegrees_180(dir - 180)) <= 45.1)
1199                                                 col += 0;
1200                                         else if(fabs(wrapDegrees_180(dir + 90)) <= 45.1)
1201                                                 col += 1;
1202                                         else
1203                                                 col += 4;
1204                                 }
1205                         }
1206                         
1207                         // Animation goes downwards
1208                         row += m_anim_frame;
1209
1210                         float txs = m_tx_size.X;
1211                         float tys = m_tx_size.Y;
1212                         setBillboardTextureMatrix(m_spritenode,
1213                                         txs, tys, col, row);
1214                 }
1215         }
1216
1217         void updateTextures(const std::string &mod)
1218         {
1219                 ITextureSource *tsrc = m_gamedef->tsrc();
1220
1221                 if(m_spritenode)
1222                 {
1223                         if(m_prop.visual == "sprite")
1224                         {
1225                                 std::string texturestring = "unknown_block.png";
1226                                 if(m_prop.textures.size() >= 1)
1227                                         texturestring = m_prop.textures[0];
1228                                 texturestring += mod;
1229                                 m_spritenode->setMaterialTexture(0,
1230                                                 tsrc->getTextureRaw(texturestring));
1231
1232                                 // This allows setting per-material colors. However, until a real lighting
1233                                 // system is added, the code below will have no effect. Once MineTest
1234                                 // has directional lighting, it should work automatically.
1235                                 if(m_prop.colors.size() >= 1)
1236                                 {
1237                                         m_meshnode->getMaterial(0).AmbientColor = m_prop.colors[0];
1238                                         m_meshnode->getMaterial(0).DiffuseColor = m_prop.colors[0];
1239                                         m_meshnode->getMaterial(0).SpecularColor = m_prop.colors[0];
1240                                 }
1241                         }
1242                 }
1243                 if(m_animated_meshnode)
1244                 {
1245                         if(m_prop.visual == "mesh")
1246                         {
1247                                 for (u32 i = 0; i < m_prop.textures.size(); ++i)
1248                                 {
1249                                         std::string texturestring = m_prop.textures[i];
1250                                         if(texturestring == "")
1251                                                 continue; // Empty texture string means don't modify that material
1252                                         texturestring += mod;
1253                                         video::ITexture* texture = tsrc->getTextureRaw(texturestring);
1254                                         if(!texture)
1255                                         {
1256                                                 errorstream<<"GenericCAO::updateTextures(): Could not load texture "<<texturestring<<std::endl;
1257                                                 continue;
1258                                         }
1259
1260                                         // Set material flags and texture
1261                                         m_animated_meshnode->setMaterialTexture(i, texture);
1262                                         video::SMaterial& material = m_animated_meshnode->getMaterial(i);
1263                                         material.setFlag(video::EMF_LIGHTING, false);
1264                                         material.setFlag(video::EMF_BILINEAR_FILTER, false);
1265                                 }
1266                                 for (u32 i = 0; i < m_prop.colors.size(); ++i)
1267                                 {
1268                                         // This allows setting per-material colors. However, until a real lighting
1269                                         // system is added, the code below will have no effect. Once MineTest
1270                                         // has directional lighting, it should work automatically.
1271                                         m_animated_meshnode->getMaterial(i).AmbientColor = m_prop.colors[i];
1272                                         m_animated_meshnode->getMaterial(i).DiffuseColor = m_prop.colors[i];
1273                                         m_animated_meshnode->getMaterial(i).SpecularColor = m_prop.colors[i];
1274                                 }
1275                         }
1276                 }
1277                 if(m_meshnode)
1278                 {
1279                         if(m_prop.visual == "cube")
1280                         {
1281                                 for (u32 i = 0; i < 6; ++i)
1282                                 {
1283                                         std::string texturestring = "unknown_block.png";
1284                                         if(m_prop.textures.size() > i)
1285                                                 texturestring = m_prop.textures[i];
1286                                         texturestring += mod;
1287                                         AtlasPointer ap = tsrc->getTexture(texturestring);
1288
1289                                         // Get the tile texture and atlas transformation
1290                                         video::ITexture* atlas = ap.atlas;
1291                                         v2f pos = ap.pos;
1292                                         v2f size = ap.size;
1293
1294                                         // Set material flags and texture
1295                                         video::SMaterial& material = m_meshnode->getMaterial(i);
1296                                         material.setFlag(video::EMF_LIGHTING, false);
1297                                         material.setFlag(video::EMF_BILINEAR_FILTER, false);
1298                                         material.setTexture(0, atlas);
1299                                         material.getTextureMatrix(0).setTextureTranslate(pos.X, pos.Y);
1300                                         material.getTextureMatrix(0).setTextureScale(size.X, size.Y);
1301
1302                                         // This allows setting per-material colors. However, until a real lighting
1303                                         // system is added, the code below will have no effect. Once MineTest
1304                                         // has directional lighting, it should work automatically.
1305                                         if(m_prop.colors.size() > i)
1306                                         {
1307                                                 m_meshnode->getMaterial(i).AmbientColor = m_prop.colors[i];
1308                                                 m_meshnode->getMaterial(i).DiffuseColor = m_prop.colors[i];
1309                                                 m_meshnode->getMaterial(i).SpecularColor = m_prop.colors[i];
1310                                         }
1311                                 }
1312                         }
1313                         else if(m_prop.visual == "upright_sprite")
1314                         {
1315                                 scene::IMesh *mesh = m_meshnode->getMesh();
1316                                 {
1317                                         std::string tname = "unknown_object.png";
1318                                         if(m_prop.textures.size() >= 1)
1319                                                 tname = m_prop.textures[0];
1320                                         tname += mod;
1321                                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(0);
1322                                         buf->getMaterial().setTexture(0,
1323                                                         tsrc->getTextureRaw(tname));
1324                                         
1325                                         // This allows setting per-material colors. However, until a real lighting
1326                                         // system is added, the code below will have no effect. Once MineTest
1327                                         // has directional lighting, it should work automatically.
1328                                         if(m_prop.colors.size() >= 1)
1329                                         {
1330                                                 buf->getMaterial().AmbientColor = m_prop.colors[0];
1331                                                 buf->getMaterial().DiffuseColor = m_prop.colors[0];
1332                                                 buf->getMaterial().SpecularColor = m_prop.colors[0];
1333                                         }
1334                                 }
1335                                 {
1336                                         std::string tname = "unknown_object.png";
1337                                         if(m_prop.textures.size() >= 2)
1338                                                 tname = m_prop.textures[1];
1339                                         else if(m_prop.textures.size() >= 1)
1340                                                 tname = m_prop.textures[0];
1341                                         tname += mod;
1342                                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(1);
1343                                         buf->getMaterial().setTexture(0,
1344                                                         tsrc->getTextureRaw(tname));
1345
1346                                         // This allows setting per-material colors. However, until a real lighting
1347                                         // system is added, the code below will have no effect. Once MineTest
1348                                         // has directional lighting, it should work automatically.
1349                                         if(m_prop.colors.size() >= 2)
1350                                         {
1351                                                 buf->getMaterial().AmbientColor = m_prop.colors[1];
1352                                                 buf->getMaterial().DiffuseColor = m_prop.colors[1];
1353                                                 buf->getMaterial().SpecularColor = m_prop.colors[1];
1354                                         }
1355                                         else if(m_prop.colors.size() >= 1)
1356                                         {
1357                                                 buf->getMaterial().AmbientColor = m_prop.colors[0];
1358                                                 buf->getMaterial().DiffuseColor = m_prop.colors[0];
1359                                                 buf->getMaterial().SpecularColor = m_prop.colors[0];
1360                                         }
1361                                 }
1362                         }
1363                 }
1364         }
1365
1366         void updateAnimations()
1367         {
1368                 if(m_animated_meshnode == NULL)
1369                         return;
1370
1371                 m_animated_meshnode->setFrameLoop((int)m_frames.X, (int)m_frames.Y);
1372                 m_animated_meshnode->setAnimationSpeed(m_frame_speed);
1373                 m_animated_meshnode->setTransitionTime(m_frame_blend);
1374         }
1375
1376         void updateBonePosRot()
1377         {
1378                 if(!m_bone_posrot.size() || m_animated_meshnode == NULL)
1379                         return;
1380
1381                 m_animated_meshnode->setJointMode(irr::scene::EJUOR_CONTROL); // To write positions to the mesh on render
1382                 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_posrot.begin(); ii != m_bone_posrot.end(); ++ii){
1383                         std::string bone_name = (*ii).first;
1384                         v3f bone_pos = (*ii).second.X;
1385                         v3f bone_rot = (*ii).second.Y;
1386                         irr::scene::IBoneSceneNode* bone = m_animated_meshnode->getJointNode(bone_name.c_str());
1387                         if(bone)
1388                         {
1389                                 bone->setPosition(bone_pos);
1390                                 bone->setRotation(bone_rot);
1391                         }
1392                 }
1393         }
1394         
1395         void updateAttachments()
1396         {
1397                 if(getParent() == NULL || getParent()->isLocalPlayer()) // Detach
1398                 {
1399                         if(m_meshnode)
1400                         {
1401                                 v3f old_position = m_meshnode->getAbsolutePosition();
1402                                 v3f old_rotation = m_meshnode->getRotation();
1403                                 m_meshnode->setParent(m_smgr->getRootSceneNode());
1404                                 m_meshnode->setPosition(old_position);
1405                                 m_meshnode->setRotation(old_rotation);
1406                                 m_meshnode->updateAbsolutePosition();
1407                         }
1408                         if(m_animated_meshnode)
1409                         {
1410                                 v3f old_position = m_animated_meshnode->getAbsolutePosition();
1411                                 v3f old_rotation = m_animated_meshnode->getRotation();
1412                                 m_animated_meshnode->setParent(m_smgr->getRootSceneNode());
1413                                 m_animated_meshnode->setPosition(old_position);
1414                                 m_animated_meshnode->setRotation(old_rotation);
1415                                 m_animated_meshnode->updateAbsolutePosition();
1416                         }
1417                         if(m_spritenode)
1418                         {
1419                                 v3f old_position = m_spritenode->getAbsolutePosition();
1420                                 v3f old_rotation = m_spritenode->getRotation();
1421                                 m_spritenode->setParent(m_smgr->getRootSceneNode());
1422                                 m_spritenode->setPosition(old_position);
1423                                 m_spritenode->setRotation(old_rotation);
1424                                 m_spritenode->updateAbsolutePosition();
1425                         }
1426                         if(m_is_local_player)
1427                         {
1428                                 LocalPlayer *player = m_env->getLocalPlayer();
1429                                 player->isAttached = false;
1430                         }
1431                 }
1432                 else // Attach
1433                 {
1434                         scene::IMeshSceneNode *parent_mesh = NULL;
1435                         if(getParent()->getMeshSceneNode())
1436                                 parent_mesh = getParent()->getMeshSceneNode();
1437                         scene::IAnimatedMeshSceneNode *parent_animated_mesh = NULL;
1438                         if(getParent()->getAnimatedMeshSceneNode())
1439                                 parent_animated_mesh = getParent()->getAnimatedMeshSceneNode();
1440                         scene::IBillboardSceneNode *parent_sprite = NULL;
1441                         if(getParent()->getSpriteSceneNode())
1442                                 parent_sprite = getParent()->getSpriteSceneNode();
1443
1444                         scene::IBoneSceneNode *parent_bone = NULL;
1445                         if(parent_animated_mesh && m_attachment_bone != "")
1446                                 parent_bone = parent_animated_mesh->getJointNode(m_attachment_bone.c_str());
1447
1448                         // The spaghetti code below makes sure attaching works if either the parent or child is a spritenode, meshnode, or animatedmeshnode
1449                         // TODO: Perhaps use polymorphism here to save code duplication
1450                         if(m_meshnode){
1451                                 if(parent_bone){
1452                                         m_meshnode->setParent(parent_bone);
1453                                         m_meshnode->setPosition(m_attachment_position);
1454                                         m_meshnode->setRotation(m_attachment_rotation);
1455                                         m_meshnode->updateAbsolutePosition();
1456                                 }
1457                                 else
1458                                 {
1459                                         if(parent_mesh){
1460                                                 m_meshnode->setParent(parent_mesh);
1461                                                 m_meshnode->setPosition(m_attachment_position);
1462                                                 m_meshnode->setRotation(m_attachment_rotation);
1463                                                 m_meshnode->updateAbsolutePosition();
1464                                         }
1465                                         else if(parent_animated_mesh){
1466                                                 m_meshnode->setParent(parent_animated_mesh);
1467                                                 m_meshnode->setPosition(m_attachment_position);
1468                                                 m_meshnode->setRotation(m_attachment_rotation);
1469                                                 m_meshnode->updateAbsolutePosition();
1470                                         }
1471                                         else if(parent_sprite){
1472                                                 m_meshnode->setParent(parent_sprite);
1473                                                 m_meshnode->setPosition(m_attachment_position);
1474                                                 m_meshnode->setRotation(m_attachment_rotation);
1475                                                 m_meshnode->updateAbsolutePosition();
1476                                         }
1477                                 }
1478                         }
1479                         if(m_animated_meshnode){
1480                                 if(parent_bone){
1481                                         m_animated_meshnode->setParent(parent_bone);
1482                                         m_animated_meshnode->setPosition(m_attachment_position);
1483                                         m_animated_meshnode->setRotation(m_attachment_rotation);
1484                                         m_animated_meshnode->updateAbsolutePosition();
1485                                 }
1486                                 else
1487                                 {
1488                                         if(parent_mesh){
1489                                                 m_animated_meshnode->setParent(parent_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_animated_mesh){
1495                                                 m_animated_meshnode->setParent(parent_animated_mesh);
1496                                                 m_animated_meshnode->setPosition(m_attachment_position);
1497                                                 m_animated_meshnode->setRotation(m_attachment_rotation);
1498                                                 m_animated_meshnode->updateAbsolutePosition();
1499                                         }
1500                                         else if(parent_sprite){
1501                                                 m_animated_meshnode->setParent(parent_sprite);
1502                                                 m_animated_meshnode->setPosition(m_attachment_position);
1503                                                 m_animated_meshnode->setRotation(m_attachment_rotation);
1504                                                 m_animated_meshnode->updateAbsolutePosition();
1505                                         }
1506                                 }
1507                         }
1508                         if(m_spritenode){
1509                                 if(parent_bone){
1510                                         m_spritenode->setParent(parent_bone);
1511                                         m_spritenode->setPosition(m_attachment_position);
1512                                         m_spritenode->setRotation(m_attachment_rotation);
1513                                         m_spritenode->updateAbsolutePosition();
1514                                 }
1515                                 else
1516                                 {
1517                                         if(parent_mesh){
1518                                                 m_spritenode->setParent(parent_mesh);
1519                                                 m_spritenode->setPosition(m_attachment_position);
1520                                                 m_spritenode->setRotation(m_attachment_rotation);
1521                                                 m_spritenode->updateAbsolutePosition();
1522                                         }
1523                                         else if(parent_animated_mesh){
1524                                                 m_spritenode->setParent(parent_animated_mesh);
1525                                                 m_spritenode->setPosition(m_attachment_position);
1526                                                 m_spritenode->setRotation(m_attachment_rotation);
1527                                                 m_spritenode->updateAbsolutePosition();
1528                                         }
1529                                         else if(parent_sprite){
1530                                                 m_spritenode->setParent(parent_sprite);
1531                                                 m_spritenode->setPosition(m_attachment_position);
1532                                                 m_spritenode->setRotation(m_attachment_rotation);
1533                                                 m_spritenode->updateAbsolutePosition();
1534                                         }
1535                                 }
1536                         }
1537                         if(m_is_local_player)
1538                         {
1539                                 LocalPlayer *player = m_env->getLocalPlayer();
1540                                 player->isAttached = true;
1541                                 player->overridePosition = m_attachment_position;
1542                         }
1543                 }
1544         }
1545
1546         void processMessage(const std::string &data)
1547         {
1548                 //infostream<<"GenericCAO: Got message"<<std::endl;
1549                 std::istringstream is(data, std::ios::binary);
1550                 // command
1551                 u8 cmd = readU8(is);
1552                 if(cmd == GENERIC_CMD_SET_PROPERTIES)
1553                 {
1554                         m_prop = gob_read_set_properties(is);
1555
1556                         m_selection_box = m_prop.collisionbox;
1557                         m_selection_box.MinEdge *= BS;
1558                         m_selection_box.MaxEdge *= BS;
1559                                 
1560                         m_tx_size.X = 1.0 / m_prop.spritediv.X;
1561                         m_tx_size.Y = 1.0 / m_prop.spritediv.Y;
1562
1563                         if(!m_initial_tx_basepos_set){
1564                                 m_initial_tx_basepos_set = true;
1565                                 m_tx_basepos = m_prop.initial_sprite_basepos;
1566                         }
1567                         
1568                         expireVisuals();
1569                 }
1570                 else if(cmd == GENERIC_CMD_UPDATE_POSITION)
1571                 {
1572                         // Not sent by the server if this object is an attachment.
1573                         // We might however get here if the server notices the object being detached before the client.
1574                         m_position = readV3F1000(is);
1575                         m_velocity = readV3F1000(is);
1576                         m_acceleration = readV3F1000(is);
1577                         if(fabs(m_prop.automatic_rotate) < 0.001)
1578                                 m_yaw = readF1000(is);
1579                         bool do_interpolate = readU8(is);
1580                         bool is_end_position = readU8(is);
1581                         float update_interval = readF1000(is);
1582
1583                         // Place us a bit higher if we're physical, to not sink into
1584                         // the ground due to sucky collision detection...
1585                         if(m_prop.physical)
1586                                 m_position += v3f(0,0.002,0);
1587
1588                         if(getParent() != NULL) // Just in case
1589                                 return;
1590
1591                         if(do_interpolate){
1592                                 if(!m_prop.physical)
1593                                         pos_translator.update(m_position, is_end_position, update_interval);
1594                         } else {
1595                                 pos_translator.init(m_position);
1596                         }
1597                         updateNodePos();
1598                 }
1599                 else if(cmd == GENERIC_CMD_SET_TEXTURE_MOD)
1600                 {
1601                         std::string mod = deSerializeString(is);
1602                         updateTextures(mod);
1603                 }
1604                 else if(cmd == GENERIC_CMD_SET_SPRITE)
1605                 {
1606                         v2s16 p = readV2S16(is);
1607                         int num_frames = readU16(is);
1608                         float framelength = readF1000(is);
1609                         bool select_horiz_by_yawpitch = readU8(is);
1610                         
1611                         m_tx_basepos = p;
1612                         m_anim_num_frames = num_frames;
1613                         m_anim_framelength = framelength;
1614                         m_tx_select_horiz_by_yawpitch = select_horiz_by_yawpitch;
1615
1616                         updateTexturePos();
1617                 }
1618                 else if(cmd == GENERIC_CMD_SET_ANIMATIONS)
1619                 {
1620                         m_frames = readV2F1000(is);
1621                         m_frame_speed = readF1000(is);
1622                         m_frame_blend = readF1000(is);
1623
1624                         updateAnimations();
1625                 }
1626                 else if(cmd == GENERIC_CMD_SET_BONE_POSROT)
1627                 {
1628                         std::string bone = deSerializeString(is);
1629                         v3f position = readV3F1000(is);
1630                         v3f rotation = readV3F1000(is);
1631                         m_bone_posrot[bone] = core::vector2d<v3f>(position, rotation);
1632
1633                         updateBonePosRot();
1634                 }
1635                 else if(cmd == GENERIC_CMD_SET_ATTACHMENT)
1636                 {
1637                         // If an entry already exists for this object, delete it first to avoid duplicates
1638                         for(std::vector<core::vector2d<int> >::iterator ii = attachment_list.begin(); ii != attachment_list.end(); ii++)
1639                         {
1640                                 if(ii->X == this->getId()) // This is the ID of our object
1641                                 {
1642                                         attachment_list.erase(ii);
1643                                         break;
1644                                 }
1645                         }
1646                         attachment_list.push_back(core::vector2d<int>(this->getId(), readS16(is)));
1647                         m_attachment_bone = deSerializeString(is);
1648                         m_attachment_position = readV3F1000(is);
1649                         m_attachment_rotation = readV3F1000(is);
1650
1651                         updateAttachments();
1652                 }
1653                 else if(cmd == GENERIC_CMD_PUNCHED)
1654                 {
1655                         /*s16 damage =*/ readS16(is);
1656                         s16 result_hp = readS16(is);
1657                         
1658                         m_hp = result_hp;
1659                 }
1660                 else if(cmd == GENERIC_CMD_UPDATE_ARMOR_GROUPS)
1661                 {
1662                         m_armor_groups.clear();
1663                         int armor_groups_size = readU16(is);
1664                         for(int i=0; i<armor_groups_size; i++){
1665                                 std::string name = deSerializeString(is);
1666                                 int rating = readS16(is);
1667                                 m_armor_groups[name] = rating;
1668                         }
1669                 }
1670         }
1671         
1672         bool directReportPunch(v3f dir, const ItemStack *punchitem=NULL,
1673                         float time_from_last_punch=1000000)
1674         {
1675                 assert(punchitem);
1676                 const ToolCapabilities *toolcap =
1677                                 &punchitem->getToolCapabilities(m_gamedef->idef());
1678                 PunchDamageResult result = getPunchDamage(
1679                                 m_armor_groups,
1680                                 toolcap,
1681                                 punchitem,
1682                                 time_from_last_punch);
1683
1684                 if(result.did_punch && result.damage != 0)
1685                 {
1686                         if(result.damage < m_hp){
1687                                 m_hp -= result.damage;
1688                         } else {
1689                                 m_hp = 0;
1690                                 // TODO: Execute defined fast response
1691                                 // As there is no definition, make a smoke puff
1692                                 ClientSimpleObject *simple = createSmokePuff(
1693                                                 m_smgr, m_env, m_position,
1694                                                 m_prop.visual_size * BS);
1695                                 m_env->addSimpleObject(simple);
1696                         }
1697                         // TODO: Execute defined fast response
1698                         // Flashing shall suffice as there is no definition
1699                         m_reset_textures_timer = 0.05;
1700                         if(result.damage >= 2)
1701                                 m_reset_textures_timer += 0.05 * result.damage;
1702                         updateTextures("^[brighten");
1703                 }
1704                 
1705                 return false;
1706         }
1707         
1708         std::string debugInfoText()
1709         {
1710                 std::ostringstream os(std::ios::binary);
1711                 os<<"GenericCAO hp="<<m_hp<<"\n";
1712                 os<<"armor={";
1713                 for(ItemGroupList::const_iterator i = m_armor_groups.begin();
1714                                 i != m_armor_groups.end(); i++){
1715                         os<<i->first<<"="<<i->second<<", ";
1716                 }
1717                 os<<"}";
1718                 return os.str();
1719         }
1720 };
1721
1722 // Prototype
1723 GenericCAO proto_GenericCAO(NULL, NULL);
1724
1725