> 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-8-finishing-touches.md).

# Section 8: Finishing Touches

## Section Intro - Finishing Touches

There are no notes for this lecture.&#x20;

## Preparing the Reward

In this lecture, we took the time to prepare the UI to display the reward and treasure chest to the player. We had the opportunity to explore how to work with spritesheets too. There are no notes for this lecture.

## Creating a Reward Resource

In this lecture, we created a resource for storing a reward that will be given to a player when they open a treasure chest. Using a resource is great for when you need to share the same piece of information to multiple nodes across scenes. We created a class called `RewardResource:`

```csharp
public partial class RewardResource : Resource
{
    [Export] public Texture2D SpriteTexture { get; private set; }

    [Export] public string Description { get; private set; }

    [Export] public Stat TargetSat { get; private set; }

    [Export(PropertyHint.Range, "1,100,1")]
    public float Amount { get; private set; }
}
```

In this class, we're storing four pieces of information which is the texture that will be displayed in the UI, a description of the reward, the stat to update, and the value to update the stat by.

In addition to creating the resource, we also toggled the icon above the treasure chest. To do this, we registered methods to the `BodyEntered` and `BodyExited` signals.

```csharp
public override void _Ready()
{
    areaNode.BodyEntered += (body) => spriteNode.Visible = true;
    areaNode.BodyExited += (body) => spriteNode.Visible = false;
}
```

From these methods, we're just toggling the visibility, which is why we're using lambda functions.

## Applying Rewards

In this lecture, we applied the reward to the player when they opened the chest. Most of what we did are things we've done before. The newest thing we learned was about the `Monitoring` property on an `Area3D` node. This property tells an area node that it should monitor for other area nodes or bodies.&#x20;

By default, this property is enabled, but you will want to disable it so that the player can't redeem the same reward multiple times. We did so in our class.

```csharp
areaNode.Monitoring = false;
```

## Preparing the Bomb

In this lecture, we prepared the bomb by adding two animations for expanding the bomb and the explosion itself. There are no notes for this lecture.

## Exploding the Bomb

In this lecture, we learned how to make the bomb explode by instantiating it in our game. First, we had to switch from the `Expand` animation to the `Explosion` animation. To do this, we subscribed to the `AnimationFinished` signal from the node responsible for playing the animation like so:

```csharp
private void HandleExpandAnimationFinished(StringName animName)
{
    if (animName == GameConstants.ANIM_EXPAND)
    {
        playerNode.Play(GameConstants.ANIM_EXPLOSION);
    }
    else
    {
        QueueFree();
    }
}
```

Before playing the `Explosion` animation, we're checking if the current animation that just finished playing is the `Expand` animation with the help of the `animName` parameter, which contains the current animation name. If so, we're switching to the `Explosion` animation. Otherwise, we're deleting the bomb since after a bomb explodes, it should disappear from the scene.

In the `PlayerDashState` class, we're instantiating the bomb right when the state is entered. First, we need a reference to the scene.

```csharp
[Export] private PackedScene bombScene;
```

The `PackedScene` is the class you'll want to use when trying to store a scene in a variable. Next, we instantiated the bomb with the following code:

```csharp
Node3D bomb = bombScene.Instantiate<Node3D>();
GetTree().CurrentScene.AddChild(bomb);
bomb.GlobalPosition = characterNode.GlobalPosition;
```

To instantiate a scene, we're using the `Instantiate` method on the scene. In addition, we can pass in a generic to specify the root node's type.&#x20;

Afterward, we're adding the scene to the root node of our scene since it's not automatically added after instantiation. We can use the `AddChild` method to perform this task, which accepts the scene to add.

Lastly, we're positioning the bomb in the same position as the player when they begin their dash.&#x20;

## Using Interfaces

In this lecture, we fixed an issue with only being ab le to damage enemies with attacks and abilities by using interfaces. Interfaces are a feature in C# that are similar to abstract classes. They allow us to describe the methods found in a class. Unlike abstract classes, methods in interfaces are not allowed to have implementations, thus forcing child classes to provide the implementation.

To define interfaces, you use the `Interface` keyword followed by the name of the hitbox.&#x20;

```csharp
public interface IHitbox
{
    public float GetDamage();
}
```

In this example, we're defining an interface called  `IHitbox`. It's common practice to start interface names with a letter `I` to help other developers identify it as an interface. Secondly, methods in an interface, don't contain an implementation, so it's perfectly fine to end a method definition with a `;` instead of curly brackets.

To apply an interface, you must add the interface with the inherited classes like so:

```csharp
public partial class AttackHitbox : Area3D, IHitbox
{
}
```

