> For the complete documentation index, see [llms.txt](https://luisramirez.gitbook.io/godot-2.5d-rpg-course/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://luisramirez.gitbook.io/godot-2.5d-rpg-course/section-6-combat-system.md).

# Section 6: Combat System

In this section, we added a combat system for players and enemies to attack each other and take damage.

## Section Intro - Combat System

There are no notes for this lecture.

## Player Combo Attacks

In this lecture, we added two animations for player attacks and created the player attack state. There are no notes for this lecture.

## Combo Counters

In this lecture, we tracked the player attacks using a counter. When we play the animation, we're combining the `ANIM_ATTACK` constant and `comboCounter` field to generate the name for the animation to play.

```csharp
characterNode.AnimPlayerNode.Play(
    GameConstants.ANIM_ATTACK + comboCounter
);
```

Next, we subscribed to a signal called `AnimationFinished` from the animation player node. This signal gets emitted when the animation is finished playing.

```csharp
characterNode.AnimPlayerNode.AnimationFinished += HandleAnimationFinished;
```

When our method gets called, we're wrapping the comboCounter field. Something to keep in mind is that the `Mathf.Wrap` method's maximum threshold argument wraps the value when the threshold is met or exceeded. So, the max threshold is incremented by 1 so that the value doesn't get wrapped too early.

```csharp
comboCounter = Mathf.Wrap(comboCounter, 1, maxComboCount + 1);
```

## Resetting the Combo Counter With a Timer

In this lecture, we decided to reset the combo counter if the player stops attacking after a few seconds. We used the timer node's `Timeout` signal for this event. During subscription, we used a lambda function.

```csharp
comboTimerNode.Timeout += () => comboCounter = 1;
```

Lambda functions are just shorthand solutions for defining methods. If the signal gets emitted, the function gets executed. In this method, we're setting the `comboCounter` field to `1`.

The timer gets started every time we exit the attack state. By doing so, the timer resets itself even if it's still running.

```csharp
comboTimerNode.Start();
```

## Hitboxes and Hurtboxes

In this lecture, we took the time to set up hitboxes and hurtboxes with `Area3D` nodes.&#x20;

* **Hitboxes** - Cause damage. (Ex: Swords, Arrows, Floor Spikes)
* **Hurtbox** - Take damage. (Ex: Players, Enemies)

During this process, we subscribed to the `AreaEntered` signal on the `Area3D` node. Unlike the `BodyEntered` signal, this signal gets emitted when another area has been detected.&#x20;

```csharp
HurtboxNode.AreaEntered += HandleHurtboxEntered;
```

Next, we printed a message. For this example, we used string interpolation, which allows us to inject variable values into a string. First, the string must start with the `$` character. Next, we must wrap the variable with a pair of `{}` characters. Other than that, everything else is the same. For demonstration purposes, here's an example of string interpolation and string concatenation.

```csharp
private void HandleHurtboxEntered(Area3D area)
{
    GD.Print($"{area.Name} hit!"); // area.Name + " hit!"
}
```

### Resources

* Area3D - <https://docs.godotengine.org/en/stable/classes/class_area3d.html>

## Custom Stat Resources

In this lecture,  we created a custom resource. Resources are a feature in Godot for storing any kind of data that we'd like.  We can create a resource by inheriting from the `Resource` class. During this process, you'll have to make sure the class is partial.

```csharp
using System;
using Godot;

public partial class StatResource : Resource
{
    [Export] public Stat StatType { get; private set; }
    [Export] public float StatValue { get; private set; }
}
```

In this example, we're creating a resource for storing stats. The first property will be the type of stat, and the second property will be a value. For the type of stat, we're using an Enum.&#x20;

Enums are a feature for storing a list of values. They're helpful when you don't want to make typos in your program by relying on strings. We can define an enum with the `enum` keyword followed by an identifier. Inside the enum, we can provide a list of comma-separated values.

```csharp
public enum Stat
{
    Strength,
    Health,
}
```

## Understanding LINQ

In this lecture, we used LINQ to help us find a result from an array. To grab a single stat, we defined a method in the `Character` class called `GetStatResource`.&#x20;

```csharp
public StatResource GetStatResource(Stat stat)
{
    
}
```

This method will accept a specific stat and then return a resource. All resources are stored in a field called `stats` on the `Character` class. This field is an array. To search through an array, we can use a regular `foreach` and `if` but there's a feature in C# called Linq to help us filter and sort through collections. First, we must import the namespace to use it.

```csharp
using System.Linq;
```

Next, we can use the `Where` method to loop through the array. On each iteration, the function passed into the `Where` method will be called where we must return a boolean. If `true`, the current item in the loop is stored in the results. Otherwise, the element gets discarded from the new array.&#x20;

```csharp
public StatResource GetStatResource(Stat stat)
{
    return stats.Where(element => element.StatType == stat)
        .FirstOrDefault();
}
```

Lastly, we're chaining the `FirstOrDefault` method since we're only interested in the first result from the array. If nothing is found, `null` is returned by our method.

## Property Getters and Setters

In this lecture, we learned how to create flexible getter and setter accessors. We can update the `get` and `set` keywords to functions to refine their behavior. We did so with the `StatValue` property since we want to be able to set it from outside the class.

```csharp
using System;
using Godot;

[GlobalClass]
public partial class StatResource : Resource
{
    [Export] public Stat StatType { get; private set; }

    private float _statValue;

    [Export]
    public float StatValue
    {
        get => _statValue;
        set
        {
            _statValue = Mathf.Clamp(value, 0, Mathf.Inf);
        }
    }
}
```

To use this syntax, both the `get` and `set` keywords must be converted into functions. For the `get` keyword, we're returning a field called `_statValue`. A few things worth noting about this:

* Behind the scenes, C# creates a field for you using auto-properties. However, if you plan on using this syntax, you are responsible for creating a field that will actually store the value.
* It's common practice for a private field that a property will use to start with an `_` character and be camelcased.

In the `set` accessor, we're clamping the value with the `Mathf.Clamp` method. This method makes sure that a numeric value stays within a range. It accepts the value to clamp, a minimum and maximum threshold.

In this example, we're using the `value` keyword, which represents the value that the property is being set to from external sources.&#x20;

## Animation Method Tracks

In this lecture, we learned about method tracks, which are a feature to call methods from a specific frame in your animation. There are no notes for this lecture.

## Moving Nodes By Local Position

In this lecture, we moved a node by its local position. We have a hitbox that needs to be moved left or right depending on where the enemy is facing. In addition, it needs to be relative to the player. To move a node by its local positon, we can modify a node's `Position` property.

```csharp
Vector3 newPosition = characterNode.SpriteNode.FlipH ?
    Vector3.Left :
    Vector3.Right;
float distanceMultiplier = 0.75f;
newPosition *= distanceMultiplier;

characterNode.HitboxNode.Position = newPosition;
```

In this example, we're setting the `Position` property to a variable called `newPosition`. To calculate the position, we used the constants `Vector3.Left` and `Vector3.Right`. These constants store the direction of left or right, respectively. They're available on the `Vector3` class by default.&#x20;

## Toggling Collision Shapes

In this lecture, we learned how to disable collision shapes to prevent them from detecting characters. We can do so by toggling the `Disabled` property. We defined a method that will take care of setting this property when called. It accepts a boolean as an argument.

```csharp
public void ToggleHitbox(bool flag)
{
    HitboxShapeNode.Disabled = flag;
}
```

## Working on the Enemy Attack State

In this lecture, we worked on the enemy attack state. There are no notes for this lecture.

## Moving the Enemy Hitbox

In this lecture, we moved the enemy's hitbox to the player's position so that it'll always hit them when the enemy enters the attack state. In the `EnterState` method, we grabbed the player by using the `Area3D` method called `GetOverlappingBodies`, which returns a list of bodies inside the area node.

```csharp
Node3D target = characterNode.AttackAreaNode
    .GetOverlappingBodies()
    .First();

targetPosition = target.GlobalPosition;
```

Afterward, we toggled the hitbox when the `PerformHit` method was called from the animation player node.

```csharp
private void PerformHit()
{
    characterNode.ToggleHitbox(false);
}
```

## Finishing the Enemy Attack State

In this lecture, we finished the attack state by determining whether the enemy should attack again or transition away. To perform this check, we waited for the animation to finish from the `AnimationFinished` signal.

```csharp
private void HandleAnimationFinished(StringName animName)
{
    characterNode.ToggleHitbox(true);

    Node3D target = characterNode.AttackAreaNode
        .GetOverlappingBodies()
        .FirstOrDefault();

    if (target == null)
    {
        Node3D chaseTarget = characterNode.ChaseAreaNode
            .GetOverlappingBodies()
            .FirstOrDefault();

        if (chaseTarget == null)
        {
            characterNode.StateMachineNode.SwitchState<EnemyReturnState>();
            return;
        }

        characterNode.StateMachineNode.SwitchState<EnemyChaseState>();
        return;
    }

    characterNode.AnimPlayerNode.Play(GameConstants.ANIM_ATTACK);

    targetPosition = target.GlobalPosition;

    Vector3 direction = characterNode.GlobalPosition
        .DirectionTo(targetPosition);
    characterNode.SpriteNode.FlipH = direction.X < 0;
}
```

There are a few things worth mentioning about this bit of code.

1. We're checking if the area node has any bodies by using the `GetOverlappingBodies` method.&#x20;
2. If not, we're checking if the chase area has any bodies. If the player is there, we'll transition into the chase state. Otherwise, we'll transition into the return state.
3. If the player is still in the attack area, we'll perform the attack animation again.
4. The target position gets updated so that the hitbox will get moved when the attack lands.
5. Lastly, we're flipping the enemy in the direction of the player when the attack starts.

## Character Death States

In this lecture, we prepared the death states for both characters. To notify each character that their death state should be played, we created a field with the `Action` type. The `Action` type allows us to store functions/methods in a variable.&#x20;

```csharp
public Action OnZero;
```

This action will be used for when a stat reaches zero. We can run any method that gets stored in the field by calling the `Invoke` method.

```csharp
OnZero?.Invoke();
```

We're using the `?` operator to check if there's any methods stored to begin with. Otherwise, C# would throw errors if attempted to call a method that didn't exist. It can be a simple way to avoid errors.

During this process, we had to update our state machine if it attempted to transition into a state it's already in.

```csharp
if (currentState is T) { return; }
```

In this example, we're checking if the `currentState` field is a specific data type. If it is, that means we're already in the state and we don't have to transition into it again.&#x20;
