Sign in to follow this  
JWalsh

C++ Workshop - Project 2

Recommended Posts

Project 2 – Overview Welcome all to the second project of the C++ Workshop. Continuing with the theme of the first project, we will move along in our journey towards making an RPG similar in style to Dungeons & Dragons. In the first project you created characters, gave them ability scores, and had them fight 1 on 1 in an arena environment. However, RPG’s are much more than combat. They’re also about exploration and discovery, and there’s not much to discover in an arena. So in this project we will be breaking out of the arena. Project 2 – Introduction In graphical video games, as in life, what you can see is only limited by your range of vision and your field of view. Although you may not realize it, your eyes take in vast amounts of information in a microsecond, allowing your brain to identify objects, determine the presence of danger, and to become aware of the weather, atmosphere, and mood – for as far as the eye can see. In pen & paper RPG’s, as in text RPG’s, your awareness is much more restricted. If I (or the Dungeon Master) were to try and describe to you everything the eye could see in one sitting, we’d be reading paragraph after paragraph for days, and you’d never get an opportunity to actually DO something. To make the transition from visual to text environments easier, the world is often broken up into smaller, more manageable chunks called rooms. “Imagine you’re standing on a small hill in an open, circular field of dark green grass. The field stretches in all directions and extends almost as far as you can see. At the edge of the field is a forest, which surrounds you on all sides. You notice that to the north the forest has a small opening, and to the south you hear the sound of flowing water. The wind is cool, the air is dry, and the sky is blue and bright above you.” The above is an example of a description which might be read in a “room.” You’ll notice that in the example, its not really a room at all. In fact, it’s not even inside, but outside in an open field. The reason its referred to as a “room” has more to do with the subdivision of the world and the existence of exits, than with the fact that it emulates an actual physical room. When I say the room has “exits,” I mean that there are paths to follow, directions to head, ways in which you can get from this “room” to the next “room.” In the description above, there are two logical exists, north, and south. If the user were to exit either north or south, they would leave the current room and enter one of the adjoining rooms. So if the example “room” above was an entire field, how big is a room normally? Well, a room is as small as possible, in order to contain all relevant information. You notice in the room above that there was very little content. An open field, trees, and a bit of weather. There were no buildings, obstacles, people, objects, or even distinguishable landscape, other than the hill you were on. So it was quite feasible to absorb all of that information in a single paragraph. Now, imagine trying to describe the entire city of New York. It would be impossible to do in a single paragraph and be able to effectively communicate to the user the weather, the people, dangers, and all the shops and stores that would be possible to explore. It goes without saying that you’d more likely divide the city up into city blocks, with each block being described in a single “room.” Or better yet, imagine you’re inside of a medieval castle. The castle itself is smaller than a New York city block, and definitely smaller than a field as large as the eye can see, but yet, you’d still likely subdivide the castle into dozens of rooms representing the actual rooms and hallways of the castle, because that’s all the eye can see. As well, the zoomed in level of detail necessary to see the books on the shelves, the paintings on the walls, and the furniture in the room requires a much smaller subdivision. Which leads me to the next topic. As you’re exploring a medieval castle or dungeon, you’ll often see things lying around. Things such as furniture, armor, weapons, treasure chests, and even piles or coffers of money. Sometimes these things are merely part of the description – such as the furniture. Other times, however, you need to be able to pick up and interact with the object – such as the weapons, armor, or coffers. These require physical objects which you can take, examine, and put in your inventory. Once in your inventory, you want to be able to open it, equip it, or drop it. This implies that you have an inventory, perhaps a backpack, which, much like the room you’re standing in, is a container for other objects. And finally, different types of rooms allow you to do different things. For example, a room in your house might perhaps just have a bed which allows you to “sleep.” While a room in a bank might allow you to “check” your balance, “deposit” some money, or even “withdraw” some money. Another example might be a shop, which allows you to “List” the inventory, “Purchase”, an item, or even “Sell” something from your inventory. Project 2 – Conceptualization This program is a continuation of the program created in project 1. It will again consist of various menus and submenus, however the menus and submenus will now be different depending upon which room you’re in. The primary functionality will be the ability to move in an out and explore different rooms. Each room will have its own description, exits, and contents – which includes objects and other characters. While in a room you will be able to interact with objects, pick up equipment and put it in your inventory, and fight against the other characters which are in the rooms, much as you did in the arena. If you defeat a character, you may then loot his body in order to take any equipment or coins he may have had on him. Some characters will be non-hostile, meaning that they do not attack you when you enter the same room with them, however some of them WILL BE hostile, and will engage you as soon as you enter, so be prepared. As with before, you may create a character, view your stats, and gain experience and level as before. You no longer automatically gain coins for defeating a character (but you can loot their corpses), and you no longer purchase armor and weapons via the main menu. Now, your character begins with a basic, level 1 set of gear and you must enter weapon, armor, and potion shops in order to purchase new equipment. If you should die while fighting in combat, your body is left on the ground where you fell, and you return to the starting room where you began with a new body. All coins are taken by the character that defeated you (and can later be regained by defeating them), but your equipment is left on your body. To prevent losing all of your coins when defeated in combat, it would be wise to store some of it in a bank while exploring. You can reclaim the items by returning to your body and entering your corpse. Project 2 – General Analysis Use Cases 1. Entering The Game When the user first executes the program they will be shown a main menu similar to that displayed below. When the user makes a choice the screen will be cleared and the user will be shown a new menu or screen depending upon which choice was made. It is important to note that “Enter World” will only be shown if a character has been created. If not, “Create Character” is the only option available. ============================== Main Menu ============================== Menu Options: a. Create Character b. Enter World Q. Quit ============================== Choice: 2. Creating a Character When the user selects the option to create a character they will be shown a menu similar to that shown below. If the user selects “Name Character” they will receive a prompt allowing them to enter a name for the character. If the user selects roll for stats, then he will enter a loop where he can continue to “roll for stats” until he finds a suitable distribution. Returning to the previous menu clears the screen and then once again shows the main menu. 2a. If the user has already created a character by previously entering this submenu then entering the “Create Character” menu additional times should first prompt the user that they have already created a character, and that continuing will result in the removal of the old character to make room for the new. If the user opts to continue to “Create a Character” then the menu below is shown. If they decide NOT to create a new character, they will simply be returned to the main menu and their previously created character will remain. ============================== Create Character ============================== Menu Options: a. Name Character b. Roll for Stats R. Return to previous menu ============================== Choice: 3. Entering the World After the user selects “Enter World” they should be taken to a starting room. This can be any room you like, however it should always be consistent, and should be safe from opposing characters or monsters. Typical choices are a Church/Cathedral, a Town Hall, or an upstairs bedroom in an Inn. When the character is transferred to the starting room is should be treated as entering any room. See Below. 4. Entering a Room When a player enters a room they should be shown a screen similar to that listed below. It should show the title of the room, the long description, the exits, and any objects or characters in the room. After all of those have been shown, a menu will appear which lists the available choices. The menu should be dynamically, with directions for exits, “Examine objects” if there are any objects in the room, “Attack” if there are any other characters in the room, and any shopping or banking options as will be discussed later. Additionally, the “Character” option should be available in all rooms. ============================== The Grassy Hill ============================== You’re standing on a small hill in an open, circular field of dark green grass. The field stretches in all directions and extends almost as far as you can see. At the edge of the field is a forest, which surrounds you on all sides. You notice that to the north the forest has a small opening, and to the south you hear the sound of flowing water. The wind is cool, the air is dry, and the sky is blue and bright above you. Obvious Exits: North, South A small tombstone A broadsword ============================== Menu Options: n. North s. South x. Examine Objects c. Character q. Quit ============================== Choice: 5. Using Exits Using an exit simply transports the user from one room to the appropriate connected room. The screen is cleared and they are shown a new screen appropriate for entering the new room. 6. The Character Screen Whenever the user selects the “Character” screen are they choosing to view the information about their character. The screen should be cleared and they should be shown the View Stats menu from project 1. Additionally, the screen should contain 3 new options – V. View Inventory, E. Equip Items, and R. Return. Selecting either of the first 2 will show them a submenu, select R will return them to the view of the room. 6a. Viewing inventory simply gives the user a numbered list of all items their character is currently holding in their bag. From the Inventory screen the user may select any of the items by number which then prompts the user if they’d like to drop the item. Selecting yes drops the item, selecting no returns the user to the Inventory Menu. Additionally, the user may select ‘r’ for return to character screen. 6b. When the user opts to Equip Items, they should be shown what’s currently equipped (armor & weapons) and they should be shown a numbered list of all items in their inventory which can be equipped. Selecting one of those numbers swaps the item currently equipped with the item they selected. The last 2 numbers should be reserved for “remove weapon” and “remove armor,” just in case they want to remove it without first equipping something else. 7. Examining Objects When the user views a room they are shown a list of items in the room and are also given a menu option to “Examine Objects.” If the user selects that option they are taken to a sub-menu which again lists the items in the room, this time numbered. Selecting a number prints the description of the corresponding object, and also gives the user the option to view contents if the item is a container. Additionally, while viewing the object’s description the user may opt to “take” objects which are equip-able, such as weapons and armor, or small enough to carry such as potions or coins. If the object is a container and the user selects to view its contents, they are shown a sub-menu identical to that shown to “examine objects” with all items within the container numbered, etc…This system of objects within objects, and a menu with numbers to examine and then take objects is recursive, and works until all objects within objects have been examined. 8. Attacking Mobs If the room the user is currently in has other characters (non-hostile) they are shown a menu option called “Attack.” Selecting that item will list the characters in the room so the user may decide which character/monster they would like to attack. Once that has been done, combat begins similar to the way in which it was done in project 1, with one small change. 8a. After each round of combat (after both characters have attacked/defended), the user will be shown their hit points and a menu. The menu will contain three options – Attack, Use Item, and Run! Selecting Attack will cause combat to progress for another round. Selecting “Use Item” will show the user a list of items in his inventory which are “Consumable,” such as potions. Selecting one of those items will cause the user to drink the potion, and then continue with the current round of combat. Drinking a potion counts as the user’s turn for that round, so they will not get an opportunity to attack, but they WILL be attacked. Selecting “Run” will usher the character out of the room, in a random direction. (hope they don’t run into a room with a hostile mob). 8b. Attacking a mob and then running flags that mob as hostile. The next time you enter the room, the enemy will continue their attack, however you automatically gain the initiative and get the first swing. 9. Hostile Mobs Whenever you enter a room with 1 or more hostile characters, you are shown the description, the obvious exits, and the objects in the room just as before, however combat immediately begins just as though the player had chosen a character to attack, however without clearing the screen, If there are more than one hostile characters, use initiative or a random chance to determine which attacks first. The first time you enter a new room which contains hostiles they automatically gain the initiative. Every time after that, however, the player gains the initiative. 10. Dying If the player is killed in combat they “die.” Their body is left in the room as an object and their spirit is moved back to the starting room. While a “ghost” the player may only move between rooms and examine objects. They cannot view their stats, open objects, or engage other characters, either hostile or non-hostile. Additionally they cannot shop or bank. Upon returning to the room where they were killed they are given an additional menu option to “Enter Body.” Doing this brings them back to life, with 10% of their full health. If they were killed by a character which was originally “non-hostile” then the character does not attack. If the character was flagged as “hostile” then they will immediately attack once again. As the player has obviously been in this room before, they get the initiative automatically. (Take this as an opportunity to flee.) 10a. When an enemy kills you, they take all of your coins. You may regain those coins upon defeating them, and then examining and opening the corpse. 11. Banks Banks are special rooms which follow all of the rules above, with the addition of a few new menu choices. In a bank you gain the ability to “Check Balance”, “Deposit”, and “Withdraw.” Selecting “Check balance” prints the amount of money you have in the bank, and then reprints the menu “choice” prompt, without clearing the screen. Selecting either Deposit or Withdraw takes you to a suitable sub-menu, where you are shown the amount you can deposit or withdraw, and then a prompt asking you for how much you’d like to deposit or withdraw, respectively. From any of these menus you may “return” to the bank by using the “Return” menu option. 12. Shops Shops are special rooms which follow all of the rules above, with the addition of a few new menu choices. In a shop you may “List” the items for sale, or you may “Sell” something of your own. Selecting “List” displays a numbered list of items for sale, with the amount and attributes of the items identical to the system used in Project 1. Selecting “Sell” will show you all the items in your inventory, along with the prices being offered by the shop. By default, sell prices are 1 / 4 the purchase price. Project 2 – Conclusion By the time this project is over you should be able to create a character, move around in the world, and either collect objects or engage other characters in combat. This is no small project and I very much look forward to seeing what the members of this workshop can come up with. I’ve made every effort to make the analysis thorough enough that you should be able to come up with a pretty accurate design. However, its always difficult to describe a task to someone when you already have the design and implementation in mind. If there’s anything I was overly vague on, or left too much to doubt, please feel free to ask in this thread. Additionally, please feel free to post any design or implementation questions you have. You may notice that in some places, such as the costs of items, what items there are, and what potions exist that I have not specified anything. This was intentional. Use your imagination. As well, this project description should be viewed as a guideline for testing your skills. It should be treated as a minimum specification. If there’s other things you’d like to add, ideas you have of making the project more enjoyable or interesting, please feel free to post those ideas here on the forum and implement unique features as you see fit. Cheers and good luck!

