diff --git a/build-windows-visual-studio/sm64ex.vcxproj b/build-windows-visual-studio/sm64ex.vcxproj
index 3238b1914..3155c82ef 100644
--- a/build-windows-visual-studio/sm64ex.vcxproj
+++ b/build-windows-visual-studio/sm64ex.vcxproj
@@ -3950,6 +3950,7 @@
+
@@ -4392,6 +4393,7 @@
+
diff --git a/build-windows-visual-studio/sm64ex.vcxproj.filters b/build-windows-visual-studio/sm64ex.vcxproj.filters
index afc2678a0..39eb45dbb 100644
--- a/build-windows-visual-studio/sm64ex.vcxproj.filters
+++ b/build-windows-visual-studio/sm64ex.vcxproj.filters
@@ -15252,6 +15252,9 @@
Source Files\src\pc\utils
+
+ Source Files\src\pc\djui\component\compound
+
@@ -16324,5 +16327,8 @@
Source Files\src\pc\utils
+
+ Source Files\src\pc\djui\component\compound
+
\ No newline at end of file
diff --git a/src/pc/djui/djui.c b/src/pc/djui/djui.c
index f861a327e..c49ccf0fb 100644
--- a/src/pc/djui/djui.c
+++ b/src/pc/djui/djui.c
@@ -39,6 +39,7 @@ void djui_render(void) {
create_dl_ortho_matrix();
djui_panel_update();
+ djui_popup_update();
djui_base_render(&gDjuiRoot->base);
diff --git a/src/pc/djui/djui.h b/src/pc/djui/djui.h
index 1bf0a7b74..f4f2d63f9 100644
--- a/src/pc/djui/djui.h
+++ b/src/pc/djui/djui.h
@@ -27,6 +27,7 @@
#include "djui_flow_layout.h"
#include "djui_selectionbox.h"
#include "djui_bind.h"
+#include "djui_popup.h"
#include "djui_panel.h"
#include "djui_panel_menu.h"
diff --git a/src/pc/djui/djui_popup.c b/src/pc/djui/djui_popup.c
new file mode 100644
index 000000000..65c88f8b5
--- /dev/null
+++ b/src/pc/djui/djui_popup.c
@@ -0,0 +1,103 @@
+#include
+#include "djui.h"
+#include "pc/network/network.h"
+#include "pc/utils/misc.h"
+
+#define DJUI_POPUP_LIFETIME 6.0f
+
+struct DjuiPopupList {
+ struct DjuiPopup* popup;
+ clock_t createTime;
+ struct DjuiPopupList* next;
+};
+
+static struct DjuiPopupList* sPopupListHead = NULL;
+static f32 sPopupListY = 0;
+
+static void djui_popup_add_to_list(struct DjuiPopup* popup) {
+ struct DjuiPopupList* node = malloc(sizeof(struct DjuiPopupList));
+ node->popup = popup;
+ node->createTime = clock();
+ node->next = sPopupListHead;
+ sPopupListHead = node;
+}
+
+static void djui_popup_render(struct DjuiBase* base) {
+ djui_rect_render(base);
+}
+
+static void djui_popup_destroy(struct DjuiBase* base) {
+ struct DjuiPopup* popup = (struct DjuiPopup*)base;
+ free(popup);
+}
+
+struct DjuiPopup* djui_popup_create(const char* message, int lines) {
+ struct DjuiPopup* popup = malloc(sizeof(struct DjuiPopup));
+ struct DjuiBase* base = &popup->base;
+
+ f32 height = lines * 32 + 32;
+ djui_base_init(&gDjuiRoot->base, base, djui_popup_render, djui_popup_destroy);
+ djui_base_set_alignment(base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP);
+ djui_base_set_location(base, 8, -height);
+ djui_base_set_size(base, 400, height);
+ djui_base_set_border_width(base, 4);
+ djui_base_set_color(base, 0, 0, 0, 220);
+ djui_base_set_border_color(base, 0, 0, 0, 180);
+
+ struct DjuiText* text = djui_text_create(base, message);
+ djui_base_set_size_type(&text->base, DJUI_SVT_RELATIVE, DJUI_SVT_RELATIVE);
+ djui_base_set_size(&text->base, 1.0f, 1.0f);
+ djui_base_set_color(&text->base, 220, 220, 220, 255);
+ djui_text_set_alignment(text, DJUI_HALIGN_CENTER, DJUI_VALIGN_CENTER);
+ djui_text_set_drop_shadow(text, 0, 0, 0, 64);
+ popup->text = text;
+
+ sPopupListY -= height + 4;
+ djui_popup_add_to_list(popup);
+ return popup;
+}
+
+void djui_popup_update(void) {
+ struct DjuiPopupList* node = sPopupListHead;
+ struct DjuiPopupList* last = NULL;
+ f32 y = sPopupListY;
+
+ while (node != NULL) {
+ struct DjuiPopupList* next = node->next;
+ djui_base_set_location(&node->popup->base, 4, y);
+ y += node->popup->base.height.value + 4;
+ if (gNetworkType != NT_NONE && gNetworkPlayerLocal != NULL) {
+ f32 elapsed = (clock() - node->createTime) / (f32)CLOCKS_PER_SEC;
+
+ // fade out
+ f32 alpha = fmin(DJUI_POPUP_LIFETIME - elapsed, 1.0f);
+ alpha *= alpha;
+ if (elapsed > DJUI_POPUP_LIFETIME) { alpha = 0; }
+ djui_base_set_color(&node->popup->base, 0, 0, 0, 220 * alpha);
+ djui_base_set_border_color(&node->popup->base, 0, 0, 0, 180 * alpha);
+ djui_base_set_color(&node->popup->text->base, 220, 220, 220, 255 * alpha);
+ djui_text_set_drop_shadow(node->popup->text, 0, 0, 0, 64 * alpha);
+
+ // remove/deallocate popup
+ if (alpha == 0) {
+ if (last != NULL) { last->next = next; }
+ if (node == sPopupListHead) { sPopupListHead = next; }
+ djui_base_destroy(&node->popup->base);
+ free(node);
+ node = next;
+ continue;
+ }
+ } else {
+ // prevent popups from fading out when we're not connected
+ node->createTime = clock();
+ }
+
+ // iterate
+ last = node;
+ node = next;
+ }
+
+ // move entire popup list
+ sPopupListY = sPopupListY * 0.75f + 1;
+ if (sPopupListY > 4) { sPopupListY = 4; }
+}
\ No newline at end of file
diff --git a/src/pc/djui/djui_popup.h b/src/pc/djui/djui_popup.h
new file mode 100644
index 000000000..66c130fd0
--- /dev/null
+++ b/src/pc/djui/djui_popup.h
@@ -0,0 +1,11 @@
+#pragma once
+#include "djui.h"
+
+#pragma pack(1)
+struct DjuiPopup {
+ struct DjuiBase base;
+ struct DjuiText* text;
+};
+
+struct DjuiPopup* djui_popup_create(const char* message, int lines);
+void djui_popup_update(void);
diff --git a/src/pc/network/discord/discord.c b/src/pc/network/discord/discord.c
index 014efc8f7..8f6529b9e 100644
--- a/src/pc/network/discord/discord.c
+++ b/src/pc/network/discord/discord.c
@@ -5,6 +5,7 @@
#include "discord_network.h"
#include "pc/debuglog.h"
#include "pc/network/version.h"
+#include "pc/djui/djui.h"
#if defined(_WIN32) || defined(_WIN64)
#include
@@ -148,7 +149,7 @@ static bool ns_discord_initialize(enum NetworkType networkType) {
DISCORD_REQUIRE(rc);
} else if (rc) {
LOG_ERROR("DiscordCreate failed: %d", rc);
- djui_show_popup("\\#ffa0a0\\Error:\\#c8c8c8\\ Could not detect Discord.\n\nTry closing the game, restarting Discord, and opening the game again.");
+ djui_popup_create("\\#ffa0a0\\Error:\\#c8c8c8\\ Could not detect Discord.\n\\#a0a0a0\\Try closing the game, restarting Discord, and opening the game again.", 3);
gDiscordFailed = true;
return false;
}
diff --git a/src/pc/network/network.c b/src/pc/network/network.c
index a516b9555..36adfe944 100644
--- a/src/pc/network/network.c
+++ b/src/pc/network/network.c
@@ -278,8 +278,3 @@ void network_shutdown(bool sendLeaving) {
void chat_add_message(char* message) {
LOG_INFO("chat: %s", message);
}
-
-// TODO: replace
-void djui_show_popup(char* message) {
- LOG_INFO("popup: %s", message);
-}
diff --git a/src/pc/network/network.h b/src/pc/network/network.h
index df372d501..f9e18249b 100644
--- a/src/pc/network/network.h
+++ b/src/pc/network/network.h
@@ -103,5 +103,4 @@ void network_shutdown(bool sendLeaving);
// TODO: replace
void chat_add_message(char* message);
-void djui_show_popup(char* message);
#endif
diff --git a/src/pc/network/network_player.c b/src/pc/network/network_player.c
index 0ae0653d5..baff5ca83 100644
--- a/src/pc/network/network_player.c
+++ b/src/pc/network/network_player.c
@@ -2,6 +2,7 @@
#include "network_player.h"
#include "game/mario_misc.h"
#include "reservation_area.h"
+#include "pc/djui/djui.h"
#include "pc/debuglog.h"
struct NetworkPlayer gNetworkPlayers[MAX_PLAYERS] = { 0 };
@@ -177,8 +178,15 @@ u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex) {
np->lastReceived = clock();
if (gNetworkType == NT_SERVER || type == NPT_SERVER) { gNetworkSystem->save_id(i, 0); }
for (int j = 0; j < MAX_SYNC_OBJECTS; j++) { gSyncObjects[j].rxEventId[i] = 0; }
- if (type == NPT_SERVER) { gNetworkPlayerServer = np; }
- else { chat_add_message("player connected"); }
+ if (type == NPT_SERVER) {
+ gNetworkPlayerServer = np;
+ } else {
+ // display popup
+ u8* rgb = get_player_color(np->globalIndex, 0);
+ char popupMsg[128] = { 0 };
+ snprintf(popupMsg, 128, "\\#%02x%02x%02x\\Player\\#dcdcdc\\ connected.", rgb[0], rgb[1], rgb[2]);
+ djui_popup_create(popupMsg, 1);
+ }
LOG_INFO("player connected, local %d, global %d", i, np->globalIndex);
packet_ordered_clear(np->globalIndex);
return i;
@@ -218,7 +226,13 @@ u8 network_player_disconnected(u8 globalIndex) {
gNetworkSystem->clear_id(i);
for (int j = 0; j < MAX_SYNC_OBJECTS; j++) { gSyncObjects[j].rxEventId[i] = 0; }
LOG_INFO("player disconnected, local %d, global %d", i, globalIndex);
- chat_add_message("player disconnected");
+
+ // display popup
+ u8* rgb = get_player_color(np->globalIndex, 0);
+ char popupMsg[128] = { 0 };
+ snprintf(popupMsg, 128, "\\#%02x%02x%02x\\Player\\#dcdcdc\\ disconnected.", rgb[0], rgb[1], rgb[2]);
+ djui_popup_create(popupMsg, 1);
+
packet_ordered_clear(globalIndex);
reservation_area_change(np);
return i;
@@ -235,6 +249,6 @@ void network_player_shutdown(void) {
gNetworkSystem->clear_id(i);
}
- chat_add_message("network shutdown");
+ djui_popup_create("\\#ffa0a0\\Error:\\#dcdcdc\\ network shutdown.", 1);
LOG_INFO("cleared all network players");
}
diff --git a/src/pc/pc_main.c b/src/pc/pc_main.c
index 7325ceffb..b37f8b1a8 100644
--- a/src/pc/pc_main.c
+++ b/src/pc/pc_main.c
@@ -269,6 +269,10 @@ void main_func(void) {
djui_init();
+#ifdef UNSTABLE_BRANCH
+ djui_popup_create("This is an \\#ffa0a0\\unstable\\#dcdcdc\\ branch build.\nExpect many strange bugs.", 2);
+#endif
+
if (gCLIOpts.Network == NT_CLIENT) {
network_set_system(NS_SOCKET);
strncpy(configJoinIp, gCLIOpts.JoinIp, IP_MAX_LEN);
@@ -282,10 +286,6 @@ void main_func(void) {
network_init(NT_NONE);
}
-#ifdef UNSTABLE_BRANCH
- djui_show_popup("This is an unstable branch build.\n\nExpect many strange bugs.\n\nFor a more stable experience use the normal coop branch.");
-#endif
-
audio_init();
sound_init();