From 6b0e1b01c93ab7bee74bbde3ea2b4155b0afaec0 Mon Sep 17 00:00:00 2001 From: Dominicentek Date: Mon, 1 Jun 2026 04:33:46 +0200 Subject: [PATCH] [proximity chat] muting and per-player volume --- bin/custom_textures.c | 8 ++ data/dynos_mgr_builtin_externs.h | 2 + data/dynos_mgr_builtin_tex.cpp | 2 + lang/Czech.ini | 1 + lang/English.ini | 1 + src/game/hud.c | 5 +- src/pc/djui/djui_button.c | 33 ++++- src/pc/djui/djui_button.h | 6 +- src/pc/djui/djui_interactable.c | 12 +- src/pc/djui/djui_panel_pause.c | 4 + src/pc/djui/djui_panel_proximity_chat.c | 123 +++++++++++++++++- src/pc/djui/djui_panel_proximity_chat.h | 1 + src/pc/djui/djui_slider.c | 6 +- src/pc/nametags.c | 7 +- src/pc/network/packets/packet.c | 1 + src/pc/network/packets/packet.h | 3 + src/pc/network/packets/packet_proxchat.c | 28 +++- src/pc/proximity_chat.c | 25 ++-- src/pc/proximity_chat.h | 10 +- .../segment2/custom_microphone_red.rgba16.png | Bin 0 -> 470 bytes .../custom_microphone_red_muted.rgba16.png | Bin 0 -> 631 bytes 21 files changed, 247 insertions(+), 31 deletions(-) create mode 100644 textures/segment2/custom_microphone_red.rgba16.png create mode 100644 textures/segment2/custom_microphone_red_muted.rgba16.png diff --git a/bin/custom_textures.c b/bin/custom_textures.c index ef47212e0..ff518f38b 100644 --- a/bin/custom_textures.c +++ b/bin/custom_textures.c @@ -67,6 +67,14 @@ ALIGNED8 const Texture texture_microphone_muted[] = { #include "textures/segment2/custom_microphone_muted.rgba16.inc.c" }; +ALIGNED8 const Texture texture_microphone_red[] = { +#include "textures/segment2/custom_microphone_red.rgba16.inc.c" +}; + +ALIGNED8 const Texture texture_microphone_red_muted[] = { +#include "textures/segment2/custom_microphone_red_muted.rgba16.inc.c" +}; + ALIGNED8 const Texture texture_microphone_warning[] = { #include "textures/segment2/custom_microphone_warning.rgba16.inc.c" }; \ No newline at end of file diff --git a/data/dynos_mgr_builtin_externs.h b/data/dynos_mgr_builtin_externs.h index 3c800caee..0e383085f 100644 --- a/data/dynos_mgr_builtin_externs.h +++ b/data/dynos_mgr_builtin_externs.h @@ -1562,6 +1562,8 @@ extern ALIGNED8 const Texture texture_ping_four[]; extern ALIGNED8 const Texture texture_ping_full[]; extern ALIGNED8 const Texture texture_microphone[]; extern ALIGNED8 const Texture texture_microphone_muted[]; +extern ALIGNED8 const Texture texture_microphone_red[]; +extern ALIGNED8 const Texture texture_microphone_red_muted[]; extern ALIGNED8 const Texture texture_microphone_warning[]; extern ALIGNED8 const Texture pss_seg7_texture_07000000[]; extern ALIGNED8 const Texture pss_seg7_texture_07000800[]; diff --git a/data/dynos_mgr_builtin_tex.cpp b/data/dynos_mgr_builtin_tex.cpp index 1abec911a..3f8ab2d00 100644 --- a/data/dynos_mgr_builtin_tex.cpp +++ b/data/dynos_mgr_builtin_tex.cpp @@ -1191,6 +1191,8 @@ static const struct BuiltinTexInfo sDynosBuiltinTexs[] = { // Proximity Chat Microphone Icons define_builtin_tex(texture_microphone, "textures/segment2/custom_microphone.rgba16.png", 16, 16, G_IM_FMT_RGBA, G_IM_SIZ_16b), define_builtin_tex(texture_microphone_muted, "textures/segment2/custom_microphone_muted.rgba16.png", 16, 16, G_IM_FMT_RGBA, G_IM_SIZ_16b), + define_builtin_tex(texture_microphone_red, "textures/segment2/custom_microphone_red.rgba16.png", 16, 16, G_IM_FMT_RGBA, G_IM_SIZ_16b), + define_builtin_tex(texture_microphone_red_muted, "textures/segment2/custom_microphone_red_muted.rgba16.png", 16, 16, G_IM_FMT_RGBA, G_IM_SIZ_16b), define_builtin_tex(texture_microphone_warning, "textures/segment2/custom_microphone_warning.rgba16.png", 16, 16, G_IM_FMT_RGBA, G_IM_SIZ_16b), // Goddard diff --git a/lang/Czech.ini b/lang/Czech.ini index 6e87d3df9..8707ff42e 100644 --- a/lang/Czech.ini +++ b/lang/Czech.ini @@ -373,6 +373,7 @@ PLAYER = "Hráč" DYNOS_PACKS = "DynOS packy" OPTIONS = "Nastavení" CHEATS = "Cheaty" +PROXIMITY_CHAT = "Proximity chat" SERVER_SETTINGS = "Nastavení serveru" RESUME = "Pokračovat" STOP_HOSTING = "Vypnout server" diff --git a/lang/English.ini b/lang/English.ini index 9e327e121..83f1d2db3 100644 --- a/lang/English.ini +++ b/lang/English.ini @@ -373,6 +373,7 @@ PLAYER = "Player" DYNOS_PACKS = "DynOS Packs" OPTIONS = "Options" CHEATS = "Cheats" +PROXIMITY_CHAT = "Proximity Chat" SERVER_SETTINGS = "Server Settings" RESUME = "Resume" STOP_HOSTING = "Stop Hosting" diff --git a/src/game/hud.c b/src/game/hud.c index 4a552eae0..0d848e2c6 100644 --- a/src/game/hud.c +++ b/src/game/hud.c @@ -599,12 +599,13 @@ void render_hud_proxchat(void) { const Texture* tex = NULL; if (proxchat_error[0] != PROXCHAT_ERR_NONE) tex = texture_microphone_warning; + else if (*proxchat_player_muted(0) & PROXCHAT_MUTE_GLOBAL) tex = texture_microphone_red_muted; else if (configProxchatActivationMode == PROXCHAT_ACTMODE_PUSH_TO_TALK) { - if (proxchat_muted) tex = texture_microphone_muted; + if (*proxchat_player_muted(0) != PROXCHAT_UNMUTED) tex = texture_microphone_muted; else tex = texture_microphone; } else if (configProxchatActivationMode == PROXCHAT_ACTMODE_THRESHOLD) { - if (proxchat_muted) tex = texture_microphone_muted; + if (*proxchat_player_muted(0) != PROXCHAT_UNMUTED) tex = texture_microphone_muted; else if (proxchat_player_is_talking(0)) tex = texture_microphone; else tex = NULL; } diff --git a/src/pc/djui/djui_button.c b/src/pc/djui/djui_button.c index 79d3c68b8..64d4e6980 100644 --- a/src/pc/djui/djui_button.c +++ b/src/pc/djui/djui_button.c @@ -47,7 +47,7 @@ static void djui_button_destroy(struct DjuiBase* base) { free(button); } -struct DjuiButton* djui_button_create(struct DjuiBase* parent, const char* message, enum DjuiButtonStyle style, void (*on_click)(struct DjuiBase*)) { +static struct DjuiButton* djui_button_create_base(struct DjuiBase* parent, enum DjuiButtonStyle style, void (*on_click)(struct DjuiBase*)) { struct DjuiButton* button = calloc(1, sizeof(struct DjuiButton)); struct DjuiBase* base = &button->base; @@ -62,7 +62,18 @@ struct DjuiButton* djui_button_create(struct DjuiBase* parent, const char* messa djui_base_set_size(&rect->base, 1.0f, 1.0f); button->rect = rect; - struct DjuiText* text = djui_text_create(&rect->base, message); + djui_base_set_size_type(base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(base, 1.0f, configDjuiThemeCenter ? 50 : 64); + djui_interactable_hook_click(base, on_click); + button->style = style; + + return button; +} + +struct DjuiButton* djui_button_create(struct DjuiBase* parent, const char* message, enum DjuiButtonStyle style, void (*on_click)(struct DjuiBase*)) { + struct DjuiButton* button = djui_button_create_base(parent, style, on_click); + + struct DjuiText* text = djui_text_create(&button->rect->base, message); struct DjuiColor color = gDjuiThemes[configDjuiTheme]->interactables.textColor; djui_base_set_size_type(&text->base, DJUI_SVT_RELATIVE, DJUI_SVT_RELATIVE); @@ -72,11 +83,19 @@ struct DjuiButton* djui_button_create(struct DjuiBase* parent, const char* messa djui_text_set_drop_shadow(text, 64, 64, 64, 100); button->text = text; - djui_base_set_size_type(base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); - djui_base_set_size(base, 1.0f, configDjuiThemeCenter ? 50 : 64); - djui_interactable_hook_click(base, on_click); - button->style = style; - djui_button_update_style(base); + djui_button_update_style(&button->base); + + return button; +} + +struct DjuiButton* djui_image_button_create(struct DjuiBase* parent, const Texture* tex, u16 width, u16 height, u8 fmt, u8 siz, enum DjuiButtonStyle style, void(*on_click)(struct DjuiBase*)) { + struct DjuiButton* button = djui_button_create_base(parent, style, on_click); + + struct DjuiImage* icon = djui_image_create(&button->rect->base, tex, width, height, fmt, siz); + djui_base_set_alignment(&icon->base, DJUI_HALIGN_CENTER, DJUI_VALIGN_CENTER); + button->icon = icon; + + djui_button_update_style(&button->base); return button; } diff --git a/src/pc/djui/djui_button.h b/src/pc/djui/djui_button.h index dfd2d293b..a8620acf7 100644 --- a/src/pc/djui/djui_button.h +++ b/src/pc/djui/djui_button.h @@ -4,7 +4,10 @@ struct DjuiButton { struct DjuiBase base; struct DjuiRect* rect; - struct DjuiText* text; + union { + struct DjuiText* text; + struct DjuiImage* icon; + }; u8 style; }; @@ -16,5 +19,6 @@ enum DjuiButtonStyle { void djui_button_set_style(struct DjuiButton* button, enum DjuiButtonStyle style); struct DjuiButton* djui_button_create(struct DjuiBase* parent, const char* message, enum DjuiButtonStyle style, void (*on_click)(struct DjuiBase*)); +struct DjuiButton* djui_image_button_create(struct DjuiBase* parent, const Texture* tex, u16 width, u16 height, u8 fmt, u8 siz, enum DjuiButtonStyle style, void(*on_click)(struct DjuiBase*)); struct DjuiButton* djui_button_left_create(struct DjuiBase* parent, const char* message, enum DjuiButtonStyle style, void (*on_click)(struct DjuiBase*)); struct DjuiButton* djui_button_right_create(struct DjuiBase* parent, const char* message, enum DjuiButtonStyle style, void (*on_click)(struct DjuiBase*)); diff --git a/src/pc/djui/djui_interactable.c b/src/pc/djui/djui_interactable.c index fdc7102ee..fb4b79790 100644 --- a/src/pc/djui/djui_interactable.c +++ b/src/pc/djui/djui_interactable.c @@ -226,9 +226,14 @@ bool djui_interactable_on_key_down(int scancode) { if (scancode == (int)configKeyPushToTalk[i]) pressPushToTalk = true; } + u32* mute_state = proxchat_player_muted(0); if (pressChat) djui_chat_box_toggle(); - if (pressMute && configProxchatActivationMode == PROXCHAT_ACTMODE_THRESHOLD) proxchat_muted ^= 1; // flip - if (pressPushToTalk && configProxchatActivationMode == PROXCHAT_ACTMODE_PUSH_TO_TALK) proxchat_muted = false; + if (pressMute && configProxchatActivationMode == PROXCHAT_ACTMODE_THRESHOLD) { + if (*mute_state & PROXCHAT_MUTE_LOCAL) *mute_state &= ~PROXCHAT_MUTE_LOCAL; + else *mute_state |= PROXCHAT_MUTE_LOCAL; + } + if (pressPushToTalk && configProxchatActivationMode == PROXCHAT_ACTMODE_PUSH_TO_TALK) + *mute_state &= ~PROXCHAT_MUTE_LOCAL; return pressChat || pressMute || pressPushToTalk; } @@ -291,7 +296,8 @@ void djui_interactable_on_key_up(int scancode) { } if (toggleConsole) djui_console_toggle(); - if (disablePushToTalk && configProxchatActivationMode == PROXCHAT_ACTMODE_PUSH_TO_TALK) proxchat_muted = true; + if (disablePushToTalk && configProxchatActivationMode == PROXCHAT_ACTMODE_PUSH_TO_TALK) + *proxchat_player_muted(0) |= PROXCHAT_MUTE_LOCAL; } if (gDjuiPlayerList != NULL || gDjuiModList != NULL) { diff --git a/src/pc/djui/djui_panel_pause.c b/src/pc/djui/djui_panel_pause.c index 6a4c2ef9c..a8b41890b 100644 --- a/src/pc/djui/djui_panel_pause.c +++ b/src/pc/djui/djui_panel_pause.c @@ -9,6 +9,7 @@ #include "djui_panel_menu.h" #include "djui_panel_confirm.h" #include "djui_panel_mod_menu.h" +#include "djui_panel_proximity_chat.h" #include "pc/pc_main.h" #include "pc/network/network.h" #include "pc/lua/smlua_hooks.h" @@ -75,6 +76,9 @@ void djui_panel_pause_create(struct DjuiBase* caller) { djui_button_create(body, DLANG(PAUSE, SERVER_SETTINGS), DJUI_BUTTON_STYLE_NORMAL, djui_panel_client_server_settings_create); } + if (gServerSettings.proximityChat) + djui_button_create(body, DLANG(PAUSE, PROXIMITY_CHAT), DJUI_BUTTON_STYLE_NORMAL, djui_panel_proximity_chat_ingame_create); + struct Mod* addedMods[MAX_HOOKED_MOD_MENU_ELEMENTS] = { 0 }; int modCount = 0; for (int i = 0; i < gHookedModMenuElementsCount; i++) { diff --git a/src/pc/djui/djui_panel_proximity_chat.c b/src/pc/djui/djui_panel_proximity_chat.c index 3d3f1486e..0417563b7 100644 --- a/src/pc/djui/djui_panel_proximity_chat.c +++ b/src/pc/djui/djui_panel_proximity_chat.c @@ -4,6 +4,8 @@ #include "pc/configfile.h" #include "pc/proximity_chat.h" #include "pc/lua/utils/smlua_audio_utils.h" +#include "pc/network/network.h" +#include "pc/utils/misc.h" static struct DjuiBase* activation_threshold_slider; static float threshold; @@ -14,7 +16,10 @@ static void djui_panel_sound_value_change(UNUSED struct DjuiBase* caller) { static void djui_panel_mode_value_change(UNUSED struct DjuiBase* caller) { djui_base_set_enabled(activation_threshold_slider, configProxchatActivationMode == PROXCHAT_ACTMODE_THRESHOLD); - proxchat_muted = configProxchatActivationMode == PROXCHAT_ACTMODE_PUSH_TO_TALK; + + u32* mute_state = proxchat_player_muted(0); + if (configProxchatActivationMode == PROXCHAT_ACTMODE_PUSH_TO_TALK) *mute_state |= PROXCHAT_MUTE_LOCAL; + else *mute_state &= ~PROXCHAT_MUTE_LOCAL; } static void djui_panel_threshold_value_change(UNUSED struct DjuiBase* caller) { @@ -52,3 +57,119 @@ void djui_panel_proximity_chat_create(struct DjuiBase* caller) { djui_panel_add(caller, panel, NULL); } + +static struct DjuiImage* djui_texture_create(struct DjuiBase* base, struct TextureInfo* info) { + return djui_image_create(base, info->texture, info->width, info->height, info->format, info->size); +} + +static struct DjuiPaginated* sPlayerPaginated; +static struct DjuiFlowLayout* sPlayerLayout; +static struct DjuiInputbox* sSearchInputbox = NULL; + +static struct MicButton { + bool global; + s32 player_id; + struct DjuiBase* base; + struct DjuiButton* button; +} sMicButtons[32]; + +static void toggle_mute(struct DjuiBase* caller) { + struct MicButton* mic = NULL; + for (int i = 0; i < 32 && !mic; i++) { + if (sMicButtons[i].base == caller) mic = &sMicButtons[i]; + } + if (!mic) return; + + u32* mute_state = proxchat_player_muted(mic->player_id); + if (mic->global) { + if (*mute_state & PROXCHAT_MUTE_GLOBAL) *mute_state &= ~PROXCHAT_MUTE_GLOBAL; + else *mute_state |= PROXCHAT_MUTE_GLOBAL; + dynos_texture_get(*mute_state & PROXCHAT_MUTE_GLOBAL ? "texture_microphone_red_muted" : "texture_microphone_red", &mic->button->icon->textureInfo); + network_send_proxchat_muted(gNetworkPlayers[mic->player_id].globalIndex, *mute_state & PROXCHAT_MUTE_GLOBAL); + } + else { + if (*mute_state & PROXCHAT_MUTE_LOCAL) *mute_state &= ~PROXCHAT_MUTE_LOCAL; + else *mute_state |= PROXCHAT_MUTE_LOCAL; + dynos_texture_get(*mute_state & PROXCHAT_MUTE_LOCAL ? "texture_microphone_muted" : "texture_microphone", &mic->button->icon->textureInfo); + } +} + +#define TEXTURE_INFO(tex) (tex).texture, (tex).width, (tex).height, (tex).format, (tex).size + +static void djui_panel_proximity_chat_add_players(struct DjuiBase* body) { + s32 button_counter = 0; + + for (int i = 1; i < MAX_PLAYERS; i++) { + if (!gNetworkPlayers[i].connected) continue; + if (sSearchInputbox != NULL && + sSearchInputbox->buffer != NULL && + !strstr_lowercased(djui_text_get_uncolored_string(NULL, strlen(gNetworkPlayers[i].name) + 1, gNetworkPlayers[i].name), sSearchInputbox->buffer) + ) continue; + + struct DjuiFlowLayout* layout = djui_flow_layout_create(body); + djui_flow_layout_set_flow_direction(layout, DJUI_FLOW_DIR_RIGHT); + djui_base_set_size_type(&layout->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&layout->base, 1, 32); + djui_base_set_color(&layout->base, 0, 0, 0, 0); + + struct DjuiImage* icon = djui_texture_create(&layout->base, &gMarioStates[i].character->hudHeadTexture); + + struct DjuiFlowLayout* inner_layout = djui_flow_layout_create(&layout->base); + djui_flow_layout_set_flow_direction(inner_layout, DJUI_FLOW_DIR_LEFT); + djui_base_set_size_type(&inner_layout->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&inner_layout->base, 1, 32); + djui_base_set_color(&inner_layout->base, 0, 0, 0, 0); + + struct TextureInfo lmute_tex, gmute_tex; + dynos_texture_get(*proxchat_player_muted(i) & PROXCHAT_MUTE_LOCAL ? "texture_microphone_muted" : "texture_microphone", &lmute_tex); + dynos_texture_get(*proxchat_player_muted(i) & PROXCHAT_MUTE_GLOBAL ? "texture_microphone_red_muted" : "texture_microphone_red", &gmute_tex); + + struct DjuiButton* gmute = djui_image_button_create(&inner_layout->base, TEXTURE_INFO(gmute_tex), DJUI_BUTTON_STYLE_NORMAL, toggle_mute); + struct DjuiButton* lmute = djui_image_button_create(&inner_layout->base, TEXTURE_INFO(lmute_tex), DJUI_BUTTON_STYLE_NORMAL, toggle_mute); + struct DjuiSlider* vol = djui_slider_create(&inner_layout->base, gNetworkPlayers[i].name, proxchat_player_volume(i), 0, 100, NULL); + + sMicButtons[button_counter++] = (struct MicButton){ .base = &lmute->base, .button = lmute, .player_id = i, .global = false }; + sMicButtons[button_counter++] = (struct MicButton){ .base = &gmute->base, .button = gmute, .player_id = i, .global = true }; + + djui_base_set_alignment(&vol->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_CENTER); + djui_base_set_alignment(&lmute->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_CENTER); + djui_base_set_alignment(&gmute->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_CENTER); + + djui_base_set_size(&icon->base, 32, 32); + djui_base_set_size(&lmute->text->base, 32, 32); + djui_base_set_size(&gmute->text->base, 32, 32); + + djui_base_set_size_type(&lmute->base, DJUI_SVT_ABSOLUTE, DJUI_SVT_ABSOLUTE); + djui_base_set_size_type(&gmute->base, DJUI_SVT_ABSOLUTE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&lmute->base, 32, 32); + djui_base_set_size(&gmute->base, 32, 32); + + djui_base_set_enabled(&gmute->base, gNetworkPlayers[0].moderator || gNetworkPlayers[0].globalIndex == 0); + } +} + +static void djui_panel_rebuild_players_list(UNUSED struct DjuiBase* caller) { + djui_base_destroy_children(&sPlayerLayout->base); + sPlayerLayout = sPlayerPaginated->layout; + djui_panel_proximity_chat_add_players(&sPlayerLayout->base); + djui_paginated_calculate_height(sPlayerPaginated); +} + +void djui_panel_proximity_chat_ingame_create(struct DjuiBase* caller) { + struct DjuiThreePanel* panel = djui_panel_menu_create(DLANG(PROXCHAT, PROXIMITY_CHAT), false); + struct DjuiBase* body = djui_three_panel_get_body(panel); + { + struct DjuiSearchbox* searchbox = djui_searchbox_create(body, djui_panel_rebuild_players_list); + sSearchInputbox = searchbox->inputbox; + + sPlayerPaginated = djui_paginated_create(body, 8); + sPlayerPaginated->showMaxCount = true; + sPlayerLayout = sPlayerPaginated->layout; + djui_panel_proximity_chat_add_players(&sPlayerLayout->base); + djui_paginated_calculate_height(sPlayerPaginated); + + djui_button_create(body, DLANG(MENU, BACK), DJUI_BUTTON_STYLE_BACK, djui_panel_proxchat_back); + } + + djui_panel_add(caller, panel, NULL); +} diff --git a/src/pc/djui/djui_panel_proximity_chat.h b/src/pc/djui/djui_panel_proximity_chat.h index a1779b733..0f4881fc5 100644 --- a/src/pc/djui/djui_panel_proximity_chat.h +++ b/src/pc/djui/djui_panel_proximity_chat.h @@ -2,3 +2,4 @@ #include "djui.h" void djui_panel_proximity_chat_create(struct DjuiBase* caller); +void djui_panel_proximity_chat_ingame_create(struct DjuiBase* caller); \ No newline at end of file diff --git a/src/pc/djui/djui_slider.c b/src/pc/djui/djui_slider.c index 6c3069ae7..4064aad4b 100644 --- a/src/pc/djui/djui_slider.c +++ b/src/pc/djui/djui_slider.c @@ -70,9 +70,9 @@ static void djui_slider_on_cursor_down(struct DjuiBase* base) { f32 x = slider->rect->base.elem.x; f32 w = slider->rect->base.elem.width; f32 cursorX = gCursorX; - cursorX = fmax(cursorX, x); - cursorX = fmin(cursorX, x + w); - *value = ((cursorX - x) / w) * (max - min) + min; + if (cursorX < x) *value = min; + else if (cursorX > x + w) *value = max; + else *value = ((cursorX - x) / w) * (max - min) + min; if (base != NULL && base->interactable != NULL && base->interactable->on_value_change != NULL) { base->interactable->on_value_change(base); } diff --git a/src/pc/nametags.c b/src/pc/nametags.c index c1ccf59ad..1df083ec6 100644 --- a/src/pc/nametags.c +++ b/src/pc/nametags.c @@ -173,9 +173,12 @@ void nametags_render(void) { // render mic icon - if (proxchat_player_is_talking(playerIndex)) { + const char* mic = NULL; + if (*proxchat_player_muted(playerIndex) & PROXCHAT_MUTE_GLOBAL) mic = "texture_microphone_red_muted"; + else if (proxchat_player_is_talking(playerIndex)) mic = "texture_microphone"; + if (mic) { struct TextureInfo texture; - dynos_texture_get("texture_microphone", &texture); + dynos_texture_get(mic, &texture); djui_hud_render_texture_interpolated(&texture, e->prevPos[0] - prevHalfWidth - 36 * e->prevScale, prevNametagPosY, e->prevScale * 2, e->prevScale * 2, nametag->pos[0] - currHalfWidth - 36 * nametag->scale, currNametagPosY, nametag->scale * 2, nametag->scale * 2 diff --git a/src/pc/network/packets/packet.c b/src/pc/network/packets/packet.c index 4ac402dfd..86a1f655a 100644 --- a/src/pc/network/packets/packet.c +++ b/src/pc/network/packets/packet.c @@ -101,6 +101,7 @@ void packet_process(struct Packet* p) { case PACKET_NETWORK_PLAYERS: network_receive_network_players(p); break; case PACKET_DEATH: network_receive_death(p); break; case PACKET_PROXCHAT_FRAME: network_receive_proxchat_frame(p); break; + case PACKET_PROXCHAT_GLOBAL_MUTE: network_receive_proxchat_muted(p); break; case PACKET_PING: network_receive_ping(p); break; case PACKET_PONG: network_receive_pong(p); break; diff --git a/src/pc/network/packets/packet.h b/src/pc/network/packets/packet.h index 535f1fbcb..96b466789 100644 --- a/src/pc/network/packets/packet.h +++ b/src/pc/network/packets/packet.h @@ -37,6 +37,7 @@ enum PacketType { PACKET_NETWORK_PLAYERS, PACKET_DEATH, PACKET_PROXCHAT_FRAME, + PACKET_PROXCHAT_GLOBAL_MUTE, PACKET_PING, PACKET_PONG, @@ -282,6 +283,8 @@ void network_receive_death(struct Packet* p); // packet_proxchat.c void network_send_proxchat_frame(void); void network_receive_proxchat_frame(struct Packet* p); +void network_send_proxchat_muted(s32 globalIndex, bool mute); +void network_receive_proxchat_muted(struct Packet* p); // packet_ping.c void network_send_ping(struct NetworkPlayer* toNp); diff --git a/src/pc/network/packets/packet_proxchat.c b/src/pc/network/packets/packet_proxchat.c index 3e036dc22..c3ab79763 100644 --- a/src/pc/network/packets/packet_proxchat.c +++ b/src/pc/network/packets/packet_proxchat.c @@ -23,5 +23,31 @@ void network_receive_proxchat_frame(struct Packet* p) { u8 frame_data[frame_size]; packet_read(p, frame_data, frame_size); - proxchat_decode_audio(p->localIndex, frame_data, frame_size); + proxchat_decode_audio( + network_player_from_global_index(p->orderedFromGlobalId)->localIndex, + frame_data, frame_size + ); } + +void network_send_proxchat_muted(s32 globalIndex, bool muted) { + struct Packet p = {}; + + packet_init(&p, PACKET_PROXCHAT_GLOBAL_MUTE, false, PLMT_NONE); + packet_write(&p, &globalIndex, sizeof(s32)); + packet_write(&p, &muted, sizeof(bool)); + + network_send(&p); +} + +void network_receive_proxchat_muted(struct Packet* p) { + s32 globalIndex; + bool muted; + + packet_read(p, &globalIndex, sizeof(s32)); + packet_read(p, &muted, sizeof(bool)); + + if (!network_player_from_global_index(p->orderedFromGlobalId)->moderator) return; + + if (muted) *proxchat_player_muted(network_player_from_global_index(globalIndex)->localIndex) |= PROXCHAT_MUTE_GLOBAL; + else *proxchat_player_muted(network_player_from_global_index(globalIndex)->localIndex) &= ~PROXCHAT_MUTE_GLOBAL; +} \ No newline at end of file diff --git a/src/pc/proximity_chat.c b/src/pc/proximity_chat.c index 3d5f0fcbf..94fda3444 100644 --- a/src/pc/proximity_chat.c +++ b/src/pc/proximity_chat.c @@ -29,7 +29,7 @@ typedef struct { } Buffer; static struct { - bool muted; + u32 muted_state; u32 volume; Buffer audio; union { @@ -40,7 +40,6 @@ static struct { static bool inited = false; -bool proxchat_muted = false; bool proxchat_loopback = false; float proxchat_mic_level = 0; @@ -168,7 +167,8 @@ static void proxchat_callback(const u8* input, u32 bytes) { if (!inited || gNetworkType == NT_NONE || configProxchatActivationMode == PROXCHAT_ACTMODE_DISABLED || - !gServerSettings.proximityChat || proxchat_muted || is_below_threshold() + !gServerSettings.proximityChat || client->muted_state != PROXCHAT_UNMUTED || + is_below_threshold() ) { // drain the pcm buffer buffer_drain(&client->audio); @@ -202,10 +202,11 @@ void proxchat_init() { players[i].audio.capacity = FRAME_SIZE * MAX_FRAMES * sizeof(s16); players[i].audio.dynamic = false; - players[i].volume = 1; + players[i].volume = 100; } - proxchat_muted = configProxchatActivationMode == PROXCHAT_ACTMODE_PUSH_TO_TALK; + if (configProxchatActivationMode == PROXCHAT_ACTMODE_PUSH_TO_TALK) + client->muted_state |= PROXCHAT_MUTE_LOCAL; inited = true; } @@ -225,6 +226,10 @@ bool proxchat_inited() { return inited; } +u32* proxchat_player_muted(s32 id) { + return &players[id].muted_state; +} + u32* proxchat_player_volume(s32 id) { return &players[id].volume; } @@ -276,6 +281,12 @@ void proxchat_mix(s16* out_pcm, u32 num_out_samples) { if (proxchat_num_frames_in_buffer(&players[i].audio) < MIN_FRAMES_REQUIRED) continue; + s16 player_pcm[num_samples]; + u32 n = buffer_read(&players[i].audio, sizeof(player_pcm), player_pcm) / sizeof(s16); + memset(player_pcm + n, 0, sizeof(player_pcm) - n * sizeof(s16)); + + if (players[i].muted_state != PROXCHAT_UNMUTED) continue; + float volume; float dist = vec3f_dist(gMarioStates[i].pos, gMarioState->pos); if (dist < FULL_VOL_RADIUS) volume = 1; @@ -298,10 +309,6 @@ void proxchat_mix(s16* out_pcm, u32 num_out_samples) { float pan_mono = 0.5f + pan * 0.5f * (configProxchatStereoSpread / 100.f); float vol_right = pan_mono; float vol_left = 1 - pan_mono; - - s16 player_pcm[num_samples]; - u32 n = buffer_read(&players[i].audio, sizeof(player_pcm), player_pcm) / sizeof(s16); - memset(player_pcm + n, 0, sizeof(player_pcm) - n * sizeof(s16)); for (s32 s = 0; s < num_samples * 2; s++) { float pan_factor = s % 2 == 0 ? vol_left : vol_right; diff --git a/src/pc/proximity_chat.h b/src/pc/proximity_chat.h index dffbc401b..e3b381006 100644 --- a/src/pc/proximity_chat.h +++ b/src/pc/proximity_chat.h @@ -3,7 +3,6 @@ #include "types.h" -extern bool proxchat_muted; extern bool proxchat_loopback; extern float proxchat_mic_level; @@ -21,13 +20,20 @@ enum ProxchatActivationMode { PROXCHAT_ACTMODE_THRESHOLD }; +enum ProxchatMuteState { + PROXCHAT_UNMUTED = 0, + + PROXCHAT_MUTE_LOCAL = (1 << 0), + PROXCHAT_MUTE_GLOBAL = (1 << 1), +}; + void proxchat_init(); bool proxchat_inited(); void proxchat_shutdown(); const Texture* proxchat_get_microphone_texture(); -bool* proxchat_player_muted(s32 id); +u32* proxchat_player_muted(s32 id); u32* proxchat_player_volume(s32 id); bool proxchat_player_is_talking(s32 id); diff --git a/textures/segment2/custom_microphone_red.rgba16.png b/textures/segment2/custom_microphone_red.rgba16.png new file mode 100644 index 0000000000000000000000000000000000000000..238d10356ea04352122325a5f1cd2700a8ee3c19 GIT binary patch literal 470 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a z5n0T@pr;JNj1^1m%YcIHC7!;n?624u8CdvtsN4_%+Na~`;uvCax_6ST7jvLU>;BF@ zVP>JG8(m9R+-!8!;=ZVxF*PLVp?pN=1lAqQ+!uR0qziOH+8!7P6s%d(baksGbG}-> zQ{Lye!u`eP|8lP2>`qobuXtL)lcVi$L*?N!Zv@{+ySQ@vLcD<}CgV5ta+jJ1TxKlDk}%qyOT5*@9{Ly|)v5BiVNp zX_Yr5FJPBp@O|Ktlev6%f-djt#YSu7Ht7Gj@^Bg7%K)YK4ol3YHP{|Z7d=*e-(9Ga zW$lZ-D^9NO^)@I^JyYt@Rm^M3@-D5zB<-Lpw_riM@+W?el&G@J^1WApLCN6h>gTe~ HDWM4f9RH~Z literal 0 HcmV?d00001 diff --git a/textures/segment2/custom_microphone_red_muted.rgba16.png b/textures/segment2/custom_microphone_red_muted.rgba16.png new file mode 100644 index 0000000000000000000000000000000000000000..6bae48fff61e0dea9c6efbcdd6dc5c64d089afdf GIT binary patch literal 631 zcmV--0*L*IP)z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy32;bRa{vG?BLDy{BLR4&KXw2B00(qQO+^Rl1s@eS248?8r~m)~ z%}GQ-R5;6xl0j<|Q51#07lSr|c2jgIB3Pxkvp+yYTF|^ZBu+%)25W;7GT%|&3WCS0vcy5Toz<`bvxCpF=&RalWEk58&_yi26kC6{W(n$h8Lgxlh za2%ZR>-Yjc2(Uo4v&XC5UDG9S64U^*?z$0tZ)u6r#KgXH0H1%iF$T`IT9IEQkO|M# zjSU*W?YTKVPEOJWti-Cd%zGYyc@F>f;6R|fxyh}UvwSKRu@b(-isy569|j;B`*Avc zxdeKW%Tb0ClR4;kAUp}@SBrNs_!@8=xDU}*cCF|4 zd|u_{W!^WN7+|Z@Ass@aKp*h1hl!H4+)aHcec)~LC#UxQkOfYJP=9zn>Vc!EjE(8_ z;GjlRsjfAB40;ypcv3`)SB%k$6y;N(Y&e}P@Zb>l#K$R6UkuKKU$~o4{}=DB&9Khp R>R|u?002ovPDHLkV1iG(2YLVi literal 0 HcmV?d00001