Share this post


Link to post
Share on other sites
Quote:
Original post by linkofazeroth
So. We're making a single playing MUD.

AWESOME!!


You do know that MUD stands for Multi-User Dungeon, right? [smile]

The project goes in a very interesting direction. Be aware that you'll have to work a lot on what you already learnt. This is not an easy task, so don't give up!

Good night, and good luck!

Share this post


Link to post
Share on other sites
Yes, I do... but how else would you describe it? I play a MUD called Achaea that's very very similar to this. Now, that might make it easier on me since I know how a MUD works.. basically.. but I'm not sure how I'd set up monster entities, how to 'generate' rooms, etc.

Oi. This is going to be hard.

BUT FUN!

Share this post


Link to post
Share on other sites
Can anyone give some possibilities for map/room traversal? My first inclination would be to create a vector of room classes. Each element is a class "Room" where the element # is the room ID and that contains the map description, exits, the room ID #'s of the exits, drops, examinables, and npc's. The player's position gives a room ID that loads that particular room and when he exits he goes to the room ID of the exit.

ie


Class Room
{
string Description;
vector<Object> Objects;

bool NorthExit,SouthExit,WestExit,EastExit;
int NorthExitID,SouthExitID,WestExitID,EastExit,ID;
}

vector<Room> Map;

