Calling a class function from a Vector instead of an Array - c++

I am currently working on a way to load a bunch of different NPCs from a file and loading it into my game. I have everything working correctly with arrays but I would like to change it to using a vector since I can change the size in case I need more NPCs than the space available in the array and so I don't just have a mostly empty array if I dont need many NPCs at the current time. Note that the following code is from a testing program, not my actual programming. I made it so I dont mess with the complete project by accident.
int main()
{
char input;
bool Running = true;
NPC Creatures[MAX_NPCS];
//InitCreatures loads the X, Y and Type from the file. I know with vectors I have to
//resize it as I go along, Which would be included in the function.
if(Creatures[MAX_NPCS].InitCreatures(Creatures) == false)
{
Creatures[MAX_NPCS].CleanUp(Creatures);
return 0;
}
while(Running == true)
{
cout << "(C)heck an NPC, (A)ttack and NPC or (E)xit the program\n";
cin >> input;
switch(input)
{
case 'C': Creatures[MAX_NPCS].Check(Creatures); break;
case 'c': Creatures[MAX_NPCS].Check(Creatures); break;
//The Check function just shows the X, Y and Type of the NPC
case 'A': Creatures[MAX_NPCS].Attack(Creatures); break;
case 'a': Creatures[MAX_NPCS].Attack(Creatures); break;
//Attack shows X, Y and type and then removes that NPC from the array.
case 'E': Running = false; break;
case 'e': Running = false; break;
default: cout << "That was not a valid input\n"; break;
}
}
Creatures[MAX_NPCS].CleanUp(Creatures);
cout << "Exiting\n";
system("PAUSE");
return 0;
}
Really the only problem I am having is getting Main to run the NPC Class functions from a vector instead of using the Array like I have now. I can easily change the other things in the functions I'm calling to accept the vector and handle it correctly.
When trying to use a vector to run the functions I was only able to call them when I had something like this:
Creatures[1].Attack(Creatures);
Of course when I call them like that the values don't return correctly and I usually get an error and Besides I don't know how many NPCs will be loaded for the current map, if Any.
Any help with this would be appreciated. I realize I am a newbie when it comes to programming, especially when it comes to Vectors. If my function code is needed I will gladly post it.

You could just create a vector and have the first element in there to be able to call the InitCreatures function (you could also overwrite the first creature later).
vector<NPC> Creatures(1);
Creatures[0].InitCreatures(Creatures);
I'm assuming that in class you have the parameter passed by reference.
bool InitCreatures(vector<NPC>& x) { ... }
But since you give creatures as a parameter to every function you have (do you need it in check or attack?) - wouldn't it be better to have a class to hold the NPC vector?

Related

Telephone Directory: dividing code/making menu

