Sign in to follow this  
EvilCloneVlad

Global Variable Problems

Recommended Posts

EvilCloneVlad    114
I have a Player Class for my RPG and I have a function called NewPlayer(); that creates an instance of the Player class and i am getting errors when trying to access the functions of the Player class and I believe this is because when I create the instance in the NewPlayer function it isnt global, my question is how do i make it so that i can still create the instance in that function but make it Global so that my other functions can use it?

Share this post


Link to post
Share on other sites
EvilCloneVlad    114
I dont quite understand what you were saying, but the function i have that creates the player instance is not part of the player class, it is a completely seperate function

Share this post


Link to post
Share on other sites
jyk    2094
Quote:
Original post by EvilCloneVlad
I dont quite understand what you were saying, but the function i have that creates the player instance is not part of the player class, it is a completely seperate function
Post the code and error messages (don't forget to use [ source ] tags :).

Share this post


Link to post
Share on other sites
EvilCloneVlad    114
I'm assuming that it is because of global or not, but not exactly sure. I know that my method for the loop probably isnt the best, but i just want to find a way to create a new instance of the Player class and have all my other outside functions be able to access it.

And sorry for the unorganized (and long) code, still haven't gotten around to splitting it up into different files.

167 c:\docume~1\owner\mydocu~1\c__pro~1\gladia~1\main.cpp
`CurrentPlayer' undeclared (first use this function)


#include <iostream>
#include <cstdlib>
#include <string>

using namespace std;

int MainMenu();
int NewPlayer();
int LoadGame();
int GameMenu();
int ArenaMenu();
int StoreMenu();
int SaveGame();
int FightGoblin();
int FightSkeleton();
int FightOgre();


class Player {
public:
string GetPlayerName();
int GetHealth();
int GetAttack();
int GetDefense();
int GetGold();
int GetExperience();
int GetExperienceNeeded();
void SetPlayerName(string);
void SetHealth(int);
void SetAttack(int);
void SetDefense(int);
void SetGold(int);
void SetExperience(int);
void SetExperienceNeeded(int);
Player();
~Player();

private:
string PlayerName;
int Health;
int Attack;
int Defense;
int Gold;
int Experience;
int ExperienceNeeded;
};

string Player::GetPlayerName(){
return PlayerName;
};

int Player::GetHealth(){
return Health;
};

int Player::GetAttack(){
return Attack;
};

int Player::GetDefense(){
return Defense;
};

int Player::GetGold(){
return Gold;
};

int Player::GetExperience(){
return Experience;
};

int Player::GetExperienceNeeded(){
return ExperienceNeeded;
};

void Player::SetPlayerName(string name){
PlayerName = name;
};

void Player::SetHealth(int h){
Health = (Health - h);
};

void Player::SetAttack(int a){
Attack = a;
};

void Player::SetDefense(int d){
Defense = d;
};

void Player::SetGold(int g){
Gold = Gold + g;
};

void Player::SetExperience(int e){
Experience = Experience + e;
};

void Player::SetExperienceNeeded(int n){
ExperienceNeeded = n;
};

int main()
{
int GameState = 0;
Player CurrentPlayer;
while (GameState != 9)
{
switch(GameState)
{
case 0:
GameState = MainMenu();
break;
case 1:
GameState = NewPlayer();
break;
case 2:
GameState = LoadGame();
break;
case 3:
GameState = GameMenu();
break;
case 4:
GameState = ArenaMenu();
break;
case 5:
GameState = StoreMenu();
break;
case 6:
GameState = SaveGame();
break;
case 9:
QuitGame();
break;
}
}

return 0;
}

int MainMenu(){
int x = 0;
cout << "1. New Game" << endl;
cout << "2. Load Game" << endl;
cout << "3. Quit Game" << endl;
cin >> x;
if (x == 1)
{
return 1;
}
else if (x == 2)
{
return 2;
}
else if (x == 3)
{
return 9;
}
}

int NewPlayer(){
string TempPlayerName;
int TempStat;
cout << "What is your name? ";
cin >> TempPlayerName;
CurrentPlayer.SetPlayerName(TempPlayerName);
//Set Health
TempStat = rand();
TempStat = ((abs(TempStat)%6)+45);
CurrentPlayer.SetHealth(TempStat);
//Set Attack
TempStat = rand();
TempStat = ((abs(TempStat)%2)+10);
CurrentPlayer.SetAttack(TempStat);
//Set Defense
TempStat = rand();
TempStat = ((abs(TempStat)%2)+4);
CurrentPlayer.SetDefense(TempStat);
//Set Gold
TempStat = 200;
CurrentPlayer.SetGold(TempStat);
//Set EXP
TempStat = 0;
CurrentPlayer.SetExperience(TempStat);
//Set EXP Needed
TempStat = 250;
CurrentPlayer.SetExperienceNeeded(TempStat);
return 3;
}

int LoadGame(){
return 3;
}

int GameMenu(){
int x = 0;
cout << "1. Arena" << endl;
cout << "2. Store" << endl;
cout << "3. Save Game" << endl;
cin >> x;
if (x == 1)
{
return 4;
}
else if (x == 2)
{
return 5;
}
else if (x == 3)
{
return 6;
}
else
{
return 3;
}
}

int ArenaMenu() {
int x = 0;
cout << "1. Fight Goblin" << endl;
cout << "2. Fight Skeleton" << endl;
cout << "3. Fight Ogre" << endl;
cout << "4. Exit Game" << endl;
cin >> x;
if (x == 1)
{
FightGoblin();
return 3;
}
else if (x == 2)
{
FightSkeleton();
return 3;
}
else if (x == 3)
{
FightOgre();
return 3;
}
else if (x == 4)
{
return 3;
}
else
{
return 4;
}
}

int StoreMenu(){
int x = 0;
cout << "1. Potion - Heals 25 HP - 50 Gold" << endl;
cout << "2. Double Potion - Heals 50 HP - 90 Gold" << endl;
cout << "3. Super Potion - Heals 125 HP - 200 Gold" << endl;
cout << "4. Potion of Lions Might - Increases Attack by 1 - 250 Gold" << endl;
cout << "5. Elixir of Stone Skin - Increases Defense by 1 - 225 Gold" << endl;
cout << "6. Exit Store" << endl;
cin >> x;
if (x == 1)
{
UsePotion();
return 3;
}
else if (x == 2)
{
UseDblPotion();
return 3;
}
else if (x == 3)
{
UseSprPotion();
return 3;
}
else if (x == 4)
{
UseLionMight();
return 3;
}
else if (x == 5)
{
UseStoneSkin();
return 3;
}
else if (x == 6)
{
return 3;
}
else
{
return 5;
}
}

int SaveGame(){
return 3;
}

int FightGoblin(){
int GobHealth = 25;
int Damage = 0;
while (CurrentPlayer.GetHealth() > 0 || GobHealth > 0)
{
cout << CurrentPlayer.GetPlayerName() << "swings and deals ";
cout << CurrentPlayer.GetAttack() - 6 << " damage." << endl;
GobHealth = (GobHealth - (CurrentPlayer.GetAttack() - 6));
cout << "Goblin swings and deals ";
cout << 4 - CurrentPlayer.GetDefense() << " damage." << endl;
Damage = (4 - CurrentPlayer.GetDefense());
CurrentPlayer.SetPlayerHealth(Damage);
}
if (CurrentPlayer.GetPlayerHealth() > 0)
{
cout << "Congratulations you have defeated the Goblin!" << endl;
cout << "You have gained 50 EXP and 25 Gold" << endl;
CurrentPlayer.SetGold(25);
CurrentPlayer.SetExperience(50);
return 4;
}
else
{
cout << "You have been defeated by the mighty Goblin!" << endl;
return 2;
}
}

Share this post


Link to post
Share on other sites
Sphet    631
Quote:
Original post by EvilCloneVlad
I'm assuming that it is because of global or not, but not exactly sure. I know that my method for the loop probably isnt the best, but i just want to find a way to create a new instance of the Player class and have all my other outside functions be able to access it.

And sorry for the unorganized (and long) code, still haven't gotten around to splitting it up into different files.

167 c:\docume~1\owner\mydocu~1\c__pro~1\gladia~1\main.cpp
`CurrentPlayer' undeclared (first use this function)


*** Source Snippet Removed ***


Try declaring CurrentPlayer outside of main() - it's declared inside it so no other functions can see it. Declaring it inside makes it local to the scope of main() not the file.

Share this post


Link to post
Share on other sites
Zahlman    1682
Quote:
Original post by EvilCloneVlad
And sorry for the unorganized (and long) code, still haven't gotten around to splitting it up into different files.

*** Source Snippet Removed ***


Ummm.... *wow*.

Let me try to give you a few good ideas by example (and also lengthy comments ;) ). Some of these are things that beginners probably can't reasonably be expected to know about, but they're things that I strongly feel they *should* learn ASAP. Especially for projects of this (apparent; you'll note that a main effect of what I'm doing is to *shorten* the code... by a lot... although it's hard to tell with all the educational block comments) size. Also, note that I also haven't made an attempt to split things up... this is partly because I'm at least as lazy as you, and partly to emphasize the value of all the other changes. :)


// Your indents are a strange and inconsistent size. This is inelegant, to say
// the least. I have changed the indentation to tabs; indenting with tabs
// *properly* takes a little discipline, but it has the advantage that everyone
// working on the code can just set their tab stop to whatever amount of
// indentation they like, and things just "look right" automatically.

#include <iostream>
#include <cstdlib>
#include <string>
// I'm going to need some other libraries...
#include <vector>
#include <sstream>

// When you *do* split the class off to a header, *don't* put 'using namespace
// std;' in the header file. Putting using-declarations into a header file
// means that every source file that includes the header is *stuck* with that
// declaration, which the code author might not be aware of. This can cause
// subtle bugs. Really.
using namespace std;

// Don't declare your functions at the top and then implement them later;
// instead, implement them in order. This carries several benefits:
// - You avoid typing the separate function prototypes.
// - You avoid *maintaining* the separate function prototypes.
// - If there is any circular dependency between your functions (could indicate
// a problem - check for undesired mutual recursion), it will be automatically
// highlighted, because you will *have* to provide a prototype to make it
// compile - if you are providing prototypes for everything anyway, then you
// never find out what was forced.
// Anyway, none of those functions are needed by the Player definition, so
// I define Player fully first...

class Player {
// Labels (the 'case' labels within a switch statement, as well as
// 'public', 'private', 'protected', 'default' and goto-targets) don't
// create a scope, so I personally don't indent for them (because I like
// my indentation to tell me what the scope is).
//
// You'll also note that I've changed the way braces are used in the code,
// so that opening braces *do not* get their own line. You start out coding
// the player class this way, but then write the rest of the code the other
// way - either is fine (I prefer this way personally), but inconsistency is
// bad.
public:
// It looks like you read some reference somewhere that told you to make an
// "accessor" and a "mutator" for evry data member of an object, so that you
// can look at and change the values "in the OO way".
//
// This is *utter nonsense and sacrilege*. Please find that reference and
// *destroy it* if at all possible, because it is spreading *lies* and
// teaching *the exact opposite of what OO is supposed to be*. Yes, I really
// do feel this strongly about it, and yes, I can back up what I'm saying:
//
// OO is supposed to encourage you to think about objects in terms of their
// interface; i.e. what they can do. You are supposed to "program to an
// interface", so that you can change the underlying implementation.
//
// Using accessor/mutator pairs encourages you to think about the objects in
// terms of the data members named in the function names. You abstract
// nothing this way, because you simply create an interface of "looking at
// and changing values". Even if the named member happens to have been
// replaced - as these functions allow you to do - the user is still
// *thinking* about the object in an overly primitive way.
//
// Meanwhile, OO is also designed to provide convenient places (ordinary
// member functions) to put common code that represents a conceptual "task"
// upon an object - so that you don't have to repeat all the steps explicitly
// in your code each time the code needs to perform the action. That is to
// say, it gives you a way to *avoid redundancy*, and create *encapsulation*.
//
// Accessor/mutator pairs, as they are commonly seen, *increase redundancy*,
// by having you write the member name three times instead of just once.
// Meanwhile, in the most common case (even though you can theoretically do
// better), you encapsulate exactly nothing, because there is no code put
// into the functions beyond reading or writing the value.
//
// That said, you did have some "mutators" that were not just plain mutators
// (because they performed arithmetic upon a member instead of just setting
// it); but consequently, those functions were *wrongly named*.
//
// To fix all of this, I ripped out *all* of the Player member functions,
// noted the compile errors, and then studied the relevant code to figure out
// what the *functionality* of the Player is.
// Also, DO NOT declare a destructor unless you need one; the only thing it
// accomplishes is to force you to implement it somewhere with an empty
// function body. That's extra work in *two* places, communicating exactly
// nothing about your object design. (Note that because the destructor is
// called *automatically* rather than by calling code, it isn't really part
// of the class interface, even though it technically is to the compiler).
// For the same reason, DO NOT declare default constructors if they will
// do nothing. In fact, don't declare them *anyway* unless you have a good
// reason. In your case, it is *a bad idea* to make it *possible* to
// default-construct Players, because there is no meaningful "default value
// for a Player".
int damage(int opposedArmor) { return Attack - opposedArmor; }
string& name() { return Name; }
int armor() { return Defense; }
void takeDamage(int damage) {
Health -= damage;
}
bool alive() {
return (Health > 0);
}
void award(int gold, int xp) {
Gold += gold;
Experience += xp;
}

// I assume that experience should *always* start at zero, and that your
// "level progression" will be constant. But I allow for, say, the starting
// gold to be specified, even though it's *currently* always the same. This
// isn't a YAGNI problem; we're trying to specify a good *interface* first.
// We want to change interfaces as little as possible - much less often than
// implementation code.
// Anyway, this shows a good example of a constructor using an initialization
// list to initialize the data members. Look how much shorter this is (in
// combination with the calling code) compared to setting members
// individually :)
Player(const string& Name, int Health, int Attack, int Defense, int Gold) :
Name(Name), Health(Health), Attack(Attack), Defense(Defense), Gold(Gold),
Experience(0), ExperienceNeeded(250) {}

private:
// Members of a struct - that means both data members and member functions,
// and classes as well as structs, since they're basically the same in C++ -
// should almost never include the struct name in their name. It's extra
// typing for no added information.
string Name;
int Health;
int Attack;
int Defense;
int Gold;
int Experience;
int ExperienceNeeded;
};

// Instead of using magic numbers all over the place to represent the game
// state, you can use an enumeration. This is the simplest fix; more complex
// solutions have some very real advantages, but I don't want to overload you
// just yet. Anyway, just by using an enumeration, you avoid any possibility
// of putting the wrong number, AND you make sure that your "quit" option will
// never collide with a meaningful value, AND you give meaningful names to
// those values. It is vastly more maintainable.
// I took out the "load game" and "save game" states because they do nothing
// right now. The proper time to add these is when you have something for them
// to do. See http://c2.com/cgi/wiki?YouArentGonnaNeedIt.
// (Actually, I could remove lots more based on that principle, but I won't
// this time - but seriously, don't code too far ahead of yourself; get it to
// work - or at least compile - *first*. Then add the next thing. One step at
// a time.)
// Note how much easier the enum will make it to add these when the time comes
// :)
enum GameState {
MAINMENU, NEWPLAYER, GAMEMENU, ARENAMENU, STOREMENU, QUIT, DIE
};

// You commonly repeat code that (a) prints a set of options, which are
// numbered 1 through n, (b) inputs a number and (c) acts on that value.
// There are a couple of problems here:
// 1) Code redundancy. We can make a function to abstract this process, which
// gives us an ideal place to add
// 2) Error checking. Put bluntly, if the user *ever* inputs anything that's
// *not* recognizable as a number, your program is *screwed*. This is because
// of how formatted I/O works: it leaves the "garbage" value around, so that
// it would be picked up on the next read (it is a *very good thing* that it
// does this, because it lets you "retry" reading a value as a different type -
// important for some file formats, for example), and also sets a flag that
// temporarily disables reading from the stream (also a *very good thing*,
// because it gives you a way to *know* that something wasn't read - there is
// no way to look at an int value and tell whether it is uninitalized or was
// really set to that strange value).
// The function also does the numbering for us, which avoids any typos in
// numbering (as well as busy-work when we modify a menu).
// We can actually improve on these ideas much further, but again, I don't want
// to overload you.

typedef vector<string> menu;

int getMenuOption(const menu& m) {
typedef vector<string>::iterator iterator;
int result = 0;
int count = m.size();
// Repeat until we get a proper value: (notice we initialize 'result' to an
// "invalid" value - 0 - to make sure we loop at least once)
while (result < 1 || result > count) {
// Output the menu (the menuItems contain the text, of course)
for (int i = 0; i < count; ++i) {
// Please don't use std::endl as a substitute for newlines.
// std::endl also flushes the buffer, which is a different thing.
cout << (i + 1) << ". " << m[i] << '\n';
}
// Using cin will automatically flush cout.
// To avoid causing any problems with reading from cin, we read from it in
// always-successful ways into a string, and then re-parse the string
// contents, using a std::stringstream (from <sstream>). Garbage input will
// then leave stuff in the sstream, and set the flag on the sstream; and
// we don't care - we'll just throw it away.
string line;
getline(cin, line);
stringstream ss(line);
ss >> result; // if it fails, 'result' will still be 0, and the loop will
// repeat. The next time through, we have a brand new stringstream object
// holding the next line of user input.
// Handling the standard input this way also avoids problems with "leftover"
// text from a previous user input.
}
return result;
}

GameState MainMenu() {
menu main;
// We "build our menu", and then use it. Notice that a switch statement is
// more appropriate than a tree of if's here; with the switch, we don't need
// a temporary variable at all.
main.push_back("New Game");
main.push_back("Load Game");
main.push_back("Quit Game");

switch (getMenuOption(main)) {
case 1: return NEWPLAYER;

case 2:
cout << "Sorry, can't load games yet. Let's start a new one..." << endl;
return NEWPLAYER;

default: return QUIT;
}
}

GameState GameMenu(Player& p) {
menu game;
game.push_back("Arena");
game.push_back("Store");
game.push_back("Save Game");
switch (getMenuOption(game)) {
case 1: return ARENAMENU;
case 2: return STOREMENU;
default: return GAMEMENU; // since there is no save-game yet.
}
}

GameState FightGoblin(Player& p){
int GobHealth = 25;
while (p.alive() && GobHealth > 0) { // should be and, not or
// When a value is used more than once, that's when you put it into a
// variable and use the variable. Here, that's the damage calculation.
// Also, please scope your variables properly. Here I show some examples
// of good variable manipulation techniques.
int pdamage = p.damage(6);
int gdamage = 4 - p.armor();
// You don't need a separate cout for each line of source code, you know.
cout << p.name() << " swings and deals "
<< pdamage << " damage.\nGoblin swings and deals "
<< gdamage << " damage." << endl;
GobHealth -= pdamage; // notice the '-=' operator is used to avoid
// repeating the variable name.
p.takeDamage(gdamage);
}
if (p.alive()) {
cout << "Congratulations you have defeated the Goblin!\n"
<< "You have gained 50 EXP and 25 Gold" << endl;
p.award(25, 50);
return ARENAMENU;
} else {
cout << "You have been defeated by the mighty Goblin!" << endl;
return NEWPLAYER;
}
}

GameState ArenaMenu(Player& p) {
menu arena;
arena.push_back("Fight Goblin");
arena.push_back("Fight Skeleton");
arena.push_back("Fight Ogre");
arena.push_back("Exit Game");
switch (getMenuOption(arena)) {
// FightGoblin() was written to return a "game state" value, so it's silly
// to ignore it. Instead, we pass it back directly:
case 1: return FightGoblin(p);
case 2: cout << "Sorry, skeletons not implemented yet" << endl;
return GAMEMENU;
case 3: cout << "Sorry, ogres not implemented yet" << endl;
return GAMEMENU;
default: return QUIT;
// You actually had some wrong values in here which have been fixed :)
}
}

GameState StoreMenu(Player& p){
menu store;
store.push_back("Potion - Heals 25 HP - 50 Gold");
store.push_back("Double Potion - Heals 50 HP - 90 Gold");
store.push_back("Super Potion - Heals 125 HP - 200 Gold");
store.push_back("Potion of Lions Might - Increases Attack by 1 - 250 Gold");
store.push_back("Elixir of Stone Skin - Increases Defense by 1 - 225 Gold");
store.push_back("Exit Store");
switch (getMenuOption(store)) {
default: return GAMEMENU;
// We'll implement the actual potions later. For now, let's see the menu
// working ;)
}
}

// Now, here's how we'll handle the Player. We really want to *create* the
// Player as a result of NewPlayer(), and then start *passing it between
// functions* (this is how you communicate a variable between functions
// without it being a global: just pass it as a parameter).
// What we need to do is distinguish between states that do and don't involve
// Players. I'll make two separate "game loop" functions, for the two sets of
// states. We start in the loop for states that *don't* use it; the NewPlayer()
// function will call into the loop for states that *do* use it, which will in
// turn return a GameState to indicate whether the user quit (in which case we
// should break from the first loop too) or died (in which case we should go
// back to NewPlayer()).

// The original loop will provide us with the Player to use.
GameState GameLoop(Player& p) {
GameState state = GAMEMENU;
while (state != QUIT && state != DIE) {
switch (state) {
// Really short case statements can go on one line, like this.
case GAMEMENU: state = GameMenu(p); break;
case ARENAMENU: state = ArenaMenu(p); break;
case STOREMENU: state = StoreMenu(p); break;
// An assertion is useful here to make sure the state value is a valid
// one. The way it works is, if it *isn't* for any reason at this point
// in the code, the assert will deliberately crash the program, in a
// controlled way. This makes sure that it doesn't keep trying to
// calculate stuff with garbage data, and can also give us information
// to the effect that the code is wrong (and also help us figure out
// where).
default: assert(false);
}
}
return state;
}

GameState NewPlayer() {
// Create a player, pass it to the GameLoop(), and forward back the result.
cout << "What is your name? ";
string name;
getline(cin, name); // for consistency with the menu: always read whole lines
// of input, to avoid "leftover data" problems. This also allows the player
// to have a space in his/her name, e.g. to put a first and last name. :)
Player p(name,
(abs(rand())%6)+45, (abs(rand())%2)+10, (abs(rand())%2)+4,
200);
// Can't use a temporary for the Player here; we need for the other functions
// to be able to modify the Player, so it needs an "identity" (dedicated
// location in memory) which temporaries don't (the compiler might, in effect,
// get rid of a temporary completely as a result of its optimizations).
return GameLoop(p);
}

int main() {
srand(time(0)); // so that you don't get the same set of random numbers
// every time you run the program. (Being able to do that is a *good thing*,
// btw; often useful for testing.)
GameState state = MAINMENU;
while (state != QUIT) {
switch (state) {
case MAINMENU: state = MainMenu(); break;
case NEWPLAYER: state = NewPlayer(); break;
case DIE: state = NEWPLAYER; break;
// Of course, we could have just arranged to return NEWPLAYER directly
// from GameLoop(), but this way is a little more readable.
// If we get QUIT back from GameLoop() (in turn from NewPlayer()), it
// will be of course caught by the while loop.
default: assert(false);
}
}
// This is the proper place for your QuitGame() logic. Within the switch
// statement, it would actually never be called, because the while loop
// would not run that time. Of course, right now there seems not to be any :)
// You don't need to 'return 0' explicitly here - it's implicit, as a special
// case for the main() function.
}




(STATS: Out of 327 lines of code, I removed 148 (some of which were comments), and put back 198 lines of comments, almost none of which I would actually write if I were doing this myself but which are just there for education. :) )

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