5
\$\begingroup\$

A Plane of existence is like a universe in the multiverse. At first, I thought that a Plane will consist of many Worlds, and each World consists of many Locations. But it is not so simple.

Here are some instances of Planes:

  • MaterialPlane: It has different Worlds, as I originally understood Planes to be.
  • Arcadia: An OuterPlane (derived from Plane) that has 2 Layers, which I suppose can be interpreted as Worlds, but has properties not found in Worlds.
  • Abyss: Similar to Arcadia, but has an infinite number of Layers.
  • TemporalPlane: It has TemporalWorlds, where time has changed, but the physical location has not. Not a very clear description, but that's what I found.
  • ShadowPlane: No worlds, no layers, no subplanes, but has regions that continually flow onto other planes. Not a very clear description, but that's what I found.
  • Neth: A plane that has no substructure, and is a plane that is actually living.

This is the design I've come up with to set all this up. Note that class Location has already been defined and used extensively in my program, as does the class World.

Originally, I only had Location that was attached to a World. This is what I'm planning to expand this to.

#include <iostream>
#include <array>
#include <fstream>

enum PlaneEnum {MATERIAL_PLANE, SHADOW_PLANE, ABYSS, ARCADIA, NUM_PLANES};
enum RealmEnum {FORGOTTEN_REALMS, FAERUN, WATERDEEP, PAZUNIA, DRILLERS_HIVES,
    FORGOTTEN_LAND, GRAND_ABYSS, NUM_REALMS};

class Plane {} *MaterialPlane = new Plane, *ShadowPlane = new Plane;

class OuterPlane : public Plane {} *Abyss = new OuterPlane, *Arcadia = new OuterPlane;

const std::array<Plane*, NUM_PLANES> planes = {MaterialPlane, ShadowPlane, Abyss, Arcadia};

class Realm {
    private:
        const PlaneEnum planeEnum;
    protected:
        Realm (PlaneEnum p) : planeEnum(p) {}
    public:
        Plane* getPlane() const {return planes[planeEnum];}
};

class World : public Realm {
    public:
        World (PlaneEnum p) : Realm(p) {}
} *ForgottenRealms = new World(MATERIAL_PLANE), *Faerun = new World(MATERIAL_PLANE),
    *Waterdeep = new World(MATERIAL_PLANE);

class Layer : public Realm {
    public:
        Layer (PlaneEnum p) : Realm(p) {}
} *Pazunia = new Layer(ABYSS), *DrillersHives = new Layer(ABYSS),
    *ForgottenLand = new Layer(ABYSS), *GrandAbyss = new Layer(ABYSS);

class TemporalWorld : public Realm {};  // etc...

const std::array<Realm*, NUM_REALMS> realms = {ForgottenRealms, Faerun, Waterdeep,
    Pazunia, DrillersHives, ForgottenLand, GrandAbyss};

class Location {
    private:
        RealmEnum realmEnum;
    public:
        Location() = default;
        Location (RealmEnum r) : realmEnum(r) {}
        Realm* getRealm() const {return realms[realmEnum];}
        Plane* getPlane() const {return getRealm()->getPlane();}
        // One of the many reasons for using RealmEnum as data member instead of Realm*.
        void save (std::ostream& os) {os << realmEnum << '\n';}
        void load (std::istream& is) {
            int n;  is >> n;  realmEnum = static_cast<RealmEnum>(n);
        }
};

int main() {
    Location* myLocation = new Location(GRAND_ABYSS);
    if (myLocation->getRealm() == GrandAbyss) std::cout << "Correct.\n";  // Correct.
    if (myLocation->getPlane() == Abyss) std::cout << "Correct.\n";  // Correct.
    std::ofstream saveGame("save.txt");
    myLocation->save(saveGame);
    saveGame.close();

    std::cout << "\nGame ended.  Loading saved game...\n";
    std::ifstream loadGame("save.txt");
    Location* locationNewGame = new Location;
    locationNewGame->load(loadGame);
    if (locationNewGame->getRealm() == GrandAbyss) std::cout << "Correct.\n";  // Correct.
    if (locationNewGame->getPlane() == Abyss) std::cout << "Correct.\n";  // Correct.
}

