Sign in to follow this  

Combat recursion member function...function HELP

This topic is 3858 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

K, so I have a menu system up that: - directs the user to a function that allows them to create their character, then returns to the main menu. - directs the user to a function that allows them to generate some enemies, then returns to the main menu. - directs the user to a function that allows them to fight the enemies they just generated, one-on-one I have the first two functions up and working, but I don't know what to do for the fight function. I have an idea that I want to implement, here it is: When 2 Characters battle, the player against an enemy, I want some function, either a member function in class Character or a function in my main source, to take a look at the stats of each of the Characters, and then use those stats to determine who strikes first, then have each of the Characters strike 1 time at the other, reducing each of the Characters health by some amount. Then, if both Characters are still alive, I want to call the function again, so they each strike each other 1 more time, reduce the health, and continue this loop until one of the Characters' health <= 0. I was thinking of adding a member function to class Character that would take a parameter of type Character, so calling this function from the player Character would look like: player.versus( enemy1 ). Then this would calculate or do something and return the winner, perhaps. But then I don't know if I should return a pointer to the Character or return the Character itself or what, and then what to do with the information that is returned. I'm lost. How do I do this? Here is my code:
// This is the header file for the Classes
#include <string>
#include <vector>
#include <cstdlib>
#include <iostream>
#include <ctime>
using namespace std;

const string basenames[10] = { "Troll", "Ogre", "Ooze", "Gargoyle", "Dragon", "Orc", "Elemental", "Blood-Elf", "Goblin", "Gremlin" };

void createCharacter();
void generateEnemies();
void fight();

class Character
{
private:
	string name;
	int health;
	int power;
	int defense;
	
public:
	Character();
	Character( string chname, int hp, int ap, int def );
	void setName( string chname );
	void setHealth( int hp );
	void setPower( int ap );
	void setDefense( int def );
	string getName();
	int getHealth() const;
	int getPower() const;
	int getDefense() const;
	//Character* versus( Character thisCharacter );

};

Character::Character() : name( basenames[ rand() % 10 ] ), health( rand() % 11 + 10 ), power( rand() % 11 + 10 ), defense( rand() % 11 + 10 )
{}

Character::Character( string chname, int hp, int ap, int def ) : health( hp ), power( ap ), defense( def )
{}

void Character::setName( string chname )
{
	name = chname;
}

void Character::setHealth( int hp )
{
	health = hp;
}

void Character::setPower( int ap )
{
	power = ap;
}

void Character::setDefense( int def )
{
	defense = def;
}

string Character::getName()
{
	return ( name );
}

int Character::getHealth() const
{
	return ( health );
}

int Character::getPower() const
{
	return ( power );
}

int Character::getDefense() const
{
	return ( defense );
}

/*
Character* Character::versus( Character thisCharacter )
{
	if( this -> getHealth() > thisCharacter.getHealth() )
		return this;
	else if( this -> getHealth() < thisCharacter.getHealth() )
		return thisCharacter;
	else
		return this;
}
*/
class ChDatabase
{
private:
	vector<Character> database;

public:
	ChDatabase();
	void addCharacter( Character thisCharacter );
	void eraseFirstCharacter();
	void displayAllCharacters();

};

ChDatabase::ChDatabase()
{}

void ChDatabase::addCharacter( Character thisCharacter )
{
	database.push_back( thisCharacter );
}

void ChDatabase::eraseFirstCharacter()
{
	database.erase( database.begin() );
}

void ChDatabase::displayAllCharacters()
{
	if( database.empty() )
		cout << "Database empty." << endl;
	else
	{
		for( vector<Character>::iterator iter = database.begin(); iter != database.end(); ++iter )
		{
			cout << "   Name: " << iter -> getName() << endl;
			cout << " Health: " << iter -> getHealth() << endl;
			cout << " Attack: " << iter -> getPower() << endl;
			cout << "Defense: " << iter -> getDefense() << endl << endl;
		}
	}

}

#include "classheader.h"

