Python or C++

if you want to become a programmer professionally, I've always been told to start with C, then C++. C will teach you everything you need.

Sorry, but this is, imo, terrible advice. C is the last place a new programmer should begin unless they're very specifically going into an area that is heavily dominated by C. It's lacking so many modern features you'll learn very little about programming today. And there are so many little gotchas and difficulties. Do you really want someone in this day and age to start off their programming journey learning about memory allocation when no modern language even requires that sort of thing nowadays. I'm pretty sure OP doesn't want to be a kernel writer or write low-level device drivers!


C++ can be a bit daunting at the beginning, pointers and references throw people off if not taught properly and simply. Classes can be confusing, the real problems are when your code base gets large, it's difficult keeping track of what your doing. Remember though these are languages and can really do little by themselves, you can only really do basic console calculations or file I/O. To do any GUI stuff you need to either use the win32 API or some third party toolkit such as Qt. As for games you would need to either learn openGl or DirectX which is when things get proper tricky.

Classes are the last confusing thing in programming there is! Whatever OP chooses, it MUST be an Object Orientated language or what they'll learn is so at odds with real world programming that trying to transition will confuse the Hell out of them.

Most people nowadays use engine such as unity or unreal.

That's specific to games programming. And even then you still need a solid grounding in the language.

In the old days most things revolved around efficiency. People would use say c++ with inline ASM for max efficiency. Nowadays this isn't such a problem as pcs are powerful and lots of ram.

It's not so much that. Modern compilers are very good at optimizing code and handling this stuff automatically. They days of someone being able to hand-craft some low-level code and do a better job than the compiler are mostly gone. Especially for C++ or C#. Even PHP's "compiler" is pretty good as of PHP 7.
 
Sorry, but this is, imo, terrible advice. C is the last place a new programmer should begin unless they're very specifically going into an area that is heavily dominated by C. It's lacking so many modern features you'll learn very little about programming today. And there are so many little gotchas and difficulties. Do you really want someone in this day and age to start off their programming journey learning about memory allocation when no modern language even requires that sort of thing nowadays. I'm pretty sure OP doesn't want to be a kernel writer or write low-level device drivers!

The reasoning being that C being such low level it gives you an excellent understanding of how languages interact with hardware and it gives you a much stronger foundation in other languages. Those people that have suggested this to me over the years are people who quite frankly blow most dev's out of the water, one developed the picking software used by a world leading supply company. Comparing those the devs I currently work with, I could sack ten of them and their comp science c# and asp knowledge and replace with a single one of the devs I do know.

You're correct that modern languages don't need to worry about memory allocation or even setting the data type of a variable, that doesn't mean having a good understanding of it all is a bad thing to work from.

As I did say though, which you seemed to miss, if the op is looking to dabble and 'have a go' they would be better off with python. It's an easier language to get into and with less concerns regarding memory and data types.
 
The reasoning being that C being such low level it gives you an excellent understanding of how languages interact with hardware and it gives you a much stronger foundation in other languages.

The first part is correct. C will give you a fair understanding of how computers work at the low level. (ish). No to the second part because that understanding has little to do with most other languages. At least any current ones. Pick popular languages today:
  • Python
  • PHP
  • C#
  • Java
  • Javascript (Gods help us!)
  • C++
  • Ruby
That's off the top of my head. Only one of those will at all benefit from becoming familiar with the low-level functionality of C and that's C++ which is a superset of C and even there you might well avoid such matters because the tools have been added to C++ to do so, precisely because such approaches are antiquated. I well believe you that you work with some high-quality devs. I hope I am one myself, however. I used to be a C and then C++ programmer and have some pretty significant projects behind me as well. I suspect that those programmers who recommend C++ are, however good they may be, dyed in the wool greybeard types who have a very long history with it. I'm sticking to my guns on this one - Object Orientation has more to do with modern programming than memory allocation and other close to the metal language features. Like I say, I was a C++ programmer myself for a good long while and I flatter myself pretty good. But if we threw your advanced devs into working with modern languages and frameworks, they'd probably flounder a little and spend half their time grumbling about "inefficiency" and "what's this decorator pattern nonsense? It's just a wrapper class - I don't need that!" When they'd finished discarding half the features and complaining about the rest, they'd build a system that wasn't any faster than if they'd done it right (and possibly slower) because modern compilers blow humans out of the water when it comes to all those low-level tricks C and C++ programmers are so proud of having spent years learning. Where they would probably do fine is with higher level design but you don't learn that any less effectively in Python than you do with C++.

