Damage groups WIP
[oweals/minetest.git] / src / content_cao.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "content_cao.h"
21 #include "tile.h"
22 #include "environment.h"
23 #include "collision.h"
24 #include "settings.h"
25 #include <ICameraSceneNode.h>
26 #include <ITextSceneNode.h>
27 #include <IBillboardSceneNode.h>
28 #include "serialization.h" // For decompressZlib
29 #include "gamedef.h"
30 #include "clientobject.h"
31 #include "content_object.h"
32 #include "mesh.h"
33 #include "utility.h" // For IntervalLimiter
34 #include "itemdef.h"
35 #include "tool.h"
36 class Settings;
37 struct ToolCapabilities;
38
39 core::map<u16, ClientActiveObject::Factory> ClientActiveObject::m_types;
40
41 /*
42         SmoothTranslator
43 */
44
45 struct SmoothTranslator
46 {
47         v3f vect_old;
48         v3f vect_show;
49         v3f vect_aim;
50         f32 anim_counter;
51         f32 anim_time;
52         f32 anim_time_counter;
53         bool aim_is_end;
54
55         SmoothTranslator():
56                 vect_old(0,0,0),
57                 vect_show(0,0,0),
58                 vect_aim(0,0,0),
59                 anim_counter(0),
60                 anim_time(0),
61                 anim_time_counter(0),
62                 aim_is_end(true)
63         {}
64
65         void init(v3f vect)
66         {
67                 vect_old = vect;
68                 vect_show = vect;
69                 vect_aim = vect;
70                 anim_counter = 0;
71                 anim_time = 0;
72                 anim_time_counter = 0;
73                 aim_is_end = true;
74         }
75
76         void sharpen()
77         {
78                 init(vect_show);
79         }
80
81         void update(v3f vect_new, bool is_end_position=false, float update_interval=-1)
82         {
83                 aim_is_end = is_end_position;
84                 vect_old = vect_show;
85                 vect_aim = vect_new;
86                 if(update_interval > 0){
87                         anim_time = update_interval;
88                 } else {
89                         if(anim_time < 0.001 || anim_time > 1.0)
90                                 anim_time = anim_time_counter;
91                         else
92                                 anim_time = anim_time * 0.9 + anim_time_counter * 0.1;
93                 }
94                 anim_time_counter = 0;
95                 anim_counter = 0;
96         }
97
98         void translate(f32 dtime)
99         {
100                 anim_time_counter = anim_time_counter + dtime;
101                 anim_counter = anim_counter + dtime;
102                 v3f vect_move = vect_aim - vect_old;
103                 f32 moveratio = 1.0;
104                 if(anim_time > 0.001)
105                         moveratio = anim_time_counter / anim_time;
106                 // Move a bit less than should, to avoid oscillation
107                 moveratio = moveratio * 0.8;
108                 float move_end = 1.5;
109                 if(aim_is_end)
110                         move_end = 1.0;
111                 if(moveratio > move_end)
112                         moveratio = move_end;
113                 vect_show = vect_old + vect_move * moveratio;
114         }
115
116         bool is_moving()
117         {
118                 return ((anim_time_counter / anim_time) < 1.4);
119         }
120 };
121
122 /*
123         Other stuff
124 */
125
126 static void setBillboardTextureMatrix(scene::IBillboardSceneNode *bill,
127                 float txs, float tys, int col, int row)
128 {
129         video::SMaterial& material = bill->getMaterial(0);
130         core::matrix4& matrix = material.getTextureMatrix(0);
131         matrix.setTextureTranslate(txs*col, tys*row);
132         matrix.setTextureScale(txs, tys);
133 }
134
135 /*
136         TestCAO
137 */
138
139 class TestCAO : public ClientActiveObject
140 {
141 public:
142         TestCAO(IGameDef *gamedef, ClientEnvironment *env);
143         virtual ~TestCAO();
144         
145         u8 getType() const
146         {
147                 return ACTIVEOBJECT_TYPE_TEST;
148         }
149         
150         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env);
151
152         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
153                         IrrlichtDevice *irr);
154         void removeFromScene();
155         void updateLight(u8 light_at_pos);
156         v3s16 getLightPosition();
157         void updateNodePos();
158
159         void step(float dtime, ClientEnvironment *env);
160
161         void processMessage(const std::string &data);
162
163 private:
164         scene::IMeshSceneNode *m_node;
165         v3f m_position;
166 };
167
168 /*
169         ItemCAO
170 */
171
172 class ItemCAO : public ClientActiveObject
173 {
174 public:
175         ItemCAO(IGameDef *gamedef, ClientEnvironment *env);
176         virtual ~ItemCAO();
177         
178         u8 getType() const
179         {
180                 return ACTIVEOBJECT_TYPE_ITEM;
181         }
182         
183         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env);
184
185         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
186                         IrrlichtDevice *irr);
187         void removeFromScene();
188         void updateLight(u8 light_at_pos);
189         v3s16 getLightPosition();
190         void updateNodePos();
191         void updateInfoText();
192         void updateTexture();
193
194         void step(float dtime, ClientEnvironment *env);
195
196         void processMessage(const std::string &data);
197
198         void initialize(const std::string &data);
199         
200         core::aabbox3d<f32>* getSelectionBox()
201                 {return &m_selection_box;}
202         v3f getPosition()
203                 {return m_position;}
204         
205         std::string infoText()
206                 {return m_infotext;}
207
208 private:
209         core::aabbox3d<f32> m_selection_box;
210         scene::IMeshSceneNode *m_node;
211         v3f m_position;
212         std::string m_itemstring;
213         std::string m_infotext;
214 };
215
216 /*
217         LuaEntityCAO
218 */
219
220 #include "luaentity_common.h"
221
222 class LuaEntityCAO : public ClientActiveObject
223 {
224 private:
225         core::aabbox3d<f32> m_selection_box;
226         scene::IMeshSceneNode *m_meshnode;
227         scene::IBillboardSceneNode *m_spritenode;
228         v3f m_position;
229         v3f m_velocity;
230         v3f m_acceleration;
231         float m_yaw;
232         s16 m_hp;
233         struct LuaEntityProperties *m_prop;
234         SmoothTranslator pos_translator;
235         // Spritesheet/animation stuff
236         v2f m_tx_size;
237         v2s16 m_tx_basepos;
238         bool m_tx_select_horiz_by_yawpitch;
239         int m_anim_frame;
240         int m_anim_num_frames;
241         float m_anim_framelength;
242         float m_anim_timer;
243
244 public:
245         LuaEntityCAO(IGameDef *gamedef, ClientEnvironment *env):
246                 ClientActiveObject(0, gamedef, env),
247                 m_selection_box(-BS/3.,-BS/3.,-BS/3., BS/3.,BS/3.,BS/3.),
248                 m_meshnode(NULL),
249                 m_spritenode(NULL),
250                 m_position(v3f(0,10*BS,0)),
251                 m_velocity(v3f(0,0,0)),
252                 m_acceleration(v3f(0,0,0)),
253                 m_yaw(0),
254                 m_hp(1),
255                 m_prop(new LuaEntityProperties),
256                 m_tx_size(1,1),
257                 m_tx_basepos(0,0),
258                 m_tx_select_horiz_by_yawpitch(false),
259                 m_anim_frame(0),
260                 m_anim_num_frames(1),
261                 m_anim_framelength(0.2),
262                 m_anim_timer(0)
263         {
264                 if(gamedef == NULL)
265                         ClientActiveObject::registerType(getType(), create);
266         }
267
268         void initialize(const std::string &data)
269         {
270                 infostream<<"LuaEntityCAO: Got init data"<<std::endl;
271                 
272                 std::istringstream is(data, std::ios::binary);
273                 // version
274                 u8 version = readU8(is);
275                 // check version
276                 if(version != 1)
277                         return;
278                 // pos
279                 m_position = readV3F1000(is);
280                 // yaw
281                 m_yaw = readF1000(is);
282                 // hp
283                 m_hp = readS16(is);
284                 // properties
285                 std::istringstream prop_is(deSerializeLongString(is), std::ios::binary);
286                 m_prop->deSerialize(prop_is);
287
288                 infostream<<"m_prop: "<<m_prop->dump()<<std::endl;
289
290                 m_selection_box = m_prop->collisionbox;
291                 m_selection_box.MinEdge *= BS;
292                 m_selection_box.MaxEdge *= BS;
293                         
294                 pos_translator.init(m_position);
295
296                 m_tx_size.X = 1.0 / m_prop->spritediv.X;
297                 m_tx_size.Y = 1.0 / m_prop->spritediv.Y;
298                 m_tx_basepos.X = m_tx_size.X * m_prop->initial_sprite_basepos.X;
299                 m_tx_basepos.Y = m_tx_size.Y * m_prop->initial_sprite_basepos.Y;
300                 
301                 updateNodePos();
302         }
303
304         ~LuaEntityCAO()
305         {
306                 delete m_prop;
307         }
308
309         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env)
310         {
311                 return new LuaEntityCAO(gamedef, env);
312         }
313
314         u8 getType() const
315         {
316                 return ACTIVEOBJECT_TYPE_LUAENTITY;
317         }
318         core::aabbox3d<f32>* getSelectionBox()
319         {
320                 return &m_selection_box;
321         }
322         v3f getPosition()
323         {
324                 return pos_translator.vect_show;
325         }
326                 
327         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
328                         IrrlichtDevice *irr)
329         {
330                 if(m_meshnode != NULL || m_spritenode != NULL)
331                         return;
332                 
333                 //video::IVideoDriver* driver = smgr->getVideoDriver();
334
335                 if(m_prop->visual == "sprite"){
336                         infostream<<"LuaEntityCAO::addToScene(): single_sprite"<<std::endl;
337                         m_spritenode = smgr->addBillboardSceneNode(
338                                         NULL, v2f(1, 1), v3f(0,0,0), -1);
339                         m_spritenode->setMaterialTexture(0,
340                                         tsrc->getTextureRaw("unknown_block.png"));
341                         m_spritenode->setMaterialFlag(video::EMF_LIGHTING, false);
342                         m_spritenode->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
343                         m_spritenode->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF);
344                         m_spritenode->setMaterialFlag(video::EMF_FOG_ENABLE, true);
345                         m_spritenode->setColor(video::SColor(255,0,0,0));
346                         m_spritenode->setVisible(false); /* Set visible when brightness is known */
347                         m_spritenode->setSize(m_prop->visual_size*BS);
348                         {
349                                 const float txs = 1.0 / 1;
350                                 const float tys = 1.0 / 1;
351                                 setBillboardTextureMatrix(m_spritenode,
352                                                 txs, tys, 0, 0);
353                         }
354                 } else if(m_prop->visual == "cube"){
355                         infostream<<"LuaEntityCAO::addToScene(): cube"<<std::endl;
356                         scene::IMesh *mesh = createCubeMesh(v3f(BS,BS,BS));
357                         m_meshnode = smgr->addMeshSceneNode(mesh, NULL);
358                         mesh->drop();
359                         
360                         m_meshnode->setScale(v3f(1));
361                         // Will be shown when we know the brightness
362                         m_meshnode->setVisible(false);
363                 } else {
364                         infostream<<"LuaEntityCAO::addToScene(): \""<<m_prop->visual
365                                         <<"\" not supported"<<std::endl;
366                 }
367                 updateTextures("");
368                 updateNodePos();
369         }
370
371         void removeFromScene()
372         {
373                 if(m_meshnode){
374                         m_meshnode->remove();
375                         m_meshnode = NULL;
376                 }
377                 if(m_spritenode){
378                         m_spritenode->remove();
379                         m_spritenode = NULL;
380                 }
381         }
382
383         void updateLight(u8 light_at_pos)
384         {
385                 u8 li = decode_light(light_at_pos);
386                 video::SColor color(255,li,li,li);
387                 if(m_meshnode){
388                         setMeshColor(m_meshnode->getMesh(), color);
389                         m_meshnode->setVisible(true);
390                 }
391                 if(m_spritenode){
392                         m_spritenode->setColor(color);
393                         m_spritenode->setVisible(true);
394                 }
395         }
396
397         v3s16 getLightPosition()
398         {
399                 return floatToInt(m_position, BS);
400         }
401
402         void updateNodePos()
403         {
404                 if(m_meshnode){
405                         m_meshnode->setPosition(pos_translator.vect_show);
406                 }
407                 if(m_spritenode){
408                         m_spritenode->setPosition(pos_translator.vect_show);
409                 }
410         }
411
412         void step(float dtime, ClientEnvironment *env)
413         {
414                 if(m_prop->physical){
415                         core::aabbox3d<f32> box = m_prop->collisionbox;
416                         box.MinEdge *= BS;
417                         box.MaxEdge *= BS;
418                         collisionMoveResult moveresult;
419                         f32 pos_max_d = BS*0.25; // Distance per iteration
420                         v3f p_pos = m_position;
421                         v3f p_velocity = m_velocity;
422                         IGameDef *gamedef = env->getGameDef();
423                         moveresult = collisionMovePrecise(&env->getMap(), gamedef,
424                                         pos_max_d, box, dtime, p_pos, p_velocity);
425                         // Apply results
426                         m_position = p_pos;
427                         m_velocity = p_velocity;
428                         
429                         bool is_end_position = moveresult.collides;
430                         pos_translator.update(m_position, is_end_position, dtime);
431                         pos_translator.translate(dtime);
432                         updateNodePos();
433
434                         m_velocity += dtime * m_acceleration;
435                 } else {
436                         m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration;
437                         m_velocity += dtime * m_acceleration;
438                         pos_translator.update(m_position, pos_translator.aim_is_end, pos_translator.anim_time);
439                         pos_translator.translate(dtime);
440                         updateNodePos();
441                 }
442
443                 m_anim_timer += dtime;
444                 if(m_anim_timer >= m_anim_framelength){
445                         m_anim_timer -= m_anim_framelength;
446                         m_anim_frame++;
447                         if(m_anim_frame >= m_anim_num_frames)
448                                 m_anim_frame = 0;
449                 }
450
451                 updateTexturePos();
452         }
453
454         void updateTexturePos()
455         {
456                 if(m_spritenode){
457                         scene::ICameraSceneNode* camera =
458                                         m_spritenode->getSceneManager()->getActiveCamera();
459                         if(!camera)
460                                 return;
461                         v3f cam_to_entity = m_spritenode->getAbsolutePosition()
462                                         - camera->getAbsolutePosition();
463                         cam_to_entity.normalize();
464
465                         int row = m_tx_basepos.Y;
466                         int col = m_tx_basepos.X;
467                         
468                         if(m_tx_select_horiz_by_yawpitch)
469                         {
470                                 if(cam_to_entity.Y > 0.75)
471                                         col += 5;
472                                 else if(cam_to_entity.Y < -0.75)
473                                         col += 4;
474                                 else{
475                                         float mob_dir = atan2(cam_to_entity.Z, cam_to_entity.X) / PI * 180.;
476                                         float dir = mob_dir - m_yaw;
477                                         dir = wrapDegrees_180(dir);
478                                         //infostream<<"id="<<m_id<<" dir="<<dir<<std::endl;
479                                         if(fabs(wrapDegrees_180(dir - 0)) <= 45.1)
480                                                 col += 2;
481                                         else if(fabs(wrapDegrees_180(dir - 90)) <= 45.1)
482                                                 col += 3;
483                                         else if(fabs(wrapDegrees_180(dir - 180)) <= 45.1)
484                                                 col += 0;
485                                         else if(fabs(wrapDegrees_180(dir + 90)) <= 45.1)
486                                                 col += 1;
487                                         else
488                                                 col += 4;
489                                 }
490                         }
491                         
492                         // Animation goes downwards
493                         row += m_anim_frame;
494
495                         float txs = m_tx_size.X;
496                         float tys = m_tx_size.Y;
497                         setBillboardTextureMatrix(m_spritenode,
498                                         txs, tys, col, row);
499                 }
500         }
501
502         void updateTextures(const std::string &mod)
503         {
504                 ITextureSource *tsrc = m_gamedef->tsrc();
505
506                 if(m_spritenode){
507                         std::string texturestring = "unknown_block.png";
508                         if(m_prop->textures.size() >= 1)
509                                 texturestring = m_prop->textures[0];
510                         texturestring += mod;
511                         m_spritenode->setMaterialTexture(0,
512                                         tsrc->getTextureRaw(texturestring));
513                 }
514                 if(m_meshnode){
515                         for (u32 i = 0; i < 6; ++i)
516                         {
517                                 std::string texturestring = "unknown_block.png";
518                                 if(m_prop->textures.size() > i)
519                                         texturestring = m_prop->textures[i];
520                                 texturestring += mod;
521                                 AtlasPointer ap = tsrc->getTexture(texturestring);
522
523                                 // Get the tile texture and atlas transformation
524                                 video::ITexture* atlas = ap.atlas;
525                                 v2f pos = ap.pos;
526                                 v2f size = ap.size;
527
528                                 // Set material flags and texture
529                                 video::SMaterial& material = m_meshnode->getMaterial(i);
530                                 material.setFlag(video::EMF_LIGHTING, false);
531                                 material.setFlag(video::EMF_BILINEAR_FILTER, false);
532                                 material.setTexture(0, atlas);
533                                 material.getTextureMatrix(0).setTextureTranslate(pos.X, pos.Y);
534                                 material.getTextureMatrix(0).setTextureScale(size.X, size.Y);
535                         }
536                 }
537         }
538
539         void processMessage(const std::string &data)
540         {
541                 //infostream<<"LuaEntityCAO: Got message"<<std::endl;
542                 std::istringstream is(data, std::ios::binary);
543                 // command
544                 u8 cmd = readU8(is);
545                 if(cmd == LUAENTITY_CMD_UPDATE_POSITION) // update position
546                 {
547                         // do_interpolate
548                         bool do_interpolate = readU8(is);
549                         // pos
550                         m_position = readV3F1000(is);
551                         // velocity
552                         m_velocity = readV3F1000(is);
553                         // acceleration
554                         m_acceleration = readV3F1000(is);
555                         // yaw
556                         m_yaw = readF1000(is);
557                         // is_end_position (for interpolation)
558                         bool is_end_position = readU8(is);
559                         // update_interval
560                         float update_interval = readF1000(is);
561                         
562                         if(do_interpolate){
563                                 if(!m_prop->physical)
564                                         pos_translator.update(m_position, is_end_position, update_interval);
565                         } else {
566                                 pos_translator.init(m_position);
567                         }
568                         updateNodePos();
569                 }
570                 else if(cmd == LUAENTITY_CMD_SET_TEXTURE_MOD) // set texture modification
571                 {
572                         std::string mod = deSerializeString(is);
573                         updateTextures(mod);
574                 }
575                 else if(cmd == LUAENTITY_CMD_SET_SPRITE) // set sprite
576                 {
577                         v2s16 p = readV2S16(is);
578                         int num_frames = readU16(is);
579                         float framelength = readF1000(is);
580                         bool select_horiz_by_yawpitch = readU8(is);
581                         
582                         m_tx_basepos = p;
583                         m_anim_num_frames = num_frames;
584                         m_anim_framelength = framelength;
585                         m_tx_select_horiz_by_yawpitch = select_horiz_by_yawpitch;
586
587                         updateTexturePos();
588                 }
589                 else if(cmd == LUAENTITY_CMD_PUNCHED)
590                 {
591                         /*s16 damage =*/ readS16(is);
592                         s16 result_hp = readS16(is);
593                         
594                         m_hp = result_hp;
595                         // TODO: Execute defined fast response
596                 }
597         }
598         
599         bool directReportPunch(v3f dir, const ItemStack *punchitem=NULL,
600                         float time_from_last_punch=1000000)
601         {
602                 // TODO: Transfer this from the server
603                 ItemGroupList m_armor_groups;
604                 
605                 assert(punchitem);
606                 const ToolCapabilities *toolcap =
607                                 &punchitem->getToolCapabilities(m_gamedef->idef());
608                 PunchDamageResult result = getPunchDamage(
609                                 m_armor_groups,
610                                 toolcap,
611                                 punchitem,
612                                 time_from_last_punch);
613                 
614                 if(result.did_punch)
615                 {
616                         // TODO: Decrease hp by 
617                         if(result.damage < m_hp)
618                                 m_hp -= result.damage;
619                         else
620                                 m_hp = 0;
621                         // TODO: Execute defined fast response
622                 }
623                 
624                 return false;
625         }
626 };
627
628 // Prototype
629 LuaEntityCAO proto_LuaEntityCAO(NULL, NULL);
630
631 /*
632         PlayerCAO
633 */
634
635 class PlayerCAO : public ClientActiveObject
636 {
637 private:
638         core::aabbox3d<f32> m_selection_box;
639         scene::IMeshSceneNode *m_node;
640         scene::ITextSceneNode* m_text;
641         std::string m_name;
642         v3f m_position;
643         float m_yaw;
644         SmoothTranslator pos_translator;
645         bool m_is_local_player;
646         LocalPlayer *m_local_player;
647         float m_damage_visual_timer;
648         bool m_dead;
649
650 public:
651         PlayerCAO(IGameDef *gamedef, ClientEnvironment *env):
652                 ClientActiveObject(0, gamedef, env),
653                 m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2.0,BS/3.),
654                 m_node(NULL),
655                 m_text(NULL),
656                 m_position(v3f(0,10*BS,0)),
657                 m_yaw(0),
658                 m_is_local_player(false),
659                 m_local_player(NULL),
660                 m_damage_visual_timer(0),
661                 m_dead(false)
662         {
663                 if(gamedef == NULL)
664                         ClientActiveObject::registerType(getType(), create);
665         }
666
667         void initialize(const std::string &data)
668         {
669                 infostream<<"PlayerCAO: Got init data"<<std::endl;
670                 
671                 std::istringstream is(data, std::ios::binary);
672                 // version
673                 u8 version = readU8(is);
674                 // check version
675                 if(version != 0)
676                         return;
677                 // name
678                 m_name = deSerializeString(is);
679                 // pos
680                 m_position = readV3F1000(is);
681                 // yaw
682                 m_yaw = readF1000(is);
683                 // dead
684                 m_dead = readU8(is);
685
686                 pos_translator.init(m_position);
687
688                 Player *player = m_env->getPlayer(m_name.c_str());
689                 if(player && player->isLocal()){
690                         m_is_local_player = true;
691                         m_local_player = (LocalPlayer*)player;
692                 }
693         }
694
695         ~PlayerCAO()
696         {
697                 if(m_node)
698                         m_node->remove();
699         }
700
701         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env)
702         {
703                 return new PlayerCAO(gamedef, env);
704         }
705
706         u8 getType() const
707         {
708                 return ACTIVEOBJECT_TYPE_PLAYER;
709         }
710         core::aabbox3d<f32>* getSelectionBox()
711         {
712                 if(m_is_local_player)
713                         return NULL;
714                 if(m_dead)
715                         return NULL;
716                 return &m_selection_box;
717         }
718         v3f getPosition()
719         {
720                 return pos_translator.vect_show;
721         }
722                 
723         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
724                         IrrlichtDevice *irr)
725         {
726                 if(m_node != NULL)
727                         return;
728                 if(m_is_local_player)
729                         return;
730                 
731                 //video::IVideoDriver* driver = smgr->getVideoDriver();
732                 gui::IGUIEnvironment* gui = irr->getGUIEnvironment();
733                 
734                 scene::SMesh *mesh = new scene::SMesh();
735                 { // Front
736                 scene::IMeshBuffer *buf = new scene::SMeshBuffer();
737                 video::SColor c(255,255,255,255);
738                 video::S3DVertex vertices[4] =
739                 {
740                         video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
741                         video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
742                         video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0),
743                         video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0),
744                 };
745                 u16 indices[] = {0,1,2,2,3,0};
746                 buf->append(vertices, 4, indices, 6);
747                 // Set material
748                 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
749                 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
750                 buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
751                 buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
752                 // Add to mesh
753                 mesh->addMeshBuffer(buf);
754                 buf->drop();
755                 }
756                 { // Back
757                 scene::IMeshBuffer *buf = new scene::SMeshBuffer();
758                 video::SColor c(255,255,255,255);
759                 video::S3DVertex vertices[4] =
760                 {
761                         video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
762                         video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
763                         video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0),
764                         video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0),
765                 };
766                 u16 indices[] = {0,1,2,2,3,0};
767                 buf->append(vertices, 4, indices, 6);
768                 // Set material
769                 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
770                 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
771                 buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
772                 buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
773                 // Add to mesh
774                 mesh->addMeshBuffer(buf);
775                 buf->drop();
776                 }
777                 m_node = smgr->addMeshSceneNode(mesh, NULL);
778                 mesh->drop();
779                 // Set it to use the materials of the meshbuffers directly.
780                 // This is needed for changing the texture in the future
781                 m_node->setReadOnlyMaterials(true);
782                 updateNodePos();
783
784                 // Add a text node for showing the name
785                 std::wstring wname = narrow_to_wide(m_name);
786                 m_text = smgr->addTextSceneNode(gui->getBuiltInFont(),
787                                 wname.c_str(), video::SColor(255,255,255,255), m_node);
788                 m_text->setPosition(v3f(0, (f32)BS*2.1, 0));
789                 
790                 updateTextures("");
791                 updateVisibility();
792                 updateNodePos();
793         }
794
795         void removeFromScene()
796         {
797                 if(m_node == NULL)
798                         return;
799
800                 m_node->remove();
801                 m_node = NULL;
802         }
803
804         void updateLight(u8 light_at_pos)
805         {
806                 if(m_node == NULL)
807                         return;
808                 
809                 u8 li = decode_light(light_at_pos);
810                 video::SColor color(255,li,li,li);
811                 setMeshColor(m_node->getMesh(), color);
812
813                 updateVisibility();
814         }
815
816         v3s16 getLightPosition()
817         {
818                 return floatToInt(m_position+v3f(0,BS*1.5,0), BS);
819         }
820
821         void updateVisibility()
822         {
823                 if(m_node == NULL)
824                         return;
825
826                 m_node->setVisible(!m_dead);
827         }
828
829         void updateNodePos()
830         {
831                 if(m_node == NULL)
832                         return;
833
834                 m_node->setPosition(pos_translator.vect_show);
835
836                 v3f rot = m_node->getRotation();
837                 rot.Y = -m_yaw;
838                 m_node->setRotation(rot);
839         }
840
841         void step(float dtime, ClientEnvironment *env)
842         {
843                 pos_translator.translate(dtime);
844                 updateVisibility();
845                 updateNodePos();
846
847                 if(m_damage_visual_timer > 0){
848                         m_damage_visual_timer -= dtime;
849                         if(m_damage_visual_timer <= 0){
850                                 updateTextures("");
851                         }
852                 }
853         }
854
855         void processMessage(const std::string &data)
856         {
857                 //infostream<<"PlayerCAO: Got message"<<std::endl;
858                 std::istringstream is(data, std::ios::binary);
859                 // command
860                 u8 cmd = readU8(is);
861                 if(cmd == 0) // update position
862                 {
863                         // pos
864                         m_position = readV3F1000(is);
865                         // yaw
866                         m_yaw = readF1000(is);
867
868                         pos_translator.update(m_position, false);
869
870                         updateNodePos();
871                 }
872                 else if(cmd == 1) // punched
873                 {
874                         // damage
875                         s16 damage = readS16(is);
876                         m_damage_visual_timer = 0.05;
877                         if(damage >= 2)
878                                 m_damage_visual_timer += 0.05 * damage;
879                         updateTextures("^[brighten");
880                 }
881                 else if(cmd == 2) // died or respawned
882                 {
883                         m_dead = readU8(is);
884                         updateVisibility();
885                 }
886         }
887
888         void updateTextures(const std::string &mod)
889         {
890                 if(!m_node)
891                         return;
892                 ITextureSource *tsrc = m_gamedef->tsrc();
893                 scene::IMesh *mesh = m_node->getMesh();
894                 if(mesh){
895                         {
896                                 std::string tname = "player.png";
897                                 tname += mod;
898                                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(0);
899                                 buf->getMaterial().setTexture(0,
900                                                 tsrc->getTextureRaw(tname));
901                         }
902                         {
903                                 std::string tname = "player_back.png";
904                                 tname += mod;
905                                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(1);
906                                 buf->getMaterial().setTexture(0,
907                                                 tsrc->getTextureRaw(tname));
908                         }
909                 }
910         }
911 };
912
913 // Prototype
914 PlayerCAO proto_PlayerCAO(NULL, NULL);
915
916