Create your own
Lesson illustration

Pattern-Based Enemy AI

Hello! Welcome back to your JRPG development course.

In our last lesson, we gave our combat system a major strategic upgrade by implementing status effects and elemental properties. Your characters can now poison enemies, boost their own stats, and exploit elemental weaknesses. This added a fantastic layer of depth for the player, but it also highlights a new challenge: if enemies can't use these systems, they'll quickly become predictable and easy to defeat.

Today, we address that head-on. This lesson is dedicated to the learning outcome: Design and implement a simple enemy AI system for selecting actions based on predefined patterns or conditions. We will explore how to make enemies feel intelligent, reactive, and challenging by giving them a "brain" to decide which actions to take and when. This is a critical step in turning a simple combat prototype into a dynamic and engaging game.

1. What "AI" Means in a Classic JRPG

First, let's set the right expectations. When we talk about AI for a classic JRPG, we are not building a complex learning machine. The goal is to create believable behavior using a set of clear, deterministic rules. The "intelligence" of an enemy like a Goblin or a Dragon in Final Fantasy comes from a well-designed script that makes it act in character—Goblins might attack recklessly, while Dragons might save their powerful breath weapon for the right moment.

At its core, an enemy's turn consists of two decisions:

  1. What to do? (Select a skill from its available actions.)
  2. Who to do it to? (Select a target or targets.)

Our task is to design a system that makes these decisions in an interesting way. Let's look at a few common approaches, starting with the simplest.

2. Approach 1: Fixed Action Patterns

The most basic form of enemy AI uses a predefined list of actions, each with a weight or "rating" that determines its probability. This creates a consistent but slightly unpredictable behavior pattern. RPG Maker has used this simple, effective system for decades.

How to make Troop Events and Enemies in RPG Maker MZ The Basics Tutorial

To see this system in action, watch this short segment from the Driftwood Gaming tutorial on RPG Maker MZ. It explains how to set up enemy attack patterns using ratings.

Watch the section 'Designing Enemy Attack Patterns' from 02:01 to 03:55. Notice how the 'rating' is a relative value that influences the chance of an action being chosen. This is a classic weighted random selection system.

Data and Implementation

We can represent this directly in our enemy's JSON data. Let's imagine a "Wild Boar" enemy that has a basic attack and a more powerful "Charge" attack.

// In an enemy data file, e.g., /data/enemies/wild_boar.json
{
    "id": "wild_boar",
    "name": "Wild Boar",
    // ... stats ...
    "actionPatterns": [
        { "skillId": "attack", "rating": 5 },
        { "skillId": "charge", "rating": 3 }
    ]
}

On the boar's turn, our AI logic would perform a weighted random selection:

  1. Calculate the sum of all ratings (5 + 3 = 8).
  2. Generate a random number between 1 and 8.
  3. If the number is 1-5, use "Attack".
  4. If the number is 6-8, use "Charge".

This results in the Wild Boar using its basic attack 5/8 of the time (62.5%) and its Charge attack 3/8 of the time (37.5%). This is simple to implement and tune, but it's not reactive. The boar will follow this pattern regardless of what's happening in the battle.

3. Approach 2: Conditional Action Patterns

To make enemies feel more intelligent, we need them to react to the state of the battle. This is where conditional logic comes in. An action is only considered if a specific condition is met.

This is also a core feature in tools like RPG Maker, often handled through a parallel "battle event" system.

How to make Troop Events and Enemies in RPG Maker MZ The Basics Tutorial

Let's return to the Driftwood Gaming video. This time, focus on how battle events are used to create conditional actions.

Watch the section 'Creating Troop Events and Conditional Actions' from 09:17 to 11:39. Pay close attention to how a condition like 'Enemy HP is below 50%' can trigger a specific event or action. This is the foundation of reactive AI.

Data and Implementation

