]> git.lizzy.rs Git - minetest.git/blob - src/content_sao.cpp
5fb8f936e58acfe04325db3fb58ba5a5eb69e0db
[minetest.git] / src / content_sao.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 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 Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser 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 "util/serialize.h"
22 #include "util/mathconstants.h"
23 #include "collision.h"
24 #include "environment.h"
25 #include "settings.h"
26 #include "serialization.h" // For compressZlib
27 #include "tool.h" // For ToolCapabilities
28 #include "gamedef.h"
29 #include "remoteplayer.h"
30 #include "server.h"
31 #include "scripting_game.h"
32 #include "genericobject.h"
33 #include "log.h"
34
35 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
36
37 /*
38         TestSAO
39 */
40
41 class TestSAO : public ServerActiveObject
42 {
43 public:
44         TestSAO(ServerEnvironment *env, v3f pos):
45                 ServerActiveObject(env, pos),
46                 m_timer1(0),
47                 m_age(0)
48         {
49                 ServerActiveObject::registerType(getType(), create);
50         }
51         ActiveObjectType getType() const
52         { return ACTIVEOBJECT_TYPE_TEST; }
53
54         static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
55                         const std::string &data)
56         {
57                 return new TestSAO(env, pos);
58         }
59
60         void step(float dtime, bool send_recommended)
61         {
62                 m_age += dtime;
63                 if(m_age > 10)
64                 {
65                         m_removed = true;
66                         return;
67                 }
68
69                 m_base_position.Y += dtime * BS * 2;
70                 if(m_base_position.Y > 8*BS)
71                         m_base_position.Y = 2*BS;
72
73                 if(send_recommended == false)
74                         return;
75
76                 m_timer1 -= dtime;
77                 if(m_timer1 < 0.0)
78                 {
79                         m_timer1 += 0.125;
80
81                         std::string data;
82
83                         data += itos(0); // 0 = position
84                         data += " ";
85                         data += itos(m_base_position.X);
86                         data += " ";
87                         data += itos(m_base_position.Y);
88                         data += " ";
89                         data += itos(m_base_position.Z);
90
91                         ActiveObjectMessage aom(getId(), false, data);
92                         m_messages_out.push(aom);
93                 }
94         }
95
96         bool getCollisionBox(aabb3f *toset) {
97                 return false;
98         }
99
100         bool collideWithObjects() {
101                 return false;
102         }
103
104 private:
105         float m_timer1;
106         float m_age;
107 };
108
109 // Prototype (registers item for deserialization)
110 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
111
112 /*
113         LuaEntitySAO
114 */
115
116 // Prototype (registers item for deserialization)
117 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
118
119 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
120                 const std::string &name, const std::string &state):
121         UnitSAO(env, pos),
122         m_init_name(name),
123         m_init_state(state),
124         m_registered(false),
125         m_velocity(0,0,0),
126         m_acceleration(0,0,0),
127         m_properties_sent(true),
128         m_last_sent_yaw(0),
129         m_last_sent_position(0,0,0),
130         m_last_sent_velocity(0,0,0),
131         m_last_sent_position_timer(0),
132         m_last_sent_move_precision(0),
133         m_armor_groups_sent(false),
134         m_animation_speed(0),
135         m_animation_blend(0),
136         m_animation_loop(true),
137         m_animation_sent(false),
138         m_bone_position_sent(false),
139         m_attachment_parent_id(0),
140         m_attachment_sent(false)
141 {
142         // Only register type if no environment supplied
143         if(env == NULL){
144                 ServerActiveObject::registerType(getType(), create);
145                 return;
146         }
147
148         // Initialize something to armor groups
149         m_armor_groups["fleshy"] = 100;
150 }
151
152 LuaEntitySAO::~LuaEntitySAO()
153 {
154         if(m_registered){
155                 m_env->getScriptIface()->luaentity_Remove(m_id);
156         }
157
158         for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
159                 it != m_attached_particle_spawners.end(); ++it) {
160                 m_env->deleteParticleSpawner(*it, false);
161         }
162 }
163
164 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
165 {
166         ServerActiveObject::addedToEnvironment(dtime_s);
167
168         // Create entity from name
169         m_registered = m_env->getScriptIface()->
170                 luaentity_Add(m_id, m_init_name.c_str());
171
172         if(m_registered){
173                 // Get properties
174                 m_env->getScriptIface()->
175                         luaentity_GetProperties(m_id, &m_prop);
176                 // Initialize HP from properties
177                 m_hp = m_prop.hp_max;
178                 // Activate entity, supplying serialized state
179                 m_env->getScriptIface()->
180                         luaentity_Activate(m_id, m_init_state.c_str(), dtime_s);
181         } else {
182                 m_prop.infotext = m_init_name;
183         }
184 }
185
186 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
187                 const std::string &data)
188 {
189         std::string name;
190         std::string state;
191         s16 hp = 1;
192         v3f velocity;
193         float yaw = 0;
194         if(data != ""){
195                 std::istringstream is(data, std::ios::binary);
196                 // read version
197                 u8 version = readU8(is);
198                 // check if version is supported
199                 if(version == 0){
200                         name = deSerializeString(is);
201                         state = deSerializeLongString(is);
202                 }
203                 else if(version == 1){
204                         name = deSerializeString(is);
205                         state = deSerializeLongString(is);
206                         hp = readS16(is);
207                         velocity = readV3F1000(is);
208                         yaw = readF1000(is);
209                 }
210         }
211         // create object
212         infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
213                         <<state<<"\")"<<std::endl;
214         LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
215         sao->m_hp = hp;
216         sao->m_velocity = velocity;
217         sao->m_yaw = yaw;
218         return sao;
219 }
220
221 bool LuaEntitySAO::isAttached()
222 {
223         if(!m_attachment_parent_id)
224                 return false;
225         // Check if the parent still exists
226         ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
227         if(obj)
228                 return true;
229         return false;
230 }
231
232 void LuaEntitySAO::step(float dtime, bool send_recommended)
233 {
234         if(!m_properties_sent)
235         {
236                 m_properties_sent = true;
237                 std::string str = getPropertyPacket();
238                 // create message and add to list
239                 ActiveObjectMessage aom(getId(), true, str);
240                 m_messages_out.push(aom);
241         }
242
243         // If attached, check that our parent is still there. If it isn't, detach.
244         if(m_attachment_parent_id && !isAttached())
245         {
246                 m_attachment_parent_id = 0;
247                 m_attachment_bone = "";
248                 m_attachment_position = v3f(0,0,0);
249                 m_attachment_rotation = v3f(0,0,0);
250                 sendPosition(false, true);
251         }
252
253         m_last_sent_position_timer += dtime;
254
255         // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
256         // If the object gets detached this comes into effect automatically from the last known origin
257         if(isAttached())
258         {
259                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
260                 m_base_position = pos;
261                 m_velocity = v3f(0,0,0);
262                 m_acceleration = v3f(0,0,0);
263         }
264         else
265         {
266                 if(m_prop.physical){
267                         aabb3f box = m_prop.collisionbox;
268                         box.MinEdge *= BS;
269                         box.MaxEdge *= BS;
270                         collisionMoveResult moveresult;
271                         f32 pos_max_d = BS*0.25; // Distance per iteration
272                         v3f p_pos = m_base_position;
273                         v3f p_velocity = m_velocity;
274                         v3f p_acceleration = m_acceleration;
275                         moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
276                                         pos_max_d, box, m_prop.stepheight, dtime,
277                                         &p_pos, &p_velocity, p_acceleration,
278                                         this, m_prop.collideWithObjects);
279
280                         // Apply results
281                         m_base_position = p_pos;
282                         m_velocity = p_velocity;
283                         m_acceleration = p_acceleration;
284                 } else {
285                         m_base_position += dtime * m_velocity + 0.5 * dtime
286                                         * dtime * m_acceleration;
287                         m_velocity += dtime * m_acceleration;
288                 }
289
290                 if((m_prop.automatic_face_movement_dir) &&
291                                 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
292                 {
293                         float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
294                                         + m_prop.automatic_face_movement_dir_offset;
295                         float max_rotation_delta =
296                                         dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
297
298                         if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
299                                 (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
300
301                                 m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
302                         } else {
303                                 m_yaw = optimal_yaw;
304                         }
305                 }
306         }
307
308         if(m_registered){
309                 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
310         }
311
312         if(send_recommended == false)
313                 return;
314
315         if(!isAttached())
316         {
317                 // TODO: force send when acceleration changes enough?
318                 float minchange = 0.2*BS;
319                 if(m_last_sent_position_timer > 1.0){
320                         minchange = 0.01*BS;
321                 } else if(m_last_sent_position_timer > 0.2){
322                         minchange = 0.05*BS;
323                 }
324                 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
325                 move_d += m_last_sent_move_precision;
326                 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
327                 if(move_d > minchange || vel_d > minchange ||
328                                 fabs(m_yaw - m_last_sent_yaw) > 1.0){
329                         sendPosition(true, false);
330                 }
331         }
332
333         if(m_armor_groups_sent == false){
334                 m_armor_groups_sent = true;
335                 std::string str = gob_cmd_update_armor_groups(
336                                 m_armor_groups);
337                 // create message and add to list
338                 ActiveObjectMessage aom(getId(), true, str);
339                 m_messages_out.push(aom);
340         }
341
342         if(m_animation_sent == false){
343                 m_animation_sent = true;
344                 std::string str = gob_cmd_update_animation(
345                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
346                 // create message and add to list
347                 ActiveObjectMessage aom(getId(), true, str);
348                 m_messages_out.push(aom);
349         }
350
351         if(m_bone_position_sent == false){
352                 m_bone_position_sent = true;
353                 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
354                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
355                         std::string str = gob_cmd_update_bone_position((*ii).first,
356                                         (*ii).second.X, (*ii).second.Y);
357                         // create message and add to list
358                         ActiveObjectMessage aom(getId(), true, str);
359                         m_messages_out.push(aom);
360                 }
361         }
362
363         if(m_attachment_sent == false){
364                 m_attachment_sent = true;
365                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
366                 // create message and add to list
367                 ActiveObjectMessage aom(getId(), true, str);
368                 m_messages_out.push(aom);
369         }
370 }
371
372 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
373 {
374         std::ostringstream os(std::ios::binary);
375
376         if(protocol_version >= 14)
377         {
378                 writeU8(os, 1); // version
379                 os<<serializeString(""); // name
380                 writeU8(os, 0); // is_player
381                 writeS16(os, getId()); //id
382                 writeV3F1000(os, m_base_position);
383                 writeF1000(os, m_yaw);
384                 writeS16(os, m_hp);
385
386                 writeU8(os, 4 + m_bone_position.size() + m_attachment_child_ids.size()); // number of messages stuffed in here
387                 os<<serializeLongString(getPropertyPacket()); // message 1
388                 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
389                 os<<serializeLongString(gob_cmd_update_animation(
390                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
391                 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
392                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
393                         os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
394                                         (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
395                 }
396                 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
397                 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
398                                 (ii != m_attachment_child_ids.end()); ++ii) {
399                         if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
400                                 os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(), obj->getClientInitializationData(protocol_version)));
401                         }
402                 }
403         }
404         else
405         {
406                 writeU8(os, 0); // version
407                 os<<serializeString(""); // name
408                 writeU8(os, 0); // is_player
409                 writeV3F1000(os, m_base_position);
410                 writeF1000(os, m_yaw);
411                 writeS16(os, m_hp);
412                 writeU8(os, 2); // number of messages stuffed in here
413                 os<<serializeLongString(getPropertyPacket()); // message 1
414                 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
415         }
416
417         // return result
418         return os.str();
419 }
420
421 std::string LuaEntitySAO::getStaticData()
422 {
423         verbosestream<<FUNCTION_NAME<<std::endl;
424         std::ostringstream os(std::ios::binary);
425         // version
426         writeU8(os, 1);
427         // name
428         os<<serializeString(m_init_name);
429         // state
430         if(m_registered){
431                 std::string state = m_env->getScriptIface()->
432                         luaentity_GetStaticdata(m_id);
433                 os<<serializeLongString(state);
434         } else {
435                 os<<serializeLongString(m_init_state);
436         }
437         // hp
438         writeS16(os, m_hp);
439         // velocity
440         writeV3F1000(os, m_velocity);
441         // yaw
442         writeF1000(os, m_yaw);
443         return os.str();
444 }
445
446 int LuaEntitySAO::punch(v3f dir,
447                 const ToolCapabilities *toolcap,
448                 ServerActiveObject *puncher,
449                 float time_from_last_punch)
450 {
451         if (!m_registered){
452                 // Delete unknown LuaEntities when punched
453                 m_removed = true;
454                 return 0;
455         }
456
457         // It's best that attachments cannot be punched
458         if (isAttached())
459                 return 0;
460
461         ItemStack *punchitem = NULL;
462         ItemStack punchitem_static;
463         if (puncher) {
464                 punchitem_static = puncher->getWieldedItem();
465                 punchitem = &punchitem_static;
466         }
467
468         PunchDamageResult result = getPunchDamage(
469                         m_armor_groups,
470                         toolcap,
471                         punchitem,
472                         time_from_last_punch);
473
474         if (result.did_punch) {
475                 setHP(getHP() - result.damage);
476
477                 if (result.damage > 0) {
478                         std::string punchername = puncher ? puncher->getDescription() : "nil";
479
480                         actionstream << getDescription() << " punched by "
481                                         << punchername << ", damage " << result.damage
482                                         << " hp, health now " << getHP() << " hp" << std::endl;
483                 }
484
485                 std::string str = gob_cmd_punched(result.damage, getHP());
486                 // create message and add to list
487                 ActiveObjectMessage aom(getId(), true, str);
488                 m_messages_out.push(aom);
489         }
490
491         if (getHP() == 0)
492                 m_removed = true;
493
494         m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
495                         time_from_last_punch, toolcap, dir);
496
497         return result.wear;
498 }
499
500 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
501 {
502         if (!m_registered)
503                 return;
504         // It's best that attachments cannot be clicked
505         if (isAttached())
506                 return;
507         m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
508 }
509
510 void LuaEntitySAO::setPos(const v3f &pos)
511 {
512         if(isAttached())
513                 return;
514         m_base_position = pos;
515         sendPosition(false, true);
516 }
517
518 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
519 {
520         if(isAttached())
521                 return;
522         m_base_position = pos;
523         if(!continuous)
524                 sendPosition(true, true);
525 }
526
527 float LuaEntitySAO::getMinimumSavedMovement()
528 {
529         return 0.1 * BS;
530 }
531
532 std::string LuaEntitySAO::getDescription()
533 {
534         std::ostringstream os(std::ios::binary);
535         os<<"LuaEntitySAO at (";
536         os<<(m_base_position.X/BS)<<",";
537         os<<(m_base_position.Y/BS)<<",";
538         os<<(m_base_position.Z/BS);
539         os<<")";
540         return os.str();
541 }
542
543 void LuaEntitySAO::setHP(s16 hp)
544 {
545         if(hp < 0) hp = 0;
546         m_hp = hp;
547 }
548
549 s16 LuaEntitySAO::getHP() const
550 {
551         return m_hp;
552 }
553
554 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
555 {
556         m_armor_groups = armor_groups;
557         m_armor_groups_sent = false;
558 }
559
560 ItemGroupList LuaEntitySAO::getArmorGroups()
561 {
562         return m_armor_groups;
563 }
564
565 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
566 {
567         m_animation_range = frame_range;
568         m_animation_speed = frame_speed;
569         m_animation_blend = frame_blend;
570         m_animation_loop = frame_loop;
571         m_animation_sent = false;
572 }
573
574 void LuaEntitySAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
575 {
576         *frame_range = m_animation_range;
577         *frame_speed = m_animation_speed;
578         *frame_blend = m_animation_blend;
579         *frame_loop = m_animation_loop;
580 }
581
582 void LuaEntitySAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
583 {
584         m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
585         m_bone_position_sent = false;
586 }
587
588 void LuaEntitySAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
589 {
590         *position = m_bone_position[bone].X;
591         *rotation = m_bone_position[bone].Y;
592 }
593
594 void LuaEntitySAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
595 {
596         // Attachments need to be handled on both the server and client.
597         // If we just attach on the server, we can only copy the position of the parent. Attachments
598         // are still sent to clients at an interval so players might see them lagging, plus we can't
599         // read and attach to skeletal bones.
600         // If we just attach on the client, the server still sees the child at its original location.
601         // This breaks some things so we also give the server the most accurate representation
602         // even if players only see the client changes.
603
604         m_attachment_parent_id = parent_id;
605         m_attachment_bone = bone;
606         m_attachment_position = position;
607         m_attachment_rotation = rotation;
608         m_attachment_sent = false;
609 }
610
611 void LuaEntitySAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
612         v3f *rotation)
613 {
614         *parent_id = m_attachment_parent_id;
615         *bone = m_attachment_bone;
616         *position = m_attachment_position;
617         *rotation = m_attachment_rotation;
618 }
619
620 void LuaEntitySAO::addAttachmentChild(int child_id)
621 {
622         m_attachment_child_ids.insert(child_id);
623 }
624
625 void LuaEntitySAO::removeAttachmentChild(int child_id)
626 {
627         m_attachment_child_ids.erase(child_id);
628 }
629
630 UNORDERED_SET<int> LuaEntitySAO::getAttachmentChildIds()
631 {
632         return m_attachment_child_ids;
633 }
634
635 ObjectProperties* LuaEntitySAO::accessObjectProperties()
636 {
637         return &m_prop;
638 }
639
640 void LuaEntitySAO::notifyObjectPropertiesModified()
641 {
642         m_properties_sent = false;
643 }
644
645 void LuaEntitySAO::setVelocity(v3f velocity)
646 {
647         m_velocity = velocity;
648 }
649
650 v3f LuaEntitySAO::getVelocity()
651 {
652         return m_velocity;
653 }
654
655 void LuaEntitySAO::setAcceleration(v3f acceleration)
656 {
657         m_acceleration = acceleration;
658 }
659
660 v3f LuaEntitySAO::getAcceleration()
661 {
662         return m_acceleration;
663 }
664
665 void LuaEntitySAO::setTextureMod(const std::string &mod)
666 {
667         std::string str = gob_cmd_set_texture_mod(mod);
668         // create message and add to list
669         ActiveObjectMessage aom(getId(), true, str);
670         m_messages_out.push(aom);
671 }
672
673 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
674                 bool select_horiz_by_yawpitch)
675 {
676         std::string str = gob_cmd_set_sprite(
677                 p,
678                 num_frames,
679                 framelength,
680                 select_horiz_by_yawpitch
681         );
682         // create message and add to list
683         ActiveObjectMessage aom(getId(), true, str);
684         m_messages_out.push(aom);
685 }
686
687 std::string LuaEntitySAO::getName()
688 {
689         return m_init_name;
690 }
691
692 std::string LuaEntitySAO::getPropertyPacket()
693 {
694         return gob_cmd_set_properties(m_prop);
695 }
696
697 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
698 {
699         // If the object is attached client-side, don't waste bandwidth sending its position to clients
700         if(isAttached())
701                 return;
702
703         m_last_sent_move_precision = m_base_position.getDistanceFrom(
704                         m_last_sent_position);
705         m_last_sent_position_timer = 0;
706         m_last_sent_yaw = m_yaw;
707         m_last_sent_position = m_base_position;
708         m_last_sent_velocity = m_velocity;
709         //m_last_sent_acceleration = m_acceleration;
710
711         float update_interval = m_env->getSendRecommendedInterval();
712
713         std::string str = gob_cmd_update_position(
714                 m_base_position,
715                 m_velocity,
716                 m_acceleration,
717                 m_yaw,
718                 do_interpolate,
719                 is_movement_end,
720                 update_interval
721         );
722         // create message and add to list
723         ActiveObjectMessage aom(getId(), false, str);
724         m_messages_out.push(aom);
725 }
726
727 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) {
728         if (m_prop.physical)
729         {
730                 //update collision box
731                 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
732                 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
733
734                 toset->MinEdge += m_base_position;
735                 toset->MaxEdge += m_base_position;
736
737                 return true;
738         }
739
740         return false;
741 }
742
743 bool LuaEntitySAO::collideWithObjects(){
744         return m_prop.collideWithObjects;
745 }
746
747 /*
748         PlayerSAO
749 */
750
751 // No prototype, PlayerSAO does not need to be deserialized
752
753 PlayerSAO::PlayerSAO(ServerEnvironment *env_, u16 peer_id_, bool is_singleplayer):
754         UnitSAO(env_, v3f(0,0,0)),
755         m_player(NULL),
756         m_peer_id(peer_id_),
757         m_inventory(NULL),
758         m_damage(0),
759         m_last_good_position(0,0,0),
760         m_time_from_last_punch(0),
761         m_nocheat_dig_pos(32767, 32767, 32767),
762         m_nocheat_dig_time(0),
763         m_wield_index(0),
764         m_position_not_sent(false),
765         m_armor_groups_sent(false),
766         m_properties_sent(true),
767         m_is_singleplayer(is_singleplayer),
768         m_animation_speed(0),
769         m_animation_blend(0),
770         m_animation_loop(true),
771         m_animation_sent(false),
772         m_bone_position_sent(false),
773         m_attachment_parent_id(0),
774         m_attachment_sent(false),
775         m_breath(PLAYER_MAX_BREATH),
776         m_pitch(0),
777         // public
778         m_physics_override_speed(1),
779         m_physics_override_jump(1),
780         m_physics_override_gravity(1),
781         m_physics_override_sneak(true),
782         m_physics_override_sneak_glitch(true),
783         m_physics_override_sent(false)
784 {
785         assert(m_peer_id != 0); // pre-condition
786         m_armor_groups["fleshy"] = 100;
787
788         m_prop.hp_max = PLAYER_MAX_HP;
789         m_prop.physical = false;
790         m_prop.weight = 75;
791         m_prop.collisionbox = aabb3f(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
792         // start of default appearance, this should be overwritten by LUA
793         m_prop.visual = "upright_sprite";
794         m_prop.visual_size = v2f(1, 2);
795         m_prop.textures.clear();
796         m_prop.textures.push_back("player.png");
797         m_prop.textures.push_back("player_back.png");
798         m_prop.colors.clear();
799         m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
800         m_prop.spritediv = v2s16(1,1);
801         // end of default appearance
802         m_prop.is_visible = true;
803         m_prop.makes_footstep_sound = true;
804         m_hp = PLAYER_MAX_HP;
805 }
806
807 PlayerSAO::~PlayerSAO()
808 {
809         if(m_inventory != &m_player->inventory)
810                 delete m_inventory;
811 }
812
813 void PlayerSAO::initialize(RemotePlayer *player, const std::set<std::string> &privs)
814 {
815         assert(player);
816         m_player = player;
817         m_privs = privs;
818         m_inventory = &m_player->inventory;
819 }
820
821 std::string PlayerSAO::getDescription()
822 {
823         return std::string("player ") + m_player->getName();
824 }
825
826 // Called after id has been set and has been inserted in environment
827 void PlayerSAO::addedToEnvironment(u32 dtime_s)
828 {
829         ServerActiveObject::addedToEnvironment(dtime_s);
830         ServerActiveObject::setBasePosition(m_base_position);
831         m_player->setPlayerSAO(this);
832         m_player->peer_id = m_peer_id;
833         m_last_good_position = m_base_position;
834 }
835
836 // Called before removing from environment
837 void PlayerSAO::removingFromEnvironment()
838 {
839         ServerActiveObject::removingFromEnvironment();
840         if (m_player->getPlayerSAO() == this) {
841                 m_player->peer_id = 0;
842                 m_env->savePlayer(m_player);
843                 m_player->setPlayerSAO(NULL);
844                 m_env->removePlayer(m_player);
845                 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
846                         it != m_attached_particle_spawners.end(); ++it) {
847                         m_env->deleteParticleSpawner(*it, false);
848                 }
849         }
850 }
851
852 bool PlayerSAO::isStaticAllowed() const
853 {
854         return false;
855 }
856
857 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
858 {
859         std::ostringstream os(std::ios::binary);
860
861         if(protocol_version >= 15)
862         {
863                 writeU8(os, 1); // version
864                 os<<serializeString(m_player->getName()); // name
865                 writeU8(os, 1); // is_player
866                 writeS16(os, getId()); //id
867                 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
868                 writeF1000(os, m_yaw);
869                 writeS16(os, getHP());
870
871                 writeU8(os, 6 + m_bone_position.size() + m_attachment_child_ids.size()); // number of messages stuffed in here
872                 os<<serializeLongString(getPropertyPacket()); // message 1
873                 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
874                 os<<serializeLongString(gob_cmd_update_animation(
875                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
876                 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
877                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
878                         os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
879                 }
880                 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
881                 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
882                                 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
883                                 m_physics_override_sneak_glitch)); // 5
884                 os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6 (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
885                 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
886                                 ii != m_attachment_child_ids.end(); ++ii) {
887                         if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
888                                 os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(), obj->getClientInitializationData(protocol_version)));
889                         }
890                 }
891         }
892         else
893         {
894                 writeU8(os, 0); // version
895                 os<<serializeString(m_player->getName()); // name
896                 writeU8(os, 1); // is_player
897                 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
898                 writeF1000(os, m_yaw);
899                 writeS16(os, getHP());
900                 writeU8(os, 2); // number of messages stuffed in here
901                 os<<serializeLongString(getPropertyPacket()); // message 1
902                 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
903         }
904
905         // return result
906         return os.str();
907 }
908
909 std::string PlayerSAO::getStaticData()
910 {
911         FATAL_ERROR("Deprecated function (?)");
912         return "";
913 }
914
915 bool PlayerSAO::isAttached()
916 {
917         if(!m_attachment_parent_id)
918                 return false;
919         // Check if the parent still exists
920         ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
921         if(obj)
922                 return true;
923         return false;
924 }
925
926 void PlayerSAO::step(float dtime, bool send_recommended)
927 {
928         if(!m_properties_sent)
929         {
930                 m_properties_sent = true;
931                 std::string str = getPropertyPacket();
932                 // create message and add to list
933                 ActiveObjectMessage aom(getId(), true, str);
934                 m_messages_out.push(aom);
935         }
936
937         // If attached, check that our parent is still there. If it isn't, detach.
938         if(m_attachment_parent_id && !isAttached())
939         {
940                 m_attachment_parent_id = 0;
941                 m_attachment_bone = "";
942                 m_attachment_position = v3f(0,0,0);
943                 m_attachment_rotation = v3f(0,0,0);
944                 setBasePosition(m_last_good_position);
945                 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
946         }
947
948         //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
949
950         // Set lag pool maximums based on estimated lag
951         const float LAG_POOL_MIN = 5.0;
952         float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
953         if(lag_pool_max < LAG_POOL_MIN)
954                 lag_pool_max = LAG_POOL_MIN;
955         m_dig_pool.setMax(lag_pool_max);
956         m_move_pool.setMax(lag_pool_max);
957
958         // Increment cheat prevention timers
959         m_dig_pool.add(dtime);
960         m_move_pool.add(dtime);
961         m_time_from_last_punch += dtime;
962         m_nocheat_dig_time += dtime;
963
964         // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
965         // If the object gets detached this comes into effect automatically from the last known origin
966         if (isAttached()) {
967                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
968                 m_last_good_position = pos;
969                 setBasePosition(pos);
970         }
971
972         if (!send_recommended)
973                 return;
974
975         // If the object is attached client-side, don't waste bandwidth sending its position to clients
976         if(m_position_not_sent && !isAttached())
977         {
978                 m_position_not_sent = false;
979                 float update_interval = m_env->getSendRecommendedInterval();
980                 v3f pos;
981                 if(isAttached()) // Just in case we ever do send attachment position too
982                         pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
983                 else
984                         pos = m_base_position + v3f(0,BS*1,0);
985                 std::string str = gob_cmd_update_position(
986                         pos,
987                         v3f(0,0,0),
988                         v3f(0,0,0),
989                         m_yaw,
990                         true,
991                         false,
992                         update_interval
993                 );
994                 // create message and add to list
995                 ActiveObjectMessage aom(getId(), false, str);
996                 m_messages_out.push(aom);
997         }
998
999         if (!m_armor_groups_sent) {
1000                 m_armor_groups_sent = true;
1001                 std::string str = gob_cmd_update_armor_groups(
1002                                 m_armor_groups);
1003                 // create message and add to list
1004                 ActiveObjectMessage aom(getId(), true, str);
1005                 m_messages_out.push(aom);
1006         }
1007
1008         if (!m_physics_override_sent) {
1009                 m_physics_override_sent = true;
1010                 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1011                                 m_physics_override_jump, m_physics_override_gravity,
1012                                 m_physics_override_sneak, m_physics_override_sneak_glitch);
1013                 // create message and add to list
1014                 ActiveObjectMessage aom(getId(), true, str);
1015                 m_messages_out.push(aom);
1016         }
1017
1018         if (!m_animation_sent) {
1019                 m_animation_sent = true;
1020                 std::string str = gob_cmd_update_animation(
1021                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1022                 // create message and add to list
1023                 ActiveObjectMessage aom(getId(), true, str);
1024                 m_messages_out.push(aom);
1025         }
1026
1027         if (!m_bone_position_sent) {
1028                 m_bone_position_sent = true;
1029                 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
1030                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1031                         std::string str = gob_cmd_update_bone_position((*ii).first,
1032                                         (*ii).second.X, (*ii).second.Y);
1033                         // create message and add to list
1034                         ActiveObjectMessage aom(getId(), true, str);
1035                         m_messages_out.push(aom);
1036                 }
1037         }
1038
1039         if (!m_attachment_sent){
1040                 m_attachment_sent = true;
1041                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1042                                 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1043                 // create message and add to list
1044                 ActiveObjectMessage aom(getId(), true, str);
1045                 m_messages_out.push(aom);
1046         }
1047 }
1048
1049 void PlayerSAO::setBasePosition(const v3f &position)
1050 {
1051         if (m_player && position != m_base_position)
1052                 m_player->setDirty(true);
1053
1054         // This needs to be ran for attachments too
1055         ServerActiveObject::setBasePosition(position);
1056         m_position_not_sent = true;
1057 }
1058
1059 void PlayerSAO::setPos(const v3f &pos)
1060 {
1061         if(isAttached())
1062                 return;
1063
1064         setBasePosition(pos);
1065         // Movement caused by this command is always valid
1066         m_last_good_position = pos;
1067         ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1068 }
1069
1070 void PlayerSAO::moveTo(v3f pos, bool continuous)
1071 {
1072         if(isAttached())
1073                 return;
1074
1075         setBasePosition(pos);
1076         // Movement caused by this command is always valid
1077         m_last_good_position = pos;
1078         ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1079 }
1080
1081 void PlayerSAO::setYaw(const float yaw)
1082 {
1083         if (m_player && yaw != m_yaw)
1084                 m_player->setDirty(true);
1085
1086         UnitSAO::setYaw(yaw);
1087 }
1088
1089 void PlayerSAO::setYawAndSend(const float yaw)
1090 {
1091         setYaw(yaw);
1092         ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1093 }
1094
1095 void PlayerSAO::setPitch(const float pitch)
1096 {
1097         if (m_player && pitch != m_pitch)
1098                 m_player->setDirty(true);
1099
1100         m_pitch = pitch;
1101 }
1102
1103 void PlayerSAO::setPitchAndSend(const float pitch)
1104 {
1105         setPitch(pitch);
1106         ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1107 }
1108
1109 int PlayerSAO::punch(v3f dir,
1110         const ToolCapabilities *toolcap,
1111         ServerActiveObject *puncher,
1112         float time_from_last_punch)
1113 {
1114         // It's best that attachments cannot be punched
1115         if (isAttached())
1116                 return 0;
1117
1118         if (!toolcap)
1119                 return 0;
1120
1121         // No effect if PvP disabled
1122         if (g_settings->getBool("enable_pvp") == false) {
1123                 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1124                         std::string str = gob_cmd_punched(0, getHP());
1125                         // create message and add to list
1126                         ActiveObjectMessage aom(getId(), true, str);
1127                         m_messages_out.push(aom);
1128                         return 0;
1129                 }
1130         }
1131
1132         HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1133                         time_from_last_punch);
1134
1135         std::string punchername = "nil";
1136
1137         if (puncher != 0)
1138                 punchername = puncher->getDescription();
1139
1140         PlayerSAO *playersao = m_player->getPlayerSAO();
1141
1142         bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1143                                 puncher, time_from_last_punch, toolcap, dir,
1144                                 hitparams.hp);
1145
1146         if (!damage_handled) {
1147                 setHP(getHP() - hitparams.hp);
1148         } else { // override client prediction
1149                 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1150                         std::string str = gob_cmd_punched(0, getHP());
1151                         // create message and add to list
1152                         ActiveObjectMessage aom(getId(), true, str);
1153                         m_messages_out.push(aom);
1154                 }
1155         }
1156
1157
1158         actionstream << "Player " << m_player->getName() << " punched by "
1159                         << punchername;
1160         if (!damage_handled) {
1161                 actionstream << ", damage " << hitparams.hp << " HP";
1162         } else {
1163                 actionstream << ", damage handled by lua";
1164         }
1165         actionstream << std::endl;
1166
1167         return hitparams.wear;
1168 }
1169
1170 void PlayerSAO::rightClick(ServerActiveObject *)
1171 {
1172 }
1173
1174 s16 PlayerSAO::readDamage()
1175 {
1176         s16 damage = m_damage;
1177         m_damage = 0;
1178         return damage;
1179 }
1180
1181 void PlayerSAO::setHP(s16 hp)
1182 {
1183         s16 oldhp = m_hp;
1184
1185         s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1186         if (hp_change == 0)
1187                 return;
1188         hp = oldhp + hp_change;
1189
1190         if (hp < 0)
1191                 hp = 0;
1192         else if (hp > PLAYER_MAX_HP)
1193                 hp = PLAYER_MAX_HP;
1194
1195         if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1196                 return;
1197         }
1198
1199         m_hp = hp;
1200
1201         if (oldhp > hp)
1202                 m_damage += (oldhp - hp);
1203
1204         // Update properties on death
1205         if ((hp == 0) != (oldhp == 0))
1206                 m_properties_sent = false;
1207 }
1208
1209 void PlayerSAO::setBreath(const u16 breath)
1210 {
1211         if (m_player && breath != m_breath)
1212                 m_player->setDirty(true);
1213
1214         m_breath = breath;
1215 }
1216
1217 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1218 {
1219         m_armor_groups = armor_groups;
1220         m_armor_groups_sent = false;
1221 }
1222
1223 ItemGroupList PlayerSAO::getArmorGroups()
1224 {
1225         return m_armor_groups;
1226 }
1227
1228 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
1229 {
1230         // store these so they can be updated to clients
1231         m_animation_range = frame_range;
1232         m_animation_speed = frame_speed;
1233         m_animation_blend = frame_blend;
1234         m_animation_loop = frame_loop;
1235         m_animation_sent = false;
1236 }
1237
1238 void PlayerSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
1239 {
1240         *frame_range = m_animation_range;
1241         *frame_speed = m_animation_speed;
1242         *frame_blend = m_animation_blend;
1243         *frame_loop = m_animation_loop;
1244 }
1245
1246 void PlayerSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
1247 {
1248         // store these so they can be updated to clients
1249         m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1250         m_bone_position_sent = false;
1251 }
1252
1253 void PlayerSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
1254 {
1255         *position = m_bone_position[bone].X;
1256         *rotation = m_bone_position[bone].Y;
1257 }
1258
1259 void PlayerSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
1260 {
1261         // Attachments need to be handled on both the server and client.
1262         // If we just attach on the server, we can only copy the position of the parent. Attachments
1263         // are still sent to clients at an interval so players might see them lagging, plus we can't
1264         // read and attach to skeletal bones.
1265         // If we just attach on the client, the server still sees the child at its original location.
1266         // This breaks some things so we also give the server the most accurate representation
1267         // even if players only see the client changes.
1268
1269         m_attachment_parent_id = parent_id;
1270         m_attachment_bone = bone;
1271         m_attachment_position = position;
1272         m_attachment_rotation = rotation;
1273         m_attachment_sent = false;
1274 }
1275
1276 void PlayerSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
1277         v3f *rotation)
1278 {
1279         *parent_id = m_attachment_parent_id;
1280         *bone = m_attachment_bone;
1281         *position = m_attachment_position;
1282         *rotation = m_attachment_rotation;
1283 }
1284
1285 void PlayerSAO::addAttachmentChild(int child_id)
1286 {
1287         m_attachment_child_ids.insert(child_id);
1288 }
1289
1290 void PlayerSAO::removeAttachmentChild(int child_id)
1291 {
1292         m_attachment_child_ids.erase(child_id);
1293 }
1294
1295 UNORDERED_SET<int> PlayerSAO::getAttachmentChildIds()
1296 {
1297         return m_attachment_child_ids;
1298 }
1299
1300 ObjectProperties* PlayerSAO::accessObjectProperties()
1301 {
1302         return &m_prop;
1303 }
1304
1305 void PlayerSAO::notifyObjectPropertiesModified()
1306 {
1307         m_properties_sent = false;
1308 }
1309
1310 Inventory* PlayerSAO::getInventory()
1311 {
1312         return m_inventory;
1313 }
1314 const Inventory* PlayerSAO::getInventory() const
1315 {
1316         return m_inventory;
1317 }
1318
1319 InventoryLocation PlayerSAO::getInventoryLocation() const
1320 {
1321         InventoryLocation loc;
1322         loc.setPlayer(m_player->getName());
1323         return loc;
1324 }
1325
1326 std::string PlayerSAO::getWieldList() const
1327 {
1328         return "main";
1329 }
1330
1331 int PlayerSAO::getWieldIndex() const
1332 {
1333         return m_wield_index;
1334 }
1335
1336 void PlayerSAO::setWieldIndex(int i)
1337 {
1338         if(i != m_wield_index) {
1339                 m_wield_index = i;
1340         }
1341 }
1342
1343 void PlayerSAO::disconnected()
1344 {
1345         m_peer_id = 0;
1346         m_removed = true;
1347         if(m_player->getPlayerSAO() == this)
1348         {
1349                 m_player->setPlayerSAO(NULL);
1350                 m_player->peer_id = 0;
1351         }
1352 }
1353
1354 std::string PlayerSAO::getPropertyPacket()
1355 {
1356         m_prop.is_visible = (true);
1357         return gob_cmd_set_properties(m_prop);
1358 }
1359
1360 bool PlayerSAO::checkMovementCheat()
1361 {
1362         if (isAttached() || m_is_singleplayer ||
1363                         g_settings->getBool("disable_anticheat")) {
1364                 m_last_good_position = m_base_position;
1365                 return false;
1366         }
1367
1368         bool cheated = false;
1369         /*
1370                 Check player movements
1371
1372                 NOTE: Actually the server should handle player physics like the
1373                 client does and compare player's position to what is calculated
1374                 on our side. This is required when eg. players fly due to an
1375                 explosion. Altough a node-based alternative might be possible
1376                 too, and much more lightweight.
1377         */
1378
1379         float player_max_speed = 0;
1380
1381         if (m_privs.count("fast") != 0) {
1382                 // Fast speed
1383                 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1384         } else {
1385                 // Normal speed
1386                 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1387         }
1388         // Tolerance. The lag pool does this a bit.
1389         //player_max_speed *= 2.5;
1390
1391         v3f diff = (m_base_position - m_last_good_position);
1392         float d_vert = diff.Y;
1393         diff.Y = 0;
1394         float d_horiz = diff.getLength();
1395         float required_time = d_horiz / player_max_speed;
1396
1397         if (d_vert > 0 && d_vert / player_max_speed > required_time)
1398                 required_time = d_vert / player_max_speed; // Moving upwards
1399
1400         if (m_move_pool.grab(required_time)) {
1401                 m_last_good_position = m_base_position;
1402         } else {
1403                 actionstream << "Player " << m_player->getName()
1404                                 << " moved too fast; resetting position"
1405                                 << std::endl;
1406                 setBasePosition(m_last_good_position);
1407                 cheated = true;
1408         }
1409         return cheated;
1410 }
1411
1412 bool PlayerSAO::getCollisionBox(aabb3f *toset)
1413 {
1414         *toset = aabb3f(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30);
1415         toset->MinEdge += m_base_position;
1416         toset->MaxEdge += m_base_position;
1417         return true;
1418 }
1419
1420 bool PlayerSAO::collideWithObjects()
1421 {
1422         return true;
1423 }