diff --git a/lib/world/mob/1.mob b/lib/world/mob/1.mob index a3e9a19..eb87cc5 100644 --- a/lib/world/mob/1.mob +++ b/lib/world/mob/1.mob @@ -12,8 +12,8 @@ call his stature bulky, as he has quite a bit of muscle. 1 3d20+40 8 8 1 Str: 16 -Dex: 14 -Con: 14 +SaveStr: 3 +Skill 134 60 E L 3 118 1 L 5 131 1 @@ -40,9 +40,6 @@ her nose. 2122 0 0 0 0 0 0 0 0 E 1 3d20+40 8 8 2 -Str: 14 -Dex: 18 -Con: 14 E L 17 127 1 L 16 117 1 @@ -69,9 +66,6 @@ others. 10 0 0 0 0 0 0 0 0 E 1 3d12+60 8 8 1 -Str: 18 -Int: 12 -Con: 18 E L 14 113 1 L 9 112 1 @@ -90,9 +84,5 @@ appear slightly bloodshot. 10 0 0 0 0 0 0 0 0 E 1 3d8+60 8 8 2 -Str: 12 -Dex: 14 -Con: 14 -Cha: 14 E $ diff --git a/src/db.c b/src/db.c index 7d77e87..7844074 100644 --- a/src/db.c +++ b/src/db.c @@ -1596,6 +1596,28 @@ static void parse_simple_mob(FILE *mob_f, int i, int nr) GET_SAVE(mob_proto + i, j) = 0; } +static void parse_espec(char *buf, int i, int nr) +{ + char *ptr; + + /* Split on ':' if present (e.g., "Str: 16") */ + if ((ptr = strchr(buf, ':')) != NULL) { + *(ptr++) = '\0'; + while (isspace(*ptr)) + ptr++; + } else { + /* No colon: treat the remainder as value start (may be NULL) */ + ptr = NULL; + } + + /* Trim leading spaces from keyword */ + while (isspace(*buf)) + buf++; + + /* Always route to interpret_espec so we only write to real_abils there. */ + interpret_espec(buf, ptr, i, nr); +} + /* interpret_espec is the function that takes espec keywords and values and * assigns the correct value to the mob as appropriate. Adding new e-specs is * straightforward: just add a new CASE() block. */ @@ -1608,40 +1630,41 @@ static void interpret_espec(const char *keyword, const char *value, int i, int n { int num_arg = 0; bool matched = FALSE; + bool touched_ability = FALSE; - /* Defensive check: if there's a value, convert to int */ if (value) num_arg = atoi(value); - /* --- Ability Scores --- */ + /* --- Ability Scores (write REAL, then sync AFF) --- */ CASE("Str") { RANGE(3, 25); mob_proto[i].real_abils.str = num_arg; + touched_ability = TRUE; } - CASE("Dex") { RANGE(3, 25); mob_proto[i].real_abils.dex = num_arg; + touched_ability = TRUE; } - CASE("Con") { RANGE(3, 25); mob_proto[i].real_abils.con = num_arg; + touched_ability = TRUE; } - CASE("Int") { RANGE(3, 25); mob_proto[i].real_abils.intel = num_arg; + touched_ability = TRUE; } - CASE("Wis") { RANGE(3, 25); mob_proto[i].real_abils.wis = num_arg; + touched_ability = TRUE; } - CASE("Cha") { RANGE(3, 25); mob_proto[i].real_abils.cha = num_arg; + touched_ability = TRUE; } /* --- 5e-style Saving Throws --- */ @@ -1649,32 +1672,31 @@ static void interpret_espec(const char *keyword, const char *value, int i, int n RANGE(0, 100); mob_proto[i].char_specials.saved.saving_throws[ABIL_STR] = num_arg; } - CASE("SaveDex") { RANGE(0, 100); mob_proto[i].char_specials.saved.saving_throws[ABIL_DEX] = num_arg; } - CASE("SaveCon") { RANGE(0, 100); mob_proto[i].char_specials.saved.saving_throws[ABIL_CON] = num_arg; } - CASE("SaveInt") { RANGE(0, 100); mob_proto[i].char_specials.saved.saving_throws[ABIL_INT] = num_arg; } - CASE("SaveWis") { RANGE(0, 100); mob_proto[i].char_specials.saved.saving_throws[ABIL_WIS] = num_arg; } - CASE("SaveCha") { RANGE(0, 100); mob_proto[i].char_specials.saved.saving_throws[ABIL_CHA] = num_arg; } + /* If we changed a base ability, keep aff_abils in sync for the prototype. */ + if (touched_ability) + mob_proto[i].aff_abils = mob_proto[i].real_abils; + /* --- Debug + Fallback --- */ if (!matched) { log("DEBUG: Unmatched espec line '%s' value '%s' in mob #%d", @@ -1684,58 +1706,10 @@ static void interpret_espec(const char *keyword, const char *value, int i, int n } } -/* Prevent macro bleed outside this function’s scope */ +/* Prevent macro bleed */ #undef CASE #undef RANGE -static void parse_espec(char *buf, int i, int nr) -{ - char *ptr; - int value; - - /* Split on ':' if present (e.g., "Str: 16") */ - if ((ptr = strchr(buf, ':')) != NULL) { - *(ptr++) = '\0'; - while (isspace(*ptr)) - ptr++; - } - - /* Trim leading spaces from keyword */ - while (isspace(*buf)) - buf++; - - /* --- Handle ability score lines --- */ - if (!str_cmp(buf, "Str") && sscanf(ptr, "%d", &value) == 1) - GET_STR(mob_proto + i) = LIMIT(value, 3, 25); - else if (!str_cmp(buf, "Dex") && sscanf(ptr, "%d", &value) == 1) - GET_DEX(mob_proto + i) = LIMIT(value, 3, 25); - else if (!str_cmp(buf, "Int") && sscanf(ptr, "%d", &value) == 1) - GET_INT(mob_proto + i) = LIMIT(value, 3, 25); - else if (!str_cmp(buf, "Wis") && sscanf(ptr, "%d", &value) == 1) - GET_WIS(mob_proto + i) = LIMIT(value, 3, 25); - else if (!str_cmp(buf, "Con") && sscanf(ptr, "%d", &value) == 1) - GET_CON(mob_proto + i) = LIMIT(value, 3, 25); - else if (!str_cmp(buf, "Cha") && sscanf(ptr, "%d", &value) == 1) - GET_CHA(mob_proto + i) = LIMIT(value, 3, 25); - - /* --- Optional saving throw support (if you use these in files) --- */ - else if (!str_cmp(buf, "SaveStr") && sscanf(ptr, "%d", &value) == 1) - GET_SAVE(mob_proto + i, ABIL_STR) = value; - else if (!str_cmp(buf, "SaveDex") && sscanf(ptr, "%d", &value) == 1) - GET_SAVE(mob_proto + i, ABIL_DEX) = value; - else if (!str_cmp(buf, "SaveCon") && sscanf(ptr, "%d", &value) == 1) - GET_SAVE(mob_proto + i, ABIL_CON) = value; - else if (!str_cmp(buf, "SaveInt") && sscanf(ptr, "%d", &value) == 1) - GET_SAVE(mob_proto + i, ABIL_INT) = value; - else if (!str_cmp(buf, "SaveWis") && sscanf(ptr, "%d", &value) == 1) - GET_SAVE(mob_proto + i, ABIL_WIS) = value; - else if (!str_cmp(buf, "SaveCha") && sscanf(ptr, "%d", &value) == 1) - GET_SAVE(mob_proto + i, ABIL_CHA) = value; - - /* --- Fallback: hand off anything else to the old interpreter --- */ - else - interpret_espec(buf, ptr, i, nr); -} static void parse_enhanced_mob(FILE *mob_f, int i, int nr) { diff --git a/src/medit.c b/src/medit.c index ce9c068..511b740 100644 --- a/src/medit.c +++ b/src/medit.c @@ -230,8 +230,14 @@ static void init_mobile(struct char_data *mob) GET_WEIGHT(mob) = 200; GET_HEIGHT(mob) = 198; - mob->real_abils.str = mob->real_abils.intel = mob->real_abils.wis = 11; - mob->real_abils.dex = mob->real_abils.con = mob->real_abils.cha = 11; + /* Only assign defaults if the individual stat is unset (zero) */ + if (!mob->real_abils.str) mob->real_abils.str = 11; + if (!mob->real_abils.intel) mob->real_abils.intel = 11; + if (!mob->real_abils.wis) mob->real_abils.wis = 11; + if (!mob->real_abils.dex) mob->real_abils.dex = 11; + if (!mob->real_abils.con) mob->real_abils.con = 11; + if (!mob->real_abils.cha) mob->real_abils.cha = 11; + mob->aff_abils = mob->real_abils; for (i = 0; i < NUM_ABILITIES; i++) @@ -1061,27 +1067,39 @@ void medit_parse(struct descriptor_data *d, char *arg) case MEDIT_SAVE_STR: GET_SAVE(OLC_MOB(d), ABIL_STR) = LIMIT(atoi(arg), -20, 20); - break; + OLC_VAL(d) = TRUE; + medit_disp_stats_menu(d); + return; case MEDIT_SAVE_DEX: GET_SAVE(OLC_MOB(d), ABIL_DEX) = LIMIT(atoi(arg), -20, 20); - break; + OLC_VAL(d) = TRUE; + medit_disp_stats_menu(d); + return; case MEDIT_SAVE_CON: GET_SAVE(OLC_MOB(d), ABIL_CON) = LIMIT(atoi(arg), -20, 20); - break; + OLC_VAL(d) = TRUE; + medit_disp_stats_menu(d); + return; case MEDIT_SAVE_INT: GET_SAVE(OLC_MOB(d), ABIL_INT) = LIMIT(atoi(arg), -20, 20); - break; + OLC_VAL(d) = TRUE; + medit_disp_stats_menu(d); + return; case MEDIT_SAVE_WIS: GET_SAVE(OLC_MOB(d), ABIL_WIS) = LIMIT(atoi(arg), -20, 20); - break; + OLC_VAL(d) = TRUE; + medit_disp_stats_menu(d); + return; case MEDIT_SAVE_CHA: GET_SAVE(OLC_MOB(d), ABIL_CHA) = LIMIT(atoi(arg), -20, 20); - break; + OLC_VAL(d) = TRUE; + medit_disp_stats_menu(d); + return; case MEDIT_POS: GET_POS(OLC_MOB(d)) = LIMIT(i - 1, 0, NUM_POSITIONS - 1);