Generic NodeMetadata text input
[oweals/minetest.git] / src / content_sao.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_sao.h"
21 #include "collision.h"
22 #include "environment.h"
23 #include "settings.h"
24 #include "profiler.h"
25
26 core::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
27
28 /* Some helper functions */
29
30 // Y is copied, X and Z change is limited
31 void accelerate_xz(v3f &speed, v3f target_speed, f32 max_increase)
32 {
33         v3f d_wanted = target_speed - speed;
34         d_wanted.Y = 0;
35         f32 dl_wanted = d_wanted.getLength();
36         f32 dl = dl_wanted;
37         if(dl > max_increase)
38                 dl = max_increase;
39         
40         v3f d = d_wanted.normalize() * dl;
41
42         speed.X += d.X;
43         speed.Z += d.Z;
44         speed.Y = target_speed.Y;
45 }
46
47 /*
48         TestSAO
49 */
50
51 // Prototype
52 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
53
54 TestSAO::TestSAO(ServerEnvironment *env, v3f pos):
55         ServerActiveObject(env, pos),
56         m_timer1(0),
57         m_age(0)
58 {
59         ServerActiveObject::registerType(getType(), create);
60 }
61
62 ServerActiveObject* TestSAO::create(ServerEnvironment *env, v3f pos,
63                 const std::string &data)
64 {
65         return new TestSAO(env, pos);
66 }
67
68 void TestSAO::step(float dtime, bool send_recommended)
69 {
70         m_age += dtime;
71         if(m_age > 10)
72         {
73                 m_removed = true;
74                 return;
75         }
76
77         m_base_position.Y += dtime * BS * 2;
78         if(m_base_position.Y > 8*BS)
79                 m_base_position.Y = 2*BS;
80
81         if(send_recommended == false)
82                 return;
83
84         m_timer1 -= dtime;
85         if(m_timer1 < 0.0)
86         {
87                 m_timer1 += 0.125;
88
89                 std::string data;
90
91                 data += itos(0); // 0 = position
92                 data += " ";
93                 data += itos(m_base_position.X);
94                 data += " ";
95                 data += itos(m_base_position.Y);
96                 data += " ";
97                 data += itos(m_base_position.Z);
98
99                 ActiveObjectMessage aom(getId(), false, data);
100                 m_messages_out.push_back(aom);
101         }
102 }
103
104
105 /*
106         ItemSAO
107 */
108
109 // Prototype
110 ItemSAO proto_ItemSAO(NULL, v3f(0,0,0), "");
111
112 ItemSAO::ItemSAO(ServerEnvironment *env, v3f pos,
113                 const std::string inventorystring):
114         ServerActiveObject(env, pos),
115         m_inventorystring(inventorystring),
116         m_speed_f(0,0,0),
117         m_last_sent_position(0,0,0)
118 {
119         ServerActiveObject::registerType(getType(), create);
120 }
121
122 ServerActiveObject* ItemSAO::create(ServerEnvironment *env, v3f pos,
123                 const std::string &data)
124 {
125         std::istringstream is(data, std::ios::binary);
126         char buf[1];
127         // read version
128         is.read(buf, 1);
129         u8 version = buf[0];
130         // check if version is supported
131         if(version != 0)
132                 return NULL;
133         std::string inventorystring = deSerializeString(is);
134         infostream<<"ItemSAO::create(): Creating item \""
135                         <<inventorystring<<"\""<<std::endl;
136         return new ItemSAO(env, pos, inventorystring);
137 }
138
139 void ItemSAO::step(float dtime, bool send_recommended)
140 {
141         ScopeProfiler sp2(g_profiler, "ItemSAO::step avg", SPT_AVG);
142
143         assert(m_env);
144
145         const float interval = 0.2;
146         if(m_move_interval.step(dtime, interval)==false)
147                 return;
148         dtime = interval;
149         
150         core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.);
151         collisionMoveResult moveresult;
152         // Apply gravity
153         m_speed_f += v3f(0, -dtime*9.81*BS, 0);
154         // Maximum movement without glitches
155         f32 pos_max_d = BS*0.25;
156         // Limit speed
157         if(m_speed_f.getLength()*dtime > pos_max_d)
158                 m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
159         v3f pos_f = getBasePosition();
160         v3f pos_f_old = pos_f;
161         moveresult = collisionMoveSimple(&m_env->getMap(), pos_max_d,
162                         box, dtime, pos_f, m_speed_f);
163         
164         if(send_recommended == false)
165                 return;
166
167         if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
168         {
169                 setBasePosition(pos_f);
170                 m_last_sent_position = pos_f;
171
172                 std::ostringstream os(std::ios::binary);
173                 char buf[6];
174                 // command (0 = update position)
175                 buf[0] = 0;
176                 os.write(buf, 1);
177                 // pos
178                 writeS32((u8*)buf, m_base_position.X*1000);
179                 os.write(buf, 4);
180                 writeS32((u8*)buf, m_base_position.Y*1000);
181                 os.write(buf, 4);
182                 writeS32((u8*)buf, m_base_position.Z*1000);
183                 os.write(buf, 4);
184                 // create message and add to list
185                 ActiveObjectMessage aom(getId(), false, os.str());
186                 m_messages_out.push_back(aom);
187         }
188 }
189
190 std::string ItemSAO::getClientInitializationData()
191 {
192         std::ostringstream os(std::ios::binary);
193         char buf[6];
194         // version
195         buf[0] = 0;
196         os.write(buf, 1);
197         // pos
198         writeS32((u8*)buf, m_base_position.X*1000);
199         os.write(buf, 4);
200         writeS32((u8*)buf, m_base_position.Y*1000);
201         os.write(buf, 4);
202         writeS32((u8*)buf, m_base_position.Z*1000);
203         os.write(buf, 4);
204         // inventorystring
205         os<<serializeString(m_inventorystring);
206         return os.str();
207 }
208
209 std::string ItemSAO::getStaticData()
210 {
211         infostream<<__FUNCTION_NAME<<std::endl;
212         std::ostringstream os(std::ios::binary);
213         char buf[1];
214         // version
215         buf[0] = 0;
216         os.write(buf, 1);
217         // inventorystring
218         os<<serializeString(m_inventorystring);
219         return os.str();
220 }
221
222 InventoryItem * ItemSAO::createInventoryItem()
223 {
224         try{
225                 std::istringstream is(m_inventorystring, std::ios_base::binary);
226                 InventoryItem *item = InventoryItem::deSerialize(is);
227                 infostream<<__FUNCTION_NAME<<": m_inventorystring=\""
228                                 <<m_inventorystring<<"\" -> item="<<item
229                                 <<std::endl;
230                 return item;
231         }
232         catch(SerializationError &e)
233         {
234                 infostream<<__FUNCTION_NAME<<": serialization error: "
235                                 <<"m_inventorystring=\""<<m_inventorystring<<"\""<<std::endl;
236                 return NULL;
237         }
238 }
239
240 void ItemSAO::punch(ServerActiveObject *puncher)
241 {
242         InventoryItem *item = createInventoryItem();
243         bool fits = puncher->addToInventory(item);
244         if(fits)
245                 m_removed = true;
246         else
247                 delete item;
248 }
249
250 void ItemSAO::rightClick(ServerActiveObject *clicker)
251 {
252         infostream<<__FUNCTION_NAME<<std::endl;
253         InventoryItem *item = createInventoryItem();
254         if(item == NULL)
255                 return;
256         
257         bool to_be_deleted = item->use(m_env, clicker);
258
259         if(to_be_deleted)
260                 m_removed = true;
261         else
262                 // Reflect changes to the item here
263                 m_inventorystring = item->getItemString();
264         
265         delete item; // Delete temporary item
266 }
267
268 /*
269         RatSAO
270 */
271
272 // Prototype
273 RatSAO proto_RatSAO(NULL, v3f(0,0,0));
274
275 RatSAO::RatSAO(ServerEnvironment *env, v3f pos):
276         ServerActiveObject(env, pos),
277         m_is_active(false),
278         m_speed_f(0,0,0)
279 {
280         ServerActiveObject::registerType(getType(), create);
281
282         m_oldpos = v3f(0,0,0);
283         m_last_sent_position = v3f(0,0,0);
284         m_yaw = myrand_range(0,PI*2);
285         m_counter1 = 0;
286         m_counter2 = 0;
287         m_age = 0;
288         m_touching_ground = false;
289 }
290
291 ServerActiveObject* RatSAO::create(ServerEnvironment *env, v3f pos,
292                 const std::string &data)
293 {
294         std::istringstream is(data, std::ios::binary);
295         char buf[1];
296         // read version
297         is.read(buf, 1);
298         u8 version = buf[0];
299         // check if version is supported
300         if(version != 0)
301                 return NULL;
302         return new RatSAO(env, pos);
303 }
304
305 void RatSAO::step(float dtime, bool send_recommended)
306 {
307         ScopeProfiler sp2(g_profiler, "RatSAO::step avg", SPT_AVG);
308
309         assert(m_env);
310
311         if(m_is_active == false)
312         {
313                 if(m_inactive_interval.step(dtime, 0.5)==false)
314                         return;
315         }
316
317         /*
318                 The AI
319         */
320
321         /*m_age += dtime;
322         if(m_age > 60)
323         {
324                 // Die
325                 m_removed = true;
326                 return;
327         }*/
328
329         // Apply gravity
330         m_speed_f.Y -= dtime*9.81*BS;
331
332         /*
333                 Move around if some player is close
334         */
335         bool player_is_close = false;
336         // Check connected players
337         core::list<Player*> players = m_env->getPlayers(true);
338         core::list<Player*>::Iterator i;
339         for(i = players.begin();
340                         i != players.end(); i++)
341         {
342                 Player *player = *i;
343                 v3f playerpos = player->getPosition();
344                 if(m_base_position.getDistanceFrom(playerpos) < BS*10.0)
345                 {
346                         player_is_close = true;
347                         break;
348                 }
349         }
350
351         m_is_active = player_is_close;
352         
353         if(player_is_close == false)
354         {
355                 m_speed_f.X = 0;
356                 m_speed_f.Z = 0;
357         }
358         else
359         {
360                 // Move around
361                 v3f dir(cos(m_yaw/180*PI),0,sin(m_yaw/180*PI));
362                 f32 speed = 2*BS;
363                 m_speed_f.X = speed * dir.X;
364                 m_speed_f.Z = speed * dir.Z;
365
366                 if(m_touching_ground && (m_oldpos - m_base_position).getLength()
367                                 < dtime*speed/2)
368                 {
369                         m_counter1 -= dtime;
370                         if(m_counter1 < 0.0)
371                         {
372                                 m_counter1 += 1.0;
373                                 m_speed_f.Y = 5.0*BS;
374                         }
375                 }
376
377                 {
378                         m_counter2 -= dtime;
379                         if(m_counter2 < 0.0)
380                         {
381                                 m_counter2 += (float)(myrand()%100)/100*3.0;
382                                 m_yaw += ((float)(myrand()%200)-100)/100*180;
383                                 m_yaw = wrapDegrees(m_yaw);
384                         }
385                 }
386         }
387         
388         m_oldpos = m_base_position;
389
390         /*
391                 Move it, with collision detection
392         */
393
394         core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.);
395         collisionMoveResult moveresult;
396         // Maximum movement without glitches
397         f32 pos_max_d = BS*0.25;
398         // Limit speed
399         if(m_speed_f.getLength()*dtime > pos_max_d)
400                 m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
401         v3f pos_f = getBasePosition();
402         v3f pos_f_old = pos_f;
403         moveresult = collisionMoveSimple(&m_env->getMap(), pos_max_d,
404                         box, dtime, pos_f, m_speed_f);
405         m_touching_ground = moveresult.touching_ground;
406         
407         setBasePosition(pos_f);
408
409         if(send_recommended == false)
410                 return;
411
412         if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
413         {
414                 m_last_sent_position = pos_f;
415
416                 std::ostringstream os(std::ios::binary);
417                 // command (0 = update position)
418                 writeU8(os, 0);
419                 // pos
420                 writeV3F1000(os, m_base_position);
421                 // yaw
422                 writeF1000(os, m_yaw);
423                 // create message and add to list
424                 ActiveObjectMessage aom(getId(), false, os.str());
425                 m_messages_out.push_back(aom);
426         }
427 }
428
429 std::string RatSAO::getClientInitializationData()
430 {
431         std::ostringstream os(std::ios::binary);
432         // version
433         writeU8(os, 0);
434         // pos
435         writeV3F1000(os, m_base_position);
436         return os.str();
437 }
438
439 std::string RatSAO::getStaticData()
440 {
441         //infostream<<__FUNCTION_NAME<<std::endl;
442         std::ostringstream os(std::ios::binary);
443         // version
444         writeU8(os, 0);
445         return os.str();
446 }
447
448 void RatSAO::punch(ServerActiveObject *puncher)
449 {
450         std::istringstream is("CraftItem rat 1", std::ios_base::binary);
451         InventoryItem *item = InventoryItem::deSerialize(is);
452         bool fits = puncher->addToInventory(item);
453         if(fits)
454                 m_removed = true;
455         else
456                 delete item;
457 }
458
459 /*
460         Oerkki1SAO
461 */
462
463 // Prototype
464 Oerkki1SAO proto_Oerkki1SAO(NULL, v3f(0,0,0));
465
466 Oerkki1SAO::Oerkki1SAO(ServerEnvironment *env, v3f pos):
467         ServerActiveObject(env, pos),
468         m_is_active(false),
469         m_speed_f(0,0,0)
470 {
471         ServerActiveObject::registerType(getType(), create);
472
473         m_oldpos = v3f(0,0,0);
474         m_last_sent_position = v3f(0,0,0);
475         m_yaw = 0;
476         m_counter1 = 0;
477         m_counter2 = 0;
478         m_age = 0;
479         m_touching_ground = false;
480         m_hp = 20;
481         m_after_jump_timer = 0;
482 }
483
484 ServerActiveObject* Oerkki1SAO::create(ServerEnvironment *env, v3f pos,
485                 const std::string &data)
486 {
487         std::istringstream is(data, std::ios::binary);
488         // read version
489         u8 version = readU8(is);
490         // read hp
491         u8 hp = readU8(is);
492         // check if version is supported
493         if(version != 0)
494                 return NULL;
495         Oerkki1SAO *o = new Oerkki1SAO(env, pos);
496         o->m_hp = hp;
497         return o;
498 }
499
500 void Oerkki1SAO::step(float dtime, bool send_recommended)
501 {
502         ScopeProfiler sp2(g_profiler, "Oerkki1SAO::step avg", SPT_AVG);
503
504         assert(m_env);
505
506         if(m_is_active == false)
507         {
508                 if(m_inactive_interval.step(dtime, 0.5)==false)
509                         return;
510         }
511
512         /*
513                 The AI
514         */
515
516         m_age += dtime;
517         if(m_age > 120)
518         {
519                 // Die
520                 m_removed = true;
521                 return;
522         }
523
524         m_after_jump_timer -= dtime;
525
526         v3f old_speed = m_speed_f;
527
528         // Apply gravity
529         m_speed_f.Y -= dtime*9.81*BS;
530
531         /*
532                 Move around if some player is close
533         */
534         bool player_is_close = false;
535         bool player_is_too_close = false;
536         v3f near_player_pos;
537         // Check connected players
538         core::list<Player*> players = m_env->getPlayers(true);
539         core::list<Player*>::Iterator i;
540         for(i = players.begin();
541                         i != players.end(); i++)
542         {
543                 Player *player = *i;
544                 v3f playerpos = player->getPosition();
545                 f32 dist = m_base_position.getDistanceFrom(playerpos);
546                 if(dist < BS*0.6)
547                 {
548                         m_removed = true;
549                         return;
550                         player_is_too_close = true;
551                         near_player_pos = playerpos;
552                 }
553                 else if(dist < BS*15.0 && !player_is_too_close)
554                 {
555                         player_is_close = true;
556                         near_player_pos = playerpos;
557                 }
558         }
559
560         m_is_active = player_is_close;
561
562         v3f target_speed = m_speed_f;
563
564         if(!player_is_close)
565         {
566                 target_speed = v3f(0,0,0);
567         }
568         else
569         {
570                 // Move around
571
572                 v3f ndir = near_player_pos - m_base_position;
573                 ndir.Y = 0;
574                 ndir.normalize();
575
576                 f32 nyaw = 180./PI*atan2(ndir.Z,ndir.X);
577                 if(nyaw < m_yaw - 180)
578                         nyaw += 360;
579                 else if(nyaw > m_yaw + 180)
580                         nyaw -= 360;
581                 m_yaw = 0.95*m_yaw + 0.05*nyaw;
582                 m_yaw = wrapDegrees(m_yaw);
583                 
584                 f32 speed = 2*BS;
585
586                 if((m_touching_ground || m_after_jump_timer > 0.0)
587                                 && !player_is_too_close)
588                 {
589                         v3f dir(cos(m_yaw/180*PI),0,sin(m_yaw/180*PI));
590                         target_speed.X = speed * dir.X;
591                         target_speed.Z = speed * dir.Z;
592                 }
593
594                 if(m_touching_ground && (m_oldpos - m_base_position).getLength()
595                                 < dtime*speed/2)
596                 {
597                         m_counter1 -= dtime;
598                         if(m_counter1 < 0.0)
599                         {
600                                 m_counter1 += 0.2;
601                                 // Jump
602                                 target_speed.Y = 5.0*BS;
603                                 m_after_jump_timer = 1.0;
604                         }
605                 }
606
607                 {
608                         m_counter2 -= dtime;
609                         if(m_counter2 < 0.0)
610                         {
611                                 m_counter2 += (float)(myrand()%100)/100*3.0;
612                                 //m_yaw += ((float)(myrand()%200)-100)/100*180;
613                                 m_yaw += ((float)(myrand()%200)-100)/100*90;
614                                 m_yaw = wrapDegrees(m_yaw);
615                         }
616                 }
617         }
618         
619         if((m_speed_f - target_speed).getLength() > BS*4 || player_is_too_close)
620                 accelerate_xz(m_speed_f, target_speed, dtime*BS*8);
621         else
622                 accelerate_xz(m_speed_f, target_speed, dtime*BS*4);
623         
624         m_oldpos = m_base_position;
625
626         /*
627                 Move it, with collision detection
628         */
629
630         core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*5./3.,BS/3.);
631         collisionMoveResult moveresult;
632         // Maximum movement without glitches
633         f32 pos_max_d = BS*0.25;
634         /*// Limit speed
635         if(m_speed_f.getLength()*dtime > pos_max_d)
636                 m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);*/
637         v3f pos_f = getBasePosition();
638         v3f pos_f_old = pos_f;
639         moveresult = collisionMovePrecise(&m_env->getMap(), pos_max_d,
640                         box, dtime, pos_f, m_speed_f);
641         m_touching_ground = moveresult.touching_ground;
642         
643         // Do collision damage
644         float tolerance = BS*30;
645         float factor = BS*0.5;
646         v3f speed_diff = old_speed - m_speed_f;
647         // Increase effect in X and Z
648         speed_diff.X *= 2;
649         speed_diff.Z *= 2;
650         float vel = speed_diff.getLength();
651         if(vel > tolerance)
652         {
653                 f32 damage_f = (vel - tolerance)/BS*factor;
654                 u16 damage = (u16)(damage_f+0.5);
655                 doDamage(damage);
656         }
657
658         setBasePosition(pos_f);
659
660         if(send_recommended == false && m_speed_f.getLength() < 3.0*BS)
661                 return;
662
663         if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
664         {
665                 m_last_sent_position = pos_f;
666
667                 std::ostringstream os(std::ios::binary);
668                 // command (0 = update position)
669                 writeU8(os, 0);
670                 // pos
671                 writeV3F1000(os, m_base_position);
672                 // yaw
673                 writeF1000(os, m_yaw);
674                 // create message and add to list
675                 ActiveObjectMessage aom(getId(), false, os.str());
676                 m_messages_out.push_back(aom);
677         }
678 }
679
680 std::string Oerkki1SAO::getClientInitializationData()
681 {
682         std::ostringstream os(std::ios::binary);
683         // version
684         writeU8(os, 0);
685         // pos
686         writeV3F1000(os, m_base_position);
687         return os.str();
688 }
689
690 std::string Oerkki1SAO::getStaticData()
691 {
692         //infostream<<__FUNCTION_NAME<<std::endl;
693         std::ostringstream os(std::ios::binary);
694         // version
695         writeU8(os, 0);
696         // hp
697         writeU8(os, m_hp);
698         return os.str();
699 }
700
701 void Oerkki1SAO::punch(ServerActiveObject *puncher)
702 {
703         v3f dir = (getBasePosition() - puncher->getBasePosition()).normalize();
704
705         std::string toolname = "";
706         InventoryItem *item = puncher->getWieldedItem();
707         if(item && (std::string)item->getName() == "ToolItem"){
708                 ToolItem *titem = (ToolItem*)item;
709                 toolname = titem->getToolName();
710         }
711
712         m_speed_f += dir*12*BS;
713
714         u16 amount = 5;
715         /* See tool names in inventory.h */
716         if(toolname == "WSword")
717                 amount = 10;
718         if(toolname == "STSword")
719                 amount = 12;
720         if(toolname == "SteelSword")
721                 amount = 16;
722         if(toolname == "STAxe")
723                 amount = 7;
724         if(toolname == "SteelAxe")
725                 amount = 9;
726         if(toolname == "SteelPick")
727                 amount = 7;
728         doDamage(amount);
729         
730         puncher->damageWieldedItem(65536/100);
731 }
732
733 void Oerkki1SAO::doDamage(u16 d)
734 {
735         infostream<<"oerkki damage: "<<d<<std::endl;
736         
737         if(d < m_hp)
738         {
739                 m_hp -= d;
740         }
741         else
742         {
743                 // Die
744                 m_hp = 0;
745                 m_removed = true;
746         }
747
748         {
749                 std::ostringstream os(std::ios::binary);
750                 // command (1 = damage)
751                 writeU8(os, 1);
752                 // amount
753                 writeU8(os, d);
754                 // create message and add to list
755                 ActiveObjectMessage aom(getId(), false, os.str());
756                 m_messages_out.push_back(aom);
757         }
758 }
759
760 /*
761         FireflySAO
762 */
763
764 // Prototype
765 FireflySAO proto_FireflySAO(NULL, v3f(0,0,0));
766
767 FireflySAO::FireflySAO(ServerEnvironment *env, v3f pos):
768         ServerActiveObject(env, pos),
769         m_is_active(false),
770         m_speed_f(0,0,0)
771 {
772         ServerActiveObject::registerType(getType(), create);
773
774         m_oldpos = v3f(0,0,0);
775         m_last_sent_position = v3f(0,0,0);
776         m_yaw = 0;
777         m_counter1 = 0;
778         m_counter2 = 0;
779         m_age = 0;
780         m_touching_ground = false;
781 }
782
783 ServerActiveObject* FireflySAO::create(ServerEnvironment *env, v3f pos,
784                 const std::string &data)
785 {
786         std::istringstream is(data, std::ios::binary);
787         char buf[1];
788         // read version
789         is.read(buf, 1);
790         u8 version = buf[0];
791         // check if version is supported
792         if(version != 0)
793                 return NULL;
794         return new FireflySAO(env, pos);
795 }
796
797 void FireflySAO::step(float dtime, bool send_recommended)
798 {
799         ScopeProfiler sp2(g_profiler, "FireflySAO::step avg", SPT_AVG);
800
801         assert(m_env);
802
803         if(m_is_active == false)
804         {
805                 if(m_inactive_interval.step(dtime, 0.5)==false)
806                         return;
807         }
808
809         /*
810                 The AI
811         */
812
813         // Apply (less) gravity
814         m_speed_f.Y -= dtime*3*BS;
815
816         /*
817                 Move around if some player is close
818         */
819         bool player_is_close = false;
820         // Check connected players
821         core::list<Player*> players = m_env->getPlayers(true);
822         core::list<Player*>::Iterator i;
823         for(i = players.begin();
824                         i != players.end(); i++)
825         {
826                 Player *player = *i;
827                 v3f playerpos = player->getPosition();
828                 if(m_base_position.getDistanceFrom(playerpos) < BS*10.0)
829                 {
830                         player_is_close = true;
831                         break;
832                 }
833         }
834
835         m_is_active = player_is_close;
836         
837         if(player_is_close == false)
838         {
839                 m_speed_f.X = 0;
840                 m_speed_f.Z = 0;
841         }
842         else
843         {
844                 // Move around
845                 v3f dir(cos(m_yaw/180*PI),0,sin(m_yaw/180*PI));
846                 f32 speed = BS/2;
847                 m_speed_f.X = speed * dir.X;
848                 m_speed_f.Z = speed * dir.Z;
849
850                 if(m_touching_ground && (m_oldpos - m_base_position).getLength()
851                                 < dtime*speed/2)
852                 {
853                         m_counter1 -= dtime;
854                         if(m_counter1 < 0.0)
855                         {
856                                 m_counter1 += 1.0;
857                                 m_speed_f.Y = 5.0*BS;
858                         }
859                 }
860
861                 {
862                         m_counter2 -= dtime;
863                         if(m_counter2 < 0.0)
864                         {
865                                 m_counter2 += (float)(myrand()%100)/100*3.0;
866                                 m_yaw += ((float)(myrand()%200)-100)/100*180;
867                                 m_yaw = wrapDegrees(m_yaw);
868                         }
869                 }
870         }
871         
872         m_oldpos = m_base_position;
873
874         /*
875                 Move it, with collision detection
876         */
877
878         core::aabbox3d<f32> box(-BS/3.,-BS*2/3.0,-BS/3., BS/3.,BS*4./3.,BS/3.);
879         collisionMoveResult moveresult;
880         // Maximum movement without glitches
881         f32 pos_max_d = BS*0.25;
882         // Limit speed
883         if(m_speed_f.getLength()*dtime > pos_max_d)
884                 m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
885         v3f pos_f = getBasePosition();
886         v3f pos_f_old = pos_f;
887         moveresult = collisionMoveSimple(&m_env->getMap(), pos_max_d,
888                         box, dtime, pos_f, m_speed_f);
889         m_touching_ground = moveresult.touching_ground;
890         
891         setBasePosition(pos_f);
892
893         if(send_recommended == false)
894                 return;
895
896         if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
897         {
898                 m_last_sent_position = pos_f;
899
900                 std::ostringstream os(std::ios::binary);
901                 // command (0 = update position)
902                 writeU8(os, 0);
903                 // pos
904                 writeV3F1000(os, m_base_position);
905                 // yaw
906                 writeF1000(os, m_yaw);
907                 // create message and add to list
908                 ActiveObjectMessage aom(getId(), false, os.str());
909                 m_messages_out.push_back(aom);
910         }
911 }
912
913 std::string FireflySAO::getClientInitializationData()
914 {
915         std::ostringstream os(std::ios::binary);
916         // version
917         writeU8(os, 0);
918         // pos
919         writeV3F1000(os, m_base_position);
920         return os.str();
921 }
922
923 std::string FireflySAO::getStaticData()
924 {
925         //infostream<<__FUNCTION_NAME<<std::endl;
926         std::ostringstream os(std::ios::binary);
927         // version
928         writeU8(os, 0);
929         return os.str();
930 }
931
932 InventoryItem* FireflySAO::createPickedUpItem()
933 {
934         std::istringstream is("CraftItem firefly 1", std::ios_base::binary);
935         InventoryItem *item = InventoryItem::deSerialize(is);
936         return item;
937 }
938
939 /*
940         MobV2SAO
941 */
942
943 // Prototype
944 MobV2SAO proto_MobV2SAO(NULL, v3f(0,0,0), NULL);
945
946 MobV2SAO::MobV2SAO(ServerEnvironment *env, v3f pos,
947                 Settings *init_properties):
948         ServerActiveObject(env, pos),
949         m_move_type("ground_nodes"),
950         m_speed(0,0,0),
951         m_last_sent_position(0,0,0),
952         m_oldpos(0,0,0),
953         m_yaw(0),
954         m_counter1(0),
955         m_counter2(0),
956         m_age(0),
957         m_touching_ground(false),
958         m_hp(10),
959         m_walk_around(false),
960         m_walk_around_timer(0),
961         m_next_pos_exists(false),
962         m_shoot_reload_timer(0),
963         m_shooting(false),
964         m_shooting_timer(0),
965         m_falling(false),
966         m_disturb_timer(100000),
967         m_random_disturb_timer(0),
968         m_shoot_y(0)
969 {
970         ServerActiveObject::registerType(getType(), create);
971         
972         m_properties = new Settings();
973         if(init_properties)
974                 m_properties->update(*init_properties);
975         
976         m_properties->setV3F("pos", pos);
977         
978         setPropertyDefaults();
979         readProperties();
980 }
981         
982 MobV2SAO::~MobV2SAO()
983 {
984         delete m_properties;
985 }
986
987 ServerActiveObject* MobV2SAO::create(ServerEnvironment *env, v3f pos,
988                 const std::string &data)
989 {
990         std::istringstream is(data, std::ios::binary);
991         Settings properties;
992         properties.parseConfigLines(is, "MobArgsEnd");
993         MobV2SAO *o = new MobV2SAO(env, pos, &properties);
994         return o;
995 }
996
997 std::string MobV2SAO::getStaticData()
998 {
999         updateProperties();
1000
1001         std::ostringstream os(std::ios::binary);
1002         m_properties->writeLines(os);
1003         return os.str();
1004 }
1005
1006 std::string MobV2SAO::getClientInitializationData()
1007 {
1008         //infostream<<__FUNCTION_NAME<<std::endl;
1009
1010         updateProperties();
1011
1012         std::ostringstream os(std::ios::binary);
1013
1014         // version
1015         writeU8(os, 0);
1016         
1017         Settings client_properties;
1018         
1019         /*client_properties.set("version", "0");
1020         client_properties.updateValue(*m_properties, "pos");
1021         client_properties.updateValue(*m_properties, "yaw");
1022         client_properties.updateValue(*m_properties, "hp");*/
1023
1024         // Just send everything for simplicity
1025         client_properties.update(*m_properties);
1026
1027         std::ostringstream os2(std::ios::binary);
1028         client_properties.writeLines(os2);
1029         compressZlib(os2.str(), os);
1030
1031         return os.str();
1032 }
1033
1034 bool checkFreePosition(Map *map, v3s16 p0, v3s16 size)
1035 {
1036         for(int dx=0; dx<size.X; dx++)
1037         for(int dy=0; dy<size.Y; dy++)
1038         for(int dz=0; dz<size.Z; dz++){
1039                 v3s16 dp(dx, dy, dz);
1040                 v3s16 p = p0 + dp;
1041                 MapNode n = map->getNodeNoEx(p);
1042                 if(n.getContent() != CONTENT_AIR)
1043                         return false;
1044         }
1045         return true;
1046 }
1047
1048 bool checkWalkablePosition(Map *map, v3s16 p0)
1049 {
1050         v3s16 p = p0 + v3s16(0,-1,0);
1051         MapNode n = map->getNodeNoEx(p);
1052         if(n.getContent() != CONTENT_AIR)
1053                 return true;
1054         return false;
1055 }
1056
1057 bool checkFreeAndWalkablePosition(Map *map, v3s16 p0, v3s16 size)
1058 {
1059         if(!checkFreePosition(map, p0, size))
1060                 return false;
1061         if(!checkWalkablePosition(map, p0))
1062                 return false;
1063         return true;
1064 }
1065
1066 static void get_random_u32_array(u32 a[], u32 len)
1067 {
1068         u32 i, n;
1069         for(i=0; i<len; i++)
1070                 a[i] = i;
1071         n = len;
1072         while(n > 1){
1073                 u32 k = myrand() % n;
1074                 n--;
1075                 u32 temp = a[n];
1076                 a[n] = a[k];
1077                 a[k] = temp;
1078         }
1079 }
1080
1081 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
1082
1083 static void explodeSquare(Map *map, v3s16 p0, v3s16 size)
1084 {
1085         core::map<v3s16, MapBlock*> modified_blocks;
1086
1087         for(int dx=0; dx<size.X; dx++)
1088         for(int dy=0; dy<size.Y; dy++)
1089         for(int dz=0; dz<size.Z; dz++){
1090                 v3s16 dp(dx - size.X/2, dy - size.Y/2, dz - size.Z/2);
1091                 v3s16 p = p0 + dp;
1092                 MapNode n = map->getNodeNoEx(p);
1093                 if(n.getContent() == CONTENT_IGNORE)
1094                         continue;
1095                 //map->removeNodeWithEvent(p);
1096                 map->removeNodeAndUpdate(p, modified_blocks);
1097         }
1098
1099         // Send a MEET_OTHER event
1100         MapEditEvent event;
1101         event.type = MEET_OTHER;
1102         for(core::map<v3s16, MapBlock*>::Iterator
1103                   i = modified_blocks.getIterator();
1104                   i.atEnd() == false; i++)
1105         {
1106                 v3s16 p = i.getNode()->getKey();
1107                 event.modified_blocks.insert(p, true);
1108         }
1109         map->dispatchEvent(&event);
1110 }
1111
1112 void MobV2SAO::step(float dtime, bool send_recommended)
1113 {
1114         ScopeProfiler sp2(g_profiler, "MobV2SAO::step avg", SPT_AVG);
1115
1116         assert(m_env);
1117         Map *map = &m_env->getMap();
1118
1119         m_age += dtime;
1120
1121         if(m_die_age >= 0.0 && m_age >= m_die_age){
1122                 m_removed = true;
1123                 return;
1124         }
1125
1126         m_random_disturb_timer += dtime;
1127         if(m_random_disturb_timer >= 5.0)
1128         {
1129                 m_random_disturb_timer = 0;
1130                 // Check connected players
1131                 core::list<Player*> players = m_env->getPlayers(true);
1132                 core::list<Player*>::Iterator i;
1133                 for(i = players.begin();
1134                                 i != players.end(); i++)
1135                 {
1136                         Player *player = *i;
1137                         v3f playerpos = player->getPosition();
1138                         f32 dist = m_base_position.getDistanceFrom(playerpos);
1139                         if(dist < BS*16)
1140                         {
1141                                 if(myrand_range(0,3) == 0){
1142                                         actionstream<<"Mob id="<<m_id<<" at "
1143                                                         <<PP(m_base_position/BS)
1144                                                         <<" got randomly disturbed by "
1145                                                         <<player->getName()<<std::endl;
1146                                         m_disturbing_player = player->getName();
1147                                         m_disturb_timer = 0;
1148                                         break;
1149                                 }
1150                         }
1151                 }
1152         }
1153
1154         Player *disturbing_player =
1155                         m_env->getPlayer(m_disturbing_player.c_str());
1156         v3f disturbing_player_off = v3f(0,1,0);
1157         v3f disturbing_player_norm = v3f(0,1,0);
1158         float disturbing_player_distance = 1000000;
1159         float disturbing_player_dir = 0;
1160         if(disturbing_player){
1161                 disturbing_player_off =
1162                                 disturbing_player->getPosition() - m_base_position;
1163                 disturbing_player_distance = disturbing_player_off.getLength();
1164                 disturbing_player_norm = disturbing_player_off;
1165                 disturbing_player_norm.normalize();
1166                 disturbing_player_dir = 180./PI*atan2(disturbing_player_norm.Z,
1167                                 disturbing_player_norm.X);
1168         }
1169
1170         m_disturb_timer += dtime;
1171         
1172         if(!m_falling)
1173         {
1174                 m_shooting_timer -= dtime;
1175                 if(m_shooting_timer <= 0.0 && m_shooting){
1176                         m_shooting = false;
1177                         
1178                         std::string shoot_type = m_properties->get("shoot_type");
1179                         v3f shoot_pos(0,0,0);
1180                         shoot_pos.Y += m_properties->getFloat("shoot_y") * BS;
1181                         if(shoot_type == "fireball"){
1182                                 v3f dir(cos(m_yaw/180*PI),0,sin(m_yaw/180*PI));
1183                                 dir.Y = m_shoot_y;
1184                                 dir.normalize();
1185                                 v3f speed = dir * BS * 10.0;
1186                                 v3f pos = m_base_position + shoot_pos;
1187                                 infostream<<__FUNCTION_NAME<<": Mob id="<<m_id
1188                                                 <<" shooting fireball from "<<PP(pos)
1189                                                 <<" at speed "<<PP(speed)<<std::endl;
1190                                 Settings properties;
1191                                 properties.set("looks", "fireball");
1192                                 properties.setV3F("speed", speed);
1193                                 properties.setFloat("die_age", 5.0);
1194                                 properties.set("move_type", "constant_speed");
1195                                 properties.setFloat("hp", 1000);
1196                                 properties.set("lock_full_brightness", "true");
1197                                 properties.set("player_hit_damage", "9");
1198                                 properties.set("player_hit_distance", "2");
1199                                 properties.set("player_hit_interval", "1");
1200                                 ServerActiveObject *obj = new MobV2SAO(m_env,
1201                                                 pos, &properties);
1202                                 //m_env->addActiveObjectAsStatic(obj);
1203                                 m_env->addActiveObject(obj);
1204                         } else {
1205                                 infostream<<__FUNCTION_NAME<<": Mob id="<<m_id
1206                                                 <<": Unknown shoot_type="<<shoot_type
1207                                                 <<std::endl;
1208                         }
1209                 }
1210
1211                 m_shoot_reload_timer += dtime;
1212
1213                 float reload_time = 15.0;
1214                 if(m_disturb_timer <= 15.0)
1215                         reload_time = 3.0;
1216
1217                 bool shoot_without_player = false;
1218                 if(m_properties->getBool("mindless_rage"))
1219                         shoot_without_player = true;
1220
1221                 if(!m_shooting && m_shoot_reload_timer >= reload_time &&
1222                                 !m_next_pos_exists &&
1223                                 (m_disturb_timer <= 60.0 || shoot_without_player))
1224                 {
1225                         m_shoot_y = 0;
1226                         if(m_disturb_timer < 60.0 && disturbing_player &&
1227                                         disturbing_player_distance < 16*BS &&
1228                                         fabs(disturbing_player_norm.Y) < 0.8){
1229                                 m_yaw = disturbing_player_dir;
1230                                 sendPosition();
1231                                 m_shoot_y += disturbing_player_norm.Y;
1232                         } else {
1233                                 m_shoot_y = 0.01 * myrand_range(-30,10);
1234                         }
1235                         m_shoot_reload_timer = 0.0;
1236                         m_shooting = true;
1237                         m_shooting_timer = 1.5;
1238                         {
1239                                 std::ostringstream os(std::ios::binary);
1240                                 // command (2 = shooting)
1241                                 writeU8(os, 2);
1242                                 // time
1243                                 writeF1000(os, m_shooting_timer + 0.1);
1244                                 // bright?
1245                                 writeU8(os, true);
1246                                 // create message and add to list
1247                                 ActiveObjectMessage aom(getId(), false, os.str());
1248                                 m_messages_out.push_back(aom);
1249                         }
1250                 }
1251         }
1252         
1253         if(m_move_type == "ground_nodes")
1254         {
1255                 if(!m_shooting){
1256                         m_walk_around_timer -= dtime;
1257                         if(m_walk_around_timer <= 0.0){
1258                                 m_walk_around = !m_walk_around;
1259                                 if(m_walk_around)
1260                                         m_walk_around_timer = 0.1*myrand_range(10,50);
1261                                 else
1262                                         m_walk_around_timer = 0.1*myrand_range(30,70);
1263                         }
1264                 }
1265
1266                 /* Move */
1267                 if(m_next_pos_exists){
1268                         v3f pos_f = m_base_position;
1269                         v3f next_pos_f = intToFloat(m_next_pos_i, BS);
1270
1271                         v3f v = next_pos_f - pos_f;
1272                         m_yaw = atan2(v.Z, v.X) / PI * 180;
1273                         
1274                         v3f diff = next_pos_f - pos_f;
1275                         v3f dir = diff;
1276                         dir.normalize();
1277                         float speed = BS * 0.5;
1278                         if(m_falling)
1279                                 speed = BS * 3.0;
1280                         dir *= dtime * speed;
1281                         bool arrived = false;
1282                         if(dir.getLength() > diff.getLength()){
1283                                 dir = diff;
1284                                 arrived = true;
1285                         }
1286                         pos_f += dir;
1287                         m_base_position = pos_f;
1288
1289                         if((pos_f - next_pos_f).getLength() < 0.1 || arrived){
1290                                 m_next_pos_exists = false;
1291                         }
1292                 }
1293
1294                 v3s16 pos_i = floatToInt(m_base_position, BS);
1295                 v3s16 size_blocks = v3s16(m_size.X+0.5,m_size.Y+0.5,m_size.X+0.5);
1296                 v3s16 pos_size_off(0,0,0);
1297                 if(m_size.X >= 2.5){
1298                         pos_size_off.X = -1;
1299                         pos_size_off.Y = -1;
1300                 }
1301                 
1302                 if(!m_next_pos_exists){
1303                         /* Check whether to drop down */
1304                         if(checkFreePosition(map,
1305                                         pos_i + pos_size_off + v3s16(0,-1,0), size_blocks)){
1306                                 m_next_pos_i = pos_i + v3s16(0,-1,0);
1307                                 m_next_pos_exists = true;
1308                                 m_falling = true;
1309                         } else {
1310                                 m_falling = false;
1311                         }
1312                 }
1313
1314                 if(m_walk_around)
1315                 {
1316                         if(!m_next_pos_exists){
1317                                 /* Find some position where to go next */
1318                                 v3s16 dps[3*3*3];
1319                                 int num_dps = 0;
1320                                 for(int dx=-1; dx<=1; dx++)
1321                                 for(int dy=-1; dy<=1; dy++)
1322                                 for(int dz=-1; dz<=1; dz++){
1323                                         if(dx == 0 && dy == 0)
1324                                                 continue;
1325                                         if(dx != 0 && dz != 0 && dy != 0)
1326                                                 continue;
1327                                         dps[num_dps++] = v3s16(dx,dy,dz);
1328                                 }
1329                                 u32 order[3*3*3];
1330                                 get_random_u32_array(order, num_dps);
1331                                 for(int i=0; i<num_dps; i++){
1332                                         v3s16 p = dps[order[i]] + pos_i;
1333                                         bool is_free = checkFreeAndWalkablePosition(map,
1334                                                         p + pos_size_off, size_blocks);
1335                                         if(!is_free)
1336                                                 continue;
1337                                         m_next_pos_i = p;
1338                                         m_next_pos_exists = true;
1339                                         break;
1340                                 }
1341                         }
1342                 }
1343         }
1344         else if(m_move_type == "constant_speed")
1345         {
1346                 m_base_position += m_speed * dtime;
1347                 
1348                 v3s16 pos_i = floatToInt(m_base_position, BS);
1349                 v3s16 size_blocks = v3s16(m_size.X+0.5,m_size.Y+0.5,m_size.X+0.5);
1350                 v3s16 pos_size_off(0,0,0);
1351                 if(m_size.X >= 2.5){
1352                         pos_size_off.X = -1;
1353                         pos_size_off.Y = -1;
1354                 }
1355                 bool free = checkFreePosition(map, pos_i + pos_size_off, size_blocks);
1356                 if(!free){
1357                         explodeSquare(map, pos_i, v3s16(3,3,3));
1358                         m_removed = true;
1359                         return;
1360                 }
1361         }
1362         else
1363         {
1364                 errorstream<<"MobV2SAO::step(): id="<<m_id<<" unknown move_type=\""
1365                                 <<m_move_type<<"\""<<std::endl;
1366         }
1367
1368         if(send_recommended == false)
1369                 return;
1370
1371         if(m_base_position.getDistanceFrom(m_last_sent_position) > 0.05*BS)
1372         {
1373                 sendPosition();
1374         }
1375 }
1376
1377 void MobV2SAO::punch(ServerActiveObject *puncher)
1378 {
1379         v3f dir = (getBasePosition() - puncher->getBasePosition()).normalize();
1380
1381         std::string toolname = "";
1382         InventoryItem *item = puncher->getWieldedItem();
1383         if(item && (std::string)item->getName() == "ToolItem"){
1384                 ToolItem *titem = (ToolItem*)item;
1385                 toolname = titem->getToolName();
1386         }
1387         
1388         // A quick hack; SAO description is player name for player
1389         std::string playername = puncher->getDescription();
1390
1391         Map *map = &m_env->getMap();
1392         
1393         actionstream<<playername<<" punches mob id="<<m_id
1394                         <<" with a \""<<toolname<<"\" at "
1395                         <<PP(m_base_position/BS)<<std::endl;
1396
1397         m_disturb_timer = 0;
1398         m_disturbing_player = playername;
1399         m_next_pos_exists = false; // Cancel moving immediately
1400         
1401         m_yaw = wrapDegrees_180(180./PI*atan2(dir.Z, dir.X) + 180.);
1402         v3f new_base_position = m_base_position + dir * BS;
1403         {
1404                 v3s16 pos_i = floatToInt(new_base_position, BS);
1405                 v3s16 size_blocks = v3s16(m_size.X+0.5,m_size.Y+0.5,m_size.X+0.5);
1406                 v3s16 pos_size_off(0,0,0);
1407                 if(m_size.X >= 2.5){
1408                         pos_size_off.X = -1;
1409                         pos_size_off.Y = -1;
1410                 }
1411                 bool free = checkFreePosition(map, pos_i + pos_size_off, size_blocks);
1412                 if(free)
1413                         m_base_position = new_base_position;
1414         }
1415         sendPosition();
1416         
1417         u16 amount = 2;
1418         /* See tool names in inventory.h */
1419         if(toolname == "WSword")
1420                 amount = 4;
1421         if(toolname == "STSword")
1422                 amount = 6;
1423         if(toolname == "SteelSword")
1424                 amount = 8;
1425         if(toolname == "STAxe")
1426                 amount = 3;
1427         if(toolname == "SteelAxe")
1428                 amount = 4;
1429         if(toolname == "SteelPick")
1430                 amount = 3;
1431         doDamage(amount);
1432         
1433         puncher->damageWieldedItem(65536/100);
1434 }
1435
1436 bool MobV2SAO::isPeaceful()
1437 {
1438         return m_properties->getBool("is_peaceful");
1439 }
1440
1441 void MobV2SAO::sendPosition()
1442 {
1443         m_last_sent_position = m_base_position;
1444
1445         std::ostringstream os(std::ios::binary);
1446         // command (0 = update position)
1447         writeU8(os, 0);
1448         // pos
1449         writeV3F1000(os, m_base_position);
1450         // yaw
1451         writeF1000(os, m_yaw);
1452         // create message and add to list
1453         ActiveObjectMessage aom(getId(), false, os.str());
1454         m_messages_out.push_back(aom);
1455 }
1456
1457 void MobV2SAO::setPropertyDefaults()
1458 {
1459         m_properties->setDefault("is_peaceful", "false");
1460         m_properties->setDefault("move_type", "ground_nodes");
1461         m_properties->setDefault("speed", "(0,0,0)");
1462         m_properties->setDefault("age", "0");
1463         m_properties->setDefault("yaw", "0");
1464         m_properties->setDefault("pos", "(0,0,0)");
1465         m_properties->setDefault("hp", "0");
1466         m_properties->setDefault("die_age", "-1");
1467         m_properties->setDefault("size", "(1,2)");
1468         m_properties->setDefault("shoot_type", "fireball");
1469         m_properties->setDefault("shoot_y", "0");
1470         m_properties->setDefault("mindless_rage", "false");
1471 }
1472 void MobV2SAO::readProperties()
1473 {
1474         m_move_type = m_properties->get("move_type");
1475         m_speed = m_properties->getV3F("speed");
1476         m_age = m_properties->getFloat("age");
1477         m_yaw = m_properties->getFloat("yaw");
1478         m_base_position = m_properties->getV3F("pos");
1479         m_hp = m_properties->getS32("hp");
1480         m_die_age = m_properties->getFloat("die_age");
1481         m_size = m_properties->getV2F("size");
1482 }
1483 void MobV2SAO::updateProperties()
1484 {
1485         m_properties->set("move_type", m_move_type);
1486         m_properties->setV3F("speed", m_speed);
1487         m_properties->setFloat("age", m_age);
1488         m_properties->setFloat("yaw", m_yaw);
1489         m_properties->setV3F("pos", m_base_position);
1490         m_properties->setS32("hp", m_hp);
1491         m_properties->setFloat("die_age", m_die_age);
1492         m_properties->setV2F("size", m_size);
1493
1494         m_properties->setS32("version", 0);
1495 }
1496
1497 void MobV2SAO::doDamage(u16 d)
1498 {
1499         infostream<<"MobV2 hp="<<m_hp<<" damage="<<d<<std::endl;
1500         
1501         if(d < m_hp)
1502         {
1503                 m_hp -= d;
1504         }
1505         else
1506         {
1507                 actionstream<<"A "<<(isPeaceful()?"peaceful":"non-peaceful")
1508                                 <<" mob id="<<m_id<<" dies at "<<PP(m_base_position)<<std::endl;
1509                 // Die
1510                 m_hp = 0;
1511                 m_removed = true;
1512         }
1513
1514         {
1515                 std::ostringstream os(std::ios::binary);
1516                 // command (1 = damage)
1517                 writeU8(os, 1);
1518                 // amount
1519                 writeU16(os, d);
1520                 // create message and add to list
1521                 ActiveObjectMessage aom(getId(), false, os.str());
1522                 m_messages_out.push_back(aom);
1523         }
1524 }
1525
1526
1527 /*
1528         LuaEntitySAO
1529 */
1530
1531 #include "scriptapi.h"
1532 #include "luaentity_common.h"
1533
1534 // Prototype
1535 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
1536
1537 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
1538                 const std::string &name, const std::string &state):
1539         ServerActiveObject(env, pos),
1540         m_init_name(name),
1541         m_init_state(state),
1542         m_registered(false),
1543         m_prop(new LuaEntityProperties),
1544         m_yaw(0),
1545         m_last_sent_yaw(0),
1546         m_last_sent_position(0,0,0),
1547         m_last_sent_position_timer(0),
1548         m_last_sent_move_precision(0)
1549 {
1550         // Only register type if no environment supplied
1551         if(env == NULL){
1552                 ServerActiveObject::registerType(getType(), create);
1553                 return;
1554         }
1555 }
1556
1557 LuaEntitySAO::~LuaEntitySAO()
1558 {
1559         if(m_registered){
1560                 lua_State *L = m_env->getLua();
1561                 scriptapi_luaentity_rm(L, m_id);
1562         }
1563         delete m_prop;
1564 }
1565
1566 void LuaEntitySAO::addedToEnvironment(u16 id)
1567 {
1568         ServerActiveObject::addedToEnvironment(id);
1569         
1570         // Create entity from name and state
1571         lua_State *L = m_env->getLua();
1572         m_registered = scriptapi_luaentity_add(L, id, m_init_name.c_str(), m_init_state.c_str());
1573         
1574         if(m_registered){
1575                 // Get properties
1576                 scriptapi_luaentity_get_properties(L, m_id, m_prop);
1577         }
1578 }
1579
1580 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
1581                 const std::string &data)
1582 {
1583         std::istringstream is(data, std::ios::binary);
1584         // read version
1585         u8 version = readU8(is);
1586         // check if version is supported
1587         if(version != 0)
1588                 return NULL;
1589         // read name
1590         std::string name = deSerializeString(is);
1591         // read state
1592         std::string state = deSerializeLongString(is);
1593         // create object
1594         infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
1595                         <<state<<"\")"<<std::endl;
1596         return new LuaEntitySAO(env, pos, name, state);
1597 }
1598
1599 void LuaEntitySAO::step(float dtime, bool send_recommended)
1600 {
1601         m_last_sent_position_timer += dtime;
1602         
1603         if(m_registered){
1604                 lua_State *L = m_env->getLua();
1605                 scriptapi_luaentity_step(L, m_id, dtime);
1606         }
1607
1608         if(send_recommended == false)
1609                 return;
1610         
1611         float minchange = 0.2*BS;
1612         if(m_last_sent_position_timer > 1.0){
1613                 minchange = 0.01*BS;
1614         } else if(m_last_sent_position_timer > 0.2){
1615                 minchange = 0.05*BS;
1616         }
1617         float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
1618         move_d += m_last_sent_move_precision;
1619         if(move_d > minchange || fabs(m_yaw - m_last_sent_yaw) > 1.0){
1620                 sendPosition(true, false);
1621         }
1622 }
1623
1624 std::string LuaEntitySAO::getClientInitializationData()
1625 {
1626         std::ostringstream os(std::ios::binary);
1627         // version
1628         writeU8(os, 0);
1629         // pos
1630         writeV3F1000(os, m_base_position);
1631         // yaw
1632         writeF1000(os, m_yaw);
1633         // properties
1634         std::ostringstream prop_os(std::ios::binary);
1635         m_prop->serialize(prop_os);
1636         os<<serializeLongString(prop_os.str());
1637         // return result
1638         return os.str();
1639 }
1640
1641 std::string LuaEntitySAO::getStaticData()
1642 {
1643         infostream<<__FUNCTION_NAME<<std::endl;
1644         std::ostringstream os(std::ios::binary);
1645         // version
1646         writeU8(os, 0);
1647         // name
1648         os<<serializeString(m_init_name);
1649         // state
1650         if(m_registered){
1651                 lua_State *L = m_env->getLua();
1652                 std::string state = scriptapi_luaentity_get_state(L, m_id);
1653                 os<<serializeLongString(state);
1654         } else {
1655                 os<<serializeLongString(m_init_state);
1656         }
1657         return os.str();
1658 }
1659
1660 InventoryItem* LuaEntitySAO::createPickedUpItem()
1661 {
1662         std::istringstream is("CraftItem testobject1 1", std::ios_base::binary);
1663         InventoryItem *item = InventoryItem::deSerialize(is);
1664         return item;
1665 }
1666
1667 void LuaEntitySAO::punch(ServerActiveObject *puncher)
1668 {
1669         if(!m_registered)
1670                 return;
1671         lua_State *L = m_env->getLua();
1672         scriptapi_luaentity_punch(L, m_id, puncher);
1673 }
1674
1675 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
1676 {
1677         if(!m_registered)
1678                 return;
1679         lua_State *L = m_env->getLua();
1680         scriptapi_luaentity_rightclick(L, m_id, clicker);
1681 }
1682
1683 void LuaEntitySAO::setPos(v3f pos)
1684 {
1685         m_base_position = pos;
1686         sendPosition(false, true);
1687 }
1688
1689 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
1690 {
1691         m_base_position = pos;
1692         if(!continuous)
1693                 sendPosition(true, true);
1694 }
1695
1696 float LuaEntitySAO::getMinimumSavedMovement()
1697 {
1698         return 0.1 * BS;
1699 }
1700
1701 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
1702 {
1703         m_last_sent_move_precision = m_base_position.getDistanceFrom(
1704                         m_last_sent_position);
1705         m_last_sent_position_timer = 0;
1706         m_last_sent_yaw = m_yaw;
1707         m_last_sent_position = m_base_position;
1708
1709         float update_interval = m_env->getSendRecommendedInterval();
1710
1711         std::ostringstream os(std::ios::binary);
1712         // command (0 = update position)
1713         writeU8(os, 0);
1714
1715         // do_interpolate
1716         writeU8(os, do_interpolate);
1717         // pos
1718         writeV3F1000(os, m_base_position);
1719         // yaw
1720         writeF1000(os, m_yaw);
1721         // is_end_position (for interpolation)
1722         writeU8(os, is_movement_end);
1723         // update_interval (for interpolation)
1724         writeF1000(os, update_interval);
1725
1726         // create message and add to list
1727         ActiveObjectMessage aom(getId(), false, os.str());
1728         m_messages_out.push_back(aom);
1729 }
1730
1731