I already have a template class Portal<T> that allows travelling to a different World (by letting T = World).

Now I can let:

  • T = Layer to travel to a different Plane, or
  • T = Plane to travel to a Plane that has no subregions, or
  • T = TemporalWorld to travel to the TemporalPlane, etc...
\$\endgroup\$
  • 1
    \$\begingroup\$ It's not clear to me what one would actually do with this. Can you add some context as to its use? \$\endgroup\$ – Edward Jul 27 '15 at 21:52
  • \$\begingroup\$ I'm just trying to design how the cosmos is set up in this adventure game. I already have a class Portal<T> that allows travelling to a different World (by letting T = World). Now I can let T = Layer to travel to a different Plane, or T = Plane to travel to a Plane that has no subregions, or T = TemporalWorld to travel to the Temoral Plane, etc... \$\endgroup\$ – prestokeys Jul 27 '15 at 21:55
4
\$\begingroup\$

You're suffering from an excess of mimesis here. You've done an impressive job of translating your fantasy setting from English prose into a C++ object model... but sadly that's not worth anything, because the computer will never appreciate it, and the player will never see it.

Let's start over, as if we were designing an adventure game program, instead of transcribing a worldbuilding session. We probably have a player (just one player, so we can just use a singleton named player to hold his data), and the player has a current location, right?

enum Location : int {
    FORGOTTEN_REALMS, FAERUN, WATERDEEP, PAZUNIA, DRILLERS_HIVES,
    FORGOTTEN_LAND, GRAND_ABYSS,  // ...
};

struct Player {
    Location location;
    // ...other stuff, like inventory and so on...
} player;

void newGame() {
    player.location = GRAND_ABYSS;
}

void travel(Location newloc) {
    player.location = newloc;
    describe_surroundings();
}

Okay, so now we have all the mechanics that we need in order to put the player in a location and to move the player from one location to another.

But what about planes? How will we represent that certain locations belong to a given plane? Ah, well, that sounds like a table lookup.

enum Plane : int {
    MATERIAL_PLANE, SHADOW_PLANE, ABYSS, ARCADIA, // ...
};

struct LocationData {
    Plane plane;
    std::string description;
    // ...
};

std::map<Location, LocationData> locData = {
    { FORGOTTEN_REALMS, { MATERIAL_PLANE, "You wander in the Forgotten Realms." } },
    { FAERUN, { MATERIAL_PLANE, "You are welcomed in Faerun." } },
    { WATERDEEP, { MATERIAL_PLANE, "You explore Waterdeep." } },
    { PAZUNIA, { ABYSS, "You climb among the crags of Pazunia." } },
    { DRILLERS_HIVES, { ABYSS, "You skulk among the Drillers' Hives." } },
    { FORGOTTEN_LAND, { ABYSS, "You search the Forgotten Land." } },
    { GRAND_ABYSS, { ABYSS, "You fall swiftly through the Grand Abyss." } },
};

Now we can express things such as

newGame();
assert(player.location == GRAND_ABYSS);
assert(locData[player.location].plane == ABYSS);
player.location = FAERUN;  // travel to a new location
assert(locData[player.location].plane == MATERIAL_PLANE);

and even

void travel(Location newloc) {
    if (locData[player.location].plane != locData[newloc].plane) {
        puts("You have no means of travel between the planes.");
    } else {
        player.location = newloc;
        describe_surroundings();
    }
}

None of this stuff requires a complicated object model; we're not trying to mirror a whole fantasy world in our adventure game program. Instead, we're trying to mirror just enough of it to manage the player's experience of being in that world. Any concept that doesn't directly affect the player's experience can be tossed out of the program.

And in case you're wondering, describe_surroundings() would be implemented something like

void describe_surroundings() {
    puts(locData[player.location].description);
}

Don't overcomplicate things. :)

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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