1 #include "ControllableComponent.h"
2 #include "../../Events/Events.h"
3 #include "../../Events/EventMgr.h"
4 #include "AnimationComponent.h"
5 #include "PhysicsComponent.h"
6 #include "RenderComponent.h"
7 #include "PositionComponent.h"
8 #include "../ActorTemplates.h"
9 #include "ControllerComponents/AmmoComponent.h"
10 #include "ControllerComponents/PowerupComponent.h"
11 #include "ControllerComponents/HealthComponent.h"
12 #include "ControllerComponents/LifeComponent.h"
13 #include "FollowableComponent.h"
14
15 #include "../../GameApp/BaseGameApp.h"
16 #include "../../GameApp/BaseGameLogic.h"
17 #include "../../UserInterface/HumanView.h"
18 #include "../../Scene/SceneNodes.h"
19
20 #include "../../Util/ClawLevelUtil.h"
21
22 const char* ControllableComponent::g_Name = "ControllableComponent";
23 const char* ClawControllableComponent::g_Name = "ClawControllableComponent";
24
ControllableComponent()25 ControllableComponent::ControllableComponent()
26 :
27 m_Active(false),
28 m_DuckingTime(0),
29 m_LookingUpTime(0),
30 m_bFrozen(false),
31 m_MaxJumpHeight(0)
32 { }
33
VInit(TiXmlElement * data)34 bool ControllableComponent::VInit(TiXmlElement* data)
35 {
36 assert(data != NULL);
37
38 if (TiXmlElement* isActiveElement = data->FirstChildElement("IsActive"))
39 {
40 m_Active = std::string(isActiveElement->GetText()) == "true";
41 }
42 else
43 {
44 m_Active = false;
45 }
46
47 return VInitDelegate(data);
48 }
49
VPostInit()50 void ControllableComponent::VPostInit()
51 {
52 shared_ptr<PhysicsComponent> pPhysicsComponent =
53 MakeStrongPtr(m_pOwner->GetComponent<PhysicsComponent>(PhysicsComponent::g_Name));
54 if (pPhysicsComponent)
55 {
56 pPhysicsComponent->SetControllableComponent(this);
57 }
58
59 if (m_Active)
60 {
61 shared_ptr<EventData_Attach_Actor> pEvent(new EventData_Attach_Actor(m_pOwner->GetGUID()));
62 IEventMgr::Get()->VTriggerEvent(pEvent);
63 }
64 }
65
VGenerateXml()66 TiXmlElement* ControllableComponent::VGenerateXml()
67 {
68 TiXmlElement* baseElement = new TiXmlElement(VGetName());
69
70 //
71
72 return baseElement;
73 }
74
75 //=====================================================================================================================
76
ClawControllableComponent()77 ClawControllableComponent::ClawControllableComponent()
78 {
79 m_pClawAnimationComponent = NULL;
80 m_pRenderComponent = NULL;
81 m_State = ClawState_None;
82 m_LastState = ClawState_None;
83 m_Direction = Direction_Right;
84 m_IdleTime = 0;
85 m_TakeDamageDuration = 500; // TODO: Data drive
86 m_TakeDamageTimeLeftMs = 0;
87 m_bIsInBossFight = false;
88 m_ThrowingTime = 0;
89
90 IEventMgr::Get()->VAddListener(MakeDelegate(this, &ClawControllableComponent::BossFightStartedDelegate), EventData_Boss_Fight_Started::sk_EventType);
91 IEventMgr::Get()->VAddListener(MakeDelegate(this, &ClawControllableComponent::BossFightEndedDelegate), EventData_Boss_Fight_Ended::sk_EventType);
92 }
93
~ClawControllableComponent()94 ClawControllableComponent::~ClawControllableComponent()
95 {
96 IEventMgr::Get()->VRemoveListener(MakeDelegate(this, &ClawControllableComponent::BossFightStartedDelegate), EventData_Boss_Fight_Started::sk_EventType);
97 IEventMgr::Get()->VRemoveListener(MakeDelegate(this, &ClawControllableComponent::BossFightEndedDelegate), EventData_Boss_Fight_Ended::sk_EventType);
98 }
99
VInitDelegate(TiXmlElement * data)100 bool ClawControllableComponent::VInitDelegate(TiXmlElement* data)
101 {
102 return true;
103 }
104
VPostInit()105 void ClawControllableComponent::VPostInit()
106 {
107 ControllableComponent::VPostInit();
108
109 m_pRenderComponent = MakeStrongPtr(m_pOwner->GetComponent<ActorRenderComponent>(ActorRenderComponent::g_Name)).get();
110 m_pClawAnimationComponent = MakeStrongPtr(m_pOwner->GetComponent<AnimationComponent>(AnimationComponent::g_Name)).get();
111 m_pPositionComponent = MakeStrongPtr(m_pOwner->GetComponent<PositionComponent>(PositionComponent::g_Name)).get();
112 m_pAmmoComponent = MakeStrongPtr(m_pOwner->GetComponent<AmmoComponent>(AmmoComponent::g_Name)).get();
113 m_pPowerupComponent = MakeStrongPtr(m_pOwner->GetComponent<PowerupComponent>(PowerupComponent::g_Name)).get();
114 m_pHealthComponent = MakeStrongPtr(m_pOwner->GetComponent<HealthComponent>(HealthComponent::g_Name)).get();
115 m_pExclamationMark = MakeStrongPtr(m_pOwner->GetComponent<FollowableComponent>()).get();
116 assert(m_pClawAnimationComponent);
117 assert(m_pRenderComponent);
118 assert(m_pPositionComponent);
119 assert(m_pAmmoComponent);
120 assert(m_pPowerupComponent);
121 assert(m_pHealthComponent);
122 assert(m_pExclamationMark);
123 m_pClawAnimationComponent->AddObserver(this);
124
125 auto pHealthComponent = MakeStrongPtr(m_pOwner->GetComponent<HealthComponent>(HealthComponent::g_Name));
126 pHealthComponent->AddObserver(this);
127
128 m_pPhysicsComponent = MakeStrongPtr(m_pOwner->GetComponent<PhysicsComponent>(PhysicsComponent::g_Name)).get();
129
130 // Sounds that play when claw takes some damage
131 m_TakeDamageSoundList.push_back(SOUND_CLAW_TAKE_DAMAGE1);
132 m_TakeDamageSoundList.push_back(SOUND_CLAW_TAKE_DAMAGE2);
133 m_TakeDamageSoundList.push_back(SOUND_CLAW_TAKE_DAMAGE3);
134 m_TakeDamageSoundList.push_back(SOUND_CLAW_TAKE_DAMAGE4);
135
136 // Sounds that play when claw is idle for some time
137 m_IdleQuoteSoundList.push_back(SOUND_CLAW_IDLE1);
138 //m_IdleQuoteSoundList.push_back(SOUND_CLAW_IDLE2);
139 m_IdleQuoteSoundList.push_back(SOUND_CLAW_IDLE3);
140 m_IdleQuoteSoundList.push_back(SOUND_CLAW_IDLE4);
141 m_IdleQuoteSoundList.push_back(SOUND_CLAW_IDLE5);
142 m_IdleQuoteSoundList.push_back(SOUND_CLAW_IDLE6);
143 m_IdleQuoteSoundList.push_back(SOUND_CLAW_IDLE7);
144 m_IdleQuoteSoundList.push_back(SOUND_CLAW_IDLE8);
145 m_IdleQuoteSoundList.push_back(SOUND_CLAW_IDLE9);
146 m_IdleQuoteSoundList.push_back(SOUND_CLAW_IDLE10);
147 m_IdleQuoteSoundList.push_back(SOUND_CLAW_IDLE11);
148 m_IdleQuoteSoundList.push_back(SOUND_CLAW_IDLE12);
149
150 m_pIdleQuotesSequence.reset(new PrimeSearch(m_IdleQuoteSoundList.size()));
151
152 // No existing animation for the top-ladder climb...
153 {
154 std::vector<AnimationFrame> climbAnimFrames;
155 for (int i = 0, climbImageId = 383; climbImageId <= 389; climbImageId++, i++)
156 {
157 AnimationFrame frame;
158 frame.idx = i;
159 frame.imageId = climbImageId;
160 frame.imageName = "frame" + ToStr(climbImageId);
161 frame.duration = 55;
162 climbAnimFrames.push_back(frame);
163 }
164 Animation* pClimbAnim = Animation::CreateAnimation(climbAnimFrames, "topclimb", m_pClawAnimationComponent);
165 assert(pClimbAnim);
166 assert(m_pClawAnimationComponent->AddAnimation("topclimb", pClimbAnim));
167 }
168
169 {
170 std::vector<AnimationFrame> climbAnimFrames;
171 for (int i = 0, climbImageId = 389; climbImageId >= 383; climbImageId--, i++)
172 {
173 AnimationFrame frame;
174 frame.idx = i;
175 frame.imageId = climbImageId;
176 frame.imageName = "frame" + ToStr(climbImageId);
177 frame.duration = 55;
178 climbAnimFrames.push_back(frame);
179 }
180 Animation* pClimbAnim = Animation::CreateAnimation(climbAnimFrames, "topclimbdown", m_pClawAnimationComponent);
181 assert(pClimbAnim);
182 assert(m_pClawAnimationComponent->AddAnimation("topclimbdown", pClimbAnim));
183 }
184
185 {
186 std::vector<AnimationFrame> highFallAnimFrames;
187 AnimationFrame frame;
188 frame.idx = 0;
189 frame.imageId = 401;
190 frame.imageName = "frame401";
191 frame.duration = 2000;
192 highFallAnimFrames.push_back(frame);
193
194 Animation* pHighFallAnim = Animation::CreateAnimation(highFallAnimFrames, "highfall", m_pClawAnimationComponent);
195 assert(pHighFallAnim);
196 assert(m_pClawAnimationComponent->AddAnimation("highfall", pHighFallAnim));
197 }
198 }
199
VPostPostInit()200 void ClawControllableComponent::VPostPostInit()
201 {
202 // So that Claw can attach to rope
203 ActorFixtureDef fixtureDef;
204 fixtureDef.collisionShape = "Rectangle";
205 fixtureDef.fixtureType = FixtureType_RopeSensor;
206 fixtureDef.size = Point(25, 25);
207 fixtureDef.collisionMask = CollisionFlag_Rope;
208 fixtureDef.collisionFlag = CollisionFlag_RopeSensor;
209 fixtureDef.isSensor = true;
210
211 g_pApp->GetGameLogic()->VGetGamePhysics()->VAddActorFixtureToBody(m_pOwner->GetGUID(), &fixtureDef);
212 }
213
VUpdate(uint32 msDiff)214 void ClawControllableComponent::VUpdate(uint32 msDiff)
215 {
216 if (m_State == ClawState_Standing ||
217 m_State == ClawState_Idle)
218 {
219 m_IdleTime += msDiff;
220 if (m_IdleTime > g_pApp->GetGlobalOptions()->idleSoundQuoteIntervalMs)
221 {
222 // This is to prevent the same sound to play multiple times in a row
223 int idleQuoteSoundIdx = m_pIdleQuotesSequence->GetNext();
224 if (idleQuoteSoundIdx == -1)
225 {
226 idleQuoteSoundIdx = m_pIdleQuotesSequence->GetNext(true);
227 }
228
229 SoundInfo soundInfo(m_IdleQuoteSoundList[idleQuoteSoundIdx]);
230 IEventMgr::Get()->VTriggerEvent(IEventDataPtr(
231 new EventData_Request_Play_Sound(soundInfo)));
232
233 m_pExclamationMark->Activate(2000);
234
235 m_pClawAnimationComponent->SetAnimation("idle");
236
237 m_State = ClawState_Idle;
238 m_IdleTime = 0;
239 }
240 }
241 else
242 {
243 m_IdleTime = 0;
244 }
245
246 // Check if invulnerability caused by previously taking damage is gone
247 if (m_TakeDamageTimeLeftMs > 0)
248 {
249 m_TakeDamageTimeLeftMs -= msDiff;
250 if (m_TakeDamageTimeLeftMs <= 0)
251 {
252 if (!m_pPowerupComponent->HasPowerup(PowerupType_Invulnerability))
253 {
254 m_pHealthComponent->SetInvulnerable(false);
255 }
256 m_TakeDamageTimeLeftMs = 0;
257 }
258 }
259
260 assert(g_pApp->GetHumanView() && g_pApp->GetHumanView()->GetCamera());
261 shared_ptr<CameraNode> pCamera = g_pApp->GetHumanView()->GetCamera();
262
263 if (m_DuckingTime > g_pApp->GetGlobalOptions()->startLookUpOrDownTime)
264 {
265 if (pCamera->GetCameraOffsetY() < g_pApp->GetGlobalOptions()->maxLookUpOrDownDistance)
266 {
267 // Pixels per milisecond
268 double cameraOffsetSpeed = g_pApp->GetGlobalOptions()->lookUpOrDownSpeed / 1000.0;
269 pCamera->AddCameraOffsetY(cameraOffsetSpeed * msDiff);
270 }
271 }
272 else if (m_LookingUpTime > g_pApp->GetGlobalOptions()->startLookUpOrDownTime)
273 {
274 if (pCamera->GetCameraOffsetY() > -1.0 * g_pApp->GetGlobalOptions()->maxLookUpOrDownDistance)
275 {
276 // Pixels per milisecond
277 double cameraOffsetSpeed = -1.0 * (g_pApp->GetGlobalOptions()->lookUpOrDownSpeed / 1000.0);
278 pCamera->AddCameraOffsetY(cameraOffsetSpeed * msDiff);
279 }
280 m_pClawAnimationComponent->SetAnimation("lookup");
281 }
282 else
283 {
284 if (m_State != ClawState_Ducking)
285 {
286 m_DuckingTime = 0;
287 }
288 if (m_State != ClawState_Standing)
289 {
290 m_LookingUpTime = 0;
291 }
292
293 if (fabs(pCamera->GetCameraOffsetY()) > DBL_EPSILON)
294 {
295 double cameraOffsetSpeed = ((double)g_pApp->GetGlobalOptions()->lookUpOrDownSpeed * 2.0) / 1000.0;
296 if (pCamera->GetCameraOffsetY() > DBL_EPSILON)
297 {
298 // Camera should move back up
299 double cameraMovePx = -1.0 * cameraOffsetSpeed * msDiff;
300 pCamera->AddCameraOffsetY(cameraMovePx);
301 if (pCamera->GetCameraOffsetY() < DBL_EPSILON)
302 {
303 pCamera->SetCameraOffsetY(0.0);
304 }
305 }
306 else if (pCamera->GetCameraOffsetY() < (-1.0 * DBL_EPSILON))
307 {
308 // Camera should move back down
309 double cameraMovePx = cameraOffsetSpeed * msDiff;
310 pCamera->AddCameraOffsetY(cameraMovePx);
311 if (pCamera->GetCameraOffsetY() > DBL_EPSILON)
312 {
313 pCamera->SetCameraOffsetY(0.0);
314 }
315 }
316 }
317 }
318
319 if (m_bFrozen)
320 {
321 m_pClawAnimationComponent->SetAnimation("stand");
322 m_IdleTime = 0;
323 m_LookingUpTime = 0;
324 m_DuckingTime = 0;
325 }
326
327 if (m_State == ClawState_HoldingRope)
328 {
329 m_IdleTime = 0;
330 m_LookingUpTime = 0;
331 m_DuckingTime = 0;
332 }
333
334 if (m_pPhysicsComponent->IsFalling() &&
335 m_pClawAnimationComponent->GetCurrentAnimationName() == "fall" &&
336 m_pPhysicsComponent->GetFallHeight() > g_pApp->GetGlobalOptions()->clawMinFallHeight)
337 {
338 m_pClawAnimationComponent->SetAnimation("highfall");
339 }
340
341 if (m_pClawAnimationComponent->GetCurrentAnimationName().find("predynamite") != std::string::npos ||
342 m_pClawAnimationComponent->GetCurrentAnimationName().find("postdynamite") != std::string::npos)
343 {
344 if (m_pClawAnimationComponent->GetCurrentAnimationName().find("predynamite") != std::string::npos)
345 {
346 m_ThrowingTime += msDiff;
347 if (m_ThrowingTime >= 2000)
348 {
349 if (IsDucking())
350 {
351 m_pClawAnimationComponent->SetAnimation("duckpostdynamite");
352 }
353 else
354 {
355 m_pClawAnimationComponent->SetAnimation("postdynamite");
356 }
357 }
358 }
359 }
360 else
361 {
362 m_ThrowingTime = 0;
363 }
364
365 //LOG("State: " + ToStr((int)m_State));
366
367 m_LastState = m_State;
368 }
369
370 // Interface for subclasses
VOnStartFalling()371 void ClawControllableComponent::VOnStartFalling()
372 {
373 if (m_State == ClawState_JumpShooting ||
374 m_State == ClawState_JumpAttacking)
375 {
376 return;
377 }
378
379 m_pClawAnimationComponent->SetAnimation("fall");
380 m_State = ClawState_Falling;
381 }
382
VOnLandOnGround(float fromHeight)383 void ClawControllableComponent::VOnLandOnGround(float fromHeight)
384 {
385 if (fromHeight > g_pApp->GetGlobalOptions()->clawMinFallHeight)
386 {
387 m_pClawAnimationComponent->SetAnimation("land");
388 }
389 else
390 {
391 m_pClawAnimationComponent->SetAnimation("stand");
392
393 SoundInfo soundInfo(SOUND_CLAW_LAND_SHORT);
394 soundInfo.soundVolume = 150;
395 IEventMgr::Get()->VTriggerEvent(IEventDataPtr(
396 new EventData_Request_Play_Sound(soundInfo)));
397 }
398
399 m_State = ClawState_Standing;
400 }
401
VOnStartJumping()402 void ClawControllableComponent::VOnStartJumping()
403 {
404 if (m_State == ClawState_JumpShooting ||
405 m_State == ClawState_JumpAttacking ||
406 m_bFrozen)
407 {
408 return;
409 }
410
411 m_pClawAnimationComponent->SetAnimation("jump");
412 m_State = ClawState_Jumping;
413 }
414
VOnDirectionChange(Direction direction)415 bool ClawControllableComponent::VOnDirectionChange(Direction direction)
416 {
417 if (m_bFrozen/* || (IsAttackingOrShooting() && fabs(m_pPhysicsComponent->GetVelocity().x) < DBL_EPSILON)*/)
418 {
419 return false;
420 }
421
422 m_pRenderComponent->SetMirrored(direction == Direction_Left);
423 m_Direction = direction;
424
425 return true;
426 }
427
VOnStopMoving()428 void ClawControllableComponent::VOnStopMoving()
429 {
430 if (m_State == ClawState_Shooting ||
431 m_State == ClawState_Idle ||
432 IsDucking())
433 {
434 return;
435 }
436
437 if (m_pClawAnimationComponent->GetCurrentAnimationName() != "land")
438 {
439 m_pClawAnimationComponent->SetAnimation("stand");
440 }
441
442 m_State = ClawState_Standing;
443 }
444
VOnRun()445 void ClawControllableComponent::VOnRun()
446 {
447 if (m_State == ClawState_Shooting ||
448 m_pClawAnimationComponent->GetCurrentAnimationName() == "land")
449 {
450 return;
451 }
452 m_pClawAnimationComponent->SetAnimation("walk");
453 m_LookingUpTime = 0;
454 m_DuckingTime = 0;
455 m_State = ClawState_Walking;
456 }
457
VOnClimb(bool bIsClimbingUp,bool bIsOnTopEdge)458 void ClawControllableComponent::VOnClimb(bool bIsClimbingUp, bool bIsOnTopEdge)
459 {
460 // TODO: Decide whether to keep it
461 static bool useAlternativeClimbAnim = true;
462 if (bIsOnTopEdge && useAlternativeClimbAnim)
463 {
464 if (!bIsClimbingUp)
465 {
466 m_pClawAnimationComponent->SetAnimation("topclimbdown");
467 }
468 else
469 {
470 m_pClawAnimationComponent->SetAnimation("topclimb");
471 }
472 }
473 else
474 {
475 if (!bIsClimbingUp)
476 {
477 m_pClawAnimationComponent->SetAnimation("climbdown");
478 }
479 else
480 {
481 m_pClawAnimationComponent->SetAnimation("climb");
482 }
483 }
484
485 m_pClawAnimationComponent->ResumeAnimation();
486 m_State = ClawState_Climbing;
487 }
488
VOnStopClimbing()489 void ClawControllableComponent::VOnStopClimbing()
490 {
491 m_pClawAnimationComponent->PauseAnimation();
492 }
493
OnAttack()494 void ClawControllableComponent::OnAttack()
495 {
496 if (IsAttackingOrShooting() ||
497 m_State == ClawState_Climbing ||
498 m_State == ClawState_TakingDamage ||
499 m_State == ClawState_Dying ||
500 m_State == ClawState_HoldingRope ||
501 m_bFrozen)
502 {
503 return;
504 }
505
506 if (m_State == ClawState_Falling ||
507 m_State == ClawState_Jumping)
508 {
509 m_pClawAnimationComponent->SetAnimation("jumpswipe");
510 m_State = ClawState_JumpAttacking;
511 }
512 else if (IsDucking())
513 {
514 m_pClawAnimationComponent->SetAnimation("duckswipe");
515 m_State = ClawState_DuckAttacking;
516 }
517 else
518 {
519 // Fire/Ice/Lightning sword powerups have always swipe anim
520 if (m_pPowerupComponent->HasPowerup(PowerupType_FireSword) ||
521 m_pPowerupComponent->HasPowerup(PowerupType_FrostSword) ||
522 m_pPowerupComponent->HasPowerup(PowerupType_LightningSword))
523 {
524 m_pClawAnimationComponent->SetAnimation("swipe");
525 }
526 else
527 {
528 int attackType = rand() % 5;
529 if (attackType == 0)
530 {
531 m_pClawAnimationComponent->SetAnimation("kick");
532 }
533 else if (attackType == 1)
534 {
535 m_pClawAnimationComponent->SetAnimation("uppercut");
536 }
537 else
538 {
539 m_pClawAnimationComponent->SetAnimation("swipe");
540 }
541 }
542
543 m_State = ClawState_Attacking;
544 }
545
546 // If its one of the magic swords, play its corresponding sound
547
548 if (m_pPowerupComponent->HasPowerup(PowerupType_FireSword))
549 {
550 SoundInfo soundInfo(SOUND_CLAW_FIRE_SWORD);
551 IEventMgr::Get()->VTriggerEvent(IEventDataPtr(
552 new EventData_Request_Play_Sound(soundInfo)));
553 }
554 else if (m_pPowerupComponent->HasPowerup(PowerupType_FrostSword))
555 {
556 SoundInfo soundInfo(SOUND_CLAW_FROST_SWORD);
557 IEventMgr::Get()->VTriggerEvent(IEventDataPtr(
558 new EventData_Request_Play_Sound(soundInfo)));
559 }
560 else if (m_pPowerupComponent->HasPowerup(PowerupType_LightningSword))
561 {
562 SoundInfo soundInfo(SOUND_CLAW_LIGHTNING_SWORD);
563 IEventMgr::Get()->VTriggerEvent(IEventDataPtr(
564 new EventData_Request_Play_Sound(soundInfo)));
565 }
566 }
567
OnFire(bool outOfAmmo)568 void ClawControllableComponent::OnFire(bool outOfAmmo)
569 {
570 if (IsAttackingOrShooting() ||
571 m_State == ClawState_Climbing ||
572 m_State == ClawState_Dying ||
573 m_State == ClawState_TakingDamage ||
574 m_State == ClawState_HoldingRope ||
575 m_bFrozen)
576 {
577 return;
578 }
579
580 AmmoType activeAmmoType = m_pAmmoComponent->GetActiveAmmoType();
581 if (m_State == ClawState_Falling ||
582 m_State == ClawState_Jumping)
583 {
584 if (activeAmmoType == AmmoType_Pistol)
585 {
586 if (m_pAmmoComponent->CanFire())
587 {
588 m_pClawAnimationComponent->SetAnimation("jumppistol");
589 }
590 else
591 {
592 m_pClawAnimationComponent->SetAnimation("emptyjumppistol");
593 }
594 }
595 else if (activeAmmoType == AmmoType_Magic)
596 {
597 if (m_pAmmoComponent->CanFire())
598 {
599 m_pClawAnimationComponent->SetAnimation("jumpmagic");
600 }
601 else
602 {
603 m_pClawAnimationComponent->SetAnimation("emptyjumpmagic");
604 }
605 }
606 else if (activeAmmoType == AmmoType_Dynamite)
607 {
608 if (m_pAmmoComponent->CanFire())
609 {
610 m_pClawAnimationComponent->SetAnimation("jumpdynamite");
611 }
612 else
613 {
614 m_pClawAnimationComponent->SetAnimation("emptyjumpdynamite");
615 }
616 }
617 m_State = ClawState_JumpShooting;
618 }
619 else if (IsDucking())
620 {
621 if (activeAmmoType == AmmoType_Pistol)
622 {
623 if (m_pAmmoComponent->CanFire())
624 {
625 m_pClawAnimationComponent->SetAnimation("duckpistol");
626 }
627 else
628 {
629 m_pClawAnimationComponent->SetAnimation("duckemptypistol");
630 }
631 }
632 else if (activeAmmoType == AmmoType_Magic)
633 {
634 if (m_pAmmoComponent->CanFire())
635 {
636 m_pClawAnimationComponent->SetAnimation("duckmagic");
637 }
638 else
639 {
640 m_pClawAnimationComponent->SetAnimation("duckemptymagic");
641 }
642 }
643 else if (activeAmmoType == AmmoType_Dynamite)
644 {
645 if (m_pAmmoComponent->CanFire())
646 {
647 m_pClawAnimationComponent->SetAnimation("duckpredynamite");
648 }
649 else
650 {
651 m_pClawAnimationComponent->SetAnimation("duckemptydynamite");
652 }
653 }
654 m_State = ClawState_DuckAttacking;
655 }
656 else
657 {
658 if (activeAmmoType == AmmoType_Pistol)
659 {
660 if (m_pAmmoComponent->CanFire())
661 {
662 m_pClawAnimationComponent->SetAnimation("pistol");
663 }
664 else
665 {
666 m_pClawAnimationComponent->SetAnimation("emptypistol");
667 }
668 }
669 else if (activeAmmoType == AmmoType_Magic)
670 {
671 if (m_pAmmoComponent->CanFire())
672 {
673 m_pClawAnimationComponent->SetAnimation("magic");
674 }
675 else
676 {
677 m_pClawAnimationComponent->SetAnimation("emptymagic");
678 }
679 }
680 else if (activeAmmoType == AmmoType_Dynamite)
681 {
682 if (m_pAmmoComponent->CanFire())
683 {
684 m_pClawAnimationComponent->SetAnimation("predynamite");
685 }
686 else
687 {
688 m_pClawAnimationComponent->SetAnimation("emptydynamite");
689 }
690 }
691 m_State = ClawState_Shooting;
692 }
693 }
694
OnFireEnded()695 void ClawControllableComponent::OnFireEnded()
696 {
697 if (m_pClawAnimationComponent->GetCurrentAnimationName().find("predynamite") != std::string::npos)
698 {
699 if (IsDucking())
700 {
701 m_pClawAnimationComponent->SetAnimation("duckpostdynamite");
702 }
703 else
704 {
705 m_pClawAnimationComponent->SetAnimation("postdynamite");
706 }
707 }
708 }
709
CanMove()710 bool ClawControllableComponent::CanMove()
711 {
712 if ((m_State == ClawState_Shooting && m_pClawAnimationComponent->GetCurrentAnimationName() != "predynamite") ||
713 m_State == ClawState_Attacking ||
714 m_State == ClawState_Dying ||
715 m_State == ClawState_DuckAttacking ||
716 m_State == ClawState_DuckShooting ||
717 m_State == ClawState_TakingDamage ||
718 m_pClawAnimationComponent->GetCurrentAnimationName() == "land" ||
719 (m_LookingUpTime > g_pApp->GetGlobalOptions()->startLookUpOrDownTime) ||
720 m_bFrozen)
721 {
722 return false;
723 }
724
725 return true;
726 }
727
SetCurrentPhysicsState()728 void ClawControllableComponent::SetCurrentPhysicsState()
729 {
730 if (m_pPhysicsComponent->IsFalling())
731 {
732 m_pClawAnimationComponent->SetAnimation("fall");
733 m_State = ClawState_Falling;
734 }
735 else if (m_pPhysicsComponent->IsJumping())
736 {
737 m_pClawAnimationComponent->SetAnimation("jump");
738 m_State = ClawState_Jumping;
739 }
740 else if (IsDucking())
741 {
742 m_pClawAnimationComponent->SetAnimation("duck");
743 m_State = ClawState_Ducking;
744 }
745 else if (m_pPhysicsComponent->IsOnGround())
746 {
747 m_pClawAnimationComponent->SetAnimation("stand");
748 m_State = ClawState_Standing;
749 }
750 else if (m_State == ClawState_HoldingRope)
751 {
752 m_pClawAnimationComponent->SetAnimation("swing");
753 m_pPhysicsComponent->SetGravityScale(0.0f);
754 return;
755 }
756 else
757 {
758 m_pClawAnimationComponent->SetAnimation("fall");
759 m_State = ClawState_Standing;
760 //LOG_ERROR("Unknown physics state. Assume falling");
761 }
762
763 m_pPhysicsComponent->RestoreGravityScale();
764 }
765
VOnAnimationFrameChanged(Animation * pAnimation,AnimationFrame * pLastFrame,AnimationFrame * pNewFrame)766 void ClawControllableComponent::VOnAnimationFrameChanged(Animation* pAnimation, AnimationFrame* pLastFrame, AnimationFrame* pNewFrame)
767 {
768 const std::string animName = pAnimation->GetName();
769
770 if (animName.find("predynamite") != std::string::npos && pAnimation->IsAtLastAnimFrame())
771 {
772 pAnimation->Pause();
773 return;
774 }
775
776 // Shooting, only magic and pistol supported at the moment
777 if (((animName.find("pistol") != std::string::npos) && pNewFrame->hasEvent) ||
778 ((animName.find("magic") != std::string::npos) && pNewFrame->hasEvent) ||
779 ((animName.find("dynamite") != std::string::npos) && pNewFrame->hasEvent))
780 {
781 if (m_pAmmoComponent->CanFire())
782 {
783 AmmoType projectileType = m_pAmmoComponent->GetActiveAmmoType();
784 int32 offsetX = 50;
785 if (m_Direction == Direction_Left) { offsetX *= -1; }
786 int32 offsetY = -20;
787 if (IsDucking()) { offsetY += 40; }
788
789 // Dynamite hacks
790 Point initialImpulse(0, 0);
791 if (animName.find("postdynamite") != std::string::npos)
792 {
793 initialImpulse.Set(10, -10);
794 float factor = (float)m_ThrowingTime / (float)2000.0f;
795 initialImpulse.x *= factor;
796 initialImpulse.y *= factor;
797 }
798 else if (animName == "jumpdynamite")
799 {
800 initialImpulse.Set(3.2, 0);
801 }
802
803 ActorTemplates::CreateClawProjectile(
804 projectileType,
805 m_Direction,
806 Point(m_pPositionComponent->GetX() + offsetX,
807 m_pPositionComponent->GetY() + offsetY),
808 m_pOwner->GetGUID(),
809 initialImpulse);
810
811 int soundPlayChance = 33;
812 if (!m_pExclamationMark->IsActive() &&
813 Util::RollDice(soundPlayChance) &&
814 animName.find("duck") == std::string::npos &&
815 animName.find("jump") == std::string::npos)
816 {
817 if (projectileType == AmmoType_Pistol)
818 {
819 SoundInfo soundInfo(SOUND_CLAW_KILL_PISTOL1);
820 soundInfo.soundVolume = 200;
821 IEventMgr::Get()->VTriggerEvent(IEventDataPtr(
822 new EventData_Request_Play_Sound(soundInfo)));
823 m_pExclamationMark->Activate(Util::GetSoundDurationMs(SOUND_CLAW_KILL_PISTOL1));
824 }
825 else if ((projectileType == AmmoType_Dynamite) && Util::RollDice(soundPlayChance))
826 {
827 SoundInfo soundInfo(SOUND_CLAW_SCREW_ALL_THIS);
828 soundInfo.soundVolume = 200;
829 IEventMgr::Get()->VTriggerEvent(IEventDataPtr(
830 new EventData_Request_Play_Sound(soundInfo)));
831 m_pExclamationMark->Activate(Util::GetSoundDurationMs(SOUND_CLAW_SCREW_ALL_THIS));
832 }
833 }
834
835 if (IsDucking())
836 {
837 SetCurrentPhysicsState();
838 }
839
840 if (!g_pApp->GetGameCheats()->clawInfiniteAmmo)
841 {
842 // TODO: Better name of this method ?
843 m_pAmmoComponent->OnFired();
844 }
845 }
846 }
847
848 if (((animName.find("pistol") != std::string::npos) ||
849 (animName.find("magic") != std::string::npos) ||
850 (animName.find("dynamite") != std::string::npos))
851 && pAnimation->IsAtLastAnimFrame())
852 {
853 SetCurrentPhysicsState();
854 }
855 else if ((animName == "swipe" ||
856 animName == "kick" ||
857 animName == "uppercut" ||
858 animName == "jumpswipe" ||
859 animName == "duckswipe"))
860 {
861 if (pAnimation->GetCurrentAnimationFrame()->hasEvent ||
862 (animName == "swipe" && pNewFrame->idx == 3))
863 {
864 Point position = m_pPositionComponent->GetPosition();
865 Point positionOffset(60, 20);
866 if (animName == "jumpswipe")
867 {
868 positionOffset.y -= 10;
869 positionOffset.x += 5;
870 }
871 if (m_Direction == Direction_Left)
872 {
873 positionOffset = Point(-1.0 * positionOffset.x, positionOffset.y);
874 }
875 position += positionOffset;
876
877 if (m_pPowerupComponent->HasPowerup(PowerupType_FireSword))
878 {
879 ActorTemplates::CreateActor_Projectile(
880 ActorPrototype_FireSwordProjectile,
881 position,
882 m_Direction,
883 m_pOwner->GetGUID());
884 }
885 else if (m_pPowerupComponent->HasPowerup(PowerupType_FrostSword))
886 {
887 ActorTemplates::CreateActor_Projectile(
888 ActorPrototype_FrostSwordProjectile,
889 position,
890 m_Direction,
891 m_pOwner->GetGUID());
892 }
893 else if (m_pPowerupComponent->HasPowerup(PowerupType_LightningSword))
894 {
895 ActorTemplates::CreateActor_Projectile(
896 ActorPrototype_LightningSwordProjectile,
897 position,
898 m_Direction,
899 m_pOwner->GetGUID());
900 }
901 else
902 {
903 int damage = 10;
904
905 // When Claw is ducking he deals 1/2 damage
906 if (IsDucking())
907 {
908 damage = 5;
909 }
910 if (m_pPowerupComponent->HasPowerup(PowerupType_Catnip))
911 {
912 damage = 100;
913 }
914
915 ActorTemplates::CreateAreaDamage(
916 position,
917 Point(50, 25),
918 damage,
919 CollisionFlag_ClawAttack,
920 "Rectangle",
921 DamageType_MeleeAttack,
922 m_Direction,
923 m_pOwner->GetGUID());
924 }
925 }
926 if (pAnimation->IsAtLastAnimFrame())
927 {
928 SetCurrentPhysicsState();
929 }
930 }
931
932 if (animName == "lookup" && pAnimation->IsAtLastAnimFrame())
933 {
934 pAnimation->Pause();
935 }
936 }
937
VOnAnimationLooped(Animation * pAnimation)938 void ClawControllableComponent::VOnAnimationLooped(Animation* pAnimation)
939 {
940 std::string animName = pAnimation->GetName();
941 if (pAnimation->GetName().find("death") != std::string::npos)
942 {
943 shared_ptr<LifeComponent> pClawLifeComponent = MakeStrongPtr(m_pOwner->GetComponent<LifeComponent>());
944 assert(pClawLifeComponent != nullptr);
945
946 shared_ptr<EventData_Claw_Died> pEvent(new EventData_Claw_Died(
947 m_pOwner->GetGUID(),
948 m_pPositionComponent->GetPosition(),
949 pClawLifeComponent->GetLives() - 1));
950 IEventMgr::Get()->VTriggerEvent(pEvent);
951
952 SetCurrentPhysicsState();
953 m_pPhysicsComponent->RestoreGravityScale();
954 m_pRenderComponent->SetVisible(true);
955 }
956 else if (animName == "damage1" ||
957 animName == "damage2")
958 {
959 SetCurrentPhysicsState();
960 }
961 else if (animName == "land")
962 {
963 m_pClawAnimationComponent->SetAnimation("stand");
964 }
965 }
966
IsAttackingOrShooting()967 bool ClawControllableComponent::IsAttackingOrShooting()
968 {
969 if (m_State == ClawState_Shooting ||
970 m_State == ClawState_JumpShooting ||
971 m_State == ClawState_DuckShooting ||
972 m_State == ClawState_Attacking ||
973 m_State == ClawState_DuckAttacking ||
974 m_State == ClawState_JumpAttacking)
975 {
976 return true;
977 }
978
979 return false;
980 }
981
VOnHealthBelowZero(DamageType damageType,int sourceActorId)982 void ClawControllableComponent::VOnHealthBelowZero(DamageType damageType, int sourceActorId)
983 {
984 if (m_State == ClawState_Dying)
985 {
986 return;
987 }
988
989 IEventMgr::Get()->VQueueEvent(IEventDataPtr(new EventData_Claw_Health_Below_Zero(m_pOwner->GetGUID())));
990
991 // TODO: Track how exactly claw died
992 if (m_pClawAnimationComponent->GetCurrentAnimationName() != "spikedeath")
993 {
994 m_pClawAnimationComponent->SetAnimation("spikedeath");
995 m_pPhysicsComponent->SetGravityScale(0.0f);
996 m_State = ClawState_Dying;
997
998 std::string deathSound = SOUND_CLAW_DEATH_SPIKES;
999
1000 // TODO: When claw dies, in the original game he falls off the screen and respawns
1001 if (damageType == DamageType_DeathTile)
1002 {
1003 int currentLevel = g_pApp->GetGameLogic()->GetCurrentLevelData()->GetLevelNumber();
1004
1005 // Sound should be always like this
1006 deathSound = "/LEVEL" + ToStr(currentLevel) + "/SOUNDS/DEATHTILE.WAV";
1007
1008 // Check if we fell into some stuff (tar, water) - if yes, hide Claw's body since he is underwater
1009 // or something similar
1010 if (ClawLevelUtil::CreateSpecialDeathEffect(m_pPositionComponent->GetPosition(), currentLevel) != nullptr)
1011 {
1012 m_pRenderComponent->SetVisible(false);
1013 }
1014 }
1015
1016 SoundInfo soundInfo(deathSound);
1017 IEventMgr::Get()->VTriggerEvent(IEventDataPtr(
1018 new EventData_Request_Play_Sound(soundInfo)));
1019 }
1020
1021 if (m_bIsInBossFight)
1022 {
1023 IEventMgr::Get()->VQueueEvent(IEventDataPtr(new EventData_Boss_Fight_Ended(false)));
1024 }
1025 }
1026
VOnHealthChanged(int32 oldHealth,int32 newHealth,DamageType damageType,Point impactPoint,int sourceActorId)1027 void ClawControllableComponent::VOnHealthChanged(int32 oldHealth, int32 newHealth, DamageType damageType, Point impactPoint, int sourceActorId)
1028 {
1029 // When claw takes damage but does not actually die
1030 if (newHealth > 0 && oldHealth > newHealth)
1031 {
1032
1033 // When Claw is holding rope his animation does not change
1034 if (m_State != ClawState_HoldingRope)
1035 {
1036 if (Util::GetRandomNumber(0, 1) == 0)
1037 {
1038 m_pClawAnimationComponent->SetAnimation("damage1");
1039 }
1040 else
1041 {
1042 m_pClawAnimationComponent->SetAnimation("damage2");
1043 }
1044 }
1045
1046 // Play random "take damage" sound
1047 int takeDamageSoundIdx = Util::GetRandomNumber(0, m_TakeDamageSoundList.size() - 1);
1048 SoundInfo soundInfo(m_TakeDamageSoundList[takeDamageSoundIdx]);
1049 IEventMgr::Get()->VTriggerEvent(IEventDataPtr(
1050 new EventData_Request_Play_Sound(soundInfo)));
1051
1052 Point knockback(-10, 0);
1053 if (m_pRenderComponent->IsMirrored())
1054 {
1055 knockback = Point(knockback.x * -1.0, knockback.y);
1056 }
1057
1058 // TODO: How to make this work well with enemy damage auras ?
1059 /*m_pPositionComponent->SetPosition(m_pPositionComponent->GetX() + knockback.x, m_pPositionComponent->GetY() + knockback.y);
1060
1061 shared_ptr<EventData_Teleport_Actor> pEvent(new EventData_Teleport_Actor
1062 (m_pOwner->GetGUID(), m_pPositionComponent->GetPosition()));
1063 IEventMgr::Get()->VQueueEvent(pEvent);*/
1064
1065 m_pHealthComponent->SetInvulnerable(true);
1066 m_TakeDamageTimeLeftMs = m_TakeDamageDuration;
1067 m_pPhysicsComponent->SetGravityScale(0.0f);
1068
1069 if (m_State != ClawState_HoldingRope)
1070 {
1071 m_State = ClawState_TakingDamage;
1072 }
1073
1074 // Spawn graphics in the hit point
1075 ActorTemplates::CreateSingleAnimation(impactPoint, AnimationType_BlueHitPoint);
1076 Util::PlayRandomHitSound();
1077 }
1078 }
1079
IsDucking()1080 bool ClawControllableComponent::IsDucking()
1081 {
1082 return (m_State == ClawState_Ducking ||
1083 m_State == ClawState_DuckAttacking ||
1084 m_State == ClawState_DuckShooting);
1085 }
1086
OnDuck()1087 void ClawControllableComponent::OnDuck()
1088 {
1089 if (!IsDucking())
1090 {
1091 m_pClawAnimationComponent->SetAnimation("duck");
1092 m_State = ClawState_Ducking;
1093 }
1094 }
1095
OnStand()1096 void ClawControllableComponent::OnStand()
1097 {
1098 m_State = ClawState_Standing;
1099 SetCurrentPhysicsState();
1100 }
1101
IsClimbing()1102 bool ClawControllableComponent::IsClimbing()
1103 {
1104 return m_pClawAnimationComponent->GetCurrentAnimationName().find("climb") != std::string::npos &&
1105 !m_pClawAnimationComponent->GetCurrentAnimation()->IsPaused();
1106 }
1107
VOnAttachedToRope()1108 void ClawControllableComponent::VOnAttachedToRope()
1109 {
1110 m_pClawAnimationComponent->SetAnimation("swing");
1111 m_pPhysicsComponent->SetGravityScale(0.0f);
1112
1113 m_State = ClawState_HoldingRope;
1114
1115 m_pPhysicsComponent->OnAttachedToRope();
1116 }
1117
VDetachFromRope()1118 void ClawControllableComponent::VDetachFromRope()
1119 {
1120 m_State = ClawState_Jumping;
1121 m_pPhysicsComponent->RestoreGravityScale();
1122
1123 SetCurrentPhysicsState();
1124
1125 m_pPhysicsComponent->OnDetachedFromRope();
1126 }
1127
BossFightStartedDelegate(IEventDataPtr pEvent)1128 void ClawControllableComponent::BossFightStartedDelegate(IEventDataPtr pEvent)
1129 {
1130 m_TakeDamageDuration = 500;
1131
1132 m_bIsInBossFight = true;
1133 }
1134
BossFightEndedDelegate(IEventDataPtr pEvent)1135 void ClawControllableComponent::BossFightEndedDelegate(IEventDataPtr pEvent)
1136 {
1137 m_TakeDamageDuration = 500;
1138
1139 m_bIsInBossFight = false;
1140 }
1141
OnClawKilledEnemy(DamageType killDamageType,Actor * pKilledEnemyActor)1142 void ClawControllableComponent::OnClawKilledEnemy(DamageType killDamageType, Actor* pKilledEnemyActor)
1143 {
1144 assert(pKilledEnemyActor != NULL);
1145
1146 static std::vector<std::string> s_OnEnemyKillSoundList =
1147 {
1148 SOUND_CLAW_KILL_MELEE1,
1149 SOUND_CLAW_KILL_MELEE2,
1150 SOUND_CLAW_KILL_MELEE3,
1151 SOUND_CLAW_KILL_MELEE4,
1152 SOUND_CLAW_KILL_MELEE5,
1153 SOUND_CLAW_KILL_MELEE6,
1154 SOUND_CLAW_LAND_LOVER1_SHORT,
1155 SOUND_CLAW_LAND_LOVER2_LONG
1156 };
1157
1158 int soundPlayChance = 36;
1159 if ((killDamageType == DamageType_MeleeAttack) &&
1160 !m_pExclamationMark->IsActive() &&
1161 Util::RollDice(soundPlayChance))
1162 {
1163 std::string snd = Util::PlayRandomSoundFromList(s_OnEnemyKillSoundList, 260);
1164 m_pExclamationMark->Activate(Util::GetSoundDurationMs(snd));
1165 }
1166 }