ChDatabase enemyDataBase;
ChDatabase playerDataBase;

bool exitmainloop = false;
bool playercreated = false;
bool enemiescreated = false;

int main()
{
	system("CLS");

	srand(time(NULL));

	do
	{
		int choice;
		
		system("CLS");
		cout << "\tCreate-a-Character!\n\n";
		cout << "What would you like to do?\n";
		cout << "1 - Create your Character!\n"
			<< "2 - Generate some enemies!\n"
			<< "3 - FIGHT!\n"
			<< "4 - Exit\n"
			<< "Selection: ";
		cin >> choice;

		switch( choice )
		{
		case 1:
			char YorN;
			if( playercreated )
			{
				cout << "\n\tYou already have a character.  Continuing will delete your character.\n"
					 << "\tAre you sure you want to continue? ";
				cin >> YorN;
				if( tolower( YorN ) == 'y' )
					{
						playerDataBase.eraseFirstCharacter();
						createCharacter();
					}
				else
					continue;
			}
			else
				createCharacter();
			break;
		case 2:
			if( !playercreated )
			{
				cout << "\n\tYou must create your character first!\n";
				cin.get();
				cin.get();
				continue;
			}
			generateEnemies();
			break;
		case 3:
			if( playercreated && enemiescreated )
				fight();
			else
			{
				cout << "\n\tYou need to create your character or generate your enemies!\n";
				cin.get();
				cin.get();
				continue;
			}
			break;			
		case 4:
			exitmainloop = true;
			break;
		default:
			cout << "Invalid choice.";
			break;
		}
	}while( !exitmainloop );

return 0;
}

void createCharacter()
{	

	string chname;
	char keep;

	Character* pplayer;

	do
	{

	pplayer = new Character;

	system("CLS");

	cout << "\tWelcome to Character Creation!" << endl;
	cout << "Here are your stats: " << endl;
	cout << " Health: " << pplayer -> getHealth() << endl;
	cout << " Attack: " << pplayer -> getPower() << endl;
	cout << "Defense: " << pplayer -> getDefense() << endl << endl;
	cout << "Would you like to keep these (y/n)? ";
	cin >> keep;

		if( tolower( keep ) == 'n'  )
		{
			delete pplayer;		
		}
	}
	while( tolower( keep ) == 'n' );

	cout << "\nCharacter name: ";
	cin >> chname;
	pplayer -> setName( chname );

	playerDataBase.addCharacter( *pplayer );
	delete pplayer;

	system("CLS");

	cout << "\tCongratulations! Here is your character: " << endl << endl;
	playerDataBase.displayAllCharacters();

	playercreated = true;

	system("PAUSE");

	main();
}

void generateEnemies()
{
	system("CLS");

	int num;

	cout << "\tWelcome to the Enemy Generator!\n\n";
	cout << "How many enemies would you like to generate? ";
	cin >> num;
	cout << endl;

	for( int i = 0; i < num; ++i )
	{
		enemyDataBase.addCharacter( Character() );
	}

	enemyDataBase.displayAllCharacters();

	enemiescreated = true;

	system("PAUSE");
	main();
}

void fight()
{
	cout << "Prepare to fight!";
	system("PAUSE");
}

Share this post


Link to post
Share on other sites
Why do this recursively? Why not just a while loop:


while (player.health > 0 && enemy.health > 0)
{
//do combat...
}


Or:


while (player.InCombat())
{
player.GetAction();
ResolveCombat();
}


Or something along those lines? Recursion seems a bit unnecessary for something like this...

Share this post


Link to post
Share on other sites
Quote:
Original post by Driv3MeFar
Why do this recursively? Why not just a while loop:


while (player.health > 0 && enemy.health > 0)
{
//do combat...
}


Or:


while (player.InCombat())
{
player.GetAction();
ResolveCombat();
}


Or something along those lines? Recursion seems a bit unnecessary for something like this...


Ha becuase I don't know how to write an algorithm for "// do combat," and this idea popped into my head and I thought it would be interesting to see it done this way.

