You may want to consider making each character a class instance, instead of having one instance handle all your characters. That's what I meant before. But I've created many classes that handle everything in one instance as you have. There are advantages to doing it each way. One advantage to designing your class to handle one character (instead of all of them) is simplicity. Since you've said your goal is to learn OOP, I would recommend taking that route, as that is more OOP-oriented.
I've noticed that your takeDamage() function can result in your health becoming negative. So when checking for death, you may want to check if that health is <= 0 rather than != 0. But I didn't see any such check in your code.
I also notice you're implementing your function definitions in the same place you're declaring them. For more OOPness, greater simplicity and modularity, I would recommend, doing your declarations (prototypes) in an .h file and your definitions (implementation) in a .cpp file. For example:
character.h
#ifndef CHARACTER_H
#define CHARACTER_H
#include "darkGDK.h"
#include "math.h"
#define MAX_CHARACTERS 100
struct charStruct
{
int id;
int health;
bool alive;
};
class character
{
public:
character(void);
~character();
void createCharacter(int id,int parent, int x, int y, int z);
void takeDamage(int id,float d);
void TakeHit(int id, int damageObject);
void walk(int id,int DestinationObject);
void idle(int id);
float getHealth(int id);
void setHealth(int id,int x);
int getDist(int id,int Other);
void die(int id,int startFrame,int endFrame);
bool Alive(int id);
void attack(int id);
private:
int CharCNT;
charStruct charHolder[MAX_CHARACTERS];
};
#endif
character.cpp
#include "character.h"
character::character(void)
{
CharCNT = 0;
}
character::~character()
{
}
void character::createCharacter(int id,int parent, int x, int y, int z)
{
charHolder[CharCNT].id=id;
charHolder[CharCNT].health = 100; // default Health... use SetHealth(int id, int x);
charHolder[CharCNT].alive = true;
//dbCloneObject(id,parent);
dbMakeObjectBox(id,100,100,100);//temporary primative box for testing
dbPositionObject(id,x,y,z);
CharCNT++;
}
void character::takeDamage(int id,float d)
{
charHolder[id].health-=d;
}
void character::TakeHit(int id, int damageObject)
{
if(dbObjectHit(damageObject,charHolder[id].id))
{
takeDamage(id,dbRND(25));
}
}
void character::walk(int id,int DestinationObject)
{
dbPointObject(charHolder[id].id,dbObjectPositionX(DestinationObject),dbObjectPositionY(charHolder[id].id),dbObjectPositionZ(DestinationObject));
dbMoveObject(charHolder[id].id,2.5);
}
void character::idle(int id)
{
dbTurnObjectLeft(charHolder[id].id,1);
}
float character::getHealth(int id)
{
return charHolder[id].health;
}
void character::setHealth(int id,int x)
{
charHolder[id].health = x;
}
int character::getDist(int id,int Other)
{
return ((pow(dbObjectPositionX(charHolder[id].id)-dbObjectPositionX(Other),2))+(pow(dbObjectPositionZ(charHolder[id].id)-dbObjectPositionZ(Other),2)));
}
void character::die(int id,int startFrame,int endFrame)
{
dbMoveObjectDown(charHolder[id].id,1);
}
bool character::Alive(int id)
{
return charHolder[id].alive;
}
void character::attack(int id)
{
dbRollObjectLeft(charHolder[id].id,2);
}
One of the benefits of doing it this way is you can glance at your .h file for a quick-reference of available functions. And the definitions and declarations really should be separate, as you don't want to confuse the calling of the functions with their implementations, when calling class functions from an outer scope. I believe the term that refers to this principle is encapsulation. It's more modular, far cleaner, and it compiles faster. Microsoft managed code tends to violate this principle (and many others). Their code and style are horrific examples, abominations, absolutely not to be followed. Notice what I moved over to Private in character.h. Any functions that will only be called from within the class and not from outside of it should be moved to Private as well. Also, it's good to keep your naming conventions consistent. Sometimes your function names start with capital letters. Other times they start with lower-case letters.
One other observation - you used constants throughout your code. Generally it's a good idea to define those constants all in one place (for example using #define), then it's easier to remember what you were thinking when you decided on their values, and to change them later if needed. For example, instead of:
takeDamage(id,dbRND(25));
Have something like this:
// At the top of the file
#define DAMAGE_RANDOM_RANGE 25
// In Your Function:
takeDamage(id,dbRND(DAMAGE_RANDOM_RANGE));
Lastly, I second what mr_d said about debugging your program as you go. That's a real key and can reveal much. But with Visual Studio, you don't need to add print statements as you might in BASIC. Just compile for Debug (which is the default) and set breakpoints, and inspect variables when the breakpoints are triggered. For a group of variables you intent to regular inspect, you can add them to a watch list (Add Watch), which will display them all in one window.
Judging what we see is the greatest blinder and self-limiter in the universe.
What we perceive is never reality. It is only a story we tell ourselves based on our current perspective, which has far more to do with our beliefs about ourselves than with anything else.