33079fd118513213bd30ff91b11ac026d5bc2057
[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         ItemGroupList m_armor_groups;
244
245 public:
246         LuaEntityCAO(IGameDef *gamedef, ClientEnvironment *env):
247                 ClientActiveObject(0, gamedef, env),
248                 m_selection_box(-BS/3.,-BS/3.,-BS/3., BS/3.,BS/3.,BS/3.),
249                 m_meshnode(NULL),
250                 m_spritenode(NULL),
251                 m_position(v3f(0,10*BS,0)),
252                 m_velocity(v3f(0,0,0)),
253                 m_acceleration(v3f(0,0,0)),
254                 m_yaw(0),
255                 m_hp(1),
256                 m_prop(new LuaEntityProperties),
257                 m_tx_size(1,1),
258                 m_tx_basepos(0,0),
259                 m_tx_select_horiz_by_yawpitch(false),
260                 m_anim_frame(0),
261                 m_anim_num_frames(1),
262                 m_anim_framelength(0.2),
263                 m_anim_timer(0)
264         {
265                 if(gamedef == NULL)
266                         ClientActiveObject::registerType(getType(), create);
267         }
268
269         void initialize(const std::string &data)
270         {
271                 infostream<<"LuaEntityCAO: Got init data"<<std::endl;
272                 
273                 std::istringstream is(data, std::ios::binary);
274                 // version
275                 u8 version = readU8(is);
276                 // check version
277                 if(version != 1)
278                         return;
279                 // pos
280                 m_position = readV3F1000(is);
281                 // yaw
282                 m_yaw = readF1000(is);
283                 // hp
284                 m_hp = readS16(is);
285                 // properties
286                 std::istringstream prop_is(deSerializeLongString(is), std::ios::binary);
287                 m_prop->deSerialize(prop_is);
288
289                 infostream<<"m_prop: "<<m_prop->dump()<<std::endl;
290
291                 m_selection_box = m_prop->collisionbox;
292                 m_selection_box.MinEdge *= BS;
293                 m_selection_box.MaxEdge *= BS;
294                         
295                 pos_translator.init(m_position);
296
297                 m_tx_size.X = 1.0 / m_prop->spritediv.X;
298                 m_tx_size.Y = 1.0 / m_prop->spritediv.Y;
299                 m_tx_basepos.X = m_tx_size.X * m_prop->initial_sprite_basepos.X;
300                 m_tx_basepos.Y = m_tx_size.Y * m_prop->initial_sprite_basepos.Y;
301                 
302                 updateNodePos();
303         }
304
305         ~LuaEntityCAO()
306         {
307                 delete m_prop;
308         }
309
310         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env)
311         {
312                 return new LuaEntityCAO(gamedef, env);
313         }
314
315         u8 getType() const
316         {
317                 return ACTIVEOBJECT_TYPE_LUAENTITY;
318         }
319         core::aabbox3d<f32>* getSelectionBox()
320         {
321                 return &m_selection_box;
322         }
323         v3f getPosition()
324         {
325                 return pos_translator.vect_show;
326         }
327                 
328         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
329                         IrrlichtDevice *irr)
330         {
331                 if(m_meshnode != NULL || m_spritenode != NULL)
332                         return;
333                 
334                 //video::IVideoDriver* driver = smgr->getVideoDriver();
335
336                 if(m_prop->visual == "sprite"){
337                         infostream<<"LuaEntityCAO::addToScene(): single_sprite"<<std::endl;
338                         m_spritenode = smgr->addBillboardSceneNode(
339                                         NULL, v2f(1, 1), v3f(0,0,0), -1);
340                         m_spritenode->setMaterialTexture(0,
341                                         tsrc->getTextureRaw("unknown_block.png"));
342                         m_spritenode->setMaterialFlag(video::EMF_LIGHTING, false);
343                         m_spritenode->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
344                         m_spritenode->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF);
345                         m_spritenode->setMaterialFlag(video::EMF_FOG_ENABLE, true);
346                         m_spritenode->setColor(video::SColor(255,0,0,0));
347                         m_spritenode->setVisible(false); /* Set visible when brightness is known */
348                         m_spritenode->setSize(m_prop->visual_size*BS);
349                         {
350                                 const float txs = 1.0 / 1;
351                                 const float tys = 1.0 / 1;
352                                 setBillboardTextureMatrix(m_spritenode,
353                                                 txs, tys, 0, 0);
354                         }
355                 } else if(m_prop->visual == "cube"){
356                         infostream<<"LuaEntityCAO::addToScene(): cube"<<std::endl;
357                         scene::IMesh *mesh = createCubeMesh(v3f(BS,BS,BS));
358                         m_meshnode = smgr->addMeshSceneNode(mesh, NULL);
359                         mesh->drop();
360                         
361                         m_meshnode->setScale(v3f(1));
362                         // Will be shown when we know the brightness
363                         m_meshnode->setVisible(false);
364                 } else {
365                         infostream<<"LuaEntityCAO::addToScene(): \""<<m_prop->visual
366                                         <<"\" not supported"<<std::endl;
367                 }
368                 updateTextures("");
369                 updateNodePos();
370         }
371
372         void removeFromScene()
373         {
374                 if(m_meshnode){
375                         m_meshnode->remove();
376                         m_meshnode = NULL;
377                 }
378                 if(m_spritenode){
379                         m_spritenode->remove();
380                         m_spritenode = NULL;
381                 }
382         }
383
384         void updateLight(u8 light_at_pos)
385         {
386                 u8 li = decode_light(light_at_pos);
387                 video::SColor color(255,li,li,li);
388                 if(m_meshnode){
389                         setMeshColor(m_meshnode->getMesh(), color);
390                         m_meshnode->setVisible(true);
391                 }
392                 if(m_spritenode){
393                         m_spritenode->setColor(color);
394                         m_spritenode->setVisible(true);
395                 }
396         }
397
398         v3s16 getLightPosition()
399         {
400                 return floatToInt(m_position, BS);
401         }
402
403         void updateNodePos()
404         {
405                 if(m_meshnode){
406                         m_meshnode->setPosition(pos_translator.vect_show);
407                 }
408                 if(m_spritenode){
409                         m_spritenode->setPosition(pos_translator.vect_show);
410                 }
411         }
412
413         void step(float dtime, ClientEnvironment *env)
414         {
415                 if(m_prop->physical){
416                         core::aabbox3d<f32> box = m_prop->collisionbox;
417                         box.MinEdge *= BS;
418                         box.MaxEdge *= BS;
419                         collisionMoveResult moveresult;
420                         f32 pos_max_d = BS*0.25; // Distance per iteration
421                         v3f p_pos = m_position;
422                         v3f p_velocity = m_velocity;
423                         IGameDef *gamedef = env->getGameDef();
424                         moveresult = collisionMovePrecise(&env->getMap(), gamedef,
425                                         pos_max_d, box, dtime, p_pos, p_velocity);
426                         // Apply results
427                         m_position = p_pos;
428                         m_velocity = p_velocity;
429                         
430                         bool is_end_position = moveresult.collides;
431                         pos_translator.update(m_position, is_end_position, dtime);
432                         pos_translator.translate(dtime);
433                         updateNodePos();
434
435                         m_velocity += dtime * m_acceleration;
436                 } else {
437                         m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration;
438                         m_velocity += dtime * m_acceleration;
439                         pos_translator.update(m_position, pos_translator.aim_is_end, pos_translator.anim_time);
440                         pos_translator.translate(dtime);
441                         updateNodePos();
442                 }
443
444                 m_anim_timer += dtime;
445                 if(m_anim_timer >= m_anim_framelength){
446                         m_anim_timer -= m_anim_framelength;
447                         m_anim_frame++;
448                         if(m_anim_frame >= m_anim_num_frames)
449                                 m_anim_frame = 0;
450                 }
451
452                 updateTexturePos();
453         }
454
455         void updateTexturePos()
456         {
457                 if(m_spritenode){
458                         scene::ICameraSceneNode* camera =
459                                         m_spritenode->getSceneManager()->getActiveCamera();
460                         if(!camera)
461                                 return;
462                         v3f cam_to_entity = m_spritenode->getAbsolutePosition()
463                                         - camera->getAbsolutePosition();
464                         cam_to_entity.normalize();
465
466                         int row = m_tx_basepos.Y;
467                         int col = m_tx_basepos.X;
468                         
469                         if(m_tx_select_horiz_by_yawpitch)
470                         {
471                                 if(cam_to_entity.Y > 0.75)
472                                         col += 5;
473                                 else if(cam_to_entity.Y < -0.75)
474                                         col += 4;
475                                 else{
476                                         float mob_dir = atan2(cam_to_entity.Z, cam_to_entity.X) / PI * 180.;
477                                         float dir = mob_dir - m_yaw;
478                                         dir = wrapDegrees_180(dir);
479                                         //infostream<<"id="<<m_id<<" dir="<<dir<<std::endl;
480                                         if(fabs(wrapDegrees_180(dir - 0)) <= 45.1)
481                                                 col += 2;
482                                         else if(fabs(wrapDegrees_180(dir - 90)) <= 45.1)
483                                                 col += 3;
484                                         else if(fabs(wrapDegrees_180(dir - 180)) <= 45.1)
485                                                 col += 0;
486                                         else if(fabs(wrapDegrees_180(dir + 90)) <= 45.1)
487                                                 col += 1;
488                                         else
489                                                 col += 4;
490                                 }
491                         }
492                         
493                         // Animation goes downwards
494                         row += m_anim_frame;
495
496                         float txs = m_tx_size.X;
497                         float tys = m_tx_size.Y;
498                         setBillboardTextureMatrix(m_spritenode,
499                                         txs, tys, col, row);
500                 }
501         }
502
503         void updateTextures(const std::string &mod)
504         {
505                 ITextureSource *tsrc = m_gamedef->tsrc();
506
507                 if(m_spritenode){
508                         std::string texturestring = "unknown_block.png";
509                         if(m_prop->textures.size() >= 1)
510                                 texturestring = m_prop->textures[0];
511                         texturestring += mod;
512                         m_spritenode->setMaterialTexture(0,
513                                         tsrc->getTextureRaw(texturestring));
514                 }
515                 if(m_meshnode){
516                         for (u32 i = 0; i < 6; ++i)
517                         {
518                                 std::string texturestring = "unknown_block.png";
519                                 if(m_prop->textures.size() > i)
520                                         texturestring = m_prop->textures[i];
521                                 texturestring += mod;
522                                 AtlasPointer ap = tsrc->getTexture(texturestring);
523
524                                 // Get the tile texture and atlas transformation
525                                 video::ITexture* atlas = ap.atlas;
526                                 v2f pos = ap.pos;
527                                 v2f size = ap.size;
528
529                                 // Set material flags and texture
530                                 video::SMaterial& material = m_meshnode->getMaterial(i);
531                                 material.setFlag(video::EMF_LIGHTING, false);
532                                 material.setFlag(video::EMF_BILINEAR_FILTER, false);
533                                 material.setTexture(0, atlas);
534                                 material.getTextureMatrix(0).setTextureTranslate(pos.X, pos.Y);
535                                 material.getTextureMatrix(0).setTextureScale(size.X, size.Y);
536                         }
537                 }
538         }
539
540         void processMessage(const std::string &data)
541         {
542                 //infostream<<"LuaEntityCAO: Got message"<<std::endl;
543                 std::istringstream is(data, std::ios::binary);
544                 // command
545                 u8 cmd = readU8(is);
546                 if(cmd == LUAENTITY_CMD_UPDATE_POSITION) // update position
547                 {
548                         // do_interpolate
549                         bool do_interpolate = readU8(is);
550                         // pos
551                         m_position = readV3F1000(is);
552                         // velocity
553                         m_velocity = readV3F1000(is);
554                         // acceleration
555                         m_acceleration = readV3F1000(is);
556                         // yaw
557                         m_yaw = readF1000(is);
558                         // is_end_position (for interpolation)
559                         bool is_end_position = readU8(is);
560                         // update_interval
561                         float update_interval = readF1000(is);
562                         
563                         if(do_interpolate){
564                                 if(!m_prop->physical)
565                                         pos_translator.update(m_position, is_end_position, update_interval);
566                         } else {
567                                 pos_translator.init(m_position);
568                         }
569                         updateNodePos();
570                 }
571                 else if(cmd == LUAENTITY_CMD_SET_TEXTURE_MOD) // set texture modification
572                 {
573                         std::string mod = deSerializeString(is);
574                         updateTextures(mod);
575                 }
576                 else if(cmd == LUAENTITY_CMD_SET_SPRITE) // set sprite
577                 {
578                         v2s16 p = readV2S16(is);
579                         int num_frames = readU16(is);
580                         float framelength = readF1000(is);
581                         bool select_horiz_by_yawpitch = readU8(is);
582                         
583                         m_tx_basepos = p;
584                         m_anim_num_frames = num_frames;
585                         m_anim_framelength = framelength;
586                         m_tx_select_horiz_by_yawpitch = select_horiz_by_yawpitch;
587
588                         updateTexturePos();
589                 }
590                 else if(cmd == LUAENTITY_CMD_PUNCHED)
591                 {
592                         /*s16 damage =*/ readS16(is);
593                         s16 result_hp = readS16(is);
594                         
595                         m_hp = result_hp;
596                         // TODO: Execute defined fast response
597                 }
598                 else if(cmd == LUAENTITY_CMD_UPDATE_ARMOR_GROUPS)
599                 {
600                         m_armor_groups.clear();
601                         int armor_groups_size = readU16(is);
602                         for(int i=0; i<armor_groups_size; i++){
603                                 std::string name = deSerializeString(is);
604                                 int rating = readS16(is);
605                                 m_armor_groups[name] = rating;
606                         }
607                 }
608         }
609         
610         bool directReportPunch(v3f dir, const ItemStack *punchitem=NULL,
611                         float time_from_last_punch=1000000)
612         {
613                 assert(punchitem);
614                 const ToolCapabilities *toolcap =
615                                 &punchitem->getToolCapabilities(m_gamedef->idef());
616                 PunchDamageResult result = getPunchDamage(
617                                 m_armor_groups,
618                                 toolcap,
619                                 punchitem,
620                                 time_from_last_punch);
621                 
622                 if(result.did_punch)
623                 {
624                         if(result.damage < m_hp)
625                                 m_hp -= result.damage;
626                         else
627                                 m_hp = 0;
628                         // TODO: Execute defined fast response
629                 }
630                 
631                 return false;
632         }
633         
634         std::string debugInfoText()
635         {
636                 std::ostringstream os(std::ios::binary);
637                 os<<"LuaEntityCAO \n";
638                 os<<"armor={";
639                 for(ItemGroupList::const_iterator i = m_armor_groups.begin();
640                                 i != m_armor_groups.end(); i++){
641                         os<<i->first<<"="<<i->second<<", ";
642                 }
643                 os<<"}";
644                 return os.str();
645         }
646 };
647
648 // Prototype
649 LuaEntityCAO proto_LuaEntityCAO(NULL, NULL);
650
651 /*
652         PlayerCAO
653 */
654
655 class PlayerCAO : public ClientActiveObject
656 {
657 private:
658         core::aabbox3d<f32> m_selection_box;
659         scene::IMeshSceneNode *m_node;
660         scene::ITextSceneNode* m_text;
661         std::string m_name;
662         v3f m_position;
663         float m_yaw;
664         SmoothTranslator pos_translator;
665         bool m_is_local_player;
666         LocalPlayer *m_local_player;
667         float m_damage_visual_timer;
668         bool m_dead;
669
670 public:
671         PlayerCAO(IGameDef *gamedef, ClientEnvironment *env):
672                 ClientActiveObject(0, gamedef, env),
673                 m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2.0,BS/3.),
674                 m_node(NULL),
675                 m_text(NULL),
676                 m_position(v3f(0,10*BS,0)),
677                 m_yaw(0),
678                 m_is_local_player(false),
679                 m_local_player(NULL),
680                 m_damage_visual_timer(0),
681                 m_dead(false)
682         {
683                 if(gamedef == NULL)
684                         ClientActiveObject::registerType(getType(), create);
685         }
686
687         void initialize(const std::string &data)
688         {
689                 infostream<<"PlayerCAO: Got init data"<<std::endl;
690                 
691                 std::istringstream is(data, std::ios::binary);
692                 // version
693                 u8 version = readU8(is);
694                 // check version
695                 if(version != 0)
696                         return;
697                 // name
698                 m_name = deSerializeString(is);
699                 // pos
700                 m_position = readV3F1000(is);
701                 // yaw
702                 m_yaw = readF1000(is);
703                 // dead
704                 m_dead = readU8(is);
705
706                 pos_translator.init(m_position);
707
708                 Player *player = m_env->getPlayer(m_name.c_str());
709                 if(player && player->isLocal()){
710                         m_is_local_player = true;
711                         m_local_player = (LocalPlayer*)player;
712                 }
713         }
714
715         ~PlayerCAO()
716         {
717                 if(m_node)
718                         m_node->remove();
719         }
720
721         static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env)
722         {
723                 return new PlayerCAO(gamedef, env);
724         }
725
726         u8 getType() const
727         {
728                 return ACTIVEOBJECT_TYPE_PLAYER;
729         }
730         core::aabbox3d<f32>* getSelectionBox()
731         {
732                 if(m_is_local_player)
733                         return NULL;
734                 if(m_dead)
735                         return NULL;
736                 return &m_selection_box;
737         }
738         v3f getPosition()
739         {
740                 return pos_translator.vect_show;
741         }
742                 
743         void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
744                         IrrlichtDevice *irr)
745         {
746                 if(m_node != NULL)
747                         return;
748                 if(m_is_local_player)
749                         return;
750                 
751                 //video::IVideoDriver* driver = smgr->getVideoDriver();
752                 gui::IGUIEnvironment* gui = irr->getGUIEnvironment();
753                 
754                 scene::SMesh *mesh = new scene::SMesh();
755                 { // Front
756                 scene::IMeshBuffer *buf = new scene::SMeshBuffer();
757                 video::SColor c(255,255,255,255);
758                 video::S3DVertex vertices[4] =
759                 {
760                         video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
761                         video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
762                         video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0),
763                         video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0),
764                 };
765                 u16 indices[] = {0,1,2,2,3,0};
766                 buf->append(vertices, 4, indices, 6);
767                 // Set material
768                 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
769                 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
770                 buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
771                 buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
772                 // Add to mesh
773                 mesh->addMeshBuffer(buf);
774                 buf->drop();
775                 }
776                 { // Back
777                 scene::IMeshBuffer *buf = new scene::SMeshBuffer();
778                 video::SColor c(255,255,255,255);
779                 video::S3DVertex vertices[4] =
780                 {
781                         video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
782                         video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
783                         video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0),
784                         video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0),
785                 };
786                 u16 indices[] = {0,1,2,2,3,0};
787                 buf->append(vertices, 4, indices, 6);
788                 // Set material
789                 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
790                 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
791                 buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
792                 buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
793                 // Add to mesh
794                 mesh->addMeshBuffer(buf);
795                 buf->drop();
796                 }
797                 m_node = smgr->addMeshSceneNode(mesh, NULL);
798                 mesh->drop();
799                 // Set it to use the materials of the meshbuffers directly.
800                 // This is needed for changing the texture in the future
801                 m_node->setReadOnlyMaterials(true);
802                 updateNodePos();
803
804                 // Add a text node for showing the name
805                 std::wstring wname = narrow_to_wide(m_name);
806                 m_text = smgr->addTextSceneNode(gui->getBuiltInFont(),
807                                 wname.c_str(), video::SColor(255,255,255,255), m_node);
808                 m_text->setPosition(v3f(0, (f32)BS*2.1, 0));
809                 
810                 updateTextures("");
811                 updateVisibility();
812                 updateNodePos();
813         }
814
815         void removeFromScene()
816         {
817                 if(m_node == NULL)
818                         return;
819
820                 m_node->remove();
821                 m_node = NULL;
822         }
823
824         void updateLight(u8 light_at_pos)
825         {
826                 if(m_node == NULL)
827                         return;
828                 
829                 u8 li = decode_light(light_at_pos);
830                 video::SColor color(255,li,li,li);
831                 setMeshColor(m_node->getMesh(), color);
832
833                 updateVisibility();
834         }
835
836         v3s16 getLightPosition()
837         {
838                 return floatToInt(m_position+v3f(0,BS*1.5,0), BS);
839         }
840
841         void updateVisibility()
842         {
843                 if(m_node == NULL)
844                         return;
845
846                 m_node->setVisible(!m_dead);
847         }
848
849         void updateNodePos()
850         {
851                 if(m_node == NULL)
852                         return;
853
854                 m_node->setPosition(pos_translator.vect_show);
855
856                 v3f rot = m_node->getRotation();
857                 rot.Y = -m_yaw;
858                 m_node->setRotation(rot);
859         }
860
861         void step(float dtime, ClientEnvironment *env)
862         {
863                 pos_translator.translate(dtime);
864                 updateVisibility();
865                 updateNodePos();
866
867                 if(m_damage_visual_timer > 0){
868                         m_damage_visual_timer -= dtime;
869                         if(m_damage_visual_timer <= 0){
870                                 updateTextures("");
871                         }
872                 }
873         }
874
875         void processMessage(const std::string &data)
876         {
877                 //infostream<<"PlayerCAO: Got message"<<std::endl;
878                 std::istringstream is(data, std::ios::binary);
879                 // command
880                 u8 cmd = readU8(is);
881                 if(cmd == 0) // update position
882                 {
883                         // pos
884                         m_position = readV3F1000(is);
885                         // yaw
886                         m_yaw = readF1000(is);
887
888                         pos_translator.update(m_position, false);
889
890                         updateNodePos();
891                 }
892                 else if(cmd == 1) // punched
893                 {
894                         // damage
895                         s16 damage = readS16(is);
896                         m_damage_visual_timer = 0.05;
897                         if(damage >= 2)
898                                 m_damage_visual_timer += 0.05 * damage;
899                         updateTextures("^[brighten");
900                 }
901                 else if(cmd == 2) // died or respawned
902                 {
903                         m_dead = readU8(is);
904                         updateVisibility();
905                 }
906         }
907
908         void updateTextures(const std::string &mod)
909         {
910                 if(!m_node)
911                         return;
912                 ITextureSource *tsrc = m_gamedef->tsrc();
913                 scene::IMesh *mesh = m_node->getMesh();
914                 if(mesh){
915                         {
916                                 std::string tname = "player.png";
917                                 tname += mod;
918                                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(0);
919                                 buf->getMaterial().setTexture(0,
920                                                 tsrc->getTextureRaw(tname));
921                         }
922                         {
923                                 std::string tname = "player_back.png";
924                                 tname += mod;
925                                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(1);
926                                 buf->getMaterial().setTexture(0,
927                                                 tsrc->getTextureRaw(tname));
928                         }
929                 }
930         }
931 };
932
933 // Prototype
934 PlayerCAO proto_PlayerCAO(NULL, NULL);
935
936