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