Skip to content

Khemitron Industries

Amateur Game Dev, Gaming, and TTRPGs

Menu
  • Home
  • Game Dev
  • About
Menu

Taking Action – Strikes and Damage

Posted on 2025-04-07

This week it’s time to add a new basic action to the repertoire – Strike. In the Pathfinder 2E ruleset, Strike is the act of attacking with the user’s equipped weapon. This means it could be a melee or ranged attack, and is a core component of many more complex actions. For my first pass, however, since I have yet to add the ability to equip different weapons, and the concept of health points to which one can do damage doesn’t exist right now, I’m going to keep things simple and create a melee Strike action.

Making some healthy choices

As is the case in many game systems, in the PF2E ruleset, health in combat is represented by a pool of hit points (HP). Whenever you attack someone, or someone attacks you, the damage for the attack is rolled and subtracted from your hit points.

If your attack reduces an enemy to zero HP, that enemy usually dies outright. When a player character is reduced to zero HP, however, things are a bit different – the character is dying, but not quite dead and can still be healed and returned to the fight. We’ll go into that in more detail when I start implementing the death and dying rules though.

The initiative tracker looks pretty good with health bars!

To display HP, I created a health bar component, which displays the current HP and maximum HP for player characters. As per my initial UI sketch (which you can see in Starting the Combat Scene), I’ve added the health bar onto each unit summary in the initiative tracker.

Obfuscated enemy HP – in the PF2E ruleset, exact enemy HP values are unknown. I’ve included this in my game for now with the idea that the player can use PF2E’s Recall Knowledge rules to discover the HP the first time they run into an enemy. However, this might not be as tactically engaging as just being able to see the exact HP values straight away, so I shall see how this feels when playtesting.

I also added the health bar into the Active Unit Summary which sits at the bottom left of the screen. There’s been a minor tweak to the UI here too – the action points are now down the side to match the UI sketch.

Before – action points displayed as three squares at the top
After – action points displayed down the side, and unit name and health bar added

Reworking actions

When I added Stride as an action, I spent a long time thinking about how to make actions abstract enough that I could easily add new ones that just work with the existing code, but failed to think of a good solution. Fortunately I had a spark of inspiration while working on the enemy turn last week and realised that all actions can be simplified according to this flowchart.

Stride Action

Choose Target – a position within movement range

Verify Target – check that the chosen position is within movement range

Perform Action – move to the chosen position

Strike Action

Choose Target – a position within weapon range

Verify Target – check that there is line of sight and another unit at that position

Perform Action – try to hit the unit at that position

My strike summary here says the target is a position within weapon range. This is because the player might want to try and hit an empty square where they suspect an invisible enemy to be. I have not coded this ability in yet, but it’s good to note that it is something that might be needed.

This flowchart and framework let me create an action interface – I know that every action will have these properties and functions.

public interface ICombatAction
{
  // event that can be emitted when the action is complete
  public event ActionCompleteEventHandler ActionComplete;

  // function to get valid targets for a combatant
  public IEnumberable<ITargetPosition> GetValidTargets(ICombatant combatant);
  
  // function to validate the chosen target
  public bool VerifyTarget(ITargetPosition position);

  // function to execute the action using the chosen target and combatant
  public void ExecuteAtPosition(ITargetPosition position, ICombatant combatant);

  // function to continue action execution after animations or during complex actions
  public void ExecuteNext(ICombatant combatant);
}

This fits neatly into the flowchart.

  • The action is chosen and valid targets are requested with GetValidTargets
  • A target can be chosen, either by the player in the UI, or by the AI, at which point it is passed into VerifyTarget
  • If this target is valid, we then use ExecuteAtPosition to start using the action. This may involve multiple steps with animations or that could trigger reactions from other combatants
  • Once an event or animation triggered by the action is complete, we can use ExecuteNext to continue the action
  • Finally, once the action is complete, the ActionComplete event is emitted, which can trigger the UI to re-enable, or another action to be chosen

And now we strike!

With this new interface, adding the Strike action was very straightforward.

The player can choose the strike action and use it on an adjacent enemy

As was adding the AI version, although I put in a 500ms pause since otherwise the code was running too fast for me to see what was going on.

The enemy picks the closest target to attack. If there are multiple adjacent player units, the enemy picks the one with the least HP to attack

Finally, to make it really clear what’s happening, I added a simple animation where the attacker lunges towards the target and the target flashes a lighter colour as they’re hit.

The lunge and brief change to a brighter colour really help the player see what’s happening

I would like to add better animations one day. Once I have a version of the game that is nearly complete, I intend to commission proper art, but I don’t see much point in doing that right now. For the time being, you’ll just have to accept my placeholders.

Ending the combat

This is nearly at a point where I can integrate it with the world map and see how the full gameplay loop feels.

There are a couple of things that I definitely need to add before this is viable though:

  • Nobody should be able to take actions after they drop to 0 HP
  • Dropping every enemy to 0 HP should win the combat
  • Every player being on 0 HP should lose the combat

All these are the cases for a minimum viable gameplay loop. I would like to add in different objectives for combat encounters (e.g. disabling some kind of artifact that keeps reviving enemies, or winning when a boss enemy is dropped to 0 HP because the minions flee at that point). These things, however, are something I can add in later.

On the other hand, having the full gameplay loop means that I can start using the player’s chosen adventuring party as the units in combat. It means I can work on the inventory so the player can equip their characters and have that reflected in combat. It allows me to persist character information across multiple combats and have quest rewards matter.

In short, I’m excited to work on the parts of the game that will allow the player to customise their adventurers and use that customisation in the game, because I think that will be something that many players find fun.

Share on Social Media
facebook tumblr reddit emailwhatsapp

Like this:

Like Loading...

Recent Posts

  • Refactoring the World Map 2025-06-09
  • Using Themes to Enhance my Game 2025-05-19
  • Quality of Life Improvements – Nested Tooltips 2025-05-12
  • Improving Quality of Life 2025-05-05
  • Godot4.4 – Powerful Localisation with gettext 2025-04-27
©2025 Khemitron Industries | Design: Newspaperly WordPress Theme
%d