]> git.lizzy.rs Git - minetest.git/blob - src/content_cao.cpp
da4ab614406f9640c5a330ac7a43bc103c53c48d
[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 "collision.h"
24 #include "settings.h"
25 #include <ICameraSceneNode.h>
26 #include <ITextSceneNode.h>
27 #include <IBillboardSceneNode.h>
28 #include "serialization.h" // For decompressZlib
29 #include "gamedef.h"
30 #include "clientobject.h"
31 #include "content_object.h"
32 #include "mesh.h"
33 #include "utility.h" // For IntervalLimiter
34 #include "itemdef.h"
35 #include "tool.h"
36 #include "content_cso.h"
37 #include "sound.h"
38 #include "nodedef.h"
39 class Settings;
40 struct ToolCapabilities;
41
42 core::map<u16, ClientActiveObject::Factory> ClientActiveObject::m_types;
43
44 /*
45         SmoothTranslator
46 */
47
48 struct SmoothTranslator
49 {
50         v3f vect_old;
51         v3f vect_show;
52         v3f vect_aim;
53         f32 anim_counter;
54         f32 anim_time;
55         f32 anim_time_counter;
56         bool aim_is_end;
57
58         SmoothTranslator():
59                 vect_old(0,0,0),
60                 vect_show(0,0,0),
61                 vect_aim(0,0,0),
62                 anim_counter(0),
63                 anim_time(0),
64                 anim_time_counter(0),
65                 aim_is_end(true)
66         {}
67
68         void init(v3f vect)
69         {
70                 vect_old = vect;
71                 vect_show = vect;
72                 vect_aim = vect;
73                 anim_counter = 0;
74                 anim_time = 0;
75                 anim_time_counter = 0;
76                 aim_is_end = true;
77         }
78
79         void sharpen()
80         {
81                 init(vect_show);
82         }
83
84         void update(v3f vect_new, bool is_end_position=false, float update_interval=-1)
85         {
86                 aim_is_end = is_end_position;
87                 vect_old = vect_show;
88                 vect_aim = vect_new;
89                 if(update_interval > 0){
90                         anim_time = update_interval;
91                 } else {
92                         if(anim_time < 0.001 || anim_time > 1.0)
93                                 anim_time = anim_time_counter;
94                         else
95                                 anim_time = anim_time * 0.9 + anim_time_counter * 0.1;
96                 }
97                 anim_time_counter = 0;
98                 anim_counter = 0;
99         }
100
101         void translate(f32 dtime)
102         {
103                 anim_time_counter = anim_time_counter + dtime;
104                 anim_counter = anim_counter + dtime;
105                 v3f vect_move = vect_aim - vect_old;
106                 f32 moveratio = 1.0;
107                 if(anim_time > 0.001)
108                         moveratio = anim_time_counter / anim_time;
109                 // Move a bit less than should, to avoid oscillation
110                 moveratio = moveratio * 0.8;
111                 float move_end = 1.5;
112                 if(aim_is_end)
113                         move_end = 1.0;
114                 if(moveratio > move_end)
115                         moveratio = move_end;
116                 vect_show = vect_old + vect_move * moveratio;
117         }
118
119         bool is_moving()
120         {
121                 return ((anim_time_counter / anim_time) < 1.4);
122         }
123 };
124
125 /*
126         Other stuff
127 */
128
129 static void setBillboardTextureMatrix(scene::IBillboardSceneNode *bill,
130                 float txs, float tys, int col, int row)
131 {
132         video::SMaterial& material = bill->getMaterial(0);
133         core::matrix4& matrix = material.getTextureMatrix(0);
134         matrix.setTextureTranslate(txs*col, tys*row);
135         matrix.setTextureScale(txs, tys);
136 }
137
138 /*
139         TestCAO
140 */
141
142 class TestCAO : public ClientActiveObject
143 {
144 public:
145         TestCAO(IGameDef *gamedef, ClientEnvironment *env);
146         virtual ~TestCAO();
147         
148         u8 getType() const
149         {
150                 return ACTIVEOBJECT_TYPE_TEST;
151         }
152         
153         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env);
154
155         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
156                         IrrlichtDevice *irr);
157         void removeFromScene();
158         void updateLight(u8 light_at_pos);
159         v3s16 getLightPosition();
160         void updateNodePos();
161
162         void step(float dtime, ClientEnvironment *env);
163
164         void processMessage(const std::string &data);
165
166 private:
167         scene::IMeshSceneNode *m_node;
168         v3f m_position;
169 };
170
171 // Prototype
172 TestCAO proto_TestCAO(NULL, NULL);
173
174 TestCAO::TestCAO(IGameDef *gamedef, ClientEnvironment *env):
175         ClientActiveObject(0, gamedef, env),
176         m_node(NULL),
177         m_position(v3f(0,10*BS,0))
178 {
179         ClientActiveObject::registerType(getType(), create);
180 }
181
182 TestCAO::~TestCAO()
183 {
184 }
185
186 ClientActiveObject* TestCAO::create(IGameDef *gamedef, ClientEnvironment *env)
187 {
188         return new TestCAO(gamedef, env);
189 }
190
191 void TestCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
192                         IrrlichtDevice *irr)
193 {
194         if(m_node != NULL)
195                 return;
196         
197         //video::IVideoDriver* driver = smgr->getVideoDriver();
198         
199         scene::SMesh *mesh = new scene::SMesh();
200         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
201         video::SColor c(255,255,255,255);
202         video::S3DVertex vertices[4] =
203         {
204                 video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1),
205                 video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1),
206                 video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0),
207                 video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),
208         };
209         u16 indices[] = {0,1,2,2,3,0};
210         buf->append(vertices, 4, indices, 6);
211         // Set material
212         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
213         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
214         buf->getMaterial().setTexture(0, tsrc->getTextureRaw("rat.png"));
215         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
216         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
217         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
218         // Add to mesh
219         mesh->addMeshBuffer(buf);
220         buf->drop();
221         m_node = smgr->addMeshSceneNode(mesh, NULL);
222         mesh->drop();
223         updateNodePos();
224 }
225
226 void TestCAO::removeFromScene()
227 {
228         if(m_node == NULL)
229                 return;
230
231         m_node->remove();
232         m_node = NULL;
233 }
234
235 void TestCAO::updateLight(u8 light_at_pos)
236 {
237 }
238
239 v3s16 TestCAO::getLightPosition()
240 {
241         return floatToInt(m_position, BS);
242 }
243
244 void TestCAO::updateNodePos()
245 {
246         if(m_node == NULL)
247                 return;
248
249         m_node->setPosition(m_position);
250         //m_node->setRotation(v3f(0, 45, 0));
251 }
252
253 void TestCAO::step(float dtime, ClientEnvironment *env)
254 {
255         if(m_node)
256         {
257                 v3f rot = m_node->getRotation();
258                 //infostream<<"dtime="<<dtime<<", rot.Y="<<rot.Y<<std::endl;
259                 rot.Y += dtime * 180;
260                 m_node->setRotation(rot);
261         }
262 }
263
264 void TestCAO::processMessage(const std::string &data)
265 {
266         infostream<<"TestCAO: Got data: "<<data<<std::endl;
267         std::istringstream is(data, std::ios::binary);
268         u16 cmd;
269         is>>cmd;
270         if(cmd == 0)
271         {
272                 v3f newpos;
273                 is>>newpos.X;
274                 is>>newpos.Y;
275                 is>>newpos.Z;
276                 m_position = newpos;
277                 updateNodePos();
278         }
279 }
280
281 /*
282         ItemCAO
283 */
284
285 class ItemCAO : public ClientActiveObject
286 {
287 public:
288         ItemCAO(IGameDef *gamedef, ClientEnvironment *env);
289         virtual ~ItemCAO();
290         
291         u8 getType() const
292         {
293                 return ACTIVEOBJECT_TYPE_ITEM;
294         }
295         
296         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env);
297
298         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
299                         IrrlichtDevice *irr);
300         void removeFromScene();
301         void updateLight(u8 light_at_pos);
302         v3s16 getLightPosition();
303         void updateNodePos();
304         void updateInfoText();
305         void updateTexture();
306
307         void step(float dtime, ClientEnvironment *env);
308
309         void processMessage(const std::string &data);
310
311         void initialize(const std::string &data);
312         
313         core::aabbox3d<f32>* getSelectionBox()
314                 {return &m_selection_box;}
315         v3f getPosition()
316                 {return m_position;}
317         
318         std::string infoText()
319                 {return m_infotext;}
320
321 private:
322         core::aabbox3d<f32> m_selection_box;
323         scene::IMeshSceneNode *m_node;
324         v3f m_position;
325         std::string m_itemstring;
326         std::string m_infotext;
327 };
328
329 #include "inventory.h"
330
331 // Prototype
332 ItemCAO proto_ItemCAO(NULL, NULL);
333
334 ItemCAO::ItemCAO(IGameDef *gamedef, ClientEnvironment *env):
335         ClientActiveObject(0, gamedef, env),
336         m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.),
337         m_node(NULL),
338         m_position(v3f(0,10*BS,0))
339 {
340         if(!gamedef && !env)
341         {
342                 ClientActiveObject::registerType(getType(), create);
343         }
344 }
345
346 ItemCAO::~ItemCAO()
347 {
348 }
349
350 ClientActiveObject* ItemCAO::create(IGameDef *gamedef, ClientEnvironment *env)
351 {
352         return new ItemCAO(gamedef, env);
353 }
354
355 void ItemCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
356                         IrrlichtDevice *irr)
357 {
358         if(m_node != NULL)
359                 return;
360         
361         //video::IVideoDriver* driver = smgr->getVideoDriver();
362         
363         scene::SMesh *mesh = new scene::SMesh();
364         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
365         video::SColor c(255,255,255,255);
366         video::S3DVertex vertices[4] =
367         {
368                 /*video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1),
369                 video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1),
370                 video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0),
371                 video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),*/
372                 video::S3DVertex(BS/3.,0,0, 0,0,0, c, 0,1),
373                 video::S3DVertex(-BS/3.,0,0, 0,0,0, c, 1,1),
374                 video::S3DVertex(-BS/3.,0+BS*2./3.,0, 0,0,0, c, 1,0),
375                 video::S3DVertex(BS/3.,0+BS*2./3.,0, 0,0,0, c, 0,0),
376         };
377         u16 indices[] = {0,1,2,2,3,0};
378         buf->append(vertices, 4, indices, 6);
379         // Set material
380         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
381         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
382         // Initialize with a generated placeholder texture
383         buf->getMaterial().setTexture(0, tsrc->getTextureRaw(""));
384         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
385         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
386         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
387         // Add to mesh
388         mesh->addMeshBuffer(buf);
389         buf->drop();
390         m_node = smgr->addMeshSceneNode(mesh, NULL);
391         mesh->drop();
392         updateNodePos();
393
394         /*
395                 Update image of node
396         */
397
398         updateTexture();
399 }
400
401 void ItemCAO::removeFromScene()
402 {
403         if(m_node == NULL)
404                 return;
405
406         m_node->remove();
407         m_node = NULL;
408 }
409
410 void ItemCAO::updateLight(u8 light_at_pos)
411 {
412         if(m_node == NULL)
413                 return;
414
415         u8 li = decode_light(light_at_pos);
416         video::SColor color(255,li,li,li);
417         setMeshColor(m_node->getMesh(), color);
418 }
419
420 v3s16 ItemCAO::getLightPosition()
421 {
422         return floatToInt(m_position + v3f(0,0.5*BS,0), BS);
423 }
424
425 void ItemCAO::updateNodePos()
426 {
427         if(m_node == NULL)
428                 return;
429
430         m_node->setPosition(m_position);
431 }
432
433 void ItemCAO::updateInfoText()
434 {
435         try{
436                 IItemDefManager *idef = m_gamedef->idef();
437                 ItemStack item;
438                 item.deSerialize(m_itemstring, idef);
439                 if(item.isKnown(idef))
440                         m_infotext = item.getDefinition(idef).description;
441                 else
442                         m_infotext = "Unknown item: '" + m_itemstring + "'";
443                 if(item.count >= 2)
444                         m_infotext += " (" + itos(item.count) + ")";
445         }
446         catch(SerializationError &e)
447         {
448                 m_infotext = "Unknown item: '" + m_itemstring + "'";
449         }
450 }
451
452 void ItemCAO::updateTexture()
453 {
454         if(m_node == NULL)
455                 return;
456
457         // Create an inventory item to see what is its image
458         std::istringstream is(m_itemstring, std::ios_base::binary);
459         video::ITexture *texture = NULL;
460         try{
461                 IItemDefManager *idef = m_gamedef->idef();
462                 ItemStack item;
463                 item.deSerialize(is, idef);
464                 texture = item.getDefinition(idef).inventory_texture;
465         }
466         catch(SerializationError &e)
467         {
468                 infostream<<"WARNING: "<<__FUNCTION_NAME
469                                 <<": error deSerializing itemstring \""
470                                 <<m_itemstring<<std::endl;
471         }
472         
473         // Set meshbuffer texture
474         m_node->getMaterial(0).setTexture(0, texture);
475 }
476
477
478 void ItemCAO::step(float dtime, ClientEnvironment *env)
479 {
480         if(m_node)
481         {
482                 /*v3f rot = m_node->getRotation();
483                 rot.Y += dtime * 120;
484                 m_node->setRotation(rot);*/
485                 LocalPlayer *player = env->getLocalPlayer();
486                 assert(player);
487                 v3f rot = m_node->getRotation();
488                 rot.Y = 180.0 - (player->getYaw());
489                 m_node->setRotation(rot);
490         }
491 }
492
493 void ItemCAO::processMessage(const std::string &data)
494 {
495         //infostream<<"ItemCAO: Got message"<<std::endl;
496         std::istringstream is(data, std::ios::binary);
497         // command
498         u8 cmd = readU8(is);
499         if(cmd == 0)
500         {
501                 // pos
502                 m_position = readV3F1000(is);
503                 updateNodePos();
504         }
505         if(cmd == 1)
506         {
507                 // itemstring
508                 m_itemstring = deSerializeString(is);
509                 updateInfoText();
510                 updateTexture();
511         }
512 }
513
514 void ItemCAO::initialize(const std::string &data)
515 {
516         infostream<<"ItemCAO: Got init data"<<std::endl;
517         
518         {
519                 std::istringstream is(data, std::ios::binary);
520                 // version
521                 u8 version = readU8(is);
522                 // check version
523                 if(version != 0)
524                         return;
525                 // pos
526                 m_position = readV3F1000(is);
527                 // itemstring
528                 m_itemstring = deSerializeString(is);
529         }
530         
531         updateNodePos();
532         updateInfoText();
533 }
534
535 /*
536         LuaEntityCAO
537 */
538
539 #include "luaentity_common.h"
540
541 class LuaEntityCAO : public ClientActiveObject
542 {
543 private:
544         scene::ISceneManager *m_smgr;
545         core::aabbox3d<f32> m_selection_box;
546         scene::IMeshSceneNode *m_meshnode;
547         scene::IBillboardSceneNode *m_spritenode;
548         v3f m_position;
549         v3f m_velocity;
550         v3f m_acceleration;
551         float m_yaw;
552         s16 m_hp;
553         struct LuaEntityProperties *m_prop;
554         SmoothTranslator pos_translator;
555         // Spritesheet/animation stuff
556         v2f m_tx_size;
557         v2s16 m_tx_basepos;
558         bool m_tx_select_horiz_by_yawpitch;
559         int m_anim_frame;
560         int m_anim_num_frames;
561         float m_anim_framelength;
562         float m_anim_timer;
563         ItemGroupList m_armor_groups;
564         float m_reset_textures_timer;
565
566 public:
567         LuaEntityCAO(IGameDef *gamedef, ClientEnvironment *env):
568                 ClientActiveObject(0, gamedef, env),
569                 m_smgr(NULL),
570                 m_selection_box(-BS/3.,-BS/3.,-BS/3., BS/3.,BS/3.,BS/3.),
571                 m_meshnode(NULL),
572                 m_spritenode(NULL),
573                 m_position(v3f(0,10*BS,0)),
574                 m_velocity(v3f(0,0,0)),
575                 m_acceleration(v3f(0,0,0)),
576                 m_yaw(0),
577                 m_hp(1),
578                 m_prop(new LuaEntityProperties),
579                 m_tx_size(1,1),
580                 m_tx_basepos(0,0),
581                 m_tx_select_horiz_by_yawpitch(false),
582                 m_anim_frame(0),
583                 m_anim_num_frames(1),
584                 m_anim_framelength(0.2),
585                 m_anim_timer(0),
586                 m_reset_textures_timer(-1)
587         {
588                 if(gamedef == NULL)
589                         ClientActiveObject::registerType(getType(), create);
590         }
591
592         void initialize(const std::string &data)
593         {
594                 infostream<<"LuaEntityCAO: Got init data"<<std::endl;
595                 
596                 std::istringstream is(data, std::ios::binary);
597                 // version
598                 u8 version = readU8(is);
599                 // check version
600                 if(version != 1)
601                         return;
602                 // pos
603                 m_position = readV3F1000(is);
604                 // yaw
605                 m_yaw = readF1000(is);
606                 // hp
607                 m_hp = readS16(is);
608                 // properties
609                 std::istringstream prop_is(deSerializeLongString(is), std::ios::binary);
610                 m_prop->deSerialize(prop_is);
611
612                 infostream<<"m_prop: "<<m_prop->dump()<<std::endl;
613
614                 m_selection_box = m_prop->collisionbox;
615                 m_selection_box.MinEdge *= BS;
616                 m_selection_box.MaxEdge *= BS;
617                         
618                 pos_translator.init(m_position);
619
620                 m_tx_size.X = 1.0 / m_prop->spritediv.X;
621                 m_tx_size.Y = 1.0 / m_prop->spritediv.Y;
622                 m_tx_basepos.X = m_tx_size.X * m_prop->initial_sprite_basepos.X;
623                 m_tx_basepos.Y = m_tx_size.Y * m_prop->initial_sprite_basepos.Y;
624                 
625                 updateNodePos();
626         }
627
628         ~LuaEntityCAO()
629         {
630                 delete m_prop;
631         }
632
633         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env)
634         {
635                 return new LuaEntityCAO(gamedef, env);
636         }
637
638         u8 getType() const
639         {
640                 return ACTIVEOBJECT_TYPE_LUAENTITY;
641         }
642         core::aabbox3d<f32>* getSelectionBox()
643         {
644                 return &m_selection_box;
645         }
646         v3f getPosition()
647         {
648                 return pos_translator.vect_show;
649         }
650                 
651         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
652                         IrrlichtDevice *irr)
653         {
654                 m_smgr = smgr;
655
656                 if(m_meshnode != NULL || m_spritenode != NULL)
657                         return;
658                 
659                 //video::IVideoDriver* driver = smgr->getVideoDriver();
660
661                 if(m_prop->visual == "sprite"){
662                         infostream<<"LuaEntityCAO::addToScene(): single_sprite"<<std::endl;
663                         m_spritenode = smgr->addBillboardSceneNode(
664                                         NULL, v2f(1, 1), v3f(0,0,0), -1);
665                         m_spritenode->setMaterialTexture(0,
666                                         tsrc->getTextureRaw("unknown_block.png"));
667                         m_spritenode->setMaterialFlag(video::EMF_LIGHTING, false);
668                         m_spritenode->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
669                         m_spritenode->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF);
670                         m_spritenode->setMaterialFlag(video::EMF_FOG_ENABLE, true);
671                         m_spritenode->setColor(video::SColor(255,0,0,0));
672                         m_spritenode->setVisible(false); /* Set visible when brightness is known */
673                         m_spritenode->setSize(m_prop->visual_size*BS);
674                         {
675                                 const float txs = 1.0 / 1;
676                                 const float tys = 1.0 / 1;
677                                 setBillboardTextureMatrix(m_spritenode,
678                                                 txs, tys, 0, 0);
679                         }
680                 } else if(m_prop->visual == "cube"){
681                         infostream<<"LuaEntityCAO::addToScene(): cube"<<std::endl;
682                         scene::IMesh *mesh = createCubeMesh(v3f(BS,BS,BS));
683                         m_meshnode = smgr->addMeshSceneNode(mesh, NULL);
684                         mesh->drop();
685                         
686                         m_meshnode->setScale(v3f(1));
687                         // Will be shown when we know the brightness
688                         m_meshnode->setVisible(false);
689                 } else {
690                         infostream<<"LuaEntityCAO::addToScene(): \""<<m_prop->visual
691                                         <<"\" not supported"<<std::endl;
692                 }
693                 updateTextures("");
694                 updateNodePos();
695         }
696
697         void removeFromScene()
698         {
699                 if(m_meshnode){
700                         m_meshnode->remove();
701                         m_meshnode = NULL;
702                 }
703                 if(m_spritenode){
704                         m_spritenode->remove();
705                         m_spritenode = NULL;
706                 }
707         }
708
709         void updateLight(u8 light_at_pos)
710         {
711                 bool is_visible = (m_hp != 0);
712                 u8 li = decode_light(light_at_pos);
713                 video::SColor color(255,li,li,li);
714                 if(m_meshnode){
715                         setMeshColor(m_meshnode->getMesh(), color);
716                         m_meshnode->setVisible(is_visible);
717                 }
718                 if(m_spritenode){
719                         m_spritenode->setColor(color);
720                         m_spritenode->setVisible(is_visible);
721                 }
722         }
723
724         v3s16 getLightPosition()
725         {
726                 return floatToInt(m_position, BS);
727         }
728
729         void updateNodePos()
730         {
731                 if(m_meshnode){
732                         m_meshnode->setPosition(pos_translator.vect_show);
733                 }
734                 if(m_spritenode){
735                         m_spritenode->setPosition(pos_translator.vect_show);
736                 }
737         }
738
739         void step(float dtime, ClientEnvironment *env)
740         {
741                 if(m_prop->physical){
742                         core::aabbox3d<f32> box = m_prop->collisionbox;
743                         box.MinEdge *= BS;
744                         box.MaxEdge *= BS;
745                         collisionMoveResult moveresult;
746                         f32 pos_max_d = BS*0.25; // Distance per iteration
747                         v3f p_pos = m_position;
748                         v3f p_velocity = m_velocity;
749                         IGameDef *gamedef = env->getGameDef();
750                         moveresult = collisionMovePrecise(&env->getMap(), gamedef,
751                                         pos_max_d, box, dtime, p_pos, p_velocity);
752                         // Apply results
753                         m_position = p_pos;
754                         m_velocity = p_velocity;
755                         
756                         bool is_end_position = moveresult.collides;
757                         pos_translator.update(m_position, is_end_position, dtime);
758                         pos_translator.translate(dtime);
759                         updateNodePos();
760
761                         m_velocity += dtime * m_acceleration;
762                 } else {
763                         m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration;
764                         m_velocity += dtime * m_acceleration;
765                         pos_translator.update(m_position, pos_translator.aim_is_end, pos_translator.anim_time);
766                         pos_translator.translate(dtime);
767                         updateNodePos();
768                 }
769
770                 m_anim_timer += dtime;
771                 if(m_anim_timer >= m_anim_framelength){
772                         m_anim_timer -= m_anim_framelength;
773                         m_anim_frame++;
774                         if(m_anim_frame >= m_anim_num_frames)
775                                 m_anim_frame = 0;
776                 }
777
778                 updateTexturePos();
779
780                 if(m_reset_textures_timer >= 0){
781                         m_reset_textures_timer -= dtime;
782                         if(m_reset_textures_timer <= 0){
783                                 m_reset_textures_timer = -1;
784                                 updateTextures("");
785                         }
786                 }
787         }
788
789         void updateTexturePos()
790         {
791                 if(m_spritenode){
792                         scene::ICameraSceneNode* camera =
793                                         m_spritenode->getSceneManager()->getActiveCamera();
794                         if(!camera)
795                                 return;
796                         v3f cam_to_entity = m_spritenode->getAbsolutePosition()
797                                         - camera->getAbsolutePosition();
798                         cam_to_entity.normalize();
799
800                         int row = m_tx_basepos.Y;
801                         int col = m_tx_basepos.X;
802                         
803                         if(m_tx_select_horiz_by_yawpitch)
804                         {
805                                 if(cam_to_entity.Y > 0.75)
806                                         col += 5;
807                                 else if(cam_to_entity.Y < -0.75)
808                                         col += 4;
809                                 else{
810                                         float mob_dir = atan2(cam_to_entity.Z, cam_to_entity.X) / PI * 180.;
811                                         float dir = mob_dir - m_yaw;
812                                         dir = wrapDegrees_180(dir);
813                                         //infostream<<"id="<<m_id<<" dir="<<dir<<std::endl;
814                                         if(fabs(wrapDegrees_180(dir - 0)) <= 45.1)
815                                                 col += 2;
816                                         else if(fabs(wrapDegrees_180(dir - 90)) <= 45.1)
817                                                 col += 3;
818                                         else if(fabs(wrapDegrees_180(dir - 180)) <= 45.1)
819                                                 col += 0;
820                                         else if(fabs(wrapDegrees_180(dir + 90)) <= 45.1)
821                                                 col += 1;
822                                         else
823                                                 col += 4;
824                                 }
825                         }
826                         
827                         // Animation goes downwards
828                         row += m_anim_frame;
829
830                         float txs = m_tx_size.X;
831                         float tys = m_tx_size.Y;
832                         setBillboardTextureMatrix(m_spritenode,
833                                         txs, tys, col, row);
834                 }
835         }
836
837         void updateTextures(const std::string &mod)
838         {
839                 ITextureSource *tsrc = m_gamedef->tsrc();
840
841                 if(m_spritenode){
842                         std::string texturestring = "unknown_block.png";
843                         if(m_prop->textures.size() >= 1)
844                                 texturestring = m_prop->textures[0];
845                         texturestring += mod;
846                         m_spritenode->setMaterialTexture(0,
847                                         tsrc->getTextureRaw(texturestring));
848                 }
849                 if(m_meshnode){
850                         for (u32 i = 0; i < 6; ++i)
851                         {
852                                 std::string texturestring = "unknown_block.png";
853                                 if(m_prop->textures.size() > i)
854                                         texturestring = m_prop->textures[i];
855                                 texturestring += mod;
856                                 AtlasPointer ap = tsrc->getTexture(texturestring);
857
858                                 // Get the tile texture and atlas transformation
859                                 video::ITexture* atlas = ap.atlas;
860                                 v2f pos = ap.pos;
861                                 v2f size = ap.size;
862
863                                 // Set material flags and texture
864                                 video::SMaterial& material = m_meshnode->getMaterial(i);
865                                 material.setFlag(video::EMF_LIGHTING, false);
866                                 material.setFlag(video::EMF_BILINEAR_FILTER, false);
867                                 material.setTexture(0, atlas);
868                                 material.getTextureMatrix(0).setTextureTranslate(pos.X, pos.Y);
869                                 material.getTextureMatrix(0).setTextureScale(size.X, size.Y);
870                         }
871                 }
872         }
873
874         void processMessage(const std::string &data)
875         {
876                 //infostream<<"LuaEntityCAO: Got message"<<std::endl;
877                 std::istringstream is(data, std::ios::binary);
878                 // command
879                 u8 cmd = readU8(is);
880                 if(cmd == LUAENTITY_CMD_UPDATE_POSITION) // update position
881                 {
882                         // do_interpolate
883                         bool do_interpolate = readU8(is);
884                         // pos
885                         m_position = readV3F1000(is);
886                         // velocity
887                         m_velocity = readV3F1000(is);
888                         // acceleration
889                         m_acceleration = readV3F1000(is);
890                         // yaw
891                         m_yaw = readF1000(is);
892                         // is_end_position (for interpolation)
893                         bool is_end_position = readU8(is);
894                         // update_interval
895                         float update_interval = readF1000(is);
896                         
897                         if(do_interpolate){
898                                 if(!m_prop->physical)
899                                         pos_translator.update(m_position, is_end_position, update_interval);
900                         } else {
901                                 pos_translator.init(m_position);
902                         }
903                         updateNodePos();
904                 }
905                 else if(cmd == LUAENTITY_CMD_SET_TEXTURE_MOD) // set texture modification
906                 {
907                         std::string mod = deSerializeString(is);
908                         updateTextures(mod);
909                 }
910                 else if(cmd == LUAENTITY_CMD_SET_SPRITE) // set sprite
911                 {
912                         v2s16 p = readV2S16(is);
913                         int num_frames = readU16(is);
914                         float framelength = readF1000(is);
915                         bool select_horiz_by_yawpitch = readU8(is);
916                         
917                         m_tx_basepos = p;
918                         m_anim_num_frames = num_frames;
919                         m_anim_framelength = framelength;
920                         m_tx_select_horiz_by_yawpitch = select_horiz_by_yawpitch;
921
922                         updateTexturePos();
923                 }
924                 else if(cmd == LUAENTITY_CMD_PUNCHED)
925                 {
926                         /*s16 damage =*/ readS16(is);
927                         s16 result_hp = readS16(is);
928                         
929                         m_hp = result_hp;
930                         // TODO: Execute defined fast response
931                 }
932                 else if(cmd == LUAENTITY_CMD_UPDATE_ARMOR_GROUPS)
933                 {
934                         m_armor_groups.clear();
935                         int armor_groups_size = readU16(is);
936                         for(int i=0; i<armor_groups_size; i++){
937                                 std::string name = deSerializeString(is);
938                                 int rating = readS16(is);
939                                 m_armor_groups[name] = rating;
940                         }
941                 }
942         }
943         
944         bool directReportPunch(v3f dir, const ItemStack *punchitem=NULL,
945                         float time_from_last_punch=1000000)
946         {
947                 assert(punchitem);
948                 const ToolCapabilities *toolcap =
949                                 &punchitem->getToolCapabilities(m_gamedef->idef());
950                 PunchDamageResult result = getPunchDamage(
951                                 m_armor_groups,
952                                 toolcap,
953                                 punchitem,
954                                 time_from_last_punch);
955                 
956                 if(result.did_punch && result.damage != 0)
957                 {
958                         if(result.damage < m_hp){
959                                 m_hp -= result.damage;
960                         } else {
961                                 m_hp = 0;
962                                 // TODO: Execute defined fast response
963                                 // As there is no definition, make a smoke puff
964                                 ClientSimpleObject *simple = createSmokePuff(
965                                                 m_smgr, m_env, m_position,
966                                                 m_prop->visual_size * BS);
967                                 m_env->addSimpleObject(simple);
968                         }
969                         // TODO: Execute defined fast response
970                         // Flashing shall suffice as there is no definition
971                         updateTextures("^[brighten");
972                         m_reset_textures_timer = 0.1;
973                 }
974                 
975                 return false;
976         }
977         
978         std::string debugInfoText()
979         {
980                 std::ostringstream os(std::ios::binary);
981                 os<<"LuaEntityCAO hp="<<m_hp<<"\n";
982                 os<<"armor={";
983                 for(ItemGroupList::const_iterator i = m_armor_groups.begin();
984                                 i != m_armor_groups.end(); i++){
985                         os<<i->first<<"="<<i->second<<", ";
986                 }
987                 os<<"}";
988                 return os.str();
989         }
990 };
991
992 // Prototype
993 LuaEntityCAO proto_LuaEntityCAO(NULL, NULL);
994
995 /*
996         PlayerCAO
997 */
998
999 class PlayerCAO : public ClientActiveObject
1000 {
1001 private:
1002         core::aabbox3d<f32> m_selection_box;
1003         scene::IMeshSceneNode *m_node;
1004         scene::ITextSceneNode* m_text;
1005         std::string m_name;
1006         v3f m_position;
1007         float m_yaw;
1008         SmoothTranslator pos_translator;
1009         bool m_is_local_player;
1010         LocalPlayer *m_local_player;
1011         float m_damage_visual_timer;
1012         bool m_dead;
1013         float m_step_distance_counter;
1014         std::string m_wielded_item;
1015
1016 public:
1017         PlayerCAO(IGameDef *gamedef, ClientEnvironment *env):
1018                 ClientActiveObject(0, gamedef, env),
1019                 m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2.0,BS/3.),
1020                 m_node(NULL),
1021                 m_text(NULL),
1022                 m_position(v3f(0,10*BS,0)),
1023                 m_yaw(0),
1024                 m_is_local_player(false),
1025                 m_local_player(NULL),
1026                 m_damage_visual_timer(0),
1027                 m_dead(false),
1028                 m_step_distance_counter(0),
1029                 m_wielded_item("")
1030         {
1031                 if(gamedef == NULL)
1032                         ClientActiveObject::registerType(getType(), create);
1033         }
1034
1035         void initialize(const std::string &data)
1036         {
1037                 infostream<<"PlayerCAO: Got init data"<<std::endl;
1038                 
1039                 std::istringstream is(data, std::ios::binary);
1040                 // version
1041                 u8 version = readU8(is);
1042                 // check version
1043                 if(version != 0)
1044                         return;
1045                 // name
1046                 m_name = deSerializeString(is);
1047                 // pos
1048                 m_position = readV3F1000(is);
1049                 // yaw
1050                 m_yaw = readF1000(is);
1051                 // dead
1052                 m_dead = readU8(is);
1053                 // wielded item
1054                 try{
1055                         m_wielded_item = deSerializeString(is);
1056                 }
1057                 catch(SerializationError &e){}
1058
1059                 pos_translator.init(m_position);
1060
1061                 Player *player = m_env->getPlayer(m_name.c_str());
1062                 if(player && player->isLocal()){
1063                         m_is_local_player = true;
1064                         m_local_player = (LocalPlayer*)player;
1065                 }
1066         }
1067
1068         ~PlayerCAO()
1069         {
1070                 if(m_node)
1071                         m_node->remove();
1072         }
1073
1074         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env)
1075         {
1076                 return new PlayerCAO(gamedef, env);
1077         }
1078
1079         u8 getType() const
1080         {
1081                 return ACTIVEOBJECT_TYPE_PLAYER;
1082         }
1083         core::aabbox3d<f32>* getSelectionBox()
1084         {
1085                 if(m_is_local_player)
1086                         return NULL;
1087                 if(m_dead)
1088                         return NULL;
1089                 return &m_selection_box;
1090         }
1091         v3f getPosition()
1092         {
1093                 return pos_translator.vect_show;
1094         }
1095                 
1096         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
1097                         IrrlichtDevice *irr)
1098         {
1099                 if(m_node != NULL)
1100                         return;
1101                 if(m_is_local_player)
1102                         return;
1103                 
1104                 //video::IVideoDriver* driver = smgr->getVideoDriver();
1105                 gui::IGUIEnvironment* gui = irr->getGUIEnvironment();
1106                 
1107                 scene::SMesh *mesh = new scene::SMesh();
1108                 { // Front
1109                 scene::IMeshBuffer *buf = new scene::SMeshBuffer();
1110                 video::SColor c(255,255,255,255);
1111                 video::S3DVertex vertices[4] =
1112                 {
1113                         video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
1114                         video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
1115                         video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0),
1116                         video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0),
1117                 };
1118                 u16 indices[] = {0,1,2,2,3,0};
1119                 buf->append(vertices, 4, indices, 6);
1120                 // Set material
1121                 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
1122                 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
1123                 buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
1124                 buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
1125                 // Add to mesh
1126                 mesh->addMeshBuffer(buf);
1127                 buf->drop();
1128                 }
1129                 { // Back
1130                 scene::IMeshBuffer *buf = new scene::SMeshBuffer();
1131                 video::SColor c(255,255,255,255);
1132                 video::S3DVertex vertices[4] =
1133                 {
1134                         video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
1135                         video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
1136                         video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0),
1137                         video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0),
1138                 };
1139                 u16 indices[] = {0,1,2,2,3,0};
1140                 buf->append(vertices, 4, indices, 6);
1141                 // Set material
1142                 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
1143                 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
1144                 buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
1145                 buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1146                 // Add to mesh
1147                 mesh->addMeshBuffer(buf);
1148                 buf->drop();
1149                 }
1150                 m_node = smgr->addMeshSceneNode(mesh, NULL);
1151                 mesh->drop();
1152                 // Set it to use the materials of the meshbuffers directly.
1153                 // This is needed for changing the texture in the future
1154                 m_node->setReadOnlyMaterials(true);
1155                 updateNodePos();
1156
1157                 // Add a text node for showing the name
1158                 std::wstring wname = narrow_to_wide(m_name);
1159                 m_text = smgr->addTextSceneNode(gui->getBuiltInFont(),
1160                                 wname.c_str(), video::SColor(255,255,255,255), m_node);
1161                 m_text->setPosition(v3f(0, (f32)BS*2.1, 0));
1162                 
1163                 updateTextures("");
1164                 updateVisibility();
1165                 updateNodePos();
1166         }
1167
1168         void removeFromScene()
1169         {
1170                 if(m_node == NULL)
1171                         return;
1172
1173                 m_node->remove();
1174                 m_node = NULL;
1175         }
1176
1177         void updateLight(u8 light_at_pos)
1178         {
1179                 if(m_node == NULL)
1180                         return;
1181                 
1182                 u8 li = decode_light(light_at_pos);
1183                 video::SColor color(255,li,li,li);
1184                 setMeshColor(m_node->getMesh(), color);
1185
1186                 updateVisibility();
1187         }
1188
1189         v3s16 getLightPosition()
1190         {
1191                 return floatToInt(m_position+v3f(0,BS*1.5,0), BS);
1192         }
1193
1194         void updateVisibility()
1195         {
1196                 if(m_node == NULL)
1197                         return;
1198
1199                 m_node->setVisible(!m_dead);
1200         }
1201
1202         void updateNodePos()
1203         {
1204                 if(m_node == NULL)
1205                         return;
1206
1207                 m_node->setPosition(pos_translator.vect_show);
1208
1209                 v3f rot = m_node->getRotation();
1210                 rot.Y = -m_yaw;
1211                 m_node->setRotation(rot);
1212         }
1213
1214         void step(float dtime, ClientEnvironment *env)
1215         {
1216                 v3f lastpos = pos_translator.vect_show;
1217                 pos_translator.translate(dtime);
1218                 float moved = lastpos.getDistanceFrom(pos_translator.vect_show);
1219                 updateVisibility();
1220                 updateNodePos();
1221
1222                 if(m_damage_visual_timer > 0){
1223                         m_damage_visual_timer -= dtime;
1224                         if(m_damage_visual_timer <= 0){
1225                                 updateTextures("");
1226                         }
1227                 }
1228                 
1229                 m_step_distance_counter += moved;
1230                 if(m_step_distance_counter > 1.5*BS){
1231                         m_step_distance_counter = 0;
1232                         if(!m_is_local_player){
1233                                 INodeDefManager *ndef = m_gamedef->ndef();
1234                                 v3s16 p = floatToInt(getPosition()+v3f(0,-0.5*BS, 0), BS);
1235                                 MapNode n = m_env->getMap().getNodeNoEx(p);
1236                                 SimpleSoundSpec spec = ndef->get(n).sound_footstep;
1237                                 m_gamedef->sound()->playSoundAt(spec, false, getPosition());
1238                         }
1239                 }
1240         }
1241
1242         void processMessage(const std::string &data)
1243         {
1244                 //infostream<<"PlayerCAO: Got message"<<std::endl;
1245                 std::istringstream is(data, std::ios::binary);
1246                 // command
1247                 u8 cmd = readU8(is);
1248                 if(cmd == 0) // update position
1249                 {
1250                         // pos
1251                         m_position = readV3F1000(is);
1252                         // yaw
1253                         m_yaw = readF1000(is);
1254
1255                         pos_translator.update(m_position, false);
1256
1257                         updateNodePos();
1258                 }
1259                 else if(cmd == 1) // punched
1260                 {
1261                         // damage
1262                         s16 damage = readS16(is);
1263                         m_damage_visual_timer = 0.05;
1264                         if(damage >= 2)
1265                                 m_damage_visual_timer += 0.05 * damage;
1266                         updateTextures("^[brighten");
1267                 }
1268                 else if(cmd == 2) // died or respawned
1269                 {
1270                         m_dead = readU8(is);
1271                         updateVisibility();
1272                 }
1273                 else if(cmd == 3) // wielded item
1274                 {
1275                         m_wielded_item = deSerializeString(is);
1276                         updateWieldedItem();
1277                 }
1278         }
1279
1280         void updateTextures(const std::string &mod)
1281         {
1282                 if(!m_node)
1283                         return;
1284                 ITextureSource *tsrc = m_gamedef->tsrc();
1285                 scene::IMesh *mesh = m_node->getMesh();
1286                 if(mesh){
1287                         {
1288                                 std::string tname = "player.png";
1289                                 tname += mod;
1290                                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(0);
1291                                 buf->getMaterial().setTexture(0,
1292                                                 tsrc->getTextureRaw(tname));
1293                         }
1294                         {
1295                                 std::string tname = "player_back.png";
1296                                 tname += mod;
1297                                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(1);
1298                                 buf->getMaterial().setTexture(0,
1299                                                 tsrc->getTextureRaw(tname));
1300                         }
1301                 }
1302         }
1303
1304         void updateWieldedItem()
1305         {
1306                 if(m_is_local_player)
1307                 {
1308                         // ignoring player item for local player
1309                         return;
1310                 }
1311
1312                 ItemStack item;
1313                 try
1314                 {
1315                         item.deSerialize(m_wielded_item, m_gamedef->idef());
1316                 }
1317                 catch(SerializationError &e)
1318                 {
1319                         errorstream<<"PlayerCAO: SerializationError "
1320                                 "while reading wielded item: "
1321                                 <<m_wielded_item<<std::endl;
1322                         return;
1323                 }
1324
1325                 // do something with the item, for example:
1326                 Player *player = m_env->getPlayer(m_name.c_str());
1327                 if(player)
1328                 {
1329                         InventoryList *inv = player->inventory.getList("main");
1330                         assert(inv);
1331                         inv->changeItem(0, item);
1332                 }
1333
1334                 if(item.empty())
1335                 {
1336                         infostream<<"PlayerCAO: empty player item for player "
1337                                         <<m_name<<std::endl;
1338                 }
1339                 else
1340                 {
1341                         infostream<<"PlayerCAO: player item for player "
1342                                         <<m_name<<": "
1343                                         <<item.getItemString()<<std::endl;
1344                 }
1345         }
1346
1347 };
1348
1349 // Prototype
1350 PlayerCAO proto_PlayerCAO(NULL, NULL);
1351
1352 /*
1353         GenericCAO
1354 */
1355
1356 #include "genericobject.h"
1357
1358 class GenericCAO : public ClientActiveObject
1359 {
1360 private:
1361         // Property-ish things
1362         s16 m_hp_max;
1363         bool m_physical;
1364         float m_weight;
1365         core::aabbox3d<f32> m_collisionbox;
1366         std::string m_visual;
1367         v2f m_visual_size;
1368         core::array<std::string> m_textures;
1369         v2s16 m_spritediv;
1370         //
1371         scene::ISceneManager *m_smgr;
1372         core::aabbox3d<f32> m_selection_box;
1373         scene::IMeshSceneNode *m_meshnode;
1374         scene::IBillboardSceneNode *m_spritenode;
1375         v3f m_position;
1376         v3f m_velocity;
1377         v3f m_acceleration;
1378         float m_yaw;
1379         s16 m_hp;
1380         SmoothTranslator pos_translator;
1381         // Spritesheet/animation stuff
1382         v2f m_tx_size;
1383         v2s16 m_tx_basepos;
1384         bool m_tx_select_horiz_by_yawpitch;
1385         int m_anim_frame;
1386         int m_anim_num_frames;
1387         float m_anim_framelength;
1388         float m_anim_timer;
1389         ItemGroupList m_armor_groups;
1390         float m_reset_textures_timer;
1391
1392 public:
1393         GenericCAO(IGameDef *gamedef, ClientEnvironment *env):
1394                 ClientActiveObject(0, gamedef, env),
1395                 //
1396                 m_hp_max(1),
1397                 m_physical(false),
1398                 m_weight(5),
1399                 m_collisionbox(-0.5,-0.5,-0.5, 0.5,0.5,0.5),
1400                 m_visual("sprite"),
1401                 m_visual_size(1,1),
1402                 m_spritediv(1,1),
1403                 //
1404                 m_smgr(NULL),
1405                 m_selection_box(-BS/3.,-BS/3.,-BS/3., BS/3.,BS/3.,BS/3.),
1406                 m_meshnode(NULL),
1407                 m_spritenode(NULL),
1408                 m_position(v3f(0,10*BS,0)),
1409                 m_velocity(v3f(0,0,0)),
1410                 m_acceleration(v3f(0,0,0)),
1411                 m_yaw(0),
1412                 m_hp(1),
1413                 m_tx_size(1,1),
1414                 m_tx_basepos(0,0),
1415                 m_tx_select_horiz_by_yawpitch(false),
1416                 m_anim_frame(0),
1417                 m_anim_num_frames(1),
1418                 m_anim_framelength(0.2),
1419                 m_anim_timer(0),
1420                 m_reset_textures_timer(-1)
1421         {
1422                 m_textures.push_back("unknown_object.png");
1423                 if(gamedef == NULL)
1424                         ClientActiveObject::registerType(getType(), create);
1425         }
1426
1427         void initialize(const std::string &data)
1428         {
1429                 infostream<<"GenericCAO: Got init data"<<std::endl;
1430                 
1431                 std::istringstream is(data, std::ios::binary);
1432                 // version
1433                 u8 version = readU8(is);
1434                 // check version
1435                 if(version != 1)
1436                         return;
1437                 // pos
1438                 m_position = readV3F1000(is);
1439                 // yaw
1440                 m_yaw = readF1000(is);
1441                 // hp
1442                 m_hp = readS16(is);
1443
1444                 pos_translator.init(m_position);
1445
1446                 updateNodePos();
1447         }
1448
1449         ~GenericCAO()
1450         {
1451         }
1452
1453         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env)
1454         {
1455                 return new GenericCAO(gamedef, env);
1456         }
1457
1458         u8 getType() const
1459         {
1460                 return ACTIVEOBJECT_TYPE_GENERIC;
1461         }
1462         core::aabbox3d<f32>* getSelectionBox()
1463         {
1464                 return &m_selection_box;
1465         }
1466         v3f getPosition()
1467         {
1468                 return pos_translator.vect_show;
1469         }
1470
1471         void removeFromScene()
1472         {
1473                 if(m_meshnode){
1474                         m_meshnode->remove();
1475                         m_meshnode = NULL;
1476                 }
1477                 if(m_spritenode){
1478                         m_spritenode->remove();
1479                         m_spritenode = NULL;
1480                 }
1481         }
1482
1483         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
1484                         IrrlichtDevice *irr)
1485         {
1486                 m_smgr = smgr;
1487
1488                 if(m_meshnode != NULL || m_spritenode != NULL)
1489                         return;
1490                 
1491                 //video::IVideoDriver* driver = smgr->getVideoDriver();
1492
1493                 if(m_visual == "sprite"){
1494                         infostream<<"GenericCAO::addToScene(): single_sprite"<<std::endl;
1495                         m_spritenode = smgr->addBillboardSceneNode(
1496                                         NULL, v2f(1, 1), v3f(0,0,0), -1);
1497                         m_spritenode->setMaterialTexture(0,
1498                                         tsrc->getTextureRaw("unknown_block.png"));
1499                         m_spritenode->setMaterialFlag(video::EMF_LIGHTING, false);
1500                         m_spritenode->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
1501                         m_spritenode->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF);
1502                         m_spritenode->setMaterialFlag(video::EMF_FOG_ENABLE, true);
1503                         m_spritenode->setColor(video::SColor(255,0,0,0));
1504                         m_spritenode->setVisible(false); /* Set visible when brightness is known */
1505                         m_spritenode->setSize(m_visual_size*BS);
1506                         {
1507                                 const float txs = 1.0 / 1;
1508                                 const float tys = 1.0 / 1;
1509                                 setBillboardTextureMatrix(m_spritenode,
1510                                                 txs, tys, 0, 0);
1511                         }
1512                 } else if(m_visual == "cube"){
1513                         infostream<<"GenericCAO::addToScene(): cube"<<std::endl;
1514                         scene::IMesh *mesh = createCubeMesh(v3f(BS,BS,BS));
1515                         m_meshnode = smgr->addMeshSceneNode(mesh, NULL);
1516                         mesh->drop();
1517                         
1518                         m_meshnode->setScale(v3f(1));
1519                         // Will be shown when we know the brightness
1520                         m_meshnode->setVisible(false);
1521                 } else {
1522                         infostream<<"GenericCAO::addToScene(): \""<<m_visual
1523                                         <<"\" not supported"<<std::endl;
1524                 }
1525                 updateTextures("");
1526                 updateNodePos();
1527         }
1528
1529         void updateVisuals()
1530         {
1531                 removeFromScene();
1532                 // We haven't got any IrrlichtDevices but it isn't actually needed
1533                 addToScene(m_smgr, m_gamedef->tsrc(), NULL);
1534         }
1535                 
1536         void updateLight(u8 light_at_pos)
1537         {
1538                 bool is_visible = (m_hp != 0);
1539                 u8 li = decode_light(light_at_pos);
1540                 video::SColor color(255,li,li,li);
1541                 if(m_meshnode){
1542                         setMeshColor(m_meshnode->getMesh(), color);
1543                         m_meshnode->setVisible(is_visible);
1544                 }
1545                 if(m_spritenode){
1546                         m_spritenode->setColor(color);
1547                         m_spritenode->setVisible(is_visible);
1548                 }
1549         }
1550
1551         v3s16 getLightPosition()
1552         {
1553                 return floatToInt(m_position, BS);
1554         }
1555
1556         void updateNodePos()
1557         {
1558                 if(m_meshnode){
1559                         m_meshnode->setPosition(pos_translator.vect_show);
1560                 }
1561                 if(m_spritenode){
1562                         m_spritenode->setPosition(pos_translator.vect_show);
1563                 }
1564         }
1565
1566         void step(float dtime, ClientEnvironment *env)
1567         {
1568                 if(m_physical){
1569                         core::aabbox3d<f32> box = m_collisionbox;
1570                         box.MinEdge *= BS;
1571                         box.MaxEdge *= BS;
1572                         collisionMoveResult moveresult;
1573                         f32 pos_max_d = BS*0.25; // Distance per iteration
1574                         v3f p_pos = m_position;
1575                         v3f p_velocity = m_velocity;
1576                         IGameDef *gamedef = env->getGameDef();
1577                         moveresult = collisionMovePrecise(&env->getMap(), gamedef,
1578                                         pos_max_d, box, dtime, p_pos, p_velocity);
1579                         // Apply results
1580                         m_position = p_pos;
1581                         m_velocity = p_velocity;
1582                         
1583                         bool is_end_position = moveresult.collides;
1584                         pos_translator.update(m_position, is_end_position, dtime);
1585                         pos_translator.translate(dtime);
1586                         updateNodePos();
1587
1588                         m_velocity += dtime * m_acceleration;
1589                 } else {
1590                         m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration;
1591                         m_velocity += dtime * m_acceleration;
1592                         pos_translator.update(m_position, pos_translator.aim_is_end, pos_translator.anim_time);
1593                         pos_translator.translate(dtime);
1594                         updateNodePos();
1595                 }
1596
1597                 m_anim_timer += dtime;
1598                 if(m_anim_timer >= m_anim_framelength){
1599                         m_anim_timer -= m_anim_framelength;
1600                         m_anim_frame++;
1601                         if(m_anim_frame >= m_anim_num_frames)
1602                                 m_anim_frame = 0;
1603                 }
1604
1605                 updateTexturePos();
1606
1607                 if(m_reset_textures_timer >= 0){
1608                         m_reset_textures_timer -= dtime;
1609                         if(m_reset_textures_timer <= 0){
1610                                 m_reset_textures_timer = -1;
1611                                 updateTextures("");
1612                         }
1613                 }
1614         }
1615
1616         void updateTexturePos()
1617         {
1618                 if(m_spritenode){
1619                         scene::ICameraSceneNode* camera =
1620                                         m_spritenode->getSceneManager()->getActiveCamera();
1621                         if(!camera)
1622                                 return;
1623                         v3f cam_to_entity = m_spritenode->getAbsolutePosition()
1624                                         - camera->getAbsolutePosition();
1625                         cam_to_entity.normalize();
1626
1627                         int row = m_tx_basepos.Y;
1628                         int col = m_tx_basepos.X;
1629                         
1630                         if(m_tx_select_horiz_by_yawpitch)
1631                         {
1632                                 if(cam_to_entity.Y > 0.75)
1633                                         col += 5;
1634                                 else if(cam_to_entity.Y < -0.75)
1635                                         col += 4;
1636                                 else{
1637                                         float mob_dir = atan2(cam_to_entity.Z, cam_to_entity.X) / PI * 180.;
1638                                         float dir = mob_dir - m_yaw;
1639                                         dir = wrapDegrees_180(dir);
1640                                         //infostream<<"id="<<m_id<<" dir="<<dir<<std::endl;
1641                                         if(fabs(wrapDegrees_180(dir - 0)) <= 45.1)
1642                                                 col += 2;
1643                                         else if(fabs(wrapDegrees_180(dir - 90)) <= 45.1)
1644                                                 col += 3;
1645                                         else if(fabs(wrapDegrees_180(dir - 180)) <= 45.1)
1646                                                 col += 0;
1647                                         else if(fabs(wrapDegrees_180(dir + 90)) <= 45.1)
1648                                                 col += 1;
1649                                         else
1650                                                 col += 4;
1651                                 }
1652                         }
1653                         
1654                         // Animation goes downwards
1655                         row += m_anim_frame;
1656
1657                         float txs = m_tx_size.X;
1658                         float tys = m_tx_size.Y;
1659                         setBillboardTextureMatrix(m_spritenode,
1660                                         txs, tys, col, row);
1661                 }
1662         }
1663
1664         void updateTextures(const std::string &mod)
1665         {
1666                 ITextureSource *tsrc = m_gamedef->tsrc();
1667
1668                 if(m_spritenode){
1669                         std::string texturestring = "unknown_block.png";
1670                         if(m_textures.size() >= 1)
1671                                 texturestring = m_textures[0];
1672                         texturestring += mod;
1673                         m_spritenode->setMaterialTexture(0,
1674                                         tsrc->getTextureRaw(texturestring));
1675                 }
1676                 if(m_meshnode){
1677                         for (u32 i = 0; i < 6; ++i)
1678                         {
1679                                 std::string texturestring = "unknown_block.png";
1680                                 if(m_textures.size() > i)
1681                                         texturestring = m_textures[i];
1682                                 texturestring += mod;
1683                                 AtlasPointer ap = tsrc->getTexture(texturestring);
1684
1685                                 // Get the tile texture and atlas transformation
1686                                 video::ITexture* atlas = ap.atlas;
1687                                 v2f pos = ap.pos;
1688                                 v2f size = ap.size;
1689
1690                                 // Set material flags and texture
1691                                 video::SMaterial& material = m_meshnode->getMaterial(i);
1692                                 material.setFlag(video::EMF_LIGHTING, false);
1693                                 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1694                                 material.setTexture(0, atlas);
1695                                 material.getTextureMatrix(0).setTextureTranslate(pos.X, pos.Y);
1696                                 material.getTextureMatrix(0).setTextureScale(size.X, size.Y);
1697                         }
1698                 }
1699         }
1700
1701         void processMessage(const std::string &data)
1702         {
1703                 //infostream<<"GenericCAO: Got message"<<std::endl;
1704                 std::istringstream is(data, std::ios::binary);
1705                 // command
1706                 u8 cmd = readU8(is);
1707                 if(cmd == GENERIC_CMD_SET_PROPERTIES)
1708                 {
1709                         m_hp_max = readS16(is);
1710                         m_physical = readU8(is);
1711                         m_weight = readF1000(is);
1712                         m_collisionbox.MinEdge = readV3F1000(is);
1713                         m_collisionbox.MaxEdge = readV3F1000(is);
1714                         m_visual = deSerializeString(is);
1715                         m_visual_size = readV2F1000(is);
1716                         m_textures.clear();
1717                         u32 texture_count = readU16(is);
1718                         for(u32 i=0; i<texture_count; i++){
1719                                 m_textures.push_back(deSerializeString(is));
1720                         }
1721                         m_spritediv = readV2S16(is);
1722                         
1723                         m_selection_box = m_collisionbox;
1724                         m_selection_box.MinEdge *= BS;
1725                         m_selection_box.MaxEdge *= BS;
1726                                 
1727                         m_tx_size.X = 1.0 / m_spritediv.X;
1728                         m_tx_size.Y = 1.0 / m_spritediv.Y;
1729                         
1730                         updateVisuals();
1731                 }
1732                 else if(cmd == GENERIC_CMD_UPDATE_POSITION)
1733                 {
1734                         m_position = readV3F1000(is);
1735                         m_velocity = readV3F1000(is);
1736                         m_acceleration = readV3F1000(is);
1737                         m_yaw = readF1000(is);
1738                         bool do_interpolate = readU8(is);
1739                         bool is_end_position = readU8(is);
1740                         float update_interval = readF1000(is);
1741                         
1742                         if(do_interpolate){
1743                                 if(!m_physical)
1744                                         pos_translator.update(m_position, is_end_position, update_interval);
1745                         } else {
1746                                 pos_translator.init(m_position);
1747                         }
1748                         updateNodePos();
1749                 }
1750                 else if(cmd == GENERIC_CMD_SET_TEXTURE_MOD)
1751                 {
1752                         std::string mod = deSerializeString(is);
1753                         updateTextures(mod);
1754                 }
1755                 else if(cmd == GENERIC_CMD_SET_SPRITE)
1756                 {
1757                         v2s16 p = readV2S16(is);
1758                         int num_frames = readU16(is);
1759                         float framelength = readF1000(is);
1760                         bool select_horiz_by_yawpitch = readU8(is);
1761                         
1762                         m_tx_basepos = p;
1763                         m_anim_num_frames = num_frames;
1764                         m_anim_framelength = framelength;
1765                         m_tx_select_horiz_by_yawpitch = select_horiz_by_yawpitch;
1766
1767                         updateTexturePos();
1768                 }
1769                 else if(cmd == GENERIC_CMD_PUNCHED)
1770                 {
1771                         /*s16 damage =*/ readS16(is);
1772                         s16 result_hp = readS16(is);
1773                         
1774                         m_hp = result_hp;
1775                         // TODO: Execute defined fast response
1776                 }
1777                 else if(cmd == GENERIC_CMD_UPDATE_ARMOR_GROUPS)
1778                 {
1779                         m_armor_groups.clear();
1780                         int armor_groups_size = readU16(is);
1781                         for(int i=0; i<armor_groups_size; i++){
1782                                 std::string name = deSerializeString(is);
1783                                 int rating = readS16(is);
1784                                 m_armor_groups[name] = rating;
1785                         }
1786                 }
1787         }
1788         
1789         bool directReportPunch(v3f dir, const ItemStack *punchitem=NULL,
1790                         float time_from_last_punch=1000000)
1791         {
1792                 assert(punchitem);
1793                 const ToolCapabilities *toolcap =
1794                                 &punchitem->getToolCapabilities(m_gamedef->idef());
1795                 PunchDamageResult result = getPunchDamage(
1796                                 m_armor_groups,
1797                                 toolcap,
1798                                 punchitem,
1799                                 time_from_last_punch);
1800                 
1801                 if(result.did_punch && result.damage != 0)
1802                 {
1803                         if(result.damage < m_hp){
1804                                 m_hp -= result.damage;
1805                         } else {
1806                                 m_hp = 0;
1807                                 // TODO: Execute defined fast response
1808                                 // As there is no definition, make a smoke puff
1809                                 ClientSimpleObject *simple = createSmokePuff(
1810                                                 m_smgr, m_env, m_position,
1811                                                 m_visual_size * BS);
1812                                 m_env->addSimpleObject(simple);
1813                         }
1814                         // TODO: Execute defined fast response
1815                         // Flashing shall suffice as there is no definition
1816                         updateTextures("^[brighten");
1817                         m_reset_textures_timer = 0.1;
1818                 }
1819                 
1820                 return false;
1821         }
1822         
1823         std::string debugInfoText()
1824         {
1825                 std::ostringstream os(std::ios::binary);
1826                 os<<"GenericCAO hp="<<m_hp<<"\n";
1827                 os<<"armor={";
1828                 for(ItemGroupList::const_iterator i = m_armor_groups.begin();
1829                                 i != m_armor_groups.end(); i++){
1830                         os<<i->first<<"="<<i->second<<", ";
1831                 }
1832                 os<<"}";
1833                 return os.str();
1834         }
1835 };
1836
1837 // Prototype
1838 GenericCAO proto_GenericCAO(NULL, NULL);
1839
1840