Re: Class Restrictions

From: Daniel A. Koepke (
Date: 09/21/01

On Fri, 21 Sep 2001, Edward wrote:

> ... I went a complex but working way, ...

True.  Sort of.

> its bulky for certain, but it is dynamic...

There are much better ways of achieving the same from the user's point of
view, without being nearly as taxing.  From the user's point of view,
there are ways of doing better.

> [... snip do_list_playerinfo() ...]

General rule: It's always a bad idea to shove more information at the
player than he really needs.  Let him choose his desired race.  Let him
choose his desired class.  If he decides he wants to change his mind, let
him backup and choose again.  Newbies will be deterred by a bunch of stats
and tables that don't make a whole lot of sense to them.  Veterans have
seen the schlock before and know who they want to be.  No one benefits
from having stats flung at them.  You should present the character
creation sequence as being near-linear:

  Choose Race <-> Choose Gender <-> Roll Stats <-> Choose Class

Remember to permit people ways to go _back_ and to explore the system in
greater detail (by providing extension help available from the menu).
But do not throw the detail at them and have them start planning their
character out.  Let them _navigate_ the creation process to learn it.  If
the system is becoming too difficult to navigate and chart a reasonable
path to, you need to go back to the original credo of game design: "I
shall never deceive *myself* about the direction of the game."  Gain some
focus and trim away the fat.  People don't like needlessly expansive games
because that generally means: inconsistent, unbalanced, un-fun.  Ask
yourself if you're mindlessly repeating a formula to follow the direction
of another game.  That's generally just as bad an idea.

>   const char *def_classes[NUM_CLASSES][1];

Why are you doing this?

  char *def_classes[NUM_CLASSES];

if you really wanted to.  But (IMO, of course) you don't.

>   for (i = 0; i < NUM_CLASSES; i++)
>      def_classes[i][0] = str_dup("NONE");

Bluntly: you don't want to do this.  You're leaking memory everywhere in
your function.  str_dup() obviously does not do what you think it does.
The only thing str_dup() does is take a given string and duplicates it in
a new, dynamic string.  This means it _allocates_ memory.  If you don't
free this memory, then it's simply not freed at all during the MUD
process's lifespan.  When exactly the memory is recycled for use by the
system is determined by your platform.  Sane platforms (generally meaning
not Win32) will reclaim the memory when the process dies, but this is not
true of some other systems that never reclaim the memory and you must
reboot the machine before it reappears.  Fortunately, the latter sort have
the useful feature of _crashing_ (the machine) to remind you that you've
consumed enormous amounts of memory and should reboot.

> [... snip ...]

Combine the comparison with the check.  There's no reason to keep them
separate.  Pass d->character as a char_data pointer to your function,
since that's all you're using anyway.  After incorporating these changes
your entire parse_general_class() function becomes:

  int parse_general_class(struct char_data *ch, char *arg)
    int i;

    for (i = 0; i < NUM_CLASSES; i++)
      if (classes[i].allowed_races[(int)GET_RACE(ch)] &&
          classes[i].stats_needed[R_STR] <= GET_STR(ch) &&
          classes[i].stats_needed[R_INT] <= GET_INT(ch) &&
          classes[i].stats_needed[R_WIS] <= GET_WIS(ch) &&
          classes[i].stats_needed[R_DEX] <= GET_DEX(ch) &&
          classes[i].stats_needed[R_CON] <= GET_CON(ch) &&
          classes[i].stats_needed[R_CHA] <= GET_CHA(ch) &&
          is_abbrev(arg, classes[i].name))
        return (i);

     return (CLASS_UNDEFINED);


   | FAQ: |
   | Archives: |

This archive was generated by hypermail 2b30 : 12/06/01 PST