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 }