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