Map[1].Description="hi there";
Map[1].NorthExit=true;
Map[1].NorthExitID=25;

//if player goes to the north

LoadMap(Map[25]);




Is there a better way?

Share this post


Link to post
Share on other sites
p.s. Thanks' JWalsh for coming up with these fun projects. This one is definitely going to take a while, but be a fun challenge. When it's over we'll have a great RPG system in place that can easily be upgraded to a 2d adventure.

Share this post


Link to post
Share on other sites
Quote:
Original post by ChurchSkiz
Can anyone give some possibilities for map/room traversal? My first inclination would be to create a vector of room classes. Each element is a class "Room" where the element # is the room ID and that contains the map description, exits, the room ID #'s of the exits, drops, examinables, and npc's. The player's position gives a room ID that loads that particular room and when he exits he goes to the room ID of the exit.

ie
*** Source Snippet Removed ***

Is there a better way?


That's a pefectly valid method. However, there's no need to store the bool about whether the direction exists or not, just initialize the room ID to -1 for that direction. That indicates "no exit," since you cant have a negative index.

Also, you could potentially store pointers within the objects to the adjacent rooms, similar to the way tree structures often contain pointers to their child nodes; Creating a "web" of rooms. ie..

class Room
{
public:
....
protected:
Room* m_pNorth;
Room* m_pSouth;
Room* m_pEast;
Room* m_pWest;
};