You're correct that modern languages don't need to worry about memory allocation or even setting the data type of a variable, that doesn't mean having a good understanding of it all is a bad thing to work from.

Understanding is seldom a bad thing. But I'm all about the opportunity cost. Programming is a HUGE area. Time spent figuring out how malloc works or what a buffer overrun is, is time not spent understanding when to use a Protected method rather than a Private one or what's a good way to handle Exceptions. Simply put, modern languages don't require understanding of memory management, all modern languages require understanding object orientation and modern design patterns (well, unless we're talking Functional languages which neither of us are going to be recommending to the OP! ;) ). Hell, even Javascript requires it these days as you get into transpiling languages like TypeScript and frameworks like Angular or Vue.

As I did say though, which you seemed to miss, if the op is looking to dabble and 'have a go' they would be better off with python. It's an easier language to get into and with less concerns regarding memory and data types.

Well, I'm not taking issue with your ultimate recommendation or even if you were to recommend C++ (though I'd suggest C# instead). I'm chiefly objecting to the suggetion C would be a beneficial introduction to programming. Respectfully (because I find myself agreeing with your posts on OCUK more often than not), I'd suggest you've picked up a bit of programming elitism from hoary old C++ vikings. They were impressive in their time. In their way, far more impressive than many modern programmers. But those days are gone and OP should be concerned with easy list operations, not which of twenty different linked list libraries and algorithms they should be choosing between.

IMHO. :)
 
Sorry, but this is, imo, terrible advice. C is the last place a new programmer should begin unless they're very specifically going into an area that is heavily dominated by C. It's lacking so many modern features you'll learn very little about programming today. And there are so many little gotchas and difficulties. Do you really want someone in this day and age to start off their programming journey learning about memory allocation when no modern language even requires that sort of thing nowadays. I'm pretty sure OP doesn't want to be a kernel writer or write low-level device drivers!




Classes are the last confusing thing in programming there is! Whatever OP chooses, it MUST be an Object Orientated language or what they'll learn is so at odds with real world programming that trying to transition will confuse the Hell out of them.



That's specific to games programming. And even then you still need a solid grounding in the language.



It's not so much that. Modern compilers are very good at optimizing code and handling this stuff automatically. They days of someone being able to hand-craft some low-level code and do a better job than the compiler are mostly gone. Especially for C++ or C#. Even PHP's "compiler" is pretty good as of PHP 7.

I assume you meant least and not last, so for a beginner you say multiple inheritance, polymorphism, encapsulation etc are the least difficult especially when working with larger and larger code bases?

I should have also said about how efficient compilers are along with faster hardware etc.
 
C is a horrid place to learn really - though if you do master it you will be well setup for almost any language as you will have a good grasp of what is going on underneath many stock commands, etc. of other languages. I think it puts off more people than it helps though - I don't even recommend C++ as a starting point in this respect.

I'm doing a bit of Quake 2 modding in my spare time which is very much C and some stuff (granted I don't have a proper grounding in C) is a properly a mind **** if you don't have a solid understanding of the underlying theory - never mind that in my usage here you often have to think in 3D visually at the same time as working out complex program flow, etc. hah.

I'm actually not a big fan of the modern approach to object oriented programming a lot of it is unnecessarily sprawling partly in an attempt to make it more readable and obscure in implementation until you've learnt it - I've not found anything yet I can't do better with linked lists and some function pointers.
 
I assume you meant least and not last

Correct - and a perfect example of why I prefer strongly typed languages ;) No disagreement on the rest, btw! :)

C is a horrid place to learn really - though if you do master it you will be well setup for almost any language as you will have a good grasp of what is going on underneath many stock commands, etc. of other languages. I think it puts off more people than it helps though - I don't even recommend C++ as a starting point in this respect.

I think we mostly agree, but at the risk of repeating my earlier post I'd question how much real value you get from understanding what lies beneath many stock commands. Again, understanding it isn't a bad thing, but which is more useful - knowing you can type list.count('MyValue') and focusing on your program's logic, or knowing how in C you would write such a function and then never actually needing to do so because most modern languages have a way to do it built in?

