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