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