I'm actually not a big fan of the modern approach to object oriented programming a lot of it is unnecessarily sprawling partly in an attempt to make it more readable and obscure in implementation until you've learnt it - I've not found anything yet I can't do better with linked lists and some function pointers.

I'd be fascinated to know how you managed encapsulation with linked lists and some function pointers. ;)
 
I think we mostly agree, but at the risk of repeating my earlier post I'd question how much real value you get from understanding what lies beneath many stock commands. Again, understanding it isn't a bad thing, but which is more useful - knowing you can type list.count('MyValue') and focusing on your program's logic, or knowing how in C you would write such a function and then never actually needing to do so because most modern languages have a way to do it built in?

Performance and/or avoiding limitations or hardware bugs, etc. or, though potentially risky, making use of the way something works internally in an unintended way to pack additional data/flags, etc.

I'd be fascinated to know how you managed encapsulation with linked lists and some function pointers. ;)

Obviously there is the use of structures, etc. in there was simplifying but I don't even think in the concept of things like encapsulation, interfaces, lambdas and closures, etc. in the way I program.

EDIT: Not sure if this will make any sense as it is a mixture of excerpts, with mismatching definitions and a lot of missing code due to the complexity of it and nothing that shows how it all comes together, of an RPGlite implementation I did for a Quake 2 mod - using plain C with some limitations - the sample function just shows iterating through the items available in a specific upgrade group (I'm aware there are a couple of bugs in the function):

Code:
#define U_MEMBER 0
#define U_START 1
#define U_END 2

#define GROUP_EQUIP 0
#define GROUP_POWER 1
#define GROUP_PERK 2

#define   UF_DEFAULT 0
#define   UF_HIDDEN 1

typedef enum
{
   AT_MASK,
   AT_BOOTS,
   AT_GLOVES,
   AT_POWER1,
   AT_POWER2,
   AT_MAX
} attribs_t;


typedef enum
{
   MASK_NONE,
   MASK_TEST,
   MASK_END,
   BOOTS_NONE,
   BOOTS_TEST,
   BOOTS_END,
   UP_MAX
} upident_t

typedef struct stats_s
{
   float       health;
   float       damage;
   float       jump;
   float       tech;
   float       unused1;
   float       unused2;
   float       unused3;
} stats_t;

typedef struct slots_s
{
   char    *title;
   char    *backimage;
   int    atnum;
   int   group;
} slots_t;

extern slots_t slotlist[];

typedef struct upgrades_s
{
   int       unum; // upgrade ident
   char       *desc; // desciption
   char       *icon;
   int       utype;
   stats_t       *stat;
   int       flags; // disabled, etc.
   int       timer; // delay until can be reused - tech reduces this
   void       (*use)(struct edict_s *ent);
} upgrades_t;

extern upgrades_t upgradeslist[];


void Cmd_UpNext_f(edict_t *ent)
{
   int   i, c;
   upgrades_t *up;

   if (ent->client->showattrib == false)
       return;

   if ((ent->client->attribSel == AT_POWER1) && (level.time < ent->client->power1end))
   {
       gi.sound(ent, CHAN_VOICE, gi.soundindex("misc/menu3.wav"), 1, ATTN_STATIC, 0);
       return;
   }

   if ((ent->client->attribSel == AT_POWER2) && (level.time < ent->client->power2end))
   {
       gi.sound(ent, CHAN_VOICE, gi.soundindex("misc/menu3.wav"), 1, ATTN_STATIC, 0);
       return;
   }

   if ((ent->client->attribSel == AT_SPECIAL) && (level.time < ent->client->specialend))
   {
       gi.sound(ent, CHAN_VOICE, gi.soundindex("misc/menu3.wav"), 1, ATTN_STATIC, 0);
       return;
   }

   if (ent->client->turret)
   {
       gi.sound(ent, CHAN_VOICE, gi.soundindex("misc/menu3.wav"), 1, ATTN_STATIC, 0);
       return;
   }

   i = ent->client->pers.upgrades[ent->client->attribSel] + 1;


   up = upgradeslist;

   up += i;

   // Never make non U_MEMBER entries hidden

   c = 1;

   while (1)
   {
       if (!(up->flags & UF_HIDDEN))
           break;
       up++;
       c++;
   }

   if (up->utype == U_MEMBER)
   {
       ent->client->pers.upgrades[ent->client->attribSel]+=c;
       gi.sound(ent, CHAN_VOICE, gi.soundindex("misc/menu2.wav"), 1, ATTN_STATIC, 0);
       calcUpgrades(ent);
       return;
   }

   gi.sound(ent, CHAN_VOICE, gi.soundindex("misc/menu3.wav"), 1, ATTN_STATIC, 0);
}

slots_t slotlist[] =
{
   {
       "Mask",
       "tag1",
       AT_MASK,
       GROUP_EQUIP,
   },
   {
       "Boots",
       "tag1",
       AT_BOOTS,
       GROUP_EQUIP,
   },
   { NULL }
};

stats_t   stat_blank = {
/*resists, health, regen*/       0, 0, 0,
/*health on kill, health per hit*/   0, 0,
/*global damage bonus*/           0,
/*MG,combat shotgun,SS,grenade,GL*/   0, 0, 0, 0, 0,
/*RL,chaingun,HB,RG,turret*/       0, 0, 0, 0, 0,
/*air, gravity*/           0, 0,
/*tech*/               0,
/*unused*/               0, 0, 0
};


upgrades_t   upgradeslist[] =
{
   {
       MASK_NONE,       // upgrade id
       "No Mask",       // ui name
       "i_fixme",       // ui icon
       U_START,       // list
       &stat_blank,       // stats array
       0,           // flags i.e. DF_HIDDEN
       0,           // cooldown delay if used
       NULL,           // function if used
   },

   {
       MASK_TEST,
       "Test mask",
       "p_rebreather",
       U_MEMBER,
       &stat_mask_recon,
       0,
       0,
       NULL,
   },

   {
       MASK_END, // dummy entry to mark the end of this group in the list
       "No mask",
       "i_fixme",
       U_END,
       &stat_blank,
       0,
       0,
       NULL,
   },
...
}

To be frank though I only know enough to be dangerous in C :s and self taught.

EDIT: Also aware of the amount of pollution with all those typedefs (and that is nothing compared to the complete code) but it is necessary with the engine.
 
Last edited:
Go (also called Golang) is worth looking at too. It's a relatively newish language and considered a systems language. But it's slightly higher level than C++.
 
Performance and/or avoiding limitations or hardware bugs, etc. or, though potentially risky, making use of the way something works internally in an unintended way to pack additional data/flags, etc.

See that - that right there! ^^^^ Bad programmer! BAD! This is the sort of thing beardy Guru types who've been coding in C/C++ for forty years do and which lead to unfathomable bugs six years later that wipe hundreds of thousands of pounds off your share value and nobody can fix because the original coder has left and nobody can make head or tail of their code. And then I get called in to try and untangle their mess and have to spend days figuring out what they meant to do and how to fix it, when all I want to do is burn the entire code base to the ground and rewrite it from scratch.

*breathes*

Sorry - I actually no longer do that for a living for a long time now, but the memories... Oh, the things I have seen. The nesting - the endless nesting!

But it's very much to the point I was making. We're no longer using punch cards or 386 processors. When I was at university we still studied YACC for Pity's sake. Back then there was worth in these things. And it's still cool to KNOW them. But modern compilers mean there are minimal gains to be had. More to the point, we're no longer in an era where the software industry is a handful of very smart obsessives who tended their code bases like bonsai trees and random failure was acceptable and cause for hilarious jokes about out by one errors. The industry is mature now. People come and go, they hand off projects, they work in teams and sometimes with people they've never met in other countries. At the same time that modern compilers have rendered the gains minimal, modern industry has magnified the negatives of "clever" hacks a hundred-fold. Unpredictability, unmaintainability, unclear intent. No 0.04% increase in performance is worth these in modern software development.

Now to be clear, it's still very possible to write badly performing code. My point is that this is badly performing because of higher level design considerations that would apply in any language, not because of low-level tricks. If I write something that makes hundred calls to the database rather than one, that's bad design in any language. But picking the most performant list implementation is (mostly) no longer something I should worry about. If I use a List in C#, it is in almost all circumstances as performant as if I use an Array. I'm not saying there's no value in knowing how things work underneath - you do still find ways to code better if you understand what's happening underneath. But I'm saying other knowledge is much more important to learn.

Obviously there is the use of structures, etc. in there was simplifying but I don't even think in the concept of things like encapsulation, interfaces, lambdas and closures, etc. in the way I program.

Structs are not the same as classes. I mean, they're more or less classes where all the properties are public and they have no methods. But there are fundamental differences, for example, they don't provide encapsulation. By encapsulation, I mean access control and information hiding. Anyone can reach in and meddle with the contents of a struct. The point of OO design is that the consequences of doing that don't exist only in the original developer's head. ;)

EDIT: Not sure if this will make any sense as it is a mixture of excerpts, with mismatching definitions and a lot of missing code due to the complexity of it and nothing that shows how it all comes together, of an RPGlite implementation I did for a Quake 2 mod - using plain C with some limitations - the sample function just shows iterating through the items available in a specific upgrade group (I'm aware there are a couple of bugs in the function):

Code:
#define U_MEMBER 0
#define U_START 1
#define U_END 2

#define GROUP_EQUIP 0
#define GROUP_POWER 1
#define GROUP_PERK 2

#define   UF_DEFAULT 0
#define   UF_HIDDEN 1

typedef enum
{
   AT_MASK,
   AT_BOOTS,
   AT_GLOVES,
   AT_POWER1,
   AT_POWER2,
   AT_MAX
} attribs_t;


typedef enum
{
   MASK_NONE,
   MASK_TEST,
   MASK_END,
   BOOTS_NONE,
   BOOTS_TEST,
   BOOTS_END,
   UP_MAX
} upident_t

typedef struct stats_s
{
   float       health;
   float       damage;
   float       jump;
   float       tech;
   float       unused1;
   float       unused2;
   float       unused3;
} stats_t;

typedef struct slots_s
{
   char    *title;
   char    *backimage;
   int    atnum;
   int   group;
} slots_t;

extern slots_t slotlist[];

typedef struct upgrades_s
{
   int       unum; // upgrade ident
   char       *desc; // desciption
   char       *icon;
   int       utype;
   stats_t       *stat;
   int       flags; // disabled, etc.
   int       timer; // delay until can be reused - tech reduces this
   void       (*use)(struct edict_s *ent);
} upgrades_t;

extern upgrades_t upgradeslist[];


void Cmd_UpNext_f(edict_t *ent)
{
   int   i, c;
   upgrades_t *up;

   if (ent->client->showattrib == false)
       return;

   if ((ent->client->attribSel == AT_POWER1) && (level.time < ent->client->power1end))
   {
       gi.sound(ent, CHAN_VOICE, gi.soundindex("misc/menu3.wav"), 1, ATTN_STATIC, 0);
       return;
   }

   if ((ent->client->attribSel == AT_POWER2) && (level.time < ent->client->power2end))
   {
       gi.sound(ent, CHAN_VOICE, gi.soundindex("misc/menu3.wav"), 1, ATTN_STATIC, 0);
       return;
   }

   if ((ent->client->attribSel == AT_SPECIAL) && (level.time < ent->client->specialend))
   {
       gi.sound(ent, CHAN_VOICE, gi.soundindex("misc/menu3.wav"), 1, ATTN_STATIC, 0);
       return;
   }

   if (ent->client->turret)
   {
       gi.sound(ent, CHAN_VOICE, gi.soundindex("misc/menu3.wav"), 1, ATTN_STATIC, 0);
       return;
   }

   i = ent->client->pers.upgrades[ent->client->attribSel] + 1;


   up = upgradeslist;

   up += i;

   // Never make non U_MEMBER entries hidden

   c = 1;

   while (1)
   {
       if (!(up->flags & UF_HIDDEN))
           break;
       up++;
       c++;
   }

   if (up->utype == U_MEMBER)
   {
       ent->client->pers.upgrades[ent->client->attribSel]+=c;
       gi.sound(ent, CHAN_VOICE, gi.soundindex("misc/menu2.wav"), 1, ATTN_STATIC, 0);
       calcUpgrades(ent);
       return;
   }

   gi.sound(ent, CHAN_VOICE, gi.soundindex("misc/menu3.wav"), 1, ATTN_STATIC, 0);
}

slots_t slotlist[] =
{
   {
       "Mask",
       "tag1",
       AT_MASK,
       GROUP_EQUIP,
   },
   {
       "Boots",
       "tag1",
       AT_BOOTS,
       GROUP_EQUIP,
   },
   { NULL }
};

stats_t   stat_blank = {
/*resists, health, regen*/       0, 0, 0,
/*health on kill, health per hit*/   0, 0,
/*global damage bonus*/           0,
/*MG,combat shotgun,SS,grenade,GL*/   0, 0, 0, 0, 0,
/*RL,chaingun,HB,RG,turret*/       0, 0, 0, 0, 0,
/*air, gravity*/           0, 0,
/*tech*/               0,
/*unused*/               0, 0, 0
};


upgrades_t   upgradeslist[] =
{
   {
       MASK_NONE,       // upgrade id
       "No Mask",       // ui name
       "i_fixme",       // ui icon
       U_START,       // list
       &stat_blank,       // stats array
       0,           // flags i.e. DF_HIDDEN
       0,           // cooldown delay if used
       NULL,           // function if used
   },

   {
       MASK_TEST,
       "Test mask",
       "p_rebreather",
       U_MEMBER,
       &stat_mask_recon,
       0,
       0,
       NULL,
   },

   {
       MASK_END, // dummy entry to mark the end of this group in the list
       "No mask",
       "i_fixme",
       U_END,
       &stat_blank,
       0,
       0,
       NULL,
   },
...
}

To be frank though I only know enough to be dangerous in C :s and self taught.

EDIT: Also aware of the amount of pollution with all those typedefs (and that is nothing compared to the complete code) but it is necessary with the engine.

Yep - I can follow it, though it's many been years since I worked in C/C++. Respect for being self-taught! But you see how your code could be incredibly dangerous once it was in a multi-developer environment? Or how hard it would be to maintain? Lets say you had a linked attribute to your health variable above. Maybe it's an associated representation like health_percentage_of_max; maybe it's an action that needs to be triggered when heatlh falls below a certain level like change_healthbar_colour(). Perhaps you want a special type of character whose health behaves differently - maybe they're undead and their health has two different tracks for holy damage and normal damage. I don't know - these are just examples. For the first, you need to make sure that ANY time the health attribute is changed, the health_percentage_of_max gets changed as well. If it's a class where health can only be changed by calling an appropriate method (such as decrementHealth() or setHealth()), you can wrap the change to health_percentage_of_max in the same function call. And due to encapsulation you know those methods can't be bypassed by another programmer (who might just be you in the future) saying "Oh, when this happens I'll need to reduce the health value by 100" and forgetting to update anything else. Or to take the second example where you want to trigger a specific action - again, you could add an event to the global queue when health falls below a certain value in the same way. That means anytime their health falls, that health bar on screen turns read, or they die or whatever. There are no exploits where someone forgot to do the value check and the character becomes immortal if they lose their last health point from a self-inflicted wound or a certain spell or whathaveyou. And in the third case, you can implement this by making some special sub-class of the Creature class called Undead that inherits all the normal functionality but overrides the "health" methods to get its special functionality. That way there's both no code duplication and no weird pointer to a method that gets orphaned when someone decides they need to add, remove or change a method on the more generic creature and forgets about the undead type.

Just to illustrate. OO has real value which is why modern (non-Functional) languages, including C++, all follow it.

Gah - I'm done! This isn't meant to read as a rant. It's an interesting subject and I enjoy discussing it. :)
 
Yep - I can follow it, though it's many been years since I worked in C/C++. But you see how your code could be incredibly dangerous once it was in a multi-developer environment? Or how hard it would be to maintain? Lets say you had a linked attribute to your health variable above. Maybe it's an associated representation like health_percentage_of_max; maybe it's an action that needs to be triggered when heatlh falls below a certain level like change_healthbar_colour(). Perhaps you want a special type of character whose health behaves differently - maybe they're undead and their health has two different tracks for holy damage and normal damage. I don't know - these are just examples. For the first, you need to make sure that ANY time the health attribute is changed, the health_percentage_of_max gets changed as well. If it's a class where health can only be changed by calling an appropriate method (such as decrementHealth() or setHealth()), you can wrap the change to health_percentage_of_max in the same function call. And due to encapsulation you know those methods can't be bypassed by another programmer (who might just be you in the future) saying "Oh, when this happens I'll need to reduce the health value by 100" and forgetting to update anything else. Or to take the second example where you want to trigger a specific action - again, you could add an event to the global queue when health falls below a certain value in the same way. That means anytime their health falls, that health bar on screen turns read, or they die or whatever. There are no exploits where someone forgot to do the value check and the character becomes immortal if they lose their last health point from a self-inflicted wound or a certain spell or whathaveyou. And in the third case, you can implement this by making some special sub-class of the Creature class called Undead that inherits all the normal functionality but overrides the "health" methods to get its special functionality. That way there's both no code duplication and no weird pointer to a method that gets orphaned when someone decides they need to add, remove or change a method on the more generic creature.

Just to illustrate. OO has real value which is why modern (non-Functional) languages, including C++, all follow it.

Gah - I'm done! This isn't meant to read as a rant. It's an interesting subject and I enjoy discussing it. :)

Believe me a lot of what you said has gone through my mind 100x while working on this - though I don't envision anyone else working on it atleast at any stage before it is fully rounded out.
 
Believe me a lot of what you said has gone through my mind 100x while working on this - though I don't envision anyone else working on it atleast at any stage before it is fully rounded out.

If I had a penny for everytime I've written a piece of code with the expectation that either nobody else would ever see it or that it would be temporary and replaced soon, I'd have, well, probably around a quid. But that's a LOT more times than I'd like!

You never know. ;)
 
I was initially self taught years ago (in the 1980's). I have programmed professionally in several roles since, although not for at least ten years now. I learned a huge amount from working with others. One of the lessons was to always use standard approaches. What I mean by that is to avoid 'clever' code wherever possible. Always try to write simple code even if it means a few more lines of typing. Whenever I've written something clever I can't make head nor tail of it a few months later.
 
I was initially self taught years ago (in the 1980's). I have programmed professionally in several roles since, although not for at least ten years now. I learned a huge amount from working with others. One of the lessons was to always use standard approaches. What I mean by that is to avoid 'clever' code wherever possible. Always try to write simple code even if it means a few more lines of typing. Whenever I've written something clever I can't make head nor tail of it a few months later.

I had an interesting experience with that one - amongst other things using bitwise encoding in some slightly outside the box, but not really non-standard, way in a database system I had some part in developing - bare in mind this was back when floppy discs were still in general use and you'd still see the odd 486 in use - my approach resulted in an 11x storage space reduction and 7x speed up in queries over the very by the textbook implementation the previous guy did, which was a big boost to productivity but after I left no one else who worked on it could grasp the concepts and it caused quite a bit of trouble - but I had little sympathy as they managed to lose the (comprehensive) documentation I provided when I left (and the recovery disc I'd left).

Lets say you had a linked attribute to your health variable above. Maybe it's an associated representation like health_percentage_of_max; maybe it's an action that needs to be triggered when heatlh falls below a certain level like change_healthbar_colour(). Perhaps you want a special type of character whose health behaves differently - maybe they're undead and their health has two different tracks for holy damage and normal damage. I don't know - these are just examples. For the first, you need to make sure that ANY time the health attribute is changed, the health_percentage_of_max gets changed as well. If it's a class where health can only be changed by calling an appropriate method (such as decrementHealth() or setHealth()), you can wrap the change to health_percentage_of_max in the same function call. And due to encapsulation you know those methods can't be bypassed by another programmer (who might just be you in the future) saying "Oh, when this happens I'll need to reduce the health value by 100" and forgetting to update anything else.

Along those lines - the health itself is part of the original game code - the health variables within the snippet I provided is a bonus to that and is calculated every time any of the stats are adjusted:

Code:
void calcUpgrades(edict_t *ent)
{
   int           i;
   upgrades_t       *up;
   int           bonus;

   ent->client->pers.stat.resist = 0;
   ent->client->pers.stat.health = 0;
   ent->client->pers.stat.regen = 0;
   ent->client->pers.stat.hok = 0;
   ent->client->pers.stat.hod = 0;
   ent->client->pers.stat.dmg = 0;
   ent->client->pers.stat.mg = 0;
   ent->client->pers.stat.sg = 0;
   ent->client->pers.stat.ss = 0;
   ent->client->pers.stat.gren = 0;
   ent->client->pers.stat.gl = 0;
   ent->client->pers.stat.rl = 0;
   ent->client->pers.stat.cg = 0;
   ent->client->pers.stat.hb = 0;
   ent->client->pers.stat.rg = 0;
   ent->client->pers.stat.tr = 0;
   ent->client->pers.stat.air = 0;
   ent->client->pers.stat.jump = 0;
   ent->client->pers.stat.tech = 0;
   ent->client->pers.stat.unused1 = 0;
   ent->client->pers.stat.unused2 = 0;
   ent->client->pers.stat.unused3 = 0;

   for (i = 0; i < AT_MAX; i++)
   {
       up = upgradeslist;
       up += ent->client->pers.upgrades[i];
       ent->client->pers.stat.resist += up->stat->resist;
       ent->client->pers.stat.health += up->stat->health;
       ent->client->pers.stat.regen += up->stat->regen;
       ent->client->pers.stat.hok += up->stat->hok;
       ent->client->pers.stat.hod += up->stat->hod;
       ent->client->pers.stat.dmg += up->stat->dmg;
       ent->client->pers.stat.mg += up->stat->mg;
       ent->client->pers.stat.sg += up->stat->sg;
       ent->client->pers.stat.ss += up->stat->ss;
       ent->client->pers.stat.gren += up->stat->gren;
       ent->client->pers.stat.gl += up->stat->gl;
       ent->client->pers.stat.rl += up->stat->rl;
       ent->client->pers.stat.cg += up->stat->cg;
       ent->client->pers.stat.hb += up->stat->hb;
       ent->client->pers.stat.rg += up->stat->rg;
       ent->client->pers.stat.tr += up->stat->tr;
       ent->client->pers.stat.air += up->stat->air;
       ent->client->pers.stat.jump += up->stat->jump;
       ent->client->pers.stat.tech += up->stat->tech;
       ent->client->pers.stat.unused1 += up->stat->unused1;
       ent->client->pers.stat.unused2 += up->stat->unused2;
       ent->client->pers.stat.unused3 += up->stat->unused3;
   }

   bonus = 100 * ent->client->pers.stat.health;

   ent->client->pers.max_health = ent->max_health = 100 + bonus;
}

(I was using memset for some of that but had a small issue I've put off sorting to work on other stuff - might move some of it to inline asm later for performance reasons and/or so that I don't have to manually add in any new element each time)

The health attribute itself, as per the original game code implementation, is changed quite ad hoc there are bits of code all over the place that can modify the health and max health amounts :s what you talk about is something that has to be kept in mind with any change like that unfortunately - it is one of the changes with newer game engines where those kind of values can only be modified via the appropriate method but actually IMO that is the product of more bugs than it actually solves as people working on the code become reliant on that/facilitates greater laziness and often then people don't have as indepth knowledge of how the whole product works :( though the opposite as you implied is that it can be easy to forget every instance of something that needs to be changed to support a new feature, etc. and it can stand in the way of progress.
 
Last edited:
My advice would be to start at the beginning with the easier option of Python.

You should be able to learn enough Python to start doing useful things fairly quickly and this will give you a good grounding to then go on and learn other languages which will also be easier with one language already under your belt.

Having learnt Python you will be in a better position to decide what to learn next be that C, C++, Java et. al. or one of the newer languages like Go or Rust depending on the requirements at the time.

Learn to walk first and good luck.
 
Last edited:
c#/vb.net are good to start programming. Here is how you could stop/start services in vb.net:
Code:
    Private Sub StartService(ServiceName As String)
        Try
            Dim t = New TimeSpan(0, 0, 8)
            Using s = New ServiceController(ServiceName)
                If s IsNot Nothing Then
                    s.Start()
                    s.WaitForStatus(ServiceControllerStatus.Running, t)
                End If
            End Using
        Catch ex As Exception
            MsgBox("Error: " + ex.Message)
        End Try
    End Sub

    Private Sub StopService(ServiceName As String)
        Try
            Dim t = New TimeSpan(0, 0, 8)
            Using s = New ServiceController(ServiceName)
                If s IsNot Nothing Then
                    s.Stop()
                    s.WaitForStatus(ServiceControllerStatus.Stopped, t)
                End If
            End Using
        Catch ex As Exception
            MsgBox("Error: " + ex.Message)
        End Try
    End Sub

    Private Function GetServiceStatus(ServiceName As String) As String
        Try
            Using s = New ServiceController(ServiceName)
                If s IsNot Nothing Then
                    Return s.Status().ToString()
                End If
            End Using
        Catch ex As Exception
            'MsgBox("Error: " + ex.Message)
        End Try
        Return "Service Not Found."
    End Function
 
Back
Top Bottom