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 }