Clean up log messages everywhere
[oweals/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 class Settings;
38 struct ToolCapabilities;
39
40 core::map<u16, ClientActiveObject::Factory> ClientActiveObject::m_types;
41
42 /*
43         SmoothTranslator
44 */
45
46 struct SmoothTranslator
47 {
48         v3f vect_old;
49         v3f vect_show;
50         v3f vect_aim;
51         f32 anim_counter;
52         f32 anim_time;
53         f32 anim_time_counter;
54         bool aim_is_end;
55
56         SmoothTranslator():
57                 vect_old(0,0,0),
58                 vect_show(0,0,0),
59                 vect_aim(0,0,0),
60                 anim_counter(0),
61                 anim_time(0),
62                 anim_time_counter(0),
63                 aim_is_end(true)
64         {}
65
66         void init(v3f vect)
67         {
68                 vect_old = vect;
69                 vect_show = vect;
70                 vect_aim = vect;
71                 anim_counter = 0;
72                 anim_time = 0;
73                 anim_time_counter = 0;
74                 aim_is_end = true;
75         }
76
77         void sharpen()
78         {
79                 init(vect_show);
80         }
81
82         void update(v3f vect_new, bool is_end_position=false, float update_interval=-1)
83         {
84                 aim_is_end = is_end_position;
85                 vect_old = vect_show;
86                 vect_aim = vect_new;
87                 if(update_interval > 0){
88                         anim_time = update_interval;
89                 } else {
90                         if(anim_time < 0.001 || anim_time > 1.0)
91                                 anim_time = anim_time_counter;
92                         else
93                                 anim_time = anim_time * 0.9 + anim_time_counter * 0.1;
94                 }
95                 anim_time_counter = 0;
96                 anim_counter = 0;
97         }
98
99         void translate(f32 dtime)
100         {
101                 anim_time_counter = anim_time_counter + dtime;
102                 anim_counter = anim_counter + dtime;
103                 v3f vect_move = vect_aim - vect_old;
104                 f32 moveratio = 1.0;
105                 if(anim_time > 0.001)
106                         moveratio = anim_time_counter / anim_time;
107                 // Move a bit less than should, to avoid oscillation
108                 moveratio = moveratio * 0.8;
109                 float move_end = 1.5;
110                 if(aim_is_end)
111                         move_end = 1.0;
112                 if(moveratio > move_end)
113                         moveratio = move_end;
114                 vect_show = vect_old + vect_move * moveratio;
115         }
116
117         bool is_moving()
118         {
119                 return ((anim_time_counter / anim_time) < 1.4);
120         }
121 };
122
123 /*
124         Other stuff
125 */
126
127 static void setBillboardTextureMatrix(scene::IBillboardSceneNode *bill,
128                 float txs, float tys, int col, int row)
129 {
130         video::SMaterial& material = bill->getMaterial(0);
131         core::matrix4& matrix = material.getTextureMatrix(0);
132         matrix.setTextureTranslate(txs*col, tys*row);
133         matrix.setTextureScale(txs, tys);
134 }
135
136 /*
137         TestCAO
138 */
139
140 class TestCAO : public ClientActiveObject
141 {
142 public:
143         TestCAO(IGameDef *gamedef, ClientEnvironment *env);
144         virtual ~TestCAO();
145         
146         u8 getType() const
147         {
148                 return ACTIVEOBJECT_TYPE_TEST;
149         }
150         
151         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env);
152
153         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
154                         IrrlichtDevice *irr);
155         void removeFromScene();
156         void updateLight(u8 light_at_pos);
157         v3s16 getLightPosition();
158         void updateNodePos();
159
160         void step(float dtime, ClientEnvironment *env);
161
162         void processMessage(const std::string &data);
163
164 private:
165         scene::IMeshSceneNode *m_node;
166         v3f m_position;
167 };
168
169 // Prototype
170 TestCAO proto_TestCAO(NULL, NULL);
171
172 TestCAO::TestCAO(IGameDef *gamedef, ClientEnvironment *env):
173         ClientActiveObject(0, gamedef, env),
174         m_node(NULL),
175         m_position(v3f(0,10*BS,0))
176 {
177         ClientActiveObject::registerType(getType(), create);
178 }
179
180 TestCAO::~TestCAO()
181 {
182 }
183
184 ClientActiveObject* TestCAO::create(IGameDef *gamedef, ClientEnvironment *env)
185 {
186         return new TestCAO(gamedef, env);
187 }
188
189 void TestCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
190                         IrrlichtDevice *irr)
191 {
192         if(m_node != NULL)
193                 return;
194         
195         //video::IVideoDriver* driver = smgr->getVideoDriver();
196         
197         scene::SMesh *mesh = new scene::SMesh();
198         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
199         video::SColor c(255,255,255,255);
200         video::S3DVertex vertices[4] =
201         {
202                 video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1),
203                 video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1),
204                 video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0),
205                 video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),
206         };
207         u16 indices[] = {0,1,2,2,3,0};
208         buf->append(vertices, 4, indices, 6);
209         // Set material
210         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
211         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
212         buf->getMaterial().setTexture(0, tsrc->getTextureRaw("rat.png"));
213         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
214         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
215         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
216         // Add to mesh
217         mesh->addMeshBuffer(buf);
218         buf->drop();
219         m_node = smgr->addMeshSceneNode(mesh, NULL);
220         mesh->drop();
221         updateNodePos();
222 }
223
224 void TestCAO::removeFromScene()
225 {
226         if(m_node == NULL)
227                 return;
228
229         m_node->remove();
230         m_node = NULL;
231 }
232
233 void TestCAO::updateLight(u8 light_at_pos)
234 {
235 }
236
237 v3s16 TestCAO::getLightPosition()
238 {
239         return floatToInt(m_position, BS);
240 }
241
242 void TestCAO::updateNodePos()
243 {
244         if(m_node == NULL)
245                 return;
246
247         m_node->setPosition(m_position);
248         //m_node->setRotation(v3f(0, 45, 0));
249 }
250
251 void TestCAO::step(float dtime, ClientEnvironment *env)
252 {
253         if(m_node)
254         {
255                 v3f rot = m_node->getRotation();
256                 //infostream<<"dtime="<<dtime<<", rot.Y="<<rot.Y<<std::endl;
257                 rot.Y += dtime * 180;
258                 m_node->setRotation(rot);
259         }
260 }
261
262 void TestCAO::processMessage(const std::string &data)
263 {
264         infostream<<"TestCAO: Got data: "<<data<<std::endl;
265         std::istringstream is(data, std::ios::binary);
266         u16 cmd;
267         is>>cmd;
268         if(cmd == 0)
269         {
270                 v3f newpos;
271                 is>>newpos.X;
272                 is>>newpos.Y;
273                 is>>newpos.Z;
274                 m_position = newpos;
275                 updateNodePos();
276         }
277 }
278
279 /*
280         ItemCAO
281 */
282
283 class ItemCAO : public ClientActiveObject
284 {
285 public:
286         ItemCAO(IGameDef *gamedef, ClientEnvironment *env);
287         virtual ~ItemCAO();
288         
289         u8 getType() const
290         {
291                 return ACTIVEOBJECT_TYPE_ITEM;
292         }
293         
294         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env);
295
296         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
297                         IrrlichtDevice *irr);
298         void removeFromScene();
299         void updateLight(u8 light_at_pos);
300         v3s16 getLightPosition();
301         void updateNodePos();
302         void updateInfoText();
303         void updateTexture();
304
305         void step(float dtime, ClientEnvironment *env);
306
307         void processMessage(const std::string &data);
308
309         void initialize(const std::string &data);
310         
311         core::aabbox3d<f32>* getSelectionBox()
312                 {return &m_selection_box;}
313         v3f getPosition()
314                 {return m_position;}
315         
316         std::string infoText()
317                 {return m_infotext;}
318
319 private:
320         core::aabbox3d<f32> m_selection_box;
321         scene::IMeshSceneNode *m_node;
322         v3f m_position;
323         std::string m_itemstring;
324         std::string m_infotext;
325 };
326
327 #include "inventory.h"
328
329 // Prototype
330 ItemCAO proto_ItemCAO(NULL, NULL);
331
332 ItemCAO::ItemCAO(IGameDef *gamedef, ClientEnvironment *env):
333         ClientActiveObject(0, gamedef, env),
334         m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.),
335         m_node(NULL),
336         m_position(v3f(0,10*BS,0))
337 {
338         if(!gamedef && !env)
339         {
340                 ClientActiveObject::registerType(getType(), create);
341         }
342 }
343
344 ItemCAO::~ItemCAO()
345 {
346 }
347
348 ClientActiveObject* ItemCAO::create(IGameDef *gamedef, ClientEnvironment *env)
349 {
350         return new ItemCAO(gamedef, env);
351 }
352
353 void ItemCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
354                         IrrlichtDevice *irr)
355 {
356         if(m_node != NULL)
357                 return;
358         
359         //video::IVideoDriver* driver = smgr->getVideoDriver();
360         
361         scene::SMesh *mesh = new scene::SMesh();
362         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
363         video::SColor c(255,255,255,255);
364         video::S3DVertex vertices[4] =
365         {
366                 /*video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1),
367                 video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1),
368                 video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0),
369                 video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),*/
370                 video::S3DVertex(BS/3.,0,0, 0,0,0, c, 0,1),
371                 video::S3DVertex(-BS/3.,0,0, 0,0,0, c, 1,1),
372                 video::S3DVertex(-BS/3.,0+BS*2./3.,0, 0,0,0, c, 1,0),
373                 video::S3DVertex(BS/3.,0+BS*2./3.,0, 0,0,0, c, 0,0),
374         };
375         u16 indices[] = {0,1,2,2,3,0};
376         buf->append(vertices, 4, indices, 6);
377         // Set material
378         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
379         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
380         // Initialize with a generated placeholder texture
381         buf->getMaterial().setTexture(0, tsrc->getTextureRaw(""));
382         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
383         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
384         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
385         // Add to mesh
386         mesh->addMeshBuffer(buf);
387         buf->drop();
388         m_node = smgr->addMeshSceneNode(mesh, NULL);
389         mesh->drop();
390         updateNodePos();
391
392         /*
393                 Update image of node
394         */
395
396         updateTexture();
397 }
398
399 void ItemCAO::removeFromScene()
400 {
401         if(m_node == NULL)
402                 return;
403
404         m_node->remove();
405         m_node = NULL;
406 }
407
408 void ItemCAO::updateLight(u8 light_at_pos)
409 {
410         if(m_node == NULL)
411                 return;
412
413         u8 li = decode_light(light_at_pos);
414         video::SColor color(255,li,li,li);
415         setMeshColor(m_node->getMesh(), color);
416 }
417
418 v3s16 ItemCAO::getLightPosition()
419 {
420         return floatToInt(m_position + v3f(0,0.5*BS,0), BS);
421 }
422
423 void ItemCAO::updateNodePos()
424 {
425         if(m_node == NULL)
426                 return;
427
428         m_node->setPosition(m_position);
429 }
430
431 void ItemCAO::updateInfoText()
432 {
433         try{
434                 IItemDefManager *idef = m_gamedef->idef();
435                 ItemStack item;
436                 item.deSerialize(m_itemstring, idef);
437                 if(item.isKnown(idef))
438                         m_infotext = item.getDefinition(idef).description;
439                 else
440                         m_infotext = "Unknown item: '" + m_itemstring + "'";
441                 if(item.count >= 2)
442                         m_infotext += " (" + itos(item.count) + ")";
443         }
444         catch(SerializationError &e)
445         {
446                 m_infotext = "Unknown item: '" + m_itemstring + "'";
447         }
448 }
449
450 void ItemCAO::updateTexture()
451 {
452         if(m_node == NULL)
453                 return;
454
455         // Create an inventory item to see what is its image
456         std::istringstream is(m_itemstring, std::ios_base::binary);
457         video::ITexture *texture = NULL;
458         try{
459                 IItemDefManager *idef = m_gamedef->idef();
460                 ItemStack item;
461                 item.deSerialize(is, idef);
462                 texture = item.getDefinition(idef).inventory_texture;
463         }
464         catch(SerializationError &e)
465         {
466                 infostream<<"WARNING: "<<__FUNCTION_NAME
467                                 <<": error deSerializing itemstring \""
468                                 <<m_itemstring<<std::endl;
469         }
470         
471         // Set meshbuffer texture
472         m_node->getMaterial(0).setTexture(0, texture);
473 }
474
475
476 void ItemCAO::step(float dtime, ClientEnvironment *env)
477 {
478         if(m_node)
479         {
480                 /*v3f rot = m_node->getRotation();
481                 rot.Y += dtime * 120;
482                 m_node->setRotation(rot);*/
483                 LocalPlayer *player = env->getLocalPlayer();
484                 assert(player);
485                 v3f rot = m_node->getRotation();
486                 rot.Y = 180.0 - (player->getYaw());
487                 m_node->setRotation(rot);
488         }
489 }
490
491 void ItemCAO::processMessage(const std::string &data)
492 {
493         //infostream<<"ItemCAO: Got message"<<std::endl;
494         std::istringstream is(data, std::ios::binary);
495         // command
496         u8 cmd = readU8(is);
497         if(cmd == 0)
498         {
499                 // pos
500                 m_position = readV3F1000(is);
501                 updateNodePos();
502         }
503         if(cmd == 1)
504         {
505                 // itemstring
506                 m_itemstring = deSerializeString(is);
507                 updateInfoText();
508                 updateTexture();
509         }
510 }
511
512 void ItemCAO::initialize(const std::string &data)
513 {
514         infostream<<"ItemCAO: Got init data"<<std::endl;
515         
516         {
517                 std::istringstream is(data, std::ios::binary);
518                 // version
519                 u8 version = readU8(is);
520                 // check version
521                 if(version != 0)
522                         return;
523                 // pos
524                 m_position = readV3F1000(is);
525                 // itemstring
526                 m_itemstring = deSerializeString(is);
527         }
528         
529         updateNodePos();
530         updateInfoText();
531 }
532
533 /*
534         LuaEntityCAO
535 */
536
537 #include "luaentity_common.h"
538
539 class LuaEntityCAO : public ClientActiveObject
540 {
541 private:
542         scene::ISceneManager *m_smgr;
543         core::aabbox3d<f32> m_selection_box;
544         scene::IMeshSceneNode *m_meshnode;
545         scene::IBillboardSceneNode *m_spritenode;
546         v3f m_position;
547         v3f m_velocity;
548         v3f m_acceleration;
549         float m_yaw;
550         s16 m_hp;
551         struct LuaEntityProperties *m_prop;
552         SmoothTranslator pos_translator;
553         // Spritesheet/animation stuff
554         v2f m_tx_size;
555         v2s16 m_tx_basepos;
556         bool m_tx_select_horiz_by_yawpitch;
557         int m_anim_frame;
558         int m_anim_num_frames;
559         float m_anim_framelength;
560         float m_anim_timer;
561         ItemGroupList m_armor_groups;
562         float m_reset_textures_timer;
563
564 public:
565         LuaEntityCAO(IGameDef *gamedef, ClientEnvironment *env):
566                 ClientActiveObject(0, gamedef, env),
567                 m_smgr(NULL),
568                 m_selection_box(-BS/3.,-BS/3.,-BS/3., BS/3.,BS/3.,BS/3.),
569                 m_meshnode(NULL),
570                 m_spritenode(NULL),
571                 m_position(v3f(0,10*BS,0)),
572                 m_velocity(v3f(0,0,0)),
573                 m_acceleration(v3f(0,0,0)),
574                 m_yaw(0),
575                 m_hp(1),
576                 m_prop(new LuaEntityProperties),
577                 m_tx_size(1,1),
578                 m_tx_basepos(0,0),
579                 m_tx_select_horiz_by_yawpitch(false),
580                 m_anim_frame(0),
581                 m_anim_num_frames(1),
582                 m_anim_framelength(0.2),
583                 m_anim_timer(0),
584                 m_reset_textures_timer(-1)
585         {
586                 if(gamedef == NULL)
587                         ClientActiveObject::registerType(getType(), create);
588         }
589
590         void initialize(const std::string &data)
591         {
592                 infostream<<"LuaEntityCAO: Got init data"<<std::endl;
593                 
594                 std::istringstream is(data, std::ios::binary);
595                 // version
596                 u8 version = readU8(is);
597                 // check version
598                 if(version != 1)
599                         return;
600                 // pos
601                 m_position = readV3F1000(is);
602                 // yaw
603                 m_yaw = readF1000(is);
604                 // hp
605                 m_hp = readS16(is);
606                 // properties
607                 std::istringstream prop_is(deSerializeLongString(is), std::ios::binary);
608                 m_prop->deSerialize(prop_is);
609
610                 infostream<<"m_prop: "<<m_prop->dump()<<std::endl;
611
612                 m_selection_box = m_prop->collisionbox;
613                 m_selection_box.MinEdge *= BS;
614                 m_selection_box.MaxEdge *= BS;
615                         
616                 pos_translator.init(m_position);
617
618                 m_tx_size.X = 1.0 / m_prop->spritediv.X;
619                 m_tx_size.Y = 1.0 / m_prop->spritediv.Y;
620                 m_tx_basepos.X = m_tx_size.X * m_prop->initial_sprite_basepos.X;
621                 m_tx_basepos.Y = m_tx_size.Y * m_prop->initial_sprite_basepos.Y;
622                 
623                 updateNodePos();
624         }
625
626         ~LuaEntityCAO()
627         {
628                 delete m_prop;
629         }
630
631         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env)
632         {
633                 return new LuaEntityCAO(gamedef, env);
634         }
635
636         u8 getType() const
637         {
638                 return ACTIVEOBJECT_TYPE_LUAENTITY;
639         }
640         core::aabbox3d<f32>* getSelectionBox()
641         {
642                 return &m_selection_box;
643         }
644         v3f getPosition()
645         {
646                 return pos_translator.vect_show;
647         }
648                 
649         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
650                         IrrlichtDevice *irr)
651         {
652                 m_smgr = smgr;
653
654                 if(m_meshnode != NULL || m_spritenode != NULL)
655                         return;
656                 
657                 //video::IVideoDriver* driver = smgr->getVideoDriver();
658
659                 if(m_prop->visual == "sprite"){
660                         infostream<<"LuaEntityCAO::addToScene(): single_sprite"<<std::endl;
661                         m_spritenode = smgr->addBillboardSceneNode(
662                                         NULL, v2f(1, 1), v3f(0,0,0), -1);
663                         m_spritenode->setMaterialTexture(0,
664                                         tsrc->getTextureRaw("unknown_block.png"));
665                         m_spritenode->setMaterialFlag(video::EMF_LIGHTING, false);
666                         m_spritenode->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
667                         m_spritenode->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF);
668                         m_spritenode->setMaterialFlag(video::EMF_FOG_ENABLE, true);
669                         m_spritenode->setColor(video::SColor(255,0,0,0));
670                         m_spritenode->setVisible(false); /* Set visible when brightness is known */
671                         m_spritenode->setSize(m_prop->visual_size*BS);
672                         {
673                                 const float txs = 1.0 / 1;
674                                 const float tys = 1.0 / 1;
675                                 setBillboardTextureMatrix(m_spritenode,
676                                                 txs, tys, 0, 0);
677                         }
678                 } else if(m_prop->visual == "cube"){
679                         infostream<<"LuaEntityCAO::addToScene(): cube"<<std::endl;
680                         scene::IMesh *mesh = createCubeMesh(v3f(BS,BS,BS));
681                         m_meshnode = smgr->addMeshSceneNode(mesh, NULL);
682                         mesh->drop();
683                         
684                         m_meshnode->setScale(v3f(1));
685                         // Will be shown when we know the brightness
686                         m_meshnode->setVisible(false);
687                 } else {
688                         infostream<<"LuaEntityCAO::addToScene(): \""<<m_prop->visual
689                                         <<"\" not supported"<<std::endl;
690                 }
691                 updateTextures("");
692                 updateNodePos();
693         }
694
695         void removeFromScene()
696         {
697                 if(m_meshnode){
698                         m_meshnode->remove();
699                         m_meshnode = NULL;
700                 }
701                 if(m_spritenode){
702                         m_spritenode->remove();
703                         m_spritenode = NULL;
704                 }
705         }
706
707         void updateLight(u8 light_at_pos)
708         {
709                 bool is_visible = (m_hp != 0);
710                 u8 li = decode_light(light_at_pos);
711                 video::SColor color(255,li,li,li);
712                 if(m_meshnode){
713                         setMeshColor(m_meshnode->getMesh(), color);
714                         m_meshnode->setVisible(is_visible);
715                 }
716                 if(m_spritenode){
717                         m_spritenode->setColor(color);
718                         m_spritenode->setVisible(is_visible);
719                 }
720         }
721
722         v3s16 getLightPosition()
723         {
724                 return floatToInt(m_position, BS);
725         }
726
727         void updateNodePos()
728         {
729                 if(m_meshnode){
730                         m_meshnode->setPosition(pos_translator.vect_show);
731                 }
732                 if(m_spritenode){
733                         m_spritenode->setPosition(pos_translator.vect_show);
734                 }
735         }
736
737         void step(float dtime, ClientEnvironment *env)
738         {
739                 if(m_prop->physical){
740                         core::aabbox3d<f32> box = m_prop->collisionbox;
741                         box.MinEdge *= BS;
742                         box.MaxEdge *= BS;
743                         collisionMoveResult moveresult;
744                         f32 pos_max_d = BS*0.25; // Distance per iteration
745                         v3f p_pos = m_position;
746                         v3f p_velocity = m_velocity;
747                         IGameDef *gamedef = env->getGameDef();
748                         moveresult = collisionMovePrecise(&env->getMap(), gamedef,
749                                         pos_max_d, box, dtime, p_pos, p_velocity);
750                         // Apply results
751                         m_position = p_pos;
752                         m_velocity = p_velocity;
753                         
754                         bool is_end_position = moveresult.collides;
755                         pos_translator.update(m_position, is_end_position, dtime);
756                         pos_translator.translate(dtime);
757                         updateNodePos();
758
759                         m_velocity += dtime * m_acceleration;
760                 } else {
761                         m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration;
762                         m_velocity += dtime * m_acceleration;
763                         pos_translator.update(m_position, pos_translator.aim_is_end, pos_translator.anim_time);
764                         pos_translator.translate(dtime);
765                         updateNodePos();
766                 }
767
768                 m_anim_timer += dtime;
769                 if(m_anim_timer >= m_anim_framelength){
770                         m_anim_timer -= m_anim_framelength;
771                         m_anim_frame++;
772                         if(m_anim_frame >= m_anim_num_frames)
773                                 m_anim_frame = 0;
774                 }
775
776                 updateTexturePos();
777
778                 if(m_reset_textures_timer >= 0){
779                         m_reset_textures_timer -= dtime;
780                         if(m_reset_textures_timer <= 0){
781                                 m_reset_textures_timer = -1;
782                                 updateTextures("");
783                         }
784                 }
785         }
786
787         void updateTexturePos()
788         {
789                 if(m_spritenode){
790                         scene::ICameraSceneNode* camera =
791                                         m_spritenode->getSceneManager()->getActiveCamera();
792                         if(!camera)
793                                 return;
794                         v3f cam_to_entity = m_spritenode->getAbsolutePosition()
795                                         - camera->getAbsolutePosition();
796                         cam_to_entity.normalize();
797
798                         int row = m_tx_basepos.Y;
799                         int col = m_tx_basepos.X;
800                         
801                         if(m_tx_select_horiz_by_yawpitch)
802                         {
803                                 if(cam_to_entity.Y > 0.75)
804                                         col += 5;
805                                 else if(cam_to_entity.Y < -0.75)
806                                         col += 4;
807                                 else{
808                                         float mob_dir = atan2(cam_to_entity.Z, cam_to_entity.X) / PI * 180.;
809                                         float dir = mob_dir - m_yaw;
810                                         dir = wrapDegrees_180(dir);
811                                         //infostream<<"id="<<m_id<<" dir="<<dir<<std::endl;
812                                         if(fabs(wrapDegrees_180(dir - 0)) <= 45.1)
813                                                 col += 2;
814                                         else if(fabs(wrapDegrees_180(dir - 90)) <= 45.1)
815                                                 col += 3;
816                                         else if(fabs(wrapDegrees_180(dir - 180)) <= 45.1)
817                                                 col += 0;
818                                         else if(fabs(wrapDegrees_180(dir + 90)) <= 45.1)
819                                                 col += 1;
820                                         else
821                                                 col += 4;
822                                 }
823                         }
824                         
825                         // Animation goes downwards
826                         row += m_anim_frame;
827
828                         float txs = m_tx_size.X;
829                         float tys = m_tx_size.Y;
830                         setBillboardTextureMatrix(m_spritenode,
831                                         txs, tys, col, row);
832                 }
833         }
834
835         void updateTextures(const std::string &mod)
836         {
837                 ITextureSource *tsrc = m_gamedef->tsrc();
838
839                 if(m_spritenode){
840                         std::string texturestring = "unknown_block.png";
841                         if(m_prop->textures.size() >= 1)
842                                 texturestring = m_prop->textures[0];
843                         texturestring += mod;
844                         m_spritenode->setMaterialTexture(0,
845                                         tsrc->getTextureRaw(texturestring));
846                 }
847                 if(m_meshnode){
848                         for (u32 i = 0; i < 6; ++i)
849                         {
850                                 std::string texturestring = "unknown_block.png";
851                                 if(m_prop->textures.size() > i)
852                                         texturestring = m_prop->textures[i];
853                                 texturestring += mod;
854                                 AtlasPointer ap = tsrc->getTexture(texturestring);
855
856                                 // Get the tile texture and atlas transformation
857                                 video::ITexture* atlas = ap.atlas;
858                                 v2f pos = ap.pos;
859                                 v2f size = ap.size;
860
861                                 // Set material flags and texture
862                                 video::SMaterial& material = m_meshnode->getMaterial(i);
863                                 material.setFlag(video::EMF_LIGHTING, false);
864                                 material.setFlag(video::EMF_BILINEAR_FILTER, false);
865                                 material.setTexture(0, atlas);
866                                 material.getTextureMatrix(0).setTextureTranslate(pos.X, pos.Y);
867                                 material.getTextureMatrix(0).setTextureScale(size.X, size.Y);
868                         }
869                 }
870         }
871
872         void processMessage(const std::string &data)
873         {
874                 //infostream<<"LuaEntityCAO: Got message"<<std::endl;
875                 std::istringstream is(data, std::ios::binary);
876                 // command
877                 u8 cmd = readU8(is);
878                 if(cmd == LUAENTITY_CMD_UPDATE_POSITION) // update position
879                 {
880                         // do_interpolate
881                         bool do_interpolate = readU8(is);
882                         // pos
883                         m_position = readV3F1000(is);
884                         // velocity
885                         m_velocity = readV3F1000(is);
886                         // acceleration
887                         m_acceleration = readV3F1000(is);
888                         // yaw
889                         m_yaw = readF1000(is);
890                         // is_end_position (for interpolation)
891                         bool is_end_position = readU8(is);
892                         // update_interval
893                         float update_interval = readF1000(is);
894                         
895                         if(do_interpolate){
896                                 if(!m_prop->physical)
897                                         pos_translator.update(m_position, is_end_position, update_interval);
898                         } else {
899                                 pos_translator.init(m_position);
900                         }
901                         updateNodePos();
902                 }
903                 else if(cmd == LUAENTITY_CMD_SET_TEXTURE_MOD) // set texture modification
904                 {
905                         std::string mod = deSerializeString(is);
906                         updateTextures(mod);
907                 }
908                 else if(cmd == LUAENTITY_CMD_SET_SPRITE) // set sprite
909                 {
910                         v2s16 p = readV2S16(is);
911                         int num_frames = readU16(is);
912                         float framelength = readF1000(is);
913                         bool select_horiz_by_yawpitch = readU8(is);
914                         
915                         m_tx_basepos = p;
916                         m_anim_num_frames = num_frames;
917                         m_anim_framelength = framelength;
918                         m_tx_select_horiz_by_yawpitch = select_horiz_by_yawpitch;
919
920                         updateTexturePos();
921                 }
922                 else if(cmd == LUAENTITY_CMD_PUNCHED)
923                 {
924                         /*s16 damage =*/ readS16(is);
925                         s16 result_hp = readS16(is);
926                         
927                         m_hp = result_hp;
928                         // TODO: Execute defined fast response
929                 }
930                 else if(cmd == LUAENTITY_CMD_UPDATE_ARMOR_GROUPS)
931                 {
932                         m_armor_groups.clear();
933                         int armor_groups_size = readU16(is);
934                         for(int i=0; i<armor_groups_size; i++){
935                                 std::string name = deSerializeString(is);
936                                 int rating = readS16(is);
937                                 m_armor_groups[name] = rating;
938                         }
939                 }
940         }
941         
942         bool directReportPunch(v3f dir, const ItemStack *punchitem=NULL,
943                         float time_from_last_punch=1000000)
944         {
945                 assert(punchitem);
946                 const ToolCapabilities *toolcap =
947                                 &punchitem->getToolCapabilities(m_gamedef->idef());
948                 PunchDamageResult result = getPunchDamage(
949                                 m_armor_groups,
950                                 toolcap,
951                                 punchitem,
952                                 time_from_last_punch);
953                 
954                 if(result.did_punch && result.damage != 0)
955                 {
956                         if(result.damage < m_hp){
957                                 m_hp -= result.damage;
958                         } else {
959                                 m_hp = 0;
960                                 // TODO: Execute defined fast response
961                                 // As there is no definition, make a smoke puff
962                                 ClientSimpleObject *simple = createSmokePuff(
963                                                 m_smgr, m_env, m_position,
964                                                 m_prop->visual_size * BS);
965                                 m_env->addSimpleObject(simple);
966                         }
967                         // TODO: Execute defined fast response
968                         // Flashing shall suffice as there is no definition
969                         updateTextures("^[brighten");
970                         m_reset_textures_timer = 0.1;
971                 }
972                 
973                 return false;
974         }
975         
976         std::string debugInfoText()
977         {
978                 std::ostringstream os(std::ios::binary);
979                 os<<"LuaEntityCAO hp="<<m_hp<<"\n";
980                 os<<"armor={";
981                 for(ItemGroupList::const_iterator i = m_armor_groups.begin();
982                                 i != m_armor_groups.end(); i++){
983                         os<<i->first<<"="<<i->second<<", ";
984                 }
985                 os<<"}";
986                 return os.str();
987         }
988 };
989
990 // Prototype
991 LuaEntityCAO proto_LuaEntityCAO(NULL, NULL);
992
993 /*
994         PlayerCAO
995 */
996
997 class PlayerCAO : public ClientActiveObject
998 {
999 private:
1000         core::aabbox3d<f32> m_selection_box;
1001         scene::IMeshSceneNode *m_node;
1002         scene::ITextSceneNode* m_text;
1003         std::string m_name;
1004         v3f m_position;
1005         float m_yaw;
1006         SmoothTranslator pos_translator;
1007         bool m_is_local_player;
1008         LocalPlayer *m_local_player;
1009         float m_damage_visual_timer;
1010         bool m_dead;
1011
1012 public:
1013         PlayerCAO(IGameDef *gamedef, ClientEnvironment *env):
1014                 ClientActiveObject(0, gamedef, env),
1015                 m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2.0,BS/3.),
1016                 m_node(NULL),
1017                 m_text(NULL),
1018                 m_position(v3f(0,10*BS,0)),
1019                 m_yaw(0),
1020                 m_is_local_player(false),
1021                 m_local_player(NULL),
1022                 m_damage_visual_timer(0),
1023                 m_dead(false)
1024         {
1025                 if(gamedef == NULL)
1026                         ClientActiveObject::registerType(getType(), create);
1027         }
1028
1029         void initialize(const std::string &data)
1030         {
1031                 infostream<<"PlayerCAO: Got init data"<<std::endl;
1032                 
1033                 std::istringstream is(data, std::ios::binary);
1034                 // version
1035                 u8 version = readU8(is);
1036                 // check version
1037                 if(version != 0)
1038                         return;
1039                 // name
1040                 m_name = deSerializeString(is);
1041                 // pos
1042                 m_position = readV3F1000(is);
1043                 // yaw
1044                 m_yaw = readF1000(is);
1045                 // dead
1046                 m_dead = readU8(is);
1047
1048                 pos_translator.init(m_position);
1049
1050                 Player *player = m_env->getPlayer(m_name.c_str());
1051                 if(player && player->isLocal()){
1052                         m_is_local_player = true;
1053                         m_local_player = (LocalPlayer*)player;
1054                 }
1055         }
1056
1057         ~PlayerCAO()
1058         {
1059                 if(m_node)
1060                         m_node->remove();
1061         }
1062
1063         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env)
1064         {
1065                 return new PlayerCAO(gamedef, env);
1066         }
1067
1068         u8 getType() const
1069         {
1070                 return ACTIVEOBJECT_TYPE_PLAYER;
1071         }
1072         core::aabbox3d<f32>* getSelectionBox()
1073         {
1074                 if(m_is_local_player)
1075                         return NULL;
1076                 if(m_dead)
1077                         return NULL;
1078                 return &m_selection_box;
1079         }
1080         v3f getPosition()
1081         {
1082                 return pos_translator.vect_show;
1083         }
1084                 
1085         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
1086                         IrrlichtDevice *irr)
1087         {
1088                 if(m_node != NULL)
1089                         return;
1090                 if(m_is_local_player)
1091                         return;
1092                 
1093                 //video::IVideoDriver* driver = smgr->getVideoDriver();
1094                 gui::IGUIEnvironment* gui = irr->getGUIEnvironment();
1095                 
1096                 scene::SMesh *mesh = new scene::SMesh();
1097                 { // Front
1098                 scene::IMeshBuffer *buf = new scene::SMeshBuffer();
1099                 video::SColor c(255,255,255,255);
1100                 video::S3DVertex vertices[4] =
1101                 {
1102                         video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
1103                         video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
1104                         video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0),
1105                         video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0),
1106                 };
1107                 u16 indices[] = {0,1,2,2,3,0};
1108                 buf->append(vertices, 4, indices, 6);
1109                 // Set material
1110                 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
1111                 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
1112                 buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
1113                 buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
1114                 // Add to mesh
1115                 mesh->addMeshBuffer(buf);
1116                 buf->drop();
1117                 }
1118                 { // Back
1119                 scene::IMeshBuffer *buf = new scene::SMeshBuffer();
1120                 video::SColor c(255,255,255,255);
1121                 video::S3DVertex vertices[4] =
1122                 {
1123                         video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
1124                         video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
1125                         video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0),
1126                         video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0),
1127                 };
1128                 u16 indices[] = {0,1,2,2,3,0};
1129                 buf->append(vertices, 4, indices, 6);
1130                 // Set material
1131                 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
1132                 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
1133                 buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
1134                 buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1135                 // Add to mesh
1136                 mesh->addMeshBuffer(buf);
1137                 buf->drop();
1138                 }
1139                 m_node = smgr->addMeshSceneNode(mesh, NULL);
1140                 mesh->drop();
1141                 // Set it to use the materials of the meshbuffers directly.
1142                 // This is needed for changing the texture in the future
1143                 m_node->setReadOnlyMaterials(true);
1144                 updateNodePos();
1145
1146                 // Add a text node for showing the name
1147                 std::wstring wname = narrow_to_wide(m_name);
1148                 m_text = smgr->addTextSceneNode(gui->getBuiltInFont(),
1149                                 wname.c_str(), video::SColor(255,255,255,255), m_node);
1150                 m_text->setPosition(v3f(0, (f32)BS*2.1, 0));
1151                 
1152                 updateTextures("");
1153                 updateVisibility();
1154                 updateNodePos();
1155         }
1156
1157         void removeFromScene()
1158         {
1159                 if(m_node == NULL)
1160                         return;
1161
1162                 m_node->remove();
1163                 m_node = NULL;
1164         }
1165
1166         void updateLight(u8 light_at_pos)
1167         {
1168                 if(m_node == NULL)
1169                         return;
1170                 
1171                 u8 li = decode_light(light_at_pos);
1172                 video::SColor color(255,li,li,li);
1173                 setMeshColor(m_node->getMesh(), color);
1174
1175                 updateVisibility();
1176         }
1177
1178         v3s16 getLightPosition()
1179         {
1180                 return floatToInt(m_position+v3f(0,BS*1.5,0), BS);
1181         }
1182
1183         void updateVisibility()
1184         {
1185                 if(m_node == NULL)
1186                         return;
1187
1188                 m_node->setVisible(!m_dead);
1189         }
1190
1191         void updateNodePos()
1192         {
1193                 if(m_node == NULL)
1194                         return;
1195
1196                 m_node->setPosition(pos_translator.vect_show);
1197
1198                 v3f rot = m_node->getRotation();
1199                 rot.Y = -m_yaw;
1200                 m_node->setRotation(rot);
1201         }
1202
1203         void step(float dtime, ClientEnvironment *env)
1204         {
1205                 pos_translator.translate(dtime);
1206                 updateVisibility();
1207                 updateNodePos();
1208
1209                 if(m_damage_visual_timer > 0){
1210                         m_damage_visual_timer -= dtime;
1211                         if(m_damage_visual_timer <= 0){
1212                                 updateTextures("");
1213                         }
1214                 }
1215         }
1216
1217         void processMessage(const std::string &data)
1218         {
1219                 //infostream<<"PlayerCAO: Got message"<<std::endl;
1220                 std::istringstream is(data, std::ios::binary);
1221                 // command
1222                 u8 cmd = readU8(is);
1223                 if(cmd == 0) // update position
1224                 {
1225                         // pos
1226                         m_position = readV3F1000(is);
1227                         // yaw
1228                         m_yaw = readF1000(is);
1229
1230                         pos_translator.update(m_position, false);
1231
1232                         updateNodePos();
1233                 }
1234                 else if(cmd == 1) // punched
1235                 {
1236                         // damage
1237                         s16 damage = readS16(is);
1238                         m_damage_visual_timer = 0.05;
1239                         if(damage >= 2)
1240                                 m_damage_visual_timer += 0.05 * damage;
1241                         updateTextures("^[brighten");
1242                 }
1243                 else if(cmd == 2) // died or respawned
1244                 {
1245                         m_dead = readU8(is);
1246                         updateVisibility();
1247                 }
1248         }
1249
1250         void updateTextures(const std::string &mod)
1251         {
1252                 if(!m_node)
1253                         return;
1254                 ITextureSource *tsrc = m_gamedef->tsrc();
1255                 scene::IMesh *mesh = m_node->getMesh();
1256                 if(mesh){
1257                         {
1258                                 std::string tname = "player.png";
1259                                 tname += mod;
1260                                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(0);
1261                                 buf->getMaterial().setTexture(0,
1262                                                 tsrc->getTextureRaw(tname));
1263                         }
1264                         {
1265                                 std::string tname = "player_back.png";
1266                                 tname += mod;
1267                                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(1);
1268                                 buf->getMaterial().setTexture(0,
1269                                                 tsrc->getTextureRaw(tname));
1270                         }
1271                 }
1272         }
1273 };
1274
1275 // Prototype
1276 PlayerCAO proto_PlayerCAO(NULL, NULL);
1277
1278