🔦

DUNGEON DISPATCH

// devlog for CryptsOfEtherwyn — a 2D pixel RPG

🔦

Designing Turn-Based Combat That Doesn't Feel Like Waiting

Turn-based combat has a reputation problem. People who haven't played great turn-based games think it means "waiting for your turn." People who have played great turn-based games know the reality: when designed well, every single turn is a micro-puzzle, a moment of genuine decision-making, a beat of drama.

I spent most of February asking myself: what makes a turn feel weighty? What makes a player lean forward instead of wait? Here's what I landed on.

The Energy System

The core of Etherwyn's combat is an Energy system rather than a simple "one action per turn." Each character (player and enemy alike) has 3 Energy Points per turn. Different actions cost different amounts:

Move 1 tile = 1 EP. Basic attack = 2 EP. Special ability = 2-3 EP. Use item = 1 EP. Wait/Defend = 0 EP (and grants a defense bonus).

This means each turn is a small resource allocation puzzle. Do you move into flanking position (2 EP) and then attack (2 EP), spending 1 borrowed from next turn? Do you use your 3 EP all on movement to reposition? The math is simple enough to do in your head but the decisions are genuinely interesting.

Giving enemies the same energy system as the player is crucial. When an enemy has 2 EP left and is adjacent to you, you know it can attack you. When it has 1 EP, it can only move. This creates readable, learnable patterns that reward player knowledge without requiring memorization of hidden stats.

Making Each Hit Feel Like Something

Turn-based games live or die on feedback. If an attack connects and nothing much happens visually or audibly, the interaction feels hollow even if the numbers are interesting.

My feedback stack for a basic attack hit:

1. Screen shake — 4-6 frames, magnitude scales with damage

2. Hit pause — 2 frames of freeze-frame on impact (this is the secret sauce)

3. Knockback — half-tile knockback animation, 0.12s duration

4. Damage number — floats up, color-coded (white = normal, yellow = critical, red = heavy)

5. Enemy flash — 1 frame white flash, then back to normal

The hit pause (sometimes called "hitstop") is something I borrowed from fighting games. Even two frames of freeze on impact makes attacks feel infinitely more satisfying. It's imperceptible consciously but your brain registers the impact as more "real."

# Hit pause implementation in Godot
func apply_hit_pause(frames: int) -> void:
    Engine.time_scale = 0.0
    await get_tree().create_timer(
        frames * (1.0 / 60.0),  # real time, not game time
        true  # process_always = true, ignores time_scale
    ).timeout
    Engine.time_scale = 1.0

Enemy Design: Making Fights a Conversation

The biggest mistake I made early on was designing enemies as "damage dealers" and "tanks." That framing creates boring fights. I switched to designing enemies around verbs — what unique action does this enemy want to do?

The Shade wants to flank — it will spend all its energy repositioning to get diagonal advantage. The Bone Knight wants to push you into walls — its attacks have knockback and it loves backing you into corners. The Venom Crawler wants to close distance fast and then make you move — it applies a Burning status that does damage if you stay still.

Each of these enemies creates a different spatial challenge, and when you encounter two of them together, the combination creates emergent situations that I never explicitly designed.

Don't add too many enemy behaviors to one enemy type. I made an enemy called the "Dread Warden" that could: cleave, push, teleport, and apply two status effects. Every playtest session ended with someone saying "what just happened?" and not in a good way. Split complex behaviors across multiple enemy types.

Status Effects: The Spice Layer

I currently have six status effects implemented: Poison (damage over time), Burning (punishes staying still), Frozen (can't move, takes extra physical damage), Confused (random movement), Weakened (attacks cost +1 EP), and Blessed (crits on odd-numbered attacks).

The key design constraint: each status has to be visually obvious at a glance. Burning enemies have a flame particle effect. Frozen enemies have an ice overlay tint. Confused enemies have a small spinning icon. In a game where you need to quickly assess a room's threat level, a status effect you can't immediately parse is a design failure.

That's the combat system as it stands. Next month is inventory and loot tables — turning "a sword dropped" into "a sword that I want and that feels good to find." The roguelite loop hinges on this, so no pressure.

← DEVLOG #10: Pixel Art Pipeline DEVLOG #12: Procgen →