Zhengyupeng commited on
Commit
8d44bc8
·
verified ·
1 Parent(s): 22097ff

Upload folder using huggingface_hub

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. Source/Pockmon.Target.cs +15 -0
  2. Source/Pockmon/Pockmon.Build.cs +51 -0
  3. Source/Pockmon/Pockmon.cpp +8 -0
  4. Source/Pockmon/Pockmon.h +8 -0
  5. Source/Pockmon/PockmonCharacter.cpp +133 -0
  6. Source/Pockmon/PockmonCharacter.h +96 -0
  7. Source/Pockmon/PockmonGameMode.cpp +8 -0
  8. Source/Pockmon/PockmonGameMode.h +24 -0
  9. Source/Pockmon/PockmonPlayerController.cpp +67 -0
  10. Source/Pockmon/PockmonPlayerController.h +52 -0
  11. Source/Pockmon/Variant_Combat/AI/CombatAIController.cpp +19 -0
  12. Source/Pockmon/Variant_Combat/AI/CombatAIController.h +27 -0
  13. Source/Pockmon/Variant_Combat/AI/CombatEnemy.cpp +343 -0
  14. Source/Pockmon/Variant_Combat/AI/CombatEnemy.h +232 -0
  15. Source/Pockmon/Variant_Combat/AI/CombatEnemySpawner.cpp +126 -0
  16. Source/Pockmon/Variant_Combat/AI/CombatEnemySpawner.h +109 -0
  17. Source/Pockmon/Variant_Combat/AI/CombatStateTreeUtility.cpp +325 -0
  18. Source/Pockmon/Variant_Combat/AI/CombatStateTreeUtility.h +365 -0
  19. Source/Pockmon/Variant_Combat/AI/EnvQueryContext_Danger.cpp +17 -0
  20. Source/Pockmon/Variant_Combat/AI/EnvQueryContext_Danger.h +23 -0
  21. Source/Pockmon/Variant_Combat/AI/EnvQueryContext_Player.cpp +18 -0
  22. Source/Pockmon/Variant_Combat/AI/EnvQueryContext_Player.h +22 -0
  23. Source/Pockmon/Variant_Combat/Animation/AnimNotify_CheckChargedAttack.cpp +21 -0
  24. Source/Pockmon/Variant_Combat/Animation/AnimNotify_CheckChargedAttack.h +24 -0
  25. Source/Pockmon/Variant_Combat/Animation/AnimNotify_CheckCombo.cpp +21 -0
  26. Source/Pockmon/Variant_Combat/Animation/AnimNotify_CheckCombo.h +24 -0
  27. Source/Pockmon/Variant_Combat/Animation/AnimNotify_DoAttackTrace.cpp +20 -0
  28. Source/Pockmon/Variant_Combat/Animation/AnimNotify_DoAttackTrace.h +30 -0
  29. Source/Pockmon/Variant_Combat/CombatCharacter.cpp +547 -0
  30. Source/Pockmon/Variant_Combat/CombatCharacter.h +334 -0
  31. Source/Pockmon/Variant_Combat/CombatGameMode.cpp +9 -0
  32. Source/Pockmon/Variant_Combat/CombatGameMode.h +20 -0
  33. Source/Pockmon/Variant_Combat/CombatPlayerController.cpp +95 -0
  34. Source/Pockmon/Variant_Combat/CombatPlayerController.h +76 -0
  35. Source/Pockmon/Variant_Combat/Gameplay/CombatActivationVolume.cpp +49 -0
  36. Source/Pockmon/Variant_Combat/Gameplay/CombatActivationVolume.h +40 -0
  37. Source/Pockmon/Variant_Combat/Gameplay/CombatCheckpointVolume.cpp +47 -0
  38. Source/Pockmon/Variant_Combat/Gameplay/CombatCheckpointVolume.h +32 -0
  39. Source/Pockmon/Variant_Combat/Gameplay/CombatDamageableBox.cpp +83 -0
  40. Source/Pockmon/Variant_Combat/Gameplay/CombatDamageableBox.h +71 -0
  41. Source/Pockmon/Variant_Combat/Gameplay/CombatDummy.cpp +56 -0
  42. Source/Pockmon/Variant_Combat/Gameplay/CombatDummy.h +63 -0
  43. Source/Pockmon/Variant_Combat/Gameplay/CombatLavaFloor.cpp +27 -0
  44. Source/Pockmon/Variant_Combat/Gameplay/CombatLavaFloor.h +40 -0
  45. Source/Pockmon/Variant_Combat/Interfaces/CombatActivatable.cpp +4 -0
  46. Source/Pockmon/Variant_Combat/Interfaces/CombatActivatable.h +36 -0
  47. Source/Pockmon/Variant_Combat/Interfaces/CombatAttacker.cpp +4 -0
  48. Source/Pockmon/Variant_Combat/Interfaces/CombatAttacker.h +36 -0
  49. Source/Pockmon/Variant_Combat/Interfaces/CombatDamageable.cpp +6 -0
  50. Source/Pockmon/Variant_Combat/Interfaces/CombatDamageable.h +41 -0
