So, I've only started to learn actual programming about a week ago (only did some basic matlab scripting before) and because I wanted a challenge I decided to start with C++.
To get more familiar with the concept of classes and OOP in general, I started this little project, which is basically some classes that define characters in an RPG and also provides an interactive way of creating those. Well so far, it's really just a prototype, cause it doesn't nearly include all properties I want to assign to a character and the classes have some behaviour which isn't getting used (yet!). But since I'm really new to the whole thing, I thought: better let someone correct any bad practice early on before I get used to it.
The purpose of the program is (read as: will be) to take input from the user and output something (maybe a .txt file; not yet implemented) with all the necessary information about the characters he created. ANYHOW, I plan on also writing a little clone of the pen and paper game as a text-based video game later on and will then probably want to use the same classes/modules. That's why there are some methods in the classes, that the character creator doesn't use, but some type of game surely will/would.
So please correct anything that would stand in the way of the above mentioned purposes and any bad practice I'm using. Be as picky as you want, but please sort your advice in categories like "MUST CHANGE", "strongly recommended" and "recommended/subjective".
Also: I rarely (read as: virtually never) comment my code because I don't know whats worth commenting, and whats self-explanatory. So please recommend me some effective commenting behaviour for a) making myself remember what needs changing/iteration and b) making my code better understandable to others. I will edit my code appropriately if needed for easier reviewing.
So heres what I've got so far, this is what my file structure looks like:
- "RPGCreator_cpp"
- "RPGGhoul_cpp"
- "RPGGhoul_h"
- "RPGAttr_cpp"
- "RPGAttr_h"
- "RPGDesc_cpp"
- "RPGDesc_h"
- "RPGGhoul_cpp"
and the file contents are the following: (link to online IDE)
"RPGCharCreator.cpp"
#include <vector>
#include "RPGGhoul.cpp"
int main() {
std::vector <Ghoul> ghouls;
std::string username = "unknown";
int numghouls;
std::cout << "Whats your (nick-)name?" << std::endl;
std::getline (std::cin, username);
for (;;) {
std::cout << "How many ghouls do you want to create?" << std::endl;
if (std::cin >> numghouls) {std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); break;}
else {std::cin.clear(); std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');}
}
for (int i=1; i<=numghouls; i++) {
std::cout << "\n***Creating Ghoul number " << i << " ***" << std::endl;
ghouls.push_back(Ghoul(username));
}
for (int i=0; i<numghouls; i++) {
std::cout << "\n***Created Ghoul number " << i+1 << " ***" << std::endl;
ghouls[i].desc.dispName();
ghouls[i].desc.dispDesc();
ghouls[i].attr.dispAttr();
}
return 0;
}
"RPGGhoul.h"
#ifndef RPGGHOUL_H
#define RPGGHOUL_H
#include "RPGAttr.cpp"
#include "RPGDesc.cpp"
#include <string>
class Ghoul {
public:
RPGAttr attr{10};
RPGDesc desc{"ghoul"};
Ghoul (std::string);
private:
int d_b_min, d_b_max, i_b_min, i_b_max, c_b_min, c_b_max, e_b_min, e_b_max,
p_b_min, p_b_max, a_b_min, a_b_max, l_b_min, l_b_max, s_b_min, s_b_max;
const int AttrAv;
std::string nameOfCreator;
std::string type;
std::string creature;
};
#endif
"RPGGhoul.cpp"
#ifndef RPGGHOUL_CPP
#define RPGGHOUL_CPP
#include "RPGGhoul.h"
#include "RPGAttr.cpp"
#include "RPGDesc.cpp"
#include <string>
#include <iostream>
Ghoul::Ghoul (std::string noc): d_b_min(5), d_b_max(13), i_b_min(6), i_b_max(14), c_b_min(5), c_b_max(12), e_b_min(7), e_b_max(16),
p_b_min(7), p_b_max(13), a_b_min(7), a_b_max(15), l_b_min(4), l_b_max(12), s_b_min(6), s_b_max(14), AttrAv(10), nameOfCreator(noc) {
std::string input;
std::cout << "==========================================\n" << std::endl;
for (;;) {
std::cout << "Want to create a unique or generic character? (u/g)" << std::endl;
std::getline(std::cin,input);
type = input;
if (type == "u" || type == "g"){break;}
}
if (type == "u"){
std::cout << "------------------------------------------\n" << std::endl;
desc.interactiveName(); std::cout << "------------------------------------------\n" << std::endl;
desc.interactiveDesc(); std::cout << "------------------------------------------\n" << std::endl;
attr.interactiveAttr_b_menu(d_b_min, d_b_max, i_b_min, i_b_max, c_b_min, c_b_max, e_b_min, e_b_max,
p_b_min, p_b_max, a_b_min, a_b_max, l_b_min, l_b_max, s_b_min, s_b_max);
}
}
#endif
"RPGAttr.h"
#ifndef RPGATTR_H
#define RPGATTR_H
#include <string>
class RPGAttr {
public:
RPGAttr (int);
int D (); int I (); int C (); int E ();
int P (); int A (); int L (); int S ();
int TotalAttr_b ();
int TotalAttr_ad ();
int TotalAttr ();
void incdecD (int);
void incdecI (int);
void incdecC (int);
void incdecE (int);
void incdecP (int);
void incdecA (int);
void incdecL (int);
void incdecS (int);
void setAttr_b(std::string,int);
void interactiveAttr_b (std::string, int, int);
void interactiveAttr_b_menu (int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int);
void dispAttr();
void dispMeaning();
private:
int default_av;
int d_b,i_b,c_b,e_b,p_b,a_b,l_b,s_b,
d_ad,i_ad,c_ad,e_ad,p_ad,a_ad,l_ad,s_ad;
int limitAttr (int, int, int);
};
#endif
"RPGAttr.cpp"
#ifndef RPGATTR_CPP
#define RPGATTR_CPP
#include "RPGAttr.h"
#include <iomanip>
#include <iostream>
#include <limits>
RPGAttr::RPGAttr (int av):default_av(11), d_b(av), i_b(av), c_b(av), e_b(av), p_b(av), a_b(av), l_b(av), s_b(av),
d_ad(0), i_ad(0), c_ad(0), e_ad(0), p_ad(0), a_ad(0), l_ad(0), s_ad(0){}
int RPGAttr::D () {return limitAttr(d_b+d_ad,1,20);}
int RPGAttr::I () {return limitAttr(i_b+i_ad,1,20);}
int RPGAttr::C () {return limitAttr(c_b+c_ad,1,20);}
int RPGAttr::E () {return limitAttr(e_b+e_ad,1,20);}
int RPGAttr::P () {return limitAttr(p_b+p_ad,1,20);}
int RPGAttr::A () {return limitAttr(a_b+a_ad,1,20);}
int RPGAttr::L () {return limitAttr(l_b+l_ad,1,20);}
int RPGAttr::S () {return limitAttr(s_b+s_ad,1,20);}
int RPGAttr::TotalAttr_b () {return d_b+i_b+c_b+e_b+p_b+a_b+l_b+s_b;}
int RPGAttr::TotalAttr_ad () {return d_ad+i_ad+c_ad+e_ad+p_ad+a_ad+l_ad+s_ad;}
int RPGAttr::TotalAttr () {return D()+I()+C()+E()+P()+A()+L()+S();}
void RPGAttr::incdecD (int incdec) {d_ad=d_ad+incdec;}
void RPGAttr::incdecI (int incdec) {i_ad=i_ad+incdec;}
void RPGAttr::incdecC (int incdec) {c_ad=c_ad+incdec;}
void RPGAttr::incdecE (int incdec) {e_ad=e_ad+incdec;}
void RPGAttr::incdecP (int incdec) {p_ad=p_ad+incdec;}
void RPGAttr::incdecA (int incdec) {a_ad=a_ad+incdec;}
void RPGAttr::incdecL (int incdec) {l_ad=l_ad+incdec;}
void RPGAttr::incdecS (int incdec) {s_ad=s_ad+incdec;}
void RPGAttr::setAttr_b(std::string At,int val){
if (At== "D") {d_b=limitAttr(val,1,20);} else if (At=="I") {i_b=limitAttr(val,1,20);}
else if (At=="C") {c_b=limitAttr(val,1,20);} else if (At== "E") {e_b=limitAttr(val,1,20);}
else if (At=="P") {p_b=limitAttr(val,1,20);} else if (At=="A") {a_b=limitAttr(val,1,20);}
else if (At== "L") {l_b=val;} else if (At=="S") {s_b=limitAttr(val,1,20);}
}
void RPGAttr::interactiveAttr_b (std::string At, int Atmin, int Atmax) {
if (Atmin>Atmax) {std::cerr << "Error: Invalid arguments for function Attributes::interactiveAttr_b_menu" << std::endl;}
else {
int ans;
for (;;) {
std::cout << "What value should " << At << " be set to (" << Atmin << "-" << Atmax << ")?" << std::endl;
if (std::cin >> ans && (ans>=Atmin && ans <=Atmax)) {
setAttr_b(At, ans);
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); break;}
else { std::cin.clear(); std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); std::cin.sync();}
}
}
}
void RPGAttr::interactiveAttr_b_menu (int dmin, int dmax, int imin, int imax, int cmin, int cmax, int emin, int emax,
int pmin, int pmax, int amin, int amax, int lmin, int lmax, int smin, int smax) {
if (dmin>dmax || imin>imax || cmin>cmax || emin>emax || pmin>pmax || amin>amax || lmin>lmax || smin>smax) {
std::cerr << "Error: Invalid arguments for function Attributes::interactiveAttr_b_menu" << std::endl;}
else{
int Atmin=1;
int Atmax=20;
std::string ans;
int before = TotalAttr();
for (;;){std::cout << "Want to change Attributes? (n for no; help for meaning of attributes; D,I,C,E,P,A,L,S for changing the respective Attribute)" << std::endl;
dispAttr();
std::cout << "Points to distribute (must equal 0 to proceed): " << before-TotalAttr() << std::endl;
std::getline (std::cin, ans);
if (ans=="n" && before-TotalAttr()==0) {break;}
else if (ans == "help") {dispMeaning();}
else if (ans == "D") {Atmin=dmin; Atmax=dmax;} else if (ans == "I") {Atmin=imin; Atmax=imax;}
else if (ans == "C") {Atmin=cmin; Atmax=cmax;} else if (ans == "E") {Atmin=emin; Atmax=emax;}
else if (ans == "P") {Atmin=pmin; Atmax=pmax;} else if (ans == "A") {Atmin=amin; Atmax=amax;}
else if (ans == "L") {Atmin=lmin; Atmax=lmax;} else if (ans == "S") {Atmin=smin; Atmax=smax;}
if ( ans == "D" || ans == "I" || ans == "C" || ans == "E" || ans == "P" || ans == "A" || ans == "L" || ans == "S" ) {
interactiveAttr_b(ans, Atmin, Atmax);
}
}
}
}
void RPGAttr::dispAttr(){
int f=4;
std::cout << std::left << std::setw(f) << "D" <<std::setw(f)<< "I" <<std::setw(f)<< "C" <<std::setw(f)<< "E" ;
std::cout << std::setw(f) << "P" <<std::setw(f)<< "A" <<std::setw(f)<< "L" <<std::setw(f)<< "S" ;
std::cout << std::setw(f+2) << "Tot." << std::endl;
std::cout << std::left << std::setw(f) << D() <<std::setw(f)<< I() <<std::setw(f)<< C() <<std::setw(f)<< E() ;
std::cout << std::setw(f) << P() <<std::setw(f)<< A() <<std::setw(f)<< L() <<std::setw(f)<< S();
std::cout << std::setw(f+2) << TotalAttr() << std::endl;
}
void RPGAttr::dispMeaning(){
int f=15;
std::cout << std::left << std::setw(f) << "D: Dexterity" << std::setw(f) << "I: Intelligence" << std::endl;
std::cout << std::left << std::setw(f) << "C: Charisma" << std::setw(f) << "E: Endurance" << std::endl;
std::cout << std::left << std::setw(f) << "P: Perception" << std::setw(f) << "A: Agility" << std::endl;
std::cout << std::left << std::setw(f) << "L: Luck" << std::setw(f) << "S: Strength" << std::endl;
}
int RPGAttr::limitAttr (int val, int lo, int hi) {
if (val<=hi && val>=lo) {return val;} else if (val>hi) {return hi;} else if (val<lo) {return lo;} else {return 10;}
}
#endif
"RPGDesc.h"
#ifndef RPGDESC_H
#define RPGDESC_H
#include <string>
class RPGDesc {
public:
RPGDesc (std::string);
void setName(std::string,std::string);
void interactiveName();
void dispName();
void setDesc(std::string);
void interactiveDesc ();
void dispDesc();
private:
std::string firstname;
std::string lastname;
std::string shortdesc;
};
#endif
"RPGDesc.cpp"
#ifndef RPGDesc_CPP
#define RPGDesc_CPP
#include "RPGDesc.h"
#include <iostream>
#include <string>
RPGDesc::RPGDesc (std::string creature): firstname("Unnamed"), lastname(creature),
shortdesc("a " + creature + " like any other"){}
void RPGDesc::dispName() {
std::cout << "First name: " << firstname << std::endl;
std::cout << "Last name: " << lastname << std::endl;
}
void RPGDesc::setName(std::string first, std::string last) {firstname = first; lastname = last;}
void RPGDesc::interactiveName (){
std::string answer1;
std::string answer2;
std::string answer3;
for (;;) {
std::cout << "Want to change name? (y/n)" << std::endl;
dispName();
std::getline (std::cin, answer1);
if (answer1=="n") {break;}
else if (answer1 == "y") {
std::cout << "Enter new first name" << std::endl;
std::getline (std::cin, answer2);
std::cout << "Enter new last name" << std::endl;
std::getline (std::cin, answer3);
setName(answer2,answer3);
}
}
}
void RPGDesc::dispDesc() {std::cout << "Current description: " << shortdesc << std::endl;}
void RPGDesc::setDesc(std::string newdesc) {shortdesc = newdesc;}
void RPGDesc::interactiveDesc (){
std::string answer1;
std::string answer2;
bool loop1=1;
while (loop1==1){
std::cout << "Want to change description? (y/n)" << std::endl;
dispDesc();
std::getline (std::cin, answer1);
if (answer1=="n") {loop1=0;}
else if (answer1 == "y") {
std::cout << "Enter new description" << std::endl;
std::getline (std::cin, answer2);
setDesc(answer2);
}
}
}
#endif
EDIT:
The online IDE is acting very differently from my CodeBlocks configuration using GCC...will see if I can find the reason for that...
EDIT2:
Using another online IDE now, which seems to work fine, but doesnt support multiple files, so I will just paste the contents as they would be pasted using the "#includes".