]> git.lizzy.rs Git - minetest.git/blob - src/content_cao.cpp
Tune generation responsiveness and cheat inhibition on server
[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
1015 public:
1016         PlayerCAO(IGameDef *gamedef, ClientEnvironment *env):
1017                 ClientActiveObject(0, gamedef, env),
1018                 m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2.0,BS/3.),
1019                 m_node(NULL),
1020                 m_text(NULL),
1021                 m_position(v3f(0,10*BS,0)),
1022                 m_yaw(0),
1023                 m_is_local_player(false),
1024                 m_local_player(NULL),
1025                 m_damage_visual_timer(0),
1026                 m_dead(false),
1027                 m_step_distance_counter(0)
1028         {
1029                 if(gamedef == NULL)
1030                         ClientActiveObject::registerType(getType(), create);
1031         }
1032
1033         void initialize(const std::string &data)
1034         {
1035                 infostream<<"PlayerCAO: Got init data"<<std::endl;
1036                 
1037                 std::istringstream is(data, std::ios::binary);
1038                 // version
1039                 u8 version = readU8(is);
1040                 // check version
1041                 if(version != 0)
1042                         return;
1043                 // name
1044                 m_name = deSerializeString(is);
1045                 // pos
1046                 m_position = readV3F1000(is);
1047                 // yaw
1048                 m_yaw = readF1000(is);
1049                 // dead
1050                 m_dead = readU8(is);
1051
1052                 pos_translator.init(m_position);
1053
1054                 Player *player = m_env->getPlayer(m_name.c_str());
1055                 if(player && player->isLocal()){
1056                         m_is_local_player = true;
1057                         m_local_player = (LocalPlayer*)player;
1058                 }
1059         }
1060
1061         ~PlayerCAO()
1062         {
1063                 if(m_node)
1064                         m_node->remove();
1065         }
1066
1067         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env)
1068         {
1069                 return new PlayerCAO(gamedef, env);
1070         }
1071
1072         u8 getType() const
1073         {
1074                 return ACTIVEOBJECT_TYPE_PLAYER;
1075         }
1076         core::aabbox3d<f32>* getSelectionBox()
1077         {
1078                 if(m_is_local_player)
1079                         return NULL;
1080                 if(m_dead)
1081                         return NULL;
1082                 return &m_selection_box;
1083         }
1084         v3f getPosition()
1085         {
1086                 return pos_translator.vect_show;
1087         }
1088                 
1089         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
1090                         IrrlichtDevice *irr)
1091         {
1092                 if(m_node != NULL)
1093                         return;
1094                 if(m_is_local_player)
1095                         return;
1096                 
1097                 //video::IVideoDriver* driver = smgr->getVideoDriver();
1098                 gui::IGUIEnvironment* gui = irr->getGUIEnvironment();
1099                 
1100                 scene::SMesh *mesh = new scene::SMesh();
1101                 { // Front
1102                 scene::IMeshBuffer *buf = new scene::SMeshBuffer();
1103                 video::SColor c(255,255,255,255);
1104                 video::S3DVertex vertices[4] =
1105                 {
1106                         video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
1107                         video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
1108                         video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0),
1109                         video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0),
1110                 };
1111                 u16 indices[] = {0,1,2,2,3,0};
1112                 buf->append(vertices, 4, indices, 6);
1113                 // Set material
1114                 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
1115                 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
1116                 buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
1117                 buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
1118                 // Add to mesh
1119                 mesh->addMeshBuffer(buf);
1120                 buf->drop();
1121                 }
1122                 { // Back
1123                 scene::IMeshBuffer *buf = new scene::SMeshBuffer();
1124                 video::SColor c(255,255,255,255);
1125                 video::S3DVertex vertices[4] =
1126                 {
1127                         video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
1128                         video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
1129                         video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0),
1130                         video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0),
1131                 };
1132                 u16 indices[] = {0,1,2,2,3,0};
1133                 buf->append(vertices, 4, indices, 6);
1134                 // Set material
1135                 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
1136                 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
1137                 buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
1138                 buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1139                 // Add to mesh
1140                 mesh->addMeshBuffer(buf);
1141                 buf->drop();
1142                 }
1143                 m_node = smgr->addMeshSceneNode(mesh, NULL);
1144                 mesh->drop();
1145                 // Set it to use the materials of the meshbuffers directly.
1146                 // This is needed for changing the texture in the future
1147                 m_node->setReadOnlyMaterials(true);
1148                 updateNodePos();
1149
1150                 // Add a text node for showing the name
1151                 std::wstring wname = narrow_to_wide(m_name);
1152                 m_text = smgr->addTextSceneNode(gui->getBuiltInFont(),
1153                                 wname.c_str(), video::SColor(255,255,255,255), m_node);
1154                 m_text->setPosition(v3f(0, (f32)BS*2.1, 0));
1155                 
1156                 updateTextures("");
1157                 updateVisibility();
1158                 updateNodePos();
1159         }
1160
1161         void removeFromScene()
1162         {
1163                 if(m_node == NULL)
1164                         return;
1165
1166                 m_node->remove();
1167                 m_node = NULL;
1168         }
1169
1170         void updateLight(u8 light_at_pos)
1171         {
1172                 if(m_node == NULL)
1173                         return;
1174                 
1175                 u8 li = decode_light(light_at_pos);
1176                 video::SColor color(255,li,li,li);
1177                 setMeshColor(m_node->getMesh(), color);
1178
1179                 updateVisibility();
1180         }
1181
1182         v3s16 getLightPosition()
1183         {
1184                 return floatToInt(m_position+v3f(0,BS*1.5,0), BS);
1185         }
1186
1187         void updateVisibility()
1188         {
1189                 if(m_node == NULL)
1190                         return;
1191
1192                 m_node->setVisible(!m_dead);
1193         }
1194
1195         void updateNodePos()
1196         {
1197                 if(m_node == NULL)
1198                         return;
1199
1200                 m_node->setPosition(pos_translator.vect_show);
1201
1202                 v3f rot = m_node->getRotation();
1203                 rot.Y = -m_yaw;
1204                 m_node->setRotation(rot);
1205         }
1206
1207         void step(float dtime, ClientEnvironment *env)
1208         {
1209                 v3f lastpos = pos_translator.vect_show;
1210                 pos_translator.translate(dtime);
1211                 float moved = lastpos.getDistanceFrom(pos_translator.vect_show);
1212                 updateVisibility();
1213                 updateNodePos();
1214
1215                 if(m_damage_visual_timer > 0){
1216                         m_damage_visual_timer -= dtime;
1217                         if(m_damage_visual_timer <= 0){
1218                                 updateTextures("");
1219                         }
1220                 }
1221                 
1222                 m_step_distance_counter += moved;
1223                 if(m_step_distance_counter > 1.5*BS){
1224                         m_step_distance_counter = 0;
1225                         if(!m_is_local_player){
1226                                 INodeDefManager *ndef = m_gamedef->ndef();
1227                                 v3s16 p = floatToInt(getPosition()+v3f(0,-0.5*BS, 0), BS);
1228                                 MapNode n = m_env->getMap().getNodeNoEx(p);
1229                                 SimpleSoundSpec spec = ndef->get(n).sound_footstep;
1230                                 m_gamedef->sound()->playSoundAt(spec, false, getPosition());
1231                         }
1232                 }
1233         }
1234
1235         void processMessage(const std::string &data)
1236         {
1237                 //infostream<<"PlayerCAO: Got message"<<std::endl;
1238                 std::istringstream is(data, std::ios::binary);
1239                 // command
1240                 u8 cmd = readU8(is);
1241                 if(cmd == 0) // update position
1242                 {
1243                         // pos
1244                         m_position = readV3F1000(is);
1245                         // yaw
1246                         m_yaw = readF1000(is);
1247
1248                         pos_translator.update(m_position, false);
1249
1250                         updateNodePos();
1251                 }
1252                 else if(cmd == 1) // punched
1253                 {
1254                         // damage
1255                         s16 damage = readS16(is);
1256                         m_damage_visual_timer = 0.05;
1257                         if(damage >= 2)
1258                                 m_damage_visual_timer += 0.05 * damage;
1259                         updateTextures("^[brighten");
1260                 }
1261                 else if(cmd == 2) // died or respawned
1262                 {
1263                         m_dead = readU8(is);
1264                         updateVisibility();
1265                 }
1266         }
1267
1268         void updateTextures(const std::string &mod)
1269         {
1270                 if(!m_node)
1271                         return;
1272                 ITextureSource *tsrc = m_gamedef->tsrc();
1273                 scene::IMesh *mesh = m_node->getMesh();
1274                 if(mesh){
1275                         {
1276                                 std::string tname = "player.png";
1277                                 tname += mod;
1278                                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(0);
1279                                 buf->getMaterial().setTexture(0,
1280                                                 tsrc->getTextureRaw(tname));
1281                         }
1282                         {
1283                                 std::string tname = "player_back.png";
1284                                 tname += mod;
1285                                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(1);
1286                                 buf->getMaterial().setTexture(0,
1287                                                 tsrc->getTextureRaw(tname));
1288                         }
1289                 }
1290         }
1291 };
1292
1293 // Prototype
1294 PlayerCAO proto_PlayerCAO(NULL, NULL);
1295
1296