Share this post


Link to post
Share on other sites
Ha alright well I guess any ideas on how to simulate combat would be appreciated. I mean gimme the basics, assume nothing. I have no clue how to do any sort of combat other than making a single comparison between two values and declaring a winner! Hah that doesn't quite work

Share this post


Link to post
Share on other sites
Combat is, at the core, just comparisons between numbers [lol]. Since you're characters don't have a "speed" attribute, you could just always make the player go first, or just generate a random number (just a 0 or 1 would suffice) and make the player or enemy go first depending on that.

From there, just compare the player's power to the enemy's defense, and apply damage based on that. I'll leave exactly how to determine how much damage up to you, but you could just do a subtraction and count that as the damage done, or add some randomness to it. Rinse and repeat as needed.

Share this post


Link to post
Share on other sites
Quote:
Original post by Driv3MeFar
Combat is, at the core, just comparisons between numbers [lol]. Since you're characters don't have a "speed" attribute, you could just always make the player go first, or just generate a random number (just a 0 or 1 would suffice) and make the player or enemy go first depending on that.

From there, just compare the player's power to the enemy's defense, and apply damage based on that. I'll leave exactly how to determine how much damage up to you, but you could just do a subtraction and count that as the damage done, or add some randomness to it. Rinse and repeat as needed.


So should I make a function in my source code or a member function of class Character that would run through these comparisons and declare a winner? Also, I' m having trouble understand how to call a function have the function return an object of class Character ( I think I understand "return this;" ), but I don't understand what I can then do with an object that's been returned from a function.
Just to make sure, could somebody gimme a quick and simple example of using the this pointer. Thanks!

Share this post


Link to post
Share on other sites
Lets say you have something like this:

const Character *Character::GetCharacter()
{
return this;
}

int main()
{
Character *character = new Character();
const Character* pointer = character->GetCharacter();

pointer->whatever...
}

Basically, if you have a member function return it's this pointer, you can use that pointer to access any const member function of that instance of that object. The this pointer is just like any other const pointer to an object, the only difference is that every instance of a class has one automatically, and it points to itself.

As for how to structure where combat gets done, that's up to you. I would probably make an "attack" member function in the Character class, or something like that.

Share this post


Link to post
Share on other sites
Ok so I tried my hand at a combat function for Character. Here it is:


Character* Character::versus( Character thisCharacter )
{
bool InCombat = true;

while( InCombat )
{
if( this->getHealth() > 0 && thisCharacter.getHealth() > 0 )
{
int sp = rand() % 10 + thisCharacter.getSpeed();
int sp2 = rand() % 10 + this->getSpeed();

if( sp2 >= sp )
{
int hp = thisCharacter.getHealth();
int hp2 = this->getHealth();

hp -= ( (this->getPower()) - (thisCharacter.getDefense()) );
thisCharacter.setHealth( hp );

if( thisCharacter.getHealth() > 0 )
{
hp2 -= ( (thisCharacter.getPower()) - (this->getDefense()) );
this->setHealth( hp2 );
}
else
InCombat = false;
return this;

continue;
}
else
{
int hp = thisCharacter.getHealth();
int hp2 = this->getHealth();

hp2 -= ( (thisCharacter.getPower()) - (this->getDefense()) );
this->setHealth( hp2 );

if( this->getHealth() > 0 )
{
hp -= ( (this->getPower()) - (thisCharacter.getDefense()) );
thisCharacter.setHealth( hp );
}
else
InCombat = false;
return *thisCharacter;

continue;
}
}
}
}



This doesn't compile

And here's where I call it:

cout << *( playerDataBase[0].versus( enemyDataBase[0] ) ).getName();


And this doesn't compile either.

Here are my errors, but I don't know how to fix any of this...