We can evolve our actionPatterns data structure to support this. We'll treat the list as a priority queue: the AI checks each action from top to bottom and executes the first one whose conditions are met.

Let's design a "Goblin Shaman" enemy. It can attack, cast a fire spell, and heal itself when wounded.

// /data/enemies/goblin_shaman.json
{
    "id": "goblin_shaman",
    "name": "Goblin Shaman",
    // ... stats ...
    "actionPatterns": [
        { 
            "skillId": "cure", 
            "priority": 9,
            "condition": { "type": "SELF_HP_BELOW", "value": 0.4 } // 40% HP
        },
        { 
            "skillId": "fireball",
            "priority": 5,
            "condition": { "type": "ALWAYS" }
        },
        { 
            "skillId": "attack",
            "priority": 3,
            "condition": { "type": "ALWAYS" }
        }
    ]
}

The AI logic would be:

  1. Check the first action: Is the Shaman's HP below 40%?
    • If yes, cast "Cure" on itself and end its turn.
    • If no, proceed to the next action.
  2. Now it must decide between "Fireball" and "Attack", since both have the ALWAYS condition. We can use our rating system from Approach 1 to decide between actions of the same condition type. Here, we can use the priority as a rating (5 for Fireball, 3 for Attack).
    • It will choose Fireball 5/8 of the time and Attack 3/8 of the time.

This is a huge improvement! The enemy now acts with purpose—it prioritizes survival. You can easily add more conditions like ALLY_HP_BELOW, TARGET_HAS_STATUS:poison, or TURN_COUNT_GREATER_THAN:4 to create very sophisticated behaviors.

4. Approach 3: Utility-Based AI (A Scoring System)

While conditional patterns are powerful, they can become a long, rigid list of if-else statements. A more flexible and scalable architecture is Utility AI. This is a common technique in modern game development and should resonate with your experience in designing logical systems.

Instead of a fixed priority list, the AI evaluates all possible actions against all possible targets and calculates a "score" for each combination. It then executes the action with the highest score.

Implementing artificial intelligence for games

Before we dive into our JRPG example, let's understand the concept of Utility AI more broadly. The article 'Implementing artificial intelligence for games' from Kreonit provides a good overview of this and other common AI techniques.

Read the sections 'Key applications of AI in video games' and 'Implementing AI for NPC behavior and gameplay'. Focus on the descriptions of Utility AI / Utility Systems. This will give you the high-level concept.

How To Make Enemies Smarter: AI Weighted Decision-Making

Now, let's see a practical demonstration. This video from the GameMaker channel, while using a different engine, provides an excellent visualization of how a weighted decision-making (Utility) system works.

Watch the 'Demonstration' (00:47-04:10), 'Weight Calculation Logic' (06:22-09:07), and 'Selecting the Highest Weighted Action' (09:07-10:31). Notice how the 'weight' (our 'score') of an action like picking up a heart changes dynamically based on the character's current health. This is the core idea we will apply to combat actions.

Data and Implementation

With a Utility AI, the data doesn't need complex conditional flags. We just need the list of skills the enemy knows. The "intelligence" is in the scoring function.

The selectAction function now becomes an optimization problem: find the (skill, target) pair that maximizes a score.

// Simplified AI Controller Logic
function selectAction(enemy, playerParty, enemyParty) {
    let bestChoice = { skill: null, target: null, score: -1 };

    for (const skill of enemy.knownSkills) {
        const possibleTargets = getPossibleTargets(skill, playerParty, enemyParty);
        
        for (const target of possibleTargets) {
            const score = calculateScore(enemy, skill, target);
            if (score > bestChoice.score) {
                bestChoice = { skill, target, score };
            }
        }
    }
    
    return bestChoice;
}

The magic is in calculateScore. This is where you encode the enemy's "personality".