Then when the user follows a path, you just update a global "location" pointer with the address of the new room, then call "DisplayRoom()" or somesuch on the pointer with the new location.

Also, suppose you want to have exits other than just north, south, east, and west? What if you want up and down? Would you go back and add 2 more variables for every room in your world (int or ptr), even if they dont use that direction?

Which brings up a good point, if you've got rooms with variables (integers ID's or pointers) which are frequently null or -1, it seems like a potential waste of memory.

Perhaps there's a more dynamic way to do this. Perhaps you could tell the rooms which directions they support on construction? maybe add:

class Exit
{
...
protected:
int m_RoomID; // ex. 5
char m_MenuChoice; // ex. 'n'
string m_Direction; // ex. "North"
}

vector<Exit> m_Exits;

or some-such. [smile]

Then you can GetExits() when you display the room and use that to show the menu. Then when they enter a menu option, check it against the menu choices stored in each Exit...ie.

for( int i = 0; i < m_Exits.length; i++ )
{
if( choice == m_Exits[i].GetChoice() )
....
}

Anyways...just some ideas and food for thought.

Cheers!

Share this post


Link to post
Share on other sites
Quote:
Original post by ChurchSkiz
p.s. Thanks' JWalsh for coming up with these fun projects. This one is definitely going to take a while, but be a fun challenge. When it's over we'll have a great RPG system in place that can easily be upgraded to a 2d adventure.


