Show infotext for unknown items placed on ground
[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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
14
15 You should have received a copy of the GNU 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 "settings.h"
24 #include <ICameraSceneNode.h>
25 #include <ITextSceneNode.h>
26 #include "serialization.h" // For decompressZlib
27 #include "gamedef.h"
28 #include "clientobject.h"
29 #include "content_object.h"
30 #include "mesh.h"
31 #include "utility.h" // For IntervalLimiter
32 class Settings;
33 #include "MyBillboardSceneNode.h"
34
35 core::map<u16, ClientActiveObject::Factory> ClientActiveObject::m_types;
36
37 /*
38         SmoothTranslator
39 */
40
41 struct SmoothTranslator
42 {
43         v3f vect_old;
44         v3f vect_show;
45         v3f vect_aim;
46         f32 anim_counter;
47         f32 anim_time;
48         f32 anim_time_counter;
49         bool aim_is_end;
50
51         SmoothTranslator():
52                 vect_old(0,0,0),
53                 vect_show(0,0,0),
54                 vect_aim(0,0,0),
55                 anim_counter(0),
56                 anim_time(0),
57                 anim_time_counter(0),
58                 aim_is_end(true)
59         {}
60
61         void init(v3f vect)
62         {
63                 vect_old = vect;
64                 vect_show = vect;
65                 vect_aim = vect;
66                 anim_counter = 0;
67                 anim_time = 0;
68                 anim_time_counter = 0;
69                 aim_is_end = true;
70         }
71
72         void sharpen()
73         {
74                 init(vect_show);
75         }
76
77         void update(v3f vect_new, bool is_end_position=false, float update_interval=-1)
78         {
79                 aim_is_end = is_end_position;
80                 vect_old = vect_show;
81                 vect_aim = vect_new;
82                 if(update_interval > 0){
83                         anim_time = update_interval;
84                 } else {
85                         if(anim_time < 0.001 || anim_time > 1.0)
86                                 anim_time = anim_time_counter;
87                         else
88                                 anim_time = anim_time * 0.9 + anim_time_counter * 0.1;
89                 }
90                 anim_time_counter = 0;
91                 anim_counter = 0;
92         }
93
94         void translate(f32 dtime)
95         {
96                 anim_time_counter = anim_time_counter + dtime;
97                 anim_counter = anim_counter + dtime;
98                 v3f vect_move = vect_aim - vect_old;
99                 f32 moveratio = 1.0;
100                 if(anim_time > 0.001)
101                         moveratio = anim_time_counter / anim_time;
102                 // Move a bit less than should, to avoid oscillation
103                 moveratio = moveratio * 0.8;
104                 float move_end = 1.5;
105                 if(aim_is_end)
106                         move_end = 1.0;
107                 if(moveratio > move_end)
108                         moveratio = move_end;
109                 vect_show = vect_old + vect_move * moveratio;
110         }
111
112         bool is_moving()
113         {
114                 return ((anim_time_counter / anim_time) < 1.4);
115         }
116 };
117
118
119 /*
120         TestCAO
121 */
122
123 class TestCAO : public ClientActiveObject
124 {
125 public:
126         TestCAO(IGameDef *gamedef, ClientEnvironment *env);
127         virtual ~TestCAO();
128         
129         u8 getType() const
130         {
131                 return ACTIVEOBJECT_TYPE_TEST;
132         }
133         
134         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env);
135
136         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
137                         IrrlichtDevice *irr);
138         void removeFromScene();
139         void updateLight(u8 light_at_pos);
140         v3s16 getLightPosition();
141         void updateNodePos();
142
143         void step(float dtime, ClientEnvironment *env);
144
145         void processMessage(const std::string &data);
146
147 private:
148         scene::IMeshSceneNode *m_node;
149         v3f m_position;
150 };
151
152 /*
153         ItemCAO
154 */
155
156 class ItemCAO : public ClientActiveObject
157 {
158 public:
159         ItemCAO(IGameDef *gamedef, ClientEnvironment *env);
160         virtual ~ItemCAO();
161         
162         u8 getType() const
163         {
164                 return ACTIVEOBJECT_TYPE_ITEM;
165         }
166         
167         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env);
168
169         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
170                         IrrlichtDevice *irr);
171         void removeFromScene();
172         void updateLight(u8 light_at_pos);
173         v3s16 getLightPosition();
174         void updateNodePos();
175
176         void step(float dtime, ClientEnvironment *env);
177
178         void processMessage(const std::string &data);
179
180         void initialize(const std::string &data);
181         
182         core::aabbox3d<f32>* getSelectionBox()
183                 {return &m_selection_box;}
184         v3f getPosition()
185                 {return m_position;}
186         
187         std::string infoText()
188                 {return m_infotext;}
189
190 private:
191         core::aabbox3d<f32> m_selection_box;
192         scene::IMeshSceneNode *m_node;
193         v3f m_position;
194         std::string m_inventorystring;
195         std::string m_infotext;
196 };
197
198 /*
199         RatCAO
200 */
201
202 class RatCAO : public ClientActiveObject
203 {
204 public:
205         RatCAO(IGameDef *gamedef, ClientEnvironment *env);
206         virtual ~RatCAO();
207         
208         u8 getType() const
209         {
210                 return ACTIVEOBJECT_TYPE_RAT;
211         }
212         
213         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env);
214
215         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
216                         IrrlichtDevice *irr);
217         void removeFromScene();
218         void updateLight(u8 light_at_pos);
219         v3s16 getLightPosition();
220         void updateNodePos();
221
222         void step(float dtime, ClientEnvironment *env);
223
224         void processMessage(const std::string &data);
225
226         void initialize(const std::string &data);
227         
228         core::aabbox3d<f32>* getSelectionBox()
229                 {return &m_selection_box;}
230         v3f getPosition()
231                 {return pos_translator.vect_show;}
232                 //{return m_position;}
233
234 private:
235         core::aabbox3d<f32> m_selection_box;
236         scene::IMeshSceneNode *m_node;
237         v3f m_position;
238         float m_yaw;
239         SmoothTranslator pos_translator;
240 };
241
242 /*
243         Oerkki1CAO
244 */
245
246 class Oerkki1CAO : public ClientActiveObject
247 {
248 public:
249         Oerkki1CAO(IGameDef *gamedef, ClientEnvironment *env);
250         virtual ~Oerkki1CAO();
251         
252         u8 getType() const
253         {
254                 return ACTIVEOBJECT_TYPE_OERKKI1;
255         }
256         
257         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env);
258
259         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
260                         IrrlichtDevice *irr);
261         void removeFromScene();
262         void updateLight(u8 light_at_pos);
263         v3s16 getLightPosition();
264         void updateNodePos();
265
266         void step(float dtime, ClientEnvironment *env);
267
268         void processMessage(const std::string &data);
269
270         void initialize(const std::string &data);
271         
272         core::aabbox3d<f32>* getSelectionBox()
273                 {return &m_selection_box;}
274         v3f getPosition()
275                 {return pos_translator.vect_show;}
276                 //{return m_position;}
277
278         // If returns true, punch will not be sent to the server
279         bool directReportPunch(const std::string &toolname, v3f dir);
280
281 private:
282         IntervalLimiter m_attack_interval;
283         core::aabbox3d<f32> m_selection_box;
284         scene::IMeshSceneNode *m_node;
285         v3f m_position;
286         float m_yaw;
287         SmoothTranslator pos_translator;
288         float m_damage_visual_timer;
289         bool m_damage_texture_enabled;
290 };
291
292 /*
293         FireflyCAO
294 */
295
296 class FireflyCAO : public ClientActiveObject
297 {
298 public:
299         FireflyCAO(IGameDef *gamedef, ClientEnvironment *env);
300         virtual ~FireflyCAO();
301         
302         u8 getType() const
303         {
304                 return ACTIVEOBJECT_TYPE_FIREFLY;
305         }
306         
307         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env);
308
309         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
310                         IrrlichtDevice *irr);
311         void removeFromScene();
312         void updateLight(u8 light_at_pos);
313         v3s16 getLightPosition();
314         void updateNodePos();
315
316         void step(float dtime, ClientEnvironment *env);
317
318         void processMessage(const std::string &data);
319
320         void initialize(const std::string &data);
321         
322         core::aabbox3d<f32>* getSelectionBox()
323                 {return &m_selection_box;}
324         v3f getPosition()
325                 {return m_position;}
326
327 private:
328         core::aabbox3d<f32> m_selection_box;
329         scene::IMeshSceneNode *m_node;
330         v3f m_position;
331         float m_yaw;
332         SmoothTranslator pos_translator;
333 };
334
335 /*
336         MobV2CAO
337 */
338
339 class MobV2CAO : public ClientActiveObject
340 {
341 public:
342         MobV2CAO(IGameDef *gamedef, ClientEnvironment *env);
343         virtual ~MobV2CAO();
344         
345         u8 getType() const
346         {
347                 return ACTIVEOBJECT_TYPE_MOBV2;
348         }
349         
350         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env);
351
352         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
353                         IrrlichtDevice *irr);
354         void removeFromScene();
355         void updateLight(u8 light_at_pos);
356         v3s16 getLightPosition();
357         void updateNodePos();
358
359         void step(float dtime, ClientEnvironment *env);
360
361         void processMessage(const std::string &data);
362
363         void initialize(const std::string &data);
364         
365         core::aabbox3d<f32>* getSelectionBox()
366                 {return &m_selection_box;}
367         v3f getPosition()
368                 {return pos_translator.vect_show;}
369                 //{return m_position;}
370         bool doShowSelectionBox(){return false;}
371
372         // If returns true, punch will not be sent to the server
373         bool directReportPunch(const std::string &toolname, v3f dir);
374
375 private:
376         void setLooks(const std::string &looks);
377         
378         IntervalLimiter m_attack_interval;
379         core::aabbox3d<f32> m_selection_box;
380         scene::MyBillboardSceneNode *m_node;
381         v3f m_position;
382         std::string m_texture_name;
383         float m_yaw;
384         SmoothTranslator pos_translator;
385         bool m_walking;
386         float m_walking_unset_timer;
387         float m_walk_timer;
388         int m_walk_frame;
389         float m_damage_visual_timer;
390         u8 m_last_light;
391         bool m_shooting;
392         float m_shooting_unset_timer;
393         v2f m_sprite_size;
394         float m_sprite_y;
395         bool m_bright_shooting;
396         std::string m_sprite_type;
397         int m_simple_anim_frames;
398         float m_simple_anim_frametime;
399         bool m_lock_full_brightness;
400         int m_player_hit_damage;
401         float m_player_hit_distance;
402         float m_player_hit_interval;
403         float m_player_hit_timer;
404
405         Settings *m_properties;
406 };
407
408 /*
409         TestCAO
410 */
411
412 // Prototype
413 TestCAO proto_TestCAO(NULL, NULL);
414
415 TestCAO::TestCAO(IGameDef *gamedef, ClientEnvironment *env):
416         ClientActiveObject(0, gamedef, env),
417         m_node(NULL),
418         m_position(v3f(0,10*BS,0))
419 {
420         ClientActiveObject::registerType(getType(), create);
421 }
422
423 TestCAO::~TestCAO()
424 {
425 }
426
427 ClientActiveObject* TestCAO::create(IGameDef *gamedef, ClientEnvironment *env)
428 {
429         return new TestCAO(gamedef, env);
430 }
431
432 void TestCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
433                         IrrlichtDevice *irr)
434 {
435         if(m_node != NULL)
436                 return;
437         
438         //video::IVideoDriver* driver = smgr->getVideoDriver();
439         
440         scene::SMesh *mesh = new scene::SMesh();
441         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
442         video::SColor c(255,255,255,255);
443         video::S3DVertex vertices[4] =
444         {
445                 video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1),
446                 video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1),
447                 video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0),
448                 video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),
449         };
450         u16 indices[] = {0,1,2,2,3,0};
451         buf->append(vertices, 4, indices, 6);
452         // Set material
453         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
454         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
455         buf->getMaterial().setTexture(0, tsrc->getTextureRaw("rat.png"));
456         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
457         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
458         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
459         // Add to mesh
460         mesh->addMeshBuffer(buf);
461         buf->drop();
462         m_node = smgr->addMeshSceneNode(mesh, NULL);
463         mesh->drop();
464         updateNodePos();
465 }
466
467 void TestCAO::removeFromScene()
468 {
469         if(m_node == NULL)
470                 return;
471
472         m_node->remove();
473         m_node = NULL;
474 }
475
476 void TestCAO::updateLight(u8 light_at_pos)
477 {
478 }
479
480 v3s16 TestCAO::getLightPosition()
481 {
482         return floatToInt(m_position, BS);
483 }
484
485 void TestCAO::updateNodePos()
486 {
487         if(m_node == NULL)
488                 return;
489
490         m_node->setPosition(m_position);
491         //m_node->setRotation(v3f(0, 45, 0));
492 }
493
494 void TestCAO::step(float dtime, ClientEnvironment *env)
495 {
496         if(m_node)
497         {
498                 v3f rot = m_node->getRotation();
499                 //infostream<<"dtime="<<dtime<<", rot.Y="<<rot.Y<<std::endl;
500                 rot.Y += dtime * 180;
501                 m_node->setRotation(rot);
502         }
503 }
504
505 void TestCAO::processMessage(const std::string &data)
506 {
507         infostream<<"TestCAO: Got data: "<<data<<std::endl;
508         std::istringstream is(data, std::ios::binary);
509         u16 cmd;
510         is>>cmd;
511         if(cmd == 0)
512         {
513                 v3f newpos;
514                 is>>newpos.X;
515                 is>>newpos.Y;
516                 is>>newpos.Z;
517                 m_position = newpos;
518                 updateNodePos();
519         }
520 }
521
522 /*
523         ItemCAO
524 */
525
526 #include "inventory.h"
527
528 // Prototype
529 ItemCAO proto_ItemCAO(NULL, NULL);
530
531 ItemCAO::ItemCAO(IGameDef *gamedef, ClientEnvironment *env):
532         ClientActiveObject(0, gamedef, env),
533         m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.),
534         m_node(NULL),
535         m_position(v3f(0,10*BS,0))
536 {
537         if(!gamedef && !env)
538         {
539                 ClientActiveObject::registerType(getType(), create);
540         }
541 }
542
543 ItemCAO::~ItemCAO()
544 {
545 }
546
547 ClientActiveObject* ItemCAO::create(IGameDef *gamedef, ClientEnvironment *env)
548 {
549         return new ItemCAO(gamedef, env);
550 }
551
552 void ItemCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
553                         IrrlichtDevice *irr)
554 {
555         if(m_node != NULL)
556                 return;
557         
558         //video::IVideoDriver* driver = smgr->getVideoDriver();
559         
560         scene::SMesh *mesh = new scene::SMesh();
561         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
562         video::SColor c(255,255,255,255);
563         video::S3DVertex vertices[4] =
564         {
565                 /*video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1),
566                 video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1),
567                 video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0),
568                 video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),*/
569                 video::S3DVertex(BS/3.,0,0, 0,0,0, c, 0,1),
570                 video::S3DVertex(-BS/3.,0,0, 0,0,0, c, 1,1),
571                 video::S3DVertex(-BS/3.,0+BS*2./3.,0, 0,0,0, c, 1,0),
572                 video::S3DVertex(BS/3.,0+BS*2./3.,0, 0,0,0, c, 0,0),
573         };
574         u16 indices[] = {0,1,2,2,3,0};
575         buf->append(vertices, 4, indices, 6);
576         // Set material
577         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
578         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
579         //buf->getMaterial().setTexture(0, NULL);
580         // Initialize with the stick texture
581         buf->getMaterial().setTexture(0, tsrc->getTextureRaw("stick.png"));
582         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
583         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
584         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
585         // Add to mesh
586         mesh->addMeshBuffer(buf);
587         buf->drop();
588         m_node = smgr->addMeshSceneNode(mesh, NULL);
589         mesh->drop();
590         // Set it to use the materials of the meshbuffers directly.
591         // This is needed for changing the texture in the future
592         m_node->setReadOnlyMaterials(true);
593         updateNodePos();
594
595         /*
596                 Update image of node
597         */
598
599         // Create an inventory item to see what is its image
600         std::istringstream is(m_inventorystring, std::ios_base::binary);
601         video::ITexture *texture = NULL;
602         try{
603                 InventoryItem *item = NULL;
604                 item = InventoryItem::deSerialize(is, m_gamedef);
605                 infostream<<__FUNCTION_NAME<<": m_inventorystring=\""
606                                 <<m_inventorystring<<"\" -> item="<<item
607                                 <<std::endl;
608                 if(item)
609                 {
610                         texture = item->getImage();
611                         delete item;
612                 }
613         }
614         catch(SerializationError &e)
615         {
616                 infostream<<"WARNING: "<<__FUNCTION_NAME
617                                 <<": error deSerializing inventorystring \""
618                                 <<m_inventorystring<<"\""<<std::endl;
619         }
620         
621         // Set meshbuffer texture
622         buf->getMaterial().setTexture(0, texture);
623 }
624
625 void ItemCAO::removeFromScene()
626 {
627         if(m_node == NULL)
628                 return;
629
630         m_node->remove();
631         m_node = NULL;
632 }
633
634 void ItemCAO::updateLight(u8 light_at_pos)
635 {
636         if(m_node == NULL)
637                 return;
638
639         u8 li = decode_light(light_at_pos);
640         video::SColor color(255,li,li,li);
641         setMeshColor(m_node->getMesh(), color);
642 }
643
644 v3s16 ItemCAO::getLightPosition()
645 {
646         return floatToInt(m_position, BS);
647 }
648
649 void ItemCAO::updateNodePos()
650 {
651         if(m_node == NULL)
652                 return;
653
654         m_node->setPosition(m_position);
655 }
656
657 void ItemCAO::step(float dtime, ClientEnvironment *env)
658 {
659         if(m_node)
660         {
661                 /*v3f rot = m_node->getRotation();
662                 rot.Y += dtime * 120;
663                 m_node->setRotation(rot);*/
664                 LocalPlayer *player = env->getLocalPlayer();
665                 assert(player);
666                 v3f rot = m_node->getRotation();
667                 rot.Y = 180.0 - (player->getYaw());
668                 m_node->setRotation(rot);
669         }
670 }
671
672 void ItemCAO::processMessage(const std::string &data)
673 {
674         //infostream<<"ItemCAO: Got message"<<std::endl;
675         std::istringstream is(data, std::ios::binary);
676         // command
677         u8 cmd = readU8(is);
678         if(cmd == 0)
679         {
680                 // pos
681                 m_position = readV3F1000(is);
682                 updateNodePos();
683         }
684 }
685
686 void ItemCAO::initialize(const std::string &data)
687 {
688         infostream<<"ItemCAO: Got init data"<<std::endl;
689         
690         {
691                 std::istringstream is(data, std::ios::binary);
692                 // version
693                 u8 version = readU8(is);
694                 // check version
695                 if(version != 0)
696                         return;
697                 // pos
698                 m_position = readV3F1000(is);
699                 // inventorystring
700                 m_inventorystring = deSerializeString(is);
701         }
702         
703         updateNodePos();
704         
705         /*
706                 Set infotext to item name if item cannot be deserialized
707         */
708         try{
709                 InventoryItem *item = NULL;
710                 item = InventoryItem::deSerialize(m_inventorystring, m_gamedef);
711                 if(item){
712                         if(!item->isKnown())
713                                 m_infotext = "Unknown item: '" + m_inventorystring + "'";
714                 }
715                 delete item;
716         }
717         catch(SerializationError &e)
718         {
719                 m_infotext = "Unknown item: '" + m_inventorystring + "'";
720         }
721 }
722
723 /*
724         RatCAO
725 */
726
727 #include "inventory.h"
728
729 // Prototype
730 RatCAO proto_RatCAO(NULL, NULL);
731
732 RatCAO::RatCAO(IGameDef *gamedef, ClientEnvironment *env):
733         ClientActiveObject(0, gamedef, env),
734         m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS/2.,BS/3.),
735         m_node(NULL),
736         m_position(v3f(0,10*BS,0)),
737         m_yaw(0)
738 {
739         ClientActiveObject::registerType(getType(), create);
740 }
741
742 RatCAO::~RatCAO()
743 {
744 }
745
746 ClientActiveObject* RatCAO::create(IGameDef *gamedef, ClientEnvironment *env)
747 {
748         return new RatCAO(gamedef, env);
749 }
750
751 void RatCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
752                         IrrlichtDevice *irr)
753 {
754         if(m_node != NULL)
755                 return;
756         
757         //video::IVideoDriver* driver = smgr->getVideoDriver();
758         
759         scene::SMesh *mesh = new scene::SMesh();
760         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
761         video::SColor c(255,255,255,255);
762         video::S3DVertex vertices[4] =
763         {
764                 video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
765                 video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
766                 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
767                 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
768         };
769         u16 indices[] = {0,1,2,2,3,0};
770         buf->append(vertices, 4, indices, 6);
771         // Set material
772         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
773         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
774         //buf->getMaterial().setTexture(0, NULL);
775         buf->getMaterial().setTexture(0, tsrc->getTextureRaw("rat.png"));
776         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
777         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
778         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
779         // Add to mesh
780         mesh->addMeshBuffer(buf);
781         buf->drop();
782         m_node = smgr->addMeshSceneNode(mesh, NULL);
783         mesh->drop();
784         // Set it to use the materials of the meshbuffers directly.
785         // This is needed for changing the texture in the future
786         m_node->setReadOnlyMaterials(true);
787         updateNodePos();
788 }
789
790 void RatCAO::removeFromScene()
791 {
792         if(m_node == NULL)
793                 return;
794
795         m_node->remove();
796         m_node = NULL;
797 }
798
799 void RatCAO::updateLight(u8 light_at_pos)
800 {
801         if(m_node == NULL)
802                 return;
803
804         u8 li = decode_light(light_at_pos);
805         video::SColor color(255,li,li,li);
806         setMeshColor(m_node->getMesh(), color);
807 }
808
809 v3s16 RatCAO::getLightPosition()
810 {
811         return floatToInt(m_position+v3f(0,BS*0.5,0), BS);
812 }
813
814 void RatCAO::updateNodePos()
815 {
816         if(m_node == NULL)
817                 return;
818
819         //m_node->setPosition(m_position);
820         m_node->setPosition(pos_translator.vect_show);
821
822         v3f rot = m_node->getRotation();
823         rot.Y = 180.0 - m_yaw;
824         m_node->setRotation(rot);
825 }
826
827 void RatCAO::step(float dtime, ClientEnvironment *env)
828 {
829         pos_translator.translate(dtime);
830         updateNodePos();
831 }
832
833 void RatCAO::processMessage(const std::string &data)
834 {
835         //infostream<<"RatCAO: Got message"<<std::endl;
836         std::istringstream is(data, std::ios::binary);
837         // command
838         u8 cmd = readU8(is);
839         if(cmd == 0)
840         {
841                 // pos
842                 m_position = readV3F1000(is);
843                 pos_translator.update(m_position);
844                 // yaw
845                 m_yaw = readF1000(is);
846                 updateNodePos();
847         }
848 }
849
850 void RatCAO::initialize(const std::string &data)
851 {
852         //infostream<<"RatCAO: Got init data"<<std::endl;
853         
854         {
855                 std::istringstream is(data, std::ios::binary);
856                 // version
857                 u8 version = readU8(is);
858                 // check version
859                 if(version != 0)
860                         return;
861                 // pos
862                 m_position = readV3F1000(is);
863                 pos_translator.init(m_position);
864         }
865         
866         updateNodePos();
867 }
868
869 /*
870         Oerkki1CAO
871 */
872
873 #include "inventory.h"
874
875 // Prototype
876 Oerkki1CAO proto_Oerkki1CAO(NULL, NULL);
877
878 Oerkki1CAO::Oerkki1CAO(IGameDef *gamedef, ClientEnvironment *env):
879         ClientActiveObject(0, gamedef, env),
880         m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2.,BS/3.),
881         m_node(NULL),
882         m_position(v3f(0,10*BS,0)),
883         m_yaw(0),
884         m_damage_visual_timer(0),
885         m_damage_texture_enabled(false)
886 {
887         ClientActiveObject::registerType(getType(), create);
888 }
889
890 Oerkki1CAO::~Oerkki1CAO()
891 {
892 }
893
894 ClientActiveObject* Oerkki1CAO::create(IGameDef *gamedef, ClientEnvironment *env)
895 {
896         return new Oerkki1CAO(gamedef, env);
897 }
898
899 void Oerkki1CAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
900                         IrrlichtDevice *irr)
901 {
902         if(m_node != NULL)
903                 return;
904         
905         //video::IVideoDriver* driver = smgr->getVideoDriver();
906         
907         scene::SMesh *mesh = new scene::SMesh();
908         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
909         video::SColor c(255,255,255,255);
910         video::S3DVertex vertices[4] =
911         {
912                 video::S3DVertex(-BS/2-BS,0,0, 0,0,0, c, 0,1),
913                 video::S3DVertex(BS/2+BS,0,0, 0,0,0, c, 1,1),
914                 video::S3DVertex(BS/2+BS,BS*2,0, 0,0,0, c, 1,0),
915                 video::S3DVertex(-BS/2-BS,BS*2,0, 0,0,0, c, 0,0),
916         };
917         u16 indices[] = {0,1,2,2,3,0};
918         buf->append(vertices, 4, indices, 6);
919         // Set material
920         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
921         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
922         //buf->getMaterial().setTexture(0, NULL);
923         buf->getMaterial().setTexture(0, tsrc->getTextureRaw("oerkki1.png"));
924         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
925         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
926         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
927         // Add to mesh
928         mesh->addMeshBuffer(buf);
929         buf->drop();
930         m_node = smgr->addMeshSceneNode(mesh, NULL);
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_node->setReadOnlyMaterials(true);
935         updateNodePos();
936 }
937
938 void Oerkki1CAO::removeFromScene()
939 {
940         if(m_node == NULL)
941                 return;
942
943         m_node->remove();
944         m_node = NULL;
945 }
946
947 void Oerkki1CAO::updateLight(u8 light_at_pos)
948 {
949         if(m_node == NULL)
950                 return;
951         
952         if(light_at_pos <= 2)
953         {
954                 m_node->setVisible(false);
955                 return;
956         }
957
958         m_node->setVisible(true);
959
960         u8 li = decode_light(light_at_pos);
961         video::SColor color(255,li,li,li);
962         setMeshColor(m_node->getMesh(), color);
963 }
964
965 v3s16 Oerkki1CAO::getLightPosition()
966 {
967         return floatToInt(m_position+v3f(0,BS*1.5,0), BS);
968 }
969
970 void Oerkki1CAO::updateNodePos()
971 {
972         if(m_node == NULL)
973                 return;
974
975         //m_node->setPosition(m_position);
976         m_node->setPosition(pos_translator.vect_show);
977
978         v3f rot = m_node->getRotation();
979         rot.Y = 180.0 - m_yaw + 90.0;
980         m_node->setRotation(rot);
981 }
982
983 void Oerkki1CAO::step(float dtime, ClientEnvironment *env)
984 {
985         ITextureSource *tsrc = m_gamedef->tsrc();
986
987         pos_translator.translate(dtime);
988         updateNodePos();
989
990         LocalPlayer *player = env->getLocalPlayer();
991         assert(player);
992         
993         v3f playerpos = player->getPosition();
994         v2f playerpos_2d(playerpos.X,playerpos.Z);
995         v2f objectpos_2d(m_position.X,m_position.Z);
996
997         if(fabs(m_position.Y - playerpos.Y) < 1.5*BS &&
998                         objectpos_2d.getDistanceFrom(playerpos_2d) < 1.5*BS)
999         {
1000                 if(m_attack_interval.step(dtime, 0.5))
1001                 {
1002                         env->damageLocalPlayer(2);
1003                 }
1004         }
1005
1006         if(m_damage_visual_timer > 0)
1007         {
1008                 if(!m_damage_texture_enabled)
1009                 {
1010                         // Enable damage texture
1011                         if(m_node)
1012                         {
1013                                 /*video::IVideoDriver* driver =
1014                                         m_node->getSceneManager()->getVideoDriver();*/
1015                                 
1016                                 scene::IMesh *mesh = m_node->getMesh();
1017                                 if(mesh == NULL)
1018                                         return;
1019                                 
1020                                 u16 mc = mesh->getMeshBufferCount();
1021                                 for(u16 j=0; j<mc; j++)
1022                                 {
1023                                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1024                                         buf->getMaterial().setTexture(0,
1025                                                         tsrc->getTextureRaw("oerkki1_damaged.png"));
1026                                 }
1027                         }
1028                         m_damage_texture_enabled = true;
1029                 }
1030                 m_damage_visual_timer -= dtime;
1031         }
1032         else
1033         {
1034                 if(m_damage_texture_enabled)
1035                 {
1036                         // Disable damage texture
1037                         if(m_node)
1038                         {
1039                                 /*video::IVideoDriver* driver =
1040                                         m_node->getSceneManager()->getVideoDriver();*/
1041                                 
1042                                 scene::IMesh *mesh = m_node->getMesh();
1043                                 if(mesh == NULL)
1044                                         return;
1045                                 
1046                                 u16 mc = mesh->getMeshBufferCount();
1047                                 for(u16 j=0; j<mc; j++)
1048                                 {
1049                                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1050                                         buf->getMaterial().setTexture(0,
1051                                                         tsrc->getTextureRaw("oerkki1.png"));
1052                                 }
1053                         }
1054                         m_damage_texture_enabled = false;
1055                 }
1056         }
1057 }
1058
1059 void Oerkki1CAO::processMessage(const std::string &data)
1060 {
1061         //infostream<<"Oerkki1CAO: Got message"<<std::endl;
1062         std::istringstream is(data, std::ios::binary);
1063         // command
1064         u8 cmd = readU8(is);
1065         if(cmd == 0)
1066         {
1067                 // pos
1068                 m_position = readV3F1000(is);
1069                 pos_translator.update(m_position);
1070                 // yaw
1071                 m_yaw = readF1000(is);
1072                 updateNodePos();
1073         }
1074         else if(cmd == 1)
1075         {
1076                 //u16 damage = readU8(is);
1077                 m_damage_visual_timer = 1.0;
1078         }
1079 }
1080
1081 void Oerkki1CAO::initialize(const std::string &data)
1082 {
1083         //infostream<<"Oerkki1CAO: Got init data"<<std::endl;
1084         
1085         {
1086                 std::istringstream is(data, std::ios::binary);
1087                 // version
1088                 u8 version = readU8(is);
1089                 // check version
1090                 if(version != 0)
1091                         return;
1092                 // pos
1093                 m_position = readV3F1000(is);
1094                 pos_translator.init(m_position);
1095         }
1096         
1097         updateNodePos();
1098 }
1099
1100 bool Oerkki1CAO::directReportPunch(const std::string &toolname, v3f dir)
1101 {
1102         m_damage_visual_timer = 1.0;
1103
1104         m_position += dir * BS;
1105         pos_translator.sharpen();
1106         pos_translator.update(m_position);
1107         updateNodePos();
1108         
1109         return false;
1110 }
1111
1112 /*
1113         FireflyCAO
1114 */
1115
1116 // Prototype
1117 FireflyCAO proto_FireflyCAO(NULL, NULL);
1118
1119 FireflyCAO::FireflyCAO(IGameDef *gamedef, ClientEnvironment *env):
1120         ClientActiveObject(0, gamedef, env),
1121         m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS/2.,BS/3.),
1122         m_node(NULL),
1123         m_position(v3f(0,10*BS,0)),
1124         m_yaw(0)
1125 {
1126         ClientActiveObject::registerType(getType(), create);
1127 }
1128
1129 FireflyCAO::~FireflyCAO()
1130 {
1131 }
1132
1133 ClientActiveObject* FireflyCAO::create(IGameDef *gamedef, ClientEnvironment *env)
1134 {
1135         return new FireflyCAO(gamedef, env);
1136 }
1137
1138 void FireflyCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
1139                         IrrlichtDevice *irr)
1140 {
1141         if(m_node != NULL)
1142                 return;
1143         
1144         //video::IVideoDriver* driver = smgr->getVideoDriver();
1145         
1146         scene::SMesh *mesh = new scene::SMesh();
1147         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
1148         video::SColor c(255,255,255,255);
1149         video::S3DVertex vertices[4] =
1150         {
1151                 video::S3DVertex(0,0,0, 0,0,0, c, 0,1),
1152                 video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
1153                 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
1154                 video::S3DVertex(0,BS/2,0, 0,0,0, c, 0,0),
1155         };
1156         u16 indices[] = {0,1,2,2,3,0};
1157         buf->append(vertices, 4, indices, 6);
1158         // Set material
1159         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
1160         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
1161         //buf->getMaterial().setTexture(0, NULL);
1162         buf->getMaterial().setTexture(0, tsrc->getTextureRaw("firefly.png"));
1163         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
1164         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
1165         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
1166         // Add to mesh
1167         mesh->addMeshBuffer(buf);
1168         buf->drop();
1169         m_node = smgr->addMeshSceneNode(mesh, NULL);
1170         mesh->drop();
1171         // Set it to use the materials of the meshbuffers directly.
1172         // This is needed for changing the texture in the future
1173         m_node->setReadOnlyMaterials(true);
1174         updateNodePos();
1175 }
1176
1177 void FireflyCAO::removeFromScene()
1178 {
1179         if(m_node == NULL)
1180                 return;
1181
1182         m_node->remove();
1183         m_node = NULL;
1184 }
1185
1186 void FireflyCAO::updateLight(u8 light_at_pos)
1187 {
1188         if(m_node == NULL)
1189                 return;
1190
1191         u8 li = 255;
1192         video::SColor color(255,li,li,li);
1193         setMeshColor(m_node->getMesh(), color);
1194 }
1195
1196 v3s16 FireflyCAO::getLightPosition()
1197 {
1198         return floatToInt(m_position+v3f(0,BS*0.5,0), BS);
1199 }
1200
1201 void FireflyCAO::updateNodePos()
1202 {
1203         if(m_node == NULL)
1204                 return;
1205
1206         //m_node->setPosition(m_position);
1207         m_node->setPosition(pos_translator.vect_show);
1208
1209         v3f rot = m_node->getRotation();
1210         rot.Y = 180.0 - m_yaw;
1211         m_node->setRotation(rot);
1212 }
1213
1214 void FireflyCAO::step(float dtime, ClientEnvironment *env)
1215 {
1216         pos_translator.translate(dtime);
1217         updateNodePos();
1218 }
1219
1220 void FireflyCAO::processMessage(const std::string &data)
1221 {
1222         //infostream<<"FireflyCAO: Got message"<<std::endl;
1223         std::istringstream is(data, std::ios::binary);
1224         // command
1225         u8 cmd = readU8(is);
1226         if(cmd == 0)
1227         {
1228                 // pos
1229                 m_position = readV3F1000(is);
1230                 pos_translator.update(m_position);
1231                 // yaw
1232                 m_yaw = readF1000(is);
1233                 updateNodePos();
1234         }
1235 }
1236
1237 void FireflyCAO::initialize(const std::string &data)
1238 {
1239         //infostream<<"FireflyCAO: Got init data"<<std::endl;
1240         
1241         {
1242                 std::istringstream is(data, std::ios::binary);
1243                 // version
1244                 u8 version = readU8(is);
1245                 // check version
1246                 if(version != 0)
1247                         return;
1248                 // pos
1249                 m_position = readV3F1000(is);
1250                 pos_translator.init(m_position);
1251         }
1252         
1253         updateNodePos();
1254 }
1255
1256 /*
1257         MobV2CAO
1258 */
1259
1260 // Prototype
1261 MobV2CAO proto_MobV2CAO(NULL, NULL);
1262
1263 MobV2CAO::MobV2CAO(IGameDef *gamedef, ClientEnvironment *env):
1264         ClientActiveObject(0, gamedef, env),
1265         m_selection_box(-0.4*BS,-0.4*BS,-0.4*BS, 0.4*BS,0.8*BS,0.4*BS),
1266         m_node(NULL),
1267         m_position(v3f(0,10*BS,0)),
1268         m_yaw(0),
1269         m_walking(false),
1270         m_walking_unset_timer(0),
1271         m_walk_timer(0),
1272         m_walk_frame(0),
1273         m_damage_visual_timer(0),
1274         m_last_light(0),
1275         m_shooting(0),
1276         m_shooting_unset_timer(0),
1277         m_sprite_size(BS,BS),
1278         m_sprite_y(0),
1279         m_bright_shooting(false),
1280         m_lock_full_brightness(false),
1281         m_player_hit_timer(0)
1282 {
1283         ClientActiveObject::registerType(getType(), create);
1284
1285         m_properties = new Settings;
1286 }
1287
1288 MobV2CAO::~MobV2CAO()
1289 {
1290         delete m_properties;
1291 }
1292
1293 ClientActiveObject* MobV2CAO::create(IGameDef *gamedef, ClientEnvironment *env)
1294 {
1295         return new MobV2CAO(gamedef, env);
1296 }
1297
1298 void MobV2CAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
1299                         IrrlichtDevice *irr)
1300 {
1301         if(m_node != NULL)
1302                 return;
1303         
1304         /*infostream<<"MobV2CAO::addToScene using texture_name="<<
1305                         m_texture_name<<std::endl;*/
1306         std::string texture_string = m_texture_name +
1307                         "^[makealpha:128,0,0^[makealpha:128,128,0";
1308         
1309         scene::MyBillboardSceneNode *bill = new scene::MyBillboardSceneNode(
1310                         smgr->getRootSceneNode(), smgr, -1, v3f(0,0,0), v2f(1,1));
1311         bill->setMaterialTexture(0, tsrc->getTextureRaw(texture_string));
1312         bill->setMaterialFlag(video::EMF_LIGHTING, false);
1313         bill->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
1314         bill->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF);
1315         bill->setMaterialFlag(video::EMF_FOG_ENABLE, true);
1316         bill->setColor(video::SColor(255,0,0,0));
1317         bill->setVisible(false); /* Set visible when brightness is known */
1318         bill->setSize(m_sprite_size);
1319         if(m_sprite_type == "humanoid_1"){
1320                 const float txp = 1./192;
1321                 const float txs = txp*32;
1322                 const float typ = 1./240;
1323                 const float tys = typ*48;
1324                 bill->setTCoords(0, v2f(txs*1, tys*1));
1325                 bill->setTCoords(1, v2f(txs*1, tys*0));
1326                 bill->setTCoords(2, v2f(txs*0, tys*0));
1327                 bill->setTCoords(3, v2f(txs*0, tys*1));
1328         } else if(m_sprite_type == "simple"){
1329                 const float txs = 1.0;
1330                 const float tys = 1.0 / m_simple_anim_frames;
1331                 bill->setTCoords(0, v2f(txs*1, tys*1));
1332                 bill->setTCoords(1, v2f(txs*1, tys*0));
1333                 bill->setTCoords(2, v2f(txs*0, tys*0));
1334                 bill->setTCoords(3, v2f(txs*0, tys*1));
1335         } else {
1336                 infostream<<"MobV2CAO: Unknown sprite type \""<<m_sprite_type<<"\""
1337                                 <<std::endl;
1338         }
1339
1340         m_node = bill;
1341
1342         updateNodePos();
1343 }
1344
1345 void MobV2CAO::removeFromScene()
1346 {
1347         if(m_node == NULL)
1348                 return;
1349
1350         m_node->drop();
1351         m_node->remove();
1352         m_node = NULL;
1353 }
1354
1355 void MobV2CAO::updateLight(u8 light_at_pos)
1356 {
1357         if(m_lock_full_brightness)
1358                 light_at_pos = 15;
1359         
1360         m_last_light = light_at_pos;
1361
1362         if(m_node == NULL)
1363                 return;
1364         
1365         if(m_damage_visual_timer > 0)
1366                 return;
1367         
1368         if(m_shooting && m_bright_shooting)
1369                 return;
1370         
1371         /*if(light_at_pos <= 2){
1372                 m_node->setVisible(false);
1373                 return;
1374         }*/
1375
1376         m_node->setVisible(true);
1377
1378         u8 li = decode_light(light_at_pos);
1379         video::SColor color(255,li,li,li);
1380         m_node->setColor(color);
1381 }
1382
1383 v3s16 MobV2CAO::getLightPosition()
1384 {
1385         return floatToInt(m_position+v3f(0,0,0), BS);
1386 }
1387
1388 void MobV2CAO::updateNodePos()
1389 {
1390         if(m_node == NULL)
1391                 return;
1392
1393         m_node->setPosition(pos_translator.vect_show + v3f(0,m_sprite_y,0));
1394 }
1395
1396 void MobV2CAO::step(float dtime, ClientEnvironment *env)
1397 {
1398         scene::MyBillboardSceneNode *bill = m_node;
1399         if(!bill)
1400                 return;
1401
1402         pos_translator.translate(dtime);
1403         
1404         if(m_sprite_type == "humanoid_1"){
1405                 scene::ICameraSceneNode* camera = m_node->getSceneManager()->getActiveCamera();
1406                 if(!camera)
1407                         return;
1408                 v3f cam_to_mob = m_node->getAbsolutePosition() - camera->getAbsolutePosition();
1409                 cam_to_mob.normalize();
1410                 int col = 0;
1411                 if(cam_to_mob.Y > 0.75)
1412                         col = 5;
1413                 else if(cam_to_mob.Y < -0.75)
1414                         col = 4;
1415                 else{
1416                         float mob_dir = atan2(cam_to_mob.Z, cam_to_mob.X) / PI * 180.;
1417                         float dir = mob_dir - m_yaw;
1418                         dir = wrapDegrees_180(dir);
1419                         //infostream<<"id="<<m_id<<" dir="<<dir<<std::endl;
1420                         if(fabs(wrapDegrees_180(dir - 0)) <= 45.1)
1421                                 col = 2;
1422                         else if(fabs(wrapDegrees_180(dir - 90)) <= 45.1)
1423                                 col = 3;
1424                         else if(fabs(wrapDegrees_180(dir - 180)) <= 45.1)
1425                                 col = 0;
1426                         else if(fabs(wrapDegrees_180(dir + 90)) <= 45.1)
1427                                 col = 1;
1428                         else
1429                                 col = 4;
1430                 }
1431
1432                 int row = 0;
1433                 if(m_shooting){
1434                         row = 3;
1435                 } else if(m_walking){
1436                         m_walk_timer += dtime;
1437                         if(m_walk_timer >= 0.5){
1438                                 m_walk_frame = (m_walk_frame + 1) % 2;
1439                                 m_walk_timer = 0;
1440                         }
1441                         if(m_walk_frame == 0)
1442                                 row = 1;
1443                         else
1444                                 row = 2;
1445                 }
1446
1447                 const float txp = 1./192;
1448                 const float txs = txp*32;
1449                 const float typ = 1./240;
1450                 const float tys = typ*48;
1451                 bill->setTCoords(0, v2f(txs*(1+col), tys*(1+row)));
1452                 bill->setTCoords(1, v2f(txs*(1+col), tys*(0+row)));
1453                 bill->setTCoords(2, v2f(txs*(0+col), tys*(0+row)));
1454                 bill->setTCoords(3, v2f(txs*(0+col), tys*(1+row)));
1455         } else if(m_sprite_type == "simple"){
1456                 m_walk_timer += dtime;
1457                 if(m_walk_timer >= m_simple_anim_frametime){
1458                         m_walk_frame = (m_walk_frame + 1) % m_simple_anim_frames;
1459                         m_walk_timer = 0;
1460                 }
1461                 int col = 0;
1462                 int row = m_walk_frame;
1463                 const float txs = 1.0;
1464                 const float tys = 1.0 / m_simple_anim_frames;
1465                 bill->setTCoords(0, v2f(txs*(1+col), tys*(1+row)));
1466                 bill->setTCoords(1, v2f(txs*(1+col), tys*(0+row)));
1467                 bill->setTCoords(2, v2f(txs*(0+col), tys*(0+row)));
1468                 bill->setTCoords(3, v2f(txs*(0+col), tys*(1+row)));
1469         } else {
1470                 infostream<<"MobV2CAO::step(): Unknown sprite type \""
1471                                 <<m_sprite_type<<"\""<<std::endl;
1472         }
1473
1474         updateNodePos();
1475
1476         /* Damage local player */
1477         if(m_player_hit_damage && m_player_hit_timer <= 0.0){
1478                 LocalPlayer *player = env->getLocalPlayer();
1479                 assert(player);
1480                 
1481                 v3f playerpos = player->getPosition();
1482                 v2f playerpos_2d(playerpos.X,playerpos.Z);
1483                 v2f objectpos_2d(m_position.X,m_position.Z);
1484
1485                 if(fabs(m_position.Y - playerpos.Y) < m_player_hit_distance*BS &&
1486                 objectpos_2d.getDistanceFrom(playerpos_2d) < m_player_hit_distance*BS)
1487                 {
1488                         env->damageLocalPlayer(m_player_hit_damage);
1489                         m_player_hit_timer = m_player_hit_interval;
1490                 }
1491         }
1492
1493         /* Run timers */
1494
1495         m_player_hit_timer -= dtime;
1496
1497         if(m_damage_visual_timer >= 0){
1498                 m_damage_visual_timer -= dtime;
1499                 if(m_damage_visual_timer <= 0){
1500                         infostream<<"id="<<m_id<<" damage visual ended"<<std::endl;
1501                 }
1502         }
1503
1504         m_walking_unset_timer += dtime;
1505         if(m_walking_unset_timer >= 1.0){
1506                 m_walking = false;
1507         }
1508
1509         m_shooting_unset_timer -= dtime;
1510         if(m_shooting_unset_timer <= 0.0){
1511                 if(m_bright_shooting){
1512                         u8 li = decode_light(m_last_light);
1513                         video::SColor color(255,li,li,li);
1514                         bill->setColor(color);
1515                         m_bright_shooting = false;
1516                 }
1517                 m_shooting = false;
1518         }
1519
1520 }
1521
1522 void MobV2CAO::processMessage(const std::string &data)
1523 {
1524         //infostream<<"MobV2CAO: Got message"<<std::endl;
1525         std::istringstream is(data, std::ios::binary);
1526         // command
1527         u8 cmd = readU8(is);
1528
1529         // Move
1530         if(cmd == 0)
1531         {
1532                 // pos
1533                 m_position = readV3F1000(is);
1534                 pos_translator.update(m_position);
1535                 // yaw
1536                 m_yaw = readF1000(is);
1537
1538                 m_walking = true;
1539                 m_walking_unset_timer = 0;
1540
1541                 updateNodePos();
1542         }
1543         // Damage
1544         else if(cmd == 1)
1545         {
1546                 //u16 damage = readU16(is);
1547
1548                 /*u8 li = decode_light(m_last_light);
1549                 if(li >= 100)
1550                         li = 30;
1551                 else
1552                         li = 255;*/
1553
1554                 /*video::SColor color(255,255,0,0);
1555                 m_node->setColor(color);
1556
1557                 m_damage_visual_timer = 0.2;*/
1558         }
1559         // Trigger shooting
1560         else if(cmd == 2)
1561         {
1562                 // length
1563                 m_shooting_unset_timer = readF1000(is);
1564                 // bright?
1565                 m_bright_shooting = readU8(is);
1566                 if(m_bright_shooting){
1567                         u8 li = 255;
1568                         video::SColor color(255,li,li,li);
1569                         m_node->setColor(color);
1570                 }
1571
1572                 m_shooting = true;
1573         }
1574 }
1575
1576 void MobV2CAO::initialize(const std::string &data)
1577 {
1578         //infostream<<"MobV2CAO: Got init data"<<std::endl;
1579         
1580         {
1581                 std::istringstream is(data, std::ios::binary);
1582                 // version
1583                 u8 version = readU8(is);
1584                 // check version
1585                 if(version != 0){
1586                         infostream<<__FUNCTION_NAME<<": Invalid version"<<std::endl;
1587                         return;
1588                 }
1589                 
1590                 std::ostringstream tmp_os(std::ios::binary);
1591                 decompressZlib(is, tmp_os);
1592                 std::istringstream tmp_is(tmp_os.str(), std::ios::binary);
1593                 m_properties->parseConfigLines(tmp_is, "MobArgsEnd");
1594
1595                 infostream<<"MobV2CAO::initialize(): got properties:"<<std::endl;
1596                 m_properties->writeLines(infostream);
1597                 
1598                 m_properties->setDefault("looks", "dummy_default");
1599                 m_properties->setDefault("yaw", "0");
1600                 m_properties->setDefault("pos", "(0,0,0)");
1601                 m_properties->setDefault("player_hit_damage", "0");
1602                 m_properties->setDefault("player_hit_distance", "1.5");
1603                 m_properties->setDefault("player_hit_interval", "1.5");
1604                 
1605                 setLooks(m_properties->get("looks"));
1606                 m_yaw = m_properties->getFloat("yaw");
1607                 m_position = m_properties->getV3F("pos");
1608                 m_player_hit_damage = m_properties->getS32("player_hit_damage");
1609                 m_player_hit_distance = m_properties->getFloat("player_hit_distance");
1610                 m_player_hit_interval = m_properties->getFloat("player_hit_interval");
1611
1612                 pos_translator.init(m_position);
1613         }
1614         
1615         updateNodePos();
1616 }
1617
1618 bool MobV2CAO::directReportPunch(const std::string &toolname, v3f dir)
1619 {
1620         video::SColor color(255,255,0,0);
1621         m_node->setColor(color);
1622
1623         m_damage_visual_timer = 0.05;
1624
1625         m_position += dir * BS;
1626         pos_translator.sharpen();
1627         pos_translator.update(m_position);
1628         updateNodePos();
1629         
1630         return false;
1631 }
1632
1633 void MobV2CAO::setLooks(const std::string &looks)
1634 {
1635         v2f selection_size = v2f(0.4, 0.4) * BS;
1636         float selection_y = 0 * BS;
1637
1638         if(looks == "dungeon_master"){
1639                 m_texture_name = "dungeon_master.png";
1640                 m_sprite_type = "humanoid_1";
1641                 m_sprite_size = v2f(2, 3) * BS;
1642                 m_sprite_y = 0.85 * BS;
1643                 selection_size = v2f(0.4, 2.6) * BS;
1644                 selection_y = -0.4 * BS;
1645         }
1646         else if(looks == "fireball"){
1647                 m_texture_name = "fireball.png";
1648                 m_sprite_type = "simple";
1649                 m_sprite_size = v2f(1, 1) * BS;
1650                 m_simple_anim_frames = 3;
1651                 m_simple_anim_frametime = 0.1;
1652                 m_lock_full_brightness = true;
1653         }
1654         else{
1655                 m_texture_name = "stone.png";
1656                 m_sprite_type = "simple";
1657                 m_sprite_size = v2f(1, 1) * BS;
1658                 m_simple_anim_frames = 3;
1659                 m_simple_anim_frametime = 0.333;
1660                 selection_size = v2f(0.4, 0.4) * BS;
1661                 selection_y = 0 * BS;
1662         }
1663
1664         m_selection_box = core::aabbox3d<f32>(
1665                         -selection_size.X, selection_y, -selection_size.X,
1666                         selection_size.X, selection_y+selection_size.Y,
1667                         selection_size.X);
1668 }
1669
1670 /*
1671         LuaEntityCAO
1672 */
1673
1674 #include "luaentity_common.h"
1675
1676 class LuaEntityCAO : public ClientActiveObject
1677 {
1678 private:
1679         core::aabbox3d<f32> m_selection_box;
1680         scene::IMeshSceneNode *m_meshnode;
1681         scene::MyBillboardSceneNode *m_spritenode;
1682         v3f m_position;
1683         v3f m_velocity;
1684         v3f m_acceleration;
1685         float m_yaw;
1686         struct LuaEntityProperties *m_prop;
1687         SmoothTranslator pos_translator;
1688         // Spritesheet/animation stuff
1689         v2f m_tx_size;
1690         v2s16 m_tx_basepos;
1691         bool m_tx_select_horiz_by_yawpitch;
1692         int m_anim_frame;
1693         int m_anim_num_frames;
1694         float m_anim_framelength;
1695         float m_anim_timer;
1696
1697 public:
1698         LuaEntityCAO(IGameDef *gamedef, ClientEnvironment *env):
1699                 ClientActiveObject(0, gamedef, env),
1700                 m_selection_box(-BS/3.,-BS/3.,-BS/3., BS/3.,BS/3.,BS/3.),
1701                 m_meshnode(NULL),
1702                 m_spritenode(NULL),
1703                 m_position(v3f(0,10*BS,0)),
1704                 m_velocity(v3f(0,0,0)),
1705                 m_acceleration(v3f(0,0,0)),
1706                 m_yaw(0),
1707                 m_prop(new LuaEntityProperties),
1708                 m_tx_size(1,1),
1709                 m_tx_basepos(0,0),
1710                 m_tx_select_horiz_by_yawpitch(false),
1711                 m_anim_frame(0),
1712                 m_anim_num_frames(1),
1713                 m_anim_framelength(0.2),
1714                 m_anim_timer(0)
1715         {
1716                 if(gamedef == NULL)
1717                         ClientActiveObject::registerType(getType(), create);
1718         }
1719
1720         void initialize(const std::string &data)
1721         {
1722                 infostream<<"LuaEntityCAO: Got init data"<<std::endl;
1723                 
1724                 std::istringstream is(data, std::ios::binary);
1725                 // version
1726                 u8 version = readU8(is);
1727                 // check version
1728                 if(version != 0)
1729                         return;
1730                 // pos
1731                 m_position = readV3F1000(is);
1732                 // yaw
1733                 m_yaw = readF1000(is);
1734                 // properties
1735                 std::istringstream prop_is(deSerializeLongString(is), std::ios::binary);
1736                 m_prop->deSerialize(prop_is);
1737
1738                 infostream<<"m_prop: "<<m_prop->dump()<<std::endl;
1739
1740                 m_selection_box = m_prop->collisionbox;
1741                 m_selection_box.MinEdge *= BS;
1742                 m_selection_box.MaxEdge *= BS;
1743                         
1744                 pos_translator.init(m_position);
1745
1746                 m_tx_size.X = 1.0 / m_prop->spritediv.X;
1747                 m_tx_size.Y = 1.0 / m_prop->spritediv.Y;
1748                 m_tx_basepos.X = m_tx_size.X * m_prop->initial_sprite_basepos.X;
1749                 m_tx_basepos.Y = m_tx_size.Y * m_prop->initial_sprite_basepos.Y;
1750                 
1751                 updateNodePos();
1752         }
1753
1754         ~LuaEntityCAO()
1755         {
1756                 delete m_prop;
1757         }
1758
1759         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env)
1760         {
1761                 return new LuaEntityCAO(gamedef, env);
1762         }
1763
1764         u8 getType() const
1765         {
1766                 return ACTIVEOBJECT_TYPE_LUAENTITY;
1767         }
1768         core::aabbox3d<f32>* getSelectionBox()
1769         {
1770                 return &m_selection_box;
1771         }
1772         v3f getPosition()
1773         {
1774                 return pos_translator.vect_show;
1775         }
1776                 
1777         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
1778                         IrrlichtDevice *irr)
1779         {
1780                 if(m_meshnode != NULL || m_spritenode != NULL)
1781                         return;
1782                 
1783                 //video::IVideoDriver* driver = smgr->getVideoDriver();
1784
1785                 if(m_prop->visual == "sprite"){
1786                         infostream<<"LuaEntityCAO::addToScene(): single_sprite"<<std::endl;
1787                         m_spritenode = new scene::MyBillboardSceneNode(
1788                                         smgr->getRootSceneNode(), smgr, -1, v3f(0,0,0), v2f(1,1));
1789                         m_spritenode->setMaterialTexture(0,
1790                                         tsrc->getTextureRaw("unknown_block.png"));
1791                         m_spritenode->setMaterialFlag(video::EMF_LIGHTING, false);
1792                         m_spritenode->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
1793                         m_spritenode->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF);
1794                         m_spritenode->setMaterialFlag(video::EMF_FOG_ENABLE, true);
1795                         m_spritenode->setColor(video::SColor(255,0,0,0));
1796                         m_spritenode->setVisible(false); /* Set visible when brightness is known */
1797                         m_spritenode->setSize(m_prop->visual_size*BS);
1798                         {
1799                                 const float txs = 1.0 / 1;
1800                                 const float tys = 1.0 / 1;
1801                                 m_spritenode->setTCoords(0, v2f(txs*1, tys*1));
1802                                 m_spritenode->setTCoords(1, v2f(txs*1, tys*0));
1803                                 m_spritenode->setTCoords(2, v2f(txs*0, tys*0));
1804                                 m_spritenode->setTCoords(3, v2f(txs*0, tys*1));
1805                         }
1806                 } else if(m_prop->visual == "cube"){
1807                         infostream<<"LuaEntityCAO::addToScene(): cube"<<std::endl;
1808                         video::SColor c(255,255,255,255);
1809                         video::S3DVertex vertices[24] =
1810                         {
1811                                 // Up
1812                                 video::S3DVertex(-0.5,+0.5,-0.5, 0,1,0, c, 0,1),
1813                                 video::S3DVertex(-0.5,+0.5,+0.5, 0,1,0, c, 0,0),
1814                                 video::S3DVertex(+0.5,+0.5,+0.5, 0,1,0, c, 1,0),
1815                                 video::S3DVertex(+0.5,+0.5,-0.5, 0,1,0, c, 1,1),
1816                                 // Down
1817                                 video::S3DVertex(-0.5,-0.5,-0.5, 0,-1,0, c, 0,0),
1818                                 video::S3DVertex(+0.5,-0.5,-0.5, 0,-1,0, c, 1,0),
1819                                 video::S3DVertex(+0.5,-0.5,+0.5, 0,-1,0, c, 1,1),
1820                                 video::S3DVertex(-0.5,-0.5,+0.5, 0,-1,0, c, 0,1),
1821                                 // Right
1822                                 video::S3DVertex(+0.5,-0.5,-0.5, 1,0,0, c, 0,1),
1823                                 video::S3DVertex(+0.5,+0.5,-0.5, 1,0,0, c, 0,0),
1824                                 video::S3DVertex(+0.5,+0.5,+0.5, 1,0,0, c, 1,0),
1825                                 video::S3DVertex(+0.5,-0.5,+0.5, 1,0,0, c, 1,1),
1826                                 // Left
1827                                 video::S3DVertex(-0.5,-0.5,-0.5, -1,0,0, c, 1,1),
1828                                 video::S3DVertex(-0.5,-0.5,+0.5, -1,0,0, c, 0,1),
1829                                 video::S3DVertex(-0.5,+0.5,+0.5, -1,0,0, c, 0,0),
1830                                 video::S3DVertex(-0.5,+0.5,-0.5, -1,0,0, c, 1,0),
1831                                 // Back
1832                                 video::S3DVertex(-0.5,-0.5,+0.5, 0,0,1, c, 1,1),
1833                                 video::S3DVertex(+0.5,-0.5,+0.5, 0,0,1, c, 0,1),
1834                                 video::S3DVertex(+0.5,+0.5,+0.5, 0,0,1, c, 0,0),
1835                                 video::S3DVertex(-0.5,+0.5,+0.5, 0,0,1, c, 1,0),
1836                                 // Front
1837                                 video::S3DVertex(-0.5,-0.5,-0.5, 0,0,-1, c, 0,1),
1838                                 video::S3DVertex(-0.5,+0.5,-0.5, 0,0,-1, c, 0,0),
1839                                 video::S3DVertex(+0.5,+0.5,-0.5, 0,0,-1, c, 1,0),
1840                                 video::S3DVertex(+0.5,-0.5,-0.5, 0,0,-1, c, 1,1),
1841                         };
1842                         
1843                         for(u32 i=0; i<24; ++i){
1844                                 vertices[i].Pos *= BS;
1845                                 vertices[i].Pos.Y *= m_prop->visual_size.Y;
1846                                 vertices[i].Pos.X *= m_prop->visual_size.X;
1847                                 vertices[i].Pos.Z *= m_prop->visual_size.X;
1848                         }
1849
1850                         u16 indices[6] = {0,1,2,2,3,0};
1851
1852                         scene::SMesh* mesh = new scene::SMesh();
1853                         for (u32 i=0; i<6; ++i)
1854                         {
1855                                 scene::IMeshBuffer* buf = new scene::SMeshBuffer();
1856                                 buf->append(vertices + 4 * i, 4, indices, 6);
1857                                 buf->recalculateBoundingBox();
1858                                 mesh->addMeshBuffer(buf);
1859                                 buf->drop();
1860                         }
1861                         mesh->recalculateBoundingBox();
1862                 
1863                         m_meshnode = smgr->addMeshSceneNode(mesh, NULL);
1864                         
1865                         m_meshnode->setMesh(mesh);
1866                         m_meshnode->setScale(v3f(1));
1867                         // Will be shown when we know the brightness
1868                         m_meshnode->setVisible(false);
1869                 } else {
1870                         infostream<<"LuaEntityCAO::addToScene(): \""<<m_prop->visual
1871                                         <<"\" not supported"<<std::endl;
1872                 }
1873                 updateTextures("");
1874                 updateNodePos();
1875         }
1876
1877         void removeFromScene()
1878         {
1879                 if(m_meshnode){
1880                         m_meshnode->remove();
1881                         m_meshnode = NULL;
1882                 }
1883                 if(m_spritenode){
1884                         m_spritenode->remove();
1885                         m_spritenode = NULL;
1886                 }
1887         }
1888
1889         void updateLight(u8 light_at_pos)
1890         {
1891                 u8 li = decode_light(light_at_pos);
1892                 video::SColor color(255,li,li,li);
1893                 if(m_meshnode){
1894                         setMeshColor(m_meshnode->getMesh(), color);
1895                         m_meshnode->setVisible(true);
1896                 }
1897                 if(m_spritenode){
1898                         m_spritenode->setColor(color);
1899                         m_spritenode->setVisible(true);
1900                 }
1901         }
1902
1903         v3s16 getLightPosition()
1904         {
1905                 return floatToInt(m_position, BS);
1906         }
1907
1908         void updateNodePos()
1909         {
1910                 if(m_meshnode){
1911                         m_meshnode->setPosition(pos_translator.vect_show);
1912                 }
1913                 if(m_spritenode){
1914                         m_spritenode->setPosition(pos_translator.vect_show);
1915                 }
1916         }
1917
1918         void step(float dtime, ClientEnvironment *env)
1919         {
1920                 if(m_prop->physical){
1921                         core::aabbox3d<f32> box = m_prop->collisionbox;
1922                         box.MinEdge *= BS;
1923                         box.MaxEdge *= BS;
1924                         collisionMoveResult moveresult;
1925                         f32 pos_max_d = BS*0.25; // Distance per iteration
1926                         v3f p_pos = m_position;
1927                         v3f p_velocity = m_velocity;
1928                         IGameDef *gamedef = env->getGameDef();
1929                         moveresult = collisionMovePrecise(&env->getMap(), gamedef,
1930                                         pos_max_d, box, dtime, p_pos, p_velocity);
1931                         // Apply results
1932                         m_position = p_pos;
1933                         m_velocity = p_velocity;
1934                         
1935                         bool is_end_position = moveresult.collides;
1936                         pos_translator.update(m_position, is_end_position, dtime);
1937                         pos_translator.translate(dtime);
1938                         updateNodePos();
1939
1940                         m_velocity += dtime * m_acceleration;
1941                 } else {
1942                         m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration;
1943                         m_velocity += dtime * m_acceleration;
1944                         pos_translator.update(m_position, pos_translator.aim_is_end, pos_translator.anim_time);
1945                         pos_translator.translate(dtime);
1946                         updateNodePos();
1947                 }
1948
1949                 m_anim_timer += dtime;
1950                 if(m_anim_timer >= m_anim_framelength){
1951                         m_anim_timer -= m_anim_framelength;
1952                         m_anim_frame++;
1953                         if(m_anim_frame >= m_anim_num_frames)
1954                                 m_anim_frame = 0;
1955                 }
1956
1957                 updateTexturePos();
1958         }
1959
1960         void updateTexturePos()
1961         {
1962                 if(m_spritenode){
1963                         scene::ICameraSceneNode* camera =
1964                                         m_spritenode->getSceneManager()->getActiveCamera();
1965                         if(!camera)
1966                                 return;
1967                         v3f cam_to_entity = m_spritenode->getAbsolutePosition()
1968                                         - camera->getAbsolutePosition();
1969                         cam_to_entity.normalize();
1970
1971                         int row = m_tx_basepos.Y;
1972                         int col = m_tx_basepos.X;
1973                         
1974                         if(m_tx_select_horiz_by_yawpitch)
1975                         {
1976                                 if(cam_to_entity.Y > 0.75)
1977                                         col += 5;
1978                                 else if(cam_to_entity.Y < -0.75)
1979                                         col += 4;
1980                                 else{
1981                                         float mob_dir = atan2(cam_to_entity.Z, cam_to_entity.X) / PI * 180.;
1982                                         float dir = mob_dir - m_yaw;
1983                                         dir = wrapDegrees_180(dir);
1984                                         //infostream<<"id="<<m_id<<" dir="<<dir<<std::endl;
1985                                         if(fabs(wrapDegrees_180(dir - 0)) <= 45.1)
1986                                                 col += 2;
1987                                         else if(fabs(wrapDegrees_180(dir - 90)) <= 45.1)
1988                                                 col += 3;
1989                                         else if(fabs(wrapDegrees_180(dir - 180)) <= 45.1)
1990                                                 col += 0;
1991                                         else if(fabs(wrapDegrees_180(dir + 90)) <= 45.1)
1992                                                 col += 1;
1993                                         else
1994                                                 col += 4;
1995                                 }
1996                         }
1997                         
1998                         // Animation goes downwards
1999                         row += m_anim_frame;
2000
2001                         float txs = m_tx_size.X;
2002                         float tys = m_tx_size.Y;
2003                         m_spritenode->setTCoords(0, v2f(txs*(1+col), tys*(1+row)));
2004                         m_spritenode->setTCoords(1, v2f(txs*(1+col), tys*(0+row)));
2005                         m_spritenode->setTCoords(2, v2f(txs*(0+col), tys*(0+row)));
2006                         m_spritenode->setTCoords(3, v2f(txs*(0+col), tys*(1+row)));
2007                 }
2008         }
2009
2010         void updateTextures(const std::string &mod)
2011         {
2012                 ITextureSource *tsrc = m_gamedef->tsrc();
2013
2014                 if(m_spritenode){
2015                         std::string texturestring = "unknown_block.png";
2016                         if(m_prop->textures.size() >= 1)
2017                                 texturestring = m_prop->textures[0];
2018                         texturestring += mod;
2019                         m_spritenode->setMaterialTexture(0,
2020                                         tsrc->getTextureRaw(texturestring));
2021                 }
2022                 if(m_meshnode){
2023                         for (u32 i = 0; i < 6; ++i)
2024                         {
2025                                 std::string texturestring = "unknown_block.png";
2026                                 if(m_prop->textures.size() > i)
2027                                         texturestring = m_prop->textures[i];
2028                                 texturestring += mod;
2029                                 AtlasPointer ap = tsrc->getTexture(texturestring);
2030
2031                                 // Get the tile texture and atlas transformation
2032                                 video::ITexture* atlas = ap.atlas;
2033                                 v2f pos = ap.pos;
2034                                 v2f size = ap.size;
2035
2036                                 // Set material flags and texture
2037                                 video::SMaterial& material = m_meshnode->getMaterial(i);
2038                                 material.setFlag(video::EMF_LIGHTING, false);
2039                                 material.setFlag(video::EMF_BILINEAR_FILTER, false);
2040                                 material.setTexture(0, atlas);
2041                                 material.getTextureMatrix(0).setTextureTranslate(pos.X, pos.Y);
2042                                 material.getTextureMatrix(0).setTextureScale(size.X, size.Y);
2043                         }
2044                 }
2045         }
2046
2047         void processMessage(const std::string &data)
2048         {
2049                 //infostream<<"LuaEntityCAO: Got message"<<std::endl;
2050                 std::istringstream is(data, std::ios::binary);
2051                 // command
2052                 u8 cmd = readU8(is);
2053                 if(cmd == 0) // update position
2054                 {
2055                         // do_interpolate
2056                         bool do_interpolate = readU8(is);
2057                         // pos
2058                         m_position = readV3F1000(is);
2059                         // velocity
2060                         m_velocity = readV3F1000(is);
2061                         // acceleration
2062                         m_acceleration = readV3F1000(is);
2063                         // yaw
2064                         m_yaw = readF1000(is);
2065                         // is_end_position (for interpolation)
2066                         bool is_end_position = readU8(is);
2067                         // update_interval
2068                         float update_interval = readF1000(is);
2069                         
2070                         if(do_interpolate){
2071                                 if(!m_prop->physical)
2072                                         pos_translator.update(m_position, is_end_position, update_interval);
2073                         } else {
2074                                 pos_translator.init(m_position);
2075                         }
2076                         updateNodePos();
2077                 }
2078                 else if(cmd == 1) // set texture modification
2079                 {
2080                         std::string mod = deSerializeString(is);
2081                         updateTextures(mod);
2082                 }
2083                 else if(cmd == 2) // set sprite
2084                 {
2085                         v2s16 p = readV2S16(is);
2086                         int num_frames = readU16(is);
2087                         float framelength = readF1000(is);
2088                         bool select_horiz_by_yawpitch = readU8(is);
2089                         
2090                         m_tx_basepos = p;
2091                         m_anim_num_frames = num_frames;
2092                         m_anim_framelength = framelength;
2093                         m_tx_select_horiz_by_yawpitch = select_horiz_by_yawpitch;
2094
2095                         updateTexturePos();
2096                 }
2097         }
2098 };
2099
2100 // Prototype
2101 LuaEntityCAO proto_LuaEntityCAO(NULL, NULL);
2102
2103 /*
2104         PlayerCAO
2105 */
2106
2107 class PlayerCAO : public ClientActiveObject
2108 {
2109 private:
2110         core::aabbox3d<f32> m_selection_box;
2111         scene::IMeshSceneNode *m_node;
2112         scene::ITextSceneNode* m_text;
2113         std::string m_name;
2114         v3f m_position;
2115         float m_yaw;
2116         SmoothTranslator pos_translator;
2117         bool m_is_local_player;
2118         LocalPlayer *m_local_player;
2119         float m_damage_visual_timer;
2120
2121 public:
2122         PlayerCAO(IGameDef *gamedef, ClientEnvironment *env):
2123                 ClientActiveObject(0, gamedef, env),
2124                 m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2.0,BS/3.),
2125                 m_node(NULL),
2126                 m_text(NULL),
2127                 m_position(v3f(0,10*BS,0)),
2128                 m_yaw(0),
2129                 m_is_local_player(false),
2130                 m_local_player(NULL),
2131                 m_damage_visual_timer(0)
2132         {
2133                 if(gamedef == NULL)
2134                         ClientActiveObject::registerType(getType(), create);
2135         }
2136
2137         void initialize(const std::string &data)
2138         {
2139                 infostream<<"PlayerCAO: Got init data"<<std::endl;
2140                 
2141                 std::istringstream is(data, std::ios::binary);
2142                 // version
2143                 u8 version = readU8(is);
2144                 // check version
2145                 if(version != 0)
2146                         return;
2147                 // name
2148                 m_name = deSerializeString(is);
2149                 // pos
2150                 m_position = readV3F1000(is);
2151                 // yaw
2152                 m_yaw = readF1000(is);
2153
2154                 pos_translator.init(m_position);
2155
2156                 Player *player = m_env->getPlayer(m_name.c_str());
2157                 if(player && player->isLocal()){
2158                         m_is_local_player = true;
2159                         m_local_player = (LocalPlayer*)player;
2160                 }
2161         }
2162
2163         ~PlayerCAO()
2164         {
2165                 if(m_node)
2166                         m_node->remove();
2167         }
2168
2169         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env)
2170         {
2171                 return new PlayerCAO(gamedef, env);
2172         }
2173
2174         u8 getType() const
2175         {
2176                 return ACTIVEOBJECT_TYPE_PLAYER;
2177         }
2178         core::aabbox3d<f32>* getSelectionBox()
2179         {
2180                 if(m_is_local_player)
2181                         return NULL;
2182                 return &m_selection_box;
2183         }
2184         v3f getPosition()
2185         {
2186                 return pos_translator.vect_show;
2187         }
2188                 
2189         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
2190                         IrrlichtDevice *irr)
2191         {
2192                 if(m_node != NULL)
2193                         return;
2194                 if(m_is_local_player)
2195                         return;
2196                 
2197                 //video::IVideoDriver* driver = smgr->getVideoDriver();
2198                 gui::IGUIEnvironment* gui = irr->getGUIEnvironment();
2199                 
2200                 scene::SMesh *mesh = new scene::SMesh();
2201                 { // Front
2202                 scene::IMeshBuffer *buf = new scene::SMeshBuffer();
2203                 video::SColor c(255,255,255,255);
2204                 video::S3DVertex vertices[4] =
2205                 {
2206                         video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
2207                         video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
2208                         video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0),
2209                         video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0),
2210                 };
2211                 u16 indices[] = {0,1,2,2,3,0};
2212                 buf->append(vertices, 4, indices, 6);
2213                 // Set material
2214                 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
2215                 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
2216                 buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
2217                 buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
2218                 // Add to mesh
2219                 mesh->addMeshBuffer(buf);
2220                 buf->drop();
2221                 }
2222                 { // Back
2223                 scene::IMeshBuffer *buf = new scene::SMeshBuffer();
2224                 video::SColor c(255,255,255,255);
2225                 video::S3DVertex vertices[4] =
2226                 {
2227                         video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
2228                         video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
2229                         video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0),
2230                         video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0),
2231                 };
2232                 u16 indices[] = {0,1,2,2,3,0};
2233                 buf->append(vertices, 4, indices, 6);
2234                 // Set material
2235                 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
2236                 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
2237                 buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
2238                 buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
2239                 // Add to mesh
2240                 mesh->addMeshBuffer(buf);
2241                 buf->drop();
2242                 }
2243                 m_node = smgr->addMeshSceneNode(mesh, NULL);
2244                 mesh->drop();
2245                 // Set it to use the materials of the meshbuffers directly.
2246                 // This is needed for changing the texture in the future
2247                 m_node->setReadOnlyMaterials(true);
2248                 updateNodePos();
2249
2250                 // Add a text node for showing the name
2251                 std::wstring wname = narrow_to_wide(m_name);
2252                 m_text = smgr->addTextSceneNode(gui->getBuiltInFont(),
2253                                 wname.c_str(), video::SColor(255,255,255,255), m_node);
2254                 m_text->setPosition(v3f(0, (f32)BS*2.1, 0));
2255                 
2256                 updateTextures("");
2257                 updateNodePos();
2258         }
2259
2260         void removeFromScene()
2261         {
2262                 if(m_node == NULL)
2263                         return;
2264
2265                 m_node->remove();
2266                 m_node = NULL;
2267         }
2268
2269         void updateLight(u8 light_at_pos)
2270         {
2271                 if(m_node == NULL)
2272                         return;
2273                 
2274                 m_node->setVisible(true);
2275
2276                 u8 li = decode_light(light_at_pos);
2277                 video::SColor color(255,li,li,li);
2278                 setMeshColor(m_node->getMesh(), color);
2279         }
2280
2281         v3s16 getLightPosition()
2282         {
2283                 return floatToInt(m_position+v3f(0,BS*1.5,0), BS);
2284         }
2285
2286         void updateNodePos()
2287         {
2288                 if(m_node == NULL)
2289                         return;
2290
2291                 m_node->setPosition(pos_translator.vect_show);
2292
2293                 v3f rot = m_node->getRotation();
2294                 rot.Y = -m_yaw;
2295                 m_node->setRotation(rot);
2296         }
2297
2298         void step(float dtime, ClientEnvironment *env)
2299         {
2300                 pos_translator.translate(dtime);
2301                 updateNodePos();
2302
2303                 if(m_damage_visual_timer > 0){
2304                         m_damage_visual_timer -= dtime;
2305                         if(m_damage_visual_timer <= 0){
2306                                 updateTextures("");
2307                         }
2308                 }
2309         }
2310
2311         void processMessage(const std::string &data)
2312         {
2313                 //infostream<<"PlayerCAO: Got message"<<std::endl;
2314                 std::istringstream is(data, std::ios::binary);
2315                 // command
2316                 u8 cmd = readU8(is);
2317                 if(cmd == 0) // update position
2318                 {
2319                         // pos
2320                         m_position = readV3F1000(is);
2321                         // yaw
2322                         m_yaw = readF1000(is);
2323
2324                         pos_translator.update(m_position, false);
2325
2326                         updateNodePos();
2327                 }
2328                 else if(cmd == 1) // punched
2329                 {
2330                         // damage
2331                         s16 damage = readS16(is);
2332                         
2333                         if(m_is_local_player)
2334                                 m_env->damageLocalPlayer(damage, false);
2335                         
2336                         m_damage_visual_timer = 0.5;
2337                         updateTextures("^[brighten");
2338                 }
2339         }
2340
2341         void updateTextures(const std::string &mod)
2342         {
2343                 if(!m_node)
2344                         return;
2345                 ITextureSource *tsrc = m_gamedef->tsrc();
2346                 scene::IMesh *mesh = m_node->getMesh();
2347                 if(mesh){
2348                         {
2349                                 std::string tname = "player.png";
2350                                 tname += mod;
2351                                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(0);
2352                                 buf->getMaterial().setTexture(0,
2353                                                 tsrc->getTextureRaw(tname));
2354                         }
2355                         {
2356                                 std::string tname = "player_back.png";
2357                                 tname += mod;
2358                                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(1);
2359                                 buf->getMaterial().setTexture(0,
2360                                                 tsrc->getTextureRaw(tname));
2361                         }
2362                 }
2363         }
2364 };
2365
2366 // Prototype
2367 PlayerCAO proto_PlayerCAO(NULL, NULL);
2368
2369