Even though it's not allowed to inherit from multiple classes, it's acceptable to add multiple interfaces. All you have to do is comma separate the classes and interfaces like the example above.

## Thunder Combo Damage

In this lecture, we took the time to add another ability for performing a lightning attack when a player performs a successful combo attack. In the `PlayerAttackState`, we're checking if all combo attacks were performed by comparing the current attack and the maximum combo attack.

```csharp
private void HandleBodyEntered(Node3D body)
{
    if (comboCounter != maxComboCount) { return; }

    Node3D lightning = lightningScene.Instantiate<Node3D>();
    GetTree().CurrentScene.AddChild(lightning);
    lightning.GlobalPosition = body.GlobalPosition;
}
```

Afterward, we just instantiate the lightning scene and then move it over to the enemy's current position.

## Creating a Shader

In this lecture, we learned how to create a shader. Shaders are programs for manipulating the graphics in our game. Most game engines use shader languages. Godot is no exception. It provides a language that is similar to GLSL.&#x20;

For our first shader, we decided to create a shader to change the color of a sprite. In our shader file, we first set the rendor mode to `spatial` since we're trying to manipulate a 3D node.

```glsl
shader_type spatial;
```

Next, we enabled the `unshaded` mode to prevent lighting from affecting our shader and the `cull_disabled` mode to apply the shader to both sides of a sprite.

```glsl
render_mode unshaded, cull_disabled;
```

Afterward, we added `uniform` variables to allow them to be modified outside of the shader. First, we have a variable for keeping track of if the shader should be applied to the image. Secondly, we're storing a color. Lastly, we're storing the last texture stored in the sprite.

```glsl
uniform bool active = false;
uniform vec4 flash_color: source_color = vec4(1.0, 1.0, 1.0, 1.0);
uniform sampler2D tex: source_color;
```

Afterward, we defined a `fragment()` function, which gives us access to each pixel in the sprite. In this function, we're storing the current color of the pixel with the help of the `UV` variable, which stores the coordinate of a specific pixel in the sprite.&#x20;

Once we have the color, we're setting the `ALPA` to the current alpha of the image, then we proceed to check if the shader should be active. If it is, we'll override the color with the `flash_color` variable. Otherwise, we're using the original color of the image.

```glsl
void fragment()
{
	vec4 color = texture(tex, UV);
	
	ALPHA = color.a;
	
	if (active == true)
	{
		color = flash_color;
	}
	
	ALBEDO = vec3(color.r, color.g, color.b);
}
```

## Dynamically Applying a Shader

In this lecture, we're dynamically applying the shader to our sprites. First, we need a reference to the shader. To store the reference, we created a field with the `ShaderMaterial` type.

```csharp
private ShaderMaterial shader;
```

Afterward, to store the shader, we're accessing the `MaterialOverlay` property, which contains our shader. Since materials can store different types of resources, we're casting the property to `ShaderMaterial` so that it's compatible with our variable.

```csharp
shader = (ShaderMaterial)SpriteNode.MaterialOverlay;
```

Next, we subscribed to the `TextureChanged` signal to tell us when the texture changes on the sprite from the animation player node.

```csharp
SpriteNode.TextureChanged += HandleTextureChanged;
```

From the method handler, we updated the shader's `tex` variable with the `SetShaderParameter` method.

```csharp
shader.SetShaderParameter(
    "tex", SpriteNode.Texture
);
```

We used the same method for setting the `active` parameter too.

## Stunning Enemies

In this lecture, we updated our interface to allow our attacks or abilities to stun the enemy. In the interface, we added the `CanStun` method like so:

```csharp
public interface IHitbox
{
    public float GetDamage();
    public bool CanStun();
}
```

We applied this method in both the `AttackHitbox` and `AbilityHitbox` classes like so:

```csharp
public bool CanStun()
{
    return false;
}
```

## Adding Fire Particles

In this lecture, we learned how to use Godot's particle system to create a fire. There are no notes for this lecture.

## Dash Cooldowns

In this lecture, we applied a dash cooldown to prevent players from constantly dashing. To perform this task, we added a delegate with the `Func` type. Unlike the `Action` type, we're allowed to return values from our functions. The return type must be specified as a generic.

```csharp
public Func<bool> CanTransition = () => true;
```

By default, we're always going to assume the state can be transitioned into by calling this method. If a state wants to override this method, they can. In our dash state, we used the `IsStopped` method to prevent the player from transitioning into this state while the timer is running.

```csharp
CanTransition = () => cooldownTimerNode.IsStopped();
```

Lastly, in our state machine, we're calling this method to check if we can transition into the new state.

```csharp
if (!newState.CanTransition()) { return; }
```