Compiling...
main.cpp
c:\documents and settings\nate\my documents\visual studio 2005\projects\aggregate\aggregate\classheader.h(146) : error C2100: illegal indirection
c:\documents and settings\nate\my documents\visual studio 2005\projects\aggregate\aggregate\classheader.h(146) : error C2440: 'return' : cannot convert from 'Character' to 'Character *'
No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
.\main.cpp(14) : warning C4244: 'argument' : conversion from 'time_t' to 'unsigned int', possible loss of data
.\main.cpp(160) : error C2676: binary '[' : 'ChDatabase' does not define this operator or a conversion to a type acceptable to the predefined operator
.\main.cpp(160) : error C2228: left of '.versus' must have class/struct/union
.\main.cpp(160) : error C2676: binary '[' : 'ChDatabase' does not define this operator or a conversion to a type acceptable to the predefined operator
.\main.cpp(160) : error C2228: left of '.getName' must have class/struct/union

Share this post


Link to post
Share on other sites
Combat is where you have to start using your imagination.
A text console game is not going to have combat based on quick mouse input and stuff like that, the way Quake does. What you need is a game world where the player can explore and find items/weapons/armor etc.

Things that could make a winner/looser in a text console combat:

Someone said weapon speed earlier. I'm not going to suggest that becouse it doesn't go well with the event driven RPG I'm working on.

1. Weapon damage. If you have a game world where the player can move around and explore different locations and find items/weapons, the damage of the currently equipped weapon will make a difference in combat. The more you explore, the better weapons you will have.

2. Armor. You already have this attribute. The better armor the player has equipped, the less damage he will take from a enemy hit.

3. Health potions. Spending a "turn" on drinking a health potion (If you have any left) should be worth it during a fight. The enemies in the game should also have this possibility. Other things; invisibility potions, weapon oil.

4. Escape. In a RPG like the one I'm working on there should be possible to escape from a fight. Enemies could have this ability as well.

5. Critical hits. This adds some good/bad luck to the fight, and some randomness. Other things; a chance to dodge, evade and parry. Based on skill.

6. Combinations. Items that can be used on your equipment to improve it. If your game has some kind of timer, you can have this effect fade away over time. Like a sword getting dull over time. Or you could pay a weapon smith to sharpen your sword just before you go into the dragons lair...

The list could go on and on.
What you need to make all this happen is a game with some kind of time or event driver. IMO that is what you are looking at right now.
The reason I am making all this fuzz is that I wrote a simple event driven game world some time ago that I thought I would show you.

#include <string>
#include <map>
#include <iostream>
using namespace std;

struct Location
{
map<string, Location*> exits;
string description;
};

int main()
{
string userCommand;
map<string, Location*> world;

// create locations
world["start"] = new Location;
world["start"]->description = "Start location...\nExits: town";

world["farm"] = new Location;
world["farm"]->description = "Farm...\nExits: town";

world["town"] = new Location;
world["town"]->description = "Town...\nExits: farm";

// setup links between locations
world["start"]->exits["town"] = world["town"];
world["farm"]->exits["town"] = world["town"];
world["town"]->exits["farm"] = world["farm"];

// setup current location
Location* location = world["start"];

while(1)
{
cout << location->description << endl;
getline(cin, userCommand);
if(userCommand == "exit")
break;

map<string, Location*>::iterator lookup = location->exits.find(userCommand);

if(lookup != location->exits.end())
location = lookup->second;
else
cout << "Invalid exit." << endl;
}

// release memory
for(map<string, Location*>::iterator doe = world.begin(); doe != world.end(); ++doe)
delete doe->second;

return 0;
}


This little RPG wannabe (I have a version with more flesh on it if your iterrested) has a game loop generating events based on user input, and a game world for the player to explore.