Indeed. You're quite welcome. I look forward to seeing what everyone comes up with. And you think this project is complex...wait until Project 3. [wow]

Share this post


Link to post
Share on other sites
Quote:
Original post by jwalsh
Then when the user follows a path, you just update a global "location" pointer with the address of the new room, then call "DisplayRoom()" or somesuch on the pointer with the new location.

Or perhaps it shouldn't be a global...after all, maybe other entities will be able to move through rooms...perhaps such data could be stored with the entity...including the player?[wink]
Quote:

Perhaps there's a more dynamic way to do this. Perhaps you could tell the rooms which directions they support on construction? maybe add:

class Exit
{
...
protected:
int m_RoomID; // ex. 5
char m_MenuChoice; // ex. 'n'
string m_Direction; // ex. "North"
}

vector<Exit> m_Exits;

or some-such. [smile]

Which then also gives you the added flexibility of "unlocking" doors and opening secret chambers, amongst other interesting possibilities [grin].

Share this post


Link to post
Share on other sites
Quote:

Or perhaps it shouldn't be a global...after all, maybe other entities will be able to move through rooms...perhaps such data could be stored with the entity...including the player?


Me likey. This generalizes well to wandering mobs. It also allows you to put mobs inside of objects, such as a genie in a bottle or some other creature hiding in a treasure chest. A container is a container afterall, and a room IS-A kind of container. =)

But should the container tell you which objects/characters are within it, or should the character tell you which room it's in...or both? [grin]

Share this post


Link to post
Share on other sites
My head is swiming with ideas about how to impliment this. I can see using alot of class inheritance that would work well with this scenario.

But, like one of the posters above... Im wondering about how to keep track of the player in the world. My first thought is to create a cell object that is held in a two dimensional array and the player moving aobut by simple cell arithmatic ie... north (x,y+1) south (x,y-1) east (x+1,y) etc. There would be an array for the main world, then as the character say moved into a building, then that building would have an array of cells with which to move around in... each level to that building would be its own array of cells. Cells would hold objects and object containers as well as descripters that convey what the cell looks like to the player. The player would keep track of his direction and relay that to the cell so the cell would know how to describe itself. This adds the ability to redescribe the room based on which direction the player is facing and only have certain object or containers visable if the player was facing the right direction in the cell.

I have several problems with this type of implimentation...

1. it seem expensive in memory since every cell and array would have to be built at runtime and kept alive in memory.

2. keeping reference to cells and objects in the cells seems to be complex since you run the risk of a pointer going out of scope easily and trying to determine what and how many pointers you will need at the upper scope so they are available to be assigned in later scopes.

3. a database seems to be the best route for storing and retrieving this much information.. so that its not all in memory.. but rather in a .dat file of some sort. That would be the way I would want to impliment it but I dont have the knowledge for that yet.

Good idea, bad idea or indifferent?

Share this post


