Scripting 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 "settings.h"
24 #include <ICameraSceneNode.h>
25
26 core::map<u16, ClientActiveObject::Factory> ClientActiveObject::m_types;
27
28 /*
29         TestCAO
30 */
31
32 // Prototype
33 TestCAO proto_TestCAO;
34
35 TestCAO::TestCAO():
36         ClientActiveObject(0),
37         m_node(NULL),
38         m_position(v3f(0,10*BS,0))
39 {
40         ClientActiveObject::registerType(getType(), create);
41 }
42
43 TestCAO::~TestCAO()
44 {
45 }
46
47 ClientActiveObject* TestCAO::create()
48 {
49         return new TestCAO();
50 }
51
52 void TestCAO::addToScene(scene::ISceneManager *smgr)
53 {
54         if(m_node != NULL)
55                 return;
56         
57         video::IVideoDriver* driver = smgr->getVideoDriver();
58         
59         scene::SMesh *mesh = new scene::SMesh();
60         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
61         video::SColor c(255,255,255,255);
62         video::S3DVertex vertices[4] =
63         {
64                 video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1),
65                 video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1),
66                 video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0),
67                 video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),
68         };
69         u16 indices[] = {0,1,2,2,3,0};
70         buf->append(vertices, 4, indices, 6);
71         // Set material
72         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
73         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
74         buf->getMaterial().setTexture
75                         (0, driver->getTexture(getTexturePath("rat.png").c_str()));
76         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
77         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
78         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
79         // Add to mesh
80         mesh->addMeshBuffer(buf);
81         buf->drop();
82         m_node = smgr->addMeshSceneNode(mesh, NULL);
83         mesh->drop();
84         updateNodePos();
85 }
86
87 void TestCAO::removeFromScene()
88 {
89         if(m_node == NULL)
90                 return;
91
92         m_node->remove();
93         m_node = NULL;
94 }
95
96 void TestCAO::updateLight(u8 light_at_pos)
97 {
98 }
99
100 v3s16 TestCAO::getLightPosition()
101 {
102         return floatToInt(m_position, BS);
103 }
104
105 void TestCAO::updateNodePos()
106 {
107         if(m_node == NULL)
108                 return;
109
110         m_node->setPosition(m_position);
111         //m_node->setRotation(v3f(0, 45, 0));
112 }
113
114 void TestCAO::step(float dtime, ClientEnvironment *env)
115 {
116         if(m_node)
117         {
118                 v3f rot = m_node->getRotation();
119                 //infostream<<"dtime="<<dtime<<", rot.Y="<<rot.Y<<std::endl;
120                 rot.Y += dtime * 180;
121                 m_node->setRotation(rot);
122         }
123 }
124
125 void TestCAO::processMessage(const std::string &data)
126 {
127         infostream<<"TestCAO: Got data: "<<data<<std::endl;
128         std::istringstream is(data, std::ios::binary);
129         u16 cmd;
130         is>>cmd;
131         if(cmd == 0)
132         {
133                 v3f newpos;
134                 is>>newpos.X;
135                 is>>newpos.Y;
136                 is>>newpos.Z;
137                 m_position = newpos;
138                 updateNodePos();
139         }
140 }
141
142 /*
143         ItemCAO
144 */
145
146 #include "inventory.h"
147
148 // Prototype
149 ItemCAO proto_ItemCAO;
150
151 ItemCAO::ItemCAO():
152         ClientActiveObject(0),
153         m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.),
154         m_node(NULL),
155         m_position(v3f(0,10*BS,0))
156 {
157         ClientActiveObject::registerType(getType(), create);
158 }
159
160 ItemCAO::~ItemCAO()
161 {
162 }
163
164 ClientActiveObject* ItemCAO::create()
165 {
166         return new ItemCAO();
167 }
168
169 void ItemCAO::addToScene(scene::ISceneManager *smgr)
170 {
171         if(m_node != NULL)
172                 return;
173         
174         video::IVideoDriver* driver = smgr->getVideoDriver();
175         
176         scene::SMesh *mesh = new scene::SMesh();
177         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
178         video::SColor c(255,255,255,255);
179         video::S3DVertex vertices[4] =
180         {
181                 /*video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1),
182                 video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1),
183                 video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0),
184                 video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),*/
185                 video::S3DVertex(BS/3.,0,0, 0,0,0, c, 0,1),
186                 video::S3DVertex(-BS/3.,0,0, 0,0,0, c, 1,1),
187                 video::S3DVertex(-BS/3.,0+BS*2./3.,0, 0,0,0, c, 1,0),
188                 video::S3DVertex(BS/3.,0+BS*2./3.,0, 0,0,0, c, 0,0),
189         };
190         u16 indices[] = {0,1,2,2,3,0};
191         buf->append(vertices, 4, indices, 6);
192         // Set material
193         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
194         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
195         //buf->getMaterial().setTexture(0, NULL);
196         // Initialize with the stick texture
197         buf->getMaterial().setTexture
198                         (0, driver->getTexture(getTexturePath("stick.png").c_str()));
199         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
200         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
201         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
202         // Add to mesh
203         mesh->addMeshBuffer(buf);
204         buf->drop();
205         m_node = smgr->addMeshSceneNode(mesh, NULL);
206         mesh->drop();
207         // Set it to use the materials of the meshbuffers directly.
208         // This is needed for changing the texture in the future
209         m_node->setReadOnlyMaterials(true);
210         updateNodePos();
211
212         /*
213                 Update image of node
214         */
215
216         // Create an inventory item to see what is its image
217         std::istringstream is(m_inventorystring, std::ios_base::binary);
218         video::ITexture *texture = NULL;
219         try{
220                 InventoryItem *item = NULL;
221                 item = InventoryItem::deSerialize(is);
222                 infostream<<__FUNCTION_NAME<<": m_inventorystring=\""
223                                 <<m_inventorystring<<"\" -> item="<<item
224                                 <<std::endl;
225                 if(item)
226                 {
227                         texture = item->getImage();
228                         delete item;
229                 }
230         }
231         catch(SerializationError &e)
232         {
233                 infostream<<"WARNING: "<<__FUNCTION_NAME
234                                 <<": error deSerializing inventorystring \""
235                                 <<m_inventorystring<<"\""<<std::endl;
236         }
237         
238         // Set meshbuffer texture
239         buf->getMaterial().setTexture(0, texture);
240 }
241
242 void ItemCAO::removeFromScene()
243 {
244         if(m_node == NULL)
245                 return;
246
247         m_node->remove();
248         m_node = NULL;
249 }
250
251 void ItemCAO::updateLight(u8 light_at_pos)
252 {
253         if(m_node == NULL)
254                 return;
255
256         u8 li = decode_light(light_at_pos);
257         video::SColor color(255,li,li,li);
258         setMeshVerticesColor(m_node->getMesh(), color);
259 }
260
261 v3s16 ItemCAO::getLightPosition()
262 {
263         return floatToInt(m_position, BS);
264 }
265
266 void ItemCAO::updateNodePos()
267 {
268         if(m_node == NULL)
269                 return;
270
271         m_node->setPosition(m_position);
272 }
273
274 void ItemCAO::step(float dtime, ClientEnvironment *env)
275 {
276         if(m_node)
277         {
278                 /*v3f rot = m_node->getRotation();
279                 rot.Y += dtime * 120;
280                 m_node->setRotation(rot);*/
281                 LocalPlayer *player = env->getLocalPlayer();
282                 assert(player);
283                 v3f rot = m_node->getRotation();
284                 rot.Y = 180.0 - (player->getYaw());
285                 m_node->setRotation(rot);
286         }
287 }
288
289 void ItemCAO::processMessage(const std::string &data)
290 {
291         infostream<<"ItemCAO: Got message"<<std::endl;
292         std::istringstream is(data, std::ios::binary);
293         // command
294         u8 cmd = readU8(is);
295         if(cmd == 0)
296         {
297                 // pos
298                 m_position = readV3F1000(is);
299                 updateNodePos();
300         }
301 }
302
303 void ItemCAO::initialize(const std::string &data)
304 {
305         infostream<<"ItemCAO: Got init data"<<std::endl;
306         
307         {
308                 std::istringstream is(data, std::ios::binary);
309                 // version
310                 u8 version = readU8(is);
311                 // check version
312                 if(version != 0)
313                         return;
314                 // pos
315                 m_position = readV3F1000(is);
316                 // inventorystring
317                 m_inventorystring = deSerializeString(is);
318         }
319         
320         updateNodePos();
321 }
322
323 /*
324         RatCAO
325 */
326
327 #include "inventory.h"
328
329 // Prototype
330 RatCAO proto_RatCAO;
331
332 RatCAO::RatCAO():
333         ClientActiveObject(0),
334         m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS/2.,BS/3.),
335         m_node(NULL),
336         m_position(v3f(0,10*BS,0)),
337         m_yaw(0)
338 {
339         ClientActiveObject::registerType(getType(), create);
340 }
341
342 RatCAO::~RatCAO()
343 {
344 }
345
346 ClientActiveObject* RatCAO::create()
347 {
348         return new RatCAO();
349 }
350
351 void RatCAO::addToScene(scene::ISceneManager *smgr)
352 {
353         if(m_node != NULL)
354                 return;
355         
356         video::IVideoDriver* driver = smgr->getVideoDriver();
357         
358         scene::SMesh *mesh = new scene::SMesh();
359         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
360         video::SColor c(255,255,255,255);
361         video::S3DVertex vertices[4] =
362         {
363                 video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
364                 video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
365                 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
366                 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
367         };
368         u16 indices[] = {0,1,2,2,3,0};
369         buf->append(vertices, 4, indices, 6);
370         // Set material
371         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
372         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
373         //buf->getMaterial().setTexture(0, NULL);
374         buf->getMaterial().setTexture
375                         (0, driver->getTexture(getTexturePath("rat.png").c_str()));
376         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
377         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
378         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
379         // Add to mesh
380         mesh->addMeshBuffer(buf);
381         buf->drop();
382         m_node = smgr->addMeshSceneNode(mesh, NULL);
383         mesh->drop();
384         // Set it to use the materials of the meshbuffers directly.
385         // This is needed for changing the texture in the future
386         m_node->setReadOnlyMaterials(true);
387         updateNodePos();
388 }
389
390 void RatCAO::removeFromScene()
391 {
392         if(m_node == NULL)
393                 return;
394
395         m_node->remove();
396         m_node = NULL;
397 }
398
399 void RatCAO::updateLight(u8 light_at_pos)
400 {
401         if(m_node == NULL)
402                 return;
403
404         u8 li = decode_light(light_at_pos);
405         video::SColor color(255,li,li,li);
406         setMeshVerticesColor(m_node->getMesh(), color);
407 }
408
409 v3s16 RatCAO::getLightPosition()
410 {
411         return floatToInt(m_position+v3f(0,BS*0.5,0), BS);
412 }
413
414 void RatCAO::updateNodePos()
415 {
416         if(m_node == NULL)
417                 return;
418
419         //m_node->setPosition(m_position);
420         m_node->setPosition(pos_translator.vect_show);
421
422         v3f rot = m_node->getRotation();
423         rot.Y = 180.0 - m_yaw;
424         m_node->setRotation(rot);
425 }
426
427 void RatCAO::step(float dtime, ClientEnvironment *env)
428 {
429         pos_translator.translate(dtime);
430         updateNodePos();
431 }
432
433 void RatCAO::processMessage(const std::string &data)
434 {
435         //infostream<<"RatCAO: Got message"<<std::endl;
436         std::istringstream is(data, std::ios::binary);
437         // command
438         u8 cmd = readU8(is);
439         if(cmd == 0)
440         {
441                 // pos
442                 m_position = readV3F1000(is);
443                 pos_translator.update(m_position);
444                 // yaw
445                 m_yaw = readF1000(is);
446                 updateNodePos();
447         }
448 }
449
450 void RatCAO::initialize(const std::string &data)
451 {
452         //infostream<<"RatCAO: Got init data"<<std::endl;
453         
454         {
455                 std::istringstream is(data, std::ios::binary);
456                 // version
457                 u8 version = readU8(is);
458                 // check version
459                 if(version != 0)
460                         return;
461                 // pos
462                 m_position = readV3F1000(is);
463                 pos_translator.init(m_position);
464         }
465         
466         updateNodePos();
467 }
468
469 /*
470         Oerkki1CAO
471 */
472
473 #include "inventory.h"
474
475 // Prototype
476 Oerkki1CAO proto_Oerkki1CAO;
477
478 Oerkki1CAO::Oerkki1CAO():
479         ClientActiveObject(0),
480         m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2.,BS/3.),
481         m_node(NULL),
482         m_position(v3f(0,10*BS,0)),
483         m_yaw(0),
484         m_damage_visual_timer(0),
485         m_damage_texture_enabled(false)
486 {
487         ClientActiveObject::registerType(getType(), create);
488 }
489
490 Oerkki1CAO::~Oerkki1CAO()
491 {
492 }
493
494 ClientActiveObject* Oerkki1CAO::create()
495 {
496         return new Oerkki1CAO();
497 }
498
499 void Oerkki1CAO::addToScene(scene::ISceneManager *smgr)
500 {
501         if(m_node != NULL)
502                 return;
503         
504         video::IVideoDriver* driver = smgr->getVideoDriver();
505         
506         scene::SMesh *mesh = new scene::SMesh();
507         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
508         video::SColor c(255,255,255,255);
509         video::S3DVertex vertices[4] =
510         {
511                 video::S3DVertex(-BS/2-BS,0,0, 0,0,0, c, 0,1),
512                 video::S3DVertex(BS/2+BS,0,0, 0,0,0, c, 1,1),
513                 video::S3DVertex(BS/2+BS,BS*2,0, 0,0,0, c, 1,0),
514                 video::S3DVertex(-BS/2-BS,BS*2,0, 0,0,0, c, 0,0),
515         };
516         u16 indices[] = {0,1,2,2,3,0};
517         buf->append(vertices, 4, indices, 6);
518         // Set material
519         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
520         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
521         //buf->getMaterial().setTexture(0, NULL);
522         buf->getMaterial().setTexture
523                         (0, driver->getTexture(getTexturePath("oerkki1.png").c_str()));
524         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
525         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
526         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
527         // Add to mesh
528         mesh->addMeshBuffer(buf);
529         buf->drop();
530         m_node = smgr->addMeshSceneNode(mesh, NULL);
531         mesh->drop();
532         // Set it to use the materials of the meshbuffers directly.
533         // This is needed for changing the texture in the future
534         m_node->setReadOnlyMaterials(true);
535         updateNodePos();
536 }
537
538 void Oerkki1CAO::removeFromScene()
539 {
540         if(m_node == NULL)
541                 return;
542
543         m_node->remove();
544         m_node = NULL;
545 }
546
547 void Oerkki1CAO::updateLight(u8 light_at_pos)
548 {
549         if(m_node == NULL)
550                 return;
551         
552         if(light_at_pos <= 2)
553         {
554                 m_node->setVisible(false);
555                 return;
556         }
557
558         m_node->setVisible(true);
559
560         u8 li = decode_light(light_at_pos);
561         video::SColor color(255,li,li,li);
562         setMeshVerticesColor(m_node->getMesh(), color);
563 }
564
565 v3s16 Oerkki1CAO::getLightPosition()
566 {
567         return floatToInt(m_position+v3f(0,BS*1.5,0), BS);
568 }
569
570 void Oerkki1CAO::updateNodePos()
571 {
572         if(m_node == NULL)
573                 return;
574
575         //m_node->setPosition(m_position);
576         m_node->setPosition(pos_translator.vect_show);
577
578         v3f rot = m_node->getRotation();
579         rot.Y = 180.0 - m_yaw + 90.0;
580         m_node->setRotation(rot);
581 }
582
583 void Oerkki1CAO::step(float dtime, ClientEnvironment *env)
584 {
585         pos_translator.translate(dtime);
586         updateNodePos();
587
588         LocalPlayer *player = env->getLocalPlayer();
589         assert(player);
590         
591         v3f playerpos = player->getPosition();
592         v2f playerpos_2d(playerpos.X,playerpos.Z);
593         v2f objectpos_2d(m_position.X,m_position.Z);
594
595         if(fabs(m_position.Y - playerpos.Y) < 1.5*BS &&
596                         objectpos_2d.getDistanceFrom(playerpos_2d) < 1.5*BS)
597         {
598                 if(m_attack_interval.step(dtime, 0.5))
599                 {
600                         env->damageLocalPlayer(2);
601                 }
602         }
603
604         if(m_damage_visual_timer > 0)
605         {
606                 if(!m_damage_texture_enabled)
607                 {
608                         // Enable damage texture
609                         if(m_node)
610                         {
611                                 video::IVideoDriver* driver =
612                                         m_node->getSceneManager()->getVideoDriver();
613                                 
614                                 scene::IMesh *mesh = m_node->getMesh();
615                                 if(mesh == NULL)
616                                         return;
617                                 
618                                 u16 mc = mesh->getMeshBufferCount();
619                                 for(u16 j=0; j<mc; j++)
620                                 {
621                                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
622                                         buf->getMaterial().setTexture(0, driver->getTexture(
623                                                         getTexturePath("oerkki1_damaged.png").c_str()));
624                                 }
625                         }
626                         m_damage_texture_enabled = true;
627                 }
628                 m_damage_visual_timer -= dtime;
629         }
630         else
631         {
632                 if(m_damage_texture_enabled)
633                 {
634                         // Disable damage texture
635                         if(m_node)
636                         {
637                                 video::IVideoDriver* driver =
638                                         m_node->getSceneManager()->getVideoDriver();
639                                 
640                                 scene::IMesh *mesh = m_node->getMesh();
641                                 if(mesh == NULL)
642                                         return;
643                                 
644                                 u16 mc = mesh->getMeshBufferCount();
645                                 for(u16 j=0; j<mc; j++)
646                                 {
647                                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
648                                         buf->getMaterial().setTexture(0, driver->getTexture(
649                                                         getTexturePath("oerkki1.png").c_str()));
650                                 }
651                         }
652                         m_damage_texture_enabled = false;
653                 }
654         }
655 }
656
657 void Oerkki1CAO::processMessage(const std::string &data)
658 {
659         //infostream<<"Oerkki1CAO: Got message"<<std::endl;
660         std::istringstream is(data, std::ios::binary);
661         // command
662         u8 cmd = readU8(is);
663         if(cmd == 0)
664         {
665                 // pos
666                 m_position = readV3F1000(is);
667                 pos_translator.update(m_position);
668                 // yaw
669                 m_yaw = readF1000(is);
670                 updateNodePos();
671         }
672         else if(cmd == 1)
673         {
674                 //u16 damage = readU8(is);
675                 m_damage_visual_timer = 1.0;
676         }
677 }
678
679 void Oerkki1CAO::initialize(const std::string &data)
680 {
681         //infostream<<"Oerkki1CAO: Got init data"<<std::endl;
682         
683         {
684                 std::istringstream is(data, std::ios::binary);
685                 // version
686                 u8 version = readU8(is);
687                 // check version
688                 if(version != 0)
689                         return;
690                 // pos
691                 m_position = readV3F1000(is);
692                 pos_translator.init(m_position);
693         }
694         
695         updateNodePos();
696 }
697
698 bool Oerkki1CAO::directReportPunch(const std::string &toolname, v3f dir)
699 {
700         m_damage_visual_timer = 1.0;
701
702         m_position += dir * BS;
703         pos_translator.sharpen();
704         pos_translator.update(m_position);
705         updateNodePos();
706         
707         return false;
708 }
709
710 /*
711         FireflyCAO
712 */
713
714 // Prototype
715 FireflyCAO proto_FireflyCAO;
716
717 FireflyCAO::FireflyCAO():
718         ClientActiveObject(0),
719         m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS/2.,BS/3.),
720         m_node(NULL),
721         m_position(v3f(0,10*BS,0)),
722         m_yaw(0)
723 {
724         ClientActiveObject::registerType(getType(), create);
725 }
726
727 FireflyCAO::~FireflyCAO()
728 {
729 }
730
731 ClientActiveObject* FireflyCAO::create()
732 {
733         return new FireflyCAO();
734 }
735
736 void FireflyCAO::addToScene(scene::ISceneManager *smgr)
737 {
738         if(m_node != NULL)
739                 return;
740         
741         video::IVideoDriver* driver = smgr->getVideoDriver();
742         
743         scene::SMesh *mesh = new scene::SMesh();
744         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
745         video::SColor c(255,255,255,255);
746         video::S3DVertex vertices[4] =
747         {
748                 video::S3DVertex(0,0,0, 0,0,0, c, 0,1),
749                 video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
750                 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
751                 video::S3DVertex(0,BS/2,0, 0,0,0, c, 0,0),
752         };
753         u16 indices[] = {0,1,2,2,3,0};
754         buf->append(vertices, 4, indices, 6);
755         // Set material
756         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
757         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
758         //buf->getMaterial().setTexture(0, NULL);
759         buf->getMaterial().setTexture
760                         (0, driver->getTexture(getTexturePath("firefly.png").c_str()));
761         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
762         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
763         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
764         // Add to mesh
765         mesh->addMeshBuffer(buf);
766         buf->drop();
767         m_node = smgr->addMeshSceneNode(mesh, NULL);
768         mesh->drop();
769         // Set it to use the materials of the meshbuffers directly.
770         // This is needed for changing the texture in the future
771         m_node->setReadOnlyMaterials(true);
772         updateNodePos();
773 }
774
775 void FireflyCAO::removeFromScene()
776 {
777         if(m_node == NULL)
778                 return;
779
780         m_node->remove();
781         m_node = NULL;
782 }
783
784 void FireflyCAO::updateLight(u8 light_at_pos)
785 {
786         if(m_node == NULL)
787                 return;
788
789         u8 li = 255;
790         video::SColor color(255,li,li,li);
791         setMeshVerticesColor(m_node->getMesh(), color);
792 }
793
794 v3s16 FireflyCAO::getLightPosition()
795 {
796         return floatToInt(m_position+v3f(0,BS*0.5,0), BS);
797 }
798
799 void FireflyCAO::updateNodePos()
800 {
801         if(m_node == NULL)
802                 return;
803
804         //m_node->setPosition(m_position);
805         m_node->setPosition(pos_translator.vect_show);
806
807         v3f rot = m_node->getRotation();
808         rot.Y = 180.0 - m_yaw;
809         m_node->setRotation(rot);
810 }
811
812 void FireflyCAO::step(float dtime, ClientEnvironment *env)
813 {
814         pos_translator.translate(dtime);
815         updateNodePos();
816 }
817
818 void FireflyCAO::processMessage(const std::string &data)
819 {
820         //infostream<<"FireflyCAO: Got message"<<std::endl;
821         std::istringstream is(data, std::ios::binary);
822         // command
823         u8 cmd = readU8(is);
824         if(cmd == 0)
825         {
826                 // pos
827                 m_position = readV3F1000(is);
828                 pos_translator.update(m_position);
829                 // yaw
830                 m_yaw = readF1000(is);
831                 updateNodePos();
832         }
833 }
834
835 void FireflyCAO::initialize(const std::string &data)
836 {
837         //infostream<<"FireflyCAO: Got init data"<<std::endl;
838         
839         {
840                 std::istringstream is(data, std::ios::binary);
841                 // version
842                 u8 version = readU8(is);
843                 // check version
844                 if(version != 0)
845                         return;
846                 // pos
847                 m_position = readV3F1000(is);
848                 pos_translator.init(m_position);
849         }
850         
851         updateNodePos();
852 }
853
854 /*
855         MobV2CAO
856 */
857
858 // Prototype
859 MobV2CAO proto_MobV2CAO;
860
861 MobV2CAO::MobV2CAO():
862         ClientActiveObject(0),
863         m_selection_box(-0.4*BS,-0.4*BS,-0.4*BS, 0.4*BS,0.8*BS,0.4*BS),
864         m_node(NULL),
865         m_position(v3f(0,10*BS,0)),
866         m_yaw(0),
867         m_walking(false),
868         m_walking_unset_timer(0),
869         m_walk_timer(0),
870         m_walk_frame(0),
871         m_damage_visual_timer(0),
872         m_last_light(0),
873         m_shooting(0),
874         m_shooting_unset_timer(0),
875         m_sprite_size(BS,BS),
876         m_sprite_y(0),
877         m_bright_shooting(false),
878         m_lock_full_brightness(false),
879         m_player_hit_timer(0)
880 {
881         ClientActiveObject::registerType(getType(), create);
882
883         m_properties = new Settings;
884 }
885
886 MobV2CAO::~MobV2CAO()
887 {
888         delete m_properties;
889 }
890
891 ClientActiveObject* MobV2CAO::create()
892 {
893         return new MobV2CAO();
894 }
895
896 void MobV2CAO::addToScene(scene::ISceneManager *smgr)
897 {
898         if(m_node != NULL)
899                 return;
900         
901         /*infostream<<"MobV2CAO::addToScene using texture_name="<<
902                         m_texture_name<<std::endl;*/
903         std::string texture_string = "[makealpha2:128,0,0;128,128,0:";
904         texture_string += m_texture_name;
905         
906         scene::MyBillboardSceneNode *bill = new scene::MyBillboardSceneNode(
907                         smgr->getRootSceneNode(), smgr, -1, v3f(0,0,0), v2f(1,1));
908         bill->setMaterialTexture(0, g_texturesource->getTextureRaw(texture_string));
909         bill->setMaterialFlag(video::EMF_LIGHTING, false);
910         bill->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
911         bill->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF);
912         bill->setMaterialFlag(video::EMF_FOG_ENABLE, true);
913         bill->setColor(video::SColor(255,0,0,0));
914         bill->setVisible(false); /* Set visible when brightness is known */
915         bill->setSize(m_sprite_size);
916         if(m_sprite_type == "humanoid_1"){
917                 const float txp = 1./192;
918                 const float txs = txp*32;
919                 const float typ = 1./240;
920                 const float tys = typ*48;
921                 bill->setTCoords(0, v2f(txs*1, tys*1));
922                 bill->setTCoords(1, v2f(txs*1, tys*0));
923                 bill->setTCoords(2, v2f(txs*0, tys*0));
924                 bill->setTCoords(3, v2f(txs*0, tys*1));
925         } else if(m_sprite_type == "simple"){
926                 const float txs = 1.0;
927                 const float tys = 1.0 / m_simple_anim_frames;
928                 bill->setTCoords(0, v2f(txs*1, tys*1));
929                 bill->setTCoords(1, v2f(txs*1, tys*0));
930                 bill->setTCoords(2, v2f(txs*0, tys*0));
931                 bill->setTCoords(3, v2f(txs*0, tys*1));
932         } else {
933                 infostream<<"MobV2CAO: Unknown sprite type \""<<m_sprite_type<<"\""
934                                 <<std::endl;
935         }
936
937         m_node = bill;
938
939         updateNodePos();
940 }
941
942 void MobV2CAO::removeFromScene()
943 {
944         if(m_node == NULL)
945                 return;
946
947         m_node->drop();
948         m_node->remove();
949         m_node = NULL;
950 }
951
952 void MobV2CAO::updateLight(u8 light_at_pos)
953 {
954         if(m_lock_full_brightness)
955                 light_at_pos = 15;
956         
957         m_last_light = light_at_pos;
958
959         if(m_node == NULL)
960                 return;
961         
962         if(m_damage_visual_timer > 0)
963                 return;
964         
965         if(m_shooting && m_bright_shooting)
966                 return;
967         
968         /*if(light_at_pos <= 2){
969                 m_node->setVisible(false);
970                 return;
971         }*/
972
973         m_node->setVisible(true);
974
975         u8 li = decode_light(light_at_pos);
976         video::SColor color(255,li,li,li);
977         m_node->setColor(color);
978 }
979
980 v3s16 MobV2CAO::getLightPosition()
981 {
982         return floatToInt(m_position+v3f(0,0,0), BS);
983 }
984
985 void MobV2CAO::updateNodePos()
986 {
987         if(m_node == NULL)
988                 return;
989
990         m_node->setPosition(pos_translator.vect_show + v3f(0,m_sprite_y,0));
991 }
992
993 void MobV2CAO::step(float dtime, ClientEnvironment *env)
994 {
995         scene::MyBillboardSceneNode *bill = m_node;
996         if(!bill)
997                 return;
998
999         pos_translator.translate(dtime);
1000         
1001         if(m_sprite_type == "humanoid_1"){
1002                 scene::ICameraSceneNode* camera = m_node->getSceneManager()->getActiveCamera();
1003                 if(!camera)
1004                         return;
1005                 v3f cam_to_mob = m_node->getAbsolutePosition() - camera->getAbsolutePosition();
1006                 cam_to_mob.normalize();
1007                 int col = 0;
1008                 if(cam_to_mob.Y > 0.75)
1009                         col = 5;
1010                 else if(cam_to_mob.Y < -0.75)
1011                         col = 4;
1012                 else{
1013                         float mob_dir = atan2(cam_to_mob.Z, cam_to_mob.X) / PI * 180.;
1014                         float dir = mob_dir - m_yaw;
1015                         dir = wrapDegrees_180(dir);
1016                         //infostream<<"id="<<m_id<<" dir="<<dir<<std::endl;
1017                         if(fabs(wrapDegrees_180(dir - 0)) <= 45.1)
1018                                 col = 2;
1019                         else if(fabs(wrapDegrees_180(dir - 90)) <= 45.1)
1020                                 col = 3;
1021                         else if(fabs(wrapDegrees_180(dir - 180)) <= 45.1)
1022                                 col = 0;
1023                         else if(fabs(wrapDegrees_180(dir + 90)) <= 45.1)
1024                                 col = 1;
1025                         else
1026                                 col = 4;
1027                 }
1028
1029                 int row = 0;
1030                 if(m_shooting){
1031                         row = 3;
1032                 } else if(m_walking){
1033                         m_walk_timer += dtime;
1034                         if(m_walk_timer >= 0.5){
1035                                 m_walk_frame = (m_walk_frame + 1) % 2;
1036                                 m_walk_timer = 0;
1037                         }
1038                         if(m_walk_frame == 0)
1039                                 row = 1;
1040                         else
1041                                 row = 2;
1042                 }
1043
1044                 const float txp = 1./192;
1045                 const float txs = txp*32;
1046                 const float typ = 1./240;
1047                 const float tys = typ*48;
1048                 bill->setTCoords(0, v2f(txs*(1+col), tys*(1+row)));
1049                 bill->setTCoords(1, v2f(txs*(1+col), tys*(0+row)));
1050                 bill->setTCoords(2, v2f(txs*(0+col), tys*(0+row)));
1051                 bill->setTCoords(3, v2f(txs*(0+col), tys*(1+row)));
1052         } else if(m_sprite_type == "simple"){
1053                 m_walk_timer += dtime;
1054                 if(m_walk_timer >= m_simple_anim_frametime){
1055                         m_walk_frame = (m_walk_frame + 1) % m_simple_anim_frames;
1056                         m_walk_timer = 0;
1057                 }
1058                 int col = 0;
1059                 int row = m_walk_frame;
1060                 const float txs = 1.0;
1061                 const float tys = 1.0 / m_simple_anim_frames;
1062                 bill->setTCoords(0, v2f(txs*(1+col), tys*(1+row)));
1063                 bill->setTCoords(1, v2f(txs*(1+col), tys*(0+row)));
1064                 bill->setTCoords(2, v2f(txs*(0+col), tys*(0+row)));
1065                 bill->setTCoords(3, v2f(txs*(0+col), tys*(1+row)));
1066         } else {
1067                 infostream<<"MobV2CAO::step(): Unknown sprite type \""
1068                                 <<m_sprite_type<<"\""<<std::endl;
1069         }
1070
1071         updateNodePos();
1072
1073         /* Damage local player */
1074         if(m_player_hit_damage && m_player_hit_timer <= 0.0){
1075                 LocalPlayer *player = env->getLocalPlayer();
1076                 assert(player);
1077                 
1078                 v3f playerpos = player->getPosition();
1079                 v2f playerpos_2d(playerpos.X,playerpos.Z);
1080                 v2f objectpos_2d(m_position.X,m_position.Z);
1081
1082                 if(fabs(m_position.Y - playerpos.Y) < m_player_hit_distance*BS &&
1083                 objectpos_2d.getDistanceFrom(playerpos_2d) < m_player_hit_distance*BS)
1084                 {
1085                         env->damageLocalPlayer(m_player_hit_damage);
1086                         m_player_hit_timer = m_player_hit_interval;
1087                 }
1088         }
1089
1090         /* Run timers */
1091
1092         m_player_hit_timer -= dtime;
1093
1094         if(m_damage_visual_timer >= 0){
1095                 m_damage_visual_timer -= dtime;
1096                 if(m_damage_visual_timer <= 0){
1097                         infostream<<"id="<<m_id<<" damage visual ended"<<std::endl;
1098                 }
1099         }
1100
1101         m_walking_unset_timer += dtime;
1102         if(m_walking_unset_timer >= 1.0){
1103                 m_walking = false;
1104         }
1105
1106         m_shooting_unset_timer -= dtime;
1107         if(m_shooting_unset_timer <= 0.0){
1108                 if(m_bright_shooting){
1109                         u8 li = decode_light(m_last_light);
1110                         video::SColor color(255,li,li,li);
1111                         bill->setColor(color);
1112                         m_bright_shooting = false;
1113                 }
1114                 m_shooting = false;
1115         }
1116
1117 }
1118
1119 void MobV2CAO::processMessage(const std::string &data)
1120 {
1121         //infostream<<"MobV2CAO: Got message"<<std::endl;
1122         std::istringstream is(data, std::ios::binary);
1123         // command
1124         u8 cmd = readU8(is);
1125
1126         // Move
1127         if(cmd == 0)
1128         {
1129                 // pos
1130                 m_position = readV3F1000(is);
1131                 pos_translator.update(m_position);
1132                 // yaw
1133                 m_yaw = readF1000(is);
1134
1135                 m_walking = true;
1136                 m_walking_unset_timer = 0;
1137
1138                 updateNodePos();
1139         }
1140         // Damage
1141         else if(cmd == 1)
1142         {
1143                 //u16 damage = readU16(is);
1144
1145                 /*u8 li = decode_light(m_last_light);
1146                 if(li >= 100)
1147                         li = 30;
1148                 else
1149                         li = 255;*/
1150
1151                 /*video::SColor color(255,255,0,0);
1152                 m_node->setColor(color);
1153
1154                 m_damage_visual_timer = 0.2;*/
1155         }
1156         // Trigger shooting
1157         else if(cmd == 2)
1158         {
1159                 // length
1160                 m_shooting_unset_timer = readF1000(is);
1161                 // bright?
1162                 m_bright_shooting = readU8(is);
1163                 if(m_bright_shooting){
1164                         u8 li = 255;
1165                         video::SColor color(255,li,li,li);
1166                         m_node->setColor(color);
1167                 }
1168
1169                 m_shooting = true;
1170         }
1171 }
1172
1173 void MobV2CAO::initialize(const std::string &data)
1174 {
1175         //infostream<<"MobV2CAO: Got init data"<<std::endl;
1176         
1177         {
1178                 std::istringstream is(data, std::ios::binary);
1179                 // version
1180                 u8 version = readU8(is);
1181                 // check version
1182                 if(version != 0){
1183                         infostream<<__FUNCTION_NAME<<": Invalid version"<<std::endl;
1184                         return;
1185                 }
1186                 
1187                 std::ostringstream tmp_os(std::ios::binary);
1188                 decompressZlib(is, tmp_os);
1189                 std::istringstream tmp_is(tmp_os.str(), std::ios::binary);
1190                 m_properties->parseConfigLines(tmp_is, "MobArgsEnd");
1191
1192                 infostream<<"MobV2CAO::initialize(): got properties:"<<std::endl;
1193                 m_properties->writeLines(infostream);
1194                 
1195                 m_properties->setDefault("looks", "dummy_default");
1196                 m_properties->setDefault("yaw", "0");
1197                 m_properties->setDefault("pos", "(0,0,0)");
1198                 m_properties->setDefault("player_hit_damage", "0");
1199                 m_properties->setDefault("player_hit_distance", "1.5");
1200                 m_properties->setDefault("player_hit_interval", "1.5");
1201                 
1202                 setLooks(m_properties->get("looks"));
1203                 m_yaw = m_properties->getFloat("yaw");
1204                 m_position = m_properties->getV3F("pos");
1205                 m_player_hit_damage = m_properties->getS32("player_hit_damage");
1206                 m_player_hit_distance = m_properties->getFloat("player_hit_distance");
1207                 m_player_hit_interval = m_properties->getFloat("player_hit_interval");
1208
1209                 pos_translator.init(m_position);
1210         }
1211         
1212         updateNodePos();
1213 }
1214
1215 bool MobV2CAO::directReportPunch(const std::string &toolname, v3f dir)
1216 {
1217         video::SColor color(255,255,0,0);
1218         m_node->setColor(color);
1219
1220         m_damage_visual_timer = 0.05;
1221
1222         m_position += dir * BS;
1223         pos_translator.sharpen();
1224         pos_translator.update(m_position);
1225         updateNodePos();
1226         
1227         return false;
1228 }
1229
1230 void MobV2CAO::setLooks(const std::string &looks)
1231 {
1232         v2f selection_size = v2f(0.4, 0.4) * BS;
1233         float selection_y = 0 * BS;
1234
1235         if(looks == "dungeon_master"){
1236                 m_texture_name = "dungeon_master.png";
1237                 m_sprite_type = "humanoid_1";
1238                 m_sprite_size = v2f(2, 3) * BS;
1239                 m_sprite_y = 0.85 * BS;
1240                 selection_size = v2f(0.4, 2.6) * BS;
1241                 selection_y = -0.4 * BS;
1242         }
1243         else if(looks == "fireball"){
1244                 m_texture_name = "fireball.png";
1245                 m_sprite_type = "simple";
1246                 m_sprite_size = v2f(1, 1) * BS;
1247                 m_simple_anim_frames = 3;
1248                 m_simple_anim_frametime = 0.1;
1249                 m_lock_full_brightness = true;
1250         }
1251         else{
1252                 m_texture_name = "stone.png";
1253                 m_sprite_type = "simple";
1254                 m_sprite_size = v2f(1, 1) * BS;
1255                 m_simple_anim_frames = 3;
1256                 m_simple_anim_frametime = 0.333;
1257                 selection_size = v2f(0.4, 0.4) * BS;
1258                 selection_y = 0 * BS;
1259         }
1260
1261         m_selection_box = core::aabbox3d<f32>(
1262                         -selection_size.X, selection_y, -selection_size.X,
1263                         selection_size.X, selection_y+selection_size.Y,
1264                         selection_size.X);
1265 }
1266
1267 /*
1268         LuaEntityCAO
1269 */
1270
1271 // Prototype
1272 LuaEntityCAO proto_LuaEntityCAO;
1273
1274 LuaEntityCAO::LuaEntityCAO():
1275         ClientActiveObject(0),
1276         m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.),
1277         m_node(NULL),
1278         m_position(v3f(0,10*BS,0))
1279 {
1280         ClientActiveObject::registerType(getType(), create);
1281 }
1282
1283 LuaEntityCAO::~LuaEntityCAO()
1284 {
1285 }
1286
1287 ClientActiveObject* LuaEntityCAO::create()
1288 {
1289         return new LuaEntityCAO();
1290 }
1291
1292 void LuaEntityCAO::addToScene(scene::ISceneManager *smgr)
1293 {
1294         if(m_node != NULL)
1295                 return;
1296         
1297         video::IVideoDriver* driver = smgr->getVideoDriver();
1298         
1299         scene::SMesh *mesh = new scene::SMesh();
1300         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
1301         video::SColor c(255,255,255,255);
1302         video::S3DVertex vertices[4] =
1303         {
1304                 /*video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1),
1305                 video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1),
1306                 video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0),
1307                 video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),*/
1308                 video::S3DVertex(BS/3.,0,0, 0,0,0, c, 0,1),
1309                 video::S3DVertex(-BS/3.,0,0, 0,0,0, c, 1,1),
1310                 video::S3DVertex(-BS/3.,0+BS*2./3.,0, 0,0,0, c, 1,0),
1311                 video::S3DVertex(BS/3.,0+BS*2./3.,0, 0,0,0, c, 0,0),
1312         };
1313         u16 indices[] = {0,1,2,2,3,0};
1314         buf->append(vertices, 4, indices, 6);
1315         // Set material
1316         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
1317         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
1318         //buf->getMaterial().setTexture(0, NULL);
1319         // Initialize with the stick texture
1320         buf->getMaterial().setTexture
1321                         (0, driver->getTexture(getTexturePath("mese.png").c_str()));
1322         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
1323         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
1324         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
1325         // Add to mesh
1326         mesh->addMeshBuffer(buf);
1327         buf->drop();
1328         m_node = smgr->addMeshSceneNode(mesh, NULL);
1329         mesh->drop();
1330         // Set it to use the materials of the meshbuffers directly.
1331         // This is needed for changing the texture in the future
1332         m_node->setReadOnlyMaterials(true);
1333         updateNodePos();
1334 }
1335
1336 void LuaEntityCAO::removeFromScene()
1337 {
1338         if(m_node == NULL)
1339                 return;
1340
1341         m_node->remove();
1342         m_node = NULL;
1343 }
1344
1345 void LuaEntityCAO::updateLight(u8 light_at_pos)
1346 {
1347         if(m_node == NULL)
1348                 return;
1349
1350         u8 li = decode_light(light_at_pos);
1351         video::SColor color(255,li,li,li);
1352         setMeshVerticesColor(m_node->getMesh(), color);
1353 }
1354
1355 v3s16 LuaEntityCAO::getLightPosition()
1356 {
1357         return floatToInt(m_position, BS);
1358 }
1359
1360 void LuaEntityCAO::updateNodePos()
1361 {
1362         if(m_node == NULL)
1363                 return;
1364
1365         m_node->setPosition(m_position);
1366 }
1367
1368 void LuaEntityCAO::step(float dtime, ClientEnvironment *env)
1369 {
1370         if(m_node)
1371         {
1372                 /*v3f rot = m_node->getRotation();
1373                 rot.Y += dtime * 120;
1374                 m_node->setRotation(rot);*/
1375                 LocalPlayer *player = env->getLocalPlayer();
1376                 assert(player);
1377                 v3f rot = m_node->getRotation();
1378                 rot.Y = 180.0 - (player->getYaw());
1379                 m_node->setRotation(rot);
1380         }
1381 }
1382
1383 void LuaEntityCAO::processMessage(const std::string &data)
1384 {
1385         infostream<<"LuaEntityCAO: Got message"<<std::endl;
1386         std::istringstream is(data, std::ios::binary);
1387         // command
1388         u8 cmd = readU8(is);
1389         if(cmd == 0)
1390         {
1391                 // pos
1392                 m_position = readV3F1000(is);
1393                 updateNodePos();
1394         }
1395 }
1396
1397 void LuaEntityCAO::initialize(const std::string &data)
1398 {
1399         infostream<<"LuaEntityCAO: Got init data"<<std::endl;
1400         
1401         {
1402                 std::istringstream is(data, std::ios::binary);
1403                 // version
1404                 u8 version = readU8(is);
1405                 // check version
1406                 if(version != 0)
1407                         return;
1408                 // pos
1409                 m_position = readV3F1000(is);
1410         }
1411         
1412         updateNodePos();
1413 }
1414
1415