[Edited by - pulpfist on May 22, 2007 9:38:58 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by pulpfist
Combat is where you have to start using your imagination.
A text console game is not going to have combat based on quick mouse input and stuff like that, the way Quake does. What you need is a game world where the player can explore and find items/weapons/armor etc.

Things that could make a winner/looser in a text console combat:

Someone said weapon speed earlier. I'm not going to suggest that becouse it doesn't go well with the event driven RPG I'm working on.

1. Weapon damage. If you have a game world where the player can move around and explore different locations and find items/weapons, the damage of the currently equipped weapon will make a difference in combat. The more you explore, the better weapons you will have.

2. Armor. You already have this attribute. The better armor the player has equipped, the less damage he will take from a enemy hit.

3. Health potions. Spending a "turn" on drinking a health potion (If you have any left) should be worth it during a fight. The enemies in the game should also have this possibility. Other things; invisibility potions, weapon oil.

4. Escape. In a RPG like the one I'm working on there should be possible to escape from a fight. Enemies could have this ability as well.

5. Critical hits. This adds some good/bad luck to the fight, and some randomness. Other things; a chance to dodge, evade and parry. Based on skill.

6. Combinations. Items that can be used on your equipment to improve it. If your game has some kind of timer, you can have this effect fade away over time. Like a sword getting dull over time. Or you could pay a weapon smith to sharpen your sword just before you go into the dragons lair...

The list could go on and on.
What you need to make all this happen is a game with some kind of time or event driver. IMO that is what you are looking at right now.
The reason I am making all this fuzz is that I wrote a simple event driven game world some time ago that I thought I would show you.
*** Source Snippet Removed ***
This little RPG wannabe (I have a version with more flesh on it if your iterrested) has a game loop generating events based on user input, and a game world for the player to explore.


Ha that's cool, now I need to use that to design the ability to "move around." But, I'm having trouble figuring out how to do combat still.
I know my method of combat so far isn't the best way to do it at all, but before proposing something new, could somebody correct my design to get it to work. Then once it works and I see that there are many other and better ways to do the same thing, we can discuss those =) Thanks!

Share this post


Link to post
Share on other sites
Quote:
Original post by Shakedown
Ok so I tried my hand at a combat function for Character. Here it is:

*** Source Snippet Removed ***

This doesn't compile


Because you are trying to create a pointer to 'thisCharacter' by writing '*thisCharacter', which is going the wrong way - it's saying "pretend 'thisCharacter' is a pointer and give me the thing it points at". The compiler doesn't know how to pretend Character instances are pointers (and you should not give it a way, because it doesn't make sense), so it complains. You want an & instead of the *.

However, you really should avoid this pointer nonsense by using references whereever possible. This is one of the fixes I make to your function: I pass the argument by reference. (This is actually required so that it will do what you want: when you pass by value, a copy is made, and changes are made to the copy, which gets thrown away at the end of the function. You want the passed-in thing itself to be modified, so a reference is called for.) Then I also return a reference, so that we don't have to worry about the ugly things that can happen with pointers. ;)

There are a lot of other things to fix, too:

- In normal code, 'continue' is actually pretty unusual. Here, there's no call for it, because in both cases where you write 'continue;', there's nothing more that would happen otherwise anyway, so the 'continue' *does nothing*.

- In member functions, it is possible, and usually preferred, to refer to the 'private' members directly. If you're writing accessors and mutators to protect your class *from itself*, then you are being a little too paranoid :) (And yes, member functions of a class have access to the private members of other instances of the same class, as well.) Similarly, we don't need to write 'this->' in normal cases; it's used primarily when we need to disambiguate something (e.g. if 'foo' is the name of both a parameter and a member, then the compiler assumes you are talking about the parameter unless you write 'this->foo'; but if there is no such parameter, then 'foo' must mean the member).

- We don't need the InCombat flag in this function because we are going to return (i.e., leave the function completely) as soon as we know what the result is. Also, the check at the top of the loop for both players being alive is redundant, because we are supposed to exit the loop immediately if either player dies.

(Further, if there were any way that the condition *could* happen, then the check would fail, and there would be nothing else left to do in the loop - so we'd repeat the loop, and 'InCombat' would still be set, and both players would still be dead, etc. - i.e., an infinite loop. ;) Of course, it shouldn't happen, but it's a bad idea to program in such possibilities. Although, it seems to me that it *does* happen, because the code doesn't yet check for players dying due to a counterattack.)

- 'thisCharacter' is a rather strange name for the parameter, especially since it's *not* "this character", but some other one. :) (Also, you know it's a Character from the declaration, so repeating that information in the name is not particularly useful.)

- Please get in the habit of fully bracing ifs and elses (i.e., put in the braces even if there is only one statement). It will save you headaches in the long run.

- 'sp' and 'sp2' are not very descriptive names.

- The if and else cases do very similar things. In order to avoid repeating code, what I do is create a smaller function that implements a smaller chunk of the action - i.e., one player attacking another. Then, so that I don't repeat the code logic for "one player attacks the other, then if the other is alive, s/he attacks the first", I again use references ;) The result of the speed comparison is used to initialize two variables of reference type (note that these must *always* be initialized, because of how they work), and then I just have one piece of code for the core logic: attacker hits defender; if defender survives, defender hits attacker.


bool Character::kill(Character& target) {
target.health -= power - target.defense;
return target.health <= 0; // did we actually kill?
}

Character& Character::versus(Character& other) {
while (true) {
int otherInitiative = rand() % 10 + other.speed;
int myInitiative = rand() % 10 + speed;
boolean IGoFirst = myInitiative > otherInitiative;
Character& attacker = IGoFirst ? *this : other;
Character& defender = IGoFirst ? other : *this;
if (attacker.kill(defender)) {
return attacker;
}
if (defender.kill(attacker)) {
return defender;
}
// Otherwise, we try again, implicitly via the while loop.
}
}



Quote:

And here's where I call it:

cout << *( playerDataBase[0].versus( enemyDataBase[0] ) ).getName();


And this doesn't compile either.


Because 'playerDataBase' is a ChDatabase (please, call it CharacterDatabase... did you look at my other suggestions in the previous thread BTW?) instance, and you're trying to use it like an array, and you didn't tell the compiler how to pretend it's an array. :) In this case, it *does* make sense to give the compiler such instructions. We do this in C++ with another operator overload - the 'operator[]':


class CharacterDatabase {
std::vector<Character> characters;
// etc. etc. etc.

public:
Character& operator[](size_t index) {
return characters[index];
}
const Character& operator[](size_t index) const {
return characters[index];
}
// etc. etc. etc.
};


Now the subscripts will work like you were trying to use them.

Of course, 'versus()' now returns a reference, rather than a pointer, so you'll want to get rid of that '*', and you'll be able to lose the outermost brackets (indicating what was to be dereferenced) as well:


cout << playerDataBase[0].versus(enemyDataBase[0]).getName();


OK, I'll let you keep *that* accessor :)

Share this post


Link to post
Share on other sites
Quote:
Original post by Zahlman
Quote:
Original post by Shakedown
Ok so I tried my hand at a combat function for Character. Here it is:

*** Source Snippet Removed ***

This doesn't compile


Because you are trying to create a pointer to 'thisCharacter' by writing '*thisCharacter', which is going the wrong way - it's saying "pretend 'thisCharacter' is a pointer and give me the thing it points at". The compiler doesn't know how to pretend Character instances are pointers (and you should not give it a way, because it doesn't make sense), so it complains. You want an & instead of the *.

However, you really should avoid this pointer nonsense by using references whereever possible. This is one of the fixes I make to your function: I pass the argument by reference. (This is actually required so that it will do what you want: when you pass by value, a copy is made, and changes are made to the copy, which gets thrown away at the end of the function. You want the passed-in thing itself to be modified, so a reference is called for.) Then I also return a reference, so that we don't have to worry about the ugly things that can happen with pointers. ;)

There are a lot of other things to fix, too:

- In normal code, 'continue' is actually pretty unusual. Here, there's no call for it, because in both cases where you write 'continue;', there's nothing more that would happen otherwise anyway, so the 'continue' *does nothing*.

- In member functions, it is possible, and usually preferred, to refer to the 'private' members directly. If you're writing accessors and mutators to protect your class *from itself*, then you are being a little too paranoid :) (And yes, member functions of a class have access to the private members of other instances of the same class, as well.) Similarly, we don't need to write 'this->' in normal cases; it's used primarily when we need to disambiguate something (e.g. if 'foo' is the name of both a parameter and a member, then the compiler assumes you are talking about the parameter unless you write 'this->foo'; but if there is no such parameter, then 'foo' must mean the member).

- We don't need the InCombat flag in this function because we are going to return (i.e., leave the function completely) as soon as we know what the result is. Also, the check at the top of the loop for both players being alive is redundant, because we are supposed to exit the loop immediately if either player dies.

(Further, if there were any way that the condition *could* happen, then the check would fail, and there would be nothing else left to do in the loop - so we'd repeat the loop, and 'InCombat' would still be set, and both players would still be dead, etc. - i.e., an infinite loop. ;) Of course, it shouldn't happen, but it's a bad idea to program in such possibilities. Although, it seems to me that it *does* happen, because the code doesn't yet check for players dying due to a counterattack.)

- 'thisCharacter' is a rather strange name for the parameter, especially since it's *not* "this character", but some other one. :) (Also, you know it's a Character from the declaration, so repeating that information in the name is not particularly useful.)

- Please get in the habit of fully bracing ifs and elses (i.e., put in the braces even if there is only one statement). It will save you headaches in the long run.

- 'sp' and 'sp2' are not very descriptive names.

- The if and else cases do very similar things. In order to avoid repeating code, what I do is create a smaller function that implements a smaller chunk of the action - i.e., one player attacking another. Then, so that I don't repeat the code logic for "one player attacks the other, then if the other is alive, s/he attacks the first", I again use references ;) The result of the speed comparison is used to initialize two variables of reference type (note that these must *always* be initialized, because of how they work), and then I just have one piece of code for the core logic: attacker hits defender; if defender survives, defender hits attacker.

*** Source Snippet Removed ***

Quote:

And here's where I call it:

cout << *( playerDataBase[0].versus( enemyDataBase[0] ) ).getName();


And this doesn't compile either.


Because 'playerDataBase' is a ChDatabase (please, call it CharacterDatabase... did you look at my other suggestions in the previous thread BTW?) instance, and you're trying to use it like an array, and you didn't tell the compiler how to pretend it's an array. :) In this case, it *does* make sense to give the compiler such instructions. We do this in C++ with another operator overload - the 'operator[]':


class CharacterDatabase {
std::vector<Character> characters;
// etc. etc. etc.

public:
Character& operator[](size_t index) {
return characters[index];
}
const Character& operator[](size_t index) const {
return characters[index];
}
// etc. etc. etc.
};


Now the subscripts will work like you were trying to use them.

Of course, 'versus()' now returns a reference, rather than a pointer, so you'll want to get rid of that '*', and you'll be able to lose the outermost brackets (indicating what was to be dereferenced) as well:


cout << playerDataBase[0].versus(enemyDataBase[0]).getName();


OK, I'll let you keep *that* accessor :)


Oh sweet Zahlman replied!

Wow...beautifully simple. And by the way, "did you look at my other suggestions in the previous thread BTW?" - many times =) I just didn't change the ChDatabase to CharacterDatabase for some reason.

