dff222f42a16cdf3ed2aa1a7edf4b3211e408c15
[oweals/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                 unlinkPlayerSessionAndSave();
842                 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
843                         it != m_attached_particle_spawners.end(); ++it) {
844                         m_env->deleteParticleSpawner(*it, false);
845                 }
846         }
847 }
848
849 bool PlayerSAO::isStaticAllowed() const
850 {
851         return false;
852 }
853
854 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
855 {
856         std::ostringstream os(std::ios::binary);
857
858         if(protocol_version >= 15)
859         {
860                 writeU8(os, 1); // version
861                 os<<serializeString(m_player->getName()); // name
862                 writeU8(os, 1); // is_player
863                 writeS16(os, getId()); //id
864                 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
865                 writeF1000(os, m_yaw);
866                 writeS16(os, getHP());
867
868                 writeU8(os, 6 + m_bone_position.size() + m_attachment_child_ids.size()); // number of messages stuffed in here
869                 os<<serializeLongString(getPropertyPacket()); // message 1
870                 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
871                 os<<serializeLongString(gob_cmd_update_animation(
872                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
873                 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
874                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
875                         os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
876                 }
877                 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
878                 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
879                                 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
880                                 m_physics_override_sneak_glitch)); // 5
881                 os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6 (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
882                 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
883                                 ii != m_attachment_child_ids.end(); ++ii) {
884                         if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
885                                 os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(), obj->getClientInitializationData(protocol_version)));
886                         }
887                 }
888         }
889         else
890         {
891                 writeU8(os, 0); // version
892                 os<<serializeString(m_player->getName()); // name
893                 writeU8(os, 1); // is_player
894                 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
895                 writeF1000(os, m_yaw);
896                 writeS16(os, getHP());
897                 writeU8(os, 2); // number of messages stuffed in here
898                 os<<serializeLongString(getPropertyPacket()); // message 1
899                 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
900         }
901
902         // return result
903         return os.str();
904 }
905
906 std::string PlayerSAO::getStaticData()
907 {
908         FATAL_ERROR("Deprecated function (?)");
909         return "";
910 }
911
912 bool PlayerSAO::isAttached()
913 {
914         if(!m_attachment_parent_id)
915                 return false;
916         // Check if the parent still exists
917         ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
918         if(obj)
919                 return true;
920         return false;
921 }
922
923 void PlayerSAO::step(float dtime, bool send_recommended)
924 {
925         if(!m_properties_sent)
926         {
927                 m_properties_sent = true;
928                 std::string str = getPropertyPacket();
929                 // create message and add to list
930                 ActiveObjectMessage aom(getId(), true, str);
931                 m_messages_out.push(aom);
932         }
933
934         // If attached, check that our parent is still there. If it isn't, detach.
935         if(m_attachment_parent_id && !isAttached())
936         {
937                 m_attachment_parent_id = 0;
938                 m_attachment_bone = "";
939                 m_attachment_position = v3f(0,0,0);
940                 m_attachment_rotation = v3f(0,0,0);
941                 setBasePosition(m_last_good_position);
942                 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
943         }
944
945         //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
946
947         // Set lag pool maximums based on estimated lag
948         const float LAG_POOL_MIN = 5.0;
949         float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
950         if(lag_pool_max < LAG_POOL_MIN)
951                 lag_pool_max = LAG_POOL_MIN;
952         m_dig_pool.setMax(lag_pool_max);
953         m_move_pool.setMax(lag_pool_max);
954
955         // Increment cheat prevention timers
956         m_dig_pool.add(dtime);
957         m_move_pool.add(dtime);
958         m_time_from_last_punch += dtime;
959         m_nocheat_dig_time += dtime;
960
961         // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
962         // If the object gets detached this comes into effect automatically from the last known origin
963         if (isAttached()) {
964                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
965                 m_last_good_position = pos;
966                 setBasePosition(pos);
967         }
968
969         if (!send_recommended)
970                 return;
971
972         // If the object is attached client-side, don't waste bandwidth sending its position to clients
973         if(m_position_not_sent && !isAttached())
974         {
975                 m_position_not_sent = false;
976                 float update_interval = m_env->getSendRecommendedInterval();
977                 v3f pos;
978                 if(isAttached()) // Just in case we ever do send attachment position too
979                         pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
980                 else
981                         pos = m_base_position + v3f(0,BS*1,0);
982                 std::string str = gob_cmd_update_position(
983                         pos,
984                         v3f(0,0,0),
985                         v3f(0,0,0),
986                         m_yaw,
987                         true,
988                         false,
989                         update_interval
990                 );
991                 // create message and add to list
992                 ActiveObjectMessage aom(getId(), false, str);
993                 m_messages_out.push(aom);
994         }
995
996         if (!m_armor_groups_sent) {
997                 m_armor_groups_sent = true;
998                 std::string str = gob_cmd_update_armor_groups(
999                                 m_armor_groups);
1000                 // create message and add to list
1001                 ActiveObjectMessage aom(getId(), true, str);
1002                 m_messages_out.push(aom);
1003         }
1004
1005         if (!m_physics_override_sent) {
1006                 m_physics_override_sent = true;
1007                 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1008                                 m_physics_override_jump, m_physics_override_gravity,
1009                                 m_physics_override_sneak, m_physics_override_sneak_glitch);
1010                 // create message and add to list
1011                 ActiveObjectMessage aom(getId(), true, str);
1012                 m_messages_out.push(aom);
1013         }
1014
1015         if (!m_animation_sent) {
1016                 m_animation_sent = true;
1017                 std::string str = gob_cmd_update_animation(
1018                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1019                 // create message and add to list
1020                 ActiveObjectMessage aom(getId(), true, str);
1021                 m_messages_out.push(aom);
1022         }
1023
1024         if (!m_bone_position_sent) {
1025                 m_bone_position_sent = true;
1026                 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
1027                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1028                         std::string str = gob_cmd_update_bone_position((*ii).first,
1029                                         (*ii).second.X, (*ii).second.Y);
1030                         // create message and add to list
1031                         ActiveObjectMessage aom(getId(), true, str);
1032                         m_messages_out.push(aom);
1033                 }
1034         }
1035
1036         if (!m_attachment_sent){
1037                 m_attachment_sent = true;
1038                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1039                                 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1040                 // create message and add to list
1041                 ActiveObjectMessage aom(getId(), true, str);
1042                 m_messages_out.push(aom);
1043         }
1044 }
1045
1046 void PlayerSAO::setBasePosition(const v3f &position)
1047 {
1048         if (m_player && position != m_base_position)
1049                 m_player->setDirty(true);
1050
1051         // This needs to be ran for attachments too
1052         ServerActiveObject::setBasePosition(position);
1053         m_position_not_sent = true;
1054 }
1055
1056 void PlayerSAO::setPos(const v3f &pos)
1057 {
1058         if(isAttached())
1059                 return;
1060
1061         setBasePosition(pos);
1062         // Movement caused by this command is always valid
1063         m_last_good_position = pos;
1064         ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1065 }
1066
1067 void PlayerSAO::moveTo(v3f pos, bool continuous)
1068 {
1069         if(isAttached())
1070                 return;
1071
1072         setBasePosition(pos);
1073         // Movement caused by this command is always valid
1074         m_last_good_position = pos;
1075         ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1076 }
1077
1078 void PlayerSAO::setYaw(const float yaw)
1079 {
1080         if (m_player && yaw != m_yaw)
1081                 m_player->setDirty(true);
1082
1083         UnitSAO::setYaw(yaw);
1084 }
1085
1086 void PlayerSAO::setYawAndSend(const float yaw)
1087 {
1088         setYaw(yaw);
1089         ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1090 }
1091
1092 void PlayerSAO::setPitch(const float pitch)
1093 {
1094         if (m_player && pitch != m_pitch)
1095                 m_player->setDirty(true);
1096
1097         m_pitch = pitch;
1098 }
1099
1100 void PlayerSAO::setPitchAndSend(const float pitch)
1101 {
1102         setPitch(pitch);
1103         ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1104 }
1105
1106 int PlayerSAO::punch(v3f dir,
1107         const ToolCapabilities *toolcap,
1108         ServerActiveObject *puncher,
1109         float time_from_last_punch)
1110 {
1111         // It's best that attachments cannot be punched
1112         if (isAttached())
1113                 return 0;
1114
1115         if (!toolcap)
1116                 return 0;
1117
1118         // No effect if PvP disabled
1119         if (g_settings->getBool("enable_pvp") == false) {
1120                 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1121                         std::string str = gob_cmd_punched(0, getHP());
1122                         // create message and add to list
1123                         ActiveObjectMessage aom(getId(), true, str);
1124                         m_messages_out.push(aom);
1125                         return 0;
1126                 }
1127         }
1128
1129         HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1130                         time_from_last_punch);
1131
1132         std::string punchername = "nil";
1133
1134         if (puncher != 0)
1135                 punchername = puncher->getDescription();
1136
1137         PlayerSAO *playersao = m_player->getPlayerSAO();
1138
1139         bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1140                                 puncher, time_from_last_punch, toolcap, dir,
1141                                 hitparams.hp);
1142
1143         if (!damage_handled) {
1144                 setHP(getHP() - hitparams.hp);
1145         } else { // override client prediction
1146                 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1147                         std::string str = gob_cmd_punched(0, getHP());
1148                         // create message and add to list
1149                         ActiveObjectMessage aom(getId(), true, str);
1150                         m_messages_out.push(aom);
1151                 }
1152         }
1153
1154
1155         actionstream << "Player " << m_player->getName() << " punched by "
1156                         << punchername;
1157         if (!damage_handled) {
1158                 actionstream << ", damage " << hitparams.hp << " HP";
1159         } else {
1160                 actionstream << ", damage handled by lua";
1161         }
1162         actionstream << std::endl;
1163
1164         return hitparams.wear;
1165 }
1166
1167 void PlayerSAO::rightClick(ServerActiveObject *)
1168 {
1169 }
1170
1171 s16 PlayerSAO::readDamage()
1172 {
1173         s16 damage = m_damage;
1174         m_damage = 0;
1175         return damage;
1176 }
1177
1178 void PlayerSAO::setHP(s16 hp)
1179 {
1180         s16 oldhp = m_hp;
1181
1182         s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1183         if (hp_change == 0)
1184                 return;
1185         hp = oldhp + hp_change;
1186
1187         if (hp < 0)
1188                 hp = 0;
1189         else if (hp > PLAYER_MAX_HP)
1190                 hp = PLAYER_MAX_HP;
1191
1192         if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1193                 return;
1194         }
1195
1196         m_hp = hp;
1197
1198         if (oldhp > hp)
1199                 m_damage += (oldhp - hp);
1200
1201         // Update properties on death
1202         if ((hp == 0) != (oldhp == 0))
1203                 m_properties_sent = false;
1204 }
1205
1206 void PlayerSAO::setBreath(const u16 breath)
1207 {
1208         if (m_player && breath != m_breath)
1209                 m_player->setDirty(true);
1210
1211         m_breath = breath;
1212 }
1213
1214 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1215 {
1216         m_armor_groups = armor_groups;
1217         m_armor_groups_sent = false;
1218 }
1219
1220 ItemGroupList PlayerSAO::getArmorGroups()
1221 {
1222         return m_armor_groups;
1223 }
1224
1225 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
1226 {
1227         // store these so they can be updated to clients
1228         m_animation_range = frame_range;
1229         m_animation_speed = frame_speed;
1230         m_animation_blend = frame_blend;
1231         m_animation_loop = frame_loop;
1232         m_animation_sent = false;
1233 }
1234
1235 void PlayerSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
1236 {
1237         *frame_range = m_animation_range;
1238         *frame_speed = m_animation_speed;
1239         *frame_blend = m_animation_blend;
1240         *frame_loop = m_animation_loop;
1241 }
1242
1243 void PlayerSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
1244 {
1245         // store these so they can be updated to clients
1246         m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1247         m_bone_position_sent = false;
1248 }
1249
1250 void PlayerSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
1251 {
1252         *position = m_bone_position[bone].X;
1253         *rotation = m_bone_position[bone].Y;
1254 }
1255
1256 void PlayerSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
1257 {
1258         // Attachments need to be handled on both the server and client.
1259         // If we just attach on the server, we can only copy the position of the parent. Attachments
1260         // are still sent to clients at an interval so players might see them lagging, plus we can't
1261         // read and attach to skeletal bones.
1262         // If we just attach on the client, the server still sees the child at its original location.
1263         // This breaks some things so we also give the server the most accurate representation
1264         // even if players only see the client changes.
1265
1266         m_attachment_parent_id = parent_id;
1267         m_attachment_bone = bone;
1268         m_attachment_position = position;
1269         m_attachment_rotation = rotation;
1270         m_attachment_sent = false;
1271 }
1272
1273 void PlayerSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
1274         v3f *rotation)
1275 {
1276         *parent_id = m_attachment_parent_id;
1277         *bone = m_attachment_bone;
1278         *position = m_attachment_position;
1279         *rotation = m_attachment_rotation;
1280 }
1281
1282 void PlayerSAO::addAttachmentChild(int child_id)
1283 {
1284         m_attachment_child_ids.insert(child_id);
1285 }
1286
1287 void PlayerSAO::removeAttachmentChild(int child_id)
1288 {
1289         m_attachment_child_ids.erase(child_id);
1290 }
1291
1292 UNORDERED_SET<int> PlayerSAO::getAttachmentChildIds()
1293 {
1294         return m_attachment_child_ids;
1295 }
1296
1297 ObjectProperties* PlayerSAO::accessObjectProperties()
1298 {
1299         return &m_prop;
1300 }
1301
1302 void PlayerSAO::notifyObjectPropertiesModified()
1303 {
1304         m_properties_sent = false;
1305 }
1306
1307 Inventory* PlayerSAO::getInventory()
1308 {
1309         return m_inventory;
1310 }
1311 const Inventory* PlayerSAO::getInventory() const
1312 {
1313         return m_inventory;
1314 }
1315
1316 InventoryLocation PlayerSAO::getInventoryLocation() const
1317 {
1318         InventoryLocation loc;
1319         loc.setPlayer(m_player->getName());
1320         return loc;
1321 }
1322
1323 std::string PlayerSAO::getWieldList() const
1324 {
1325         return "main";
1326 }
1327
1328 int PlayerSAO::getWieldIndex() const
1329 {
1330         return m_wield_index;
1331 }
1332
1333 void PlayerSAO::setWieldIndex(int i)
1334 {
1335         if(i != m_wield_index) {
1336                 m_wield_index = i;
1337         }
1338 }
1339
1340 // Erase the peer id and make the object for removal
1341 void PlayerSAO::disconnected()
1342 {
1343         m_peer_id = 0;
1344         m_removed = true;
1345 }
1346
1347 void PlayerSAO::unlinkPlayerSessionAndSave()
1348 {
1349         assert(m_player->getPlayerSAO() == this);
1350         m_player->peer_id = 0;
1351         m_env->savePlayer(m_player);
1352         m_player->setPlayerSAO(NULL);
1353         m_env->removePlayer(m_player);
1354 }
1355
1356 std::string PlayerSAO::getPropertyPacket()
1357 {
1358         m_prop.is_visible = (true);
1359         return gob_cmd_set_properties(m_prop);
1360 }
1361
1362 bool PlayerSAO::checkMovementCheat()
1363 {
1364         if (isAttached() || m_is_singleplayer ||
1365                         g_settings->getBool("disable_anticheat")) {
1366                 m_last_good_position = m_base_position;
1367                 return false;
1368         }
1369
1370         bool cheated = false;
1371         /*
1372                 Check player movements
1373
1374                 NOTE: Actually the server should handle player physics like the
1375                 client does and compare player's position to what is calculated
1376                 on our side. This is required when eg. players fly due to an
1377                 explosion. Altough a node-based alternative might be possible
1378                 too, and much more lightweight.
1379         */
1380
1381         float player_max_speed = 0;
1382
1383         if (m_privs.count("fast") != 0) {
1384                 // Fast speed
1385                 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1386         } else {
1387                 // Normal speed
1388                 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1389         }
1390         // Tolerance. The lag pool does this a bit.
1391         //player_max_speed *= 2.5;
1392
1393         v3f diff = (m_base_position - m_last_good_position);
1394         float d_vert = diff.Y;
1395         diff.Y = 0;
1396         float d_horiz = diff.getLength();
1397         float required_time = d_horiz / player_max_speed;
1398
1399         if (d_vert > 0 && d_vert / player_max_speed > required_time)
1400                 required_time = d_vert / player_max_speed; // Moving upwards
1401
1402         if (m_move_pool.grab(required_time)) {
1403                 m_last_good_position = m_base_position;
1404         } else {
1405                 actionstream << "Player " << m_player->getName()
1406                                 << " moved too fast; resetting position"
1407                                 << std::endl;
1408                 setBasePosition(m_last_good_position);
1409                 cheated = true;
1410         }
1411         return cheated;
1412 }
1413
1414 bool PlayerSAO::getCollisionBox(aabb3f *toset)
1415 {
1416         *toset = aabb3f(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30);
1417         toset->MinEdge += m_base_position;
1418         toset->MaxEdge += m_base_position;
1419         return true;
1420 }
1421
1422 bool PlayerSAO::collideWithObjects()
1423 {
1424         return true;
1425 }