Prioritize stats during chargen

This commit is contained in:
kinther 2025-12-29 07:16:47 -08:00
parent 0e2e80fd09
commit 5d4d724e73
4 changed files with 237 additions and 4 deletions

View file

@ -43,12 +43,12 @@ Features in Miranthas MUD Alpha release:
* PC's now use a short description for identification instead of name
* Backgrounds are now available for PC's and NPC's
* Account system for tracking players/characters over long periods of time
* Race/species selection and stat ranges (elves have higher dex, dwarves have higher str, etc)
* Prioritized stats during character generation
Features to be implemented in the next few releases:
* Race/species selection and stat ranges (elves have higher dex, dwarves have higher str, etc)
* Subclass selection to personalize character further
* Prioritized stats during character generation
* Combat is slowed down so it isn't over in < 15 seconds (unless you're far outmatched)
* Mounts added to help with long trips
* Wagons added to help with caravans

View file

@ -139,10 +139,103 @@ bool has_save_proficiency(int class_num, int ability) {
}
}
static void set_real_ability(struct char_data *ch, int ability, int value)
{
switch (ability) {
case ABIL_STR: ch->real_abils.str = value; break;
case ABIL_DEX: ch->real_abils.dex = value; break;
case ABIL_CON: ch->real_abils.con = value; break;
case ABIL_INT: ch->real_abils.intel = value; break;
case ABIL_WIS: ch->real_abils.wis = value; break;
case ABIL_CHA: ch->real_abils.cha = value; break;
}
}
static void roll_real_abils_preference(struct char_data *ch)
{
int i, j, temp;
int rolls[NUM_ABILITIES];
int sorted[NUM_ABILITIES];
bool used[NUM_ABILITIES] = { FALSE };
bool assigned[NUM_ABILITIES] = { FALSE };
static const int default_order[NUM_ABILITIES] = {
ABIL_STR, ABIL_DEX, ABIL_CON, ABIL_INT, ABIL_WIS, ABIL_CHA
};
for (i = 0; i < NUM_ABILITIES; i++) {
int die[4];
for (j = 0; j < 4; j++)
die[j] = rand_number(1, 6);
temp = die[0] + die[1] + die[2] + die[3] -
MIN(die[0], MIN(die[1], MIN(die[2], die[3])));
rolls[i] = temp;
sorted[i] = temp;
}
for (i = 0; i < NUM_ABILITIES - 1; i++) {
for (j = i + 1; j < NUM_ABILITIES; j++) {
if (sorted[j] > sorted[i]) {
temp = sorted[i];
sorted[i] = sorted[j];
sorted[j] = temp;
}
}
}
int pref_count = ch->stat_pref_count;
if (pref_count > NUM_ABILITIES)
pref_count = NUM_ABILITIES;
for (i = 0; i < pref_count; i++) {
int ability = ch->stat_pref_order[i];
if (ability < 0 || ability >= NUM_ABILITIES)
continue;
set_real_ability(ch, ability, sorted[i]);
assigned[ability] = TRUE;
for (j = 0; j < NUM_ABILITIES; j++) {
if (!used[j] && rolls[j] == sorted[i]) {
used[j] = TRUE;
break;
}
}
}
int fifo_idx = 0;
for (i = 0; i < NUM_ABILITIES; i++) {
int ability = default_order[i];
if (assigned[ability])
continue;
while (fifo_idx < NUM_ABILITIES && used[fifo_idx])
fifo_idx++;
if (fifo_idx >= NUM_ABILITIES)
break;
set_real_ability(ch, ability, rolls[fifo_idx]);
used[fifo_idx] = TRUE;
assigned[ability] = TRUE;
fifo_idx++;
}
if (HAS_VALID_SPECIES(ch))
apply_species_bonuses(ch);
else
ch->aff_abils = ch->real_abils;
ch->stat_pref_use = FALSE;
}
/* Roll the 6 stats for a character... each stat is made of the sum of the best
* 3 out of 4 rolls of a 6-sided die. Each class then decides which priority
* will be given for the best to worst stats. */
void roll_real_abils(struct char_data *ch)
static void roll_real_abils_classic(struct char_data *ch)
{
int i, j, k, temp;
ubyte table[6];
@ -238,6 +331,14 @@ void roll_real_abils(struct char_data *ch)
ch->aff_abils = ch->real_abils;
}
void roll_real_abils(struct char_data *ch)
{
if (ch && ch->stat_pref_use)
roll_real_abils_preference(ch);
else
roll_real_abils_classic(ch);
}
/* Per-class skill caps */
#define DEFAULT_CLASS_SKILL_MAX 90

View file