For the most part I understand what's going on, but Im confused about these lines:

Character& attacker = IGoFirst ? *this : other;
Character& defender = IGoFirst ? other : *this;

What's going on here? Are you creating 2 Character objects...references...equal to? And I don't see what happens with *this : other; and other : *this;.

But thank you very much for your amazing replies!

Share this post


Link to post
Share on other sites
Quote:

Character& attacker = IGoFirst ? *this : other;


Since I'm already here I can chip in...
What we are looking at here is a conditional assignment, an operator consisting of two symbols ? and :

This statement works much like a if/else clause.
If IGoFirst is true, the result of the statement will look like
Character& attacker = *this;
if it is false, the result is
Character& attacker = other;

And yes, Character& attacker is how we declare a reference to an already existing Character =)

Share this post


Link to post
Share on other sites
Quote:
Original post by Shakedown
the most part I understand what's going on, but Im confused about these lines:

Character& attacker = IGoFirst ? *this : other;
Character& defender = IGoFirst ? other : *this;

What's going on here? Are you creating 2 Character objects...references...equal to? And I don't see what happens with *this : other; and other : *this;.


The ?: construct is "the ternary operator". It's called that because it's ternary (it takes three arguments: before, in between and after the two symbols), it's the only one in C or C++ that's ternary, and noone seems to have a better name for it ;)

The meaning is: if 'IGoFirst', the value following the '?' is used; otherwise, the value following the ':' is used.

I declare and *initialize* two variables of type "reference to Character". These are aliases for existing characters. Because references can only ever refer to valid instances (unless you've already done something wrong somewhere else), and because you can't change what they refer to (called "reseating"), it follows that every variable of a reference type must be initialized. (Note that when you pass or return by reference, the initialization is implicit in the parameter passing or 'return' statement, respectively.)

You can't just declare it and then assign to it, because in the tiny space of time before the assignment, it wouldn't refer to anything, and references just don't work that way. Also because assigning to a reference actually assigns to the referred-to thing instead - remember, the variable is an alias for the referred-to thing; i.e. it becomes another name for that thing.

Therefore, we can't set up if/else logic to initialize the reference conditionally to one thing or the other, so we use the ternary operator instead. The net result is that 'attacker' is another name for either '*this' (i.e., the Character upon which the function was called) or 'other' (i.e., the other Character), and 'defender' is another name for whichever character is not the 'attacker'. After that, we can unconditionally talk about the attacker and defender, and be referring to the correct Characters automatically, without doing extra if/else logic further down.

If references are still going over your head, maybe you just need to hear the same thing multiple ways (i.e. mine and Mr. Cline's; wish I had more). [smile]




Some trivia: By now, I hope that '*this' is starting to look kind of ugly to you. ;) After all, that pointer should always point to a valid instance of the class (and not be NULL, dangling, corrupted etc.), and you shouldn't be able to change it (you can't cause the object to suddenly *be* another object; the best you could do is make it logically equal to the other object). So really, it should be a reference. Unfortunately, we're stuck with 'this' being a pointer instead? Why? Well, when C++ was in the early stages of development, emerging from the primordial ooze of C, the concept of 'this' was thought up and implemented before the concept of refernces. Sorry. [sad]

Share this post


Link to post
Share on other sites
Ok, well I made the changes Zahlman suggested, and it compiles but there is an infinite loop that gets entered sometimes. I can't figure it out. Here's the code:

bool Character::kill( Character& thisCharacter )
{
thisCharacter.health -= ( (power - thisCharacter.defense) < 0 ? 0 : (power - thisCharacter.defense) );
return( thisCharacter.health <= 0 );
}

Character& Character::versus( Character& thisCharacter )
{
while( true )
{
int thisCharacterInitiative = rand() % 10 + thisCharacter.speed;
int playerInitiative = rand() % 10 + speed;
bool IGoFirst = playerInitiative >= thisCharacterInitiative;
//cout << "1\n"; // Scaffolding
Character& attacker = IGoFirst ? *this : thisCharacter;
//cout << "2\n";
Character& defender = IGoFirst ? thisCharacter : *this;
//cout << "3\n";
if( attacker.kill( defender ) )
{
return( attacker );
}
if( defender.kill( attacker ) )
{
return( defender );
}
}
}



And here's where I call the function:


void fight()
{
cout << "Prepare to fight!\n\n";

for( int i = 0; i < enemyDataBase.vectorsize(); ++i )
{
cout << (playerDataBase[0].versus( enemyDataBase[i] )).getName() << " has come out victorious!\n\n";
}



Sometimes it works and prints out the names, sometimes it enters an infinite loop.

Share this post


Link to post
Share on other sites

This topic is 3858 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

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