Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm trying to write a short "game" where a player goes around and fights monsters but I have no idea how to handle the combat.

For example, say I have a "Warrior" and a "Troll". How do the two fight each other? I know I can do something like

Conan = Warrior.new();
CaveTroll = Troll.new();
Conan.attack(CaveTroll);
CaveTroll.attack(Conan);

But what part of the game controls the monster? Do I just stick the above sequence in a loop until one of them dies? Or does the game "engine" need to have a part that deals specifically with combat? Or is this an aspect of the Troll's artificial intelligence that needs to take care of it's actions?

Also, who/what determines the actions that the monster takes? Maybe a Troll can bash, kick, bite, cast spells, drink potions, use a magical item. Does the game engine determine what action the Troll takes or is that something the Troll class manages?

Sorry I can't be more specific but I need some guidance on which direction to go with this.

share|improve this question
    
cool! didn't know that site existed. is there a way i can move my question over there? or should i just cut/paste it there? –  Fendo May 3 '11 at 8:04
    
No worries, a mod should move it pretty soon! Or you could delete the question here and re-create over at Game Dev –  LiamB May 3 '11 at 8:12
    
@Fendo I apologize for asking, but what site do You mean? Game Dev? –  user712092 Jun 21 '11 at 20:51
add comment

4 Answers

I imagine a battle sequence as a minigame within your game. Update ticks (or turn ticks) are directed to a component handling these events. This approach encapsulates the battle sequence logic in a separate class, leaving your main game loop free to transition between game states.

void gameLoop() {
    while(gameRunning) {
        if (state == EXPLORATION) {
            // Perform actions for when player is simply walking around
            // ...
        }
        else if (state == IN_BATTLE) {
            // Perform actions for when player is in battle
            currentBattle.HandleTurn()
        }
        else if (state == IN_DIALOGUE) {
            // Perform actions for when player is talking with npcs
            // ...
        }
    }

}

The battle sequence class would look like this:

class BattleSequence {
    public:
        BattleSequence(Entity player, Entity enemy);
        void HandleTurn();
        bool battleFinished();

    private:
        Entity currentlyAttacking;
        Entity currentlyReceiving;
        bool finished;
}

Your Troll and Warrior both inherit from a common superclass called Entity. Within HandleTurn, the attacking entity is allowed to move. This is equivalent to an AI think routine.

void HandleTurn() {
    // Perform turn actions
    currentlyAttacking.fight(currentlyReceiving);

    // Switch sides
    Entity temp = currentlyAttacking;
    currentlyAttacking = currentlyReceiving;
    currentlyReceiving = temp;

    // Battle end condition
    if (currentlyReceiving.isDead() || currentlyAttacking.hasFled()) {
        finished = true;
    }
}

The fight method decides what the entity is going to do. Note that this does not need to involve the opposing entity, like drinking a potion or running away.

Update: To support multiple monsters and a player party, you introducte a Group class:

class Group {
    public:
        void fight(Group opponents) {
            // Loop through all group members so everyone gets
            // a shot at the opponents
            for (int i = 0; i < memberCount; i++) {
                Entity attacker = members[i];
                attacker.fight(opponents);
            }
        }

        Entity get(int targetID) {
            // TODO: Bounds checking
            return members[targetID];
        }

        bool isDead() {
            bool dead = true;
            for (int i = 0; i < memberCount; i++) {
                dead = dead && members[i].isDead();
            }
            return dead;
        }

        bool hasFled() {
            bool fled = true;
            for (int i = 0; i < memberCount; i++) {
                fled = fled && members[i].hasFled();
            }
            return fled;
        }

    private:
        Entity[] members;
        int memberCount;
}

The Group class will replace all occurences of Entity in the class BattleSequence. Selecting and attacking will be handled by the Entity class itself, so the AI can take the whole group into account when selecting the best course of action.

class Entity {
    public:
        void fight(Group opponents) {
            // Algorithm for selecting an entity from the group
            // ...
            int targetID = 0; // Or just pick the first one

            Entity target = opponents.get(targetID);

            // Fighting algorithm
            target.applyDamage(10);
        }
}
share|improve this answer
    
I'm assuming this will only work for one player vs one monster. Or would it be easy to update this to work for one player vs multiple monsters? –  Harv May 9 '11 at 1:23
    
It is quite easy to add support for groups on both the monster side as the player side (in your situation, the player group will only contain one member: the player character). I have updated the answer for this scenario. –  ghost May 9 '11 at 6:13
add comment

Your player and your troll are nothing but sets of data, what we call the data Model which describes your world. Life, inventory, attack capabilities, their knowledge of the world even -- all consists in the data model.

Keep a single, main Model object which holds all data describing your world. It will hold general world information such as difficulty, physics parameters etc. It will also hold a list/array of specific entities' data as I've described above. This main model can consist of many subobjects in order to describe your world. Nowhere in your model should you have any functions that control game logic or display logic; getters are the only exception, and would be used only to allow you to get data from the model more readily (if public members don't already do the trick).

Next, create functions in one or more "controller" classes; you can write them all as helper functions in your main class, although this may get a bit large after a while. These will be called every update to act upon the entities' data for different purposes (movement, attack etc.). Keep these functions outside of an entity class is more resource-efficient, and once you know what describes your entity you will automatically know what functions need to act on it.

class Main
{

//...members variables...
var model:GameModel = new GameModel();

//...member functions...
function realTimeUpdate() //called x times per second, on a timer.
{
    for each (var entity in model.entities)
    {
        //command processing
        if (entity == player)
            decideActionsFromPlayerInput(entity);
        else //everyone else is your enemy!
            decideActionsThroughDeviousAI(entity);

        act(entity);
    }
}
//OR
function turnBasedUpdate()
{
    if (model.whoseTurn == "player")
    {
        decideActionsFromInput(model.player); //may be some movement or none at all
        act(player);
    }
    else
    {
        var enemy;
        for each (var entity in model.entities)
        {
            if (entity != model.player)
            {
                enemy = entity;
                decideActions(enemy);
                act(enemy);
            }
        }
    }
}

//AND THEN... (common to both turn-based and real-time)
function decideActionsThroughDeviousAI(enemy)
{
    if (distanceBetween(enemy, player) <= enemy.maximumAttackDistance)
        storeAttackCommand(enemy, "kidney punch", model.player);
    else
        storeMoveCommand(player, getVectorFromTo(enemy, model.player));

}

function decideActionsFromPlayerInput(player)
{
    //store commands to your player data based on keyboard input
    if (KeyManager.isKeyDown("A"))
        storeMoveCommand(player, getForwardVector(player));
    if (KeyManager.isKeyDown("space"))
        storeAttackCommand(player, "groin slam", currentlyHighlightedEnemy);
}
function storeAttackCommand(entity, attackType, target)
{
    entity.target = target;

    entity.currentAttack = attackType;
    //OR
    entity.attackQueue.add(attackType);
}
function storeMoveCommand(entity, motionVector)
{
    entity.motionVector = motionVector;
}
function act(entity)
{
    entity.position += entity.motionVector;
    attack(entity.target, entity.currentAttack);
}
}

class GameModel
{
    var entities:Array = []; //or List<Entity> or whatever!
    var player:Entity; //will often also appear in the entity list, above
    var difficultyLevel:int;
    var globalMaxAttackDamage:int;
    var whoseTurn:Boolean; //if turnbased
    //etc.

}

A final note is that it is also useful to keep your display logic separate from your game logic. Display logic would be, "Where do I draw this on the screen and in what colour?" vs. game logic being what I've outlined in the pseudcode above.

(Dev's note: While using classes, this loosely follows a functional programming approach which considers all methods as ideally stateless, allowing for a clean data model and processing approach that minimises bugs caused by retained state. FP is the ultimate MVC, since it achieves MVC's goal of separation of concerns explicitly. See this question.)

share|improve this answer
1  
"Keep a single, main Model object which holds all data describing your world. It will hold general world information such as difficulty, physics parameters etc." Difficulty and physics parameters? Talk about conflation of concerns! -1. –  user744 May 3 '11 at 21:31
2  
@Joe -- Do you want me to outline to him the entire configuration hierarchy? We are keeping it simple here, are we not? I'd appreciate if you'd think before downvoting. –  Nick Wiggill May 3 '11 at 21:35
3  
Well the rest of the post is a bizarre attempt to cover MVC without covering V or anything normally recognizable as C, and I don't think MVC is good advice for game programming in the first place. I'd appreciate if you'd think before answering, but we can't always get what we want. –  user744 May 3 '11 at 21:38
1  
@Joe: I agree that MVC is a rough choice for a game, but I'm pretty sure the role of V here is obvious. –  Zach Conn May 4 '11 at 1:40
4  
@Zach: When claims like "FP is the ultimate MVC" are made, nothing is obvious, except maybe that the poster fails to understand both MVC and functional programming. –  user744 May 4 '11 at 5:26
show 4 more comments

Yes, you need to have a special part in your engine that handles combat.

I don't know how exactly you're doing your combat but I'll assume players roams aroud the game world, meet with monsters and the battle goes real time. If so then, the troll needs to know the surrounding within a certain area, maybe define how far the troll can see something in front of it (troll handles this).

About the AI, I think the engine needs to handle it itself, so that let's say you have more than one kind of enemy that can do the same thing (bite), you can just assign the AI to another monster and there you go!

share|improve this answer
add comment

I would have a dedicated Combat object that manages combat. It'd encapsulate the full combat state including things such as the list of player characters, list of enemies, current turn, battle terrain, and so on. Combat can then have an update method which manages the battle logic. It's not a good idea to just put the combat code in a simple loop, because it'd end really fast. Normally you'd have some timing and different battle stages.

For the actions taken, you can certainly just make it random, but it'd make little sense for a monster with full HP to cast a healing spell. It pays to have some basic logic for determining which action to take. For example, some actions could have more priority than others (e.g. troll kicks 30% of the time), as well as other conditions to make battles more interesting (e.g. when troll HP is less than 10% of full HP, there's a 20% chance of casting healing spell, otherwise the chance is 1%). This could be as complex as you like.

I think the monster class should handle selecting what action to make, the battle object asks the monster for an action and the monster makes a choice and then proceeds to apply it. One idea is to have a strategy object that you plug into monsters and which selects from the list of possible monster actions based on priorities, categories, and conditions assigned to each battle action. Then you can have a OffensiveStrategy class for example which prioritizes attacks over defensive skills, and another CautiousStrategy which is more likely to heal. A boss might be able to dynamically change strategy based on its current condition.

One last thing. You might want to either have both player characters and monsters inherit from the same class, be instances of the same class (actor or combatant for example), or share a common object that encapsulates the common functionality. This reduces code duplication and it'd also allow you to have AI-controlled NPCs on your side which can implement the same strategies you already coded for monsters.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.