diff --git a/doc/sound.txt b/doc/sound.txt index b70755eff..3d88e9cd7 100644 --- a/doc/sound.txt +++ b/doc/sound.txt @@ -90,6 +90,9 @@ There are 4 distinct types of sound sound_triggers used by NetHack. (ambience_upate), likely with a different hero proximity value. + SOUND_TRIGGER_VERBAL Invoked by the core when someone (or something) + is speaking. + The types of sound sound_triggers supported by a particular soundlib implementation are specified in that library's soundlib file, which is usually @@ -109,17 +112,20 @@ the sound_triggers field of the sound_procs struct: void (*sound_play_usersound)(char *filename, int32_t volume, int32_t usidx); void (*sound_ambience)(int32_t ambienceid, int32_t ambience_action, - int32_t hero_proximity); + int32_t hero_proximity); + void (*sound_verbal)(char *text, int32_t gender, int32_t tone, + int32_t vol, int32_t moreinfo); }; -A sound library integration support file can implement one, two, three or -four of the sound trigger types. The more types of sound_triggers the +A sound library integration support file can implement one, two, three, four, +five, or six of the sound trigger types. The more types of sound_triggers the soundlib implements, the more full-featured the sound experience will be during the game. The values can be or'd together in the sound_triggers field initialization. SOUND_TRIGGER_USERSOUNDS | SOUND_TRIGGER_HEROMUSIC | SOUND_TRIGGER_ACHIEVEMENTS | SOUND_TRIGGER_SOUNDEFFECTS + | SOUND_TRIGGER_AMBIENCE | SOUND_TRIGGER_VERBAL II. Interface Specification @@ -248,6 +254,18 @@ sound_ambience(int32_t ambienceid, int32_t ambience_action, If the distance of the hero from the source of the ambience does matter, then a distance value will be in hero_proximity. +sound_verbal(char *text, int32_t gender, int32_t tone, int32_t vol, + int32_t moreinfo); + -- NetHack will call this function when it wants to pass text of + spoken language by a character or creature within the game. + -- text is a transcript of what has been spoken. + -- gender indicates MALE or FEMALE or NEUTRAL (either MALE + or FEMALE) voice. + -- tone indicates the tone of the voice. + -- vol is the volume (1% - 100%) for the sound. + -- moreinfo is used to provide additional information to the soundlib. + -- there may be some accessibility uses for this function. + III. Global variables @@ -264,6 +282,11 @@ gc.chosen_soundlib ga.active_soundlib a usersound mappings reference +iflags.sounds is the master on/off swith to control whether any audio +is produced by the soundlib interface + +iflags.voice is the master on/off switch for voices produced by +the soundlib interface. IV. Other related routines @@ -360,14 +383,15 @@ use the following guidelines: SOUNDID(myprefix), SOUND_TRIGGER_USERSOUNDS | SOUND_TRIGGER_HEROMUSIC | SOUND_TRIGGER_ACHIEVEMENTS |SOUND_TRIGGER_SOUNDEFFECTS - | SOUND_TRIGGER_AMBIENCE, + | SOUND_TRIGGER_AMBIENCE | SOUND_TRIGGER_VERBAL, myprefix_init_nhsound, myprefix_exit_nhsound, myprefix_achievement, myprefix_soundeffect, myprefix_hero_playnotes, myprefix_play_usersound, - myprefix_ambience), + myprefix_ambience, + myprefix_verbal), }; The first entry in this structure should be the SOUNDID(myprefix) @@ -526,6 +550,8 @@ static void sample_hero_playnotes(int32_t, const char *, int32_t); static void sample_play_usersound(char *, int32_t, int32_t); static void sample_ambience(int32_t ambienceid, int32_t ambience_action, int32_t hero_proximity); +static void (*sound_verbal)(char *text, int32_t gender, int32_t tone, + int32_t vol, int32_t moreinfo); struct sound_procs sample_procs = { SOUNDID(sample), @@ -539,6 +565,7 @@ struct sound_procs sample_procs = { sample_hero_playnotes, sample_play_usersound, sample_ambience, + sample_verbal }; static void @@ -588,5 +615,11 @@ sample_ambience(int32_t ambienceid, int32_t ambience_action, { } +static void +sample_verbal(char *text, int32_t gender, int32_t tone, + int32_t vol, int32_t moreinfo) +{ +} + /* end of sample.c */ -- >8 -- diff --git a/include/decl.h b/include/decl.h index 7a81872dc..4a89d1492 100644 --- a/include/decl.h +++ b/include/decl.h @@ -1588,6 +1588,9 @@ struct instance_globals_v { coordxy *viz_rmax; /* max could see indices */ boolean vision_full_recalc; + /* new stuff */ + struct sound_voice voice; + boolean havestate; unsigned long magic; /* validate that structure layout is preserved */ }; diff --git a/include/extern.h b/include/extern.h index 05b5eaea3..2ef0ad09b 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1012,6 +1012,7 @@ extern char *strip_newline(char *); extern char *stripchars(char *, const char *, const char *); extern char *stripdigits(char *); extern char *eos(char *); +extern const char *c_eos(const char *); extern unsigned Strlen_(const char *, const char *, int); extern boolean str_start_is(const char *, const char *, boolean); extern boolean str_end_is(const char *, const char *); @@ -2620,6 +2621,8 @@ extern char *get_sound_effect_filename(int32_t seidint, char *buf, size_t bufsz, int32_t); #endif extern char *base_soundname_to_filename(char *, char *, size_t, int32_t); +extern void set_voice(struct monst *, int32_t, int32_t, int32_t); +extern void sound_speak(const char *); /* ### sp_lev.c ### */ diff --git a/include/flag.h b/include/flag.h index 7edec9119..5a99da206 100644 --- a/include/flag.h +++ b/include/flag.h @@ -277,6 +277,7 @@ struct instance_flags { long hilite_delta; /* number of moves to leave a temp hilite lit */ long unhilite_deadline; /* time when oldest temp hilite should be unlit */ #endif + boolean voices; /* enable text-to-speech or other talking */ boolean zerocomp; /* write zero-compressed save files */ boolean rlecomp; /* alternative to zerocomp; run-length encoding * compression of levels when writing savefile */ diff --git a/include/hack.h b/include/hack.h index 327849ac5..b53f78b4a 100644 --- a/include/hack.h +++ b/include/hack.h @@ -497,6 +497,8 @@ typedef uint32_t mmflags_nht; /* makemon MM_ flags */ #define OVERRIDE_MSGTYPE 2 #define SUPPRESS_HISTORY 4 #define URGENT_MESSAGE 8 +#define PLINE_VERBALIZE 16 +#define PLINE_SPEECH 32 /* get_count flags */ #define GC_NOFLAGS 0 diff --git a/include/optlist.h b/include/optlist.h index b71417d8c..69d6bfe18 100644 --- a/include/optlist.h +++ b/include/optlist.h @@ -662,6 +662,8 @@ static int optfn_##a(int, int, boolean, char *, char *); NHOPTC(video_height, Advanced, 10, opt_in, set_gameview, No, Yes, No, No, NoAlias, "video height") #endif + NHOPTB(voices, Advanced, 0, opt_in, set_in_game, + Off, Yes, No, No, NoAlias, &iflags.voices, Term_Off) #ifdef TTY_TILES_ESCCODES NHOPTB(vt_tiledata, Advanced, 0, opt_in, set_in_config, Off, Yes, No, No, NoAlias, &iflags.vt_tiledata, Term_False) diff --git a/include/sndprocs.h b/include/sndprocs.h index 9460a143a..a095d7f75 100755 --- a/include/sndprocs.h +++ b/include/sndprocs.h @@ -53,6 +53,18 @@ struct sound_procs { void (*sound_play_usersound)(char *filename, int32_t volume, int32_t idx); void (*sound_ambience)(int32_t ambience_action, int32_t ambienceid, int32_t proximity); + void (*sound_verbal)(char *text, int32_t gender, int32_t tone, + int32_t vol, int32_t moreinfo); +}; + +struct sound_voice { + int32_t serialno; + int32_t gender; + int32_t tone; + int32_t volume; + int32_t moreinfo; + struct monst *mon; + const char *nameid; }; extern struct sound_procs sndprocs; @@ -67,7 +79,8 @@ extern struct sound_procs sndprocs; #define SOUND_TRIGGER_ACHIEVEMENTS 0x0004L #define SOUND_TRIGGER_SOUNDEFFECTS 0x0008L #define SOUND_TRIGGER_AMBIENCE 0x0010L - /* 27 free bits */ +#define SOUND_TRIGGER_VERBAL 0x0020L + /* 26 free bits */ extern struct sound_procs soundprocs; @@ -334,6 +347,15 @@ enum ambiences { amb_noambience, }; +enum voice_moreinfo { + voice_nothing_special, + voice_talking_artifact = 0x0001, + voice_deity = 0x0002, + voice_oracle = 0x0004, + voice_throne = 0x0008, + voice_death = 0x0010 +}; + enum achievements_arg2 { sa2_zero_invalid, sa2_splashscreen, sa2_newgame_nosplash, sa2_restoregame, sa2_xplevelup, sa2_xpleveldown, number_of_sa2_entries @@ -396,8 +418,6 @@ SoundAchievement(0, sa2_xpleveldown, level); (*soundprocs.sound_hero_playnotes)((instrument), (str), (vol)); \ } while(0) -/* void (*sound_achievement)(schar, schar, int32_t); */ - /* Player's perspective, not the hero's; no Deaf suppression */ #define SoundAchievement(arg1, arg2, avals) \ do { \ @@ -406,6 +426,21 @@ SoundAchievement(0, sa2_xpleveldown, level); (*soundprocs.sound_achievement)((arg1), (arg2), (avals)); \ } while(0) +/* sound_speak is in sound.c */ +#define SoundSpeak(text) \ + do { \ + if ((gp.pline_flags & (PLINE_VERBALIZE | PLINE_SPEECH)) != 0 \ + && soundprocs.sound_verbal && iflags.voices \ + && ((soundprocs.sound_triggers & SOUND_TRIGGER_VERBAL) != 0)) \ + sound_speak(text); \ + } while(0) + +/* set_voice is in sound.c */ +#define SetVoice(mon, tone, vol, moreinfo) \ + do { \ + set_voice(mon, tone, vol, moreinfo); \ + } while(0) + /* void (*sound_achievement)(schar, schar, int32_t); */ #ifdef SOUNDLIBONLY @@ -420,6 +455,8 @@ SoundAchievement(0, sa2_xpleveldown, level); #define Soundeffect(seid, vol) #define Hero_playnotes(instrument, str, vol) #define SoundAchievement(arg1, arg2, avals) +#define SoundSpeak(text) +#define SetVoice(mon, tone, vol, moreinfo) #ifdef SOUNDLIBONLY #undef SOUNDLIBONLY #endif diff --git a/sound/macsound/macsound.m b/sound/macsound/macsound.m index 9fd37a22e..37360b7de 100644 --- a/sound/macsound/macsound.m +++ b/sound/macsound/macsound.m @@ -11,6 +11,12 @@ /* #import */ #import +#define SPEECH_SOUNDS +#ifdef SPEECH_SOUNDS +#import +#endif + + /* * Sample sound interface for NetHack * @@ -27,9 +33,11 @@ static void macsound_soundeffect(char *, int32_t, int32_t); static void macsound_hero_playnotes(int32_t, const char *, int32_t); static void macsound_play_usersound(char *, int32_t, int32_t); static void macsound_ambience(int32_t, int32_t, int32_t); - +static void macsound_verbal(char *, int32_t, int32_t, int32_t, int32_t); static int affiliate(int32_t seid, const char *soundname); + + /* * Sound capabilities that can be enabled: * SOUND_TRIGGER_USERSOUNDS | SOUND_TRIGGER_HEROMUSIC @@ -40,7 +48,10 @@ static int affiliate(int32_t seid, const char *soundname); struct sound_procs macsound_procs = { SOUNDID(macsound), SOUND_TRIGGER_HEROMUSIC | SOUND_TRIGGER_SOUNDEFFECTS - | SOUND_TRIGGER_ACHIEVEMENTS, + | SOUND_TRIGGER_ACHIEVEMENTS +#ifdef SND_SPEECH + | SOUND_TRIGGER_VERBAL, +#endif macsound_init_nhsound, macsound_exit_nhsound, macsound_achievement, @@ -48,6 +59,7 @@ struct sound_procs macsound_procs = { macsound_hero_playnotes, macsound_play_usersound, macsound_ambience, + macsound_verbal, }; static void @@ -312,6 +324,36 @@ macsound_play_usersound(char *filename UNUSED, int volume UNUSED, int idx UNUSED } +#ifdef SND_SPEECH +#define SPEECHONLY +#else +#define SPEECHONLY UNUSED +#endif + +static void +macsound_verbal(char *text SPEECHONLY, int32_t gndr SPEECHONLY, int32_t tone_of_voice UNUSED, int32_t vol UNUSED, int32_t moreinfo UNUSED) +{ +#ifdef SND_SPEECH + NSMutableString *speechstring; + AVSpeechUtterance *text2speechutt; + AVSpeechSynthesizer *synthesizer; + + synthesizer = [[AVSpeechSynthesizer alloc]init]; + speechstring = [NSMutableString stringWithUTF8String:text]; + text2speechutt = [AVSpeechUtterance speechUtteranceWithString:speechstring]; + text2speechutt.rate = 0.50f; + text2speechutt.pitchMultiplier = 0.8f; + text2speechutt.postUtteranceDelay = 0.2f; + text2speechutt.volume = (float) vol / 100; + if (gndr > 0) + text2speechutt.voice = [AVSpeechSynthesisVoice voiceWithIdentifier:@"com.apple.ttsbundle.siri_female_en-GB_compact"]; + else + text2speechutt.voice = [AVSpeechSynthesisVoice voiceWithIdentifier:@"com.apple.ttsbundle.siri_male_en-GB_compact"]; + + [synthesizer speakUtterance:text2speechutt]; +#endif /* SND_SPEECH */ +} + static int affiliate(int32_t id, const char *soundname) { diff --git a/sound/windsound/windsound.c b/sound/windsound/windsound.c index aec6942a9..01e05b7e0 100644 --- a/sound/windsound/windsound.c +++ b/sound/windsound/windsound.c @@ -25,6 +25,9 @@ static void windsound_hero_playnotes(int32_t instrument, const char *str, int32_ static void windsound_play_usersound(char *, int32_t, int32_t); static void windsound_ambience(int32_t ambienceid, int32_t ambience_action, int32_t hero_proximity); +static void windsound_verbal(char *text, int32_t gender, int32_t tone, + int32_t vol, int32_t moreinfo); + /* supporting routines */ static void adjust_soundargs_for_compiler(int32_t *, DWORD *, char **); @@ -34,7 +37,7 @@ struct sound_procs windsound_procs = { SOUNDID(windsound), SOUND_TRIGGER_USERSOUNDS | SOUND_TRIGGER_SOUNDEFFECTS | SOUND_TRIGGER_HEROMUSIC | SOUND_TRIGGER_AMBIENCE - | SOUND_TRIGGER_ACHIEVEMENTS, + | SOUND_TRIGGER_ACHIEVEMENTS | SOUND_TRIGGER_VERBAL, windsound_init_nhsound, windsound_exit_nhsound, windsound_achievement, @@ -42,6 +45,7 @@ struct sound_procs windsound_procs = { windsound_hero_playnotes, windsound_play_usersound, windsound_ambience, + windsound_verbal, }; static void @@ -110,7 +114,13 @@ windsound_achievement(schar ach1, schar ach2, int32_t repeat) static void windsound_ambience(int32_t ambienceid, int32_t ambience_action, - int32_t hero_proximity) + int32_t hero_proximity) +{ +} + +static void +windsound_verbal(char *text, int32_t gender, int32_t tone, + int32_t vol, int32_t moreinfo) { } diff --git a/src/apply.c b/src/apply.c index 6c992c7f4..7d8c93b5d 100644 --- a/src/apply.c +++ b/src/apply.c @@ -1409,11 +1409,15 @@ use_candle(struct obj **optr) pline_The("new %s magically %s!", s, vtense(s, "ignite")); else if (!otmp->lamplit && was_lamplit) pline("%s out.", (obj->quan > 1L) ? "They go" : "It goes"); - if (obj->unpaid) + if (obj->unpaid) { + struct monst *shkp = shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE)); + + SetVoice(shkp, 0, 80, 0); verbalize("You %s %s, you bought %s!", otmp->lamplit ? "burn" : "use", (obj->quan > 1L) ? "them" : "it", (obj->quan > 1L) ? "them" : "it"); + } if (obj->quan < 7L && otmp->spe == 7) pline("%s now has seven%s candles attached.", The(xname(otmp)), otmp->lamplit ? " lit" : ""); @@ -1563,8 +1567,11 @@ catch_lit(struct obj *obj) if (obj->otyp == POT_OIL) makeknown(obj->otyp); if (carried(obj) && obj->unpaid && costly_spot(u.ux, u.uy)) { + struct monst *shkp = shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE)); + /* if it catches while you have it, then it's your tough luck */ check_unpaid(obj); + SetVoice(shkp, 0, 80, 0); verbalize("That's in addition to the cost of %s %s, of course.", yname(obj), (obj->quan == 1L) ? "itself" : "themselves"); @@ -1640,7 +1647,9 @@ use_lamp(struct obj *obj) if (obj->unpaid && costly_spot(u.ux, u.uy) && obj->age == 20L * (long) objects[obj->otyp].oc_cost) { const char *ithem = (obj->quan > 1L) ? "them" : "it"; + struct monst *shkp = shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE)); + SetVoice(shkp, 0, 80, 0); verbalize("You burn %s, you bought %s!", ithem, ithem); bill_dummy_object(obj); } @@ -1685,10 +1694,13 @@ light_cocktail(struct obj **optr) Blind ? "" : " It gives off a dim light."); if (obj->unpaid && costly_spot(u.ux, u.uy)) { + struct monst *shkp = shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE)); + /* Normally, we shouldn't both partially and fully charge * for an item, but (Yendorian Fuel) Taxes are inevitable... */ check_unpaid(obj); + SetVoice(shkp, 0, 80, 0); verbalize("That's in addition to the cost of the potion, of course."); bill_dummy_object(obj); } @@ -2141,12 +2153,20 @@ use_tinning_kit(struct obj *obj) /* Mark tinned tins. No spinach allowed... */ set_tin_variety(can, HOMEMADE_TIN); if (carried(corpse)) { - if (corpse->unpaid) + if (corpse->unpaid) { + struct monst *shkp = shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE)); + + SetVoice(shkp, 0, 80, 0); verbalize(you_buy_it); + } useup(corpse); } else { - if (costly_spot(corpse->ox, corpse->oy) && !corpse->no_charge) + if (costly_spot(corpse->ox, corpse->oy) && !corpse->no_charge) { + struct monst *shkp = shop_keeper(*in_rooms(corpse->ox, + corpse->oy, SHOPBASE)); + SetVoice(shkp, 0, 80, 0); verbalize(you_buy_it); + } useupf(corpse, 1L); } (void) hold_another_object(can, "You make, but cannot pick up, %s.", diff --git a/src/artifact.c b/src/artifact.c index 216c08e93..5f56d9ecf 100644 --- a/src/artifact.c +++ b/src/artifact.c @@ -2001,6 +2001,7 @@ arti_speak(struct obj *obj) if (!*line) line = "NetHack rumors file closed for renovation."; pline("%s:", Tobjnam(obj, "whisper")); + SetVoice((struct monst *) 0, 0, 80, voice_talking_artifact); verbalize1(line); return; } diff --git a/src/cmd.c b/src/cmd.c index d2aab6345..df96c79ff 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -6557,6 +6557,12 @@ yn_function( else cmdq_clear(CQ_CANNED); /* 'res' is ESC */ } else { +#ifdef SND_SPEECH + if ((gp.pline_flags & PLINE_SPEECH) != 0) { + sound_speak(query); + gp.pline_flags &= ~PLINE_SPEECH; + } +#endif res = (*windowprocs.win_yn_function)(query, resp, def); if (addcmdq) cmdq_add_key(CQ_REPEAT, res); diff --git a/src/decl.c b/src/decl.c index 60efafcbe..3af274ae5 100644 --- a/src/decl.c +++ b/src/decl.c @@ -825,6 +825,7 @@ const struct instance_globals_v g_init_v = { UNDEFINED_PTR, /* viz_rmin */ UNDEFINED_PTR, /* viz_rmax */ FALSE, /* vision_full_recalc */ + UNDEFINED_VALUES, /* voice */ TRUE, /* havestate*/ IVMAGIC /* v_magic to validate that structure layout has been preserved */ }; diff --git a/src/dig.c b/src/dig.c index da86bce26..ef9ca03c9 100644 --- a/src/dig.c +++ b/src/dig.c @@ -1251,6 +1251,7 @@ watch_dig(struct monst *mtmp, coordxy x, coordxy y, boolean zap) mtmp = get_iter_mons(watchman_canseeu); if (mtmp) { + SetVoice(mtmp, 0, 80, 0); if (zap || gc.context.digging.warned) { verbalize("Halt, vandal! You're under arrest!"); (void) angry_guards(!!Deaf); diff --git a/src/do_name.c b/src/do_name.c index ba1b35257..68f66567a 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -1316,8 +1316,10 @@ do_mgivenname(void) } else if (mtmp->isshk && !(Deaf || helpless(mtmp) || mtmp->data->msound <= MS_ANIMAL)) { - if (!alreadynamed(mtmp, monnambuf, buf)) + if (!alreadynamed(mtmp, monnambuf, buf)) { + SetVoice(mtmp, 0, 80, 0); verbalize("I'm %s, not %s.", shkname(mtmp), buf); + } } else if (mtmp->ispriest || mtmp->isminion || mtmp->isshk || mtmp->data == &mons[PM_GHOST]) { if (!alreadynamed(mtmp, monnambuf, buf)) diff --git a/src/dokick.c b/src/dokick.c index c310833c5..7869cec66 100644 --- a/src/dokick.c +++ b/src/dokick.c @@ -327,6 +327,7 @@ ghitm(register struct monst *mtmp, register struct obj *gold) if (!robbed) make_happy_shk(mtmp, FALSE); } else { + SetVoice(mtmp, 0, 80, 0); if (mtmp->mpeaceful) { ESHK(mtmp)->credit += value; You("have %ld %s in credit.", ESHK(mtmp)->credit, @@ -335,6 +336,7 @@ ghitm(register struct monst *mtmp, register struct obj *gold) verbalize("Thanks, scum!"); } } else if (mtmp->ispriest) { + SetVoice(mtmp, 0, 80, 0); if (mtmp->mpeaceful) verbalize("Thank you for your contribution."); else @@ -346,6 +348,7 @@ ghitm(register struct monst *mtmp, register struct obj *gold) out of the vault. If he did do that, player could try fighting, then weasle out of being killed by throwing his/her gold when losing. */ + SetVoice(mtmp, 0, 80, 0); verbalize(umoney ? "Drop the rest and follow me." : hidden_gold(TRUE) ? "You still have hidden gold. Drop it now." @@ -373,13 +376,16 @@ ghitm(register struct monst *mtmp, register struct obj *gold) } if (!mtmp->mpeaceful) { + SetVoice(mtmp, 0, 80, 0); if (goldreqd) verbalize("That's not enough, coward!"); else /* unbribeable (watchman) */ verbalize("I don't take bribes from scum like you!"); } else if (was_angry) { + SetVoice(mtmp, 0, 80, 0); verbalize("That should do. Now beat it!"); } else { + SetVoice(mtmp, 0, 80, 0); verbalize("Thanks for the tip, %s.", flags.female ? "lady" : "buddy"); } diff --git a/src/hack.c b/src/hack.c index 47f213fad..ea15c795e 100644 --- a/src/hack.c +++ b/src/hack.c @@ -3172,6 +3172,7 @@ check_special_room(boolean newlev) struct monst *oracle = monstinroom(&mons[PM_ORACLE], roomno); if (oracle) { + SetVoice(oracle, 0, 80, 0); if (!oracle->mpeaceful) verbalize("You're in Delphi, %s.", gp.plname); else diff --git a/src/hacklib.c b/src/hacklib.c index 2ad8d6c66..d616cd819 100644 --- a/src/hacklib.c +++ b/src/hacklib.c @@ -26,6 +26,7 @@ char * stripdigits (char *) unsigned Strlen_ (const char *str, const char *, int) char * eos (char *) + const char * c_eos (const char *) boolean str_start_is (const char *, const char *, boolean) boolean str_end_is (const char *, const char *) int str_lines_maxlen (const char *) @@ -224,6 +225,14 @@ eos(register char *s) return s; } +const char * +c_eos(const char *s) +{ + while (*s) + s++; /* s += strlen(s); */ + return s; +} + /* like strlen(3) but returns unsigned and panics if string is unreasonably long */ unsigned Strlen_(const char *str, const char *file, int line){ diff --git a/src/lock.c b/src/lock.c index d17320cd9..77ca57e8c 100644 --- a/src/lock.c +++ b/src/lock.c @@ -552,11 +552,13 @@ pick_lock( if (mtmp && canseemon(mtmp) && M_AP_TYPE(mtmp) != M_AP_FURNITURE && M_AP_TYPE(mtmp) != M_AP_OBJECT) { if (picktyp == CREDIT_CARD - && (mtmp->isshk || mtmp->data == &mons[PM_ORACLE])) + && (mtmp->isshk || mtmp->data == &mons[PM_ORACLE])) { + SetVoice(mtmp, 0, 80, 0); verbalize("No checks, no credit, no problem."); - else + } else { pline("I don't think %s would appreciate that.", mon_nam(mtmp)); + } return PICKLOCK_LEARNED_SOMETHING; } else if (mtmp && is_door_mappear(mtmp)) { /* "The door actually was a !" */ diff --git a/src/mail.c b/src/mail.c index a73e0dcd4..09daa427b 100644 --- a/src/mail.c +++ b/src/mail.c @@ -332,6 +332,7 @@ md_rush(struct monst *md, if (fx == tx && fy == ty) break; + SetVoice(md, 0, 80, 0); if ((mon = m_at(fx, fy)) != 0) /* save monster at this position */ verbalize1(md_exclamations()); else if (u_at(fx, fy)) @@ -363,6 +364,7 @@ md_rush(struct monst *md, remove_monster(fx, fy); place_monster(md, fx, fy); /* display md with text below */ newsym(fx, fy); + SetVoice(md, 0, 80, 0); verbalize("This place's too crowded. I'm outta here."); remove_monster(fx, fy); @@ -403,6 +405,7 @@ newmail(struct mail_info *info) goto go_back; message_seen = TRUE; + SetVoice(md, 0, 80, 0); verbalize("%s, %s! %s.", Hello(md), gp.plname, info->display_txt); if (info->message_typ) { @@ -413,8 +416,10 @@ newmail(struct mail_info *info) if (info->response_cmd) new_omailcmd(obj, info->response_cmd); - if (!next2u(md->mx, md->my)) + if (!next2u(md->mx, md->my)) { + SetVoice(md, 0, 80, 0); verbalize("Catch!"); + } display_nhwindow(WIN_MESSAGE, FALSE); obj = hold_another_object(obj, "Oops!", (const char *) 0, (const char *) 0); diff --git a/src/mcastu.c b/src/mcastu.c index fdb297113..c1390e091 100644 --- a/src/mcastu.c +++ b/src/mcastu.c @@ -419,6 +419,7 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) if (!count) { ; /* nothing was created? */ } else if (mtmp->iswiz) { + SetVoice(mtmp, 0, 80, 0); verbalize("Destroy the thief, my pet%s!", plur(count)); } else { boolean one = (count == 1); diff --git a/src/mhitu.c b/src/mhitu.c index a7158cd5d..9d4e9ee6e 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -1866,6 +1866,7 @@ doseduce(struct monst *mon) " looks pretty. May I have it?\"", ring, xname, simpleonames, "ring"); makeknown(RIN_ADORNMENT); + SetVoice(mon, 0, 80, 0); if (y_n(qbuf) == 'n') continue; } else @@ -1896,6 +1897,7 @@ doseduce(struct monst *mon) " looks pretty. Would you wear it for me?\"", ring, xname, simpleonames, "ring"); makeknown(RIN_ADORNMENT); + SetVoice(mon, 0, 80, 0); if (y_n(qbuf) == 'n') continue; } else { @@ -1969,6 +1971,7 @@ doseduce(struct monst *mon) if (uarm || uarmc) { if (!Deaf) { if (!(ld() && mon->female)) { + SetVoice(mon, 0, 80, 0); verbalize("You're such a %s; I wish...", flags.female ? "sweet lady" : "nice guy"); } else { @@ -2112,6 +2115,7 @@ doseduce(struct monst *mon) if (cost > umoney) cost = umoney; if (!cost) { + SetVoice(mon, 0, 80, 0); verbalize("It's on the house!"); } else { pline("%s takes %ld %s for services rendered!", noit_Monnam(mon), @@ -2146,6 +2150,7 @@ mayberem(struct monst *mon, if (Deaf) { pline("%s takes off your %s.", seducer, str); } else if (rn2(20) < ACURR(A_CHA)) { + SetVoice(mon, 0, 80, 0); /* y_n a.k.a. yn_function is set up for this */ Sprintf(qbuf, "\"Shall I remove your %s, %s?\"", str, (!rn2(2) ? "lover" : !rn2(2) ? "dear" : "sweetheart")); if (y_n(qbuf) == 'n') @@ -2155,6 +2160,7 @@ mayberem(struct monst *mon, Sprintf(hairbuf, "let me run my fingers through your %s", body_part(HAIR)); + SetVoice(mon, 0, 80, 0); verbalize("Take off your %s; %s.", str, (obj == uarm) ? "let's get a little closer" diff --git a/src/minion.c b/src/minion.c index 1f7e67e9c..28ea2f059 100644 --- a/src/minion.c +++ b/src/minion.c @@ -241,6 +241,7 @@ summon_minion(aligntyp alignment, boolean talk) else You_feel("%s booming voice:", s_suffix(align_gname(alignment))); + SetVoice(mon, 0, 80, 0); verbalize("Thou shalt pay for thine indiscretion!"); if (canspotmon(mon)) pline("%s appears before you.", Amonnam(mon)); @@ -464,6 +465,7 @@ lose_guardian_angel(struct monst *mon) /* if null, angel hasn't been created yet if (canspotmon(mon)) { if (!Deaf) { pline("%s rebukes you, saying:", Monnam(mon)); + SetVoice(mon, 0, 80, 0); verbalize("Since you desire conflict, have some more!"); } else { pline("%s vanishes!", Monnam(mon)); @@ -496,6 +498,7 @@ gain_guardian_angel(void) pline("A voice booms:"); else You_feel("a booming voice:"); + SetVoice((struct monst *) 0, 0, 80, voice_deity); verbalize("Thy desire for conflict shall be fulfilled!"); /* send in some hostile angels instead */ lose_guardian_angel((struct monst *) 0); @@ -504,6 +507,7 @@ gain_guardian_angel(void) pline("A voice whispers:"); else You_feel("a soft voice:"); + SetVoice((struct monst *) 0, 0, 80, voice_deity); verbalize("Thou hast been worthy of me!"); mm.x = u.ux; mm.y = u.uy; diff --git a/src/mkobj.c b/src/mkobj.c index cdfb405a6..62de763dd 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -784,6 +784,8 @@ costly_alteration(struct obj *obj, int alter_type) case OBJ_INVENT: if (learn_bknown) set_bknown(obj, 1); + if (shkp) + SetVoice(shkp, 0, 80, 0); verbalize("You %s %s %s, you pay for %s!", alteration_verbs[alter_type], those, simpleonames(obj), them); @@ -793,6 +795,8 @@ costly_alteration(struct obj *obj, int alter_type) if (learn_bknown) obj->bknown = 1; /* ok to bypass set_bknown() here */ if (costly_spot(u.ux, u.uy) && objroom == *u.ushops) { + if (shkp) + SetVoice(shkp, 0, 80, 0); verbalize("You %s %s, you pay for %s!", alteration_verbs[alter_type], those, them); bill_dummy_object(obj); diff --git a/src/mon.c b/src/mon.c index 7871587d8..53bbc7508 100644 --- a/src/mon.c +++ b/src/mon.c @@ -3710,6 +3710,7 @@ peacefuls_respond(struct monst *mtmp) buf[0] = '\0'; if (humanoid(mon->data) || mon->isshk || mon->ispriest) { if (is_watch(mon->data)) { + SetVoice(mon, 0, 80, 0); verbalize("Halt! You're under arrest!"); (void) angry_guards(!!Deaf); } else { diff --git a/src/monmove.c b/src/monmove.c index ee087099a..1eecb905b 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -96,9 +96,10 @@ mon_yells(struct monst* mon, const char* shout) if (canspotmon(mon)) { pline("%s yells:", Amonnam(mon)); } else { - Soundeffect(se_someone_yells, 75); + /* Soundeffect(se_someone_yells, 75); */ You_hear("someone yell:"); } + SetVoice(mon, 0, 80, 0); verbalize1(shout); } } @@ -404,6 +405,7 @@ monflee( pline("%s flees from the painful light of %s.", Monnam(mtmp), lsrc); } else { + SetVoice(mtmp, 0, 80, 0); verbalize("Bright light!"); } } else { @@ -1313,8 +1315,10 @@ m_move(register struct monst* mtmp, register int after) #ifdef MAIL_STRUCTURES if (ptr == &mons[PM_MAIL_DAEMON]) { - if (!Deaf && canseemon(mtmp)) + if (!Deaf && canseemon(mtmp)) { + SetVoice(mtmp, 0, 80, 0); verbalize("I'm late!"); + } mongone(mtmp); return MMOVE_DIED; } diff --git a/src/mplayer.c b/src/mplayer.c index 30870c89d..c5b99af40 100644 --- a/src/mplayer.c +++ b/src/mplayer.c @@ -368,6 +368,7 @@ mplayer_talk(register struct monst* mtmp) if (mtmp->mpeaceful) return; /* will drop to humanoid talk */ + SetVoice(mtmp, 0, 80, 0); verbalize("Talk? -- %s", mtmp->data == &mons[gu.urole.mnum] ? same_class_msg[rn2(3)] : other_class_msg[rn2(3)]); diff --git a/src/muse.c b/src/muse.c index 5507fcc68..eb95611c6 100644 --- a/src/muse.c +++ b/src/muse.c @@ -104,6 +104,7 @@ precheck(struct monst *mon, struct obj *obj) pline("%s speaks.", vis ? Monnam(mtmp) : Something); /* I suspect few players will be upset that monsters */ /* can't wish for wands of death here.... */ + SetVoice(mtmp, 0, 80, 0); if (rn2(2)) { verbalize("You freed me!"); mtmp->mpeaceful = 1; @@ -1730,11 +1731,13 @@ use_offensive(struct monst *mtmp) return (DEADMONSTER(mtmp)) ? 1 : 2; } /* case MUSE_SCR_EARTH */ case MUSE_CAMERA: { - if (Hallucination) + if (Hallucination) { + SetVoice(mtmp, 0, 80, 0); verbalize("Say cheese!"); - else + } else { pline("%s takes a picture of you with %s!", Monnam(mtmp), an(xname(otmp))); + } gm.m_using = TRUE; if (!Blind) { You("are blinded by the flash of light!"); diff --git a/src/pickup.c b/src/pickup.c index 0fc66b480..183080b47 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -2196,6 +2196,7 @@ reverse_loot(void) coffers = otmp; if (coffers) { + SetVoice((struct monst *) 0, 0, 80, 0); verbalize("Thank you for your contribution to reduce the debt."); freeinv(goldob); (void) add_to_container(coffers, goldob); diff --git a/src/pline.c b/src/pline.c index 473d06ebb..2552647df 100644 --- a/src/pline.c +++ b/src/pline.c @@ -74,8 +74,8 @@ putmesg(const char *line) if ((gp.pline_flags & SUPPRESS_HISTORY) != 0 && (windowprocs.wincap2 & WC2_SUPPRESS_HIST) != 0) attr |= ATR_NOHISTORY; - putstr(WIN_MESSAGE, attr, line); + SoundSpeak(line); } static void vpline(const char *, va_list); @@ -192,8 +192,11 @@ vpline(const char *line, va_list the_args) (void) strncpy(gp.prevmsg, line, BUFSZ), gp.prevmsg[BUFSZ - 1] = '\0'; if (msgtyp == MSGTYP_STOP) display_nhwindow(WIN_MESSAGE, TRUE); /* --more-- */ - pline_done: +#ifdef SND_SPEECH + /* clear the SPEECH flag so caller never has to */ + gp.pline_flags &= ~PLINE_SPEECH; +#endif --in_pline; } @@ -386,11 +389,13 @@ verbalize(const char *line, ...) char *tmp; va_start(the_args, line); + gp.pline_flags |= PLINE_VERBALIZE; tmp = You_buf((int) strlen(line) + sizeof "\"\""); Strcpy(tmp, "\""); Strcat(tmp, line); Strcat(tmp, "\""); vpline(tmp, the_args); + gp.pline_flags &= ~PLINE_VERBALIZE; va_end(the_args); } diff --git a/src/potion.c b/src/potion.c index 83acf51db..54968fb6a 100644 --- a/src/potion.c +++ b/src/potion.c @@ -2720,6 +2720,7 @@ djinni_from_bottle(struct obj *obj) chance = (chance == 0) ? rn2(4) : 4; /* 0,1,2,3,4: b=80%,5,5,5,5; nc=20%,20,20,20,20; c=5%,5,5,5,80 */ + SetVoice(mtmp, 0, 80, 0); switch (chance) { case 0: verbalize("I am in your debt. I will grant one wish!"); diff --git a/src/pray.c b/src/pray.c index b7b4e609d..2f9be4e6c 100644 --- a/src/pray.c +++ b/src/pray.c @@ -660,10 +660,12 @@ god_zaps_you(aligntyp resp_god) } if (Is_astralevel(&u.uz) || Is_sanctum(&u.uz)) { /* one more try for high altars */ + SetVoice((struct monst *) 0, 0, 80, voice_deity); verbalize("Thou cannot escape my wrath, mortal!"); summon_minion(resp_god, FALSE); summon_minion(resp_god, FALSE); summon_minion(resp_god, FALSE); + SetVoice((struct monst *) 0, 0, 80, voice_deity); verbalize("Destroy %s, my servants!", uhim()); } } @@ -715,6 +717,7 @@ angrygods(aligntyp resp_god) ? "hast strayed from the path" : "art arrogant", gy.youmonst.data->mlet == S_HUMAN ? "mortal" : "creature"); + SetVoice((struct monst *) 0, 0, 80, voice_deity); verbalize("Thou must relearn thy lessons!"); (void) adjattrib(A_WIS, -1, FALSE); losexp((char *) 0); @@ -735,6 +738,7 @@ angrygods(aligntyp resp_god) case 7: case 8: godvoice(resp_god, (char *) 0); + SetVoice((struct monst *) 0, 0, 80, voice_deity); verbalize("Thou durst %s me?", (on_altar() && (a_align(u.ux, u.uy) != resp_god)) ? "scorn" @@ -811,6 +815,7 @@ gcrownu(void) switch (u.ualign.type) { case A_LAWFUL: u.uevent.uhand_of_elbereth = 1; + SetVoice((struct monst *) 0, 0, 80, voice_deity); verbalize("I crown thee... The Hand of Elbereth!"); livelog_printf(LL_DIVINEGIFT, "was crowned \"The Hand of Elbereth\" by %s", @@ -821,6 +826,7 @@ gcrownu(void) in_hand = u_wield_art(ART_VORPAL_BLADE); already_exists = exist_artifact(LONG_SWORD, artiname(ART_VORPAL_BLADE)); + SetVoice((struct monst *) 0, 0, 80, voice_deity); verbalize("Thou shalt be my Envoy of Balance!"); livelog_printf(LL_DIVINEGIFT, "became %s Envoy of Balance", s_suffix(u_gname())); @@ -832,6 +838,7 @@ gcrownu(void) what = (((already_exists && !in_hand) || class_gift != STRANGE_OBJECT) ? "take lives" : "steal souls"); + SetVoice((struct monst *) 0, 0, 80, voice_deity); verbalize("Thou art chosen to %s for My Glory!", what); livelog_printf(LL_DIVINEGIFT, "was chosen to %s for the Glory of %s", what, u_gname()); @@ -1187,9 +1194,11 @@ pleased(aligntyp g_align) if (!u.uevent.uopened_dbridge && !u.uevent.gehennom_entered) { if (u.uevent.uheard_tune < 1) { godvoice(g_align, (char *) 0); + SetVoice((struct monst *) 0, 0, 80, voice_deity); verbalize("Hark, %s!", (gy.youmonst.data->mlet == S_HUMAN) ? "mortal" : "creature"); + SetVoice((struct monst *) 0, 0, 80, voice_deity); verbalize( "To enter the castle, thou must play the right tune!"); u.uevent.uheard_tune++; @@ -1293,6 +1302,7 @@ pleased(aligntyp g_align) u.ublessed++; pline(msg, "my protection"); } + SetVoice((struct monst *) 0, 0, 80, voice_deity); verbalize("Use it wisely in my name!"); break; } @@ -1525,6 +1535,7 @@ offer_real_amulet(struct obj *otmp, aligntyp altaralign) pline("An invisible choir sings, and you are bathed in radiance..."); godvoice(altaralign, "Mortal, thou hast done well!"); display_nhwindow(WIN_MESSAGE, FALSE); + SetVoice((struct monst *) 0, 0, 80, voice_deity); verbalize( "In return for thy service, I grant thee the gift of Immortality!"); You("ascend to the status of Demigod%s...", @@ -2472,6 +2483,7 @@ altar_wrath(coordxy x, coordxy y) : "Despite your deafness, you seem to hear", align_gname(altaralign), !Deaf ? "?) whispers" : " say"); + SetVoice((struct monst *) 0, 0, 80, voice_deity); verbalize("Thou shalt pay, infidel!"); /* higher luck is more likely to be reduced; as it approaches -5 the chance to lose another point drops down, eventually to 0 */ diff --git a/src/priest.c b/src/priest.c index b1075961e..12471408c 100644 --- a/src/priest.c +++ b/src/priest.c @@ -464,6 +464,7 @@ intemple(int roomno) msg1 = buf; } if (msg1 && can_speak && !Deaf) { + SetVoice(priest, 0, 80, 0); verbalize1(msg1); if (msg2) verbalize1(msg2); @@ -586,6 +587,7 @@ priest_talk(struct monst *priest) priest->mcanmove = 1; } priest->mpeaceful = 0; + SetVoice(priest, 0, 80, 0); verbalize1(cranky_msg[rn2(3)]); return; } @@ -593,6 +595,7 @@ priest_talk(struct monst *priest) /* you desecrated the temple and now you want to chat? */ if (priest->mpeaceful && *in_rooms(priest->mx, priest->my, TEMPLE) && !has_shrine(priest)) { + SetVoice(priest, 0, 80, 0); verbalize( "Begone! Thou desecratest this holy place with thy presence."); priest->mpeaceful = 0; @@ -621,18 +624,22 @@ priest_talk(struct monst *priest) pline("%s asks you for a contribution for the temple.", Monnam(priest)); if ((offer = bribe(priest)) == 0) { + SetVoice(priest, 0, 80, 0); verbalize("Thou shalt regret thine action!"); if (coaligned) adjalign(-1); } else if (offer < (u.ulevel * 200)) { if (money_cnt(gi.invent) > (offer * 2L)) { + SetVoice(priest, 0, 80, 0); verbalize("Cheapskate."); } else { + SetVoice(priest, 0, 80, 0); verbalize("I thank thee for thy contribution."); /* give player some token */ exercise(A_WIS, TRUE); } } else if (offer < (u.ulevel * 400)) { + SetVoice(priest, 0, 80, 0); verbalize("Thou art indeed a pious individual."); if (money_cnt(gi.invent) < (offer * 2L)) { if (coaligned && u.ualign.record <= ALGN_SINNED) @@ -648,6 +655,7 @@ priest_talk(struct monst *priest) && (!(HProtection & INTRINSIC) || (u.ublessed < 20 && (u.ublessed < 9 || !rn2(u.ublessed))))) { + SetVoice(priest, 0, 80, 0); verbalize("Thou hast been rewarded for thy devotion."); if (!(HProtection & INTRINSIC)) { HProtection |= FROMOUTSIDE; @@ -656,6 +664,7 @@ priest_talk(struct monst *priest) } else u.ublessed++; } else { + SetVoice(priest, 0, 80, 0); verbalize("Thy selfless generosity is deeply appreciated."); if (money_cnt(gi.invent) < (offer * 2L) && coaligned) { if (strayed && (gm.moves - u.ucleansed) > 5000L) { diff --git a/src/quest.c b/src/quest.c index c9fcbfc53..c1029c263 100644 --- a/src/quest.c +++ b/src/quest.c @@ -396,6 +396,7 @@ prisoner_speaks(struct monst *mtmp) /* Awaken the prisoner */ if (canseemon(mtmp)) pline("%s speaks:", Monnam(mtmp)); + SetVoice(mtmp, 0, 80, 0); verbalize("I'm finally free!"); mtmp->mstrategy &= ~STRAT_WAITMASK; mtmp->mpeaceful = 1; diff --git a/src/read.c b/src/read.c index 7c0a83418..76790e192 100644 --- a/src/read.c +++ b/src/read.c @@ -2733,6 +2733,7 @@ do_genocide( */ if (Verbose(3, do_genocide)) pline("A thunderous voice booms through the caverns:"); + SetVoice((struct monst *) 0, 0, 80, voice_deity); verbalize("No, mortal! That will not be done."); } continue; diff --git a/src/rumors.c b/src/rumors.c index 8b41a3feb..ccecad594 100644 --- a/src/rumors.c +++ b/src/rumors.c @@ -546,6 +546,7 @@ outrumor( (!rn2(4) ? "offhandedly " : (!rn2(3) ? "casually " : (rn2(2) ? "nonchalantly " : "")))); + SetVoice((struct monst *) 0, 0, 80, voice_oracle); verbalize1(line); /* [WIS exercized by getrumor()] */ return; diff --git a/src/shk.c b/src/shk.c index 75dbbd06b..6512df649 100644 --- a/src/shk.c +++ b/src/shk.c @@ -483,15 +483,17 @@ u_left_shop(char *leavestring, boolean newlev) * Try to intimidate him into paying his bill */ boolean not_upset = !eshkp->surcharge; - if (!Deaf && !muteshk(shkp)) + if (!Deaf && !muteshk(shkp)) { + SetVoice(shkp, 0, 80, 0); verbalize(not_upset ? "%s! Please pay before leaving." : "%s! Don't you leave without paying!", gp.plname); - else + } else { pline("%s %s that you need to pay before leaving%s", Shknam(shkp), not_upset ? "points out" : "makes it clear", not_upset ? "." : "!"); + } return; } @@ -673,31 +675,37 @@ u_entered_shop(char* enterstring) if (Invis) { pline("%s senses your presence.", Shknam(shkp)); - if (!Deaf && !muteshk(shkp)) + if (!Deaf && !muteshk(shkp)) { + SetVoice(shkp, 0, 80, 0); verbalize("Invisible customers are not welcome!"); - else + } else { pline("%s stands firm as if %s knows you are there.", Shknam(shkp), noit_mhe(shkp)); + } return; } rt = gr.rooms[*enterstring - ROOMOFFSET].rtype; if (ANGRY(shkp)) { - if (!Deaf && !muteshk(shkp)) + if (!Deaf && !muteshk(shkp)) { + SetVoice(shkp, 0, 80, 0); verbalize("So, %s, you dare return to %s %s?!", gp.plname, s_suffix(shkname(shkp)), shtypes[rt - SHOPBASE].name); - else + } else { pline("%s seems %s over your return to %s %s!", Shknam(shkp), angrytexts[rn2(SIZE(angrytexts))], noit_mhis(shkp), shtypes[rt - SHOPBASE].name); + } } else if (eshkp->surcharge) { - if (!Deaf && !muteshk(shkp)) + if (!Deaf && !muteshk(shkp)) { + SetVoice(shkp, 0, 80, 0); verbalize("Back again, %s? I've got my %s on you.", gp.plname, mbodypart(shkp, EYE)); - else + } else { pline_The("atmosphere at %s %s seems unwelcoming.", s_suffix(shkname(shkp)), shtypes[rt - SHOPBASE].name); + } } else if (eshkp->robbed) { if (!Deaf) { Soundeffect(se_mutter_imprecations, 50); @@ -708,15 +716,17 @@ u_entered_shop(char* enterstring) Shknam(shkp), noit_mhis(shkp)); } } else { - if (!Deaf && !muteshk(shkp)) + if (!Deaf && !muteshk(shkp)) { + set_voice(shkp, 0, 80, 0); verbalize("%s, %s! Welcome%s to %s %s!", Hello(shkp), gp.plname, eshkp->visitct++ ? " again" : "", s_suffix(shkname(shkp)), shtypes[rt - SHOPBASE].name); - else + } else { You("enter %s %s%s!", s_suffix(shkname(shkp)), shtypes[rt - SHOPBASE].name, eshkp->visitct++ ? " again" : ""); + } } /* can't do anything about blocking if teleported in */ if (!inside_shop(u.ux, u.uy)) { @@ -746,27 +756,31 @@ u_entered_shop(char* enterstring) if (!Blind) makeknown(DWARVISH_MATTOCK); } - if (!Deaf && !muteshk(shkp)) + if (!Deaf && !muteshk(shkp)) { + SetVoice(shkp, 0, 80, 0); verbalize(not_upset ? "Will you please leave your %s%s outside?" : "Leave the %s%s outside.", tool, plur(cnt)); - else + } else { pline("%s %s to let you in with your %s%s.", Shknam(shkp), not_upset ? "is hesitant" : "refuses", tool, plur(cnt)); + } should_block = TRUE; } else if (u.usteed) { - if (!Deaf && !muteshk(shkp)) + if (!Deaf && !muteshk(shkp)) { + SetVoice(shkp, 0, 80, 0); verbalize(not_upset ? "Will you please leave %s outside?" : "Leave %s outside.", y_monnam(u.usteed)); - else + } else { pline("%s %s to let you in while you're riding %s.", Shknam(shkp), not_upset ? "doesn't want" : "refuses", y_monnam(u.usteed)); + } should_block = TRUE; } else { should_block = @@ -794,14 +808,16 @@ pick_pick(struct obj* obj) /* if you bring a sack of N picks into a shop to sell, don't repeat this N times when they're taken out */ if (gm.moves != pickmovetime) { - if (!Deaf && !muteshk(shkp)) + if (!Deaf && !muteshk(shkp)) { + SetVoice(shkp, 0, 80, 0); verbalize("You sneaky %s! Get out of here with that pick!", cad(FALSE)); - else + } else { pline("%s %s your pick!", Shknam(shkp), haseyes(shkp->data) ? "glares at" : "is dismayed because of"); + } } pickmovetime = gm.moves; } @@ -1688,16 +1704,18 @@ dopay(void) update_inventory(); /* Done in dopayobj() if itemize. */ } if (!ANGRY(shkp) && paid) { - if (!Deaf && !muteshk(shkp)) + if (!Deaf && !muteshk(shkp)) { + SetVoice(shkp, 0, 80, 0); verbalize("Thank you for shopping in %s %s%s", s_suffix(shkname(shkp)), shtypes[eshkp->shoptype - SHOPBASE].name, !eshkp->surcharge ? "!" : "."); - else + } else { pline("%s nods%s at you for shopping in %s %s%s", Shknam(shkp), !eshkp->surcharge ? " appreciatively" : "", noit_mhis(shkp), shtypes[eshkp->shoptype - SHOPBASE].name, !eshkp->surcharge ? "!" : "."); + } } return ECMD_TIME; } @@ -1763,6 +1781,7 @@ dopayobj( } else if (quan < bp->bquan && !consumed) { /* partly used goods */ obj->quan = bp->bquan - save_quan; /* used up amount */ if (!Deaf && !muteshk(shkp)) { + SetVoice(shkp, 0, 80, 0); verbalize("%s for the other %s before buying %s.", ANGRY(shkp) ? "Pay" : "Please pay", simpleonames(obj), /* short name suffices */ @@ -2449,22 +2468,27 @@ special_stock( Shknam(shkp), (obj->spe < 7) ? "horrified" : "concerned"); } else { + SetVoice(shkp, 0, 80, 0); verbalize("No thanks, I'd hang onto that if I were you."); - if (obj->spe < 7) + if (obj->spe < 7) { + SetVoice(shkp, 0, 80, 0); verbalize( "You'll need %d%s candle%s to go along with it.", (7 - obj->spe), (obj->spe > 0) ? " more" : "", plur(7 - obj->spe)); + } /* [what if hero is already carrying enough candles? should Izchak explain how to attach them instead?] */ } } else { - if (!Deaf && !muteshk(shkp)) + if (!Deaf && !muteshk(shkp)) { + SetVoice(shkp, 0, 80, 0); verbalize("I won't stock that. Take it out of here!"); - else + } else { pline("%s shakes %s %s in refusal.", Shknam(shkp), noit_mhis(shkp), mbodypart(shkp, HEAD)); + } } } return TRUE; @@ -2885,6 +2909,7 @@ addtobill( Strcat(buf, "; only"); } obj->quan = 1L; /* fool xname() into giving singular */ + set_voice(shkp, 0, 80, 0); pline("%s %ld %s %s %s%s.\"", buf, ltmp, currency(ltmp), (save_quan > 1L) ? "per" : (contentscount && !obj->unpaid) @@ -2895,14 +2920,16 @@ addtobill( obj->quan = save_quan; } } else if (!silent) { - if (ltmp) + if (ltmp) { + set_voice(shkp, 0, 80, 0); pline_The("list price of %s%s%s is %ld %s%s.", (contentscount && !obj->unpaid) ? the_contents_of : "", the(xname(obj)), (contentscount && obj->unpaid) ? and_its_contents : "", ltmp, currency(ltmp), (obj->quan > 1L) ? " each" : ""); - else + } else { pline("%s does not notice.", Shknam(shkp)); + } } } @@ -3299,10 +3326,12 @@ sellobj( eshkp = ESHK(shkp); if (ANGRY(shkp)) { /* they become shop-objects, no pay */ - if (!Deaf && !muteshk(shkp)) + if (!Deaf && !muteshk(shkp)) { + SetVoice(shkp, 0, 80, 0); verbalize("Thank you, scum!"); - else + } else { pline("%s smirks with satisfaction.", Shknam(shkp)); + } subfrombill(obj, shkp); return; } @@ -3314,9 +3343,11 @@ sellobj( offer += cgold; if ((eshkp->robbed -= offer < 0L)) eshkp->robbed = 0L; - if (offer && !Deaf && !muteshk(shkp)) + if (offer && !Deaf && !muteshk(shkp)) { + SetVoice(shkp, 0, 80, 0); verbalize( "Thank you for your contribution to restock this recently plundered shop."); + } subfrombill(obj, shkp); return; } @@ -3633,8 +3664,10 @@ shkcatch( /* if it is the shk's pos, you hit and anger him */ && (shkp->mx != x || shkp->my != y)) { if (mnearto(shkp, x, y, TRUE, RLOC_NOMSG) == 2 - && !Deaf && !muteshk(shkp)) + && !Deaf && !muteshk(shkp)) { + SetVoice(shkp, 0, 80, 0); verbalize("Out of my way, scum!"); + } if (cansee(x, y)) { pline("%s nimbly%s catches %s.", Shknam(shkp), (x == shkp->mx && y == shkp->my) ? "" : " reaches over and", @@ -3901,8 +3934,10 @@ litter_scatter( * a slang connotation which could be applicable if hero * has Passes_walls ability. */ - if (!Deaf && !muteshk(shkp)) + if (!Deaf && !muteshk(shkp)) { + SetVoice(shkp, 0, 80, 0); verbalize("Get your junk out of my wall!"); + } unplacebc(); /* pick 'em up */ placebc(); /* put 'em down */ } @@ -4155,20 +4190,24 @@ shk_move(struct monst *shkp) } if (eshkp->following) { if (strncmp(eshkp->customer, gp.plname, PL_NSIZ)) { - if (!Deaf && !muteshk(shkp)) + if (!Deaf && !muteshk(shkp)) { + SetVoice(shkp, 0, 80, 0); verbalize("%s, %s! I was looking for %s.", Hello(shkp), gp.plname, eshkp->customer); + } eshkp->following = 0; return 0; } if (gm.moves > gf.followmsg + 4) { - if (!Deaf && !muteshk(shkp)) + if (!Deaf && !muteshk(shkp)) { + SetVoice(shkp, 0, 80, 0); verbalize("%s, %s! Didn't you forget to pay?", Hello(shkp), gp.plname); - else + } else { pline("%s holds out %s upturned %s.", Shknam(shkp), noit_mhis(shkp), mbodypart(shkp, HAND)); + } gf.followmsg = gm.moves; if (!rn2(9)) { pline("%s doesn't like customers who don't pay.", @@ -4289,13 +4328,15 @@ shopdig(register int fall) if (!fall) { if (lang == 2) { if (!Deaf && !muteshk(shkp)) { - if (u.utraptype == TT_PIT) + SetVoice(shkp, 0, 80, 0); + if (u.utraptype == TT_PIT) { verbalize( "Be careful, %s, or you might fall through the floor.", flags.female ? "madam" : "sir"); - else + } else { verbalize("%s, do not damage the floor here!", flags.female ? "Madam" : "Sir"); + } } } if (Role_if(PM_KNIGHT)) { @@ -4480,8 +4521,9 @@ pay_for_damage(const char* dmgstr, boolean cant_mollify) if (MON_AT(x, y)) { if (!animal) { if (!Deaf && !muteshk(shkp)) { - Soundeffect(se_angry_voice, 75); + /* Soundeffect(se_angry_voice, 75); */ You_hear("an angry voice:"); + SetVoice(shkp, 0, 80, 0); verbalize("Out of my way, scum!"); } wait_synch(); @@ -4506,16 +4548,19 @@ pay_for_damage(const char* dmgstr, boolean cant_mollify) if (animal && !helpless(shkp)) yelp(shkp); } else if (pursue || uinshp || !um_dist(x, y, 1)) { - if (!Deaf) + if (!Deaf) { + SetVoice(shkp, 0, 80, 0); verbalize("How dare you %s my %s?", dmgstr, dugwall ? "shop" : "door"); - else + } else { pline("%s is %s that you decided to %s %s %s!", Shknam(shkp), angrytexts[rn2(SIZE(angrytexts))], dmgstr, noit_mhis(shkp), dugwall ? "shop" : "door"); + } } else { if (!Deaf) { pline("%s shouts:", Shknam(shkp)); + SetVoice(shkp, 0, 80, 0); verbalize("Who dared %s my %s?", dmgstr, dugwall ? "shop" : "door"); } else { @@ -4559,12 +4604,14 @@ pay_for_damage(const char* dmgstr, boolean cant_mollify) } } else { if (!animal) { - if (!Deaf && !muteshk(shkp)) + if (!Deaf && !muteshk(shkp)) { + SetVoice(shkp, 0, 80, 0); verbalize("Oh, yes! You'll pay!"); - else + } else { pline("%s lunges %s %s toward your %s!", Shknam(shkp), noit_mhis(shkp), mbodypart(shkp, HAND), body_part(NECK)); + } } else growl(shkp); hot_pursuit(shkp); @@ -4675,12 +4722,14 @@ price_quote(struct obj *first_obj) } else if (cnt == 1) { if (!cost) { /* ", no charge" */ + SetVoice(shkp, 0, 80, 0); verbalize("%s!", upstart(buf)); /* buf contains the string */ } else { /* print cost in slightly different format, so can't reuse buf; cost and contentsonly are already set up */ Sprintf(buf, "%s%s", contentsonly ? the_contents_of : "", doname(first_obj)); + SetVoice(shkp, 0, 80, 0); verbalize("%s, price %ld %s%s%s", upstart(buf), cost, currency(cost), (first_obj->quan > 1L) ? " each" : "", contentsonly ? "." : shk_embellish(first_obj, cost)); @@ -4769,17 +4818,21 @@ shk_chat(struct monst* shkp) noit_mhe(shkp), eshk->robbed ? "non-paying" : "rude"); } else if (eshk->following) { if (strncmp(eshk->customer, gp.plname, PL_NSIZ)) { - if (!Deaf && !muteshk(shkp)) + if (!Deaf && !muteshk(shkp)) { + SetVoice(shkp, 0, 80, 0); verbalize("%s %s! I was looking for %s.", Hello(shkp), gp.plname, eshk->customer); + } eshk->following = 0; } else { - if (!Deaf && !muteshk(shkp)) + if (!Deaf && !muteshk(shkp)) { + SetVoice(shkp, 0, 80, 0); verbalize("%s %s! Didn't you forget to pay?", Hello(shkp), gp.plname); - else + } else { pline("%s taps you on the %s.", Shknam(shkp), body_part(ARM)); + } } } else if (eshk->billct) { register long total = addupbill(shkp) + eshk->debit; @@ -4946,6 +4999,7 @@ check_unpaid_usage(struct obj* otmp, boolean altusage) } if (!Deaf && !muteshk(shkp)) { + SetVoice(shkp, 0, 80, 0); verbalize(fmt, arg1, arg2, tmp, currency(tmp)); exercise(A_WIS, TRUE); /* you just got info */ } @@ -5311,6 +5365,7 @@ globby_bill_fixup(struct obj* obj_absorber, struct obj* obj_absorbed) || (floor_absorber && !costly_spot(x, y)))) { amount = bp->price; bill_dummy_object(obj_absorbed); + SetVoice(shkp, 0, 80, 0); verbalize("You owe me %ld %s for my %s that you %s with your%s", amount, currency(amount), obj_typename(obj_absorbed->otyp), ANGRY(shkp) ? "had the audacity to mix" : "just mixed", diff --git a/src/sit.c b/src/sit.c index 876f03444..cfc6afd70 100644 --- a/src/sit.c +++ b/src/sit.c @@ -87,6 +87,7 @@ throne_sit_effect(void) /* Magical voice not affected by deafness */ pline("A voice echoes:"); + SetVoice((struct monst *) 0, 0, 80, voice_throne); verbalize("Thine audience hath been summoned, %s!", flags.female ? "Dame" : "Sire"); while (cnt--) @@ -96,6 +97,7 @@ throne_sit_effect(void) case 8: /* Magical voice not affected by deafness */ pline("A voice echoes:"); + SetVoice((struct monst *) 0, 0, 80, voice_throne); verbalize("By thine Imperious order, %s...", flags.female ? "Dame" : "Sire"); do_genocide(5); /* REALLY|ONTHRONE, see do_genocide() */ @@ -103,6 +105,7 @@ throne_sit_effect(void) case 9: /* Magical voice not affected by deafness */ pline("A voice echoes:"); + SetVoice((struct monst *) 0, 0, 80, voice_throne); verbalize( "A curse upon thee for sitting upon this most holy throne!"); if (Luck > 0) { diff --git a/src/sounds.c b/src/sounds.c index 269d37ed4..c4eeca169 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -116,12 +116,12 @@ zoo_mon_sound(struct monst *mtmp) { if ((mtmp->msleeping || is_animal(mtmp->data)) && mon_in_room(mtmp, ZOO)) { - int hallu = Hallucination ? 1 : 0; + int hallu = Hallucination ? 1 : 0, selection = rn2(2) + hallu; static const char *const zoo_msg[3] = { "a sound reminiscent of an elephant stepping on a peanut.", "a sound reminiscent of a seal barking.", "Doctor Dolittle!", }; - You_hear1(zoo_msg[rn2(2) + hallu]); + You_hear1(zoo_msg[selection]); return TRUE; } return FALSE; @@ -523,6 +523,7 @@ beg(register struct monst* mtmp) } else if (mtmp->data->msound >= MS_HUMANOID) { if (!canspotmon(mtmp)) map_invisible(mtmp->mx, mtmp->my); + SetVoice(mtmp, 0, 80, 0); verbalize("I'm hungry."); } else { /* this is pretty lame but is better than leaving out the block @@ -1058,9 +1059,10 @@ domonnoise(register struct monst* mtmp) } } break; case MS_ARREST: - if (mtmp->mpeaceful) + if (mtmp->mpeaceful) { + SetVoice(mtmp, 0, 80, 0); verbalize("Just the facts, %s.", flags.female ? "Ma'am" : "Sir"); - else { + } else { static const char *const arrest_msg[3] = { "Anything you say can be used against you.", "You're under arrest!", "Stop in the name of the Law!", @@ -1151,6 +1153,7 @@ domonnoise(register struct monst* mtmp) if (pline_msg) { pline("%s %s", Monnam(mtmp), pline_msg); } else if (mtmp->mcan && verbl_msg_mcan) { + SetVoice(mtmp, 0, 80, 0); verbalize1(verbl_msg_mcan); } else if (verbl_msg) { /* more 3.6 tribute */ @@ -1158,9 +1161,11 @@ domonnoise(register struct monst* mtmp) /* Death talks in CAPITAL LETTERS and without quotation marks */ char tmpbuf[BUFSZ]; - pline1(ucase(strcpy(tmpbuf, verbl_msg))); + SetVoice((struct monst *) 0, 0, 80, voice_death); + sound_speak(tmpbuf); } else { + SetVoice(mtmp, 0, 80, 0); verbalize1(verbl_msg); } } @@ -1652,6 +1657,7 @@ struct sound_procs nosound_procs = { (void (*)(int32_t, const char *, int32_t)) 0, /* hero_playnotes */ (void (*)(char *, int32_t, int32_t)) 0, /* play_usersound */ (void (*)(int32_t, int32_t, int32_t)) 0, /* ambience */ + (void (*)(char *, int32_t, int32_t, int32_t, int32_t)) 0, /* verbal */ }; /* The order of these array entries must match the @@ -1813,8 +1819,8 @@ static void nosound_achievement(schar, schar, int32_t); static void nosound_soundeffect(int32_t, int32_t); static void nosound_play_usersound(char *, int32_t, int32_t); static void nosound_ambience(int32_t, int32_t, int32_t); -{ -} +static void nosound_verbal(char *text, int32_t gender, int32_t tone, + int32_t vol, int32_t moreinfo); static void nosound_init_nhsound(void) @@ -1851,6 +1857,12 @@ nosound_ambience(int32_t ambienceid, int32_t ambience_action, int32_t hero_proximity) { } + +static void +nosound_verbal(char *text, int32_t gender, int32_t tone, + int32_t vol, int32_t moreinfo) +{ +} #endif #ifdef SND_SOUNDEFFECTS_AUTOMAP @@ -2236,4 +2248,68 @@ base_soundname_to_filename( return buf; } +#ifdef SND_SPEECH +#define SPEECHONLY +#else +#define SPEECHONLY UNUSED +#endif + +void +set_voice(struct monst *mtmp SPEECHONLY, int32_t tone SPEECHONLY, int32_t volume SPEECHONLY, int32_t moreinfo SPEECHONLY) +{ +#ifdef SND_SPEECH + int32_t gender = (mtmp && mtmp->female) ? FEMALE : MALE; + + if (gv.voice.nameid) + free((genericptr_t) gv.voice.nameid); + gv.voice.gender = gender; + gv.voice.serialno = mtmp ? mtmp->m_id + : ((moreinfo & voice_talking_artifact) != 0) ? 3 + : ((moreinfo & voice_deity) != 0) ? 4 : 2; + gv.voice.tone = tone; + gv.voice.volume = volume; + gv.voice.moreinfo = moreinfo; + gv.voice.nameid = (const char *) 0; + gp.pline_flags |= PLINE_SPEECH; +#endif +} + +void +sound_speak(const char *text SPEECHONLY) +{ +#ifdef SND_SPEECH + const char *cp1, *cp2; + char *cpdst; + char buf[BUFSZ + BUFSZ]; + + if (!text || (text && *text == '\0')) + return; + if (iflags.voices && soundprocs.sound_verbal + && (soundprocs.sound_triggers & SOUND_TRIGGER_VERBAL)) { + cp1 = text; + cpdst = buf; + cp2 = c_eos(cp1); + cp2--; /* last non-NUL */ + *cpdst = '\0'; + if ((gp.pline_flags & PLINE_VERBALIZE) != 0) { + if (*cp1 == '"') + cp1++; + if (*cp2 == '"') + cp2--; + } + /* cp1 -> 1st, cp2 -> last non-nul) */ + if ((unsigned)(cp2 - cp1) < (sizeof buf - 1U)) { + while (cp1 <= cp2) { + *cpdst = *cp1; + cp1++; + cpdst++; + } + *cpdst = '\0'; + } + (*soundprocs.sound_verbal)(buf, gv.voice.gender, gv.voice.tone, + gv.voice.volume, gv.voice.moreinfo); + } +#endif +} + /*sounds.c*/ diff --git a/src/teleport.c b/src/teleport.c index 28db803d9..70d4b50f3 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -1016,6 +1016,7 @@ level_tele(void) } if (newlev <= -10) { You("arrive in heaven."); + SetVoice((struct monst *) 0, 0, 80, voice_deity); verbalize("Thou art early, but we'll admit thee."); gk.killer.format = NO_KILLER_PREFIX; Strcpy(gk.killer.name, "went to heaven prematurely"); diff --git a/src/timeout.c b/src/timeout.c index 53d28a410..80fdd6d07 100644 --- a/src/timeout.c +++ b/src/timeout.c @@ -1016,6 +1016,7 @@ hatch_egg(anything *arg, long timeout) siblings ? "Their" : "Its", flags.female ? "mommy" : "daddy", egg->spe ? "." : "?"); } else if (mon->data->mlet == S_DRAGON && !Deaf) { + SetVoice(mon, 0, 80, 0); verbalize("Gleep!"); /* Mything eggs :-) */ } break; diff --git a/src/uhitm.c b/src/uhitm.c index d3c9ae26c..9b802d727 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -3937,8 +3937,10 @@ mhitm_ad_heal(struct monst *magr, struct attack *mattk, struct monst *mdef, mhm->damage = 0; } else { if (Role_if(PM_HEALER)) { - if (!Deaf && !(gm.moves % 5)) + if (!Deaf && !(gm.moves % 5)) { + SetVoice(magr, 0, 80, 0); verbalize("Doc, I can't help you unless you cooperate."); + } mhm->damage = 0; } else hitmsg(magr, mattk); @@ -4090,8 +4092,11 @@ mhitm_ad_dgst(struct monst *magr, struct attack *mattk UNUSED, mhm->done = TRUE; return; } - if (Verbose(4, mhitm_ad_dgst) && !Deaf) + if (Verbose(4, mhitm_ad_dgst) && !Deaf) { + /* Soundeffect? */ + SetVoice(magr, 0, 80, 0); verbalize("Burrrrp!"); + } mhm->damage = mdef->mhp; /* Use up amulet of life saving */ if ((obj = mlifesaver(mdef)) != 0) diff --git a/src/vault.c b/src/vault.c index 8f18519da..90a930756 100644 --- a/src/vault.c +++ b/src/vault.c @@ -442,8 +442,10 @@ invault(void) if (u.uswallow) { /* can't interrogate hero, don't interrogate engulfer */ - if (!Deaf) + if (!Deaf) { + SetVoice(guard, 0, 80, 0); verbalize("What's going on here?"); + } if (!spotted) pline_The("other presence vanishes."); mongone(guard); @@ -452,9 +454,11 @@ invault(void) if (U_AP_TYPE == M_AP_OBJECT || u.uundetected) { if (U_AP_TYPE == M_AP_OBJECT && gy.youmonst.mappearance != GOLD_PIECE) - if (!Deaf) + if (!Deaf) { + SetVoice(guard, 0, 80, 0); verbalize("Hey! Who left that %s in here?", mimic_obj_name(&gy.youmonst)); + } /* You're mimicking some object or you're hidden. */ pline("Puzzled, %s turns around and leaves.", mhe(guard)); mongone(guard); @@ -464,10 +468,12 @@ invault(void) /* [we ought to record whether this this message has already been given in order to vary it upon repeat visits, but discarding the monster and its egd data renders that hard] */ - if (Deaf) + if (Deaf) { pline("%s huffs and turns to leave.", noit_Monnam(guard)); - else + } else { + SetVoice(guard, 0, 80, 0); verbalize("I'll be back when you're ready to speak to me!"); + } mongone(guard); return; } @@ -498,6 +504,7 @@ invault(void) if (!Blind) pline("%s waves goodbye.", noit_Monnam(guard)); } else { + SetVoice(guard, 0, 80, 0); verbalize( "Oh, yes, of course. Sorry to have disturbed you."); } @@ -509,6 +516,7 @@ invault(void) pline("%s mouths something and looks very angry!", noit_Monnam(guard)); } else { + SetVoice(guard, 0, 80, 0); verbalize( "Back from the dead, are you? I'll remedy that!"); } @@ -520,18 +528,22 @@ invault(void) } return; } - if (Deaf) + if (Deaf) { pline("%s doesn't %srecognize you.", noit_Monnam(guard), (Blind) ? "" : "appear to "); - else + } else { + SetVoice(guard, 0, 80, 0); verbalize("I don't know you."); + } umoney = money_cnt(gi.invent); if (!umoney && !hidden_gold(TRUE)) { - if (Deaf) + if (Deaf) { pline("%s stomps%s.", noit_Monnam(guard), (Blind) ? "" : " and beckons"); - else + } else { + SetVoice(guard, 0, 80, 0); verbalize("Please follow me."); + } } else { if (!umoney) { if (Deaf) { @@ -539,6 +551,7 @@ invault(void) pline("%s glares at you%s.", noit_Monnam(guard), gi.invent ? "r stuff" : ""); } else { + SetVoice(guard, 0, 80, 0); verbalize("You have hidden gold."); } } @@ -549,8 +562,10 @@ invault(void) noit_Monnam(guard), noit_mhis(guard), noit_mhis(guard)); } else { + SetVoice(guard, 0, 80, 0); verbalize( "Most likely all your gold was stolen from this vault."); + SetVoice(guard, 0, 80, 0); verbalize("Please drop that gold and follow me."); } EGD(guard)->dropgoldcnt++; @@ -705,8 +720,10 @@ gd_mv_monaway(struct monst *grd, int nx, int ny) struct monst *mtmp = m_at(nx, ny); if (mtmp && mtmp != grd) { - if (!Deaf) + if (!Deaf) { + SetVoice(grd, 0, 80, 0); verbalize("Out of my way, scum!"); + } if (!rloc(mtmp, RLOC_ERR | RLOC_MSG) || MON_AT(nx, ny)) m_into_limbo(mtmp); } @@ -897,9 +914,11 @@ gd_move(struct monst *grd) return -1; /* teleported guard - treat as monster */ if (egrd->witness) { - if (!Deaf) + if (!Deaf) { + SetVoice(grd, 0, 80, 0); verbalize("How dare you %s that gold, scoundrel!", (egrd->witness & GD_EATGOLD) ? "consume" : "destroy"); + } egrd->witness = 0; grd->mpeaceful = 0; return -1; @@ -916,6 +935,7 @@ gd_move(struct monst *grd) u_carry_gold ? (!umoney ? "drop that hidden gold and " : "drop that gold and ") : ""); + SetVoice(grd, 0, 80, 0); if (egrd->dropgoldcnt || !u_carry_gold) verbalize("I repeat, %s", buf); else @@ -926,8 +946,10 @@ gd_move(struct monst *grd) if (egrd->warncnt == 7) { m = grd->mx; n = grd->my; - if (!Deaf) + if (!Deaf) { + SetVoice(grd, 0, 80, 0); verbalize("You've been warned, knave!"); + } grd->mpeaceful = 0; mnexto(grd, RLOC_NOMSG); levl[m][n].typ = egrd->fakecorr[0].ftyp; @@ -953,8 +975,10 @@ gd_move(struct monst *grd) gd_letknow(grd); return -1; } else { - if (!Deaf) + if (!Deaf) { + SetVoice(grd, 0, 80, 0); verbalize("Well, begone."); + } egrd->gddone = 1; return gd_move_cleanup(grd, semi_dead, FALSE); } @@ -983,6 +1007,7 @@ gd_move(struct monst *grd) pline("%s holds out %s palm demandingly!", noit_Monnam(grd), noit_mhis(grd)); } else { + SetVoice(grd, 0, 80, 0); verbalize("Drop all your gold, scoundrel!"); } return 0; @@ -992,6 +1017,7 @@ gd_move(struct monst *grd) pline("%s rubs %s hands with enraged delight!", noit_Monnam(grd), noit_mhis(grd)); } else { + SetVoice(grd, 0, 80, 0); verbalize("So be it, rogue!"); } grd->mpeaceful = 0; @@ -1017,8 +1043,10 @@ gd_move(struct monst *grd) } if (um_dist(grd->mx, grd->my, 1) || egrd->gddone) { if (!egrd->gddone && !rn2(10) && !Deaf && !u.uswallow - && !(u.ustuck && !sticks(gy.youmonst.data))) + && !(u.ustuck && !sticks(gy.youmonst.data))) { + SetVoice(grd, 0, 80, 0); verbalize("Move along!"); + } restfakecorr(grd); return 0; /* didn't move */ } diff --git a/src/wizard.c b/src/wizard.c index fd3af1d73..44619396c 100644 --- a/src/wizard.c +++ b/src/wizard.c @@ -738,6 +738,7 @@ resurrect(void) set_malign(mtmp); if (!Deaf) { pline("A voice booms out..."); + SetVoice(mtmp, 0, 80, 0); verbalize("So thou thought thou couldst %s me, fool.", verb); } } @@ -810,21 +811,26 @@ cuss(struct monst *mtmp) if (Deaf) return; if (mtmp->iswiz) { - if (!rn2(5)) /* typical bad guy action */ + if (!rn2(5)) { /* typical bad guy action */ pline("%s laughs fiendishly.", Monnam(mtmp)); - else if (u.uhave.amulet && !rn2(SIZE(random_insult))) + } else if (u.uhave.amulet && !rn2(SIZE(random_insult))) { + SetVoice(mtmp, 0, 80, 0); verbalize("Relinquish the amulet, %s!", random_insult[rn2(SIZE(random_insult))]); - else if (u.uhp < 5 && !rn2(2)) /* Panic */ + } else if (u.uhp < 5 && !rn2(2)) { /* Panic */ + SetVoice(mtmp, 0, 80, 0); verbalize(rn2(2) ? "Even now thy life force ebbs, %s!" : "Savor thy breath, %s, it be thy last!", random_insult[rn2(SIZE(random_insult))]); - else if (mtmp->mhp < 5 && !rn2(2)) /* Parthian shot */ + } else if (mtmp->mhp < 5 && !rn2(2)) { /* Parthian shot */ + SetVoice(mtmp, 0, 80, 0); verbalize(rn2(2) ? "I shall return." : "I'll be back."); - else + } else { + SetVoice(mtmp, 0, 80, 0); verbalize("%s %s!", random_malediction[rn2(SIZE(random_malediction))], random_insult[rn2(SIZE(random_insult))]); + } } else if (is_lminion(mtmp) && !(mtmp->isminion && EMIN(mtmp)->renegade)) { com_pager("angel_cuss"); /* TODO: the Hallucination msg */ diff --git a/sys/unix/hints/include/multisnd1-pre.370 b/sys/unix/hints/include/multisnd1-pre.370 index 45cf1b1b6..909a02fef 100644 --- a/sys/unix/hints/include/multisnd1-pre.370 +++ b/sys/unix/hints/include/multisnd1-pre.370 @@ -17,6 +17,12 @@ #LFLAGS += -lm #endif # WANT_TESTSOUND +ifeq "$(HAVE_SNDLIB)" "1" +ifeq "$(WANT_SPEECH)" "1" +SNDCFLAGS+= -DSND_SPEECH +endif # WANT_SPEECH +endif # HAVE_SNDLIB + # HAVE_SNDLIB or NEEDS_SND_USERSOUNDS or NEEDS_SND_SEAUTOMAP # would have to be set prior to this. Any of them could have # been set just above, or they could have been set in diff --git a/sys/unix/hints/macOS.370 b/sys/unix/hints/macOS.370 index c4b9246a9..01718b9b2 100755 --- a/sys/unix/hints/macOS.370 +++ b/sys/unix/hints/macOS.370 @@ -164,6 +164,9 @@ SNDCFLAGS+= -DSND_LIB_MACSOUND SNDLIBSRC = ../sound/macsound/macsound.m SNDLIBOBJ = macsound.o LFLAGS += -framework AppKit +#ifdef WANT_SPEECH +LFLAGS += -framework AVFoundation +#endif endif # diff --git a/win/Qt/qt_bind.cpp b/win/Qt/qt_bind.cpp index 6ee5fbf92..1c507efe5 100644 --- a/win/Qt/qt_bind.cpp +++ b/win/Qt/qt_bind.cpp @@ -1065,6 +1065,9 @@ void NetHackQtBind::qtsound_hero_playnotes(int32_t instrument UNUSED, const char void NetHackQtBind::qtsound_ambience(int32_t ambienceid UNUSED, int32_t ambience_action UNUSED, int32_t proximity UNUSED) { } +void NetHackQtBind::qtsound_verbal(char *text UNUSED, int32_t gender UNUSED, int32_t tone UNUSED, int32_t vol UNUSED, int32_t moreinfo UNUSED) +{ +} #endif #if defined(USER_SOUNDS) && !defined(QT_NO_SOUND) @@ -1186,6 +1189,7 @@ struct sound_procs qtsound_procs = { nethack_qt_::NetHackQtBind::qtsound_hero_playnotes, nethack_qt_::NetHackQtBind::qtsound_play_usersound, nethack_qt_::NetHackQtBind::qtsound_ambience, + nethack_qt_::NetHackQtBind::qtsound_verbal, }; #endif /* SND_LIB_QTSOUND and !QT_NO_SOUND */ diff --git a/win/Qt/qt_bind.h b/win/Qt/qt_bind.h index 9bdaf9cf3..b0b9f4f3a 100644 --- a/win/Qt/qt_bind.h +++ b/win/Qt/qt_bind.h @@ -103,6 +103,7 @@ public: static void qtsound_hero_playnotes(int32_t instrument, const char *str, int32_t volume); static void qtsound_play_usersound(char *, int32_t, int32_t); static void qtsound_ambience(int32_t, int32_t, int32_t); + static void qtsound_verbal(char *text, int32_t gender, int32_t tone, int32_t vol, int32_t moreinfo); #endif private: