From f1ef26ad17f78b98daf52c390e55432702b2adc0 Mon Sep 17 00:00:00 2001 From: GickerLDS Date: Fri, 2 Dec 2022 16:02:29 +0000 Subject: [PATCH] Added the linguistics skill, with each rank granting a new spoken language. Added spoken languages and the speak command, as well as an area in the study menu to select languages. Added the rsay command for in-character speech Added the osay/ooc command. Say now defaults to osay on Faerun, but not on Lumi. --- act.comm.c | 230 ++++++++++++++++++++++++++++++++++++++++++++++++ act.h | 3 + act.offensive.c | 4 +- class.c | 37 ++++++++ class.h | 3 +- constants.c | 2 +- depend | 3 +- interpreter.c | 8 ++ mobact.c | 2 +- oasis.h | 1 + players.c | 29 +++++- spec_procs.c | 1 - spells.h | 2 +- structs.h | 5 +- study.c | 129 ++++++++++++++++++++++++++- utils.c | 16 ++++ utils.h | 1 + 17 files changed, 461 insertions(+), 15 deletions(-) diff --git a/act.comm.c b/act.comm.c index 66137067..9fff1995 100755 --- a/act.comm.c +++ b/act.comm.c @@ -25,6 +25,106 @@ #include "act.h" #include "modify.h" #include "hlquest.h" +#include "constants.h" + +ACMDU(do_rsay) +{ + char type[20]; + char *arg2 = NULL; + + if (IS_ANIMAL(ch) && !IS_WILDSHAPED(ch)) + { + send_to_char(ch, "You can't speak!\r\n"); + return; + } + + if (AFF_FLAGGED(ch, AFF_SILENCED)) + { + send_to_char(ch, "You can't seem to make a sound.\r\n"); + return; + } + + skip_spaces(&argument); + + if (!*argument) + send_to_char(ch, "Yes, but WHAT do you want to say?\r\n"); + else + { + char buf[MAX_INPUT_LENGTH + 14]; + const char *msg = NULL; + arg2 = strdup(argument); // make a copy to send to triggers b4 parse + struct char_data *vict; + + /* TODO (Nashak): + * - Parse and remove any smiley faces? (not for now) + * - Based on smileys parsed, express emotion in message? + */ + if (CONFIG_SPECIAL_IN_COMM && legal_communication(argument)) + parse_at(argument); + sentence_case(argument); + + if (argument[strlen(argument) - 1] == '?') + { + // the argument ends in a question mark, it's probably a question + strlcpy(type, "ask", sizeof(type)); + } + else if (argument[strlen(argument) - 1] == '!') + { + strlcpy(type, "exclaim", sizeof(type)); + } + else if (argument[strlen(argument) - 1] == '.' && + argument[strlen(argument) - 2] == '.' && + argument[strlen(argument) - 3] == '.') + { + strlcpy(type, "mutter", sizeof(type)); + } + else + { + // the argument ends something else, normal tone + // append a period if it isn't already there + if (argument[strlen(argument) - 1] != '.') + strcat(argument, "."); + + strlcpy(type, "say", sizeof(type)); + } + + snprintf(buf, sizeof(buf), "\tG$n %ss in %s, '%s'\tn", type, languages[SPEAKING(ch)], argument); + // msg = act(buf, FALSE, ch, 0, 0, TO_ROOM | DG_NO_TRIG); + + // add message to history for those who heard it + for (vict = world[IN_ROOM(ch)].people; vict; vict = vict->next_in_room) + if (vict != ch && GET_POS(vict) > POS_SLEEPING && !AFF_FLAGGED(vict, AFF_DEAF)) + { + if (CAN_SPEAK(vict, SPEAKING(ch))) + { + msg = act(buf, FALSE, ch, 0, vict, TO_VICT | DG_NO_TRIG); + add_history(vict, msg, HIST_SAY); + } + else + { + msg = act("\tG$n says something in an unfamiliar tongue.\tn", FALSE, ch, 0, vict, TO_VICT | DG_NO_TRIG); + add_history(vict, msg, HIST_SAY); + } + } + + if (!IS_NPC(ch) && PRF_FLAGGED(ch, PRF_NOREPEAT)) + send_to_char(ch, "%s", CONFIG_OK); + else + { + snprintf(buf, sizeof(buf), "\tGYou %s in %s, '%s'\tn", type, languages[SPEAKING(ch)], argument); + msg = act(buf, FALSE, ch, 0, 0, TO_CHAR | DG_NO_TRIG); + add_history(ch, msg, HIST_SAY); + } + } + + /* DEBUG */ + // send_to_char(ch, "ARG2|%s|\r\n", arg2); + /* end DEBUG */ + + /* Trigger check. */ + speech_mtrigger(ch, arg2); + speech_wtrigger(ch, arg2); +} ACMDU(do_say) { @@ -114,6 +214,136 @@ ACMDU(do_say) speech_wtrigger(ch, arg2); } +ACMDU(do_osay) +{ + char type[20]; + char *arg2 = NULL; + + skip_spaces(&argument); + + if (!*argument) + send_to_char(ch, "Yes, but WHAT do you want to say out-of-character?\r\n"); + else + { + char buf[MAX_INPUT_LENGTH + 14]; + const char *msg = NULL; + arg2 = strdup(argument); // make a copy to send to triggers b4 parse + struct char_data *vict; + + /* TODO (Nashak): + * - Parse and remove any smiley faces? (not for now) + * - Based on smileys parsed, express emotion in message? + */ + if (CONFIG_SPECIAL_IN_COMM && legal_communication(argument)) + parse_at(argument); + sentence_case(argument); + + if (argument[strlen(argument) - 1] == '?') + { + // the argument ends in a question mark, it's probably a question + strlcpy(type, "ask", sizeof(type)); + } + else if (argument[strlen(argument) - 1] == '!') + { + strlcpy(type, "exclaim", sizeof(type)); + } + else if (argument[strlen(argument) - 1] == '.' && + argument[strlen(argument) - 2] == '.' && + argument[strlen(argument) - 3] == '.') + { + strlcpy(type, "mutter", sizeof(type)); + } + else + { + // the argument ends something else, normal tone + // append a period if it isn't already there + if (argument[strlen(argument) - 1] != '.') + strcat(argument, "."); + + strlcpy(type, "say", sizeof(type)); + } + + snprintf(buf, sizeof(buf), "\tG$n %ss out-of-character, '%s'\tn", type, argument); + msg = act(buf, FALSE, ch, 0, 0, TO_ROOM | DG_NO_TRIG); + + // add message to history for those who heard it + for (vict = world[IN_ROOM(ch)].people; vict; vict = vict->next_in_room) + if (vict != ch && GET_POS(vict) > POS_SLEEPING && !AFF_FLAGGED(vict, AFF_DEAF)) + add_history(vict, msg, HIST_OSAY); + + if (!IS_NPC(ch) && PRF_FLAGGED(ch, PRF_NOREPEAT)) + send_to_char(ch, "%s", CONFIG_OK); + else + { + snprintf(buf, sizeof(buf), "\tGYou %s out-of-character, '%s'\tn", type, argument); + msg = act(buf, FALSE, ch, 0, 0, TO_CHAR | DG_NO_TRIG); + add_history(ch, msg, HIST_OSAY); + } + } + + /* DEBUG */ + // send_to_char(ch, "ARG2|%s|\r\n", arg2); + /* end DEBUG */ + + /* Trigger check. */ + speech_mtrigger(ch, arg2); + speech_wtrigger(ch, arg2); +} + +ACMDU(do_speak) +{ + + skip_spaces(&argument); + int i = 0; + + if (!*argument) + { + send_to_char(ch, "You can select among the following languages to speak:\r\n"); + for (i = 0; i < NUM_LANGUAGES; i++) + { + if (CAN_SPEAK(ch, i)) + { + send_to_char(ch, "%s%s%s ", SPEAKING(ch) == i ? "(" : "", languages[i], SPEAKING(ch) == i ? ")" : ""); + } + } + send_to_char(ch, "\r\n"); + return; + } + if (!strcmp(argument, "all")) + { + send_to_char(ch, "The following languages are implemented:\r\n"); + for (i = 0; i < NUM_LANGUAGES; i++) + { + send_to_char(ch, "%s ", languages[i]); + } + send_to_char(ch, "\r\n"); + return; + } + + for (i = 0; i < NUM_LANGUAGES; i++) + { + if (is_abbrev(argument, languages[i])) + { + if (CAN_SPEAK(ch, i)) + { + SPEAKING(ch) = i; + send_to_char(ch, "You begin to speak '%s'.\r\n", languages[i]); + break; + } + else + { + send_to_char(ch, "You do not know that language.\r\n"); + break; + } + } + } + + if (i >= NUM_LANGUAGES) + { + send_to_char(ch, "That is not a valid language. Type 'speak all' for a full list, or 'speak' by itself to see the languages you know.\r\n"); + } +} + ACMDU(do_gsay) { skip_spaces(&argument); diff --git a/act.h b/act.h index d2f979d7..d685bd95 100755 --- a/act.h +++ b/act.h @@ -149,6 +149,9 @@ ACMD_DECL(do_spec_comm); /* functions without subcommands */ ACMD_DECL(do_say); ACMD_DECL(do_gsay); +ACMD_DECL(do_osay); +ACMD_DECL(do_rsay); +ACMD_DECL(do_speak); ACMD_DECL(do_page); ACMD_DECL(do_reply); ACMD_DECL(do_tell); diff --git a/act.offensive.c b/act.offensive.c index fabee4de..1cf3cf55 100755 --- a/act.offensive.c +++ b/act.offensive.c @@ -3711,8 +3711,8 @@ int perform_fear_aura(struct char_data *ch) bool got_em = FALSE; struct char_data *vict = NULL, *next_vict = NULL; - act("You raise your head and let out a bone chilling roar.", FALSE, ch, 0, 0, TO_CHAR); - act("$n raises $s head and lets out a bone chilling roar", FALSE, ch, 0, 0, TO_ROOM); + act("\tCYou raise your head and let out a bone chilling roar.\tn", FALSE, ch, 0, 0, TO_CHAR); + act("\tC$n raises $s head and lets out a bone chilling roar.\tn", FALSE, ch, 0, 0, TO_ROOM); for (vict = world[IN_ROOM(ch)].people; vict; vict = next_vict) { diff --git a/class.c b/class.c index f7955d1c..e6e9c966 100755 --- a/class.c +++ b/class.c @@ -432,6 +432,7 @@ void assign_class_abils(int class_num, class_list[class_num].class_abil[ABILITY_SWIM] = swim; class_list[class_num].class_abil[ABILITY_USE_MAGIC_DEVICE] = use_magic_device; class_list[class_num].class_abil[ABILITY_PERFORM] = perform; + class_list[class_num].class_abil[ABILITY_LINGUISTICS] = CA; } /* function to give default values for a class before assignment */ @@ -2531,6 +2532,8 @@ void init_start_char(struct char_data *ch) KNOWS_MERCY(ch, i) = 0; for (i = 0; i < NUM_BLACKGUARD_CRUELTIES; i++) KNOWS_CRUELTY(ch, i) = 0; + for (i = 0; i < NUM_LANGUAGES; i++) + ch->player_specials->saved.languages_known[i] = FALSE; ch->player_specials->saved.fiendish_boons = 0; ch->player_specials->saved.channel_energy_type = 0; // this is here so that new characters can't get extra stat points from racefix command. @@ -7834,6 +7837,40 @@ bool can_learn_paladin_mercy(struct char_data *ch, int mercy) return false; } +int num_languages_learned(struct char_data *ch) +{ + int i = 0, num = 0; + + for (i = 0; i < NUM_LANGUAGES; i++) + { + if (ch->player_specials->saved.languages_known[i] > 0) + num++; + } + return num; +} + +bool has_unchosen_languages(struct char_data *ch) +{ + if (!ch) + return false; + + if (IS_NPC(ch)) + return false; + + int num_avail = MAX(0, GET_REAL_INT_BONUS(ch)) + MAX(0, GET_ABILITY(ch, ABILITY_LINGUISTICS)); + int num_chosen = num_languages_learned(ch); + /* + send_to_char(ch, "\r\nAVAIL: %d, KNOWN: %d\r\n", num_avail, num_chosen); + send_to_char(ch, "Int: %d\r\n", MAX(0, GET_REAL_INT_BONUS(ch))); + send_to_char(ch, "Skill: %d\r\n", MAX(0, GET_ABILITY(ch, ABILITY_LINGUISTICS))); + */ + + if ((num_avail - num_chosen) > 0) + return TRUE; + + return FALSE; +} + int num_blackguard_cruelties_known(struct char_data *ch) { /* dummy check */ diff --git a/class.h b/class.h index d01909ad..a33aba67 100755 --- a/class.h +++ b/class.h @@ -125,7 +125,8 @@ void feat_assignment(int class_num, int feat_num, bool is_classfeat, int level_received, bool stacks); bool is_class_req_object(struct char_data *ch, struct obj_data *obj, bool output); bool is_class_anti_object(struct char_data *ch, struct obj_data *obj, bool output); - +int num_languages_learned(struct char_data *ch); +bool has_unchosen_languages(struct char_data *ch); bool display_region_info(struct char_data *ch, int region); /* ACMD */ diff --git a/constants.c b/constants.c index f7a79297..ff8d6a32 100755 --- a/constants.c +++ b/constants.c @@ -3648,7 +3648,7 @@ const char *ability_names[] = { "Survival", "Swim", "Use Magic Device", - "Unused8", + "Linguistics", "Perform", /*crafting*/ "woodworking", diff --git a/depend b/depend index 601eec57..03ba13b5 100644 --- a/depend +++ b/depend @@ -4,7 +4,8 @@ account.o: account.c conf.h sysdep.h structs.h bool.h protocol.h lists.h \ class.h act.h account.h act.comm.o: act.comm.c conf.h sysdep.h structs.h bool.h protocol.h \ lists.h campaign.h utils.h db.h helpers.h perfmon.h comm.h interpreter.h \ - handler.h screen.h improved-edit.h dg_scripts.h act.h modify.h hlquest.h + handler.h screen.h improved-edit.h dg_scripts.h act.h modify.h hlquest.h \ + constants.h act.comm.do_spec_comm.o: act.comm.do_spec_comm.c act.h utils.h db.h \ conf.h bool.h structs.h protocol.h sysdep.h lists.h campaign.h helpers.h \ perfmon.h comm.h handler.h hlquest.h diff --git a/interpreter.c b/interpreter.c index 95a17a30..dc13690f 100755 --- a/interpreter.c +++ b/interpreter.c @@ -549,6 +549,8 @@ cpp_extern const struct command_info cmd_info[] = { {"omit", "omit", POS_RECLINING, do_consign_to_oblivion, 0, SCMD_OMIT, FALSE, ACTION_NONE, {0, 0}, NULL}, //{ "objlist", "objlist", POS_DEAD, do_objlist, LVL_BUILDER, 0, TRUE, ACTION_NONE, {0, 0}, NULL}, {"obind", "obind", POS_DEAD, do_obind, LVL_STAFF, 0, TRUE, ACTION_NONE, {0, 0}, NULL}, + {"osay", "osay", POS_RECLINING, do_osay, 0, 0, TRUE, ACTION_NONE, {0, 0}, NULL}, + {"ooc", "ooc", POS_RECLINING, do_osay, 0, 0, TRUE, ACTION_NONE, {0, 0}, NULL}, {"outfit", "outfit", POS_RECLINING, do_outfit, 0, 0, TRUE, ACTION_NONE, {0, 0}, NULL}, /* {"command", "sort_as", minimum_position, *command_pointer, minimum_level, subcmd, ignore_wait, actions_required, {action_cooldowns}, *command_check_pointer},*/ @@ -646,11 +648,16 @@ cpp_extern const struct command_info cmd_info[] = { {"rank", "rank", POS_DEAD, do_rank, 0, 0, TRUE, ACTION_NONE, {0, 0}, NULL}, {"rp", "rp", POS_DEAD, do_gen_tog, 0, SCMD_RP, TRUE, ACTION_NONE, {0, 0}, NULL}, {"resetpassword", "resetpassword", POS_DEAD, do_resetpassword, LVL_IMPL, 0, TRUE, ACTION_NONE, {0, 0}, NULL}, + {"rsay", "rs", POS_RECLINING, do_rsay, 0, 0, TRUE, ACTION_NONE, {0, 0}, NULL}, /* {"command", "sort_as", minimum_position, *command_pointer, minimum_level, subcmd, ignore_wait, actions_required, {action_cooldowns}, *command_check_pointer},*/ {"sacrifice", "sac", POS_RECLINING, do_sac, 0, 0, FALSE, ACTION_NONE, {0, 0}, NULL}, +#ifdef CAMPAIGN_FR + {"say", "s", POS_RECLINING, do_osay, 0, 0, TRUE, ACTION_NONE, {0, 0}, NULL}, +#else {"say", "s", POS_RECLINING, do_say, 0, 0, TRUE, ACTION_NONE, {0, 0}, NULL}, +#endif {"sail", "sail", POS_RECLINING, do_sail, 0, 0, TRUE, ACTION_NONE, {0, 0}, NULL}, {"score", "sc", POS_DEAD, do_score, 0, 0, TRUE, ACTION_NONE, {0, 0}, NULL}, {"scan", "sca", POS_RECLINING, do_scan, 0, 0, FALSE, ACTION_NONE, {0, 0}, NULL}, @@ -688,6 +695,7 @@ cpp_extern const struct command_info cmd_info[] = { {"sneak", "sneak", POS_STANDING, do_sneak, 1, 0, FALSE, ACTION_NONE, {0, 0}, NULL}, {"snoop", "snoop", POS_DEAD, do_snoop, LVL_STAFF, 0, TRUE, ACTION_NONE, {0, 0}, NULL}, {"socials", "socials", POS_DEAD, do_commands, 0, SCMD_SOCIALS, TRUE, ACTION_NONE, {0, 0}, NULL}, + {"speak", "speak", POS_RECLINING, do_speak, 0, 0, TRUE, ACTION_NONE, {0, 0}, NULL}, {"spelllist", "spelllist", POS_RECLINING, do_spelllist, 1, 0, FALSE, ACTION_NONE, {0, 0}, NULL}, {"spells", "spells", POS_RECLINING, do_spells, 1, 0, FALSE, ACTION_NONE, {0, 0}, NULL}, {"split", "split", POS_SITTING, do_split, 1, 0, FALSE, ACTION_NONE, {0, 0}, NULL}, diff --git a/mobact.c b/mobact.c index a34f77c9..e1d39836 100755 --- a/mobact.c +++ b/mobact.c @@ -668,7 +668,7 @@ void npc_racial_behave(struct char_data *ch) if (!(vict = npc_find_target(ch, &num_targets))) return; - if (AFF_FLAGGED(ch, AFF_FEAR_AURA) && dice(1, 4) == 1) + if (AFF_FLAGGED(ch, AFF_FEAR_AURA) && dice(1, 2) == 1) { send_to_char(ch, "You're trying to perform your fear aura.\r\n"); do_fear_aura(ch, NULL, 0, 0); diff --git a/oasis.h b/oasis.h index 53d03d6b..80fe4786 100755 --- a/oasis.h +++ b/oasis.h @@ -657,6 +657,7 @@ i added this trying to debug issues with qedit-copy -zusuk #define STUDY_SELECT_RACIAL_ABILITY 59 #define SET_HIGH_ELF_CANTRIP 60 #define STUDY_CONFIRM_HIGH_ELF_CANTRIP 61 +#define STUDY_CHOOSE_LANGUAGES 62 int save_config(IDXTYPE nowhere); diff --git a/players.c b/players.c index 48ec82a5..aae1ebce 100755 --- a/players.c +++ b/players.c @@ -91,9 +91,10 @@ void save_char_pets(struct char_data *ch); static void load_mercies(FILE *fl, struct char_data *ch); static void load_cruelties(FILE *fl, struct char_data *ch); static void load_buffs(FILE *fl, struct char_data *ch); +static void load_languages(FILE *fl, struct char_data *ch); -// external functions -void autoroll_mob(struct char_data *mob, bool realmode, bool summoned); + // external functions + void autoroll_mob(struct char_data *mob, bool realmode, bool summoned); /* New version to build player index for ASCII Player Files. Generate index * table for the player file. */ @@ -891,6 +892,8 @@ int load_char(const char *name, struct char_data *ch) case 'L': if (!strcmp(tag, "Last")) ch->player.time.logon = atol(line); + else if (!strcmp(tag, "Lang")) + load_languages(fl, ch); else if (!strcmp(tag, "Lern")) GET_PRACTICES(ch) = atoi(line); else if (!strcmp(tag, "Levl")) @@ -1865,6 +1868,11 @@ void save_char(struct char_data *ch, int mode) fprintf(fl, "%d\n", IS_JUDGEMENT_ACTIVE(ch, i)); fprintf(fl, "-1\n"); + fprintf(fl, "Lang:\n"); + for (i = 0; i < NUM_LANGUAGES; i++) + fprintf(fl, "%d\n", ch->player_specials->saved.languages_known[i]); + fprintf(fl, "-1\n"); + /* Save Combat Feats */ for (i = 0; i < NUM_CFEATS; i++) { @@ -2836,6 +2844,23 @@ static void load_cruelties(FILE *fl, struct char_data *ch) } while (num != -1); } +static void load_languages(FILE *fl, struct char_data *ch) +{ + int num = 0, i = 0; + char line[MAX_INPUT_LENGTH + 1]; + + do + { + get_line(fl, line); + sscanf(line, "%d", &num); + if (num != -1) + { + ch->player_specials->saved.languages_known[i] = num; + i++; + } + } while (num != -1); +} + static void load_discoveries(FILE *fl, struct char_data *ch) { int num = 0, i = 0; diff --git a/spec_procs.c b/spec_procs.c index c7098bce..96c2048c 100755 --- a/spec_procs.c +++ b/spec_procs.c @@ -1374,7 +1374,6 @@ void list_abilities(struct char_data *ch, int ability_type) case ABILITY_UNUSED_5: case ABILITY_UNUSED_6: case ABILITY_UNUSED_7: - case ABILITY_UNUSED_8: continue; default: break; diff --git a/spells.h b/spells.h index 072eafc6..69954d52 100755 --- a/spells.h +++ b/spells.h @@ -1088,7 +1088,7 @@ #define ABILITY_SURVIVAL 29 /* survival, matches pfsrd */ #define ABILITY_SWIM 30 /* swim, matches pfsrd */ #define ABILITY_USE_MAGIC_DEVICE 31 /* use magic device, matches pfsrd */ -#define ABILITY_UNUSED_8 32 /* UNUSED - use to be use rope */ +#define ABILITY_LINGUISTICS 32 // Number of languages known #define ABILITY_PERFORM 33 /* perform, matches pfsrd */ /**/ #define END_GENERAL_ABILITIES 33 diff --git a/structs.h b/structs.h index aae46136..e6ae3ba7 100755 --- a/structs.h +++ b/structs.h @@ -328,8 +328,10 @@ #define HIST_CLANTALK 9 /**< Index to history of all 'clantalk' */ #define HIST_GSAY 10 /**< Index to history of all 'gsay' */ #define HIST_GTELL HIST_GSAY +#define HIST_OSAY 11 /**< Index to history of all 'osay' */ +#define HIST_RSAY 12 /**< Index to history of all 'rsay' */ /**/ -#define NUM_HIST 11 /**< Total number of history indexes */ +#define NUM_HIST 13 /**< Total number of history indexes */ #define HISTORY_SIZE 5 /**< Number of last commands kept in each history */ /* Group Defines */ @@ -4556,6 +4558,7 @@ struct level_data int tempCruelty; int dragonborn_draconic_ancestry; int high_elf_cantrip; // the cantrip selected that high elves can cast at will. Set in study menu + int languages[NUM_LANGUAGES]; }; /** The list element that makes up a list of characters following this diff --git a/study.c b/study.c index d695dd85..ce05dda1 100644 --- a/study.c +++ b/study.c @@ -479,6 +479,15 @@ void finalize_study(struct descriptor_data *d) ch->player_specials->saved.blackguard_cruelties[i] = TRUE; } + // Assign chosen languages + for (i = 0; i < NUM_LANGUAGES; i++) + { + if (LEVELUP(ch)->languages[i] == TRUE) + { + ch->player_specials->saved.languages_known[i] = TRUE; + } + } + /* set spells learned for domain */ assign_domain_spells(ch); @@ -1677,6 +1686,60 @@ static void select_paladin_mercies(struct descriptor_data *d) OLC_MODE(d) = STUDY_SELECT_PAL_MERCY; } +static void choose_languages(struct descriptor_data *d) +{ + get_char_colors(d->character); + clear_screen(d); + + struct char_data *ch = d->character; + + int i = 0, count = 0; + int langs_known = num_languages_learned(ch); + int langs_can_learn = MAX(0, GET_REAL_INT_BONUS(ch)) + MAX(0, GET_ABILITY(ch, ABILITY_LINGUISTICS)) + MAX(0, LEVELUP(ch)->skills[ABILITY_LINGUISTICS]); + + for (i = 0; i < NUM_LANGUAGES; i++) + { + if (LEVELUP(ch)->languages[i]) + langs_known++; + } + + write_to_output(d, + "\r\n-- %sSelect Languages%s\r\n" + "\r\n", + mgn, nrm); + + for (i = 0; i < NUM_LANGUAGES; i++) + { + if (!CAN_SPEAK(ch, i)) + { + if (LEVELUP(ch)->languages[i]) + send_to_char(ch, "\tM%-2d) %-35s \tn", i, languages[i]); + else + send_to_char(ch, "\tn%-2d) %-35s \tn", i, languages[i]); + if ((count % 2) == 1) + { + send_to_char(ch, "\r\n"); + } + count++; + } + } + + if ((count % 2) != 1) + { + send_to_char(ch, "\r\n"); + } + + write_to_output(d, "\r\n" + "%s -1%s) Quit\r\n" + "\r\n" + "%d language(s) can be selected\r\n" + "\r\n" + "Enter Choice : ", + grn, nrm, langs_can_learn - langs_known); + + OLC_MODE(d) = STUDY_CHOOSE_LANGUAGES; +} + static void select_blackguard_cruelties(struct descriptor_data *d) { get_char_colors(d->character); @@ -2099,7 +2162,6 @@ static void main_skills_disp_menu(struct descriptor_data *d) case ABILITY_UNUSED_5: case ABILITY_UNUSED_6: case ABILITY_UNUSED_7: - case ABILITY_UNUSED_8: continue; default: break; @@ -2226,6 +2288,7 @@ static void generic_main_disp_menu(struct descriptor_data *d) "%s D%s) Paladin Mercies%s\r\n" "%s E%s) Blackguard Cruelties%s\r\n" "%s F%s) Racial Abilities Selection%s\r\n" + "%s G%s) Languages%s\r\n" "\r\n" "%s R%s) Reset Character%s\r\n" "%s Q%s) Quit\r\n" @@ -2249,7 +2312,8 @@ static void generic_main_disp_menu(struct descriptor_data *d) MENU_OPT(has_alchemist_discoveries_unchosen(ch)), has_alchemist_discoveries_unchosen(ch) ? "" : "*", // C MENU_OPT(has_paladin_mercies_unchosen(ch)), has_paladin_mercies_unchosen(ch) ? "" : "*", // D MENU_OPT(has_blackguard_cruelties_unchosen(ch)), has_blackguard_cruelties_unchosen(ch) ? "" : "*", // E - MENU_OPT(has_racial_abils_unchosen(ch)), has_racial_abils_unchosen(ch) ? "" : "*", // D + MENU_OPT(has_racial_abils_unchosen(ch)), has_racial_abils_unchosen(ch) ? "" : "*", // F + MENU_OPT(has_unchosen_languages(ch)), has_unchosen_languages(ch) ? "" : "*", // G MENU_OPT(GET_LEVEL(ch) == 1), GET_LEVEL(ch) == 1 ? "" : "*", // R grn, nrm, (GET_PREMADE_BUILD_CLASS(ch) != CLASS_UNDEFINED) ? "(You are using premade build, options are limited!)" : ""); @@ -2359,7 +2423,6 @@ static void skfeat_disp_menu(struct descriptor_data *d) case ABILITY_UNUSED_5: case ABILITY_UNUSED_6: case ABILITY_UNUSED_7: - case ABILITY_UNUSED_8: continue; } write_to_output(d, "%d) %s\r\n", i, ability_names[i]); @@ -2665,6 +2728,19 @@ void study_parse(struct descriptor_data *d, char *arg) } break; + case 'g': + case 'G': + if (has_unchosen_languages(ch)) + { + choose_languages(d); + } + else + { + write_to_output(d, "That is an invalid choice!\r\n"); + generic_main_disp_menu(d); + } + break; + // reset levelup, level 1 only. case 'R': case 'r': @@ -2952,6 +3028,52 @@ void study_parse(struct descriptor_data *d, char *arg) OLC_MODE(d) = STUDY_CONFIRM_ADD_MERCY; break; + case STUDY_CHOOSE_LANGUAGES: + number = atoi(arg); + if (number == -1) + { + display_main_menu(d); + break; + } + + /* Check if the language is available. */ + if ((number < LANG_COMMON) || + (number >= NUM_LANGUAGES) || + (CAN_SPEAK(d->character, number))) + { + write_to_output(d, "Invalid language, try again.\r\n"); + break; + } + + if (LEVELUP(ch)->languages[number] == TRUE) + { + LEVELUP(ch)->languages[number] = FALSE; + send_to_char(ch, "You de-select language '%s'\r\n", languages[number]); + } + else + { + int langs_known = num_languages_learned(ch); + int langs_can_learn = MAX(0, GET_REAL_INT_BONUS(ch)) + MAX(0, GET_ABILITY(ch, ABILITY_LINGUISTICS)) + MAX(0, LEVELUP(ch)->skills[ABILITY_LINGUISTICS]); + for (i = 0; i < NUM_LANGUAGES; i++) + { + if (LEVELUP(ch)->languages[i]) + langs_known++; + } + + if ((langs_can_learn - langs_known) <= 0) + { + send_to_char(ch, "You cannot learn any more languages.\r\n"); + } + else + { + LEVELUP(ch)->languages[number] = TRUE; + send_to_char(ch, "You select language '%s'\r\n", languages[number]); + } + } + + choose_languages(d); + break; + case STUDY_SELECT_BG_CRUELTY: number = atoi(arg); if (number == -1) @@ -3205,7 +3327,6 @@ void study_parse(struct descriptor_data *d, char *arg) case ABILITY_UNUSED_5: case ABILITY_UNUSED_6: case ABILITY_UNUSED_7: - case ABILITY_UNUSED_8: write_to_output(d, "That is an invalid choice!\r\n"); skfeat_disp_menu(d); break; diff --git a/utils.c b/utils.c index 3f282b72..150de6a3 100755 --- a/utils.c +++ b/utils.c @@ -5141,6 +5141,22 @@ bool has_aura_of_evil(struct char_data *ch) return has_aura; } +bool can_speak_language(struct char_data *ch, int language) +{ + if (!ch) return false; + if (language < LANG_COMMON || language >= NUM_LANGUAGES) return false; + + if (GET_LEVEL(ch) >= LVL_IMMORT) return true; + if (language == LANG_COMMON) return true; + if (language == race_list[GET_REAL_RACE(ch)].racial_language) return true; + if (ch->player_specials->saved.languages_known[language]) return true; + if (language == LANG_DRUIDIC && CLASS_LEVEL(ch, CLASS_DRUID)) return true; + if (language == LANG_THEIVES_CANT && CLASS_LEVEL(ch, CLASS_ROGUE)) return true; + if (language == get_region_language(GET_REGION(ch))) return true; + + return false; +} + bool group_member_affected_by_spell(struct char_data *ch, int spellnum) { if (!ch) diff --git a/utils.h b/utils.h index e48bed29..24a4a171 100755 --- a/utils.h +++ b/utils.h @@ -144,6 +144,7 @@ bool affected_by_aura_of_righteousness(struct char_data *ch); bool is_fear_spell(int spellnum); char *apply_types_lowercase(int apply_type); bool can_learn_blackguard_cruelty(struct char_data *ch, int mercy); +bool can_speak_language(struct char_data *ch, int language); int num_blackguard_cruelties_known(struct char_data *ch); sbyte has_blackguard_cruelties_unchosen(struct char_data *ch); sbyte has_blackguard_cruelties_unchosen_study(struct char_data *ch);