Picture Interaction Question submitted by (05 January 2000) | Return to The Archives

| How do I interact with pictures in my CRPG game. It is like ff3(ff6) and zelda with a tile-based engine. My question is tartgeted at a chest scenario. There are a number of chests. How do I make it so that when one chest is opened, they get a specific object. |

| Once again, I'm going to break this into two parts - the specific workings of chests and items, and how to put them on the map. Part 1 - Chests and Items Suppose for the sake of example that all of our game items are derived from a common base class, CItem, like so:

    class CItem
    {
    public:
      CItem() { }
      virtual ~CItem() { } // virtual destructors are important here
      virtual char *GetName(void) = 0;
    }

    class CNiftyPotion : public CItem
    {
    public:
      char *GetName(void) { return("Nifty Potion"); }
    };

    class CBigSword : public CItem
    {
    public:
      char *GetName(void) { return("Big Ol' Sword"); }
    };

... and so on, for each item in the game.
Each chest is represented by a CChest object, which contains an STL vector (or if you prefer, an array) of CItem pointers - the stuff that's in the chest. Something like this:

    class CChest
    {
    public:
      std::vector<CItem *> m_Contents;
      bool m_bHasBeenOpened;
    };

Deriving everything from CItem in this way allows you to add all sorts of stuff to a chest:

    ...
    CChest myChest;
    CBigSword *bigsword = new CBigSword;
    CNiftyPotion *potion = new CNiftyPotion;
    myChest.m_Contents.push_back(bigsword); // adds the big sword to the chest
    myChest.m_Contents.push_back(potion); // adds the potion to the chest
    ...

(Note that in this example the chest is responsible for deleting the items it contains).
Obviously, it's a bad idea to hard-code the contents of each chest into the game. You're much better off coding up your level editor so that it supports "chest editing." Maybe two more CChest methods - Save() and Load(), which would serialize the contents of the chest.
So that's the gist of the CChest object.
Part 2: Putting Chests on a Map
There are two main ways you can keep track of all the CChests in a given level:
1) You can have the chests remember their position on the map. That is, you can add two members to CChest - int m_TileX, int m_TileY, which represent the (x,y) tile coordinates of where the chest is. Your game then keeps a big list of chests, each with their own contents and (x,y) position.
2) You can have the map keep track of the chests. Each grid space on the map has a pointer, which either points at a CChest object (if there's a chest on that space), or is NULL (if there's nothing there).
There are tradeoffs assocaited with both methods. Method 1 requires less memory, but involves a lot of looping through the chest list, and might bog the game down if you have a lot of chests. Method 2 requires more memory, but doesn't require as much looping. The method that's better for you depends on your game design, specifically, how many chests you have, and how big your maps are. Most people consider method 2 a better solution.
One extra suggestion - if you decide to go with Method 2, derive your CChest object from a generic "CMapObject" class... that way, if your game has other special items besides chests (say, transporters, stairs, etc.) you can derive all of these classes from CMapObject and use just one pointer to point to any of them (you can use C++'s RTTI features, specifically, the dynamic_cast keyword, to determine what the pointer is pointing at). For example, you might use a function that's called whenever the player steps on a tile, like so:

    void PlayerSteppedOnTile(int tilex, int tiley)
    {
      // get the pointer associated with this grid space,
      // assuming your map is a 2D array of CGridSpaces

      // GetSpaceAt() returns gridspace at (x,y)
      CGridSpace *gridspace = &GetMap()->GetSpaceAt(tilex,tiley);

      CMapObject *obj = gridspace->GetPointer();

      // if there's something on this space (i.e, it's pointing at
      // something...)
      if (obj != NULL) {

        // dynamic_cast it to see what it's actually pointing it
        CChest *chest = dynamic_cast<CChest *>(obj);
        CTransporter *trans = dynamic_cast<CTransporter *>(obj);
        CStairs *stairs = dynamic_cast<CStairs *>(obj);

        if (chest != NULL) { // the player stepped on a chest!
          GivePlayerItemsInChest(chest);
        }
        else if (trans != NULL) { // the player stepped on a transporter!
          // do whatever...
        }
        else if (stairs != NULL) { // the player stepped on some stairs!
          // do whatever...
        }
      }
    }

Your tile-rendering engine can use this same process to render the chest/transporter/whatever sprites on top of the tiles. Realize however that with this method each grid space can only contain one thing (i.e., you can't have both a chest and a transporter in the same spot). Most games should be cool with that, though.
Response provided by Mason McCuskey

This article was originally an entry in flipCode's Fountain of Knowledge, an open Question and Answer column that no longer exists.

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.