-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Voice masks can now change your voice sound! #32794
base: master
Are you sure you want to change the base?
Voice masks can now change your voice sound! #32794
Conversation
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some notes
/// <summary> | ||
/// List of all the loaded speech verbs (name, protoID). | ||
/// </summary> | ||
private List<(string, string)> _speechVerbs = new(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason the type is not (string, ProtoId<?>)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately yes :(
It allows me make the PopulateOptionButton
function usable for both! I couldn't figure out a way to make a generic protoID but if you know a way I'm all ears.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you could do it with something cursed with
private void PopulateOptionButton<T>(OptionButton button, string? selectedOption, List<(string, ProtoId<T>)> values) where T : class, IPrototype
Though it might make more sense to duplicate the function so that you can have a different none string for the speech sounds instead of just reusing the speech verb's for both.
Though, I'm not totally sure the added complexity is worth it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for making the changes I requested earlier!
As requested by the re-requested review, I've attached more comments and changes. I also got bored and made changes to the XAML layout.
Enjoy!
|
||
entity.Comp.VoiceMaskSpeechSound = msg.Sound; | ||
|
||
_popupSystem.PopupEntity(Loc.GetString("voice-mask-popup-success"), entity, msg.Actor); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This LocId should be moved to VoiceMaskComponent
.
RaiseLocalEvent(ent.Owner, evt); | ||
|
||
var protoId = evt.OverrideSpeechSound ?? ent.Comp.SpeechSounds; | ||
var prototype = _protoManager.Index<SpeechSoundsPrototype>(protoId); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ProtoId is nullable here (SpeechSounds and OverrideSpeechSounds are nullable). You'll have to handle this or change some type signatures.
{ | ||
_speechSounds.Add((Loc.GetString(noise.Name), noise.ID)); | ||
} | ||
_speechSounds.Sort((a, b) => a.Item1.CompareTo(b.Item1)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should be using StringComparisons, though I'm not sure if Ordinal is the best for this case.
_speechSounds.Sort((a, b) => a.Item1.CompareTo(b.Item1)); | |
_speechSounds.Sort((a, b) => string.Compare(a.Item1, b.Item1, StringComparison.Ordinal)); |
SpeechVerbSelector.SelectId(id); | ||
// Add the default option that wont do anything when selected. | ||
AddOption(button, selectedOption, Loc.GetString("chat-speech-verb-name-none"), null); | ||
foreach (var (name, id) in values) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
foreach (var (name, id) in values) | |
foreach (var (name, id) in values) |
/// <summary> | ||
/// List of all the loaded speech verbs (name, protoID). | ||
/// </summary> | ||
private List<(string, string)> _speechVerbs = new(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you could do it with something cursed with
private void PopulateOptionButton<T>(OptionButton button, string? selectedOption, List<(string, ProtoId<T>)> values) where T : class, IPrototype
Though it might make more sense to duplicate the function so that you can have a different none string for the speech sounds instead of just reusing the speech verb's for both.
Though, I'm not totally sure the added complexity is worth it.
<Label Text="{Loc 'voice-mask-name-change-info'}" Margin="0 0 0 10" /> | ||
<GridContainer Columns="2"> | ||
<GridContainer Rows="3" HorizontalExpand="True"> | ||
<LineEdit Name="NameSelector" MinWidth="175" HorizontalExpand="True" Margin="0 0 10 10" /> | ||
<Label Text="{Loc 'voice-mask-name-change-speech-style'}" Margin="0 0 0 5" /> | ||
<Label Text="{Loc 'voice-mask-name-change-speech-sound'}" /> | ||
</GridContainer> | ||
<GridContainer Rows="3"> | ||
<Button Name="NameSelectorSet" Text="{Loc 'voice-mask-name-change-set'}" Margin="0 0 0 10" /> | ||
<OptionButton Name="SpeechVerbSelector" /> <!-- Populated in AddVerbsAndSounds --> | ||
<OptionButton Name="SpeechSoundSelector" /> <!-- Populated in AddVerbsAndSounds --> | ||
</GridContainer> | ||
</GridContainer> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had some fun playing about and made some adjustments to move the label to below the text input and make it slightly darker/smaller.
I also removed the extra GridContainer as you're meant to just list them all off (this means you won't have to do the weird hack where you have to copy the margin between the subgrids as this is what the main grid is built to do).
<Label Text="{Loc 'voice-mask-name-change-info'}" Margin="0 0 0 10" /> | |
<GridContainer Columns="2"> | |
<GridContainer Rows="3" HorizontalExpand="True"> | |
<LineEdit Name="NameSelector" MinWidth="175" HorizontalExpand="True" Margin="0 0 10 10" /> | |
<Label Text="{Loc 'voice-mask-name-change-speech-style'}" Margin="0 0 0 5" /> | |
<Label Text="{Loc 'voice-mask-name-change-speech-sound'}" /> | |
</GridContainer> | |
<GridContainer Rows="3"> | |
<Button Name="NameSelectorSet" Text="{Loc 'voice-mask-name-change-set'}" Margin="0 0 0 10" /> | |
<OptionButton Name="SpeechVerbSelector" /> <!-- Populated in AddVerbsAndSounds --> | |
<OptionButton Name="SpeechSoundSelector" /> <!-- Populated in AddVerbsAndSounds --> | |
</GridContainer> | |
</GridContainer> | |
<GridContainer Columns="2"> | |
<LineEdit Name="NameSelector" HorizontalExpand="True" /> | |
<Button Name="NameSelectorSet" Text="{Loc 'voice-mask-name-change-set'}" /> | |
<Label StyleClasses="LabelSubText" Text="{Loc 'voice-mask-name-change-info'}" /> | |
<Control /> <!-- GridContainer needs a padding element so it goes to the next row --> | |
<Label Text="{Loc 'voice-mask-name-change-speech-style'}" /> | |
<OptionButton Name="SpeechVerbSelector" /> | |
<Label Text="{Loc 'voice-mask-name-change-speech-sound'}" /> | |
<OptionButton Name="SpeechSoundSelector" /> | |
</GridContainer> |
If you don't like the padding introduced by the Label, you can use this to trick the grid into ignoring it's width while still being able to render fine.
<Label StyleClasses="LabelSubText" Text="{Loc 'voice-mask-name-change-info'}" MaxWidth="1"
HorizontalAlignment="Left" />
if (value is { } metadata) | ||
button.SetItemMetadata(id, metadata); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Equivalent and less confusing:
if (value is { } metadata) | |
button.SetItemMetadata(id, metadata); | |
if (value is not null) | |
button.SetItemMetadata(id, value); |
@@ -50,6 +59,18 @@ private void OnChangeVerb(Entity<VoiceMaskComponent> entity, ref VoiceMaskChange | |||
UpdateUI(entity); | |||
} | |||
|
|||
private void OnChangeSound(Entity<VoiceMaskComponent> entity, ref VoiceMaskChangeSoundMessage msg) | |||
{ | |||
if (msg.Sound is { } id && !_proto.HasIndex<SpeechSoundsPrototype>(id)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd probably just manually check for null for clarity rather than doing the null pattern matching syntax.
if (msg.Sound is { } id && !_proto.HasIndex<SpeechSoundsPrototype>(id)) | |
if (msg.Sound is not null && !_proto.HasIndex<SpeechSoundsPrototype>(msg.Sound)) |
About the PR
Title! Same look as the verbs, but now you can also change the sound you make when you speak! This wont work for stuff like emotes, just normal speaking (Chat code 😢).
Why / Balance
Its fun and allows for some more silly antag shenanigans. You can now finally lure the CO to the weird box on bridge that totally doesn't have 5 nukies hidden inside while pretending to be a borg in distress!
Technical details
Because of the similarity to the verb dropdown, I had to refactor a lot of the verb stuff so it would work with both the verbs and the sounds! I added a lot of comments and named stuff better so it should be relatively clear what is going on.
Basic rundown:
When the window is opened
VoiceMaskBoundUserInterface.cs:18
,You first load all the verbs and speech noises into their respective lists (
ReloadVerbsAndNoises
).You then actually add the the verbs and speech noises to the buttons themselves in (
AddVerbsAndSounds
).Whenever the state gets updated, the
UpdateSelectedButtonOption
gets called on both drop downs and updates the currently selected option.Media
2024-10-13.13-21-14.mp4
Breaking changes
SpeechSoundsPrototype
s are now required to have a (Localized) name field. Example:is now:
Changelog
🆑 Beck Thompson