]> git.lizzy.rs Git - dragonfireclient.git/blob - src/serverobject.cpp
Some work-in-progress in hp and mobs and a frightening amount of random fixes.
[dragonfireclient.git] / src / serverobject.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 "serverobject.h"
21 #include <fstream>
22 #include "environment.h"
23 #include "inventory.h"
24 #include "collision.h"
25
26 core::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
27
28 ServerActiveObject::ServerActiveObject(ServerEnvironment *env, u16 id, v3f pos):
29         ActiveObject(id),
30         m_known_by_count(0),
31         m_removed(false),
32         m_static_exists(false),
33         m_static_block(1337,1337,1337),
34         m_env(env),
35         m_base_position(pos)
36 {
37 }
38
39 ServerActiveObject::~ServerActiveObject()
40 {
41 }
42
43 ServerActiveObject* ServerActiveObject::create(u8 type,
44                 ServerEnvironment *env, u16 id, v3f pos,
45                 const std::string &data)
46 {
47         // Find factory function
48         core::map<u16, Factory>::Node *n;
49         n = m_types.find(type);
50         if(n == NULL)
51         {
52                 // If factory is not found, just return.
53                 dstream<<"WARNING: ServerActiveObject: No factory for type="
54                                 <<type<<std::endl;
55                 return NULL;
56         }
57
58         Factory f = n->getValue();
59         ServerActiveObject *object = (*f)(env, id, pos, data);
60         return object;
61 }
62
63 void ServerActiveObject::registerType(u16 type, Factory f)
64 {
65         core::map<u16, Factory>::Node *n;
66         n = m_types.find(type);
67         if(n)
68                 return;
69         m_types.insert(type, f);
70 }
71
72
73 /*
74         TestSAO
75 */
76
77 // Prototype
78 TestSAO proto_TestSAO(NULL, 0, v3f(0,0,0));
79
80 TestSAO::TestSAO(ServerEnvironment *env, u16 id, v3f pos):
81         ServerActiveObject(env, id, pos),
82         m_timer1(0),
83         m_age(0)
84 {
85         ServerActiveObject::registerType(getType(), create);
86 }
87
88 ServerActiveObject* TestSAO::create(ServerEnvironment *env, u16 id, v3f pos,
89                 const std::string &data)
90 {
91         return new TestSAO(env, id, pos);
92 }
93
94 void TestSAO::step(float dtime, Queue<ActiveObjectMessage> &messages,
95                 bool send_recommended)
96 {
97         m_age += dtime;
98         if(m_age > 10)
99         {
100                 m_removed = true;
101                 return;
102         }
103
104         m_base_position.Y += dtime * BS * 2;
105         if(m_base_position.Y > 8*BS)
106                 m_base_position.Y = 2*BS;
107
108         if(send_recommended == false)
109                 return;
110
111         m_timer1 -= dtime;
112         if(m_timer1 < 0.0)
113         {
114                 m_timer1 += 0.125;
115                 //dstream<<"TestSAO: id="<<getId()<<" sending data"<<std::endl;
116
117                 std::string data;
118
119                 data += itos(0); // 0 = position
120                 data += " ";
121                 data += itos(m_base_position.X);
122                 data += " ";
123                 data += itos(m_base_position.Y);
124                 data += " ";
125                 data += itos(m_base_position.Z);
126
127                 ActiveObjectMessage aom(getId(), false, data);
128                 messages.push_back(aom);
129         }
130 }
131
132
133 /*
134         ItemSAO
135 */
136
137 // Prototype
138 ItemSAO proto_ItemSAO(NULL, 0, v3f(0,0,0), "");
139
140 ItemSAO::ItemSAO(ServerEnvironment *env, u16 id, v3f pos,
141                 const std::string inventorystring):
142         ServerActiveObject(env, id, pos),
143         m_inventorystring(inventorystring),
144         m_speed_f(0,0,0),
145         m_last_sent_position(0,0,0)
146 {
147         ServerActiveObject::registerType(getType(), create);
148 }
149
150 ServerActiveObject* ItemSAO::create(ServerEnvironment *env, u16 id, v3f pos,
151                 const std::string &data)
152 {
153         std::istringstream is(data, std::ios::binary);
154         char buf[1];
155         // read version
156         is.read(buf, 1);
157         u8 version = buf[0];
158         // check if version is supported
159         if(version != 0)
160                 return NULL;
161         std::string inventorystring = deSerializeString(is);
162         dstream<<"ItemSAO::create(): Creating item \""
163                         <<inventorystring<<"\""<<std::endl;
164         return new ItemSAO(env, id, pos, inventorystring);
165 }
166
167 void ItemSAO::step(float dtime, Queue<ActiveObjectMessage> &messages,
168                 bool send_recommended)
169 {
170         assert(m_env);
171
172         const float interval = 0.2;
173         if(m_move_interval.step(dtime, interval)==false)
174                 return;
175         dtime = interval;
176         
177         core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.);
178         collisionMoveResult moveresult;
179         // Apply gravity
180         m_speed_f += v3f(0, -dtime*9.81*BS, 0);
181         // Maximum movement without glitches
182         f32 pos_max_d = BS*0.25;
183         // Limit speed
184         if(m_speed_f.getLength()*dtime > pos_max_d)
185                 m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
186         v3f pos_f = getBasePosition();
187         v3f pos_f_old = pos_f;
188         moveresult = collisionMoveSimple(&m_env->getMap(), pos_max_d,
189                         box, dtime, pos_f, m_speed_f);
190         
191         if(send_recommended == false)
192                 return;
193
194         if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
195         {
196                 setBasePosition(pos_f);
197                 m_last_sent_position = pos_f;
198
199                 std::ostringstream os(std::ios::binary);
200                 char buf[6];
201                 // command (0 = update position)
202                 buf[0] = 0;
203                 os.write(buf, 1);
204                 // pos
205                 writeS32((u8*)buf, m_base_position.X*1000);
206                 os.write(buf, 4);
207                 writeS32((u8*)buf, m_base_position.Y*1000);
208                 os.write(buf, 4);
209                 writeS32((u8*)buf, m_base_position.Z*1000);
210                 os.write(buf, 4);
211                 // create message and add to list
212                 ActiveObjectMessage aom(getId(), false, os.str());
213                 messages.push_back(aom);
214         }
215 }
216
217 std::string ItemSAO::getClientInitializationData()
218 {
219         std::ostringstream os(std::ios::binary);
220         char buf[6];
221         // version
222         buf[0] = 0;
223         os.write(buf, 1);
224         // pos
225         writeS32((u8*)buf, m_base_position.X*1000);
226         os.write(buf, 4);
227         writeS32((u8*)buf, m_base_position.Y*1000);
228         os.write(buf, 4);
229         writeS32((u8*)buf, m_base_position.Z*1000);
230         os.write(buf, 4);
231         // inventorystring
232         os<<serializeString(m_inventorystring);
233         return os.str();
234 }
235
236 std::string ItemSAO::getStaticData()
237 {
238         dstream<<__FUNCTION_NAME<<std::endl;
239         std::ostringstream os(std::ios::binary);
240         char buf[1];
241         // version
242         buf[0] = 0;
243         os.write(buf, 1);
244         // inventorystring
245         os<<serializeString(m_inventorystring);
246         return os.str();
247 }
248
249 InventoryItem * ItemSAO::createInventoryItem()
250 {
251         try{
252                 std::istringstream is(m_inventorystring, std::ios_base::binary);
253                 InventoryItem *item = InventoryItem::deSerialize(is);
254                 dstream<<__FUNCTION_NAME<<": m_inventorystring=\""
255                                 <<m_inventorystring<<"\" -> item="<<item
256                                 <<std::endl;
257                 return item;
258         }
259         catch(SerializationError &e)
260         {
261                 dstream<<__FUNCTION_NAME<<": serialization error: "
262                                 <<"m_inventorystring=\""<<m_inventorystring<<"\""<<std::endl;
263                 return NULL;
264         }
265 }
266
267
268 /*
269         RatSAO
270 */
271
272 // Prototype
273 RatSAO proto_RatSAO(NULL, 0, v3f(0,0,0));
274
275 RatSAO::RatSAO(ServerEnvironment *env, u16 id, v3f pos):
276         ServerActiveObject(env, id, 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 = 0;
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, u16 id, 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, id, pos);
303 }
304
305 void RatSAO::step(float dtime, Queue<ActiveObjectMessage> &messages,
306                 bool send_recommended)
307 {
308         assert(m_env);
309
310         if(m_is_active == false)
311         {
312                 if(m_inactive_interval.step(dtime, 0.5)==false)
313                         return;
314         }
315
316         /*
317                 The AI
318         */
319
320         /*m_age += dtime;
321         if(m_age > 60)
322         {
323                 // Die
324                 m_removed = true;
325                 return;
326         }*/
327
328         // Apply gravity
329         m_speed_f.Y -= dtime*9.81*BS;
330
331         /*
332                 Move around if some player is close
333         */
334         bool player_is_close = false;
335         // Check connected players
336         core::list<Player*> players = m_env->getPlayers(true);
337         core::list<Player*>::Iterator i;
338         for(i = players.begin();
339                         i != players.end(); i++)
340         {
341                 Player *player = *i;
342                 v3f playerpos = player->getPosition();
343                 if(m_base_position.getDistanceFrom(playerpos) < BS*10.0)
344                 {
345                         player_is_close = true;
346                         break;
347                 }
348         }
349
350         m_is_active = player_is_close;
351         
352         if(player_is_close == false)
353         {
354                 m_speed_f.X = 0;
355                 m_speed_f.Z = 0;
356         }
357         else
358         {
359                 // Move around
360                 v3f dir(cos(m_yaw/180*PI),0,sin(m_yaw/180*PI));
361                 f32 speed = 2*BS;
362                 m_speed_f.X = speed * dir.X;
363                 m_speed_f.Z = speed * dir.Z;
364
365                 if(m_touching_ground && (m_oldpos - m_base_position).getLength()
366                                 < dtime*speed/2)
367                 {
368                         m_counter1 -= dtime;
369                         if(m_counter1 < 0.0)
370                         {
371                                 m_counter1 += 1.0;
372                                 m_speed_f.Y = 5.0*BS;
373                         }
374                 }
375
376                 {
377                         m_counter2 -= dtime;
378                         if(m_counter2 < 0.0)
379                         {
380                                 m_counter2 += (float)(myrand()%100)/100*3.0;
381                                 m_yaw += ((float)(myrand()%200)-100)/100*180;
382                                 m_yaw = wrapDegrees(m_yaw);
383                         }
384                 }
385         }
386         
387         m_oldpos = m_base_position;
388
389         /*
390                 Move it, with collision detection
391         */
392
393         core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.);
394         collisionMoveResult moveresult;
395         // Maximum movement without glitches
396         f32 pos_max_d = BS*0.25;
397         // Limit speed
398         if(m_speed_f.getLength()*dtime > pos_max_d)
399                 m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
400         v3f pos_f = getBasePosition();
401         v3f pos_f_old = pos_f;
402         moveresult = collisionMoveSimple(&m_env->getMap(), pos_max_d,
403                         box, dtime, pos_f, m_speed_f);
404         m_touching_ground = moveresult.touching_ground;
405         
406         setBasePosition(pos_f);
407
408         if(send_recommended == false)
409                 return;
410
411         if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
412         {
413                 m_last_sent_position = pos_f;
414
415                 std::ostringstream os(std::ios::binary);
416                 // command (0 = update position)
417                 writeU8(os, 0);
418                 // pos
419                 writeV3F1000(os, m_base_position);
420                 // yaw
421                 writeF1000(os, m_yaw);
422                 // create message and add to list
423                 ActiveObjectMessage aom(getId(), false, os.str());
424                 messages.push_back(aom);
425         }
426 }
427
428 std::string RatSAO::getClientInitializationData()
429 {
430         std::ostringstream os(std::ios::binary);
431         // version
432         writeU8(os, 0);
433         // pos
434         writeV3F1000(os, m_base_position);
435         return os.str();
436 }
437
438 std::string RatSAO::getStaticData()
439 {
440         //dstream<<__FUNCTION_NAME<<std::endl;
441         std::ostringstream os(std::ios::binary);
442         // version
443         writeU8(os, 0);
444         return os.str();
445 }
446
447 InventoryItem* RatSAO::createPickedUpItem()
448 {
449         std::istringstream is("CraftItem rat 1", std::ios_base::binary);
450         InventoryItem *item = InventoryItem::deSerialize(is);
451         return item;
452 }
453
454 /*
455         Oerkki1SAO
456 */
457
458 // Prototype
459 Oerkki1SAO proto_Oerkki1SAO(NULL, 0, v3f(0,0,0));
460
461 Oerkki1SAO::Oerkki1SAO(ServerEnvironment *env, u16 id, v3f pos):
462         ServerActiveObject(env, id, pos),
463         m_is_active(false),
464         m_speed_f(0,0,0)
465 {
466         ServerActiveObject::registerType(getType(), create);
467
468         m_oldpos = v3f(0,0,0);
469         m_last_sent_position = v3f(0,0,0);
470         m_yaw = 0;
471         m_counter1 = 0;
472         m_counter2 = 0;
473         m_age = 0;
474         m_touching_ground = false;
475         m_hp = 20;
476 }
477
478 ServerActiveObject* Oerkki1SAO::create(ServerEnvironment *env, u16 id, v3f pos,
479                 const std::string &data)
480 {
481         std::istringstream is(data, std::ios::binary);
482         // read version
483         u8 version = readU8(is);
484         // read hp
485         u8 hp = readU8(is);
486         // check if version is supported
487         if(version != 0)
488                 return NULL;
489         Oerkki1SAO *o = new Oerkki1SAO(env, id, pos);
490         o->m_hp = hp;
491         return o;
492 }
493
494 void Oerkki1SAO::step(float dtime, Queue<ActiveObjectMessage> &messages,
495                 bool send_recommended)
496 {
497         assert(m_env);
498
499         if(m_is_active == false)
500         {
501                 if(m_inactive_interval.step(dtime, 0.5)==false)
502                         return;
503         }
504
505         /*
506                 The AI
507         */
508
509         m_age += dtime;
510         if(m_age > 60)
511         {
512                 // Die
513                 m_removed = true;
514                 return;
515         }
516
517         // Apply gravity
518         m_speed_f.Y -= dtime*9.81*BS;
519
520         /*
521                 Move around if some player is close
522         */
523         bool player_is_close = false;
524         v3f near_player_pos;
525         // Check connected players
526         core::list<Player*> players = m_env->getPlayers(true);
527         core::list<Player*>::Iterator i;
528         for(i = players.begin();
529                         i != players.end(); i++)
530         {
531                 Player *player = *i;
532                 v3f playerpos = player->getPosition();
533                 if(m_base_position.getDistanceFrom(playerpos) < BS*15.0)
534                 {
535                         player_is_close = true;
536                         near_player_pos = playerpos;
537                         break;
538                 }
539         }
540
541         m_is_active = player_is_close;
542         
543         if(player_is_close == false)
544         {
545                 m_speed_f.X = 0;
546                 m_speed_f.Z = 0;
547         }
548         else
549         {
550                 // Move around
551
552                 v3f ndir = near_player_pos - m_base_position;
553                 ndir.Y = 0;
554                 ndir /= ndir.getLength();
555                 f32 nyaw = 180./PI*atan2(ndir.Z,ndir.X);
556                 if(nyaw < m_yaw - 180)
557                         nyaw += 360;
558                 else if(nyaw > m_yaw + 180)
559                         nyaw -= 360;
560                 m_yaw = 0.95*m_yaw + 0.05*nyaw;
561                 m_yaw = wrapDegrees(m_yaw);
562
563                 v3f dir(cos(m_yaw/180*PI),0,sin(m_yaw/180*PI));
564                 f32 speed = 2*BS;
565                 m_speed_f.X = speed * dir.X;
566                 m_speed_f.Z = speed * dir.Z;
567
568                 if(m_touching_ground && (m_oldpos - m_base_position).getLength()
569                                 < dtime*speed/2)
570                 {
571                         m_counter1 -= dtime;
572                         if(m_counter1 < 0.0)
573                         {
574                                 m_counter1 += 1.0;
575                                 // Jump
576                                 m_speed_f.Y = 5.0*BS;
577                         }
578                 }
579
580                 {
581                         m_counter2 -= dtime;
582                         if(m_counter2 < 0.0)
583                         {
584                                 m_counter2 += (float)(myrand()%100)/100*3.0;
585                                 //m_yaw += ((float)(myrand()%200)-100)/100*180;
586                                 m_yaw += ((float)(myrand()%200)-100)/100*90;
587                                 m_yaw = wrapDegrees(m_yaw);
588                         }
589                 }
590         }
591         
592         m_oldpos = m_base_position;
593
594         /*
595                 Move it, with collision detection
596         */
597
598         core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*5./3.,BS/3.);
599         collisionMoveResult moveresult;
600         // Maximum movement without glitches
601         f32 pos_max_d = BS*0.25;
602         // Limit speed
603         if(m_speed_f.getLength()*dtime > pos_max_d)
604                 m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
605         v3f pos_f = getBasePosition();
606         v3f pos_f_old = pos_f;
607         moveresult = collisionMoveSimple(&m_env->getMap(), pos_max_d,
608                         box, dtime, pos_f, m_speed_f);
609         m_touching_ground = moveresult.touching_ground;
610         
611         setBasePosition(pos_f);
612
613         if(send_recommended == false)
614                 return;
615
616         if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
617         {
618                 m_last_sent_position = pos_f;
619
620                 std::ostringstream os(std::ios::binary);
621                 // command (0 = update position)
622                 writeU8(os, 0);
623                 // pos
624                 writeV3F1000(os, m_base_position);
625                 // yaw
626                 writeF1000(os, m_yaw);
627                 // create message and add to list
628                 ActiveObjectMessage aom(getId(), false, os.str());
629                 messages.push_back(aom);
630         }
631 }
632
633 std::string Oerkki1SAO::getClientInitializationData()
634 {
635         std::ostringstream os(std::ios::binary);
636         // version
637         writeU8(os, 0);
638         // pos
639         writeV3F1000(os, m_base_position);
640         return os.str();
641 }
642
643 std::string Oerkki1SAO::getStaticData()
644 {
645         //dstream<<__FUNCTION_NAME<<std::endl;
646         std::ostringstream os(std::ios::binary);
647         // version
648         writeU8(os, 0);
649         // hp
650         writeU8(os, m_hp);
651         return os.str();
652 }
653
654 u16 Oerkki1SAO::punch(const std::string &toolname)
655 {
656         u16 amount = 5;
657         if(amount < m_hp)
658         {
659                 m_hp -= amount;
660         }
661         else
662         {
663                 // Die
664                 m_removed = true;
665         }
666         return 65536/100;
667 }
668
669