1 #include "PhysicsComponent.h"
2 #include "../../Physics/ClawPhysics.h"
3 #include "../../GameApp/BaseGameLogic.h"
4 #include "../../GameApp/BaseGameApp.h"
5 #include "RenderComponent.h"
6 #include "../../Graphics2D/Image.h"
7 
8 #include "PositionComponent.h"
9 #include "ControllableComponent.h"
10 #include "ControllerComponents/PowerupComponent.h"
11 
12 #include "../../Events/EventMgr.h"
13 #include "../../Events/Events.h"
14 
15 const char* PhysicsComponent::g_Name = "PhysicsComponent";
16 
PhysicsComponent()17 PhysicsComponent::PhysicsComponent() :
18     m_CanClimb(false),
19     m_CanBounce(false),
20     m_CanJump(false),
21     m_MaxJumpHeight(0),
22     m_GravityScale(0),
23     m_Friction(0),
24     m_Density(0),
25     m_BodySize(Point(0, 0)),
26     m_CurrentSpeed(Point(0, 0)),
27     m_ConstantSpeed(Point(0, 0)),
28     m_ClimbingSpeed(Point(0, 0)),
29     m_HasConstantSpeed(false),
30     m_NumFootContacts(0),
31     m_IsClimbing(false),
32     m_IsStopped(false),
33     m_IsRunning(false),
34     m_Direction(Direction_Right),
35     m_IsFalling(false),
36     m_IsJumping(false),
37     m_IgnoreJump(false),
38     m_HeightInAir(20),
39     m_pControllableComponent(nullptr),
40     m_pPhysics(nullptr),
41     m_pTopLadderContact(NULL),
42     m_pMovingPlatformContact(NULL),
43     m_bClampToGround(false),
44     m_DoNothingTimeout(0),
45     m_bIsForcedUp(false),
46     m_ForcedUpHeight(0),
47     m_FallHeight(0.0f)
48 { }
49 
~PhysicsComponent()50 PhysicsComponent::~PhysicsComponent()
51 {
52     m_pPhysics->VRemoveActor(m_pOwner->GetGUID());
53 }
54 
VInit(TiXmlElement * data)55 bool PhysicsComponent::VInit(TiXmlElement* data)
56 {
57     assert(data != NULL);
58 
59     m_pPhysics = g_pApp->GetGameLogic()->VGetGamePhysics();
60     if (!m_pPhysics)
61     {
62         LOG_WARNING("Attemtping to create physics component without valid physics");
63         return false;
64     }
65 
66     if (TiXmlElement* pElem = data->FirstChildElement("CanClimb"))
67     {
68         m_CanClimb = std::string(pElem->GetText()) == "true";
69     }
70     if (TiXmlElement* pElem = data->FirstChildElement("CanBounce"))
71     {
72         m_CanBounce = std::string(pElem->GetText()) == "true";
73     }
74     if (TiXmlElement* pElem = data->FirstChildElement("CanJump"))
75     {
76         m_CanJump = std::string(pElem->GetText()) == "true";
77     }
78     if (TiXmlElement* pElem = data->FirstChildElement("JumpHeight"))
79     {
80         m_MaxJumpHeight = std::stoi(pElem->GetText());
81     }
82     if (TiXmlElement* pElem = data->FirstChildElement("GravityScale"))
83     {
84         m_ActorBodyDef.gravityScale = std::stof(pElem->GetText());
85 
86         // Backwards compatibility, can be removed in future
87         m_GravityScale = std::stof(pElem->GetText());
88     }
89 
90 
91     TiXmlElement* pBodySizeElem = data->FirstChildElement("CollisionSize");
92     if (pBodySizeElem)
93     {
94         pBodySizeElem->Attribute("width", &m_ActorBodyDef.size.x);
95         pBodySizeElem->Attribute("height", &m_ActorBodyDef.size.y);
96 
97         // Backwards compatibility, can be removed in future
98         pBodySizeElem->Attribute("width", &m_BodySize.x);
99         pBodySizeElem->Attribute("height", &m_BodySize.y);
100     }
101 
102     // ActorBodyDef addition
103 
104     // Allowed types are: "Static", "Kinematic" and "Dynamic"
105     if (TiXmlElement* pElem = data->FirstChildElement("BodyType"))
106     {
107         m_ActorBodyDef.bodyType = BodyTypeStringToEnum(pElem->GetText());
108     }
109     if (TiXmlElement* pElem = data->FirstChildElement("HasFootSensor"))
110     {
111         m_ActorBodyDef.addFootSensor = std::string(pElem->GetText()) == "true";
112     }
113     if (TiXmlElement* pElem = data->FirstChildElement("HasCapsuleShape"))
114     {
115         m_ActorBodyDef.makeCapsule = std::string(pElem->GetText()) == "true";
116     }
117     if (TiXmlElement* pElem = data->FirstChildElement("HasBulletBehaviour"))
118     {
119         m_ActorBodyDef.makeBullet = std::string(pElem->GetText()) == "true";
120     }
121     if (TiXmlElement* pElem = data->FirstChildElement("HasSensorBehaviour"))
122     {
123         m_ActorBodyDef.makeSensor = std::string(pElem->GetText()) == "true";
124     }
125     // Allowed types are: "Solid", "Ground", "Climb", "Death", "Trigger", "Projectile"
126     if (TiXmlElement* pElem = data->FirstChildElement("FixtureType"))
127     {
128         m_ActorBodyDef.fixtureType = FixtureTypeStringToEnum(pElem->GetText());
129     }
130     if (TiXmlElement* pElem = data->FirstChildElement("PositionOffset"))
131     {
132         pElem->Attribute("x", &m_ActorBodyDef.positionOffset.x);
133         pElem->Attribute("y", &m_ActorBodyDef.positionOffset.y);
134     }
135     if (TiXmlElement* pElem = data->FirstChildElement("CollisionShape"))
136     {
137         m_ActorBodyDef.collisionShape = pElem->GetText();
138     }
139     if (TiXmlElement* pElem = data->FirstChildElement("HasInitialSpeed"))
140     {
141         m_ActorBodyDef.setInitialSpeed = std::string(pElem->GetText()) == "true";
142     }
143     if (TiXmlElement* pElem = data->FirstChildElement("HasInitialImpulse"))
144     {
145         m_ActorBodyDef.setInitialImpulse = std::string(pElem->GetText()) == "true";
146     }
147     if (TiXmlElement* pElem = data->FirstChildElement("InitialSpeed"))
148     {
149         pElem->Attribute("x", &m_ActorBodyDef.initialSpeed.x);
150         pElem->Attribute("y", &m_ActorBodyDef.initialSpeed.y);
151     }
152     if (TiXmlElement* pElem = data->FirstChildElement("CollisionFlag"))
153     {
154         m_ActorBodyDef.collisionFlag = CollisionFlag(std::stoi(pElem->GetText()));
155     }
156     if (TiXmlElement* pElem = data->FirstChildElement("CollisionMask"))
157     {
158         m_ActorBodyDef.collisionMask = std::stoul(pElem->GetText());
159     }
160     if (TiXmlElement* pElem = data->FirstChildElement("Friction"))
161     {
162         m_ActorBodyDef.friction = std::stof(pElem->GetText());
163     }
164     if (TiXmlElement* pElem = data->FirstChildElement("Density"))
165     {
166         m_ActorBodyDef.density = std::stof(pElem->GetText());
167     }
168     if (TiXmlElement* pElem = data->FirstChildElement("Restitution"))
169     {
170         m_ActorBodyDef.restitution = std::stof(pElem->GetText());
171     }
172     if (TiXmlElement* pElem = data->FirstChildElement("PrefabType"))
173     {
174         //m_ActorBodyDef.prefabType = pElem->GetText();
175     }
176 
177     for (TiXmlElement* pFixtureElem = data->FirstChildElement("ActorFixture");
178         pFixtureElem != NULL; pFixtureElem = pFixtureElem->NextSiblingElement("ActorFixture"))
179     {
180         ActorFixtureDef fixtureDef = ActorTemplates::XmlToActorFixtureDef(pFixtureElem);
181 
182         m_ActorBodyDef.fixtureList.push_back(fixtureDef);
183     }
184 
185     ParseValueFromXmlElem(&m_bClampToGround, data->FirstChildElement("ClampToGround"));
186 
187     return true;
188 }
189 
VPostInit()190 void PhysicsComponent::VPostInit()
191 {
192     shared_ptr<PositionComponent> pPositionComponent =
193         MakeStrongPtr(m_pOwner->GetComponent<PositionComponent>(PositionComponent::g_Name));
194     assert(pPositionComponent);
195 
196     if (m_ActorBodyDef.collisionFlag != CollisionFlag_None)
197     {
198         m_ActorBodyDef.position = pPositionComponent->GetPosition();
199 
200         if (fabs(m_ActorBodyDef.size.x) < DBL_EPSILON || fabs(m_ActorBodyDef.size.y) < DBL_EPSILON)
201         {
202             shared_ptr<ActorRenderComponent> pRenderComponent =
203                 MakeStrongPtr(m_pOwner->GetComponent<ActorRenderComponent>(ActorRenderComponent::g_Name));
204             assert(pRenderComponent);
205 
206             shared_ptr<Image> pImage = MakeStrongPtr(pRenderComponent->GetCurrentImage());
207             assert(pImage != nullptr);
208 
209             // Also offset position
210             m_ActorBodyDef.position = Point(m_ActorBodyDef.position.x + pImage->GetOffsetX(), m_ActorBodyDef.position.y + pImage->GetOffsetY());
211 
212             m_ActorBodyDef.size.x = pImage->GetWidth();
213             m_ActorBodyDef.size.y = pImage->GetHeight();
214 
215             //LOG("-------- X: " + ToStr(pImage->GetOffsetX()) + " Y: " + ToStr(pImage->GetOffsetY()));
216 
217             for (ActorFixtureDef fixture : m_ActorBodyDef.fixtureList)
218             {
219                 if (fixture.size.IsZero())
220                 {
221                     fixture.size = Point(pImage->GetWidth(), pImage->GetHeight());
222                 }
223             }
224 
225             // HACK:
226             if (m_pOwner->GetName() == "/LEVEL1/IMAGES/RATBOMB/*")
227             {
228                 pImage->SetOffset(0, 0);
229             }
230         }
231 
232         m_ActorBodyDef.position += m_ActorBodyDef.positionOffset;
233         m_ActorBodyDef.pActor = m_pOwner;
234 
235         m_pPhysics->VAddActorBody(&m_ActorBodyDef);
236     }
237     else
238     {
239         m_pPhysics->VAddDynamicActor(m_pOwner);
240     }
241 }
242 
VPostPostInit()243 void PhysicsComponent::VPostPostInit()
244 {
245     if (m_bClampToGround)
246     {
247         auto pPositionComponent = m_pOwner->GetPositionComponent();
248         // Position enemy to the floor
249         Point fromPoint = pPositionComponent->GetPosition();
250         Point toPoint = pPositionComponent->GetPosition() + Point(0, 100);
251         RaycastResult raycastDown = m_pPhysics->VRayCast(fromPoint, toPoint, (CollisionFlag_Solid | CollisionFlag_Ground | CollisionFlag_All));
252         if (!raycastDown.foundIntersection)
253         {
254             LOG_ERROR("Failed to get raycast result down from position: " + pPositionComponent->GetPosition().ToString());
255             return;
256             //assert(raycastDown.foundIntersection && "Did not find intersection. Enemy is too far in the air with no ground below him");
257         }
258 
259         double deltaY = raycastDown.deltaY - m_pPhysics->VGetAABB(m_pOwner->GetGUID(), true).h / 2;
260 
261         pPositionComponent->SetY(pPositionComponent->GetY() + deltaY - 1);
262         m_pPhysics->VSetPosition(m_pOwner->GetGUID(), pPositionComponent->GetPosition());
263     }
264 }
265 
VGenerateXml()266 TiXmlElement* PhysicsComponent::VGenerateXml()
267 {
268     TiXmlElement* baseElement = new TiXmlElement(VGetName());
269 
270     //
271 
272     return baseElement;
273 };
274 
275 //-----------------------------------------------------------------------------
276 // PhysicsComponent::VUpdate
277 //
278 //    Used to control movement of:
279 //      - Controlled actor (Claw)
280 //
281 // TODO: HACK: (so I can easily search hacks and stuff): Note that this piece of code is absolutely terrible
282 // since I dont know how to properly implement character control system. I really am aware that this
283 // horrible state machine is pretty far from being acceptable. Given time this should be refactored.
284 //
VUpdate(uint32 msDiff)285 void PhysicsComponent::VUpdate(uint32 msDiff)
286 {
287     // Just move this to ClawControllerComponent update......................
288     if (!m_pControllableComponent)
289     {
290         return;
291     }
292 
293     assert(m_NumFootContacts >= 0);
294     //assert(m_IsJumping && m_IsFalling && "Cannot be jumping and falling at the same time");
295 
296     /*LOG("Jumping: " + ToStr((m_IsJumping)) + ", Falling: " + ToStr(m_IsFalling) + ", JumpHeight: " + ToStr(m_HeightInAir) + ", NumFootContacts: " + ToStr(m_NumFootContacts));
297     LOG("Movement: " + m_CurrentSpeed.ToString());
298     LOG(ToStr(m_OverlappingLaddersList.size()));
299     LOG("Vel X: " + ToStr(GetVelocity().x) + ", Vel Y: " + ToStr(GetVelocity().y));
300     LOG(ToStr(m_OverlappingKinematicBodiesList.size()));
301 
302     LOG("CLIMBING Y: " + ToStr(m_ClimbingSpeed.y));*/
303 
304     //LOG("ROPE_1: " + ToStr(m_pControllableComponent->VIsAttachedToRope()));
305 
306     m_DoNothingTimeout -= msDiff;
307     if (m_DoNothingTimeout > 0)
308     {
309         SetVelocity(Point(0, 0));
310         m_pPhysics->VSetGravityScale(m_pOwner->GetGUID(), 0.0f);
311         m_CurrentSpeed.Set(0, 0);
312         return;
313     }
314 
315     if (m_pControllableComponent->VIsAttachedToRope())
316     {
317         m_CurrentSpeed.SetX(0);
318         if (m_ClimbingSpeed.y > 0)
319         {
320             m_ClimbingSpeed.y = 0;
321         }
322     }
323 
324     if (!m_IsJumping)
325     {
326         m_MaxJumpHeight = m_pControllableComponent->GetMaxJumpHeight();
327     }
328 
329     if (m_bIsForcedUp)
330     {
331         if (m_IsFalling || m_pControllableComponent->VIsAttachedToRope() || m_pControllableComponent->IsClimbing())
332         {
333             m_bIsForcedUp = false;
334             m_ForcedUpHeight = 0;
335             m_MaxJumpHeight = (int)g_pApp->GetGlobalOptions()->maxJumpHeight;
336         }
337         else
338         {
339             m_MaxJumpHeight = m_ForcedUpHeight;
340             m_CurrentSpeed.y = -10;
341         }
342     }
343 
344 
345     if (m_OverlappingKinematicBodiesList.empty())
346     {
347         m_ExternalSourceSpeed.Set(0, 0);
348     }
349 
350     if (m_pControllableComponent && !m_pControllableComponent->InPhysicsCapableState())
351     {
352         SetVelocity(Point(0, 0));
353         m_CurrentSpeed = Point(0, 0);
354         m_ClimbingSpeed = Point(0, 0);
355         m_IsClimbing = false;
356         // When Claw takes damage while ducking he stands up.. TODO: Think of better solution
357         m_pPhysics->VScaleActor(m_pOwner->GetGUID(), 2.0);
358 
359         m_pControllableComponent->SetDuckingTime(0);
360         m_pControllableComponent->SetLookingUpTime(0);
361 
362         return;
363     }
364 
365     if (m_IsClimbing)
366     {
367         m_HeightInAir = 0;
368     }
369 
370     if (m_pControllableComponent && m_pControllableComponent->CanMove()
371         && (m_pControllableComponent->IsDucking() && fabs(m_ClimbingSpeed.y) < DBL_EPSILON)
372         && IsOnGround())
373     {
374         m_pControllableComponent->OnStand();
375         // TODO: HACK: one of the biggest hacks so far
376         m_pPhysics->VScaleActor(m_pOwner->GetGUID(), 2.0);
377     }
378 
379     if (m_pControllableComponent &&
380         (!m_pControllableComponent->CanMove() ||
381         (m_pControllableComponent->IsDucking() && m_ClimbingSpeed.y > DBL_EPSILON)))
382     {
383         if (fabs(m_CurrentSpeed.x) > DBL_EPSILON)
384         {
385             Direction direction = m_CurrentSpeed.x < 0 ? Direction_Left : Direction_Right;
386             //m_pControllableComponent->VOnDirectionChange(m_Direction);
387 
388             SetDirection(direction);
389         }
390 
391         //LOG("RETURN 1");
392         Point currSpeed = GetVelocity();
393         SetVelocity(Point(0, currSpeed.y));
394         if (m_pMovingPlatformContact)
395         {
396             m_pMovingPlatformContact->SetFriction(100.0f);
397         }
398 
399         if (m_ClimbingSpeed.y > DBL_EPSILON)
400         {
401             m_pControllableComponent->AddDuckingTime(msDiff);
402             m_pControllableComponent->SetLookingUpTime(0);
403         }
404         else if (m_ClimbingSpeed.y < (-1.0 * DBL_EPSILON))
405         {
406             m_pControllableComponent->AddLookingUpTime(msDiff);
407             m_pControllableComponent->SetDuckingTime(0);
408         }
409         else
410         {
411             m_pControllableComponent->SetLookingUpTime(0);
412             m_pControllableComponent->SetDuckingTime(0);
413         }
414 
415         m_CurrentSpeed = Point(0, 0);
416         m_ClimbingSpeed = Point(0, 0);
417 
418         return;
419     }
420 
421     // I will give 100$ to someone who will refactor this method so that it is extendable
422     // and does not contain uncomprehandable state machine
423 
424     //LOG("Climbing: " + ToStr(m_IsClimbing));
425     if (m_IsClimbing)
426     {
427         // TODO: Climbing up when on top of the ladder should be disabled
428         /*if (m_ClimbingSpeed.y < (-1.0 * DBL_EPSILON) && m_pTopLadderContact && m_OverlappingLaddersList.size() > 0)
429         {
430             LOG(".");
431             m_ClimbingSpeed.Set(0, 0);
432             m_CurrentSpeed.Set(0, 0);
433             m_IgnoreJump = false;
434             m_IsClimbing = false;
435             m_IsJumping = false;
436             m_IsFalling = false;
437             m_HeightInAir = 0;
438             m_pPhysics->VSetGravityScale(m_pOwner->GetGUID(), m_GravityScale);
439             //m_pControllableComponent->VOnStopMoving();
440             m_pPhysics->VSetLinearSpeedEx(m_pOwner->GetGUID(), Point(0,0));
441             return;
442         }*/
443 
444         if (fabs(m_ClimbingSpeed.y) > DBL_EPSILON)
445         {
446             m_CurrentSpeed = Point(0, 0);
447             m_IgnoreJump = true;
448         }
449         else
450         {
451             m_IgnoreJump = false;
452         }
453 
454         //LOG("IgnoreJump: " + ToStr(m_IgnoreJump));
455         //if (m_IgnoreJump && m_CanJump)
456         if (!m_IgnoreJump && (fabs(m_CurrentSpeed.y) > DBL_EPSILON) &&
457             fabs(m_ClimbingSpeed.y) < DBL_EPSILON)
458         {
459             //LOG("GOTO");
460             //LOG("1");
461             m_pControllableComponent->VOnStartJumping();
462             m_IgnoreJump = false;
463             m_IsClimbing = false;
464             m_IsJumping = true;
465             m_IsFalling = false;
466             m_HeightInAir = 0;
467             m_pPhysics->VSetGravityScale(m_pOwner->GetGUID(), m_GravityScale);
468             goto set_velocity;
469         }
470         else if (!m_IgnoreJump && (fabs(m_CurrentSpeed.y) > DBL_EPSILON))
471         {
472             //LOG(".");
473             //LOG("2");
474             m_IsClimbing = false;
475             m_IsJumping = true;
476             m_IsFalling = false;
477             m_HeightInAir = 0;
478             m_pPhysics->VSetGravityScale(m_pOwner->GetGUID(), m_GravityScale);
479         }
480         if (m_OverlappingLaddersList.empty())
481         {
482             m_IsClimbing = false;
483             m_pPhysics->VSetGravityScale(m_pOwner->GetGUID(), m_GravityScale);
484         }
485         else
486         {
487             //LOG("ClimbingSpeed: " + ToStr(m_ClimbingSpeed.y));
488             m_pPhysics->VSetLinearSpeedEx(m_pOwner->GetGUID(), Point(0, m_ClimbingSpeed.y));
489             if (m_pControllableComponent && fabs(m_ClimbingSpeed.y) < DBL_EPSILON)
490             {
491                 m_pControllableComponent->VOnStopClimbing();
492             }
493             else
494             {
495                 // If on top of the ladder climb through the artificial ground patch
496                 if (m_pTopLadderContact != NULL)
497                 {
498                     m_pTopLadderContact->SetEnabled(false);
499                 }
500 
501                 bool bIsOnTopLadder = CheckOverlap(FixtureType_TopLadderGround);
502                 bool bIsClimbingUp = m_ClimbingSpeed.y < (-1.0 * DBL_EPSILON);
503                 m_pControllableComponent->VOnClimb(bIsClimbingUp, bIsOnTopLadder);
504             }
505             m_ClimbingSpeed = Point(0, 0);
506 
507             m_pControllableComponent->SetDuckingTime(0);
508             m_pControllableComponent->SetLookingUpTime(0);
509 
510             return;
511         }
512     }
513 
514     if (m_pControllableComponent)
515     {
516         if (!m_IsClimbing && !IsInAir() && m_ClimbingSpeed.y > DBL_EPSILON &&
517             (GetVelocity().y < 0.1 || IsOnGround()))
518         {
519             m_pControllableComponent->OnDuck();
520             // TODO: HACK: one of the biggest hacks so far
521             m_pPhysics->VScaleActor(m_pOwner->GetGUID(), 0.5);
522 
523             if (fabs(m_CurrentSpeed.x) > DBL_EPSILON)
524             {
525                 Direction direction = m_CurrentSpeed.x < 0 ? Direction_Left : Direction_Right;
526                 //m_pControllableComponent->VOnDirectionChange(m_Direction);
527 
528                 SetDirection(direction);
529             }
530 
531             SetVelocity(Point(0, 0));
532             m_CurrentSpeed = Point(0, 0);
533             m_ClimbingSpeed = Point(0, 0);
534             return;
535         }
536         else if (m_pControllableComponent->IsDucking())
537         {
538             m_pControllableComponent->OnStand();
539         }
540 
541         if (!m_pControllableComponent->IsDucking())
542         {
543             // TODO: HACK: one of the biggest hacks so far
544             m_pPhysics->VScaleActor(m_pOwner->GetGUID(), 2.0);
545         }
546     }
547 
548     if (!m_IsClimbing && !IsInAir() && m_ClimbingSpeed.y > 0)
549     {
550 
551     }
552 
553     /*if (m_OverlappingLaddersList.size() > 1)
554         LOG(ToStr(m_OverlappingLaddersList.size()));*/
555     // This should be available only to controlled actors
556     if (m_CanJump)
557     {
558         //LOG("Pre CurrentSpeed: " + m_CurrentSpeed.ToString());
559 
560         //LOG(ToStr(GetVelocity().y));
561         // This is to ensure one jump per one space press
562         if (m_IsClimbing)
563         {
564 
565         }
566         // "20" lets us skip uneven ground and therefore skip spamming transition between falling/jumping
567         else if (m_HeightInAir > 20 && fabs(GetVelocity().y) < FLT_EPSILON && !m_pControllableComponent->VIsAttachedToRope())
568         {
569             m_IgnoreJump = true;
570             m_CurrentSpeed.y = 0;
571         }
572         else if (fabs(m_CurrentSpeed.y) < DBL_EPSILON && (GetVelocity().y < FLT_EPSILON) && IsInAir())
573         {
574             m_IgnoreJump = true;
575             m_CurrentSpeed.y = 0;
576         }
577         else if (m_IgnoreJump &&
578                 (fabs(m_CurrentSpeed.y) < DBL_EPSILON) &&
579                 (m_NumFootContacts > 0 || m_pControllableComponent->VIsAttachedToRope()))
580         {
581             m_IgnoreJump = false;
582         }
583         else if (m_IgnoreJump)
584         {
585             m_CurrentSpeed.y = 0;
586         }
587         else if (m_IsFalling && m_NumFootContacts == 0)
588         {
589             m_IgnoreJump = true;
590             m_CurrentSpeed.y = 0;
591         }
592         if (!g_pApp->GetGameCheats()->clawInfiniteJump && (m_HeightInAir > m_MaxJumpHeight))
593         {
594             m_IgnoreJump = true;
595             m_CurrentSpeed.y = 0;
596         }
597 
598         //LOG("Post CurrentSpeed: " + m_CurrentSpeed.ToString());
599 
600 set_velocity:
601 
602         //=====================================================================
603         // Set velocity here
604         //=====================================================================
605         Point velocity = GetVelocity();
606 
607         double ySpeed = m_CurrentSpeed.y;
608         if (m_pOwner->GetName() == "Claw")
609         {
610             //LOG("CurrentSpeed.y: " + ToStr(m_CurrentSpeed.y));
611         }
612         if (ySpeed < 0)
613         {
614             SetVelocity(Point(velocity.x, -8.8));
615 
616         }
617         else if (velocity.y < -2 && ySpeed >= 0)
618         {
619             SetVelocity(Point(velocity.x, -2));
620         }
621         velocity = GetVelocity();
622 
623         if (fabs(m_CurrentSpeed.x) > DBL_EPSILON)
624         {
625             double runSpeed = g_pApp->GetGlobalOptions()->runSpeed;
626             if (auto pPowerupComp = MakeStrongPtr(m_pOwner->GetComponent<PowerupComponent>()))
627             {
628                 if (pPowerupComp->HasPowerup(PowerupType_Catnip))
629                 {
630                     runSpeed = g_pApp->GetGlobalOptions()->powerupRunSpeed;
631                 }
632             }
633             SetVelocity(Point(m_CurrentSpeed.x < 0 ? -1.0 * runSpeed : runSpeed, velocity.y));
634         }
635         else
636         {
637             SetVelocity(Point(0, velocity.y));
638         }
639 
640         if (m_pMovingPlatformContact && fabs(m_CurrentSpeed.x) < DBL_EPSILON)
641         {
642             // Not moving while getting carried, set high friction
643             m_pMovingPlatformContact->SetFriction(100.0f);
644         }
645         else if (m_pMovingPlatformContact && (fabs(m_CurrentSpeed.x) > DBL_EPSILON))
646         {
647             // Moving while on platform
648             m_pMovingPlatformContact->SetFriction(0.0f);
649             Point externalSourceSpeedX(m_ExternalSourceSpeed.x, 0);
650             m_pPhysics->VAddLinearSpeed(m_pOwner->GetGUID(), externalSourceSpeedX);
651 
652             // If moving on platform, be slower
653             velocity = GetVelocity();
654             velocity.Set((velocity.x * 2) / 3, velocity.y);
655             SetVelocity(velocity);
656         }
657         else if (fabs(m_ExternalConveyorBeltSpeed.x) > DBL_EPSILON)
658         {
659             m_pPhysics->VAddLinearSpeed(m_pOwner->GetGUID(), m_ExternalConveyorBeltSpeed);
660             m_ExternalConveyorBeltSpeed.Set(0.0, 0.0);
661         }
662 
663         bool applyForce = true;
664         // TODO: Add config to choose between fixed physics timestep and variable
665         /*if (true)
666         {
667             if (m_CurrentSpeed.y > -2)
668             {
669                 ApplyForce(m_pPhysics->GetGravity());
670             }
671             //ApplyForce(m_pPhysics->GetGravity());
672         }
673         else
674         {
675             static uint32 timeSinceLastUpdate = 0;
676             const uint32 updateInterval = 1000 / 120;
677 
678             timeSinceLastUpdate += msDiff;
679             if (timeSinceLastUpdate >= updateInterval)
680             {
681                 ApplyForce(m_pPhysics->GetGravity());
682 
683                 timeSinceLastUpdate = 0;
684             }
685         }*/
686 
687         if (m_pControllableComponent->VIsAttachedToRope())
688         {
689             applyForce = false;
690         }
691 
692         bool disableGravity = false;
693         Point gravity = m_pPhysics->GetGravity();
694         velocity = GetVelocity();
695 
696         double maxJumpSpeed = -1.0 * fabs(g_pApp->GetGlobalOptions()->maxJumpSpeed);
697         double maxFallSpeed = fabs(g_pApp->GetGlobalOptions()->maxFallSpeed);
698 
699         if (velocity.y < maxJumpSpeed)
700         {
701             SetVelocity(Point(velocity.x, maxJumpSpeed));
702             applyForce = false;
703         }
704         if (velocity.y > maxFallSpeed)
705         {
706             //LOG("Velocity: " + ToStr(velocity.y));
707             SetVelocity(Point(velocity.x, maxFallSpeed));
708             applyForce = false;
709         }
710         if (applyForce)
711         {
712             m_pPhysics->VSetGravityScale(m_pOwner->GetGUID(), m_GravityScale);
713             //ApplyForce(m_pPhysics->GetGravity());
714         }
715         else
716         {
717             m_pPhysics->VSetGravityScale(m_pOwner->GetGUID(), 0.0f);
718         }
719 
720         if (m_bIsForcedUp)
721         {
722             velocity = GetVelocity();
723 
724             double springSpeed = -1.0 * fabs(g_pApp->GetGlobalOptions()->springBoardSpringSpeed);
725             SetVelocity(Point(velocity.x, springSpeed));
726         }
727 
728         /*if (true && applyForce)
729         {
730             if (m_CurrentSpeed.y > -2)
731             {
732                 ApplyForce(m_pPhysics->GetGravity());
733             }
734             //ApplyForce(m_pPhysics->GetGravity());
735         }*/
736         /*else
737         {
738             static uint32 timeSinceLastUpdate = 0;
739             const uint32 updateInterval = 1000 / 120;
740 
741             timeSinceLastUpdate += msDiff;
742             if (timeSinceLastUpdate >= updateInterval)
743             {
744                 ApplyForce(m_pPhysics->GetGravity());
745 
746                 timeSinceLastUpdate = 0;
747             }
748         }*/
749 
750         //=====================================================================
751 
752         if (m_IsJumping || m_IsFalling)
753         {
754             m_IsRunning = false;
755             m_IsStopped = false;
756         }
757 
758         bool wasRunning = m_IsRunning;
759         bool wasStopped = m_IsStopped;
760         Direction prevDirection = m_Direction;
761         Direction currDirection = m_Direction;
762 
763         velocity = GetVelocity();
764         if (fabs(velocity.x) > DBL_EPSILON)
765         {
766             currDirection = velocity.x < 0 ? Direction_Left : Direction_Right;
767             if (!IsInAir() && IsOnGround())
768             {
769                 m_IsRunning = true;
770                 m_IsStopped = false;
771             }
772         }
773         else
774         {
775             if (!IsInAir() && IsOnGround())
776             {
777                 m_IsStopped = true;
778                 m_IsRunning = false;
779             }
780         }
781 
782         //LOG("Running: " + ToStr(m_IsRunning) + ", Stopped: " + ToStr(m_IsStopped));
783         if (m_pControllableComponent)
784         {
785             if (prevDirection != currDirection)
786             {
787                 SetDirection(currDirection);
788             }
789             if (m_IsRunning && IsOnGround()) // TODO: Dont poll here. State changing didnt work.
790             {
791                 m_pControllableComponent->VOnRun();
792             }
793             else if (m_IsStopped && (((fabs(GetVelocity().y) < DBL_EPSILON) && (fabs(GetVelocity().x) < DBL_EPSILON)) || !m_OverlappingKinematicBodiesList.empty()) && IsOnGround())
794             {
795                 m_pControllableComponent->VOnStopMoving();
796             }
797 
798             //LOG(ToStr(m_pPhysics->VGetVelocity(m_pOwner->GetGUID()).x) + " - " + ToStr(m_pPhysics->VGetVelocity(m_pOwner->GetGUID()).y));
799         }
800 
801     }
802 
803     /*if (m_HasConstantSpeed)
804     {
805         m_pPhysics->VSetLinearSpeed(m_pOwner->GetGUID(), m_ConstantSpeed);
806     }*/
807 
808     if (m_ClimbingSpeed.y < (-1.0 * DBL_EPSILON))
809     {
810         m_pControllableComponent->AddLookingUpTime(msDiff);
811     }
812     else
813     {
814         m_pControllableComponent->SetLookingUpTime(0);
815         m_pControllableComponent->SetDuckingTime(0);
816     }
817 
818     if (m_pControllableComponent->VIsAttachedToRope())
819     {
820         if (!m_IgnoreJump && m_CurrentSpeed.y <= (-1.0 * DBL_EPSILON))
821         {
822             m_pControllableComponent->VDetachFromRope();
823         }
824 
825         if (m_CurrentSpeed.y >= (-1.0 * DBL_EPSILON))
826         {
827             SetVelocity(Point(0, 0));
828             m_pPhysics->VSetGravityScale(m_pOwner->GetGUID(), 0.0f);
829         }
830 
831         //LOG("CurrentSpeed: " + m_CurrentSpeed.ToString());
832     }
833 
834     //LOG("ROPE_2: " + ToStr(m_pControllableComponent->VIsAttachedToRope()));
835 
836     m_CurrentSpeed.Set(0, 0);
837     m_ClimbingSpeed = Point(0, 0);
838 }
839 
840 // Events
OnBeginFootContact()841 void PhysicsComponent::OnBeginFootContact()
842 {
843     if (m_NumFootContacts == 0)
844     {
845         if (m_pControllableComponent && (m_HeightInAir > 2 /*|| !m_OverlappingKinematicBodiesList.empty()*/))
846         {
847             m_pControllableComponent->VOnLandOnGround(m_FallHeight);
848         }
849     }
850 
851     m_NumFootContacts++;
852     //LOG(ToStr(m_HeightInAir));
853     if (!m_bIsForcedUp)
854     {
855         m_HeightInAir = 0;
856     }
857 }
858 
OnEndFootContact()859 void PhysicsComponent::OnEndFootContact()
860 {
861     m_NumFootContacts--;
862 }
863 
OnStartFalling()864 void PhysicsComponent::OnStartFalling()
865 {
866     if (m_DoNothingTimeout > 0 || (m_pControllableComponent && m_pControllableComponent->VIsAttachedToRope()))
867     {
868         return;
869     }
870 
871     if (m_IsClimbing)
872     {
873         return;
874     }
875 
876     if (m_pControllableComponent && !m_pControllableComponent->InPhysicsCapableState())
877     {
878         return;
879     }
880 
881     //LOG("FALL");
882     m_IsFalling = true;
883     if (m_pControllableComponent)
884     {
885         m_pControllableComponent->VOnStartFalling();
886     }
887 }
888 
OnStartJumping()889 void PhysicsComponent::OnStartJumping()
890 {
891     if (m_DoNothingTimeout > 0)
892     {
893         return;
894     }
895 
896     if (m_IsClimbing)
897     {
898         return;
899     }
900 
901     if (m_pControllableComponent && !m_pControllableComponent->InPhysicsCapableState())
902     {
903         return;
904     }
905 
906     //LOG("JUMP");
907     m_IsJumping = true;
908     if (m_pControllableComponent)
909     {
910         m_pControllableComponent->VOnStartJumping();
911     }
912 }
913 
914 // HACK: TODO:
915 // Forcing falling / jumping events to fire only when it is really happening
916 // Also ignoring invisible bumps on tiles (2 pixel tolerancy, probably too much)
SetFalling(bool falling)917 void PhysicsComponent::SetFalling(bool falling)
918 {
919     if (m_DoNothingTimeout > 0 || (m_pControllableComponent && m_pControllableComponent->VIsAttachedToRope()))
920     {
921         return;
922     }
923 
924     if (falling)
925     {
926         if (m_HeightInAir < 5 && (GetVelocity().y < 5))
927         {
928             return;
929         }
930         else
931         {
932             //LOG(ToStr(m_HeightInAir) + " - " + ToStr(GetVelocity().y));
933             m_IsFalling = true;
934         }
935     }
936     else
937     {
938         m_IsFalling = false;
939     }
940 }
941 
SetJumping(bool jumping)942 void PhysicsComponent::SetJumping(bool jumping)
943 {
944     if (m_DoNothingTimeout > 0 || (m_pControllableComponent && m_pControllableComponent->VIsAttachedToRope() && !m_IgnoreJump))
945     {
946         return;
947     }
948 
949     if (jumping)
950     {
951         if (m_HeightInAir < 2)
952         {
953             //LOG(ToStr(m_HeightInAir));
954             //LOG("RETURNING");
955             return;
956         }
957         else
958         {
959             m_IsJumping = true;
960         }
961     }
962     else
963     {
964         m_IsJumping = false;
965     }
966 }
967 
AttachToLadder()968 bool PhysicsComponent::AttachToLadder()
969 {
970     if (!m_CanClimb)
971     {
972         return false;
973     }
974 
975     if (!m_pControllableComponent->InPhysicsCapableState())
976     {
977         return false;
978     }
979 
980     // Unit can attach to ladder but check if it is standing on ground and that the ground
981     // is not the top-side ladder ground patch
982     if (m_ClimbingSpeed.y > DBL_EPSILON)
983     {
984         if (m_pTopLadderContact == NULL && m_NumFootContacts > 0)
985         {
986             return false;
987         }
988     }
989 
990     shared_ptr<PositionComponent> pPositionComponent =
991         MakeStrongPtr(m_pOwner->GetComponent<PositionComponent>(PositionComponent::g_Name));
992     assert(pPositionComponent);
993 
994     Point actorPosition = pPositionComponent->GetPosition();
995     for (const b2Fixture* pLadderFixture : m_OverlappingLaddersList)
996     {
997         // Assume ladder is rectangular which means I can aswell use its aabb for exact position
998         b2AABB aabb = pLadderFixture->GetAABB(0);
999 
1000         b2Vec2 b2bottomRight = MetersToPixels(aabb.upperBound);
1001         Point bottomright = b2Vec2ToPoint(b2bottomRight);
1002 
1003         b2Vec2 b2topLeft = MetersToPixels(aabb.lowerBound);
1004         Point topleft = b2Vec2ToPoint(b2topLeft);
1005 
1006         b2Vec2 b2center = MetersToPixels(aabb.GetCenter());
1007         Point center = b2Vec2ToPoint(b2center);
1008 
1009         if (actorPosition.x > topleft.x && actorPosition.x < bottomright.x)
1010         {
1011             m_pPhysics->VSetPosition(m_pOwner->GetGUID(), Point(center.x, actorPosition.y));
1012             //LOG("CAN CLIMB");
1013             return true;
1014         }
1015     }
1016 
1017     return false;
1018 }
1019 
RequestClimb(Point climbMovement)1020 void PhysicsComponent::RequestClimb(Point climbMovement)
1021 {
1022     m_ClimbingSpeed = climbMovement;
1023 
1024     if (!m_IsClimbing)
1025     {
1026         if (AttachToLadder())
1027         {
1028             bool bIsOnTopLadder = CheckOverlap(FixtureType_TopLadderGround);
1029             bool bIsClimbingUp = m_ClimbingSpeed.y < (-1.0 * DBL_EPSILON);
1030             m_pControllableComponent->VOnClimb(bIsClimbingUp, bIsOnTopLadder);
1031 
1032             m_IsClimbing = true;
1033             m_IgnoreJump = true;
1034             m_pPhysics->VSetGravityScale(m_pOwner->GetGUID(), 0);
1035         }
1036     }
1037 }
1038 
CheckOverlap(FixtureType withWhatFixture)1039 bool PhysicsComponent::CheckOverlap(FixtureType withWhatFixture)
1040 {
1041     return m_pPhysics->VIsActorOverlap(m_pOwner->GetGUID(), withWhatFixture);
1042 }
1043 
RemoveOverlappingLadder(const b2Fixture * ladder)1044 void PhysicsComponent::RemoveOverlappingLadder(const b2Fixture* ladder)
1045 {
1046     for (auto iter = m_OverlappingLaddersList.begin(); iter != m_OverlappingLaddersList.end(); ++iter)
1047     {
1048         if ((*iter) == ladder)
1049         {
1050             m_OverlappingLaddersList.erase(iter);
1051             return;
1052         }
1053     }
1054 }
1055 
AddOverlappingGround(const b2Fixture * pGround)1056 void PhysicsComponent::AddOverlappingGround(const b2Fixture* pGround)
1057 {
1058     //LOG_ERROR("owner: " + m_pOwner->GetName());
1059     if (std::find(m_OverlappingGroundsList.begin(), m_OverlappingGroundsList.end(), pGround) == m_OverlappingGroundsList.end())
1060     {
1061         m_OverlappingGroundsList.push_back(pGround);
1062         OnBeginFootContact();
1063     }
1064 }
1065 
RemoveOverlappingGround(const b2Fixture * pGround)1066 void PhysicsComponent::RemoveOverlappingGround(const b2Fixture* pGround)
1067 {
1068     for (auto iter = m_OverlappingGroundsList.begin(); iter != m_OverlappingGroundsList.end(); ++iter)
1069     {
1070         if ((*iter) == pGround)
1071         {
1072             m_OverlappingGroundsList.erase(iter);
1073             OnEndFootContact();
1074             return;
1075         }
1076     }
1077     LOG_WARNING("Could not remove overlapping ground - no such fixture found")
1078 }
1079 
AddOverlappingKinematicBody(const b2Body * pBody)1080 void PhysicsComponent::AddOverlappingKinematicBody(const b2Body* pBody)
1081 {
1082     if (std::find(m_OverlappingKinematicBodiesList.begin(), m_OverlappingKinematicBodiesList.end(), pBody) == m_OverlappingKinematicBodiesList.end())
1083     {
1084         m_OverlappingKinematicBodiesList.push_back(pBody);
1085     }
1086 }
1087 
RemoveOverlappingKinematicBody(const b2Body * pBody)1088 void PhysicsComponent::RemoveOverlappingKinematicBody(const b2Body* pBody)
1089 {
1090     for (auto iter = m_OverlappingKinematicBodiesList.begin(); iter != m_OverlappingKinematicBodiesList.end(); ++iter)
1091     {
1092         if ((*iter) == pBody)
1093         {
1094             m_OverlappingKinematicBodiesList.erase(iter);
1095             return;
1096         }
1097     }
1098     LOG_WARNING("Could not remove overlapping kinematic body - no such body found")
1099 }
1100 
SetForceFall()1101 void PhysicsComponent::SetForceFall()
1102 {
1103     m_IsRunning = false;
1104     m_bIsForcedUp = false;
1105     m_IsStopped = false;
1106     m_IsFalling = true;
1107     m_IsJumping = false;
1108     m_HeightInAir = 0;
1109     m_IgnoreJump = true;
1110     SetVelocity(Point(GetVelocity().x, 0));
1111     if (m_pControllableComponent)
1112     {
1113         m_pControllableComponent->VOnStartFalling();
1114     }
1115 }
1116 
OnAttachedToRope()1117 void PhysicsComponent::OnAttachedToRope()
1118 {
1119     m_IgnoreJump = true;
1120     m_HeightInAir = 0;
1121     m_IsJumping = false;
1122     m_IsFalling = false;
1123     m_DoNothingTimeout = 250;
1124 }
1125 
OnDetachedFromRope()1126 void PhysicsComponent::OnDetachedFromRope()
1127 {
1128     m_HeightInAir = 0;
1129 }
1130 
SetIsForcedUp(bool isForcedUp,int forcedUpHeight)1131 void PhysicsComponent::SetIsForcedUp(bool isForcedUp, int forcedUpHeight)
1132 {
1133     m_bIsForcedUp = isForcedUp;
1134     if (isForcedUp)
1135     {
1136         m_IsJumping = true;
1137         m_IsFalling = false;
1138         m_ForcedUpHeight = forcedUpHeight;
1139         m_MaxJumpHeight = m_ForcedUpHeight;
1140     }
1141     else
1142     {
1143         m_ForcedUpHeight = 0;
1144     }
1145 }
1146 
SetMaxJumpHeight(int32 maxJumpHeight)1147 void PhysicsComponent::SetMaxJumpHeight(int32 maxJumpHeight)
1148 {
1149     //m_MaxJumpHeight = maxJumpHeight;
1150     // HACK:
1151     if (m_pControllableComponent)
1152     {
1153         m_pControllableComponent->SetMaxJumpHeight(maxJumpHeight);
1154     }
1155 }
1156 
SetControllableComponent(ControllableComponent * pComp)1157 void PhysicsComponent::SetControllableComponent(ControllableComponent* pComp)
1158 {
1159     m_pControllableComponent = pComp;
1160     // HACK:
1161     if (m_pControllableComponent)
1162     {
1163         m_pControllableComponent->SetMaxJumpHeight(m_MaxJumpHeight);
1164     }
1165 }
1166 
SetDirection(Direction newDirection)1167 void PhysicsComponent::SetDirection(Direction newDirection)
1168 {
1169     if (m_Direction == newDirection)
1170     {
1171         return;
1172     }
1173 
1174     if (m_pControllableComponent && m_pControllableComponent->VOnDirectionChange(newDirection))
1175     {
1176         m_Direction = newDirection;
1177     }
1178 }