I am making a Telephone Directory. Now, as someone said dividing code in small section is a better code. I want to make it something like, but the problem is I can't access the object from main function to a class's member function or a traditional function. I am not sure how pass an object (arrays of objects) as a parameter (to a function).
case 2:
{
print_header("Update Menu");
print_update_menu();
break;
This is my code:-
case 2:
Print_Header("Update Menu");
while (1)
{
cout<<"Please enter a Name or ID to Update:"<<endl;
cin>>search_ID_name;
for(int j=0 ; j<=records-1 ; j++)
{
if((search_ID_name==Telephone_directory[j].ID) || (search_ID_name==Telephone_directory[j].Name) )
{
Telephone_directory[j].Phone_Directory_data_display();
cout<<"Do you want to update this record? [1/0]"<<endl;
cin>>choice;
if(choice==1)
{
Telephone_directory[j].Phone_Directory_data_input();
cout<<"Record successfully updated"<<endl;
Telephone_directory[j].Phone_Directory_data_display();
}
else if (choice==0)
{
cout<<"Record not updated"<<endl;
break;
}
}
}
cout<<"Want to update another record? [y/n]"<<endl;
cin>>choice_y_n;
if(choice_y_n=='y')
{
cout<<"Redirecting to Update Menu again"<<endl;
}
else if (choice_y_n=='n')
break;
}
break;
In C++ there are two ways to pass an object to a function.
Pass by value (copy)
Pass by pointer (reference to memory location)
Of these you usually pass by reference for objects.
If you want to pass an object of type A to a function called f you would do it like so:
void f(A& a) {
// You can access a's members here i.e if a has a method called b you do a.b();
}

C++ code reduction for identical submenus

I am coding my way to the last project of the semester and I have a code duplication issue.
I am using ncurses or pdcurses to make a menu to interact with the user.
The problem: For each choice of the menu(Five in total) I need a submenu. The submenu's only difference from the main menu is, the array of Items to be printed, and the parameters that go into some functions, as a result of the Items array size. Since I need five submenus, I need five times the same code(six if you add the main menu).
Could any of you help me make a function that does the same thing, which I'll then call six times to create my menu?
Here's my code
void Menu(){
const char* Items[]={
"[1]...New tax declaration",
"[2]...Modify tax declaration",
"[3]...Cancel tax declaration",
"[4]...Additional Information",
"[5]...Exit"
};
int Cur=0;
int ch, i;
int flag=0;
do{
werase(wm);
mvwaddstr(wm, 2, 16, "MENU");
for(int i=0; i<5;i++){
if(i==Cur)
wattr_on(wm, A_REVERSE, 0);
mvwaddstr(wm, 4+i, 4, Items[i]);
wattr_off(wm, A_REVERSE, 0);
}
mvwaddstr(wm, 14, 3, "Choice: ");
wprintw(wm, "%1d", Cur+1);
wrefresh(wm);
ch=wgetch(wm);
switch(ch){
case '1':Cur=0;Sub2();break;
case '2':Cur=1;Sub1();break;
case '3':Cur=2;break;
case '4':Cur=3;break;
case '5':flag=1;break;
case KEY_UP:
case KEY_LEFT: Cur--; if (Cur<0) Cur=4; break;
case KEY_DOWN:
case KEY_RIGHT: Cur++; if(Cur>4) Cur=0; break;
case 27: flag=1; break;
case 32:
case 13:
switch (Cur){
case 0:Sub2();break;
case 1:Sub1();break;
case 2:break;
case 3:break;
case 4:flag=1;break;
}
}
}while(!flag);
}
Thank you.
p.s The code is from a book. I have little experience with ncurses
A simple menu-driven program. This is based on using std::map instead of conditional logic. This map stores a list of menuitem structures that define what the menu looks like and what each option does.
This is best explained as we work through the code, so let's dive in!
// headers for everything used in this example
#include <iostream>
#include <string>
#include <map>
#include <functional>
#include <cctype>
// function to perform menu option B sub option 1
void optionB1()
{
std::cout << "perfoming B1" << std::endl;
}
// function to perform menu option B sub option 2
void optionB2()
{
std::cout << "perfoming B2" << std::endl;
}
// function to perform menu option A
void optionA()
{
std::cout << "perfoming A" << std::endl;
}
// defines a menu item. Good naming can often save the need to comment
struct menuitem
{
std::function<void()> doIt; // function to run if option chosen
std::string description; // pretty message describing option
};
// draw menu and wait for the user to select an option.
void domenu(const std::map<char, menuitem> & menu)
{
while (true) // loop until user gives a good option. Or use a retry count.
// You decide.
{
for (auto &items : menu)
{ // for all items in the menu, print out the item and it's description text
// for what first and second mean, read up on std::map and std::pair
std::cout << items.first << ") " << items.second.description << std::endl;
}
char ch;
std::cin >> ch; // get the user's choice
// often you may want to eliminate one of the cases to reduce the amount
// of possible inputs you need to provide handling code for.
// the line below allows us to use the same code for input of A and a.
ch = std::tolower(ch); // convert input to lower case
try
{
menu.at(ch).doIt(); // call the function mapped to user's choice.
// this may do produce something or it may
// display another menu. It could end the wor--
return; // done.
}
catch (...)
{ // print error message on unsupported input
std::cout << "Error. Invalid option!" << std::endl;
}
}
}
// the B menu
std::map<char, menuitem> bmenu
{ // User input doIt function Description
{'1', {optionB1, "Option B1"}},
{'2', {optionB2, "Option B2"}}
// add more options here. Or don't. Up to you.
};
// the main menu
std::map<char, menuitem> mainmenu
{ // User input doIt function Description
{'a', {optionA, "Option A"}},
{'b', {std::bind(domenu, bmenu), "Option B"}}
// OK, so that last one was a bit weird. std::bind makes a function and
// specifies the arguments with which it will be called. This takes
// domenu binds it with bmenu so that std::function<void()> is
// satisfied. As far as the world is concerned, the bound function
// returns nothing and takes no parameters. Very complicated functions
// can be bound so long as the end result returns nothing and requires
// no parameters.
// what it's doing here is allowing us to call domenu to draw the B
// submenu, wait for valid input, and call the chosen function.
};
// good 'ol trusty main so we can test that the above code isn't utter BS.
int main()
{
while (true) // loop forever. Or use whatever exit logic is required.
{
domenu(mainmenu); // kick-start by calling do menu to run the main menu
}
return(0);
}
This will keep the code down to a minimum. All of the duplicated code is reduced to the domenu function and a smurfload of code hidden from sight in the standard library and written by folks who likely have far more experience in getting this stuff right than you or I. Whenever possible, stand on the shoulders of giants.
domenu is driven by lists of options and execution instructions for the option. Want another option? Add an item to a list and possibly provide a new function to fulfill the obligations of that option.
All you have to do is fill in the blanks.

I have an error with my return statements that I don't know to fix

In a text adventure game that I am making, all the different places are run my different functions. In my diner, market, and supply store, I have a switch statement that takes numbers 1-10. All 1-9 work, but 10 doesn't. All of these methods return back to a method called TownCenter(), but on these 3, when I do return, you have to spam it in order for it to work. Here is a code example:
void Diner(){
int answer;
cout << "Blah, blah. Type '0' to go back to town.\n";
cin >> answer;
switch (answer){
case 0:
return;
break;
}
Diner()
}
Every time you type 0, it would just go to Diner() again. It eventually works if you spam 0 over and over, but why won't it work all the time?
I don't think there is any error with the code, except for the following considerations:
Missing semicolon after Diner() in line 10.
The break; statement is not required as return; is being used before it.
I don't think you mean to use recursion here. I would suggest using while loops while people are in individual locations. By calling the same location again at the end of your case statement to go back to the same area, you are going deeper in the callstack, requiring more "exits" to "leave" the room.
A do-while loop like the following would work nicely:
int answer;
do {
cout << "Blah, blah. Type '0' to go back to town.\n";
cin >> answer;
switch (answer){
// other case statements
case 0:
break;
}
} while (answer != 0);
Notice I don't call Diner() again at the end. Getting it to process the same room until the "leave" is accomplished by the loop instead of unnecessary recursion.

How to make a function change a global array permanently

Recently I've been working on an inventory system for a text-based game that uses a global array for the inventory system and a corresponding function to read true or false in said array. The problem I've run into is this, the function I'm using to modify the array
void playerGet(bool items[], int itemNumber) //this function takes an assigned argument of the array indices variable, and changes that array indices from true, to false.
{
items[itemNumber] = true;
}
only modifies the array within the scope of the function its housed in. The array is defined in a .cpp file like this:
void inventoryArray(bool items[]) //This function establishes all the items in the game, the true false statement expresses whether or not the item is in the player's inventory.
{
items[WEAPON_RELIC_RIFLE] = false;
items[WEAPON_SCALPEL] = false;
items[MISC_ACTION_FIGURE] = false;
items[MISC_FIRE_EXTINGUISHER] = false;
items[MISC_LIFE_RAFT] = false;
}
and is then declared in a .h file like this:
void inventoryArray(bool items[]);
the enums used in the array are defined in a header file like this:
enum equipment //This declares a list of enums for each item in the game, consumables, not included.
{
WEAPON_RELIC_RIFLE, // = 0
WEAPON_SCALPEL, // = 1
MISC_ACTION_FIGURE, // = 2
MISC_FIRE_EXTINGUISHER, // = 3
MISC_LIFE_RAFT, // = 4
MAX_EQUIPMENT
};
the function that reads the inventory array is this:
void twoScavengerCombat(bool items[])
{
for (int item = 0; item < MAX_EQUIPMENT; ++item)
{
if (items[item] == true) //if true proceed
{
switch (item)
{
case 0: //if array indices identifier = 0, print relic rifle
cout << "1: Use the Relic Rifle\n";
break;
case 1:
cout << "2: Use the Scalpel\n";
break;
case 2:
break;
case 3:
cout << "3: Use the Fire Extingusher\n";
break;
case 4:
cout << "4: Use the Life Raft\n";
break;
default:
cout << "Error";
break;
}
}
else
cout << "Option Unavailible\n"; //if false print Option Unavailible
}
compiled, with the array and enums headers declared the main file would look like this:
int toolSearch()
{
bool items[MAX_EQUIPMENT];
inventoryArray(items);
playerGet(items, 0);
}
void twoScavengerCombat(bool items[])\\ declared in this file, but since its just above here i left it as a forward declaration to save space
int main()
{
toolSearch();
twoScavengerCombat(items);
return 0;
}
Ideally this would produce the result: Use Relic Rifle
Option Unavailable
Option Unavailable
Option Unavailable
Option Unavailable
but instead it produces 5 Option Unavailable's. What am I missing?
You would want
//bunch of #include<> directives
bool items[MAX_EQUIPMENT];
int toolSearch()
{
inventoryArray();
playerGet( 0);
}
void twoScavengerCombat()
...
// other functions here
int main()
{
toolSearch();
twoScavengerCombat();
return 0;
}
Note that bool items[MAX_EQUIPMENT]; is not defined in a function. It is off on it's own at the top of the file in plain view of anything defined below it. This is what it means to be global. Anyone and everyone can access it, if they know where it is or you tell them where it is with an extern statement. It is created when the program starts (even before main and that can cause some really fun debugging if the initialization logic of the variable is faulty) and dies only when the program does.
Lightness Races in Orbit delves a bit deeper here, but is more concerned with making a global variable extend past a single file
There is no need to pass items into any function because everyone can see items The downside is there is one and only one items so if you have multiple players each with different item lists, you're going to have problems.
You might want to look into std::vector (resizable array) and std::map (which will allow you to look items up by name items["sword"].attackFoe(foe);) and std::set (which makes it really easy to see what a player has (if (items.find("Vorpal Hand Grenade") != items.end()) BlowStuffUp();) rather than having to search through each item every time.

Tennis Score Keeper

I am willing to write a code of a tennis score keeper in C++ that keeps track of the score, but there are 2 problems that occur when I run the program:
I can't quit the loop with while(cin!="q")
The functions wouldn't initialize the variables
#include<iostream>
#include<string>
using namespace std;
int points1=0, points2=0;
int set1=0, set2=0;
int games1=0, games2=0;
string in="";
void score(int point,int set,int game);
int main()
{
do
{
cout<<"POINTS: "<<points1<<":"<<points2<<endl<<"SETS: "<<set1<<":"<<set2<<endl<<"GAMES: "<<games1<<":"<<games2<<endl;
cout<<"Who scored - player 1 or player 2? (p1/p2) : ";
cin>>in;
if(in=="p1")
{
void score(int points1,int set1,int games1);
}
else if(in=="p2")
{
void score(int points2,int set2,int games2);
}
else {cout<<endl<<"Error!"<<endl<<endl;}
}
while(cin!="q");
system("pause");
return 0;
}
void score(int& point,int& set,int& game){
if(set<5)
{
switch(point)
{
case '30':
point=point+10;
case '40':
set++;
point=0;
default:
point=point+15;
}
}
else game++;
}
while(cin!="q");
should be
while(in!="q");
In your function, your switch is on an integer value, so your cases should use an integer value as well:
case '30':
should be
case 30:
The others as well.
This is a function prototype:
void score(int points1,int set1,int games1);
This is a function call:
score(points1,set1,games1);
Make sure you have function calls where you want to execute the function. You have a lot of prototypes where they don't belong.
Some Tennis tips: you need to be two points ahead to win a set, two sets ahead to win a match. You may want to take that into account in your functions. Points and sets of a single player will not be enough to decide who won a set or game.
Edit:
In addition, if you want variables you pass to a function to change outside of this function, you need to pass them by reference.
void score(int& points1, int& set1, int& games1);
Note the ampersands.
Passing parameters to a function will make a [b]copy[/b] of the parameters. This is refered to as pass-by-value, because the value is passed. You can pass-by-reference, which means you don't create a copy but instead pass the location of the actual variable. Changes to it will then be reflected back to your main program.
You want an infinite while loop with a break, also get rid of type defs in function calls -- something like:
while (true) {
cin >> in;
if (in == "p1") {
score(points1, set1, games1);
}
else if (in == "p2") {
score(points2, set2, games2);
}
else if (in == "q") {
break;
} else {
cout << endl << "Error!" << endl << endl;
}
}
The following line is wrong: while(cin!="q");
Instead of cin you need to use in!="q"
The second issue is because you're calling the function in the wrong way.
When you call a function, you just write its name and pass the specified arguments, you don't need to write the function return type when calling it. Also you don't need to specify the types of the arguments you're passing. Your function call should be :
score(points2, set2, games2)
And finally you're switching on an integer, so your cases should check for integers.