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