Add NPC skills, update medit menu

This commit is contained in:
kinther 2025-10-25 13:48:34 -07:00
parent cc342ef45c
commit ed8ee4aa5f
11 changed files with 375 additions and 178 deletions

View file

@ -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;
}

View file

@ -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);
}

120
src/db.c
View file

@ -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 functions 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);

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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);
}

View file

@ -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. */

View file

@ -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)]))