@ -49,6 +49,10 @@ static bool perform_new_char_dupe_check(struct descriptor_data *d);
/* sort_commands utility */
static int sort_commands_helper(const void *a, const void *b);
static void show_species_menu(struct descriptor_data *d);
static bool is_creation_state(int state);
static void show_stat_pref_prompt(struct descriptor_data *d);
static int ability_from_pref_arg(const char *arg);
static bool parse_stat_preference(char *input, ubyte *order, ubyte *count);
/* globals defined here, used here and elsewhere */
int *cmd_sort_info = NULL;
@ -1270,7 +1274,7 @@ static bool perform_new_char_dupe_check(struct descriptor_data *d)
/* Do the player names match? */
if (!strcmp(GET_NAME(k->character), GET_NAME(d->character))) {
/* Check the other character is still in creation? */
if ((STATE(k) > CON_PLAYING) && (STATE(k) < CON_QCLASS)) {
if (is_creation_state(STATE(k))) {
/* Boot the older one */
k->character->desc = NULL;
k->character = NULL;
@ -1317,6 +1321,107 @@ static void show_species_menu(struct descriptor_data *d)
write_to_output(d, "Species: ");
}
static bool is_creation_state(int state)
{
switch (state) {
case CON_GET_NAME:
case CON_NAME_CNFRM:
case CON_PASSWORD:
case CON_NEWPASSWD:
case CON_CNFPASSWD:
case CON_QSEX:
case CON_QSPECIES:
case CON_QCLASS:
case CON_QSTAT_PREF:
case CON_QSHORTDESC:
case CON_PLR_DESC:
case CON_PLR_BACKGROUND:
return TRUE;
default:
return FALSE;
}
}
static void show_stat_pref_prompt(struct descriptor_data *d)
{
write_to_output(d,
"\r\nEnter your stat preference, with the first stat being your preferred highest,\r\n"
"followed by the others in descending order.\r\n"
"If you list fewer than six, those listed get the highest rolls; the rest are FIFO.\r\n"
"Example: strength dexterity constitution intelligence wisdom charisma\r\n"
" or: str dex con int wis cha\r\n"
"Press Enter to skip (first-in, first-out).\r\n"
"Stat preference: ");
}
static int ability_from_pref_arg(const char *arg)
{
if (!arg || !*arg)
return -1;
if (!str_cmp(arg, "str") || is_abbrev(arg, "strength"))
return ABIL_STR;
if (!str_cmp(arg, "dex") || is_abbrev(arg, "dexterity"))
return ABIL_DEX;
if (!str_cmp(arg, "con") || is_abbrev(arg, "constitution"))
return ABIL_CON;
if (!str_cmp(arg, "int") || is_abbrev(arg, "intelligence"))
return ABIL_INT;
if (!str_cmp(arg, "wis") || is_abbrev(arg, "wisdom"))
return ABIL_WIS;
if (!str_cmp(arg, "cha") || is_abbrev(arg, "charisma"))
return ABIL_CHA;
return -1;
}
static bool parse_stat_preference(char *input, ubyte *order, ubyte *count)
{
char arg[MAX_INPUT_LENGTH];
bool seen[NUM_ABILITIES] = { FALSE };
if (!order || !count)
return FALSE;
*count = 0;
skip_spaces(&input);
if (!*input)
return TRUE;
if (!str_cmp(input, "none") || !str_cmp(input, "no") || !str_cmp(input, "skip"))
return TRUE;
while (*input) {
size_t len;
int ability;
input = one_argument(input, arg);
if (!*arg)
break;
len = strlen(arg);
while (len > 0 && (arg[len - 1] == ',' || arg[len - 1] == '.')) {
arg[len - 1] = '\0';
len--;
}
if (!*arg)
continue;
ability = ability_from_pref_arg(arg);
if (ability < 0 || ability >= NUM_ABILITIES)
return FALSE;
if (seen[ability])
return FALSE;
if (*count >= NUM_ABILITIES)
return FALSE;
order[*count] = (ubyte)ability;
(*count)++;
seen[ability] = TRUE;
}
return TRUE;
}
/* load the player, put them in the right room - used by copyover_recover too */
int enter_player_game (struct descriptor_data *d)
{
@ -1912,6 +2017,27 @@ case CON_QCLASS:
GET_CLASS(d->character) = load_result;
}
show_stat_pref_prompt(d);
STATE(d) = CON_QSTAT_PREF;
return;
case CON_QSTAT_PREF: {
ubyte order[NUM_ABILITIES];
ubyte count = 0;
if (!parse_stat_preference(arg, order, &count)) {
write_to_output(d,
"\r\nInvalid stat list. Please enter a valid order, or press Enter to skip.\r\n");
show_stat_pref_prompt(d);
return;
}
d->character->stat_pref_use = TRUE;
d->character->stat_pref_count = count;
for (int i = 0; i < NUM_ABILITIES; i++) {
d->character->stat_pref_order[i] = (i < count) ? order[i] : 0;
}
/* Create player entry and initialize character now so file exists */
if (d->olc) {
free(d->olc);
@ -1958,6 +2084,7 @@ case CON_QCLASS:
STATE(d) = CON_QSHORTDESC;
return;
}
case CON_QSHORTDESC: {
skip_spaces(&arg);

View file

@ -347,6 +347,7 @@
#define CON_QSEX 7 /**< Choose character sex */
#define CON_QSPECIES 8 /**< Choose character species */
#define CON_QCLASS 9 /**< Choose character class */
#define CON_QSTAT_PREF 44 /**< Choose character stat preference order */
#define CON_QSHORTDESC 10 /**< Enter a new character short description prompt */
#define CON_RMOTD 11 /**< Reading the message of the day */
#define CON_MENU 12 /**< At the main menu */
@ -1162,6 +1163,10 @@ struct char_data
struct group_data *group; /**< Character's Group */
long pref; /**< unique session id */
bool stat_pref_use; /**< Use stat preference ordering when rolling abilities */
ubyte stat_pref_count; /**< Number of preferred stats entered */
ubyte stat_pref_order[NUM_ABILITIES]; /**< Ability order preferences */
struct list_data * events;
};