]> git.lizzy.rs Git - dragonfireclient.git/blob - src/content_cao.cpp
EnvRef:find_nodes_in_area(minp, maxp, nodenames)
[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 #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(m_prop.visual_size.X,
785                                         m_prop.visual_size.Y,
786                                         m_prop.visual_size.X));
787                         u8 li = m_last_light;
788                         setMeshColor(m_meshnode->getMesh(), video::SColor(255,li,li,li));
789                 } else if(m_prop.visual == "wielditem"){
790                         infostream<<"GenericCAO::addToScene(): node"<<std::endl;
791                         infostream<<"textures: "<<m_prop.textures.size()<<std::endl;
792                         if(m_prop.textures.size() >= 1){
793                                 infostream<<"textures[0]: "<<m_prop.textures[0]<<std::endl;
794                                 IItemDefManager *idef = m_gamedef->idef();
795                                 ItemStack item(m_prop.textures[0], 1, 0, "", idef);
796                                 scene::IMesh *mesh = item.getDefinition(idef).wield_mesh;
797                                 m_meshnode = smgr->addMeshSceneNode(mesh, NULL);
798                                 
799                                 m_meshnode->setScale(v3f(m_prop.visual_size.X/2,
800                                                 m_prop.visual_size.Y/2,
801                                                 m_prop.visual_size.X/2));
802                                 u8 li = m_last_light;
803                                 setMeshColor(m_meshnode->getMesh(), video::SColor(255,li,li,li));
804                         }
805                 } else {
806                         infostream<<"GenericCAO::addToScene(): \""<<m_prop.visual
807                                         <<"\" not supported"<<std::endl;
808                 }
809                 updateTextures("");
810                 
811                 scene::ISceneNode *node = NULL;
812                 if(m_spritenode)
813                         node = m_spritenode;
814                 else if(m_meshnode)
815                         node = m_meshnode;
816                 if(node && m_is_player && !m_is_local_player){
817                         // Add a text node for showing the name
818                         gui::IGUIEnvironment* gui = irr->getGUIEnvironment();
819                         std::wstring wname = narrow_to_wide(m_name);
820                         m_textnode = smgr->addTextSceneNode(gui->getBuiltInFont(),
821                                         wname.c_str(), video::SColor(255,255,255,255), node);
822                         m_textnode->setPosition(v3f(0, BS*1.1, 0));
823                 }
824                 
825                 updateNodePos();
826         }
827
828         void expireVisuals()
829         {
830                 m_visuals_expired = true;
831         }
832                 
833         void updateLight(u8 light_at_pos)
834         {
835                 bool is_visible = (m_hp != 0);
836                 u8 li = decode_light(light_at_pos);
837                 m_last_light = li;
838                 video::SColor color(255,li,li,li);
839                 if(m_meshnode){
840                         setMeshColor(m_meshnode->getMesh(), color);
841                         m_meshnode->setVisible(is_visible);
842                 }
843                 if(m_spritenode){
844                         m_spritenode->setColor(color);
845                         m_spritenode->setVisible(is_visible);
846                 }
847         }
848
849         v3s16 getLightPosition()
850         {
851                 return floatToInt(m_position, BS);
852         }
853
854         void updateNodePos()
855         {
856                 if(m_meshnode){
857                         m_meshnode->setPosition(pos_translator.vect_show);
858                         v3f rot = m_meshnode->getRotation();
859                         rot.Y = -m_yaw;
860                         m_meshnode->setRotation(rot);
861                 }
862                 if(m_spritenode){
863                         m_spritenode->setPosition(pos_translator.vect_show);
864                 }
865         }
866
867         void step(float dtime, ClientEnvironment *env)
868         {
869                 v3f lastpos = pos_translator.vect_show;
870
871                 if(m_visuals_expired && m_smgr && m_irr){
872                         m_visuals_expired = false;
873                         removeFromScene();
874                         addToScene(m_smgr, m_gamedef->tsrc(), m_irr);
875                 }
876
877                 if(m_prop.physical){
878                         core::aabbox3d<f32> box = m_prop.collisionbox;
879                         box.MinEdge *= BS;
880                         box.MaxEdge *= BS;
881                         collisionMoveResult moveresult;
882                         f32 pos_max_d = BS*0.25; // Distance per iteration
883                         v3f p_pos = m_position;
884                         v3f p_velocity = m_velocity;
885                         IGameDef *gamedef = env->getGameDef();
886                         moveresult = collisionMovePrecise(&env->getMap(), gamedef,
887                                         pos_max_d, box, dtime, p_pos, p_velocity);
888                         // Apply results
889                         m_position = p_pos;
890                         m_velocity = p_velocity;
891                         
892                         bool is_end_position = moveresult.collides;
893                         pos_translator.update(m_position, is_end_position, dtime);
894                         pos_translator.translate(dtime);
895                         updateNodePos();
896
897                         m_velocity += dtime * m_acceleration;
898                 } else {
899                         m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration;
900                         m_velocity += dtime * m_acceleration;
901                         pos_translator.update(m_position, pos_translator.aim_is_end, pos_translator.anim_time);
902                         pos_translator.translate(dtime);
903                         updateNodePos();
904                 }
905
906                 float moved = lastpos.getDistanceFrom(pos_translator.vect_show);
907                 m_step_distance_counter += moved;
908                 if(m_step_distance_counter > 1.5*BS){
909                         m_step_distance_counter = 0;
910                         if(!m_is_local_player && m_prop.makes_footstep_sound){
911                                 INodeDefManager *ndef = m_gamedef->ndef();
912                                 v3s16 p = floatToInt(getPosition() + v3f(0,
913                                                 (m_prop.collisionbox.MinEdge.Y-0.5)*BS, 0), BS);
914                                 MapNode n = m_env->getMap().getNodeNoEx(p);
915                                 SimpleSoundSpec spec = ndef->get(n).sound_footstep;
916                                 m_gamedef->sound()->playSoundAt(spec, false, getPosition());
917                         }
918                 }
919
920                 m_anim_timer += dtime;
921                 if(m_anim_timer >= m_anim_framelength){
922                         m_anim_timer -= m_anim_framelength;
923                         m_anim_frame++;
924                         if(m_anim_frame >= m_anim_num_frames)
925                                 m_anim_frame = 0;
926                 }
927
928                 updateTexturePos();
929
930                 if(m_reset_textures_timer >= 0){
931                         m_reset_textures_timer -= dtime;
932                         if(m_reset_textures_timer <= 0){
933                                 m_reset_textures_timer = -1;
934                                 updateTextures("");
935                         }
936                 }
937                 if(fabs(m_prop.automatic_rotate) > 0.001){
938                         m_yaw += dtime * m_prop.automatic_rotate * 180 / PI;
939                         updateNodePos();
940                 }
941         }
942
943         void updateTexturePos()
944         {
945                 if(m_spritenode){
946                         scene::ICameraSceneNode* camera =
947                                         m_spritenode->getSceneManager()->getActiveCamera();
948                         if(!camera)
949                                 return;
950                         v3f cam_to_entity = m_spritenode->getAbsolutePosition()
951                                         - camera->getAbsolutePosition();
952                         cam_to_entity.normalize();
953
954                         int row = m_tx_basepos.Y;
955                         int col = m_tx_basepos.X;
956                         
957                         if(m_tx_select_horiz_by_yawpitch)
958                         {
959                                 if(cam_to_entity.Y > 0.75)
960                                         col += 5;
961                                 else if(cam_to_entity.Y < -0.75)
962                                         col += 4;
963                                 else{
964                                         float mob_dir = atan2(cam_to_entity.Z, cam_to_entity.X) / PI * 180.;
965                                         float dir = mob_dir - m_yaw;
966                                         dir = wrapDegrees_180(dir);
967                                         //infostream<<"id="<<m_id<<" dir="<<dir<<std::endl;
968                                         if(fabs(wrapDegrees_180(dir - 0)) <= 45.1)
969                                                 col += 2;
970                                         else if(fabs(wrapDegrees_180(dir - 90)) <= 45.1)
971                                                 col += 3;
972                                         else if(fabs(wrapDegrees_180(dir - 180)) <= 45.1)
973                                                 col += 0;
974                                         else if(fabs(wrapDegrees_180(dir + 90)) <= 45.1)
975                                                 col += 1;
976                                         else
977                                                 col += 4;
978                                 }
979                         }
980                         
981                         // Animation goes downwards
982                         row += m_anim_frame;
983
984                         float txs = m_tx_size.X;
985                         float tys = m_tx_size.Y;
986                         setBillboardTextureMatrix(m_spritenode,
987                                         txs, tys, col, row);
988                 }
989         }
990
991         void updateTextures(const std::string &mod)
992         {
993                 ITextureSource *tsrc = m_gamedef->tsrc();
994
995                 if(m_spritenode)
996                 {
997                         if(m_prop.visual == "sprite")
998                         {
999                                 std::string texturestring = "unknown_block.png";
1000                                 if(m_prop.textures.size() >= 1)
1001                                         texturestring = m_prop.textures[0];
1002                                 texturestring += mod;
1003                                 m_spritenode->setMaterialTexture(0,
1004                                                 tsrc->getTextureRaw(texturestring));
1005                         }
1006                 }
1007                 if(m_meshnode)
1008                 {
1009                         if(m_prop.visual == "cube")
1010                         {
1011                                 for (u32 i = 0; i < 6; ++i)
1012                                 {
1013                                         std::string texturestring = "unknown_block.png";
1014                                         if(m_prop.textures.size() > i)
1015                                                 texturestring = m_prop.textures[i];
1016                                         texturestring += mod;
1017                                         AtlasPointer ap = tsrc->getTexture(texturestring);
1018
1019                                         // Get the tile texture and atlas transformation
1020                                         video::ITexture* atlas = ap.atlas;
1021                                         v2f pos = ap.pos;
1022                                         v2f size = ap.size;
1023
1024                                         // Set material flags and texture
1025                                         video::SMaterial& material = m_meshnode->getMaterial(i);
1026                                         material.setFlag(video::EMF_LIGHTING, false);
1027                                         material.setFlag(video::EMF_BILINEAR_FILTER, false);
1028                                         material.setTexture(0, atlas);
1029                                         material.getTextureMatrix(0).setTextureTranslate(pos.X, pos.Y);
1030                                         material.getTextureMatrix(0).setTextureScale(size.X, size.Y);
1031                                 }
1032                         }
1033                         else if(m_prop.visual == "upright_sprite")
1034                         {
1035                                 scene::IMesh *mesh = m_meshnode->getMesh();
1036                                 {
1037                                         std::string tname = "unknown_object.png";
1038                                         if(m_prop.textures.size() >= 1)
1039                                                 tname = m_prop.textures[0];
1040                                         tname += mod;
1041                                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(0);
1042                                         buf->getMaterial().setTexture(0,
1043                                                         tsrc->getTextureRaw(tname));
1044                                 }
1045                                 {
1046                                         std::string tname = "unknown_object.png";
1047                                         if(m_prop.textures.size() >= 2)
1048                                                 tname = m_prop.textures[1];
1049                                         else if(m_prop.textures.size() >= 1)
1050                                                 tname = m_prop.textures[0];
1051                                         tname += mod;
1052                                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(1);
1053                                         buf->getMaterial().setTexture(0,
1054                                                         tsrc->getTextureRaw(tname));
1055                                 }
1056                         }
1057                 }
1058         }
1059
1060         void processMessage(const std::string &data)
1061         {
1062                 //infostream<<"GenericCAO: Got message"<<std::endl;
1063                 std::istringstream is(data, std::ios::binary);
1064                 // command
1065                 u8 cmd = readU8(is);
1066                 if(cmd == GENERIC_CMD_SET_PROPERTIES)
1067                 {
1068                         m_prop = gob_read_set_properties(is);
1069
1070                         m_selection_box = m_prop.collisionbox;
1071                         m_selection_box.MinEdge *= BS;
1072                         m_selection_box.MaxEdge *= BS;
1073                                 
1074                         m_tx_size.X = 1.0 / m_prop.spritediv.X;
1075                         m_tx_size.Y = 1.0 / m_prop.spritediv.Y;
1076
1077                         if(!m_initial_tx_basepos_set){
1078                                 m_initial_tx_basepos_set = true;
1079                                 m_tx_basepos = m_prop.initial_sprite_basepos;
1080                         }
1081                         
1082                         expireVisuals();
1083                 }
1084                 else if(cmd == GENERIC_CMD_UPDATE_POSITION)
1085                 {
1086                         m_position = readV3F1000(is);
1087                         m_velocity = readV3F1000(is);
1088                         m_acceleration = readV3F1000(is);
1089                         if(fabs(m_prop.automatic_rotate) < 0.001)
1090                                 m_yaw = readF1000(is);
1091                         bool do_interpolate = readU8(is);
1092                         bool is_end_position = readU8(is);
1093                         float update_interval = readF1000(is);
1094
1095                         // Place us a bit higher if we're physical, to not sink into
1096                         // the ground due to sucky collision detection...
1097                         if(m_prop.physical)
1098                                 m_position += v3f(0,0.002,0);
1099                         
1100                         if(do_interpolate){
1101                                 if(!m_prop.physical)
1102                                         pos_translator.update(m_position, is_end_position, update_interval);
1103                         } else {
1104                                 pos_translator.init(m_position);
1105                         }
1106                         updateNodePos();
1107                 }
1108                 else if(cmd == GENERIC_CMD_SET_TEXTURE_MOD)
1109                 {
1110                         std::string mod = deSerializeString(is);
1111                         updateTextures(mod);
1112                 }
1113                 else if(cmd == GENERIC_CMD_SET_SPRITE)
1114                 {
1115                         v2s16 p = readV2S16(is);
1116                         int num_frames = readU16(is);
1117                         float framelength = readF1000(is);
1118                         bool select_horiz_by_yawpitch = readU8(is);
1119                         
1120                         m_tx_basepos = p;
1121                         m_anim_num_frames = num_frames;
1122                         m_anim_framelength = framelength;
1123                         m_tx_select_horiz_by_yawpitch = select_horiz_by_yawpitch;
1124
1125                         updateTexturePos();
1126                 }
1127                 else if(cmd == GENERIC_CMD_PUNCHED)
1128                 {
1129                         /*s16 damage =*/ readS16(is);
1130                         s16 result_hp = readS16(is);
1131                         
1132                         m_hp = result_hp;
1133                 }
1134                 else if(cmd == GENERIC_CMD_UPDATE_ARMOR_GROUPS)
1135                 {
1136                         m_armor_groups.clear();
1137                         int armor_groups_size = readU16(is);
1138                         for(int i=0; i<armor_groups_size; i++){
1139                                 std::string name = deSerializeString(is);
1140                                 int rating = readS16(is);
1141                                 m_armor_groups[name] = rating;
1142                         }
1143                 }
1144         }
1145         
1146         bool directReportPunch(v3f dir, const ItemStack *punchitem=NULL,
1147                         float time_from_last_punch=1000000)
1148         {
1149                 assert(punchitem);
1150                 const ToolCapabilities *toolcap =
1151                                 &punchitem->getToolCapabilities(m_gamedef->idef());
1152                 PunchDamageResult result = getPunchDamage(
1153                                 m_armor_groups,
1154                                 toolcap,
1155                                 punchitem,
1156                                 time_from_last_punch);
1157
1158                 if(result.did_punch && result.damage != 0)
1159                 {
1160                         if(result.damage < m_hp){
1161                                 m_hp -= result.damage;
1162                         } else {
1163                                 m_hp = 0;
1164                                 // TODO: Execute defined fast response
1165                                 // As there is no definition, make a smoke puff
1166                                 ClientSimpleObject *simple = createSmokePuff(
1167                                                 m_smgr, m_env, m_position,
1168                                                 m_prop.visual_size * BS);
1169                                 m_env->addSimpleObject(simple);
1170                         }
1171                         // TODO: Execute defined fast response
1172                         // Flashing shall suffice as there is no definition
1173                         m_reset_textures_timer = 0.05;
1174                         if(result.damage >= 2)
1175                                 m_reset_textures_timer += 0.05 * result.damage;
1176                         updateTextures("^[brighten");
1177                 }
1178                 
1179                 return false;
1180         }
1181         
1182         std::string debugInfoText()
1183         {
1184                 std::ostringstream os(std::ios::binary);
1185                 os<<"GenericCAO hp="<<m_hp<<"\n";
1186                 os<<"armor={";
1187                 for(ItemGroupList::const_iterator i = m_armor_groups.begin();
1188                                 i != m_armor_groups.end(); i++){
1189                         os<<i->first<<"="<<i->second<<", ";
1190                 }
1191                 os<<"}";
1192                 return os.str();
1193         }
1194 };
1195
1196 // Prototype
1197 GenericCAO proto_GenericCAO(NULL, NULL);
1198
1199