Smart actions

A start way to determine actions and movement

Overview

First of all, let me explain what this article exactly is about. I wrote it to propose an interesting way of dealing with all the action the PC can take based on the movement key input. That's movement, attacking opponents, opening closed doors by simply bumping into them or displaying bump message when running into impassable map features.

I shall use example code here to illustrate what I mean exactly. I will use C++ code, since I'm not really good at making understandable pseudo-code.

Classes

What has to be said before we start the actual explanation is how the map should be constructed for this approach to work. The map is an array of cells, each of which has some properties. It might be something like this:

class Cell {
    public:
        //(variables, flags, etc.)
        Feature * feature;
} cell[80][50];

There's an object called "feature" - a class that represents whatever might be found on a map cell, be it a tree, a wall, a door, a stairway or a simple decorative element. However, each specific feature type will have its own methods, so it's handy to use inheritance here.

class Feature {
    public:
    virtual void action (int x, int y);
};
 
class Door: public Feature {
    public:
        bool open;
        void action (int x, int y);
};
 
class Wall: public Feature {
    public:
         void action (int x, int y);
};
 
class Plant: public Feature {
    public:
        void action (int x, int y);
};

All the other feature types can be defined this way, with their own unique properties and methods. Note that I'm only focusing on the basic stuff, it is up to you to add the properties you need for your map features.

Methods

OK, let's focus on the methods in the Feature class. The "action" method will be the base for determining what action really has to be taken. The keyboard input function will need to determine what are the coordinates of the map cell the player is trying to move onto and pass them as arguments to the "action(int,int)" method. This is needed in case the action determined by the method is movement: the PC's coordinates will have to be updated. So, let's implement our methods.

//empty feature: movement
void Feature::action (int x, int y) {
    PC.x = x;
    PC.y = y;
}

//wall feature: display bump message
void Wall::action (int x, int y) {
screen.displayMessage("You bump into a wall!");
}

//door feature: either open or move onto the cell
void Door::action (int x, int y) {
    if (open) {
        PC.x = x;
        PC.y = y;
    }
    else {
        open = true;
    }
}

//plant feature: display a message notifying of stepping on it
void Plant::action (int x, int y) {
    PC.x = x;
    PC.y = y;
    screen.displayMessage("You step on a plant!");
}

Of course, this is a simplistic scenario, but I believe it's enough to understand the whole mechanism.

Interaction with keyboard input

This is the trivial part. I assume you've already resolved how your game interprets keypresses and determines what will be the new coordinates of the PC. As I have mentioned earlier, these new coordinates are all we need. Let's assume your function has determined that the new coordinates to which the keyboard input has pointed are [40,30]. The keyboard input interpreting function will only have to do something like this:

cell[40][30].feature->action(40,30);

This way, the feature contained in the cell we pointed to will use its "action(int,int)" method and intelligently determine what action should be taken.

Adding complexity

Of course, the possibilities are endless. Interaction with mobile things like NPCs can be resolved in the same way - it's just a matter of checking whether there's an NPC on the pointed cell and if so, requesting an action from it (chatting if it's a friendly NPC, attacking if it's hostile, perhaps asking what do to if it's neutral). All other non-standard actions can also be implemented this way, for instance:

class Feature {
public:
    virtual void action (int x, int y);
    virtual void disarmTrap (void);
};

class Trap : public Feature {
public:
    bool armed;
    void action (int x, int y);
    void disarmTrap (void);
};

//empty feature: disarmTrap impossible
void Feature::disarmTrap (void) {
    screen.displayMessage("What trap?");
}

//trap feature: move
void Trap::action (int x, int y) {
    PC.x = x;
    PC.y = y;
    if (armed) {
        screen.displayMessage("You activate the trap!!!");
        armed = false;
    }
}

//trap feature: disarmTrap
void Trap::disarmTrap (void) {
    if (armed) {
        screen.displayMessage("You disarm the trap.");
        armed = false;
    }
    else {
        screen.displayMessage("This trap is not armed.");
    }
}

In this case, all we need to do is to determine that the PC wants to use the "disarm trap" skill on a cell with given coordinates, say [40,30]. All we need to do is simply call:

cell[40][30].feature->disarmTrap();

The feature will intelligently determine whether it's possible to disarm a trap and if so, will perform the requested action.

Have fun inventing other uses for this system!