diff --git a/src/act.offensive.c b/src/act.offensive.c index 9577923..3d59499 100644 --- a/src/act.offensive.c +++ b/src/act.offensive.c @@ -130,7 +130,7 @@ ACMD(do_backstab) int roll, atk_bonus, total, target_ac; bool crit_success = FALSE, crit_fail = FALSE; - if (IS_NPC(ch) || !GET_SKILL(ch, SKILL_BACKSTAB)) { + if (!GET_SKILL(ch, SKILL_BACKSTAB)) { send_to_char(ch, "You have no idea how to do that.\r\n"); return; } @@ -314,7 +314,7 @@ ACMD(do_bash) one_argument(argument, arg); - if (IS_NPC(ch) || !GET_SKILL(ch, SKILL_BASH)) { + if (!GET_SKILL(ch, SKILL_BASH)) { send_to_char(ch, "You have no idea how.\r\n"); return; } @@ -405,7 +405,7 @@ ACMD(do_rescue) struct char_data *vict, *tmp_ch; int roll, bonus, total, dc; - if (IS_NPC(ch) || !GET_SKILL(ch, SKILL_RESCUE)) { + if (!GET_SKILL(ch, SKILL_RESCUE)) { send_to_char(ch, "You have no idea how to do that.\r\n"); return; } @@ -576,7 +576,7 @@ EVENTFUNC(event_whirlwind) ACMD(do_whirlwind) { - if (IS_NPC(ch) || !GET_SKILL(ch, SKILL_WHIRLWIND)) { + if (!GET_SKILL(ch, SKILL_WHIRLWIND)) { send_to_char(ch, "You have no idea how.\r\n"); return; } @@ -618,7 +618,7 @@ ACMD(do_kick) int roll, atk_bonus, total, target_ac; bool crit_success = FALSE, crit_miss = FALSE; - if (IS_NPC(ch) || !GET_SKILL(ch, SKILL_KICK)) { + if (!GET_SKILL(ch, SKILL_KICK)) { send_to_char(ch, "You have no idea how.\r\n"); return; } diff --git a/src/act.other.c b/src/act.other.c index 1b8253a..f96545c 100644 --- a/src/act.other.c +++ b/src/act.other.c @@ -195,7 +195,7 @@ ACMD(do_sneak) int rolla, rollb, roll, bonus, total, dc; bool disadv = FALSE; - if (IS_NPC(ch) || !GET_SKILL(ch, SKILL_SNEAK)) { + if (!GET_SKILL(ch, SKILL_SNEAK)) { send_to_char(ch, "You have no idea how to do that.\r\n"); return; } @@ -259,7 +259,7 @@ ACMD(do_hide) int rolla, rollb, roll, bonus, total, dc; bool disadv = FALSE; - if (IS_NPC(ch) || !GET_SKILL(ch, SKILL_HIDE)) { + if (!GET_SKILL(ch, SKILL_HIDE)) { send_to_char(ch, "You have no idea how to do that.\r\n"); return; } @@ -323,7 +323,7 @@ ACMD(do_perception) int roll, bonus, total; int found_chars = 0, found_objs = 0; - if (IS_NPC(ch) || !GET_SKILL(ch, SKILL_PERCEPTION)) { + if (!GET_SKILL(ch, SKILL_PERCEPTION)) { send_to_char(ch, "You have no idea how to do that.\r\n"); return; } @@ -404,7 +404,7 @@ ACMD(do_steal) char vict_name[MAX_INPUT_LENGTH], obj_name[MAX_INPUT_LENGTH]; int percent, gold, eq_pos, pcsteal = 0, ohoh = 0; - if (IS_NPC(ch) || !GET_SKILL(ch, SKILL_STEAL)) { + if (!GET_SKILL(ch, SKILL_STEAL)) { send_to_char(ch, "You have no idea how to do that.\r\n"); return; } @@ -525,9 +525,6 @@ ACMD(do_steal) ACMD(do_skills) { - if (IS_NPC(ch)) - return; - list_skills(ch); } diff --git a/src/db.c b/src/db.c index 14a7676..7d77e87 100644 --- a/src/db.c +++ b/src/db.c @@ -1597,44 +1597,28 @@ static void parse_simple_mob(FILE *mob_f, int i, int 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 - * absurdly easy -- just add a new CASE statement to this function! No other - * changes need to be made anywhere in the code. - * CASE : Requires a parameter through 'value'. */ -#define CASE(test) \ - if (value && !matched && !str_cmp(keyword, test) && (matched = TRUE)) -#define RANGE(low, high) \ - (num_arg = MAX((low), MIN((high), (num_arg)))) + * assigns the correct value to the mob as appropriate. Adding new e-specs is + * straightforward: just add a new CASE() block. */ +#define CASE(test) \ + if (value && !matched && !str_cmp(keyword, test) && (matched = TRUE)) +#define RANGE(low, high) \ + (num_arg = MAX((low), MIN((high), (num_arg)))) static void interpret_espec(const char *keyword, const char *value, int i, int nr) { - int num_arg = 0, matched = FALSE; + int num_arg = 0; + bool matched = FALSE; - /* If there isn't a colon, there is no value. While Boolean options are - * possible, we don't actually have any. Feel free to make some. */ + /* Defensive check: if there's a value, convert to int */ if (value) num_arg = atoi(value); - CASE("BareHandAttack") { - RANGE(0, NUM_ATTACK_TYPES - 1); - mob_proto[i].mob_specials.attack_type = num_arg; - } - + /* --- Ability Scores --- */ CASE("Str") { RANGE(3, 25); mob_proto[i].real_abils.str = num_arg; } - CASE("Int") { - RANGE(3, 25); - mob_proto[i].real_abils.intel = num_arg; - } - - CASE("Wis") { - RANGE(3, 25); - mob_proto[i].real_abils.wis = num_arg; - } - CASE("Dex") { RANGE(3, 25); mob_proto[i].real_abils.dex = num_arg; @@ -1645,12 +1629,22 @@ static void interpret_espec(const char *keyword, const char *value, int i, int n mob_proto[i].real_abils.con = num_arg; } + CASE("Int") { + RANGE(3, 25); + mob_proto[i].real_abils.intel = num_arg; + } + + CASE("Wis") { + RANGE(3, 25); + mob_proto[i].real_abils.wis = num_arg; + } + CASE("Cha") { RANGE(3, 25); mob_proto[i].real_abils.cha = num_arg; } - /* --- New 5e-style saving throw keywords --- */ + /* --- 5e-style Saving Throws --- */ CASE("SaveStr") { RANGE(0, 100); mob_proto[i].char_specials.saved.saving_throws[ABIL_STR] = num_arg; @@ -1681,42 +1675,100 @@ static void interpret_espec(const char *keyword, const char *value, int i, int n mob_proto[i].char_specials.saved.saving_throws[ABIL_CHA] = num_arg; } + /* --- Debug + Fallback --- */ if (!matched) { + log("DEBUG: Unmatched espec line '%s' value '%s' in mob #%d", + keyword, value ? value : "(null)", nr); log("SYSERR: Warning: unrecognized espec keyword %s in mob #%d", - keyword, nr); + keyword, nr); } } +/* Prevent macro bleed outside this function’s scope */ #undef CASE -#undef BOOL_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++; } - interpret_espec(buf, ptr, i, nr); + + /* 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) { char line[READ_SIZE]; + /* Step 1: parse the standard numeric lines (level, dice, pos, sex, etc.) */ parse_simple_mob(mob_f, i, nr); + /* Step 2: read extended attributes until 'E' line encountered */ while (get_line(mob_f, line)) { - if (!strcmp(line, "E")) /* end of the enhanced section */ + if (!strcmp(line, "E")) /* end of the enhanced section */ return; - else if (*line == '#') { /* we've hit the next mob, maybe? */ + else if (*line == '#') { /* premature next mob start */ log("SYSERR: Unterminated E section in mob #%d", nr); exit(1); - } else - parse_espec(line, i, nr); + } + + /* --- Begin NPC Skill Extension --- */ + else if (!strncmp(line, "Skill", 5)) { + int snum = 0, sval = 0; + if (sscanf(line, "Skill %d %d", &snum, &sval) == 2) { + if (snum >= 0 && snum < MAX_SKILLS) + SET_SKILL(&mob_proto[i], snum, (byte)MIN(MAX(0, sval), 100)); + else + log("SYSERR: Invalid skill index %d in mob #%d", snum, nr); + } else + log("SYSERR: Malformed Skill line in mob #%d: '%s'", nr, line); + continue; + } + /* --- End NPC Skill Extension --- */ + + else + parse_espec(line, i, nr); /* interpret Str:, Dex:, Save*, etc. */ } log("SYSERR: Unexpected end of file reached after mob #%d", nr); diff --git a/src/genmob.c b/src/genmob.c index 4b25d6e..1b500fc 100644 --- a/src/genmob.c +++ b/src/genmob.c @@ -327,37 +327,62 @@ int save_mobiles(zone_rnum rznum) int write_mobile_espec(mob_vnum mvnum, struct char_data *mob, FILE *fd) { - if (GET_ATTACK(mob) != 0) - fprintf(fd, "BareHandAttack: %d\n", GET_ATTACK(mob)); - if (GET_STR(mob) != 11) + int count = 0; + + /* --- Ability scores (only write non-defaults) --- */ + if (GET_STR(mob) != 11) { fprintf(fd, "Str: %d\n", GET_STR(mob)); - if (GET_DEX(mob) != 11) + count++; + } + if (GET_DEX(mob) != 11) { fprintf(fd, "Dex: %d\n", GET_DEX(mob)); - if (GET_INT(mob) != 11) + count++; + } + if (GET_INT(mob) != 11) { fprintf(fd, "Int: %d\n", GET_INT(mob)); - if (GET_WIS(mob) != 11) + count++; + } + if (GET_WIS(mob) != 11) { fprintf(fd, "Wis: %d\n", GET_WIS(mob)); - if (GET_CON(mob) != 11) + count++; + } + if (GET_CON(mob) != 11) { fprintf(fd, "Con: %d\n", GET_CON(mob)); - if (GET_CHA(mob) != 11) + count++; + } + if (GET_CHA(mob) != 11) { fprintf(fd, "Cha: %d\n", GET_CHA(mob)); + count++; + } - /* --- New ability-based saving throws --- */ - if (GET_SAVE(mob, ABIL_STR) != 0) + /* --- 5e-style saving throws --- */ + if (GET_SAVE(mob, ABIL_STR) != 0) { fprintf(fd, "SaveStr: %d\n", GET_SAVE(mob, ABIL_STR)); - if (GET_SAVE(mob, ABIL_DEX) != 0) + count++; + } + if (GET_SAVE(mob, ABIL_DEX) != 0) { fprintf(fd, "SaveDex: %d\n", GET_SAVE(mob, ABIL_DEX)); - if (GET_SAVE(mob, ABIL_CON) != 0) + count++; + } + if (GET_SAVE(mob, ABIL_CON) != 0) { fprintf(fd, "SaveCon: %d\n", GET_SAVE(mob, ABIL_CON)); - if (GET_SAVE(mob, ABIL_INT) != 0) + count++; + } + if (GET_SAVE(mob, ABIL_INT) != 0) { fprintf(fd, "SaveInt: %d\n", GET_SAVE(mob, ABIL_INT)); - if (GET_SAVE(mob, ABIL_WIS) != 0) + count++; + } + if (GET_SAVE(mob, ABIL_WIS) != 0) { fprintf(fd, "SaveWis: %d\n", GET_SAVE(mob, ABIL_WIS)); - if (GET_SAVE(mob, ABIL_CHA) != 0) + count++; + } + if (GET_SAVE(mob, ABIL_CHA) != 0) { fprintf(fd, "SaveCha: %d\n", GET_SAVE(mob, ABIL_CHA)); + count++; + } - fputs("E\n", fd); - return TRUE; + /* DO NOT print "E" here — handled by write_mobile_record() */ + return count; } int write_mobile_record(mob_vnum mvnum, struct char_data *mob, FILE *fd) @@ -371,60 +396,78 @@ int write_mobile_record(mob_vnum mvnum, struct char_data *mob, FILE *fd) strip_cr(strncpy(ldesc, GET_LDESC(mob), MAX_STRING_LENGTH - 1)); strip_cr(strncpy(ddesc, GET_DDESC(mob), MAX_STRING_LENGTH - 1)); - int n = snprintf(buf, MAX_STRING_LENGTH, "#%d\n" - "%s%c\n" - "%s%c\n" - "%s%c\n" - "%s%c\n", - mvnum, - GET_ALIAS(mob), STRING_TERMINATOR, - GET_SDESC(mob), STRING_TERMINATOR, - ldesc, STRING_TERMINATOR, - ddesc, STRING_TERMINATOR - ); + int n = snprintf(buf, MAX_STRING_LENGTH, + "#%d\n" + "%s%c\n" + "%s%c\n" + "%s%c\n" + "%s%c\n", + mvnum, + GET_ALIAS(mob), STRING_TERMINATOR, + GET_SDESC(mob), STRING_TERMINATOR, + ldesc, STRING_TERMINATOR, + ddesc, STRING_TERMINATOR); - if (n < MAX_STRING_LENGTH) { - fprintf(fd, "%s", convert_from_tabs(buf)); - - fprintf(fd, "%d %d %d %d %d %d %d %d %d E\n" - "%d %dd%d+%d\n", - MOB_FLAGS(mob)[0], MOB_FLAGS(mob)[1], - MOB_FLAGS(mob)[2], MOB_FLAGS(mob)[3], - AFF_FLAGS(mob)[0], AFF_FLAGS(mob)[1], - AFF_FLAGS(mob)[2], AFF_FLAGS(mob)[3], - GET_ALIGNMENT(mob), - GET_LEVEL(mob), GET_HIT(mob), - GET_MANA(mob), GET_MOVE(mob)); - - fprintf(fd, "%d %d %d\n", - GET_POS(mob), GET_DEFAULT_POS(mob), GET_SEX(mob) - ); - - /* Write any E-specs */ - if (write_mobile_espec(mvnum, mob, fd) < 0) - log("SYSERR: GenOLC: Error writing E-specs for mobile #%d.", mvnum); - - /* --- Persist prototype loadout lines (one per entry) --- */ - for (struct mob_loadout *e = mob->proto_loadout; e; e = e->next) { - fprintf(fd, "L %d %d %d\n", - (int)e->wear_pos, - (int)e->vnum, - MAX(1, e->quantity)); - } - - /* DG scripts after loadout lines */ - script_save_to_disk(fd, mob, MOB_TRIGGER); - -#if CONFIG_GENOLC_MOBPROG - if (write_mobile_mobprog(mvnum, mob, fd) < 0) - log("SYSERR: GenOLC: Error writing MobProgs for mobile #%d.", mvnum); -#endif - } else { + if (n >= MAX_STRING_LENGTH) { mudlog(BRF, LVL_BUILDER, TRUE, "SYSERR: Could not save mobile #%d due to size (%d > maximum of %d)", mvnum, n, MAX_STRING_LENGTH); + return TRUE; } + fprintf(fd, "%s", convert_from_tabs(buf)); + + /* --- FLAGS/AFFECT/ALIGN line --- */ + fprintf(fd, + "%d %d %d %d %d %d %d %d %d E\n", + MOB_FLAGS(mob)[0], MOB_FLAGS(mob)[1], + MOB_FLAGS(mob)[2], MOB_FLAGS(mob)[3], + AFF_FLAGS(mob)[0], AFF_FLAGS(mob)[1], + AFF_FLAGS(mob)[2], AFF_FLAGS(mob)[3], + GET_ALIGNMENT(mob)); + + /* --- Level, hitdice, mana, move --- */ + fprintf(fd, "%d %dd%d+%d\n", + GET_LEVEL(mob), + GET_HIT(mob), + GET_MANA(mob), + GET_MOVE(mob)); + + /* --- Position / default position / sex --- */ + fprintf(fd, "%d %d %d\n", + GET_POS(mob), + GET_DEFAULT_POS(mob), + GET_SEX(mob)); + + /* --- Enhanced (E-spec + Skills) --- */ + if (write_mobile_espec(mvnum, mob, fd) < 0) + log("SYSERR: GenOLC: Error writing E-specs for mobile #%d.", mvnum); + + /* Write NPC skills (if any set) */ + for (int s = 0; s < MAX_SKILLS; s++) { + if (mob->mob_specials.skills[s] > 0) + fprintf(fd, "Skill %d %d\n", s, mob->mob_specials.skills[s]); + } + + /* Single proper terminator */ + fprintf(fd, "E\n"); + + /* --- Loadout lines --- */ + for (struct mob_loadout *e = mob->proto_loadout; e; e = e->next) { + fprintf(fd, "L %d %d %d\n", + (int)e->wear_pos, + (int)e->vnum, + MAX(1, e->quantity)); + } + + /* --- DG Scripts --- */ + script_save_to_disk(fd, mob, MOB_TRIGGER); + +#if CONFIG_GENOLC_MOBPROG + if (write_mobile_mobprog(mvnum, mob, fd) < 0) + log("SYSERR: GenOLC: Error writing MobProgs for mobile #%d.", mvnum); +#endif + return TRUE; } diff --git a/src/graph.c b/src/graph.c index b0636f9..2b794c3 100644 --- a/src/graph.c +++ b/src/graph.c @@ -145,7 +145,7 @@ ACMD(do_track) int dir; /* The character must have the track skill. */ - if (IS_NPC(ch) || !GET_SKILL(ch, SKILL_TRACK)) { + if (!GET_SKILL(ch, SKILL_TRACK)) { send_to_char(ch, "You have no idea how.\r\n"); return; } diff --git a/src/medit.c b/src/medit.c index 6ffc84b..ce9c068 100644 --- a/src/medit.c +++ b/src/medit.c @@ -432,6 +432,7 @@ static void medit_disp_menu(struct descriptor_data *d) "%s7%s) Default : %s%s\r\n" "%s8%s) Attack : %s%s\r\n" "%s9%s) Stats Menu...\r\n" + "%s0%s) Skills Menu...\r\n" "%sA%s) NPC Flags : %s%s\r\n" "%sB%s) AFF Flags : %s%s\r\n" "%sS%s) Script : %s%s\r\n" @@ -444,6 +445,7 @@ static void medit_disp_menu(struct descriptor_data *d) grn, nrm, yel, position_types[(int)GET_DEFAULT_POS(mob)], grn, nrm, yel, attack_hit_text[(int)GET_ATTACK(mob)].singular, grn, nrm, + grn, nrm, grn, nrm, cyn, flags, grn, nrm, cyn, flag2, grn, nrm, cyn, OLC_SCRIPT(d) ?"Set.":"Not Set.", @@ -491,27 +493,27 @@ static void medit_disp_stats_menu(struct descriptor_data *d) if (CONFIG_MEDIT_ADVANCED) { /* Bottom section - non-standard stats, togglable in cedit */ write_to_output(d, - " %sAttributes%s %sSaving Throws%s\r\n" - "(%sF%s) Str: %s[%s%2d%s]%s (%sR%s) Save STR %s[%s%3d%s]%s\r\n" - "(%sG%s) Int: %s[%s%3d%s]%s (%sS%s) Save DEX %s[%s%3d%s]%s\r\n" - "(%sH%s) Wis: %s[%s%3d%s]%s (%sT%s) Save CON %s[%s%3d%s]%s\r\n" - "(%sI%s) Dex: %s[%s%3d%s]%s (%sU%s) Save INT %s[%s%3d%s]%s\r\n" - "(%sJ%s) Con: %s[%s%3d%s]%s (%sV%s) Save WIS %s[%s%3d%s]%s\r\n" + "%sAttributes Saving Throws\r\n" + "(%sF%s) Str: %s[%s%3d%s]%s (%sR%s) Save STR %s[%s%3d%s]%s\r\n" + "(%sG%s) Dex: %s[%s%3d%s]%s (%sS%s) Save DEX %s[%s%3d%s]%s\r\n" + "(%sH%s) Con: %s[%s%3d%s]%s (%sT%s) Save CON %s[%s%3d%s]%s\r\n" + "(%sI%s) Int: %s[%s%3d%s]%s (%sU%s) Save INT %s[%s%3d%s]%s\r\n" + "(%sJ%s) Wis: %s[%s%3d%s]%s (%sV%s) Save WIS %s[%s%3d%s]%s\r\n" "(%sK%s) Cha: %s[%s%3d%s]%s (%sW%s) Save CHA %s[%s%3d%s]%s\r\n\r\n", - nrm, cyn, nrm, cyn, + nrm, cyn, nrm, cyn, yel, GET_STR(mob), cyn, nrm, cyn, nrm, cyn, yel, GET_SAVE(mob, ABIL_STR), cyn, nrm, - cyn, nrm, cyn, yel, GET_INT(mob), cyn, nrm, + cyn, nrm, cyn, yel, GET_DEX(mob), cyn, nrm, cyn, nrm, cyn, yel, GET_SAVE(mob, ABIL_DEX), cyn, nrm, - cyn, nrm, cyn, yel, GET_WIS(mob), cyn, nrm, + cyn, nrm, cyn, yel, GET_CON(mob), cyn, nrm, cyn, nrm, cyn, yel, GET_SAVE(mob, ABIL_CON), cyn, nrm, - cyn, nrm, cyn, yel, GET_DEX(mob), cyn, nrm, + cyn, nrm, cyn, yel, GET_INT(mob), cyn, nrm, cyn, nrm, cyn, yel, GET_SAVE(mob, ABIL_INT), cyn, nrm, - cyn, nrm, cyn, yel, GET_CON(mob), cyn, nrm, + cyn, nrm, cyn, yel, GET_WIS(mob), cyn, nrm, cyn, nrm, cyn, yel, GET_SAVE(mob, ABIL_WIS), cyn, nrm, cyn, nrm, cyn, yel, GET_CHA(mob), cyn, nrm, @@ -525,6 +527,37 @@ static void medit_disp_stats_menu(struct descriptor_data *d) OLC_MODE(d) = MEDIT_STATS_MENU; } +static void medit_disp_skill_menu(struct descriptor_data *d) +{ + struct char_data *mob = OLC_MOB(d); + int count = 0; + + clear_screen(d); + write_to_output(d, "-- %sSkill Editor%s for %s%s%s\r\n", + cyn, nrm, yel, GET_SDESC(mob), nrm); + + /* List all skills that exist (non-zero entries only) */ + for (int i = 0; i < MAX_SKILLS; i++) { + if (mob->mob_specials.skills[i] > 0) { + write_to_output(d, "%3d) %-25s : %3d%%\r\n", + i, spell_info[i].name, mob->mob_specials.skills[i]); + count++; + } + } + + if (count == 0) + write_to_output(d, "No skills set.\r\n"); + + write_to_output(d, "\r\n" + "%sA%s) Add / modify skill\r\n" + "%sD%s) Delete skill\r\n" + "%sQ%s) Return to main menu\r\n" + "Enter choice : ", + grn, nrm, grn, nrm, grn, nrm); + + OLC_MODE(d) = MEDIT_SKILL_MENU; +} + void medit_parse(struct descriptor_data *d, char *arg) { int i = -1, j; @@ -625,6 +658,10 @@ void medit_parse(struct descriptor_data *d, char *arg) OLC_MODE(d) = MEDIT_STATS_MENU; medit_disp_stats_menu(d); return; + case '0': /* Enter skill sub-menu */ + OLC_MODE(d) = MEDIT_SKILL_MENU; + medit_disp_skill_menu(d); + return; case 'a': case 'A': OLC_MODE(d) = MEDIT_NPC_FLAGS; @@ -711,7 +748,7 @@ void medit_parse(struct descriptor_data *d, char *arg) write_to_output(d, "Invalid Choice!\r\nEnter Choice : "); return; } - OLC_MODE(d) = MEDIT_INT; + OLC_MODE(d) = MEDIT_DEX; i++; break; case 'h': @@ -720,7 +757,7 @@ void medit_parse(struct descriptor_data *d, char *arg) write_to_output(d, "Invalid Choice!\r\nEnter Choice : "); return; } - OLC_MODE(d) = MEDIT_WIS; + OLC_MODE(d) = MEDIT_CON; i++; break; case 'i': @@ -729,7 +766,7 @@ void medit_parse(struct descriptor_data *d, char *arg) write_to_output(d, "Invalid Choice!\r\nEnter Choice : "); return; } - OLC_MODE(d) = MEDIT_DEX; + OLC_MODE(d) = MEDIT_INT; i++; break; case 'j': @@ -738,7 +775,7 @@ void medit_parse(struct descriptor_data *d, char *arg) write_to_output(d, "Invalid Choice!\r\nEnter Choice : "); return; } - OLC_MODE(d) = MEDIT_CON; + OLC_MODE(d) = MEDIT_WIS; i++; break; case 'k': @@ -830,6 +867,72 @@ void medit_parse(struct descriptor_data *d, char *arg) write_to_output(d, "Oops...\r\n"); return; + case MEDIT_SKILL_MENU: + switch (*arg) { + case 'q': + case 'Q': + medit_disp_menu(d); + return; + case 'a': + case 'A': + write_to_output(d, "Enter skill name to add or modify: "); + OLC_MODE(d) = MEDIT_SKILL_EDIT; + return; + case 'd': + case 'D': + write_to_output(d, "Enter skill name to delete: "); + OLC_MODE(d) = MEDIT_SKILL_EDIT; + OLC_VAL(d) = 1; /* delete mode */ + return; + default: + medit_disp_skill_menu(d); + return; + } + break; + + case MEDIT_SKILL_EDIT: { + char skillname[MAX_INPUT_LENGTH]; + int snum; + + skip_spaces(&arg); + strlcpy(skillname, arg, sizeof(skillname)); + snum = find_skill_num(skillname); + + if (snum <= 0 || snum >= MAX_SKILLS) { + write_to_output(d, "Invalid skill.\r\n"); + medit_disp_skill_menu(d); + return; + } + + /* Delete mode */ + if (OLC_VAL(d) == 1) { + OLC_MOB(d)->mob_specials.skills[snum] = 0; + write_to_output(d, "Removed %s.\r\n", spell_info[snum].name); + OLC_VAL(d) = 0; + medit_disp_skill_menu(d); + return; + } + + write_to_output(d, "Enter skill value (0-100): "); + OLC_VAL(d) = snum; + OLC_MODE(d) = MEDIT_SKILL_EDIT + 1; + return; + } + break; + + case MEDIT_SKILL_EDIT + 1: { + int val = atoi(arg); + int snum = OLC_VAL(d); + + val = MAX(0, MIN(100, val)); + OLC_MOB(d)->mob_specials.skills[snum] = (byte)val; + + write_to_output(d, "%s set to %d%%.\r\n", spell_info[snum].name, val); + medit_disp_skill_menu(d); + return; + } + break; + case OLC_SCRIPT_EDIT: if (dg_script_edit_parse(d, arg)) return; break; diff --git a/src/oasis.h b/src/oasis.h index 53881eb..143c431 100644 --- a/src/oasis.h +++ b/src/oasis.h @@ -261,32 +261,34 @@ extern const char *nrm, *grn, *cyn, *yel; #define MEDIT_AFF_FLAGS 6 #define MEDIT_CONFIRM_SAVESTRING 7 #define MEDIT_STATS_MENU 8 +#define MEDIT_SKILL_MENU 9 +#define MEDIT_SKILL_EDIT 10 /* Numerical responses. */ -#define MEDIT_NUMERICAL_RESPONSE 10 -#define MEDIT_SEX 11 -#define MEDIT_NUM_HP_DICE 12 -#define MEDIT_SIZE_HP_DICE 13 -#define MEDIT_ADD_HP 14 -#define MEDIT_POS 15 -#define MEDIT_DEFAULT_POS 16 -#define MEDIT_ATTACK 17 -#define MEDIT_LEVEL 18 -#define MEDIT_ALIGNMENT 19 -#define MEDIT_DELETE 20 -#define MEDIT_COPY 21 -#define MEDIT_STR 22 -#define MEDIT_INT 23 -#define MEDIT_WIS 24 -#define MEDIT_DEX 25 -#define MEDIT_CON 26 -#define MEDIT_CHA 27 -#define MEDIT_SAVE_STR 28 -#define MEDIT_SAVE_DEX 29 -#define MEDIT_SAVE_CON 30 -#define MEDIT_SAVE_INT 31 -#define MEDIT_SAVE_WIS 32 -#define MEDIT_SAVE_CHA 33 +#define MEDIT_NUMERICAL_RESPONSE 11 +#define MEDIT_SEX 12 +#define MEDIT_NUM_HP_DICE 13 +#define MEDIT_SIZE_HP_DICE 14 +#define MEDIT_ADD_HP 15 +#define MEDIT_POS 16 +#define MEDIT_DEFAULT_POS 17 +#define MEDIT_ATTACK 18 +#define MEDIT_LEVEL 19 +#define MEDIT_ALIGNMENT 20 +#define MEDIT_DELETE 21 +#define MEDIT_COPY 22 +#define MEDIT_STR 23 +#define MEDIT_INT 24 +#define MEDIT_WIS 25 +#define MEDIT_DEX 26 +#define MEDIT_CON 27 +#define MEDIT_CHA 28 +#define MEDIT_SAVE_STR 29 +#define MEDIT_SAVE_DEX 30 +#define MEDIT_SAVE_CON 31 +#define MEDIT_SAVE_INT 32 +#define MEDIT_SAVE_WIS 33 +#define MEDIT_SAVE_CHA 34 /* Submodes of SEDIT connectedness. */ #define SEDIT_MAIN_MENU 0 diff --git a/src/objsave.c b/src/objsave.c index 683b051..553c615 100644 --- a/src/objsave.c +++ b/src/objsave.c @@ -36,7 +36,6 @@ static void auto_equip(struct char_data *ch, struct obj_data *obj, int location) static void Crash_restore_weight(struct obj_data *obj); static int Crash_load_objs(struct char_data *ch); static int handle_obj(struct obj_data *obj, struct char_data *ch, int locate, struct obj_data **cont_rows); -static int objsave_write_rentcode(FILE *fl, int rentcode, int cost_per_day, struct char_data *ch); /* Writes one object record to FILE. Old name: Obj_to_store(). * Updated to save all NUM_OBJ_VAL_POSITIONS values instead of only 4. */ @@ -526,24 +525,6 @@ void Crash_rentsave(struct char_data *ch, int cost) Crash_crashsave(ch); } -static int objsave_write_rentcode(FILE *fl, int rentcode, int cost_per_day, struct char_data *ch) -{ - if (fprintf(fl, "%d %ld %d %d %d %d\r\n", - rentcode, - (long) time(0), - cost_per_day, - GET_GOLD(ch), - GET_BANK_GOLD(ch), - 0) - < 1) - { - perror("Syserr: Writing rent code"); - return FALSE; - } - return TRUE; - -} - void Crash_save_all(void) { struct descriptor_data *d; diff --git a/src/players.c b/src/players.c index 45486de..a554b00 100644 --- a/src/players.c +++ b/src/players.c @@ -248,7 +248,10 @@ int load_char(const char *name, struct char_data *ch) /* Character initializations. Necessary to keep some things straight. */ ch->affected = NULL; for (i = 1; i <= MAX_SKILLS; i++) - GET_SKILL(ch, i) = 0; + if (IS_NPC(ch)) + ch->mob_specials.skills[i] = 0; + else + ch->player_specials->saved.skills[i] = 0; GET_SEX(ch) = PFDEF_SEX; GET_CLASS(ch) = PFDEF_CLASS; GET_LEVEL(ch) = PFDEF_LEVEL; @@ -479,7 +482,10 @@ int load_char(const char *name, struct char_data *ch) /* initialization for imms */ if (GET_LEVEL(ch) >= LVL_IMMORT) { for (i = 1; i <= MAX_SKILLS; i++) - GET_SKILL(ch, i) = 100; + if (IS_NPC(ch)) + ch->mob_specials.skills[i] = 100; + else + ch->player_specials->saved.skills[i] = 100; GET_COND(ch, HUNGER) = -1; GET_COND(ch, THIRST) = -1; GET_COND(ch, DRUNK) = -1; @@ -868,8 +874,13 @@ static void load_skills(FILE *fl, struct char_data *ch) do { get_line(fl, line); sscanf(line, "%d %d", &num, &num2); - if (num != 0) - GET_SKILL(ch, num) = num2; + + if (num != 0) { + if (IS_NPC(ch)) + ch->mob_specials.skills[num] = num2; + else + ch->player_specials->saved.skills[num] = num2; + } } while (num != 0); } diff --git a/src/structs.h b/src/structs.h index 34c8f44..b60fa86 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1014,6 +1014,7 @@ struct mob_special_data memory_rec *memory; /**< List of PCs to remember */ byte attack_type; /**< The primary attack type (bite, sting, hit, etc.) */ byte default_pos; /**< Default position (standing, sleeping, etc.) */ + byte skills[MAX_SKILLS]; /* NPC-specific skill proficiency (0-100) */ }; /** An affect structure. */ diff --git a/src/utils.h b/src/utils.h index 8330f25..bfef996 100644 --- a/src/utils.h +++ b/src/utils.h @@ -659,10 +659,17 @@ do \ /** The type of quest ch is currently participating in. */ #define GET_QUEST_TYPE(ch) (real_quest(GET_QUEST((ch))) != NOTHING ? aquest_table[real_quest(GET_QUEST((ch)))].type : AQ_UNDEFINED ) -/** The current skill level of ch for skill i. */ -#define GET_SKILL(ch, i) CHECK_PLAYER_SPECIAL((ch), ((ch)->player_specials->saved.skills[i])) -/** Copy the current skill level i of ch to pct. */ -#define SET_SKILL(ch, i, pct) do { CHECK_PLAYER_SPECIAL((ch), (ch)->player_specials->saved.skills[i]) = pct; } while(0) +/* Unified access macros for PC and NPC skills */ +#define GET_SKILL(ch, i) \ + (IS_NPC(ch) ? ((ch)->mob_specials.skills[(i)]) : ((ch)->player_specials->saved.skills[(i)])) + +#define SET_SKILL(ch, i, pct) do { \ + if (IS_NPC(ch)) \ + (ch)->mob_specials.skills[(i)] = (pct); \ + else { \ + CHECK_PLAYER_SPECIAL((ch), (ch)->player_specials->saved.skills[(i)]) = (pct); \ + } \ +} while (0) /** Per-skill next gain time (epoch seconds). Index with a valid skill number. **/ #define GET_SKILL_NEXT_GAIN(ch, i) \ (CHECK_PLAYER_SPECIAL((ch), (ch)->player_specials->saved.next_skill_gain[(i)]))