Skip to content

Commit

Permalink
Added the linguistics skill, with each rank granting a new spoken lan…
Browse files Browse the repository at this point in the history
…guage.

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.
  • Loading branch information
GickerLDS committed Dec 2, 2022
1 parent 4f0d4e2 commit f1ef26a
Show file tree
Hide file tree
Showing 17 changed files with 461 additions and 15 deletions.
230 changes: 230 additions & 0 deletions act.comm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions act.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions act.offensive.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
37 changes: 37 additions & 0 deletions class.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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 */
Expand Down
3 changes: 2 additions & 1 deletion class.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
2 changes: 1 addition & 1 deletion constants.c
Original file line number Diff line number Diff line change
Expand Up @@ -3648,7 +3648,7 @@ const char *ability_names[] = {
"Survival",
"Swim",
"Use Magic Device",
"Unused8",
"Linguistics",
"Perform",
/*crafting*/
"woodworking",
Expand Down
3 changes: 2 additions & 1 deletion depend
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions interpreter.c
Original file line number Diff line number Diff line change
Expand Up @@ -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},*/
Expand Down Expand Up @@ -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},
Expand Down Expand Up @@ -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},
Expand Down
2 changes: 1 addition & 1 deletion mobact.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading

0 comments on commit f1ef26a

Please sign in to comment.