#define is a little more than simple text substitution, that's what the bit that looks like a function is all about. It allows you to pass data into the text substitution, changing the outputted text. It is a MACRO in that sense. There are already a large number of predefined MACROS that you get from including "windows.h". MAKEINTRESOURCE is an example.
Using #define for known values is not necessarily frowned on. The frowning is based on misuse (like what happened here), as far as I can see. Using const is more of a data hiding technique where a static variable that is unchanging is necessary. The only decent example of that that comes to mind is the lines per page variable in MFC documents that are being printed. The variable is static, but not const in that case, however. It is necessary in that instance because the value must be accessed by Windows, too.
For cases like PI or RADIANS_PER_DEGREE it is best (imo) to use a #define. In that case, it is simply taking the place of a literal value that I could have just as easily typed in directly. If you have a const double radians_per_degree = 0.0174533, and a #define RADIANS_PER_DEGREE 0.0174533, the #define works faster, since it is an immediate value.
Anyway, the difference is ultimately that a #define utilizes compiler resources, whereas const utilizes runtime resources (memory, and time).
Your array is not what you intended at all, I think. It has only one element in the first dimension, which means it can only hold one NPC. There are more things about it, but I think I will just show you something, if you don't mind:
Start off by thinking only about 1 NPC. What do you want to track/control about 1 NPC? You are wanting to create different types of NPCs with some properties, so start off by learning about enumerators. Enumerators are predefined integer values, much like what you were trying with #define, except that you are giving C++ some tools to work with (This pleases the purists that don't like #define, btw.) You also get Intellisense improvement when using enums, and strong type checking loves enumerated types.:
typedef enum NPC_TYPE
{
UNKNOWN_TYPE = -1,
NPC_HUMAN = 0,
NPC_MONSTER = 1
};
Now, there are only 2 legal, and 1 illegal value defined for NPC_TYPE. As you add types, start here by defining them in this enumerator. Okay, now you can use NPC_TYPE like any integer value.
typedef struct
{
NPC_TYPE type;
int objID;
float x, y, z;
float angle_x, angle_y, angle_z;
} NPC, *PNPC;
Now, you can have an array of those, like this:
NPC npcs[100];
npcs[0].type = NPC_MONSTER;
Or, you can generate them dynamically:
PNPC pnpc = new NPC;
pnpc->type = NPC_MONSTER;
There are some substantial differences in those two methods, I suggest you just limit yourself to fixed arrays while you are still defining the structures, and the enumerations.
Once you have them working well, you can use them to create classes that behave in predefined, but different ways.