function calculateScore(actor, skill, target) {
    let score = 0;

    // Base score for the skill type
    if (skill.type === 'damage') {
        score = 50; // Base desire to do damage
        
        // Bonus for hitting a weakness
        const resistance = target.getElementalResistance(skill.element);
        if (resistance > 1.0) {
            score += 40; // High desire to hit a weakness
        }
        
        // Bonus for finishing off a weak target
        if (target.hp < skill.estimatedDamage) {
            score += 30; // Desire to get a KO
        }
    } 
    else if (skill.type === 'healing') {
        score = (1 - (target.hp / target.maxHp)) * 100; // Score is 0 at full health, 100 at near-zero health.
    }
    else if (skill.type === 'debuff') {
        if (target.hasStatus(skill.statusEffectId)) {
            score = 0; // Don't apply a debuff someone already has
        } else {
            score = 60; // High desire to apply a new debuff
        }
    }

    // Add a little randomness to break ties and prevent predictability
    score += Math.random() * 10;

    return score;
}

This approach is incredibly powerful. The enemy dynamically weighs its options: Is it better to finish off a weak hero, or exploit the elemental weakness of a healthy one? The answer emerges from the scoring logic, creating adaptive and seemingly intelligent behavior.

Test your understanding!

An enemy boss knows three skills:

  1. Mega-Slash: A powerful single-target physical attack.
  2. Poison Gas: Inflicts "Poison" on the entire player party.
  3. Barrier: Puts a "Defense Up" buff on itself.

The battle state is:

  • Player 1: 10% HP
  • Player 2: 90% HP
  • Player 3: 80% HP
  • None of the players are poisoned.
  • The boss does not have "Defense Up".

Using a Utility AI scoring model, which action would likely get the highest score and why? Briefly outline the scoring logic.

Show answer

Mega-Slash targeting Player 1 would likely get the highest score.

Here's the scoring breakdown:

  • Mega-Slash on Player 1: The base score for a damage action is high, and it gets a huge bonus for being able to KO a target (Player 1 is at 10% HP). Score: ~50 (base) + 30 (KO bonus) = 80.
  • Mega-Slash on Players 2 or 3: This would just get the base damage score, as there's no KO or weakness bonus. Score: ~50.
  • Poison Gas: The score for applying a new debuff is high, and it hits multiple targets. However, its immediate impact is lower than securing a KO. Score: ~60-70.
  • Barrier: The score for buffing oneself when the buff isn't active is moderately high, but it's a defensive action. Most aggressive AI would prioritize offense. Score: ~40-50.

Therefore, the AI correctly identifies that removing a player from combat is the most strategically valuable move and prioritizes Mega-Slash on the weakened target.

Conclusion

You now have a clear roadmap for designing and implementing enemy AI, from simple patterns to a sophisticated and scalable utility system. The beauty of these architectures, especially for someone with your background, is how they are all data-driven. The core AI engine can be written once, and you can then define a vast array of unique enemy behaviors simply by crafting different JSON data files.

Key Takeaways:

  • Three Tiers of AI: We covered Fixed Patterns (weighted random), Conditional Patterns (priority-based if/then), and Utility AI (scoring and optimization).
  • Data-Driven Design is Key: The enemy's "brain" is defined in its data file (actionPatterns or knownSkills), separating logic from content. This is crucial for efficient game design.
  • Utility AI is Scalable: While more complex to set up initially, a utility-based scoring system provides the most flexible and adaptive behavior, allowing enemies to react dynamically to a complex battlefield.

Preview of the next lesson:
Our combat system is now feature-complete from a logic standpoint. Players and AI can use skills, apply status effects, and exploit elemental weaknesses. But right now, it's all just text and numbers. In our next lesson, we will integrate a basic animation player for sprite-based skill effects in combat. We'll finally make that "Fireball" spell erupt in a glorious pixelated explosion and have our characters physically swing their swords, providing the essential visual feedback that makes combat feel impactful and satisfying.

Can't find a good explanation? Sign up and we'll make it for you

Sign up