Link to post
Share on other sites
Quote:
Original post by jwalsh
Quote:

Or perhaps it shouldn't be a global...after all, maybe other entities will be able to move through rooms...perhaps such data could be stored with the entity...including the player?


Me likey. This generalizes well to wandering mobs. It also allows you to put mobs inside of objects, such as a genie in a bottle or some other creature hiding in a treasure chest. A container is a container afterall, and a room IS-A kind of container. =)

Quote:

But should the container tell you which objects/characters are within it, or should the character tell you which room it's in...or both? [grin]

Perhaps both [grin] After all, it would be hardly fair for a room to not be able to notify those who exist within it that it needs to describe its self to the player! (Or to the monster, or the wandering feather...) Furthermore, one would hope that one could tell what room one was in! Less one find that one needs move to another room...and lo and behold! You're trapped in a bag of holding! Muahahahahahaaha!

Share this post


Link to post
Share on other sites
Quote:
Original post by westond
My head is swiming with ideas about how to impliment this. I can see using alot of class inheritance that would work well with this scenario.

But, like one of the posters above... Im wondering about how to keep track of the player in the world. My first thought is to create a cell object that is held in a two dimensional array and the player moving aobut by simple cell arithmatic ie... north (x,y+1) south (x,y-1) east (x+1,y) etc. There would be an array for the main world, then as the character say moved into a building, then that building would have an array of cells with which to move around in... each level to that building would be its own array of cells. Cells would hold objects and object containers as well as descripters that convey what the cell looks like to the player. The player would keep track of his direction and relay that to the cell so the cell would know how to describe itself. This adds the ability to redescribe the room based on which direction the player is facing and only have certain object or containers visable if the player was facing the right direction in the cell.

I have several problems with this type of implimentation...

1. it seem expensive in memory since every cell and array would have to be built at runtime and kept alive in memory.

The amount of memory used for this would be trivial.
Quote:

2. keeping reference to cells and objects in the cells seems to be complex since you run the risk of a pointer going out of scope easily and trying to determine what and how many pointers you will need at the upper scope so they are available to be assigned in later scopes.

Perhaps, although there are techniques that can be used to simplify this...
Quote:

3. a database seems to be the best route for storing and retrieving this much information.. so that its not all in memory.. but rather in a .dat file of some sort. That would be the way I would want to impliment it but I dont have the knowledge for that yet.

Everything is a database [grin]. The real question is, to what degree is it a database? You will need a database of some kind simply to store the various bits and pieces of information you're going to run through, including but not limited to: rooms, room connections, monsters, monster stats, players, player stats, treasure, treasure stats, quests, etc...
Quote:
Good idea, bad idea or indifferent?

Your idea of direction dependant data is a bad one IMO. Think about it this way: When you enter a room do you always stand facing one direction never looking side to side? No. You generally give it a good look around, this is where the description of the room comes in. The description is generally what you would see were you to enter the "room" and look around a bit. Now, having it describe the room differently depending on which direction you enter from is entirely possible (and presents some very fun possibilities...a maze of one room anyone?)

Share this post


Link to post
Share on other sites
Quote:
Original post by Deyja
Do we get bonus points for parsing natural english instead of using the menu system?


If you are to go down that route then I would suggest you abstract the idea of input to allow for a menu system natural english parsing. The output of a system would always be a command but the input could be natural english or menu based input. This allows you to first create a system based on menu input, so you don't get side tracked with details, then after everything is in place you can go back and add in english parsing with out having to refactor.

It's always nice to have a fall back plan. Get it working quick and easy and then go back and add in the bells and whistles.

Share this post


Link to post
Share on other sites
Quote:
Original post by Deyja
Do we get bonus points for parsing natural english instead of using the menu system?


No. This will be done in project 3 or 4 anyways. =)

Share this post


Link to post
Share on other sites
Quote:
If you are to go down that route...
It was a rhetorical question. I've done this before. :)

Share this post


Link to post
Share on other sites
Quote:
Original post by linkofazeroth
There's going to be a Project 4? O_O *dies*


LOL by project 4 JWalsh will have a server hosted and we'll be tinkering with our MUD's.