Source/Pockmon.Target.cs ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ using UnrealBuildTool;
4
+ using System.Collections.Generic;
5
+
6
+ public class PockmonTarget : TargetRules
7
+ {
8
+ public PockmonTarget(TargetInfo Target) : base(Target)
9
+ {
10
+ Type = TargetType.Game;
11
+ DefaultBuildSettings = BuildSettingsVersion.V6;
12
+ IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_7;
13
+ ExtraModuleNames.Add("Pockmon");
14
+ }
15
+ }
Source/Pockmon/Pockmon.Build.cs ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ using UnrealBuildTool;
4
+
5
+ public class Pockmon : ModuleRules
6
+ {
7
+ public Pockmon(ReadOnlyTargetRules Target) : base(Target)
8
+ {
9
+ PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
10
+
11
+ PublicDependencyModuleNames.AddRange(new string[] {
12
+ "Core",
13
+ "CoreUObject",
14
+ "Engine",
15
+ "InputCore",
16
+ "EnhancedInput",
17
+ "AIModule",
18
+ "StateTreeModule",
19
+ "GameplayStateTreeModule",
20
+ "UMG",
21
+ "Slate"
22
+ });
23
+
24
+ PrivateDependencyModuleNames.AddRange(new string[] { });
25
+
26
+ PublicIncludePaths.AddRange(new string[] {
27
+ "Pockmon",
28
+ "Pockmon/Variant_Platforming",
29
+ "Pockmon/Variant_Platforming/Animation",
30
+ "Pockmon/Variant_Combat",
31
+ "Pockmon/Variant_Combat/AI",
32
+ "Pockmon/Variant_Combat/Animation",
33
+ "Pockmon/Variant_Combat/Gameplay",
34
+ "Pockmon/Variant_Combat/Interfaces",
35
+ "Pockmon/Variant_Combat/UI",
36
+ "Pockmon/Variant_SideScrolling",
37
+ "Pockmon/Variant_SideScrolling/AI",
38
+ "Pockmon/Variant_SideScrolling/Gameplay",
39
+ "Pockmon/Variant_SideScrolling/Interfaces",
40
+ "Pockmon/Variant_SideScrolling/UI"
41
+ });
42
+
43
+ // Uncomment if you are using Slate UI
44
+ // PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
45
+
46
+ // Uncomment if you are using online features
47
+ // PrivateDependencyModuleNames.Add("OnlineSubsystem");
48
+
49
+ // To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
50
+ }
51
+ }
Source/Pockmon/Pockmon.cpp ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #include "Pockmon.h"
4
+ #include "Modules/ModuleManager.h"
5
+
6
+ IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, Pockmon, "Pockmon" );
7
+
8
+ DEFINE_LOG_CATEGORY(LogPockmon)
Source/Pockmon/Pockmon.h ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #pragma once
4
+
5
+ #include "CoreMinimal.h"
6
+
7
+ /** Main log category used across the project */
8
+ DECLARE_LOG_CATEGORY_EXTERN(LogPockmon, Log, All);
Source/Pockmon/PockmonCharacter.cpp ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #include "PockmonCharacter.h"
4
+ #include "Engine/LocalPlayer.h"
5
+ #include "Camera/CameraComponent.h"
6
+ #include "Components/CapsuleComponent.h"
7
+ #include "GameFramework/CharacterMovementComponent.h"
8
+ #include "GameFramework/SpringArmComponent.h"
9
+ #include "GameFramework/Controller.h"
10
+ #include "EnhancedInputComponent.h"
11
+ #include "EnhancedInputSubsystems.h"
12
+ #include "InputActionValue.h"
13
+ #include "Pockmon.h"
14
+
15
+ APockmonCharacter::APockmonCharacter()
16
+ {
17
+ // Set size for collision capsule
18
+ GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);
19
+
20
+ // Don't rotate when the controller rotates. Let that just affect the camera.
21
+ bUseControllerRotationPitch = false;
22
+ bUseControllerRotationYaw = false;
23
+ bUseControllerRotationRoll = false;
24
+
25
+ // Configure character movement
26
+ GetCharacterMovement()->bOrientRotationToMovement = true;
27
+ GetCharacterMovement()->RotationRate = FRotator(0.0f, 500.0f, 0.0f);
28
+
29
+ // Note: For faster iteration times these variables, and many more, can be tweaked in the Character Blueprint
30
+ // instead of recompiling to adjust them
31
+ GetCharacterMovement()->JumpZVelocity = 500.f;
32
+ GetCharacterMovement()->AirControl = 0.35f;
33
+ GetCharacterMovement()->MaxWalkSpeed = 500.f;
34
+ GetCharacterMovement()->MinAnalogWalkSpeed = 20.f;
35
+ GetCharacterMovement()->BrakingDecelerationWalking = 2000.f;
36
+ GetCharacterMovement()->BrakingDecelerationFalling = 1500.0f;
37
+
38
+ // Create a camera boom (pulls in towards the player if there is a collision)
39
+ CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
40
+ CameraBoom->SetupAttachment(RootComponent);
41
+ CameraBoom->TargetArmLength = 400.0f;
42
+ CameraBoom->bUsePawnControlRotation = true;
43
+
44
+ // Create a follow camera
45
+ FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
46
+ FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);
47
+ FollowCamera->bUsePawnControlRotation = false;
48
+
49
+ // Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character)
50
+ // are set in the derived blueprint asset named ThirdPersonCharacter (to avoid direct content references in C++)
51
+ }
52
+
53
+ void APockmonCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
54
+ {
55
+ // Set up action bindings
56
+ if (UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent)) {
57
+
58
+ // Jumping
59
+ EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Started, this, &ACharacter::Jump);
60
+ EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping);
61
+
62
+ // Moving
63
+ EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &APockmonCharacter::Move);
64
+ EnhancedInputComponent->BindAction(MouseLookAction, ETriggerEvent::Triggered, this, &APockmonCharacter::Look);
65
+
66
+ // Looking
67
+ EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &APockmonCharacter::Look);
68
+ }
69
+ else
70
+ {
71
+ UE_LOG(LogPockmon, Error, TEXT("'%s' Failed to find an Enhanced Input component! This template is built to use the Enhanced Input system. If you intend to use the legacy system, then you will need to update this C++ file."), *GetNameSafe(this));
72
+ }
73
+ }
74
+
75
+ void APockmonCharacter::Move(const FInputActionValue& Value)
76
+ {
77
+ // input is a Vector2D
78
+ FVector2D MovementVector = Value.Get<FVector2D>();
79
+
80
+ // route the input
81
+ DoMove(MovementVector.X, MovementVector.Y);
82
+ }
83
+
84
+ void APockmonCharacter::Look(const FInputActionValue& Value)
85
+ {
86
+ // input is a Vector2D
87
+ FVector2D LookAxisVector = Value.Get<FVector2D>();
88
+
89
+ // route the input
90
+ DoLook(LookAxisVector.X, LookAxisVector.Y);
91
+ }
92
+
93
+ void APockmonCharacter::DoMove(float Right, float Forward)
94
+ {
95
+ if (GetController() != nullptr)
96
+ {
97
+ // find out which way is forward
98
+ const FRotator Rotation = GetController()->GetControlRotation();
99
+ const FRotator YawRotation(0, Rotation.Yaw, 0);
100
+
101
+ // get forward vector
102
+ const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
103
+
104
+ // get right vector
105
+ const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
106
+
107
+ // add movement
108
+ AddMovementInput(ForwardDirection, Forward);
109
+ AddMovementInput(RightDirection, Right);
110
+ }
111
+ }
112
+
113
+ void APockmonCharacter::DoLook(float Yaw, float Pitch)
114
+ {
115
+ if (GetController() != nullptr)
116
+ {
117
+ // add yaw and pitch input to controller
118
+ AddControllerYawInput(Yaw);
119
+ AddControllerPitchInput(Pitch);
120
+ }
121
+ }
122
+
123
+ void APockmonCharacter::DoJumpStart()
124
+ {
125
+ // signal the character to jump
126
+ Jump();
127
+ }
128
+
129
+ void APockmonCharacter::DoJumpEnd()
130
+ {
131
+ // signal the character to stop jumping
132
+ StopJumping();
133
+ }
Source/Pockmon/PockmonCharacter.h ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #pragma once
4
+
5
+ #include "CoreMinimal.h"
6
+ #include "GameFramework/Character.h"
7
+ #include "Logging/LogMacros.h"
8
+ #include "PockmonCharacter.generated.h"
9
+
10
+ class USpringArmComponent;
11
+ class UCameraComponent;
12
+ class UInputAction;
13
+ struct FInputActionValue;
14
+
15
+ DECLARE_LOG_CATEGORY_EXTERN(LogTemplateCharacter, Log, All);
16
+
17
+ /**
18
+ * A simple player-controllable third person character
19
+ * Implements a controllable orbiting camera
20
+ */
21
+ UCLASS(abstract)
22
+ class APockmonCharacter : public ACharacter
23
+ {
24
+ GENERATED_BODY()
25
+
26
+ /** Camera boom positioning the camera behind the character */
27
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
28
+ USpringArmComponent* CameraBoom;
29
+
30
+ /** Follow camera */
31
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
32
+ UCameraComponent* FollowCamera;
33
+
34
+ protected:
35
+
36
+ /** Jump Input Action */
37
+ UPROPERTY(EditAnywhere, Category="Input")
38
+ UInputAction* JumpAction;
39
+
40
+ /** Move Input Action */
41
+ UPROPERTY(EditAnywhere, Category="Input")
42
+ UInputAction* MoveAction;
43
+
44
+ /** Look Input Action */
45
+ UPROPERTY(EditAnywhere, Category="Input")
46
+ UInputAction* LookAction;
47
+
48
+ /** Mouse Look Input Action */
49
+ UPROPERTY(EditAnywhere, Category="Input")
50
+ UInputAction* MouseLookAction;
51
+
52
+ public:
53
+
54
+ /** Constructor */
55
+ APockmonCharacter();
56
+
57
+ protected:
58
+
59
+ /** Initialize input action bindings */
60
+ virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
61
+
62
+ protected:
63
+
64
+ /** Called for movement input */
65
+ void Move(const FInputActionValue& Value);
66
+
67
+ /** Called for looking input */
68
+ void Look(const FInputActionValue& Value);
69
+
70
+ public:
71
+
72
+ /** Handles move inputs from either controls or UI interfaces */
73
+ UFUNCTION(BlueprintCallable, Category="Input")
74
+ virtual void DoMove(float Right, float Forward);
75
+
76
+ /** Handles look inputs from either controls or UI interfaces */
77
+ UFUNCTION(BlueprintCallable, Category="Input")
78
+ virtual void DoLook(float Yaw, float Pitch);
79
+
80
+ /** Handles jump pressed inputs from either controls or UI interfaces */
81
+ UFUNCTION(BlueprintCallable, Category="Input")
82
+ virtual void DoJumpStart();
83
+
84
+ /** Handles jump pressed inputs from either controls or UI interfaces */
85
+ UFUNCTION(BlueprintCallable, Category="Input")
86
+ virtual void DoJumpEnd();
87
+
88
+ public:
89
+
90
+ /** Returns CameraBoom subobject **/
91
+ FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; }
92
+
93
+ /** Returns FollowCamera subobject **/
94
+ FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }
95
+ };
96
+
Source/Pockmon/PockmonGameMode.cpp ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #include "PockmonGameMode.h"
4
+
5
+ APockmonGameMode::APockmonGameMode()
6
+ {
7
+ // stub
8
+ }
Source/Pockmon/PockmonGameMode.h ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #pragma once
4
+
5
+ #include "CoreMinimal.h"
6
+ #include "GameFramework/GameModeBase.h"
7
+ #include "PockmonGameMode.generated.h"
8
+
9
+ /**
10
+ * Simple GameMode for a third person game
11
+ */
12
+ UCLASS(abstract)
13
+ class APockmonGameMode : public AGameModeBase
14
+ {
15
+ GENERATED_BODY()
16
+
17
+ public:
18
+
19
+ /** Constructor */
20
+ APockmonGameMode();
21
+ };
22
+
23
+
24
+
Source/Pockmon/PockmonPlayerController.cpp ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+
4
+ #include "PockmonPlayerController.h"
5
+ #include "EnhancedInputSubsystems.h"
6
+ #include "Engine/LocalPlayer.h"
7
+ #include "InputMappingContext.h"
8
+ #include "Blueprint/UserWidget.h"
9
+ #include "Pockmon.h"
10
+ #include "Widgets/Input/SVirtualJoystick.h"
11
+
12
+ void APockmonPlayerController::BeginPlay()
13
+ {
14
+ Super::BeginPlay();
15
+
16
+ // only spawn touch controls on local player controllers
17
+ if (ShouldUseTouchControls() && IsLocalPlayerController())
18
+ {
19
+ // spawn the mobile controls widget
20
+ MobileControlsWidget = CreateWidget<UUserWidget>(this, MobileControlsWidgetClass);
21
+
22
+ if (MobileControlsWidget)
23
+ {
24
+ // add the controls to the player screen
25
+ MobileControlsWidget->AddToPlayerScreen(0);
26
+
27
+ } else {
28
+
29
+ UE_LOG(LogPockmon, Error, TEXT("Could not spawn mobile controls widget."));
30
+
31
+ }
32
+
33
+ }
34
+ }
35
+
36
+ void APockmonPlayerController::SetupInputComponent()
37
+ {
38
+ Super::SetupInputComponent();
39
+
40
+ // only add IMCs for local player controllers
41
+ if (IsLocalPlayerController())
42
+ {
43
+ // Add Input Mapping Contexts
44
+ if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer()))
45
+ {
46
+ for (UInputMappingContext* CurrentContext : DefaultMappingContexts)
47
+ {
48
+ Subsystem->AddMappingContext(CurrentContext, 0);
49
+ }
50
+
51
+ // only add these IMCs if we're not using mobile touch input
52
+ if (!ShouldUseTouchControls())
53
+ {
54
+ for (UInputMappingContext* CurrentContext : MobileExcludedMappingContexts)
55
+ {
56
+ Subsystem->AddMappingContext(CurrentContext, 0);
57
+ }
58
+ }
59
+ }
60
+ }
61
+ }
62
+
63
+ bool APockmonPlayerController::ShouldUseTouchControls() const
64
+ {
65
+ // are we on a mobile platform? Should we force touch?
66
+ return SVirtualJoystick::ShouldDisplayTouchInterface() || bForceTouchControls;
67
+ }
Source/Pockmon/PockmonPlayerController.h ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #pragma once
4
+
5
+ #include "CoreMinimal.h"
6
+ #include "GameFramework/PlayerController.h"
7
+ #include "PockmonPlayerController.generated.h"
8
+
9
+ class UInputMappingContext;
10
+ class UUserWidget;
11
+
12
+ /**
13
+ * Basic PlayerController class for a third person game
14
+ * Manages input mappings
15
+ */
16
+ UCLASS(abstract)
17
+ class APockmonPlayerController : public APlayerController
18
+ {
19
+ GENERATED_BODY()
20
+
21
+ protected:
22
+
23
+ /** Input Mapping Contexts */
24
+ UPROPERTY(EditAnywhere, Category ="Input|Input Mappings")
25
+ TArray<UInputMappingContext*> DefaultMappingContexts;
26
+
27
+ /** Input Mapping Contexts */
28
+ UPROPERTY(EditAnywhere, Category="Input|Input Mappings")
29
+ TArray<UInputMappingContext*> MobileExcludedMappingContexts;
30
+
31
+ /** Mobile controls widget to spawn */
32
+ UPROPERTY(EditAnywhere, Category="Input|Touch Controls")
33
+ TSubclassOf<UUserWidget> MobileControlsWidgetClass;
34
+
35
+ /** Pointer to the mobile controls widget */
36
+ UPROPERTY()
37
+ TObjectPtr<UUserWidget> MobileControlsWidget;
38
+
39
+ /** If true, the player will use UMG touch controls even if not playing on mobile platforms */
40
+ UPROPERTY(EditAnywhere, Config, Category = "Input|Touch Controls")
41
+ bool bForceTouchControls = false;
42
+
43
+ /** Gameplay initialization */
44
+ virtual void BeginPlay() override;
45
+
46
+ /** Input mapping context setup */
47
+ virtual void SetupInputComponent() override;
48
+
49
+ /** Returns true if the player should use UMG touch controls */
50
+ bool ShouldUseTouchControls() const;
51
+
52
+ };
Source/Pockmon/Variant_Combat/AI/CombatAIController.cpp ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+
4
+ #include "CombatAIController.h"
5
+ #include "Components/StateTreeAIComponent.h"
6
+
7
+ ACombatAIController::ACombatAIController()
8
+ {
9
+ // create the StateTree AI Component
10
+ StateTreeAI = CreateDefaultSubobject<UStateTreeAIComponent>(TEXT("StateTreeAI"));
11
+ check(StateTreeAI);
12
+
13
+ // ensure we start the StateTree when we possess the pawn
14
+ bStartAILogicOnPossess = true;
15
+
16
+ // ensure we're attached to the possessed character.
17
+ // this is necessary for EnvQueries to work correctly
18
+ bAttachToPawn = true;
19
+ }
Source/Pockmon/Variant_Combat/AI/CombatAIController.h ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #pragma once
4
+
5
+ #include "CoreMinimal.h"
6
+ #include "AIController.h"
7
+ #include "CombatAIController.generated.h"
8
+
9
+ class UStateTreeAIComponent;
10
+
11
+ /**
12
+ * A basic AI Controller capable of running StateTree
13
+ */
14
+ UCLASS(abstract)
15
+ class ACombatAIController : public AAIController
16
+ {
17
+ GENERATED_BODY()
18
+
19
+ /** StateTree Component */
20
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
21
+ UStateTreeAIComponent* StateTreeAI;
22
+
23
+ public:
24
+
25
+ /** Constructor */
26
+ ACombatAIController();
27
+ };
Source/Pockmon/Variant_Combat/AI/CombatEnemy.cpp ADDED
@@ -0,0 +1,343 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+
4
+ #include "CombatEnemy.h"
5
+ #include "Components/CapsuleComponent.h"
6
+ #include "GameFramework/CharacterMovementComponent.h"
7
+ #include "CombatAIController.h"
8
+ #include "Components/WidgetComponent.h"
9
+ #include "Engine/DamageEvents.h"
10
+ #include "CombatLifeBar.h"
11
+ #include "TimerManager.h"
12
+ #include "Components/SkeletalMeshComponent.h"
13
+ #include "Animation/AnimInstance.h"
14
+
15
+ ACombatEnemy::ACombatEnemy()
16
+ {
17
+ PrimaryActorTick.bCanEverTick = true;
18
+
19
+ // bind the attack montage ended delegate
20
+ OnAttackMontageEnded.BindUObject(this, &ACombatEnemy::AttackMontageEnded);
21
+
22
+ // set the AI Controller class by default
23
+ AIControllerClass = ACombatAIController::StaticClass();
24
+
25
+ // use an AI Controller regardless of whether we're placed or spawned
26
+ AutoPossessAI = EAutoPossessAI::PlacedInWorldOrSpawned;
27
+
28
+ // ignore the controller's yaw rotation
29
+ bUseControllerRotationYaw = false;
30
+
31
+ // create the life bar
32
+ LifeBar = CreateDefaultSubobject<UWidgetComponent>(TEXT("LifeBar"));
33
+ LifeBar->SetupAttachment(RootComponent);
34
+
35
+ // set the collision capsule size
36
+ GetCapsuleComponent()->SetCapsuleSize(35.0f, 90.0f);
37
+
38
+ // set the character movement properties
39
+ GetCharacterMovement()->bUseControllerDesiredRotation = true;
40
+
41
+ // reset HP to maximum
42
+ CurrentHP = MaxHP;
43
+ }
44
+
45
+ void ACombatEnemy::DoAIComboAttack()
46
+ {
47
+ // ignore if we're already playing an attack animation
48
+ if (bIsAttacking)
49
+ {
50
+ return;
51
+ }
52
+
53
+ // raise the attacking flag
54
+ bIsAttacking = true;
55
+
56
+ // choose how many times we're going to attack
57
+ TargetComboCount = FMath::RandRange(1, ComboSectionNames.Num() - 1);
58
+
59
+ // reset the attack counter
60
+ CurrentComboAttack = 0;
61
+
62
+ // play the attack montage
63
+ if (UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance())
64
+ {
65
+ const float MontageLength = AnimInstance->Montage_Play(ComboAttackMontage, 1.0f, EMontagePlayReturnType::MontageLength, 0.0f, true);
66
+
67
+ // subscribe to montage completed and interrupted events
68
+ if (MontageLength > 0.0f)
69
+ {
70
+ // set the end delegate for the montage
71
+ AnimInstance->Montage_SetEndDelegate(OnAttackMontageEnded, ComboAttackMontage);
72
+ }
73
+ }
74
+ }
75
+
76
+ void ACombatEnemy::DoAIChargedAttack()
77
+ {
78
+ // ignore if we're already playing an attack animation
79
+ if (bIsAttacking)
80
+ {
81
+ return;
82
+ }
83
+
84
+ // raise the attacking flag
85
+ bIsAttacking = true;
86
+
87
+ // choose how many loops are we going to charge for
88
+ TargetChargeLoops = FMath::RandRange(MinChargeLoops, MaxChargeLoops);
89
+
90
+ // reset the charge loop counter
91
+ CurrentChargeLoop = 0;
92
+
93
+ // play the attack montage
94
+ if (UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance())
95
+ {
96
+ const float MontageLength = AnimInstance->Montage_Play(ChargedAttackMontage, 1.0f, EMontagePlayReturnType::MontageLength, 0.0f, true);
97
+
98
+ // subscribe to montage completed and interrupted events
99
+ if (MontageLength > 0.0f)
100
+ {
101
+ // set the end delegate for the montage
102
+ AnimInstance->Montage_SetEndDelegate(OnAttackMontageEnded, ChargedAttackMontage);
103
+ }
104
+ }
105
+ }
106
+
107
+ void ACombatEnemy::AttackMontageEnded(UAnimMontage* Montage, bool bInterrupted)
108
+ {
109
+ // reset the attacking flag
110
+ bIsAttacking = false;
111
+
112
+ // call the attack completed delegate so the StateTree can continue execution
113
+ OnAttackCompleted.ExecuteIfBound();
114
+ }
115
+
116
+ const FVector& ACombatEnemy::GetLastDangerLocation() const
117
+ {
118
+ return LastDangerLocation;
119
+ }
120
+
121
+ float ACombatEnemy::GetLastDangerTime() const
122
+ {
123
+ return LastDangerTime;
124
+ }
125
+
126
+ void ACombatEnemy::DoAttackTrace(FName DamageSourceBone)
127
+ {
128
+ // sweep for objects in front of the character to be hit by the attack
129
+ TArray<FHitResult> OutHits;
130
+
131
+ // start at the provided socket location, sweep forward
132
+ const FVector TraceStart = GetMesh()->GetSocketLocation(DamageSourceBone);
133
+ const FVector TraceEnd = TraceStart + (GetActorForwardVector() * MeleeTraceDistance);
134
+
135
+ // enemies only affect Pawn collision objects; they don't knock back boxes
136
+ FCollisionObjectQueryParams ObjectParams;
137
+ ObjectParams.AddObjectTypesToQuery(ECC_Pawn);
138
+
139
+ // use a sphere shape for the sweep
140
+ FCollisionShape CollisionShape;
141
+ CollisionShape.SetSphere(MeleeTraceRadius);
142
+
143
+ // ignore self
144
+ FCollisionQueryParams QueryParams;
145
+ QueryParams.AddIgnoredActor(this);
146
+
147
+ if (GetWorld()->SweepMultiByObjectType(OutHits, TraceStart, TraceEnd, FQuat::Identity, ObjectParams, CollisionShape, QueryParams))
148
+ {
149
+ // iterate over each object hit
150
+ for (const FHitResult& CurrentHit : OutHits)
151
+ {
152
+ /** does the actor have the player tag? */
153
+ if (CurrentHit.GetActor()->ActorHasTag(FName("Player")))
154
+ {
155
+ // check if the actor is damageable
156
+ ICombatDamageable* Damageable = Cast<ICombatDamageable>(CurrentHit.GetActor());
157
+
158
+ if (Damageable)
159
+ {
160
+ // knock upwards and away from the impact normal
161
+ const FVector Impulse = (CurrentHit.ImpactNormal * -MeleeKnockbackImpulse) + (FVector::UpVector * MeleeLaunchImpulse);
162
+
163
+ // pass the damage event to the actor
164
+ Damageable->ApplyDamage(MeleeDamage, this, CurrentHit.ImpactPoint, Impulse);
165
+
166
+ }
167
+ }
168
+ }
169
+ }
170
+ }
171
+
172
+ void ACombatEnemy::CheckCombo()
173
+ {
174
+ // increase the combo counter
175
+ ++CurrentComboAttack;
176
+
177
+ // do we still have attacks to play in this string?
178
+ if (CurrentComboAttack < TargetComboCount)
179
+ {
180
+ // jump to the next attack section
181
+ if (UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance())
182
+ {
183
+ AnimInstance->Montage_JumpToSection(ComboSectionNames[CurrentComboAttack], ComboAttackMontage);
184
+ }
185
+ }
186
+ }
187
+
188
+ void ACombatEnemy::CheckChargedAttack()
189
+ {
190
+ // increase the charge loop counter
191
+ ++CurrentChargeLoop;
192
+
193
+ // jump to either the loop or attack section of the montage depending on whether we hit the loop target
194
+ if (UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance())
195
+ {
196
+ AnimInstance->Montage_JumpToSection(CurrentChargeLoop >= TargetChargeLoops ? ChargeAttackSection : ChargeLoopSection, ChargedAttackMontage);
197
+ }
198
+ }
199
+
200
+ void ACombatEnemy::ApplyDamage(float Damage, AActor* DamageCauser, const FVector& DamageLocation, const FVector& DamageImpulse)
201
+ {
202
+
203
+ // pass the damage event to the actor
204
+ FDamageEvent DamageEvent;
205
+ const float ActualDamage = TakeDamage(Damage, DamageEvent, nullptr, DamageCauser);
206
+
207
+ // only process knockback and effects if we received nonzero damage
208
+ if (ActualDamage > 0.0f)
209
+ {
210
+ // apply the knockback impulse
211
+ GetCharacterMovement()->AddImpulse(DamageImpulse, true);
212
+
213
+ // is the character ragdolling?
214
+ if (GetMesh()->IsSimulatingPhysics())
215
+ {
216
+ // apply an impulse to the ragdoll
217
+ GetMesh()->AddImpulseAtLocation(DamageImpulse * GetMesh()->GetMass(), DamageLocation);
218
+ }
219
+
220
+ // stop the attack montages to interrupt the attack
221
+ if (UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance())
222
+ {
223
+ AnimInstance->Montage_Stop(0.1f, ComboAttackMontage);
224
+ AnimInstance->Montage_Stop(0.1f, ChargedAttackMontage);
225
+ }
226
+
227
+ // pass control to BP to play effects, etc.
228
+ ReceivedDamage(ActualDamage, DamageLocation, DamageImpulse.GetSafeNormal());
229
+ }
230
+ }
231
+
232
+ void ACombatEnemy::HandleDeath()
233
+ {
234
+ // hide the life bar
235
+ LifeBar->SetHiddenInGame(true);
236
+
237
+ // disable the collision capsule to avoid being hit again while dead
238
+ GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision);
239
+
240
+ // disable character movement
241
+ GetCharacterMovement()->DisableMovement();
242
+
243
+ // enable full ragdoll physics
244
+ GetMesh()->SetSimulatePhysics(true);
245
+
246
+ // call the died delegate to notify any subscribers
247
+ OnEnemyDied.Broadcast();
248
+
249
+ // set up the death timer
250
+ GetWorld()->GetTimerManager().SetTimer(DeathTimer, this, &ACombatEnemy::RemoveFromLevel, DeathRemovalTime);
251
+ }
252
+
253
+ void ACombatEnemy::ApplyHealing(float Healing, AActor* Healer)
254
+ {
255
+ // stub
256
+ }
257
+
258
+ void ACombatEnemy::NotifyDanger(const FVector& DangerLocation, AActor* DangerSource)
259
+ {
260
+ // ensure we're being attacked by the player
261
+ if (DangerSource && DangerSource->ActorHasTag(FName("Player")))
262
+ {
263
+ // save the danger location and game time
264
+ LastDangerLocation = DangerLocation;
265
+ LastDangerTime = GetWorld()->GetTimeSeconds();
266
+ }
267
+ }
268
+
269
+ void ACombatEnemy::RemoveFromLevel()
270
+ {
271
+ // destroy this actor
272
+ Destroy();
273
+ }
274
+
275
+ float ACombatEnemy::TakeDamage(float Damage, struct FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
276
+ {
277
+ // only process damage if the character is still alive
278
+ if (CurrentHP <= 0.0f)
279
+ {
280
+ return 0.0f;
281
+ }
282
+
283
+ // reduce the current HP
284
+ CurrentHP -= Damage;
285
+
286
+ // have we run out of HP?
287
+ if (CurrentHP <= 0.0f)
288
+ {
289
+ // die
290
+ HandleDeath();
291
+ }
292
+ else
293
+ {
294
+ // update the life bar
295
+ LifeBarWidget->SetLifePercentage(CurrentHP / MaxHP);
296
+
297
+ // enable partial ragdoll physics, but keep the pelvis vertical
298
+ GetMesh()->SetPhysicsBlendWeight(0.5f);
299
+ GetMesh()->SetBodySimulatePhysics(PelvisBoneName, false);
300
+ }
301
+
302
+ // return the received damage amount
303
+ return Damage;
304
+ }
305
+
306
+ void ACombatEnemy::Landed(const FHitResult& Hit)
307
+ {
308
+ Super::Landed(Hit);
309
+
310
+ // is the character still alive?
311
+ if (CurrentHP >= 0.0f)
312
+ {
313
+ // disable ragdoll physics
314
+ GetMesh()->SetPhysicsBlendWeight(0.0f);
315
+ }
316
+
317
+ // call the landed Delegate for StateTree
318
+ OnEnemyLanded.ExecuteIfBound();
319
+ }
320
+
321
+ void ACombatEnemy::BeginPlay()
322
+ {
323
+ // reset HP to maximum
324
+ CurrentHP = MaxHP;
325
+
326
+ // we top the HP before BeginPlay so StateTree picks it up at the right value
327
+ Super::BeginPlay();
328
+
329
+ // get the life bar widget from the widget comp
330
+ LifeBarWidget = Cast<UCombatLifeBar>(LifeBar->GetUserWidgetObject());
331
+ check(LifeBarWidget);
332
+
333
+ // fill the life bar
334
+ LifeBarWidget->SetLifePercentage(1.0f);
335
+ }
336
+
337
+ void ACombatEnemy::EndPlay(EEndPlayReason::Type EndPlayReason)
338
+ {
339
+ Super::EndPlay(EndPlayReason);
340
+
341
+ // clear the death timer
342
+ GetWorld()->GetTimerManager().ClearTimer(DeathTimer);
343
+ }
Source/Pockmon/Variant_Combat/AI/CombatEnemy.h ADDED
@@ -0,0 +1,232 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #pragma once
4
+
5
+ #include "CoreMinimal.h"
6
+ #include "GameFramework/Character.h"
7
+ #include "CombatAttacker.h"
8
+ #include "CombatDamageable.h"
9
+ #include "Animation/AnimMontage.h"
10
+ #include "Engine/TimerHandle.h"
11
+ #include "CombatEnemy.generated.h"
12
+
13
+ class UWidgetComponent;
14
+ class UCombatLifeBar;
15
+ class UAnimMontage;
16
+
17
+ /** Completed attack animation delegate for StateTree */
18
+ DECLARE_DELEGATE(FOnEnemyAttackCompleted);
19
+
20
+ /** Landed delegate for StateTree */
21
+ DECLARE_DELEGATE(FOnEnemyLanded);
22
+
23
+ /** Enemy died delegate */
24
+ DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnEnemyDied);
25
+
26
+ /**
27
+ * An AI-controlled character with combat capabilities.
28
+ * Its bundled AI Controller runs logic through StateTree
29
+ */
30
+ UCLASS(abstract)
31
+ class ACombatEnemy : public ACharacter, public ICombatAttacker, public ICombatDamageable
32
+ {
33
+ GENERATED_BODY()
34
+
35
+ /** Life bar widget component */
36
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
37
+ UWidgetComponent* LifeBar;
38
+
39
+ public:
40
+
41
+ /** Constructor */
42
+ ACombatEnemy();
43
+
44
+ protected:
45
+
46
+ /** Max amount of HP the character will have on respawn */
47
+ UPROPERTY(EditAnywhere, Category="Damage")
48
+ float MaxHP = 3.0f;
49
+
50
+ public:
51
+
52
+ /** Current amount of HP the character has */
53
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Damage", meta = (ClampMin = 0, ClampMax = 100))
54
+ float CurrentHP = 0.0f;
55
+
56
+ protected:
57
+
58
+ /** Name of the pelvis bone, for damage ragdoll physics */
59
+ UPROPERTY(EditAnywhere, Category="Damage")
60
+ FName PelvisBoneName;
61
+
62
+ /** Pointer to the life bar widget */
63
+ UPROPERTY(EditAnywhere, Category="Damage")
64
+ UCombatLifeBar* LifeBarWidget;
65
+
66
+ /** If true, the character is currently playing an attack animation */
67
+ bool bIsAttacking = false;
68
+
69
+ /** Distance ahead of the character that melee attack sphere collision traces will extend */
70
+ UPROPERTY(EditAnywhere, Category="Melee Attack|Trace", meta = (ClampMin = 0, ClampMax = 500, Units = "cm"))
71
+ float MeleeTraceDistance = 75.0f;
72
+
73
+ /** Radius of the sphere trace for melee attacks */
74
+ UPROPERTY(EditAnywhere, Category="Melee Attack|Trace", meta = (ClampMin = 0, ClampMax = 500, Units = "cm"))
75
+ float MeleeTraceRadius = 50.0f;
76
+
77
+ /** Amount of damage a melee attack will deal */
78
+ UPROPERTY(EditAnywhere, Category="Melee Attack|Damage", meta = (ClampMin = 0, ClampMax = 100))
79
+ float MeleeDamage = 1.0f;
80
+
81
+ /** Amount of knockback impulse a melee attack will apply */
82
+ UPROPERTY(EditAnywhere, Category="Melee Attack|Damage", meta = (ClampMin = 0, ClampMax = 1000, Units = "cm/s"))
83
+ float MeleeKnockbackImpulse = 150.0f;
84
+
85
+ /** Amount of upwards impulse a melee attack will apply */
86
+ UPROPERTY(EditAnywhere, Category="Melee Attack|Damage", meta = (ClampMin = 0, ClampMax = 1000, Units = "cm/s"))
87
+ float MeleeLaunchImpulse = 350.0f;
88
+
89
+ /** AnimMontage that will play for combo attacks */
90
+ UPROPERTY(EditAnywhere, Category="Melee Attack|Combo")
91
+ UAnimMontage* ComboAttackMontage;
92
+
93
+ /** Names of the AnimMontage sections that correspond to each stage of the combo attack */
94
+ UPROPERTY(EditAnywhere, Category="Melee Attack|Combo")
95
+ TArray<FName> ComboSectionNames;
96
+
97
+ /** Target number of attacks in the combo attack string we're playing */
98
+ int32 TargetComboCount = 0;
99
+
100
+ /** Index of the current stage of the melee attack combo */
101
+ int32 CurrentComboAttack = 0;
102
+
103
+ /** AnimMontage that will play for charged attacks */
104
+ UPROPERTY(EditAnywhere, Category="Melee Attack|Charged")
105
+ UAnimMontage* ChargedAttackMontage;
106
+
107
+ /** Name of the AnimMontage section that corresponds to the charge loop */
108
+ UPROPERTY(EditAnywhere, Category="Melee Attack|Charged")
109
+ FName ChargeLoopSection;
110
+
111
+ /** Name of the AnimMontage section that corresponds to the attack */
112
+ UPROPERTY(EditAnywhere, Category="Melee Attack|Charged")
113
+ FName ChargeAttackSection;
114
+
115
+ /** Minimum number of charge animation loops that will be played by the AI */
116
+ UPROPERTY(EditAnywhere, Category="Melee Attack|Charged", meta = (ClampMin = 1, ClampMax = 20))
117
+ int32 MinChargeLoops = 2;
118
+
119
+ /** Maximum number of charge animation loops that will be played by the AI */
120
+ UPROPERTY(EditAnywhere, Category="Melee Attack|Charged", meta = (ClampMin = 1, ClampMax = 20))
121
+ int32 MaxChargeLoops = 5;
122
+
123
+ /** Target number of charge animation loops to play in this charged attack */
124
+ int32 TargetChargeLoops = 0;
125
+
126
+ /** Number of charge animation loop currently playing */
127
+ int32 CurrentChargeLoop = 0;
128
+
129
+ /** Time to wait before removing this character from the level after it dies */
130
+ UPROPERTY(EditAnywhere, Category="Death")
131
+ float DeathRemovalTime = 5.0f;
132
+
133
+ /** Enemy death timer */
134
+ FTimerHandle DeathTimer;
135
+
136
+ /** Attack montage ended delegate */
137
+ FOnMontageEnded OnAttackMontageEnded;
138
+
139
+ /** Last recorded location we're being attacked from */
140
+ FVector LastDangerLocation = FVector::ZeroVector;
141
+
142
+ /** Last recorded game time we were attacked */
143
+ float LastDangerTime = -1000.0f;
144
+
145
+ public:
146
+ /** Attack completed internal delegate to notify StateTree tasks */
147
+ FOnEnemyAttackCompleted OnAttackCompleted;
148
+
149
+ /** Landed internal delegate to notify StateTree tasks. We use this instead of the built-in Landed delegate so we can bind to a Lambda in StateTree tasks */
150
+ FOnEnemyLanded OnEnemyLanded;
151
+
152
+ /** Enemy died delegate. Allows external subscribers to respond to enemy death */
153
+ UPROPERTY(BlueprintAssignable, Category="Events")
154
+ FOnEnemyDied OnEnemyDied;
155
+
156
+ public:
157
+
158
+ /** Performs an AI-initiated combo attack. Number of hits will be decided by this character */
159
+ void DoAIComboAttack();
160
+
161
+ /** Performs an AI-initiated charged attack. Charge time will be decided by this character */
162
+ void DoAIChargedAttack();
163
+
164
+ /** Called from a delegate when the attack montage ends */
165
+ void AttackMontageEnded(UAnimMontage* Montage, bool bInterrupted);
166
+
167
+ /** Returns the last recorded location we were attacked from */
168
+ const FVector& GetLastDangerLocation() const;
169
+
170
+ /** Returns the last game time we were attacked */
171
+ float GetLastDangerTime() const;
172
+
173
+ public:
174
+
175
+ // ~begin ICombatAttacker interface
176
+
177
+ /** Performs an attack's collision check */
178
+ virtual void DoAttackTrace(FName DamageSourceBone) override;
179
+
180
+ /** Performs a combo attack's check to continue the string */
181
+ UFUNCTION(BlueprintCallable, Category="Attacker")
182
+ virtual void CheckCombo() override;
183
+
184
+ /** Performs a charged attack's check to loop the charge animation */
185
+ UFUNCTION(BlueprintCallable, Category="Attacker")
186
+ virtual void CheckChargedAttack() override;
187
+
188
+ // ~end ICombatAttacker interface
189
+
190
+ // ~begin ICombatDamageable interface
191
+
192
+ /** Handles damage and knockback events */
193
+ virtual void ApplyDamage(float Damage, AActor* DamageCauser, const FVector& DamageLocation, const FVector& DamageImpulse) override;
194
+
195
+ /** Handles death events */
196
+ virtual void HandleDeath() override;
197
+
198
+ /** Handles healing events */
199
+ virtual void ApplyHealing(float Healing, AActor* Healer) override;
200
+
201
+ /** Allows the enemy to react to incoming attacks */
202
+ virtual void NotifyDanger(const FVector& DangerLocation, AActor* DangerSource) override;
203
+
204
+ // ~end ICombatDamageable interface
205
+
206
+ protected:
207
+
208
+ /** Removes this character from the level after it dies */
209
+ void RemoveFromLevel();
210
+
211
+ public:
212
+
213
+ /** Overrides the default TakeDamage functionality */
214
+ virtual float TakeDamage(float Damage, struct FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser) override;
215
+
216
+ /** Overrides landing to reset damage ragdoll physics */
217
+ virtual void Landed(const FHitResult& Hit) override;
218
+
219
+ protected:
220
+
221
+ /** Blueprint handler to play damage received effects */
222
+ UFUNCTION(BlueprintImplementableEvent, Category="Combat")
223
+ void ReceivedDamage(float Damage, const FVector& ImpactPoint, const FVector& DamageDirection);
224
+
225
+ protected:
226
+
227
+ /** Gameplay initialization */
228
+ virtual void BeginPlay() override;
229
+
230
+ /** EndPlay cleanup */
231
+ virtual void EndPlay(EEndPlayReason::Type EndPlayReason) override;
232
+ };
Source/Pockmon/Variant_Combat/AI/CombatEnemySpawner.cpp ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+
4
+ #include "CombatEnemySpawner.h"
5
+ #include "Engine/World.h"
6
+ #include "Components/SceneComponent.h"
7
+ #include "Components/CapsuleComponent.h"
8
+ #include "Components/ArrowComponent.h"
9
+ #include "TimerManager.h"
10
+ #include "CombatEnemy.h"
11
+
12
+ ACombatEnemySpawner::ACombatEnemySpawner()
13
+ {
14
+ PrimaryActorTick.bCanEverTick = false;
15
+
16
+ // create the root
17
+ RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
18
+
19
+ // create the reference spawn capsule
20
+ SpawnCapsule = CreateDefaultSubobject<UCapsuleComponent>(TEXT("Spawn Capsule"));
21
+ SpawnCapsule->SetupAttachment(RootComponent);
22
+
23
+ SpawnCapsule->SetRelativeLocation(FVector(0.0f, 0.0f, 90.0f));
24
+ SpawnCapsule->SetCapsuleSize(35.0f, 90.0f);
25
+ SpawnCapsule->SetCollisionProfileName(FName("NoCollision"));
26
+
27
+ SpawnDirection = CreateDefaultSubobject<UArrowComponent>(TEXT("Spawn Direction"));
28
+ SpawnDirection->SetupAttachment(RootComponent);
29
+ }
30
+
31
+ void ACombatEnemySpawner::BeginPlay()
32
+ {
33
+ Super::BeginPlay();
34
+
35
+ // should we spawn an enemy right away?
36
+ if (bShouldSpawnEnemiesImmediately)
37
+ {
38
+ // schedule the first enemy spawn
39
+ GetWorld()->GetTimerManager().SetTimer(SpawnTimer, this, &ACombatEnemySpawner::SpawnEnemy, InitialSpawnDelay);
40
+ }
41
+
42
+ }
43
+
44
+ void ACombatEnemySpawner::EndPlay(EEndPlayReason::Type EndPlayReason)
45
+ {
46
+ Super::EndPlay(EndPlayReason);
47
+
48
+ // clear the spawn timer
49
+ GetWorld()->GetTimerManager().ClearTimer(SpawnTimer);
50
+ }
51
+
52
+ void ACombatEnemySpawner::SpawnEnemy()
53
+ {
54
+ // ensure the enemy class is valid
55
+ if (IsValid(EnemyClass))
56
+ {
57
+ // spawn the enemy at the reference capsule's transform
58
+ FActorSpawnParameters SpawnParams;
59
+ SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;
60
+
61
+ ACombatEnemy* SpawnedEnemy = GetWorld()->SpawnActor<ACombatEnemy>(EnemyClass, SpawnCapsule->GetComponentTransform(), SpawnParams);
62
+
63
+ // was the enemy successfully created?
64
+ if (SpawnedEnemy)
65
+ {
66
+ // subscribe to the death delegate
67
+ SpawnedEnemy->OnEnemyDied.AddDynamic(this, &ACombatEnemySpawner::OnEnemyDied);
68
+ }
69
+ }
70
+ }
71
+
72
+ void ACombatEnemySpawner::OnEnemyDied()
73
+ {
74
+ // decrease the spawn counter
75
+ --SpawnCount;
76
+
77
+ // is this the last enemy we should spawn?
78
+ if (SpawnCount <= 0)
79
+ {
80
+ // schedule the activation on depleted message
81
+ GetWorld()->GetTimerManager().SetTimer(SpawnTimer, this, &ACombatEnemySpawner::SpawnerDepleted, ActivationDelay);
82
+ return;
83
+ }
84
+
85
+ // schedule the next enemy spawn
86
+ GetWorld()->GetTimerManager().SetTimer(SpawnTimer, this, &ACombatEnemySpawner::SpawnEnemy, RespawnDelay);
87
+ }
88
+
89
+ void ACombatEnemySpawner::SpawnerDepleted()
90
+ {
91
+ // process the actors to activate list
92
+ for (AActor* CurrentActor : ActorsToActivateWhenDepleted)
93
+ {
94
+ // check if the actor is activatable
95
+ if (ICombatActivatable* CombatActivatable = Cast<ICombatActivatable>(CurrentActor))
96
+ {
97
+ // activate the actor
98
+ CombatActivatable->ActivateInteraction(this);
99
+ }
100
+ }
101
+ }
102
+
103
+ void ACombatEnemySpawner::ToggleInteraction(AActor* ActivationInstigator)
104
+ {
105
+ // stub
106
+ }
107
+
108
+ void ACombatEnemySpawner::ActivateInteraction(AActor* ActivationInstigator)
109
+ {
110
+ // ensure we're only activated once, and only if we've deferred enemy spawning
111
+ if (bHasBeenActivated || bShouldSpawnEnemiesImmediately)
112
+ {
113
+ return;
114
+ }
115
+
116
+ // raise the activation flag
117
+ bHasBeenActivated = true;
118
+
119
+ // spawn the first enemy
120
+ SpawnEnemy();
121
+ }
122
+
123
+ void ACombatEnemySpawner::DeactivateInteraction(AActor* ActivationInstigator)
124
+ {
125
+ // stub
126
+ }
Source/Pockmon/Variant_Combat/AI/CombatEnemySpawner.h ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #pragma once
4
+
5
+ #include "CoreMinimal.h"
6
+ #include "GameFramework/Actor.h"
7
+ #include "CombatActivatable.h"
8
+ #include "CombatEnemySpawner.generated.h"
9
+
10
+ class UCapsuleComponent;
11
+ class UArrowComponent;
12
+ class ACombatEnemy;
13
+
14
+ /**
15
+ * A basic Actor in charge of spawning Enemy Characters and monitoring their deaths.
16
+ * Enemies will be spawned one by one, and the spawner will wait until the enemy dies before spawning a new one.
17
+ * The spawner can be remotely activated through the ICombatActivatable interface
18
+ * When the last spawned enemy dies, the spawner can also activate other ICombatActivatables
19
+ */
20
+ UCLASS(abstract)
21
+ class ACombatEnemySpawner : public AActor, public ICombatActivatable
22
+ {
23
+ GENERATED_BODY()
24
+
25
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
26
+ UCapsuleComponent* SpawnCapsule;
27
+
28
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
29
+ UArrowComponent* SpawnDirection;
30
+
31
+ protected:
32
+
33
+ /** Type of enemy to spawn */
34
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Enemy Spawner")
35
+ TSubclassOf<ACombatEnemy> EnemyClass;
36
+
37
+ /** If true, the first enemy will be spawned as soon as the game starts */
38
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Enemy Spawner")
39
+ bool bShouldSpawnEnemiesImmediately = true;
40
+
41
+ /** Time to wait before spawning the first enemy on game start */
42
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Enemy Spawner", meta = (ClampMin = 0, ClampMax = 10))
43
+ float InitialSpawnDelay = 5.0f;
44
+
45
+ /** Number of enemies to spawn */
46
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Enemy Spawner", meta = (ClampMin = 0, ClampMax = 100))
47
+ int32 SpawnCount = 1;
48
+
49
+ /** Time to wait before spawning the next enemy after the current one dies */
50
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Enemy Spawner", meta = (ClampMin = 0, ClampMax = 10))
51
+ float RespawnDelay = 5.0f;
52
+
53
+ /** Time to wait after this spawner is depleted before activating the actor list */
54
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Activation", meta = (ClampMin = 0, ClampMax = 10))
55
+ float ActivationDelay = 1.0f;
56
+
57
+ /** List of actors to activate after the last enemy dies */
58
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Activation")
59
+ TArray<AActor*> ActorsToActivateWhenDepleted;
60
+
61
+ /** Flag to ensure this is only activated once */
62
+ bool bHasBeenActivated = false;
63
+
64
+ /** Timer to spawn enemies after a delay */
65
+ FTimerHandle SpawnTimer;
66
+
67
+ public:
68
+
69
+ /** Constructor */
70
+ ACombatEnemySpawner();
71
+
72
+ public:
73
+
74
+ /** Initialization */
75
+ virtual void BeginPlay() override;
76
+
77
+ /** Cleanup */
78
+ virtual void EndPlay(EEndPlayReason::Type EndPlayReason) override;
79
+
80
+ protected:
81
+
82
+ /** Spawn an enemy and subscribe to its death event */
83
+ void SpawnEnemy();
84
+
85
+ /** Called when the spawned enemy has died */
86
+ UFUNCTION()
87
+ void OnEnemyDied();
88
+
89
+ /** Called after the last spawned enemy has died */
90
+ void SpawnerDepleted();
91
+
92
+ public:
93
+
94
+ // ~begin ICombatActivatable interface
95
+
96
+ /** Toggles the Spawner */
97
+ UFUNCTION(BlueprintCallable, Category="Activatable")
98
+ virtual void ToggleInteraction(AActor* ActivationInstigator) override;
99
+
100
+ /** Activates the Spawner */
101
+ UFUNCTION(BlueprintCallable, Category="Activatable")
102
+ virtual void ActivateInteraction(AActor* ActivationInstigator) override;
103
+
104
+ /** Deactivates the Spawner */
105
+ UFUNCTION(BlueprintCallable, Category="Activatable")
106
+ virtual void DeactivateInteraction(AActor* ActivationInstigator) override;
107
+
108
+ // ~end IActivatable interface
109
+ };
Source/Pockmon/Variant_Combat/AI/CombatStateTreeUtility.cpp ADDED
@@ -0,0 +1,325 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+
4
+ #include "CombatStateTreeUtility.h"
5
+ #include "StateTreeExecutionContext.h"
6
+ #include "StateTreeExecutionTypes.h"
7
+ #include "Engine/World.h"
8
+ #include "GameFramework/Character.h"
9
+ #include "GameFramework/CharacterMovementComponent.h"
10
+ #include "AIController.h"
11
+ #include "CombatEnemy.h"
12
+ #include "Kismet/GameplayStatics.h"
13
+ #include "StateTreeAsyncExecutionContext.h"
14
+
15
+ bool FStateTreeCharacterGroundedCondition::TestCondition(FStateTreeExecutionContext& Context) const
16
+ {
17
+ const FInstanceDataType& InstanceData = Context.GetInstanceData(*this);
18
+
19
+ // is the character currently grounded?
20
+ bool bCondition = InstanceData.Character->GetMovementComponent()->IsMovingOnGround();
21
+
22
+ return InstanceData.bMustBeOnAir ? !bCondition : bCondition;
23
+ }
24
+
25
+ #if WITH_EDITOR
26
+ FText FStateTreeCharacterGroundedCondition::GetDescription(const FGuid& ID, FStateTreeDataView InstanceDataView, const IStateTreeBindingLookup& BindingLookup, EStateTreeNodeFormatting Formatting /*= EStateTreeNodeFormatting::Text*/) const
27
+ {
28
+ return FText::FromString("<b>Is Character Grounded</b>");
29
+ }
30
+ #endif // WITH_EDITOR
31
+
32
+ ////////////////////////////////////////////////////////////////////
33
+
34
+ bool FStateTreeIsInDangerCondition::TestCondition(FStateTreeExecutionContext& Context) const
35
+ {
36
+ const FInstanceDataType& InstanceData = Context.GetInstanceData(*this);
37
+
38
+ // ensure we have a valid enemy character
39
+ if (InstanceData.Character)
40
+ {
41
+ // is the last detected danger event within the reaction threshold?
42
+ const float ReactionDelta = InstanceData.Character->GetWorld()->GetTimeSeconds() - InstanceData.Character->GetLastDangerTime();
43
+
44
+ if (ReactionDelta < InstanceData.MaxReactionTime && ReactionDelta > InstanceData.MinReactionTime)
45
+ {
46
+ // do a dot product check to determine if the danger location is within the character's detection cone
47
+ const FVector DangerDir = (InstanceData.Character->GetLastDangerLocation() - InstanceData.Character->GetActorLocation()).GetSafeNormal2D();
48
+
49
+ const float DangerDot = FVector::DotProduct(DangerDir, InstanceData.Character->GetActorForwardVector());
50
+ const float ConeAngleCos = FMath::Cos(FMath::DegreesToRadians(InstanceData.DangerSightConeAngle));
51
+
52
+ return DangerDot > ConeAngleCos;
53
+ }
54
+ }
55
+
56
+ return false;
57
+ }
58
+
59
+ #if WITH_EDITOR
60
+ FText FStateTreeIsInDangerCondition::GetDescription(const FGuid& ID, FStateTreeDataView InstanceDataView, const IStateTreeBindingLookup& BindingLookup, EStateTreeNodeFormatting Formatting /*= EStateTreeNodeFormatting::Text*/) const
61
+ {
62
+ return FText::FromString("<b>Is Character In Danger</b>");
63
+ }
64
+ #endif // WITH_EDITOR
65
+
66
+ ////////////////////////////////////////////////////////////////////
67
+
68
+ EStateTreeRunStatus FStateTreeComboAttackTask::EnterState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) const
69
+ {
70
+ // have we transitioned from another state?
71
+ if (Transition.ChangeType == EStateTreeStateChangeType::Changed)
72
+ {
73
+ // get the instance data
74
+ FInstanceDataType& InstanceData = Context.GetInstanceData(*this);
75
+
76
+ // bind to the on attack completed delegate
77
+ InstanceData.Character->OnAttackCompleted.BindLambda(
78
+ [WeakContext = Context.MakeWeakExecutionContext()]()
79
+ {
80
+ WeakContext.FinishTask(EStateTreeFinishTaskType::Succeeded);
81
+ }
82
+ );
83
+
84
+
85
+ // tell the character to do a combo attack
86
+ InstanceData.Character->DoAIComboAttack();
87
+ }
88
+
89
+ return EStateTreeRunStatus::Running;
90
+ }
91
+
92
+ void FStateTreeComboAttackTask::ExitState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) const
93
+ {
94
+ // have we transitioned from another state?
95
+ if (Transition.ChangeType == EStateTreeStateChangeType::Changed)
96
+ {
97
+ // get the instance data
98
+ FInstanceDataType& InstanceData = Context.GetInstanceData(*this);
99
+
100
+ // unbind the on attack completed delegate
101
+ InstanceData.Character->OnAttackCompleted.Unbind();
102
+ }
103
+ }
104
+
105
+ #if WITH_EDITOR
106
+ FText FStateTreeComboAttackTask::GetDescription(const FGuid& ID, FStateTreeDataView InstanceDataView, const IStateTreeBindingLookup& BindingLookup, EStateTreeNodeFormatting Formatting /*= EStateTreeNodeFormatting::Text*/) const
107
+ {
108
+ return FText::FromString("<b>Do Combo Attack</b>");
109
+ }
110
+ #endif // WITH_EDITOR
111
+
112
+ ////////////////////////////////////////////////////////////////////
113
+
114
+ EStateTreeRunStatus FStateTreeChargedAttackTask::EnterState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) const
115
+ {
116
+ // have we transitioned from another state?
117
+ if (Transition.ChangeType == EStateTreeStateChangeType::Changed)
118
+ {
119
+ // get the instance data
120
+ FInstanceDataType& InstanceData = Context.GetInstanceData(*this);
121
+
122
+ // bind to the on attack completed delegate
123
+ InstanceData.Character->OnAttackCompleted.BindLambda(
124
+ [WeakContext = Context.MakeWeakExecutionContext()]()
125
+ {
126
+ WeakContext.FinishTask(EStateTreeFinishTaskType::Succeeded);
127
+ }
128
+ );
129
+
130
+ // tell the character to do a charged attack
131
+ InstanceData.Character->DoAIChargedAttack();
132
+ }
133
+
134
+ return EStateTreeRunStatus::Running;
135
+ }
136
+
137
+ void FStateTreeChargedAttackTask::ExitState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) const
138
+ {
139
+ // have we transitioned from another state?
140
+ if (Transition.ChangeType == EStateTreeStateChangeType::Changed)
141
+ {
142
+ // get the instance data
143
+ FInstanceDataType& InstanceData = Context.GetInstanceData(*this);
144
+
145
+ // unbind the on attack completed delegate
146
+ InstanceData.Character->OnAttackCompleted.Unbind();
147
+ }
148
+ }
149
+
150
+ #if WITH_EDITOR
151
+ FText FStateTreeChargedAttackTask::GetDescription(const FGuid& ID, FStateTreeDataView InstanceDataView, const IStateTreeBindingLookup& BindingLookup, EStateTreeNodeFormatting Formatting /*= EStateTreeNodeFormatting::Text*/) const
152
+ {
153
+ return FText::FromString("<b>Do Charged Attack</b>");
154
+ }
155
+ #endif // WITH_EDITOR
156
+
157
+ ////////////////////////////////////////////////////////////////////
158
+
159
+ EStateTreeRunStatus FStateTreeWaitForLandingTask::EnterState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) const
160
+ {
161
+ // have we transitioned from another state?
162
+ if (Transition.ChangeType == EStateTreeStateChangeType::Changed)
163
+ {
164
+ // get the instance data
165
+ FInstanceDataType& InstanceData = Context.GetInstanceData(*this);
166
+
167
+ // bind to the on enemy landed delegate
168
+ InstanceData.Character->OnEnemyLanded.BindLambda(
169
+ [WeakContext = Context.MakeWeakExecutionContext()]()
170
+ {
171
+ WeakContext.FinishTask(EStateTreeFinishTaskType::Succeeded);
172
+ }
173
+ );
174
+ }
175
+
176
+ return EStateTreeRunStatus::Running;
177
+ }
178
+
179
+ void FStateTreeWaitForLandingTask::ExitState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) const
180
+ {
181
+ // have we transitioned from another state?
182
+ if (Transition.ChangeType == EStateTreeStateChangeType::Changed)
183
+ {
184
+ // get the instance data
185
+ FInstanceDataType& InstanceData = Context.GetInstanceData(*this);
186
+
187
+ // unbind the on enemy landed delegate
188
+ InstanceData.Character->OnEnemyLanded.Unbind();
189
+ }
190
+ }
191
+
192
+ #if WITH_EDITOR
193
+ FText FStateTreeWaitForLandingTask::GetDescription(const FGuid& ID, FStateTreeDataView InstanceDataView, const IStateTreeBindingLookup& BindingLookup, EStateTreeNodeFormatting Formatting /*= EStateTreeNodeFormatting::Text*/) const
194
+ {
195
+ return FText::FromString("<b>Wait for Landing</b>");
196
+ }
197
+ #endif // WITH_EDITOR
198
+
199
+ ////////////////////////////////////////////////////////////////////
200
+
201
+ EStateTreeRunStatus FStateTreeFaceActorTask::EnterState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) const
202
+ {
203
+ // have we transitioned from another state?
204
+ if (Transition.ChangeType == EStateTreeStateChangeType::Changed)
205
+ {
206
+ // get the instance data
207
+ FInstanceDataType& InstanceData = Context.GetInstanceData(*this);
208
+
209
+ // set the AI Controller's focus
210
+ InstanceData.Controller->SetFocus(InstanceData.ActorToFaceTowards);
211
+ }
212
+
213
+ return EStateTreeRunStatus::Running;
214
+ }
215
+
216
+ void FStateTreeFaceActorTask::ExitState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) const
217
+ {
218
+ // have we transitioned to another state?
219
+ if (Transition.ChangeType == EStateTreeStateChangeType::Changed)
220
+ {
221
+ // get the instance data
222
+ FInstanceDataType& InstanceData = Context.GetInstanceData(*this);
223
+
224
+ // clear the AI Controller's focus
225
+ InstanceData.Controller->ClearFocus(EAIFocusPriority::Gameplay);
226
+ }
227
+ }
228
+
229
+ #if WITH_EDITOR
230
+ FText FStateTreeFaceActorTask::GetDescription(const FGuid& ID, FStateTreeDataView InstanceDataView, const IStateTreeBindingLookup& BindingLookup, EStateTreeNodeFormatting Formatting /*= EStateTreeNodeFormatting::Text*/) const
231
+ {
232
+ return FText::FromString("<b>Face Towards Actor</b>");
233
+ }
234
+ #endif // WITH_EDITOR
235
+
236
+ ////////////////////////////////////////////////////////////////////
237
+
238
+ EStateTreeRunStatus FStateTreeFaceLocationTask::EnterState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) const
239
+ {
240
+ // have we transitioned from another state?
241
+ if (Transition.ChangeType == EStateTreeStateChangeType::Changed)
242
+ {
243
+ // get the instance data
244
+ FInstanceDataType& InstanceData = Context.GetInstanceData(*this);
245
+
246
+ // set the AI Controller's focus
247
+ InstanceData.Controller->SetFocalPoint(InstanceData.FaceLocation);
248
+ }
249
+
250
+ return EStateTreeRunStatus::Running;
251
+ }
252
+
253
+ void FStateTreeFaceLocationTask::ExitState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) const
254
+ {
255
+ // have we transitioned to another state?
256
+ if (Transition.ChangeType == EStateTreeStateChangeType::Changed)
257
+ {
258
+ // get the instance data
259
+ FInstanceDataType& InstanceData = Context.GetInstanceData(*this);
260
+
261
+ // clear the AI Controller's focus
262
+ InstanceData.Controller->ClearFocus(EAIFocusPriority::Gameplay);
263
+ }
264
+ }
265
+
266
+ #if WITH_EDITOR
267
+ FText FStateTreeFaceLocationTask::GetDescription(const FGuid& ID, FStateTreeDataView InstanceDataView, const IStateTreeBindingLookup& BindingLookup, EStateTreeNodeFormatting Formatting /*= EStateTreeNodeFormatting::Text*/) const
268
+ {
269
+ return FText::FromString("<b>Face Towards Location</b>");
270
+ }
271
+ #endif // WITH_EDITOR
272
+
273
+ ////////////////////////////////////////////////////////////////////
274
+
275
+ EStateTreeRunStatus FStateTreeSetCharacterSpeedTask::EnterState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) const
276
+ {
277
+ // have we transitioned from another state?
278
+ if (Transition.ChangeType == EStateTreeStateChangeType::Changed)
279
+ {
280
+ // get the instance data
281
+ FInstanceDataType& InstanceData = Context.GetInstanceData(*this);
282
+
283
+ // set the character's max ground speed
284
+ InstanceData.Character->GetCharacterMovement()->MaxWalkSpeed = InstanceData.Speed;
285
+ }
286
+
287
+ return EStateTreeRunStatus::Running;
288
+ }
289
+
290
+ #if WITH_EDITOR
291
+ FText FStateTreeSetCharacterSpeedTask::GetDescription(const FGuid& ID, FStateTreeDataView InstanceDataView, const IStateTreeBindingLookup& BindingLookup, EStateTreeNodeFormatting Formatting /*= EStateTreeNodeFormatting::Text*/) const
292
+ {
293
+ return FText::FromString("<b>Set Character Speed</b>");
294
+ }
295
+ #endif // WITH_EDITOR
296
+
297
+ ////////////////////////////////////////////////////////////////////
298
+
299
+ EStateTreeRunStatus FStateTreeGetPlayerInfoTask::Tick(FStateTreeExecutionContext& Context, const float DeltaTime) const
300
+ {
301
+ // get the instance data
302
+ FInstanceDataType& InstanceData = Context.GetInstanceData(*this);
303
+
304
+ // get the character possessed by the first local player
305
+ InstanceData.TargetPlayerCharacter = Cast<ACharacter>(UGameplayStatics::GetPlayerPawn(InstanceData.Character, 0));
306
+
307
+ // do we have a valid target?
308
+ if (InstanceData.TargetPlayerCharacter)
309
+ {
310
+ // update the last known location
311
+ InstanceData.TargetPlayerLocation = InstanceData.TargetPlayerCharacter->GetActorLocation();
312
+ }
313
+
314
+ // update the distance
315
+ InstanceData.DistanceToTarget = FVector::Distance(InstanceData.TargetPlayerLocation, InstanceData.Character->GetActorLocation());
316
+
317
+ return EStateTreeRunStatus::Running;
318
+ }
319
+
320
+ #if WITH_EDITOR
321
+ FText FStateTreeGetPlayerInfoTask::GetDescription(const FGuid& ID, FStateTreeDataView InstanceDataView, const IStateTreeBindingLookup& BindingLookup, EStateTreeNodeFormatting Formatting /*= EStateTreeNodeFormatting::Text*/) const
322
+ {
323
+ return FText::FromString("<b>Get Player Info</b>");
324
+ }
325
+ #endif // WITH_EDITOR
Source/Pockmon/Variant_Combat/AI/CombatStateTreeUtility.h ADDED
@@ -0,0 +1,365 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #pragma once
4
+
5
+ #include "CoreMinimal.h"
6
+ #include "StateTreeTaskBase.h"
7
+ #include "StateTreeConditionBase.h"
8
+
9
+ #include "CombatStateTreeUtility.generated.h"
10
+
11
+ class ACharacter;
12
+ class AAIController;
13
+ class ACombatEnemy;
14
+
15
+ /**
16
+ * Instance data struct for the FStateTreeCharacterGroundedCondition condition
17
+ */
18
+ USTRUCT()
19
+ struct FStateTreeCharacterGroundedConditionInstanceData
20
+ {
21
+ GENERATED_BODY()
22
+
23
+ /** Character to check grounded status on */
24
+ UPROPERTY(EditAnywhere, Category = "Context")
25
+ ACharacter* Character;
26
+
27
+ /** If true, the condition passes if the character is not grounded instead */
28
+ UPROPERTY(EditAnywhere, Category = "Condition")
29
+ bool bMustBeOnAir = false;
30
+ };
31
+ STATETREE_POD_INSTANCEDATA(FStateTreeCharacterGroundedConditionInstanceData);
32
+
33
+ /**
34
+ * StateTree condition to check if the character is grounded
35
+ */
36
+ USTRUCT(DisplayName = "Character is Grounded")
37
+ struct FStateTreeCharacterGroundedCondition : public FStateTreeConditionCommonBase
38
+ {
39
+ GENERATED_BODY()
40
+
41
+ /** Set the instance data type */
42
+ using FInstanceDataType = FStateTreeCharacterGroundedConditionInstanceData;
43
+ virtual const UStruct* GetInstanceDataType() const override { return FInstanceDataType::StaticStruct(); }
44
+
45
+ /** Default constructor */
46
+ FStateTreeCharacterGroundedCondition() = default;
47
+
48
+ /** Tests the StateTree condition */
49
+ virtual bool TestCondition(FStateTreeExecutionContext& Context) const override;
50
+
51
+ #if WITH_EDITOR
52
+
53
+ /** Provides the description string */
54
+ virtual FText GetDescription(const FGuid& ID, FStateTreeDataView InstanceDataView, const IStateTreeBindingLookup& BindingLookup, EStateTreeNodeFormatting Formatting = EStateTreeNodeFormatting::Text) const override;
55
+ #endif
56
+
57
+ };
58
+
59
+ ////////////////////////////////////////////////////////////////////
60
+
61
+ /**
62
+ * Instance data struct for the FStateTreeIsInDangerCondition condition
63
+ */
64
+ USTRUCT()
65
+ struct FStateTreeIsInDangerConditionInstanceData
66
+ {
67
+ GENERATED_BODY()
68
+
69
+ /** Character to check danger status on */
70
+ UPROPERTY(EditAnywhere, Category = "Context")
71
+ ACombatEnemy* Character;
72
+
73
+ /** Minimum time to wait before reacting to the danger event */
74
+ UPROPERTY(EditAnywhere, Category = "Parameters", meta = (Units = "s"))
75
+ float MinReactionTime = 0.35f;
76
+
77
+ /** Maximum time to wait before ignoring the danger event */
78
+ UPROPERTY(EditAnywhere, Category = "Parameters", meta = (Units = "s"))
79
+ float MaxReactionTime = 0.75f;
80
+
81
+ /** Line of sight half angle for detecting incoming danger, in degrees*/
82
+ UPROPERTY(EditAnywhere, Category = "Parameters", meta = (Units = "degrees"))
83
+ float DangerSightConeAngle = 120.0f;
84
+ };
85
+ STATETREE_POD_INSTANCEDATA(FStateTreeIsInDangerConditionInstanceData);
86
+
87
+ /**
88
+ * StateTree condition to check if the character is about to be hit by an attack
89
+ */
90
+ USTRUCT(DisplayName = "Character is in Danger")
91
+ struct FStateTreeIsInDangerCondition : public FStateTreeConditionCommonBase
92
+ {
93
+ GENERATED_BODY()
94
+
95
+ /** Set the instance data type */
96
+ using FInstanceDataType = FStateTreeIsInDangerConditionInstanceData;
97
+ virtual const UStruct* GetInstanceDataType() const override { return FInstanceDataType::StaticStruct(); }
98
+
99
+ /** Default constructor */
100
+ FStateTreeIsInDangerCondition() = default;
101
+
102
+ /** Tests the StateTree condition */
103
+ virtual bool TestCondition(FStateTreeExecutionContext& Context) const override;
104
+
105
+ #if WITH_EDITOR
106
+
107
+ /** Provides the description string */
108
+ virtual FText GetDescription(const FGuid& ID, FStateTreeDataView InstanceDataView, const IStateTreeBindingLookup& BindingLookup, EStateTreeNodeFormatting Formatting = EStateTreeNodeFormatting::Text) const override;
109
+ #endif
110
+
111
+ };
112
+
113
+ ////////////////////////////////////////////////////////////////////
114
+
115
+ /**
116
+ * Instance data struct for the Combat StateTree tasks
117
+ */
118
+ USTRUCT()
119
+ struct FStateTreeAttackInstanceData
120
+ {
121
+ GENERATED_BODY()
122
+
123
+ /** Character that will perform the attack */
124
+ UPROPERTY(EditAnywhere, Category = Context)
125
+ TObjectPtr<ACombatEnemy> Character;
126
+ };
127
+
128
+ /**
129
+ * StateTree task to perform a combo attack
130
+ */
131
+ USTRUCT(meta=(DisplayName="Combo Attack", Category="Combat"))
132
+ struct FStateTreeComboAttackTask : public FStateTreeTaskCommonBase
133
+ {
134
+ GENERATED_BODY()
135
+
136
+ /* Ensure we're using the correct instance data struct */
137
+ using FInstanceDataType = FStateTreeAttackInstanceData;
138
+ virtual const UStruct* GetInstanceDataType() const override { return FInstanceDataType::StaticStruct(); }
139
+
140
+ /** Runs when the owning state is entered */
141
+ virtual EStateTreeRunStatus EnterState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) const override;
142
+
143
+ /** Runs when the owning state is ended */
144
+ virtual void ExitState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) const override;
145
+
146
+ #if WITH_EDITOR
147
+ virtual FText GetDescription(const FGuid& ID, FStateTreeDataView InstanceDataView, const IStateTreeBindingLookup& BindingLookup, EStateTreeNodeFormatting Formatting = EStateTreeNodeFormatting::Text) const override;
148
+ #endif // WITH_EDITOR
149
+ };
150
+
151
+ /**
152
+ * StateTree task to perform a charged attack
153
+ */
154
+ USTRUCT(meta=(DisplayName="Charged Attack", Category="Combat"))
155
+ struct FStateTreeChargedAttackTask : public FStateTreeTaskCommonBase
156
+ {
157
+ GENERATED_BODY()
158
+
159
+ /* Ensure we're using the correct instance data struct */
160
+ using FInstanceDataType = FStateTreeAttackInstanceData;
161
+ virtual const UStruct* GetInstanceDataType() const override { return FInstanceDataType::StaticStruct(); }
162
+
163
+ /** Runs when the owning state is entered */
164
+ virtual EStateTreeRunStatus EnterState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) const override;
165
+
166
+ /** Runs when the owning state is ended */
167
+ virtual void ExitState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) const override;
168
+
169
+ #if WITH_EDITOR
170
+ virtual FText GetDescription(const FGuid& ID, FStateTreeDataView InstanceDataView, const IStateTreeBindingLookup& BindingLookup, EStateTreeNodeFormatting Formatting = EStateTreeNodeFormatting::Text) const override;
171
+ #endif // WITH_EDITOR
172
+ };
173
+
174
+ /**
175
+ * StateTree task to wait for the character to land
176
+ */
177
+ USTRUCT(meta=(DisplayName="Wait for Landing", Category="Combat"))
178
+ struct FStateTreeWaitForLandingTask : public FStateTreeTaskCommonBase
179
+ {
180
+ GENERATED_BODY()
181
+
182
+ /* Ensure we're using the correct instance data struct */
183
+ using FInstanceDataType = FStateTreeAttackInstanceData;
184
+ virtual const UStruct* GetInstanceDataType() const override { return FInstanceDataType::StaticStruct(); }
185
+
186
+ /** Runs when the owning state is entered */
187
+ virtual EStateTreeRunStatus EnterState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) const override;
188
+
189
+ /** Runs when the owning state is ended */
190
+ virtual void ExitState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) const override;
191
+
192
+ #if WITH_EDITOR
193
+ virtual FText GetDescription(const FGuid& ID, FStateTreeDataView InstanceDataView, const IStateTreeBindingLookup& BindingLookup, EStateTreeNodeFormatting Formatting = EStateTreeNodeFormatting::Text) const override;
194
+ #endif // WITH_EDITOR
195
+ };
196
+
197
+ ////////////////////////////////////////////////////////////////////
198
+
199
+ /**
200
+ * Instance data struct for the Face Towards Actor StateTree task
201
+ */
202
+ USTRUCT()
203
+ struct FStateTreeFaceActorInstanceData
204
+ {
205
+ GENERATED_BODY()
206
+
207
+ /** AI Controller that will determine the focused actor */
208
+ UPROPERTY(EditAnywhere, Category = Context)
209
+ TObjectPtr<AAIController> Controller;
210
+
211
+ /** Actor that will be faced towards */
212
+ UPROPERTY(EditAnywhere, Category = Input)
213
+ TObjectPtr<AActor> ActorToFaceTowards;
214
+ };
215
+
216
+ /**
217
+ * StateTree task to face an AI-Controlled Pawn towards an Actor
218
+ */
219
+ USTRUCT(meta=(DisplayName="Face Towards Actor", Category="Combat"))
220
+ struct FStateTreeFaceActorTask : public FStateTreeTaskCommonBase
221
+ {
222
+ GENERATED_BODY()
223
+
224
+ /* Ensure we're using the correct instance data struct */
225
+ using FInstanceDataType = FStateTreeFaceActorInstanceData;
226
+ virtual const UStruct* GetInstanceDataType() const override { return FInstanceDataType::StaticStruct(); }
227
+
228
+ /** Runs when the owning state is entered */
229
+ virtual EStateTreeRunStatus EnterState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) const override;
230
+
231
+ /** Runs when the owning state is ended */
232
+ virtual void ExitState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) const override;
233
+
234
+ #if WITH_EDITOR
235
+ virtual FText GetDescription(const FGuid& ID, FStateTreeDataView InstanceDataView, const IStateTreeBindingLookup& BindingLookup, EStateTreeNodeFormatting Formatting = EStateTreeNodeFormatting::Text) const override;
236
+ #endif // WITH_EDITOR
237
+ };
238
+
239
+ ////////////////////////////////////////////////////////////////////
240
+
241
+ /**
242
+ * Instance data struct for the Face Towards Location StateTree task
243
+ */
244
+ USTRUCT()
245
+ struct FStateTreeFaceLocationInstanceData
246
+ {
247
+ GENERATED_BODY()
248
+
249
+ /** AI Controller that will determine the focused location */
250
+ UPROPERTY(EditAnywhere, Category = Context)
251
+ TObjectPtr<AAIController> Controller;
252
+
253
+ /** Location that will be faced towards */
254
+ UPROPERTY(EditAnywhere, Category = Parameter)
255
+ FVector FaceLocation = FVector::ZeroVector;
256
+ };
257
+
258
+ /**
259
+ * StateTree task to face an AI-Controlled Pawn towards a world location
260
+ */
261
+ USTRUCT(meta=(DisplayName="Face Towards Location", Category="Combat"))
262
+ struct FStateTreeFaceLocationTask : public FStateTreeTaskCommonBase
263
+ {
264
+ GENERATED_BODY()
265
+
266
+ /* Ensure we're using the correct instance data struct */
267
+ using FInstanceDataType = FStateTreeFaceLocationInstanceData;
268
+ virtual const UStruct* GetInstanceDataType() const override { return FInstanceDataType::StaticStruct(); }
269
+
270
+ /** Runs when the owning state is entered */
271
+ virtual EStateTreeRunStatus EnterState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) const override;
272
+
273
+ /** Runs when the owning state is ended */
274
+ virtual void ExitState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) const override;
275
+
276
+ #if WITH_EDITOR
277
+ virtual FText GetDescription(const FGuid& ID, FStateTreeDataView InstanceDataView, const IStateTreeBindingLookup& BindingLookup, EStateTreeNodeFormatting Formatting = EStateTreeNodeFormatting::Text) const override;
278
+ #endif // WITH_EDITOR
279
+ };
280
+
281
+ ////////////////////////////////////////////////////////////////////
282
+
283
+ /**
284
+ * Instance data struct for the Set Character Speed StateTree task
285
+ */
286
+ USTRUCT()
287
+ struct FStateTreeSetCharacterSpeedInstanceData
288
+ {
289
+ GENERATED_BODY()
290
+
291
+ /** Character that will be affected */
292
+ UPROPERTY(EditAnywhere, Category = Context)
293
+ TObjectPtr<ACharacter> Character;
294
+
295
+ /** Max ground speed to set for the character */
296
+ UPROPERTY(EditAnywhere, Category = Parameter)
297
+ float Speed = 600.0f;
298
+ };
299
+
300
+ /**
301
+ * StateTree task to change a Character's ground speed
302
+ */
303
+ USTRUCT(meta=(DisplayName="Set Character Speed", Category="Combat"))
304
+ struct FStateTreeSetCharacterSpeedTask : public FStateTreeTaskCommonBase
305
+ {
306
+ GENERATED_BODY()
307
+
308
+ /* Ensure we're using the correct instance data struct */
309
+ using FInstanceDataType = FStateTreeSetCharacterSpeedInstanceData;
310
+ virtual const UStruct* GetInstanceDataType() const override { return FInstanceDataType::StaticStruct(); }
311
+
312
+ /** Runs when the owning state is entered */
313
+ virtual EStateTreeRunStatus EnterState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) const override;
314
+
315
+ #if WITH_EDITOR
316
+ virtual FText GetDescription(const FGuid& ID, FStateTreeDataView InstanceDataView, const IStateTreeBindingLookup& BindingLookup, EStateTreeNodeFormatting Formatting = EStateTreeNodeFormatting::Text) const override;
317
+ #endif // WITH_EDITOR
318
+ };
319
+
320
+ ////////////////////////////////////////////////////////////////////
321
+
322
+ /**
323
+ * Instance data struct for the Get Player Info task
324
+ */
325
+ USTRUCT()
326
+ struct FStateTreeGetPlayerInfoInstanceData
327
+ {
328
+ GENERATED_BODY()
329
+
330
+ /** Character that owns this task */
331
+ UPROPERTY(EditAnywhere, Category = Context)
332
+ TObjectPtr<ACharacter> Character;
333
+
334
+ /** Character that owns this task */
335
+ UPROPERTY(VisibleAnywhere)
336
+ TObjectPtr<ACharacter> TargetPlayerCharacter;
337
+
338
+ /** Last known location for the target */
339
+ UPROPERTY(VisibleAnywhere)
340
+ FVector TargetPlayerLocation = FVector::ZeroVector;
341
+
342
+ /** Distance to the target */
343
+ UPROPERTY(VisibleAnywhere)
344
+ float DistanceToTarget = 0.0f;
345
+ };
346
+
347
+ /**
348
+ * StateTree task to get information about the player character
349
+ */
350
+ USTRUCT(meta=(DisplayName="GetPlayerInfo", Category="Combat"))
351
+ struct FStateTreeGetPlayerInfoTask : public FStateTreeTaskCommonBase
352
+ {
353
+ GENERATED_BODY()
354
+
355
+ /* Ensure we're using the correct instance data struct */
356
+ using FInstanceDataType = FStateTreeGetPlayerInfoInstanceData;
357
+ virtual const UStruct* GetInstanceDataType() const override { return FInstanceDataType::StaticStruct(); }
358
+
359
+ /** Runs while the owning state is active */
360
+ virtual EStateTreeRunStatus Tick(FStateTreeExecutionContext& Context, const float DeltaTime) const override;
361
+
362
+ #if WITH_EDITOR
363
+ virtual FText GetDescription(const FGuid& ID, FStateTreeDataView InstanceDataView, const IStateTreeBindingLookup& BindingLookup, EStateTreeNodeFormatting Formatting = EStateTreeNodeFormatting::Text) const override;
364
+ #endif // WITH_EDITOR
365
+ };
Source/Pockmon/Variant_Combat/AI/EnvQueryContext_Danger.cpp ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+
4
+ #include "Variant_Combat/AI/EnvQueryContext_Danger.h"
5
+ #include "Variant_Combat/AI/CombatEnemy.h"
6
+ #include "EnvironmentQuery/EnvQueryTypes.h"
7
+ #include "EnvironmentQuery/Items/EnvQueryItemType_Point.h"
8
+
9
+ void UEnvQueryContext_Danger::ProvideContext(FEnvQueryInstance& QueryInstance, FEnvQueryContextData& ContextData) const
10
+ {
11
+ // get the querying enemy
12
+ if (ACombatEnemy* QuerierActor = Cast<ACombatEnemy>(QueryInstance.Owner.Get()))
13
+ {
14
+ // add the last recorded danger location to the context
15
+ UEnvQueryItemType_Point::SetContextHelper(ContextData, QuerierActor->GetLastDangerLocation());
16
+ }
17
+ }
Source/Pockmon/Variant_Combat/AI/EnvQueryContext_Danger.h ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #pragma once
4
+
5
+ #include "CoreMinimal.h"
6
+ #include "EnvironmentQuery/EnvQueryContext.h"
7
+ #include "EnvQueryContext_Danger.generated.h"
8
+
9
+ /**
10
+ * UEnvQueryContext_Danger
11
+ * Returns the enemy character's last known danger location
12
+ */
13
+ UCLASS()
14
+ class POCKMON_API UEnvQueryContext_Danger : public UEnvQueryContext
15
+ {
16
+ GENERATED_BODY()
17
+
18
+ public:
19
+
20
+ /** Provides the context locations or actors for this EnvQuery */
21
+ virtual void ProvideContext(FEnvQueryInstance& QueryInstance, FEnvQueryContextData& ContextData) const override;
22
+
23
+ };
Source/Pockmon/Variant_Combat/AI/EnvQueryContext_Player.cpp ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+
4
+ #include "EnvQueryContext_Player.h"
5
+ #include "Kismet/GameplayStatics.h"
6
+ #include "EnvironmentQuery/EnvQueryTypes.h"
7
+ #include "EnvironmentQuery/Items/EnvQueryItemType_Actor.h"
8
+ #include "GameFramework/Pawn.h"
9
+
10
+ void UEnvQueryContext_Player::ProvideContext(FEnvQueryInstance& QueryInstance, FEnvQueryContextData& ContextData) const
11
+ {
12
+ // get the player pawn for the first local player
13
+ AActor* PlayerPawn = UGameplayStatics::GetPlayerPawn(QueryInstance.Owner.Get(), 0);
14
+ check(PlayerPawn);
15
+
16
+ // add the actor data to the context
17
+ UEnvQueryItemType_Actor::SetContextHelper(ContextData, PlayerPawn);
18
+ }
Source/Pockmon/Variant_Combat/AI/EnvQueryContext_Player.h ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #pragma once
4
+
5
+ #include "CoreMinimal.h"
6
+ #include "EnvironmentQuery/EnvQueryContext.h"
7
+ #include "EnvQueryContext_Player.generated.h"
8
+
9
+ /**
10
+ * UEnvQueryContext_Player
11
+ * Basic EnvQuery Context that returns the first local player
12
+ */
13
+ UCLASS()
14
+ class UEnvQueryContext_Player : public UEnvQueryContext
15
+ {
16
+ GENERATED_BODY()
17
+
18
+ public:
19
+
20
+ /** Provides the context locations or actors for this EnvQuery */
21
+ virtual void ProvideContext(FEnvQueryInstance& QueryInstance, FEnvQueryContextData& ContextData) const override;
22
+ };
Source/Pockmon/Variant_Combat/Animation/AnimNotify_CheckChargedAttack.cpp ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+
4
+ #include "AnimNotify_CheckChargedAttack.h"
5
+ #include "CombatAttacker.h"
6
+ #include "Components/SkeletalMeshComponent.h"
7
+
8
+ void UAnimNotify_CheckChargedAttack::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference)
9
+ {
10
+ // cast the owner to the attacker interface
11
+ if (ICombatAttacker* AttackerInterface = Cast<ICombatAttacker>(MeshComp->GetOwner()))
12
+ {
13
+ // tell the actor to check for a charged attack loop
14
+ AttackerInterface->CheckChargedAttack();
15
+ }
16
+ }
17
+
18
+ FString UAnimNotify_CheckChargedAttack::GetNotifyName_Implementation() const
19
+ {
20
+ return FString("Check Charged Attack");
21
+ }
Source/Pockmon/Variant_Combat/Animation/AnimNotify_CheckChargedAttack.h ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #pragma once
4
+
5
+ #include "CoreMinimal.h"
6
+ #include "Animation/AnimNotifies/AnimNotify.h"
7
+ #include "AnimNotify_CheckChargedAttack.generated.h"
8
+
9
+ /**
10
+ * AnimNotify to perform a charged attack hold check.
11
+ */
12
+ UCLASS()
13
+ class UAnimNotify_CheckChargedAttack : public UAnimNotify
14
+ {
15
+ GENERATED_BODY()
16
+
17
+ public:
18
+
19
+ /** Perform the Anim Notify */
20
+ virtual void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference) override;
21
+
22
+ /** Get the notify name */
23
+ virtual FString GetNotifyName_Implementation() const override;
24
+ };
Source/Pockmon/Variant_Combat/Animation/AnimNotify_CheckCombo.cpp ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+
4
+ #include "AnimNotify_CheckCombo.h"
5
+ #include "CombatAttacker.h"
6
+ #include "Components/SkeletalMeshComponent.h"
7
+
8
+ void UAnimNotify_CheckCombo::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference)
9
+ {
10
+ // cast the owner to the attacker interface
11
+ if (ICombatAttacker* AttackerInterface = Cast<ICombatAttacker>(MeshComp->GetOwner()))
12
+ {
13
+ // tell the actor to check for combo string
14
+ AttackerInterface->CheckCombo();
15
+ }
16
+ }
17
+
18
+ FString UAnimNotify_CheckCombo::GetNotifyName_Implementation() const
19
+ {
20
+ return FString("Check Combo String");
21
+ }
Source/Pockmon/Variant_Combat/Animation/AnimNotify_CheckCombo.h ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #pragma once
4
+
5
+ #include "CoreMinimal.h"
6
+ #include "Animation/AnimNotifies/AnimNotify.h"
7
+ #include "AnimNotify_CheckCombo.generated.h"
8
+
9
+ /**
10
+ * AnimNotify to perform a combo string check.
11
+ */
12
+ UCLASS()
13
+ class UAnimNotify_CheckCombo : public UAnimNotify
14
+ {
15
+ GENERATED_BODY()
16
+
17
+ public:
18
+
19
+ /** Perform the Anim Notify */
20
+ virtual void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference) override;
21
+
22
+ /** Get the notify name */
23
+ virtual FString GetNotifyName_Implementation() const override;
24
+ };
Source/Pockmon/Variant_Combat/Animation/AnimNotify_DoAttackTrace.cpp ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+
4
+ #include "AnimNotify_DoAttackTrace.h"
5
+ #include "CombatAttacker.h"
6
+ #include "Components/SkeletalMeshComponent.h"
7
+
8
+ void UAnimNotify_DoAttackTrace::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference)
9
+ {
10
+ // cast the owner to the attacker interface
11
+ if (ICombatAttacker* AttackerInterface = Cast<ICombatAttacker>(MeshComp->GetOwner()))
12
+ {
13
+ AttackerInterface->DoAttackTrace(AttackBoneName);
14
+ }
15
+ }
16
+
17
+ FString UAnimNotify_DoAttackTrace::GetNotifyName_Implementation() const
18
+ {
19
+ return FString("Do Attack Trace");
20
+ }
Source/Pockmon/Variant_Combat/Animation/AnimNotify_DoAttackTrace.h ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #pragma once
4
+
5
+ #include "CoreMinimal.h"
6
+ #include "Animation/AnimNotifies/AnimNotify.h"
7
+ #include "AnimNotify_DoAttackTrace.generated.h"
8
+
9
+ /**
10
+ * AnimNotify to tell the actor to perform an attack trace check to look for targets to damage.
11
+ */
12
+ UCLASS()
13
+ class UAnimNotify_DoAttackTrace : public UAnimNotify
14
+ {
15
+ GENERATED_BODY()
16
+
17
+ protected:
18
+
19
+ /** Source bone for the attack trace */
20
+ UPROPERTY(EditAnywhere, Category="Attack")
21
+ FName AttackBoneName;
22
+
23
+ public:
24
+
25
+ /** Perform the Anim Notify */
26
+ virtual void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference) override;
27
+
28
+ /** Get the notify name */
29
+ virtual FString GetNotifyName_Implementation() const override;
30
+ };
Source/Pockmon/Variant_Combat/CombatCharacter.cpp ADDED
@@ -0,0 +1,547 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+
4
+ #include "CombatCharacter.h"
5
+ #include "Components/CapsuleComponent.h"
6
+ #include "Components/WidgetComponent.h"
7
+ #include "GameFramework/CharacterMovementComponent.h"
8
+ #include "GameFramework/SpringArmComponent.h"
9
+ #include "Components/SkeletalMeshComponent.h"
10
+ #include "Camera/CameraComponent.h"
11
+ #include "EnhancedInputSubsystems.h"
12
+ #include "EnhancedInputComponent.h"
13
+ #include "CombatLifeBar.h"
14
+ #include "Engine/DamageEvents.h"
15
+ #include "TimerManager.h"
16
+ #include "Engine/LocalPlayer.h"
17
+ #include "CombatPlayerController.h"
18
+
19
+ ACombatCharacter::ACombatCharacter()
20
+ {
21
+ PrimaryActorTick.bCanEverTick = true;
22
+
23
+ // bind the attack montage ended delegate
24
+ OnAttackMontageEnded.BindUObject(this, &ACombatCharacter::AttackMontageEnded);
25
+
26
+ // Set size for collision capsule
27
+ GetCapsuleComponent()->InitCapsuleSize(35.0f, 90.0f);
28
+
29
+ // Configure character movement
30
+ GetCharacterMovement()->MaxWalkSpeed = 400.0f;
31
+
32
+ // create the camera boom
33
+ CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
34
+ CameraBoom->SetupAttachment(RootComponent);
35
+
36
+ CameraBoom->TargetArmLength = DefaultCameraDistance;
37
+ CameraBoom->bUsePawnControlRotation = true;
38
+ CameraBoom->bEnableCameraLag = true;
39
+ CameraBoom->bEnableCameraRotationLag = true;
40
+
41
+ // create the orbiting camera
42
+ FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
43
+ FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);
44
+ FollowCamera->bUsePawnControlRotation = false;
45
+
46
+ // create the life bar widget component
47
+ LifeBar = CreateDefaultSubobject<UWidgetComponent>(TEXT("LifeBar"));
48
+ LifeBar->SetupAttachment(RootComponent);
49
+
50
+ // set the player tag
51
+ Tags.Add(FName("Player"));
52
+ }
53
+
54
+ void ACombatCharacter::Move(const FInputActionValue& Value)
55
+ {
56
+ // input is a Vector2D
57
+ FVector2D MovementVector = Value.Get<FVector2D>();
58
+
59
+ // route the input
60
+ DoMove(MovementVector.X, MovementVector.Y);
61
+ }
62
+
63
+ void ACombatCharacter::Look(const FInputActionValue& Value)
64
+ {
65
+ FVector2D LookAxisVector = Value.Get<FVector2D>();
66
+
67
+ // route the input
68
+ DoLook(LookAxisVector.X, LookAxisVector.Y);
69
+ }
70
+
71
+ void ACombatCharacter::ComboAttackPressed()
72
+ {
73
+ // route the input
74
+ DoComboAttackStart();
75
+ }
76
+
77
+ void ACombatCharacter::ChargedAttackPressed()
78
+ {
79
+ // route the input
80
+ DoChargedAttackStart();
81
+ }
82
+
83
+ void ACombatCharacter::ChargedAttackReleased()
84
+ {
85
+ // route the input
86
+ DoChargedAttackEnd();
87
+ }
88
+
89
+ void ACombatCharacter::ToggleCamera()
90
+ {
91
+ // call the BP hook
92
+ BP_ToggleCamera();
93
+ }
94
+
95
+ void ACombatCharacter::DoMove(float Right, float Forward)
96
+ {
97
+ if (GetController() != nullptr)
98
+ {
99
+ // find out which way is forward
100
+ const FRotator Rotation = GetController()->GetControlRotation();
101
+ const FRotator YawRotation(0, Rotation.Yaw, 0);
102
+
103
+ // get forward vector
104
+ const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
105
+
106
+ // get right vector
107
+ const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
108
+
109
+ // add movement
110
+ AddMovementInput(ForwardDirection, Forward);
111
+ AddMovementInput(RightDirection, Right);
112
+ }
113
+ }
114
+
115
+ void ACombatCharacter::DoLook(float Yaw, float Pitch)
116
+ {
117
+ if (GetController() != nullptr)
118
+ {
119
+ // add yaw and pitch input to controller
120
+ AddControllerYawInput(Yaw);
121
+ AddControllerPitchInput(Pitch);
122
+ }
123
+ }
124
+
125
+ void ACombatCharacter::DoComboAttackStart()
126
+ {
127
+ // are we already playing an attack animation?
128
+ if (bIsAttacking)
129
+ {
130
+ // cache the input time so we can check it later
131
+ CachedAttackInputTime = GetWorld()->GetTimeSeconds();
132
+
133
+ return;
134
+ }
135
+
136
+ // perform a combo attack
137
+ ComboAttack();
138
+ }
139
+
140
+ void ACombatCharacter::DoComboAttackEnd()
141
+ {
142
+ // stub
143
+ }
144
+
145
+ void ACombatCharacter::DoChargedAttackStart()
146
+ {
147
+ // raise the charging attack flag
148
+ bIsChargingAttack = true;
149
+
150
+ if (bIsAttacking)
151
+ {
152
+ // cache the input time so we can check it later
153
+ CachedAttackInputTime = GetWorld()->GetTimeSeconds();
154
+
155
+ return;
156
+ }
157
+
158
+ ChargedAttack();
159
+ }
160
+
161
+ void ACombatCharacter::DoChargedAttackEnd()
162
+ {
163
+ // lower the charging attack flag
164
+ bIsChargingAttack = false;
165
+
166
+ // if we've done the charge loop at least once, release the charged attack right away
167
+ if (bHasLoopedChargedAttack)
168
+ {
169
+ CheckChargedAttack();
170
+ }
171
+ }
172
+
173
+ void ACombatCharacter::ResetHP()
174
+ {
175
+ // reset the current HP total
176
+ CurrentHP = MaxHP;
177
+
178
+ // update the life bar
179
+ LifeBarWidget->SetLifePercentage(1.0f);
180
+ }
181
+
182
+ void ACombatCharacter::ComboAttack()
183
+ {
184
+ // raise the attacking flag
185
+ bIsAttacking = true;
186
+
187
+ // reset the combo count
188
+ ComboCount = 0;
189
+
190
+ // notify enemies they are about to be attacked
191
+ NotifyEnemiesOfIncomingAttack();
192
+
193
+ // play the attack montage
194
+ if (UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance())
195
+ {
196
+ const float MontageLength = AnimInstance->Montage_Play(ComboAttackMontage, 1.0f, EMontagePlayReturnType::MontageLength, 0.0f, true);
197
+
198
+ // subscribe to montage completed and interrupted events
199
+ if (MontageLength > 0.0f)
200
+ {
201
+ // set the end delegate for the montage
202
+ AnimInstance->Montage_SetEndDelegate(OnAttackMontageEnded, ComboAttackMontage);
203
+ }
204
+ }
205
+
206
+ }
207
+
208
+ void ACombatCharacter::ChargedAttack()
209
+ {
210
+ // raise the attacking flag
211
+ bIsAttacking = true;
212
+
213
+ // reset the charge loop flag
214
+ bHasLoopedChargedAttack = false;
215
+
216
+ // notify enemies they are about to be attacked
217
+ NotifyEnemiesOfIncomingAttack();
218
+
219
+ // play the charged attack montage
220
+ if (UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance())
221
+ {
222
+ const float MontageLength = AnimInstance->Montage_Play(ChargedAttackMontage, 1.0f, EMontagePlayReturnType::MontageLength, 0.0f, true);
223
+
224
+ // subscribe to montage completed and interrupted events
225
+ if (MontageLength > 0.0f)
226
+ {
227
+ // set the end delegate for the montage
228
+ AnimInstance->Montage_SetEndDelegate(OnAttackMontageEnded, ChargedAttackMontage);
229
+ }
230
+ }
231
+ }
232
+
233
+ void ACombatCharacter::AttackMontageEnded(UAnimMontage* Montage, bool bInterrupted)
234
+ {
235
+ // reset the attacking flag
236
+ bIsAttacking = false;
237
+
238
+ // check if we have a non-stale cached input
239
+ if (GetWorld()->GetTimeSeconds() - CachedAttackInputTime <= AttackInputCacheTimeTolerance)
240
+ {
241
+ // are we holding the charged attack button?
242
+ if (bIsChargingAttack)
243
+ {
244
+ // do a charged attack
245
+ ChargedAttack();
246
+ }
247
+ else
248
+ {
249
+ // do a regular attack
250
+ ComboAttack();
251
+ }
252
+ }
253
+ }
254
+
255
+ void ACombatCharacter::DoAttackTrace(FName DamageSourceBone)
256
+ {
257
+ // sweep for objects in front of the character to be hit by the attack
258
+ TArray<FHitResult> OutHits;
259
+
260
+ // start at the provided socket location, sweep forward
261
+ const FVector TraceStart = GetMesh()->GetSocketLocation(DamageSourceBone);
262
+ const FVector TraceEnd = TraceStart + (GetActorForwardVector() * MeleeTraceDistance);
263
+
264
+ // check for pawn and world dynamic collision object types
265
+ FCollisionObjectQueryParams ObjectParams;
266
+ ObjectParams.AddObjectTypesToQuery(ECC_Pawn);
267
+ ObjectParams.AddObjectTypesToQuery(ECC_WorldDynamic);
268
+
269
+ // use a sphere shape for the sweep
270
+ FCollisionShape CollisionShape;
271
+ CollisionShape.SetSphere(MeleeTraceRadius);
272
+
273
+ // ignore self
274
+ FCollisionQueryParams QueryParams;
275
+ QueryParams.AddIgnoredActor(this);
276
+
277
+ if (GetWorld()->SweepMultiByObjectType(OutHits, TraceStart, TraceEnd, FQuat::Identity, ObjectParams, CollisionShape, QueryParams))
278
+ {
279
+ // iterate over each object hit
280
+ for (const FHitResult& CurrentHit : OutHits)
281
+ {
282
+ // check if we've hit a damageable actor
283
+ ICombatDamageable* Damageable = Cast<ICombatDamageable>(CurrentHit.GetActor());
284
+
285
+ if (Damageable)
286
+ {
287
+ // knock upwards and away from the impact normal
288
+ const FVector Impulse = (CurrentHit.ImpactNormal * -MeleeKnockbackImpulse) + (FVector::UpVector * MeleeLaunchImpulse);
289
+
290
+ // pass the damage event to the actor
291
+ Damageable->ApplyDamage(MeleeDamage, this, CurrentHit.ImpactPoint, Impulse);
292
+
293
+ // call the BP handler to play effects, etc.
294
+ DealtDamage(MeleeDamage, CurrentHit.ImpactPoint);
295
+ }
296
+ }
297
+ }
298
+ }
299
+
300
+ void ACombatCharacter::CheckCombo()
301
+ {
302
+ // are we playing a non-charge attack animation?
303
+ if (bIsAttacking && !bIsChargingAttack)
304
+ {
305
+ // is the last attack input not stale?
306
+ if (GetWorld()->GetTimeSeconds() - CachedAttackInputTime <= ComboInputCacheTimeTolerance)
307
+ {
308
+ // consume the attack input so we don't accidentally trigger it twice
309
+ CachedAttackInputTime = 0.0f;
310
+
311
+ // increase the combo counter
312
+ ++ComboCount;
313
+
314
+ // do we still have a combo section to play?
315
+ if (ComboCount < ComboSectionNames.Num())
316
+ {
317
+ // notify enemies they are about to be attacked
318
+ NotifyEnemiesOfIncomingAttack();
319
+
320
+ // jump to the next combo section
321
+ if (UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance())
322
+ {
323
+ AnimInstance->Montage_JumpToSection(ComboSectionNames[ComboCount], ComboAttackMontage);
324
+ }
325
+ }
326
+ }
327
+ }
328
+ }
329
+
330
+ void ACombatCharacter::CheckChargedAttack()
331
+ {
332
+ // raise the looped charged attack flag
333
+ bHasLoopedChargedAttack = true;
334
+
335
+ // jump to either the loop or the attack section depending on whether we're still holding the charge button
336
+ if (UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance())
337
+ {
338
+ AnimInstance->Montage_JumpToSection(bIsChargingAttack ? ChargeLoopSection : ChargeAttackSection, ChargedAttackMontage);
339
+ }
340
+ }
341
+
342
+ void ACombatCharacter::NotifyEnemiesOfIncomingAttack()
343
+ {
344
+ // sweep for objects in front of the character to be hit by the attack
345
+ TArray<FHitResult> OutHits;
346
+
347
+ // start at the actor location, sweep forward
348
+ const FVector TraceStart = GetActorLocation();
349
+ const FVector TraceEnd = TraceStart + (GetActorForwardVector() * DangerTraceDistance);
350
+
351
+ // check for pawn object types only
352
+ FCollisionObjectQueryParams ObjectParams;
353
+ ObjectParams.AddObjectTypesToQuery(ECC_Pawn);
354
+
355
+ // use a sphere shape for the sweep
356
+ FCollisionShape CollisionShape;
357
+ CollisionShape.SetSphere(DangerTraceRadius);
358
+
359
+ // ignore self
360
+ FCollisionQueryParams QueryParams;
361
+ QueryParams.AddIgnoredActor(this);
362
+
363
+ if (GetWorld()->SweepMultiByObjectType(OutHits, TraceStart, TraceEnd, FQuat::Identity, ObjectParams, CollisionShape, QueryParams))
364
+ {
365
+ // iterate over each object hit
366
+ for (const FHitResult& CurrentHit : OutHits)
367
+ {
368
+ // check if we've hit a damageable actor
369
+ ICombatDamageable* Damageable = Cast<ICombatDamageable>(CurrentHit.GetActor());
370
+
371
+ if (Damageable)
372
+ {
373
+ // notify the enemy
374
+ Damageable->NotifyDanger(GetActorLocation(), this);
375
+ }
376
+ }
377
+ }
378
+ }
379
+
380
+ void ACombatCharacter::ApplyDamage(float Damage, AActor* DamageCauser, const FVector& DamageLocation, const FVector& DamageImpulse)
381
+ {
382
+ // pass the damage event to the actor
383
+ FDamageEvent DamageEvent;
384
+ const float ActualDamage = TakeDamage(Damage, DamageEvent, nullptr, DamageCauser);
385
+
386
+ // only process knockback and effects if we received nonzero damage
387
+ if (ActualDamage > 0.0f)
388
+ {
389
+ // apply the knockback impulse
390
+ GetCharacterMovement()->AddImpulse(DamageImpulse, true);
391
+
392
+ // is the character ragdolling?
393
+ if (GetMesh()->IsSimulatingPhysics())
394
+ {
395
+ // apply an impulse to the ragdoll
396
+ GetMesh()->AddImpulseAtLocation(DamageImpulse * GetMesh()->GetMass(), DamageLocation);
397
+ }
398
+
399
+ // pass control to BP to play effects, etc.
400
+ ReceivedDamage(ActualDamage, DamageLocation, DamageImpulse.GetSafeNormal());
401
+ }
402
+
403
+ }
404
+
405
+ void ACombatCharacter::HandleDeath()
406
+ {
407
+ // disable movement while we're dead
408
+ GetCharacterMovement()->DisableMovement();
409
+
410
+ // enable full ragdoll physics
411
+ GetMesh()->SetSimulatePhysics(true);
412
+
413
+ // hide the life bar
414
+ LifeBar->SetHiddenInGame(true);
415
+
416
+ // pull back the camera
417
+ GetCameraBoom()->TargetArmLength = DeathCameraDistance;
418
+
419
+ // schedule respawning
420
+ GetWorld()->GetTimerManager().SetTimer(RespawnTimer, this, &ACombatCharacter::RespawnCharacter, RespawnTime, false);
421
+ }
422
+
423
+ void ACombatCharacter::ApplyHealing(float Healing, AActor* Healer)
424
+ {
425
+ // stub
426
+ }
427
+
428
+ void ACombatCharacter::NotifyDanger(const FVector& DangerLocation, AActor* DangerSource)
429
+ {
430
+ // stub
431
+ }
432
+
433
+ void ACombatCharacter::RespawnCharacter()
434
+ {
435
+ // destroy the character and let it be respawned by the Player Controller
436
+ Destroy();
437
+ }
438
+
439
+ float ACombatCharacter::TakeDamage(float Damage, struct FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
440
+ {
441
+ // only process damage if the character is still alive
442
+ if (CurrentHP <= 0.0f)
443
+ {
444
+ return 0.0f;
445
+ }
446
+
447
+ // reduce the current HP
448
+ CurrentHP -= Damage;
449
+
450
+ // have we run out of HP?
451
+ if (CurrentHP <= 0.0f)
452
+ {
453
+ // die
454
+ HandleDeath();
455
+ }
456
+ else
457
+ {
458
+ // update the life bar
459
+ LifeBarWidget->SetLifePercentage(CurrentHP / MaxHP);
460
+
461
+ // enable partial ragdoll physics, but keep the pelvis vertical
462
+ GetMesh()->SetPhysicsBlendWeight(0.5f);
463
+ GetMesh()->SetBodySimulatePhysics(PelvisBoneName, false);
464
+ }
465
+
466
+ // return the received damage amount
467
+ return Damage;
468
+ }
469
+
470
+ void ACombatCharacter::Landed(const FHitResult& Hit)
471
+ {
472
+ Super::Landed(Hit);
473
+
474
+ // is the character still alive?
475
+ if (CurrentHP >= 0.0f)
476
+ {
477
+ // disable ragdoll physics
478
+ GetMesh()->SetPhysicsBlendWeight(0.0f);
479
+ }
480
+ }
481
+
482
+ void ACombatCharacter::BeginPlay()
483
+ {
484
+ Super::BeginPlay();
485
+
486
+ // get the life bar from the widget component
487
+ LifeBarWidget = Cast<UCombatLifeBar>(LifeBar->GetUserWidgetObject());
488
+ check(LifeBarWidget);
489
+
490
+ // initialize the camera
491
+ GetCameraBoom()->TargetArmLength = DefaultCameraDistance;
492
+
493
+ // save the relative transform for the mesh so we can reset the ragdoll later
494
+ MeshStartingTransform = GetMesh()->GetRelativeTransform();
495
+
496
+ // set the life bar color
497
+ LifeBarWidget->SetBarColor(LifeBarColor);
498
+
499
+ // reset HP to maximum
500
+ ResetHP();
501
+ }
502
+
503
+ void ACombatCharacter::EndPlay(const EEndPlayReason::Type EndPlayReason)
504
+ {
505
+ Super::EndPlay(EndPlayReason);
506
+
507
+ // clear the respawn timer
508
+ GetWorld()->GetTimerManager().ClearTimer(RespawnTimer);
509
+ }
510
+
511
+ void ACombatCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
512
+ {
513
+ Super::SetupPlayerInputComponent(PlayerInputComponent);
514
+
515
+ // Set up action bindings
516
+ if (UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent))
517
+ {
518
+ // Moving
519
+ EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &ACombatCharacter::Move);
520
+
521
+ // Looking
522
+ EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &ACombatCharacter::Look);
523
+ EnhancedInputComponent->BindAction(MouseLookAction, ETriggerEvent::Triggered, this, &ACombatCharacter::Look);
524
+
525
+ // Combo Attack
526
+ EnhancedInputComponent->BindAction(ComboAttackAction, ETriggerEvent::Started, this, &ACombatCharacter::ComboAttackPressed);
527
+
528
+ // Charged Attack
529
+ EnhancedInputComponent->BindAction(ChargedAttackAction, ETriggerEvent::Started, this, &ACombatCharacter::ChargedAttackPressed);
530
+ EnhancedInputComponent->BindAction(ChargedAttackAction, ETriggerEvent::Completed, this, &ACombatCharacter::ChargedAttackReleased);
531
+
532
+ // Camera Side Toggle
533
+ EnhancedInputComponent->BindAction(ToggleCameraAction, ETriggerEvent::Triggered, this, &ACombatCharacter::ToggleCamera);
534
+ }
535
+ }
536
+
537
+ void ACombatCharacter::NotifyControllerChanged()
538
+ {
539
+ Super::NotifyControllerChanged();
540
+
541
+ // update the respawn transform on the Player Controller
542
+ if (ACombatPlayerController* PC = Cast<ACombatPlayerController>(GetController()))
543
+ {
544
+ PC->SetRespawnTransform(GetActorTransform());
545
+ }
546
+ }
547
+
Source/Pockmon/Variant_Combat/CombatCharacter.h ADDED
@@ -0,0 +1,334 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #pragma once
4
+
5
+ #include "CoreMinimal.h"
6
+ #include "GameFramework/Character.h"
7
+ #include "CombatAttacker.h"
8
+ #include "CombatDamageable.h"
9
+ #include "Animation/AnimInstance.h"
10
+ #include "CombatCharacter.generated.h"
11
+
12
+ class USpringArmComponent;
13
+ class UCameraComponent;
14
+ class UInputAction;
15
+ struct FInputActionValue;
16
+ class UCombatLifeBar;
17
+ class UWidgetComponent;
18
+
19
+ DECLARE_LOG_CATEGORY_EXTERN(LogCombatCharacter, Log, All);
20
+
21
+ /**
22
+ * An enhanced Third Person Character with melee combat capabilities:
23
+ * - Combo attack string
24
+ * - Press and hold charged attack
25
+ * - Damage dealing and reaction
26
+ * - Death
27
+ * - Respawning
28
+ */
29
+ UCLASS(abstract)
30
+ class ACombatCharacter : public ACharacter, public ICombatAttacker, public ICombatDamageable
31
+ {
32
+ GENERATED_BODY()
33
+
34
+ /** Camera boom positioning the camera behind the character */
35
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
36
+ USpringArmComponent* CameraBoom;
37
+
38
+ /** Follow camera */
39
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
40
+ UCameraComponent* FollowCamera;
41
+
42
+ /** Life bar widget component */
43
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
44
+ UWidgetComponent* LifeBar;
45
+
46
+ protected:
47
+
48
+ /** Jump Input Action */
49
+ UPROPERTY(EditAnywhere, Category ="Input")
50
+ UInputAction* JumpAction;
51
+
52
+ /** Move Input Action */
53
+ UPROPERTY(EditAnywhere, Category ="Input")
54
+ UInputAction* MoveAction;
55
+
56
+ /** Look Input Action */
57
+ UPROPERTY(EditAnywhere, Category ="Input")
58
+ UInputAction* LookAction;
59
+
60
+ /** Mouse Look Input Action */
61
+ UPROPERTY(EditAnywhere, Category="Input")
62
+ UInputAction* MouseLookAction;
63
+
64
+ /** Combo Attack Input Action */
65
+ UPROPERTY(EditAnywhere, Category ="Input")
66
+ UInputAction* ComboAttackAction;
67
+
68
+ /** Charged Attack Input Action */
69
+ UPROPERTY(EditAnywhere, Category ="Input")
70
+ UInputAction* ChargedAttackAction;
71
+
72
+ /** Toggle Camera Side Input Action */
73
+ UPROPERTY(EditAnywhere, Category ="Input")
74
+ UInputAction* ToggleCameraAction;
75
+
76
+ /** Max amount of HP the character will have on respawn */
77
+ UPROPERTY(EditAnywhere, Category="Damage", meta = (ClampMin = 0, ClampMax = 100))
78
+ float MaxHP = 5.0f;
79
+
80
+ /** Current amount of HP the character has */
81
+ UPROPERTY(VisibleAnywhere, Category="Damage")
82
+ float CurrentHP = 0.0f;
83
+
84
+ /** Life bar widget fill color */
85
+ UPROPERTY(EditAnywhere, Category="Damage")
86
+ FLinearColor LifeBarColor;
87
+
88
+ /** Name of the pelvis bone, for damage ragdoll physics */
89
+ UPROPERTY(EditAnywhere, Category="Damage")
90
+ FName PelvisBoneName;
91
+
92
+ /** Pointer to the life bar widget */
93
+ UPROPERTY(EditAnywhere, Category="Damage")
94
+ TObjectPtr<UCombatLifeBar> LifeBarWidget;
95
+
96
+ /** Max amount of time that may elapse for a non-combo attack input to not be considered stale */
97
+ UPROPERTY(EditAnywhere, Category="Melee Attack", meta = (ClampMin = 0, ClampMax = 5, Units = "s"))
98
+ float AttackInputCacheTimeTolerance = 1.0f;
99
+
100
+ /** Time at which an attack button was last pressed */
101
+ float CachedAttackInputTime = 0.0f;
102
+
103
+ /** If true, the character is currently playing an attack animation */
104
+ bool bIsAttacking = false;
105
+
106
+ /** Distance ahead of the character that melee attack sphere collision traces will extend */
107
+ UPROPERTY(EditAnywhere, Category="Melee Attack|Trace", meta = (ClampMin = 0, ClampMax = 500, Units="cm"))
108
+ float MeleeTraceDistance = 75.0f;
109
+
110
+ /** Radius of the sphere trace for melee attacks */
111
+ UPROPERTY(EditAnywhere, Category="Melee Attack|Trace", meta = (ClampMin = 0, ClampMax = 200, Units = "cm"))
112
+ float MeleeTraceRadius = 75.0f;
113
+
114
+ /** Distance ahead of the character that enemies will be notified of incoming attacks */
115
+ UPROPERTY(EditAnywhere, Category="Melee Attack|Trace", meta = (ClampMin = 0, ClampMax = 500, Units="cm"))
116
+ float DangerTraceDistance = 300.0f;
117
+
118
+ /** Radius of the sphere trace to notify enemies of incoming attacks */
119
+ UPROPERTY(EditAnywhere, Category="Melee Attack|Trace", meta = (ClampMin = 0, ClampMax = 200, Units = "cm"))
120
+ float DangerTraceRadius = 100.0f;
121
+
122
+ /** Amount of damage a melee attack will deal */
123
+ UPROPERTY(EditAnywhere, Category="Melee Attack|Damage", meta = (ClampMin = 0, ClampMax = 100))
124
+ float MeleeDamage = 1.0f;
125
+
126
+ /** Amount of knockback impulse a melee attack will apply */
127
+ UPROPERTY(EditAnywhere, Category="Melee Attack|Damage", meta = (ClampMin = 0, ClampMax = 1000, Units = "cm/s"))
128
+ float MeleeKnockbackImpulse = 250.0f;
129
+
130
+ /** Amount of upwards impulse a melee attack will apply */
131
+ UPROPERTY(EditAnywhere, Category="Melee Attack|Damage", meta = (ClampMin = 0, ClampMax = 1000, Units = "cm/s"))
132
+ float MeleeLaunchImpulse = 300.0f;
133
+
134
+ /** AnimMontage that will play for combo attacks */
135
+ UPROPERTY(EditAnywhere, Category="Melee Attack|Combo")
136
+ UAnimMontage* ComboAttackMontage;
137
+
138
+ /** Names of the AnimMontage sections that correspond to each stage of the combo attack */
139
+ UPROPERTY(EditAnywhere, Category="Melee Attack|Combo")
140
+ TArray<FName> ComboSectionNames;
141
+
142
+ /** Max amount of time that may elapse for a combo attack input to not be considered stale */
143
+ UPROPERTY(EditAnywhere, Category="Melee Attack|Combo", meta = (ClampMin = 0, ClampMax = 5, Units = "s"))
144
+ float ComboInputCacheTimeTolerance = 0.45f;
145
+
146
+ /** Index of the current stage of the melee attack combo */
147
+ int32 ComboCount = 0;
148
+
149
+ /** AnimMontage that will play for charged attacks */
150
+ UPROPERTY(EditAnywhere, Category="Melee Attack|Charged")
151
+ UAnimMontage* ChargedAttackMontage;
152
+
153
+ /** Name of the AnimMontage section that corresponds to the charge loop */
154
+ UPROPERTY(EditAnywhere, Category="Melee Attack|Charged")
155
+ FName ChargeLoopSection;
156
+
157
+ /** Name of the AnimMontage section that corresponds to the attack */
158
+ UPROPERTY(EditAnywhere, Category="Melee Attack|Charged")
159
+ FName ChargeAttackSection;
160
+
161
+ /** Flag that determines if the player is currently holding the charged attack input */
162
+ bool bIsChargingAttack = false;
163
+
164
+ /** If true, the charged attack hold check has been tested at least once */
165
+ bool bHasLoopedChargedAttack = false;
166
+
167
+ /** Camera boom length while the character is dead */
168
+ UPROPERTY(EditAnywhere, Category="Camera", meta = (ClampMin = 0, ClampMax = 1000, Units = "cm"))
169
+ float DeathCameraDistance = 400.0f;
170
+
171
+ /** Camera boom length when the character respawns */
172
+ UPROPERTY(EditAnywhere, Category="Camera", meta = (ClampMin = 0, ClampMax = 1000, Units = "cm"))
173
+ float DefaultCameraDistance = 100.0f;
174
+
175
+ /** Time to wait before respawning the character */
176
+ UPROPERTY(EditAnywhere, Category="Respawn", meta = (ClampMin = 0, ClampMax = 10, Units = "s"))
177
+ float RespawnTime = 3.0f;
178
+
179
+ /** Attack montage ended delegate */
180
+ FOnMontageEnded OnAttackMontageEnded;
181
+
182
+ /** Character respawn timer */
183
+ FTimerHandle RespawnTimer;
184
+
185
+ /** Copy of the mesh's transform so we can reset it after ragdoll animations */
186
+ FTransform MeshStartingTransform;
187
+
188
+ public:
189
+
190
+ /** Constructor */
191
+ ACombatCharacter();
192
+
193
+ protected:
194
+
195
+ /** Called for movement input */
196
+ void Move(const FInputActionValue& Value);
197
+
198
+ /** Called for looking input */
199
+ void Look(const FInputActionValue& Value);
200
+
201
+ /** Called for combo attack input */
202
+ void ComboAttackPressed();
203
+
204
+ /** Called for combo attack input pressed */
205
+ void ChargedAttackPressed();
206
+
207
+ /** Called for combo attack input released */
208
+ void ChargedAttackReleased();
209
+
210
+ /** Called for toggle camera side input */
211
+ void ToggleCamera();
212
+
213
+ /** BP hook to animate the camera side switch */
214
+ UFUNCTION(BlueprintImplementableEvent, Category="Combat")
215
+ void BP_ToggleCamera();
216
+
217
+ public:
218
+
219
+ /** Handles move inputs from either controls or UI interfaces */
220
+ UFUNCTION(BlueprintCallable, Category="Input")
221
+ virtual void DoMove(float Right, float Forward);
222
+
223
+ /** Handles look inputs from either controls or UI interfaces */
224
+ UFUNCTION(BlueprintCallable, Category="Input")
225
+ virtual void DoLook(float Yaw, float Pitch);
226
+
227
+ /** Handles combo attack pressed from either controls or UI interfaces */
228
+ UFUNCTION(BlueprintCallable, Category="Input")
229
+ virtual void DoComboAttackStart();
230
+
231
+ /** Handles combo attack released from either controls or UI interfaces */
232
+ UFUNCTION(BlueprintCallable, Category="Input")
233
+ virtual void DoComboAttackEnd();
234
+
235
+ /** Handles charged attack pressed from either controls or UI interfaces */
236
+ UFUNCTION(BlueprintCallable, Category="Input")
237
+ virtual void DoChargedAttackStart();
238
+
239
+ /** Handles charged attack released from either controls or UI interfaces */
240
+ UFUNCTION(BlueprintCallable, Category="Input")
241
+ virtual void DoChargedAttackEnd();
242
+
243
+ protected:
244
+
245
+ /** Resets the character's current HP to maximum */
246
+ void ResetHP();
247
+
248
+ /** Performs a combo attack */
249
+ void ComboAttack();
250
+
251
+ /** Performs a charged attack */
252
+ void ChargedAttack();
253
+
254
+ /** Called from a delegate when the attack montage ends */
255
+ void AttackMontageEnded(UAnimMontage* Montage, bool bInterrupted);
256
+
257
+
258
+ public:
259
+
260
+ // ~begin CombatAttacker interface
261
+
262
+ /** Performs the collision check for an attack */
263
+ virtual void DoAttackTrace(FName DamageSourceBone) override;
264
+
265
+ /** Performs the combo string check */
266
+ virtual void CheckCombo() override;
267
+
268
+ /** Performs the charged attack hold check */
269
+ virtual void CheckChargedAttack() override;
270
+
271
+ // ~end CombatAttacker interface
272
+
273
+ // ~begin CombatDamageable interface
274
+
275
+ /** Notifies nearby enemies that an attack is coming so they can react */
276
+ void NotifyEnemiesOfIncomingAttack();
277
+
278
+ /** Handles damage and knockback events */
279
+ virtual void ApplyDamage(float Damage, AActor* DamageCauser, const FVector& DamageLocation, const FVector& DamageImpulse) override;
280
+
281
+ /** Handles death events */
282
+ virtual void HandleDeath() override;
283
+
284
+ /** Handles healing events */
285
+ virtual void ApplyHealing(float Healing, AActor* Healer) override;
286
+
287
+ /** Allows reaction to incoming attacks */
288
+ virtual void NotifyDanger(const FVector& DangerLocation, AActor* DangerSource) override;
289
+
290
+ // ~end CombatDamageable interface
291
+
292
+ /** Called from the respawn timer to destroy and re-create the character */
293
+ void RespawnCharacter();
294
+
295
+ public:
296
+
297
+ /** Overrides the default TakeDamage functionality */
298
+ virtual float TakeDamage(float Damage, struct FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser) override;
299
+
300
+ /** Overrides landing to reset damage ragdoll physics */
301
+ virtual void Landed(const FHitResult& Hit) override;
302
+
303
+ protected:
304
+
305
+ /** Blueprint handler to play damage dealt effects */
306
+ UFUNCTION(BlueprintImplementableEvent, Category="Combat")
307
+ void DealtDamage(float Damage, const FVector& ImpactPoint);
308
+
309
+ /** Blueprint handler to play damage received effects */
310
+ UFUNCTION(BlueprintImplementableEvent, Category="Combat")
311
+ void ReceivedDamage(float Damage, const FVector& ImpactPoint, const FVector& DamageDirection);
312
+
313
+ protected:
314
+
315
+ /** Initialization */
316
+ virtual void BeginPlay() override;
317
+
318
+ /** Cleanup */
319
+ virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
320
+
321
+ /** Handles input bindings */
322
+ virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
323
+
324
+ /** Handles possessed initialization */
325
+ virtual void NotifyControllerChanged() override;
326
+
327
+ public:
328
+
329
+ /** Returns CameraBoom subobject **/
330
+ FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; }
331
+
332
+ /** Returns FollowCamera subobject **/
333
+ FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }
334
+ };
Source/Pockmon/Variant_Combat/CombatGameMode.cpp ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+
4
+ #include "Variant_Combat/CombatGameMode.h"
5
+
6
+ ACombatGameMode::ACombatGameMode()
7
+ {
8
+
9
+ }
Source/Pockmon/Variant_Combat/CombatGameMode.h ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #pragma once
4
+
5
+ #include "CoreMinimal.h"
6
+ #include "GameFramework/GameModeBase.h"
7
+ #include "CombatGameMode.generated.h"
8
+
9
+ /**
10
+ * Simple GameMode for a third person combat game
11
+ */
12
+ UCLASS(abstract)
13
+ class ACombatGameMode : public AGameModeBase
14
+ {
15
+ GENERATED_BODY()
16
+
17
+ public:
18
+
19
+ ACombatGameMode();
20
+ };
Source/Pockmon/Variant_Combat/CombatPlayerController.cpp ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+
4
+ #include "Variant_Combat/CombatPlayerController.h"
5
+ #include "EnhancedInputSubsystems.h"
6
+ #include "InputMappingContext.h"
7
+ #include "Kismet/GameplayStatics.h"
8
+ #include "GameFramework/PlayerStart.h"
9
+ #include "CombatCharacter.h"
10
+ #include "Engine/LocalPlayer.h"
11
+ #include "Engine/World.h"
12
+ #include "Blueprint/UserWidget.h"
13
+ #include "Pockmon.h"
14
+ #include "Widgets/Input/SVirtualJoystick.h"
15
+
16
+ void ACombatPlayerController::BeginPlay()
17
+ {
18
+ Super::BeginPlay();
19
+
20
+ // only spawn touch controls on local player controllers
21
+ if (ShouldUseTouchControls() && IsLocalPlayerController())
22
+ {
23
+ // spawn the mobile controls widget
24
+ MobileControlsWidget = CreateWidget<UUserWidget>(this, MobileControlsWidgetClass);
25
+
26
+ if (MobileControlsWidget)
27
+ {
28
+ // add the controls to the player screen
29
+ MobileControlsWidget->AddToPlayerScreen(0);
30
+
31
+ } else {
32
+
33
+ UE_LOG(LogPockmon, Error, TEXT("Could not spawn mobile controls widget."));
34
+
35
+ }
36
+
37
+ }
38
+ }
39
+
40
+ void ACombatPlayerController::SetupInputComponent()
41
+ {
42
+ Super::SetupInputComponent();
43
+
44
+ // only add IMCs for local player controllers
45
+ if (IsLocalPlayerController())
46
+ {
47
+ // add the input mapping context
48
+ if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer()))
49
+ {
50
+ for (UInputMappingContext* CurrentContext : DefaultMappingContexts)
51
+ {
52
+ Subsystem->AddMappingContext(CurrentContext, 0);
53
+ }
54
+
55
+ // only add these IMCs if we're not using mobile touch input
56
+ if (!ShouldUseTouchControls())
57
+ {
58
+ for (UInputMappingContext* CurrentContext : MobileExcludedMappingContexts)
59
+ {
60
+ Subsystem->AddMappingContext(CurrentContext, 0);
61
+ }
62
+ }
63
+ }
64
+ }
65
+ }
66
+
67
+ void ACombatPlayerController::OnPossess(APawn* InPawn)
68
+ {
69
+ Super::OnPossess(InPawn);
70
+
71
+ // subscribe to the pawn's OnDestroyed delegate
72
+ InPawn->OnDestroyed.AddDynamic(this, &ACombatPlayerController::OnPawnDestroyed);
73
+ }
74
+
75
+ void ACombatPlayerController::SetRespawnTransform(const FTransform& NewRespawn)
76
+ {
77
+ // save the new respawn transform
78
+ RespawnTransform = NewRespawn;
79
+ }
80
+
81
+ void ACombatPlayerController::OnPawnDestroyed(AActor* DestroyedActor)
82
+ {
83
+ // spawn a new character at the respawn transform
84
+ if (ACombatCharacter* RespawnedCharacter = GetWorld()->SpawnActor<ACombatCharacter>(CharacterClass, RespawnTransform))
85
+ {
86
+ // possess the character
87
+ Possess(RespawnedCharacter);
88
+ }
89
+ }
90
+
91
+ bool ACombatPlayerController::ShouldUseTouchControls() const
92
+ {
93
+ // are we on a mobile platform? Should we force touch?
94
+ return SVirtualJoystick::ShouldDisplayTouchInterface() || bForceTouchControls;
95
+ }
Source/Pockmon/Variant_Combat/CombatPlayerController.h ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #pragma once
4
+
5
+ #include "CoreMinimal.h"
6
+ #include "GameFramework/PlayerController.h"
7
+ #include "CombatPlayerController.generated.h"
8
+
9
+ class UInputMappingContext;
10
+ class ACombatCharacter;
11
+
12
+ /**
13
+ * Simple Player Controller for a third person combat game
14
+ * Manages input mappings
15
+ * Respawns the player character at the checkpoint when it's destroyed
16
+ */
17
+ UCLASS(abstract, Config="Game")
18
+ class ACombatPlayerController : public APlayerController
19
+ {
20
+ GENERATED_BODY()
21
+
22
+ protected:
23
+
24
+ /** Input mapping context for this player */
25
+ UPROPERTY(EditAnywhere, Category="Input|Input Mappings")
26
+ TArray<UInputMappingContext*> DefaultMappingContexts;
27
+
28
+ /** Input Mapping Contexts */
29
+ UPROPERTY(EditAnywhere, Category="Input|Input Mappings")
30
+ TArray<UInputMappingContext*> MobileExcludedMappingContexts;
31
+
32
+ /** Mobile controls widget to spawn */
33
+ UPROPERTY(EditAnywhere, Category="Input|Touch Controls")
34
+ TSubclassOf<UUserWidget> MobileControlsWidgetClass;
35
+
36
+ /** Pointer to the mobile controls widget */
37
+ UPROPERTY()
38
+ TObjectPtr<UUserWidget> MobileControlsWidget;
39
+
40
+ /** If true, the player will use UMG touch controls even if not playing on mobile platforms */
41
+ UPROPERTY(EditAnywhere, Config, Category = "Input|Touch Controls")
42
+ bool bForceTouchControls = false;
43
+
44
+ /** Character class to respawn when the possessed pawn is destroyed */
45
+ UPROPERTY(EditAnywhere, Category="Respawn")
46
+ TSubclassOf<ACombatCharacter> CharacterClass;
47
+
48
+ /** Transform to respawn the character at. Can be set to create checkpoints */
49
+ FTransform RespawnTransform;
50
+
51
+ protected:
52
+
53
+ /** Gameplay initialization */
54
+ virtual void BeginPlay() override;
55
+
56
+ /** Initialize input bindings */
57
+ virtual void SetupInputComponent() override;
58
+
59
+ /** Pawn initialization */
60
+ virtual void OnPossess(APawn* InPawn) override;
61
+
62
+ public:
63
+
64
+ /** Updates the character respawn transform */
65
+ void SetRespawnTransform(const FTransform& NewRespawn);
66
+
67
+ protected:
68
+
69
+ /** Called if the possessed pawn is destroyed */
70
+ UFUNCTION()
71
+ void OnPawnDestroyed(AActor* DestroyedActor);
72
+
73
+ /** Returns true if the player should use UMG touch controls */
74
+ bool ShouldUseTouchControls() const;
75
+
76
+ };
Source/Pockmon/Variant_Combat/Gameplay/CombatActivationVolume.cpp ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+
4
+ #include "CombatActivationVolume.h"
5
+ #include "Components/BoxComponent.h"
6
+ #include "GameFramework/Character.h"
7
+ #include "CombatActivatable.h"
8
+
9
+ ACombatActivationVolume::ACombatActivationVolume()
10
+ {
11
+ PrimaryActorTick.bCanEverTick = false;
12
+
13
+ // create the box volume
14
+ RootComponent = Box = CreateDefaultSubobject<UBoxComponent>(TEXT("Box"));
15
+ check(Box);
16
+
17
+ // set the box's extent
18
+ Box->SetBoxExtent(FVector(500.0f, 500.0f, 500.0f));
19
+
20
+ // set the default collision profile to overlap all dynamic
21
+ Box->SetCollisionProfileName(FName("OverlapAllDynamic"));
22
+
23
+ // bind the begin overlap
24
+ Box->OnComponentBeginOverlap.AddDynamic(this, &ACombatActivationVolume::OnOverlap);
25
+ }
26
+
27
+ void ACombatActivationVolume::OnOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
28
+ {
29
+ // has a Character entered the volume?
30
+ ACharacter* PlayerCharacter = Cast<ACharacter>(OtherActor);
31
+
32
+ if (PlayerCharacter)
33
+ {
34
+ // is the Character controlled by a player
35
+ if (PlayerCharacter->IsPlayerControlled())
36
+ {
37
+ // process the actors to activate list
38
+ for (AActor* CurrentActor : ActorsToActivate)
39
+ {
40
+ // is the referenced actor activatable?
41
+ if(ICombatActivatable* Activatable = Cast<ICombatActivatable>(CurrentActor))
42
+ {
43
+ Activatable->ActivateInteraction(PlayerCharacter);
44
+ }
45
+ }
46
+ }
47
+ }
48
+
49
+ }
Source/Pockmon/Variant_Combat/Gameplay/CombatActivationVolume.h ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #pragma once
4
+
5
+ #include "CoreMinimal.h"
6
+ #include "GameFramework/Actor.h"
7
+ #include "CombatActivationVolume.generated.h"
8
+
9
+ class UBoxComponent;
10
+
11
+ /**
12
+ * A simple volume that activates a list of actors when the player pawn enters.
13
+ */
14
+ UCLASS()
15
+ class ACombatActivationVolume : public AActor
16
+ {
17
+ GENERATED_BODY()
18
+
19
+ /** Collision box volume */
20
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category ="Components", meta = (AllowPrivateAccess = "true"))
21
+ UBoxComponent* Box;
22
+
23
+ protected:
24
+
25
+ /** List of actors to activate when this volume is entered */
26
+ UPROPERTY(EditAnywhere, Category="Activation Volume")
27
+ TArray<AActor*> ActorsToActivate;
28
+
29
+ public:
30
+
31
+ /** Constructor */
32
+ ACombatActivationVolume();
33
+
34
+ protected:
35
+
36
+ /** Handles overlaps with the box volume */
37
+ UFUNCTION()
38
+ void OnOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
39
+
40
+ };
Source/Pockmon/Variant_Combat/Gameplay/CombatCheckpointVolume.cpp ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+
4
+ #include "CombatCheckpointVolume.h"
5
+ #include "CombatCharacter.h"
6
+ #include "CombatPlayerController.h"
7
+
8
+ ACombatCheckpointVolume::ACombatCheckpointVolume()
9
+ {
10
+ // create the box volume
11
+ RootComponent = Box = CreateDefaultSubobject<UBoxComponent>(TEXT("Box"));
12
+ check(Box);
13
+
14
+ // set the box's extent
15
+ Box->SetBoxExtent(FVector(500.0f, 500.0f, 500.0f));
16
+
17
+ // set the default collision profile to overlap all dynamic
18
+ Box->SetCollisionProfileName(FName("OverlapAllDynamic"));
19
+
20
+ // bind the begin overlap
21
+ Box->OnComponentBeginOverlap.AddDynamic(this, &ACombatCheckpointVolume::OnOverlap);
22
+ }
23
+
24
+ void ACombatCheckpointVolume::OnOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
25
+ {
26
+ // ensure we use this only once
27
+ if (bCheckpointUsed)
28
+ {
29
+ return;
30
+ }
31
+
32
+ // has the player entered this volume?
33
+ ACombatCharacter* PlayerCharacter = Cast<ACombatCharacter>(OtherActor);
34
+
35
+ if (PlayerCharacter)
36
+ {
37
+ if (ACombatPlayerController* PC = Cast<ACombatPlayerController>(PlayerCharacter->GetController()))
38
+ {
39
+ // raise the checkpoint used flag
40
+ bCheckpointUsed = true;
41
+
42
+ // update the player's respawn checkpoint
43
+ PC->SetRespawnTransform(PlayerCharacter->GetActorTransform());
44
+ }
45
+
46
+ }
47
+ }
Source/Pockmon/Variant_Combat/Gameplay/CombatCheckpointVolume.h ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #pragma once
4
+
5
+ #include "CoreMinimal.h"
6
+ #include "GameFramework/Actor.h"
7
+ #include "Components/BoxComponent.h"
8
+ #include "CombatCheckpointVolume.generated.h"
9
+
10
+ UCLASS(abstract)
11
+ class ACombatCheckpointVolume : public AActor
12
+ {
13
+ GENERATED_BODY()
14
+
15
+ /** Collision box volume */
16
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Components, meta = (AllowPrivateAccess = "true"))
17
+ UBoxComponent* Box;
18
+
19
+ public:
20
+
21
+ /** Constructor */
22
+ ACombatCheckpointVolume();
23
+
24
+ protected:
25
+
26
+ /** Set to true after use to avoid accidentally resetting the checkpoint */
27
+ bool bCheckpointUsed = false;
28
+
29
+ /** Handles overlaps with the box volume */
30
+ UFUNCTION()
31
+ void OnOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
32
+ };
Source/Pockmon/Variant_Combat/Gameplay/CombatDamageableBox.cpp ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+
4
+ #include "CombatDamageableBox.h"
5
+ #include "Components/StaticMeshComponent.h"
6
+ #include "TimerManager.h"
7
+ #include "Engine/World.h"
8
+
9
+ ACombatDamageableBox::ACombatDamageableBox()
10
+ {
11
+ PrimaryActorTick.bCanEverTick = false;
12
+
13
+ // create the mesh
14
+ RootComponent = Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
15
+
16
+ // set the collision properties
17
+ Mesh->SetCollisionProfileName(FName("BlockAllDynamic"));
18
+
19
+ // enable physics
20
+ Mesh->SetSimulatePhysics(true);
21
+
22
+ // disable navigation relevance so boxes don't affect NavMesh generation
23
+ Mesh->bNavigationRelevant = false;
24
+ }
25
+
26
+ void ACombatDamageableBox::RemoveFromLevel()
27
+ {
28
+ // destroy this actor
29
+ Destroy();
30
+ }
31
+
32
+ void ACombatDamageableBox::EndPlay(EEndPlayReason::Type EndPlayReason)
33
+ {
34
+ Super::EndPlay(EndPlayReason);
35
+
36
+ // clear the death timer
37
+ GetWorld()->GetTimerManager().ClearTimer(DeathTimer);
38
+ }
39
+
40
+ void ACombatDamageableBox::ApplyDamage(float Damage, AActor* DamageCauser, const FVector& DamageLocation, const FVector& DamageImpulse)
41
+ {
42
+ // only process damage if we still have HP
43
+ if (CurrentHP > 0.0f)
44
+ {
45
+ // apply the damage
46
+ CurrentHP -= Damage;
47
+
48
+ // are we dead?
49
+ if (CurrentHP <= 0.0f)
50
+ {
51
+ HandleDeath();
52
+ }
53
+
54
+ // apply a physics impulse to the box, ignoring its mass
55
+ Mesh->AddImpulseAtLocation(DamageImpulse * Mesh->GetMass(), DamageLocation);
56
+
57
+ // call the BP handler to play effects, etc.
58
+ OnBoxDamaged(DamageLocation, DamageImpulse);
59
+ }
60
+ }
61
+
62
+ void ACombatDamageableBox::HandleDeath()
63
+ {
64
+ // change the collision object type to Visibility so we ignore most interactions but still retain physics collisions
65
+ Mesh->SetCollisionObjectType(ECC_Visibility);
66
+
67
+ // call the BP handler to play effects, etc.
68
+ OnBoxDestroyed();
69
+
70
+ // set up the death cleanup timer
71
+ GetWorld()->GetTimerManager().SetTimer(DeathTimer, this, &ACombatDamageableBox::RemoveFromLevel, DeathDelayTime);
72
+ }
73
+
74
+ void ACombatDamageableBox::ApplyHealing(float Healing, AActor* Healer)
75
+ {
76
+ // stub
77
+ }
78
+
79
+ void ACombatDamageableBox::NotifyDanger(const FVector& DangerLocation, AActor* DangerSource)
80
+ {
81
+ // stub
82
+ }
83
+
Source/Pockmon/Variant_Combat/Gameplay/CombatDamageableBox.h ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #pragma once
4
+
5
+ #include "CoreMinimal.h"
6
+ #include "GameFramework/Actor.h"
7
+ #include "CombatDamageable.h"
8
+ #include "CombatDamageableBox.generated.h"
9
+
10
+ /**
11
+ * A simple physics box that reacts to damage through the ICombatDamageable interface
12
+ */
13
+ UCLASS(abstract)
14
+ class ACombatDamageableBox : public AActor, public ICombatDamageable
15
+ {
16
+ GENERATED_BODY()
17
+
18
+ /** Damageable box mesh */
19
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
20
+ UStaticMeshComponent* Mesh;
21
+
22
+ public:
23
+
24
+ /** Constructor */
25
+ ACombatDamageableBox();
26
+
27
+ protected:
28
+
29
+ /** Amount of HP this box starts with. */
30
+ UPROPERTY(EditAnywhere, Category="Damage")
31
+ float CurrentHP = 3.0f;
32
+
33
+ /** Time to wait before we remove this box from the level. */
34
+ UPROPERTY(EditAnywhere, Category="Damage", meta = (ClampMin = 0, ClampMax = 10, Units = "s"))
35
+ float DeathDelayTime = 6.0f;
36
+
37
+ /** Timer to defer destruction of this box after its HP are depleted */
38
+ FTimerHandle DeathTimer;
39
+
40
+ /** Blueprint damage handler for effect playback */
41
+ UFUNCTION(BlueprintImplementableEvent, Category="Damage")
42
+ void OnBoxDamaged(const FVector& DamageLocation, const FVector& DamageImpulse);
43
+
44
+ /** Blueprint destruction handler for effect playback */
45
+ UFUNCTION(BlueprintImplementableEvent, Category="Damage")
46
+ void OnBoxDestroyed();
47
+
48
+ /** Timer callback to remove the box from the level after it dies */
49
+ void RemoveFromLevel();
50
+
51
+ public:
52
+
53
+ /** EndPlay cleanup */
54
+ void EndPlay(EEndPlayReason::Type EndPlayReason) override;
55
+
56
+ // ~Begin CombatDamageable interface
57
+
58
+ /** Handles damage and knockback events */
59
+ virtual void ApplyDamage(float Damage, AActor* DamageCauser, const FVector& DamageLocation, const FVector& DamageImpulse) override;
60
+
61
+ /** Handles death events */
62
+ virtual void HandleDeath() override;
63
+
64
+ /** Handles healing events */
65
+ virtual void ApplyHealing(float Healing, AActor* Healer) override;
66
+
67
+ /** Allows reaction to incoming attacks */
68
+ virtual void NotifyDanger(const FVector& DangerLocation, AActor* DangerSource) override;
69
+
70
+ // ~End CombatDamageable interface
71
+ };
Source/Pockmon/Variant_Combat/Gameplay/CombatDummy.cpp ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+
4
+ #include "CombatDummy.h"
5
+ #include "Components/SceneComponent.h"
6
+ #include "Components/StaticMeshComponent.h"
7
+ #include "PhysicsEngine/PhysicsConstraintComponent.h"
8
+
9
+ ACombatDummy::ACombatDummy()
10
+ {
11
+ PrimaryActorTick.bCanEverTick = true;
12
+
13
+ // create the root
14
+ Root = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
15
+ SetRootComponent(Root);
16
+
17
+ // create the base plate
18
+ BasePlate = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Base Plate"));
19
+ BasePlate->SetupAttachment(RootComponent);
20
+
21
+ // create the dummy
22
+ Dummy = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Dummy"));
23
+ Dummy->SetupAttachment(RootComponent);
24
+
25
+ Dummy->SetSimulatePhysics(true);
26
+
27
+ // create the physics constraint
28
+ PhysicsConstraint = CreateDefaultSubobject<UPhysicsConstraintComponent>(TEXT("Physics Constraint"));
29
+ PhysicsConstraint->SetupAttachment(RootComponent);
30
+
31
+ PhysicsConstraint->SetConstrainedComponents(BasePlate, NAME_None, Dummy, NAME_None);
32
+ }
33
+
34
+ void ACombatDummy::ApplyDamage(float Damage, AActor* DamageCauser, const FVector& DamageLocation, const FVector& DamageImpulse)
35
+ {
36
+ // apply impulse to the dummy
37
+ Dummy->AddImpulseAtLocation(DamageImpulse, DamageLocation);
38
+
39
+ // call the BP handler
40
+ BP_OnDummyDamaged(DamageLocation, DamageImpulse.GetSafeNormal());
41
+ }
42
+
43
+ void ACombatDummy::HandleDeath()
44
+ {
45
+ // unused
46
+ }
47
+
48
+ void ACombatDummy::ApplyHealing(float Healing, AActor* Healer)
49
+ {
50
+ // unused
51
+ }
52
+
53
+ void ACombatDummy::NotifyDanger(const FVector& DangerLocation, AActor* DangerSource)
54
+ {
55
+ // unused
56
+ }
Source/Pockmon/Variant_Combat/Gameplay/CombatDummy.h ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #pragma once
4
+
5
+ #include "CoreMinimal.h"
6
+ #include "GameFramework/Actor.h"
7
+ #include "CombatDamageable.h"
8
+ #include "CombatDummy.generated.h"
9
+
10
+ class UStaticMeshComponent;
11
+ class UPhysicsConstraintComponent;
12
+
13
+ /**
14
+ * A simple invincible combat training dummy
15
+ */
16
+ UCLASS(abstract)
17
+ class ACombatDummy : public AActor, public ICombatDamageable
18
+ {
19
+ GENERATED_BODY()
20
+
21
+ /** Root component */
22
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
23
+ USceneComponent* Root;
24
+
25
+ /** Static base plate */
26
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
27
+ UStaticMeshComponent* BasePlate;
28
+
29
+ /** Physics enabled dummy mesh */
30
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
31
+ UStaticMeshComponent* Dummy;
32
+
33
+ /** Physics constraint holding the dummy and base plate together */
34
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
35
+ UPhysicsConstraintComponent* PhysicsConstraint;
36
+
37
+ public:
38
+
39
+ /** Constructor */
40
+ ACombatDummy();
41
+
42
+ // ~Begin CombatDamageable interface
43
+
44
+ /** Handles damage and knockback events */
45
+ virtual void ApplyDamage(float Damage, AActor* DamageCauser, const FVector& DamageLocation, const FVector& DamageImpulse) override;
46
+
47
+ /** Handles death events */
48
+ virtual void HandleDeath() override;
49
+
50
+ /** Handles healing events */
51
+ virtual void ApplyHealing(float Healing, AActor* Healer) override;
52
+
53
+ /** Allows reaction to incoming attacks */
54
+ virtual void NotifyDanger(const FVector& DangerLocation, AActor* DangerSource) override;
55
+
56
+ // ~End CombatDamageable interface
57
+
58
+ protected:
59
+
60
+ /** Blueprint handle to apply damage effects */
61
+ UFUNCTION(BlueprintImplementableEvent, Category="Combat", meta = (DisplayName = "On Dummy Damaged"))
62
+ void BP_OnDummyDamaged(const FVector& Location, const FVector& Direction);
63
+ };
Source/Pockmon/Variant_Combat/Gameplay/CombatLavaFloor.cpp ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+
4
+ #include "CombatLavaFloor.h"
5
+ #include "CombatDamageable.h"
6
+ #include "Components/StaticMeshComponent.h"
7
+
8
+ ACombatLavaFloor::ACombatLavaFloor()
9
+ {
10
+ PrimaryActorTick.bCanEverTick = false;
11
+
12
+ // create the mesh
13
+ RootComponent = Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
14
+
15
+ // bind the hit handler
16
+ Mesh->OnComponentHit.AddDynamic(this, &ACombatLavaFloor::OnFloorHit);
17
+ }
18
+
19
+ void ACombatLavaFloor::OnFloorHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
20
+ {
21
+ // check if the hit actor is damageable by casting to the interface
22
+ if (ICombatDamageable* Damageable = Cast<ICombatDamageable>(OtherActor))
23
+ {
24
+ // damage the actor
25
+ Damageable->ApplyDamage(Damage, this, Hit.ImpactPoint, FVector::ZeroVector);
26
+ }
27
+ }
Source/Pockmon/Variant_Combat/Gameplay/CombatLavaFloor.h ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #pragma once
4
+
5
+ #include "CoreMinimal.h"
6
+ #include "GameFramework/Actor.h"
7
+ #include "CombatLavaFloor.generated.h"
8
+
9
+ class UStaticMeshComponent;
10
+ class UPrimitiveComponent;
11
+
12
+ /**
13
+ * A basic actor that applies damage on contact through the ICombatDamageable interface.
14
+ */
15
+ UCLASS(abstract)
16
+ class ACombatLavaFloor : public AActor
17
+ {
18
+ GENERATED_BODY()
19
+
20
+ /** Floor mesh */
21
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
22
+ UStaticMeshComponent* Mesh;
23
+
24
+ protected:
25
+
26
+ /** Amount of damage to deal on contact */
27
+ UPROPERTY(EditAnywhere, Category="Damage")
28
+ float Damage = 10000.0f;
29
+
30
+ public:
31
+
32
+ /** Constructor */
33
+ ACombatLavaFloor();
34
+
35
+ protected:
36
+
37
+ /** Blocking hit handler */
38
+ UFUNCTION()
39
+ void OnFloorHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
40
+ };
Source/Pockmon/Variant_Combat/Interfaces/CombatActivatable.cpp ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+
4
+ #include "CombatActivatable.h"
Source/Pockmon/Variant_Combat/Interfaces/CombatActivatable.h ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #pragma once
4
+
5
+ #include "CoreMinimal.h"
6
+ #include "UObject/Interface.h"
7
+ #include "CombatActivatable.generated.h"
8
+
9
+ /**
10
+ * Interactable Interface
11
+ * Provides a context-agnostic way of activating, deactivating or toggling actors
12
+ */
13
+ UINTERFACE(MinimalAPI, NotBlueprintable)
14
+ class UCombatActivatable : public UInterface
15
+ {
16
+ GENERATED_BODY()
17
+ };
18
+
19
+ class ICombatActivatable
20
+ {
21
+ GENERATED_BODY()
22
+
23
+ public:
24
+
25
+ /** Toggles the Interactable Actor */
26
+ UFUNCTION(BlueprintCallable, Category="Activatable")
27
+ virtual void ToggleInteraction(AActor* ActivationInstigator) = 0;
28
+
29
+ /** Activates the Interactable Actor */
30
+ UFUNCTION(BlueprintCallable, Category="Activatable")
31
+ virtual void ActivateInteraction(AActor* ActivationInstigator) = 0;
32
+
33
+ /** Deactivates the Interactable Actor */
34
+ UFUNCTION(BlueprintCallable, Category="Activatable")
35
+ virtual void DeactivateInteraction(AActor* ActivationInstigator) = 0;
36
+ };
Source/Pockmon/Variant_Combat/Interfaces/CombatAttacker.cpp ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+
4
+ #include "CombatAttacker.h"
Source/Pockmon/Variant_Combat/Interfaces/CombatAttacker.h ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #pragma once
4
+
5
+ #include "CoreMinimal.h"
6
+ #include "UObject/Interface.h"
7
+ #include "CombatAttacker.generated.h"
8
+
9
+ /**
10
+ * CombatAttacker Interface
11
+ * Provides common functionality to trigger attack animation events.
12
+ */
13
+ UINTERFACE(MinimalAPI, NotBlueprintable)
14
+ class UCombatAttacker : public UInterface
15
+ {
16
+ GENERATED_BODY()
17
+ };
18
+
19
+ class ICombatAttacker
20
+ {
21
+ GENERATED_BODY()
22
+
23
+ public:
24
+
25
+ /** Performs an attack's collision check. Usually called from a montage's AnimNotify */
26
+ UFUNCTION(BlueprintCallable, Category="Attacker")
27
+ virtual void DoAttackTrace(FName DamageSourceBone) = 0;
28
+
29
+ /** Performs a combo attack's check to continue the string. Usually called from a montage's AnimNotify */
30
+ UFUNCTION(BlueprintCallable, Category="Attacker")
31
+ virtual void CheckCombo() = 0;
32
+
33
+ /** Performs a charged attack's check to loop the charge animation. Usually called from a montage's AnimNotify */
34
+ UFUNCTION(BlueprintCallable, Category="Attacker")
35
+ virtual void CheckChargedAttack() = 0;
36
+ };
Source/Pockmon/Variant_Combat/Interfaces/CombatDamageable.cpp ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+
4
+ #include "CombatDamageable.h"
5
+
6
+ // Add default functionality here for any ICombatDamageable functions that are not pure virtual.
Source/Pockmon/Variant_Combat/Interfaces/CombatDamageable.h ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ #pragma once
4
+
5
+ #include "CoreMinimal.h"
6
+ #include "UObject/Interface.h"
7
+ #include "CombatDamageable.generated.h"
8
+
9
+ /**
10
+ * CombatDamageable interface
11
+ * Provides functionality to handle damage, healing, knockback and death
12
+ * Also provides functionality to warn characters of incoming sources of damage
13
+ */
14
+ UINTERFACE(MinimalAPI, NotBlueprintable)
15
+ class UCombatDamageable : public UInterface
16
+ {
17
+ GENERATED_BODY()
18
+ };
19
+
20
+ class ICombatDamageable
21
+ {
22
+ GENERATED_BODY()
23
+
24
+ public:
25
+
26
+ /** Handles damage and knockback events */
27
+ UFUNCTION(BlueprintCallable, Category="Damageable")
28
+ virtual void ApplyDamage(float Damage, AActor* DamageCauser, const FVector& DamageLocation, const FVector& DamageImpulse) = 0;
29
+
30
+ /** Handles death events */
31
+ UFUNCTION(BlueprintCallable, Category="Damageable")
32
+ virtual void HandleDeath() = 0;
33
+
34
+ /** Handles healing events */
35
+ UFUNCTION(BlueprintCallable, Category="Damageable")
36
+ virtual void ApplyHealing(float Healing, AActor* Healer) = 0;
37
+
38
+ /** Notifies the actor of impending danger such as an incoming hit, allowing it to react. */
39
+ UFUNCTION(BlueprintCallable, Category="Damageable")
40
+ virtual void NotifyDanger(const FVector& DangerLocation, AActor* DangerSource) = 0;
41
+ };