Share this post


Link to post
Share on other sites
Is it bad style to include run-time type information into the base class? I'm thinking of adding a data field to my Item class to indicate which derived class does the specific instance belong to, to make handling of inventories conceptually simpler, but it feels wrong somehow.

Share this post


Link to post
Share on other sites
Quote:
Original post by Darkstrike
Is it bad style to include run-time type information into the base class? I'm thinking of adding a data field to my Item class to indicate which derived class does the specific instance belong to, to make handling of inventories conceptually simpler, but it feels wrong somehow.


No, its not bad style. But I fail to see how you're planning to use the RTTI. If you're looking for different behavior based on the specific object, then you can just use polymorphism. Provide an interface for working with all objects in the base class, then provide virtual, overriden functions in each of the derived class. Calling an interface function on a base class pointer to a derived class will perform the function of the matching instanciated object, not the base class.

Share this post


Link to post
Share on other sites
Isn't adding all the interfaces on every descendant of Item wasting memory? I was about to write that it's also producing more code to write, but actually it doesn't unless I have a lot of derived types; however, combining roles would take less code in my version ... which is not in the project, so I'll use superfluous polymorphism. Thanks for opening my eyes to the obvious.

Share this post


Link to post
Share on other sites
Quote:
Original post by Darkstrike
Isn't adding all the interfaces on every descendant of Item wasting memory? I was about to write that it's also producing more code to write, but actually it doesn't unless I have a lot of derived types; however, combining roles would take less code in my version ... which is not in the project, so I'll use superfluous polymorphism. Thanks for opening my eyes to the obvious.


I wasn't encouraging superfluous use of polymorphism, just adequate ammounts. Only create an interface for a function if the function is useful for all derived classes. In this case, you're not creating excess code, you're creating the exact amount you neeed.

You should always put code which is useable by all derived classes in the base class, put interfaces in the base class only when the function makes sense for all derived classes. Otherwise, you can use multiple inheritance to solve the problem. The example in the book is the classic animal, bird, horse scenario. A bird is an animal, and so is horse, so they share common bases such as "eat", "breath", and walk. However, birds can also fly, and horses can also gallop. So what do you do when you want to create a Unicorn? Unicorns are clearly horses and can gallop, but they can also fly which, based on our definition of a bird, qualifies them as birds as well.

One option is to simply copy/paste the fly function from the bird into the unicorn, but this creates excess code which now must be kept up to date. The other option is to move "gallop" and "fly" up into the base class. This would be an example of superfluous polymorphism, because although now you can derive Unicorn from "Animal" and make it both fly and gallop, most animals cannot gallop or fly, so its excess entries in the v-table. The solution, is to create a new type which derives from BOTH bird and horse, and is of type Unicorn. This has the benefit of keeping implementation small, allows you to treat a unicorn as both a horse and a bird, and doesnt add excess entries into the vtable.

One could argue that an even better solution is to use abstract data types for bird and horse, to allow for multiple "interface" inheritance. So you'd just derive Unicorn from Animal, and then also inherit two interfaces "FlyingAnimal" and "GallopingAnimal", which are themselves abstract data types, not derived from animal. Also, you dont have to use pure virtual functions on FlyingAnimal and GallopingAnimal, so they could come with default implementation of Fly() and Gallop().

Anyways, I digress. Dont use superfluous polymorphism when you can aovid it, doing so usually suggest a design flaw.

Cheers!

Share this post


Link to post
Share on other sites
In that case, I don't understand how can I use polymorphism. My impression was that you advised to expose all interfaces in the base class, and those will handle erroneous call attempts. Example: I have an inventory, that contains weapons, armors and potions; this is a std::vector of Item*s, and when the player tells me to wield the chainmail, I need to know whether the Item* is actually a Weapon* before even attempting to do what needs to be done. If all interfaces are in the base class, then I will call the overloaded wield(), which will return an error value that I can recognize. If I have RTTI data in the base class, I can check that before forcibly casting my Item* to Weapon* (or not, if it's inappropriate). But without either of those tools, I really can't think about a way to do this well (splitting the inventory depending on function looks pretty messy to me).

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this