mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2024-11-22 03:55:11 +00:00
parent
5fe5ffda45
commit
9a0c07e53c
11 changed files with 265 additions and 21 deletions
141
src/game/chat.c
Normal file
141
src/game/chat.c
Normal file
|
@ -0,0 +1,141 @@
|
|||
#include <stdio.h>
|
||||
#include <ultra64.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "chat.h"
|
||||
#include "game_init.h"
|
||||
#include "ingame_menu.h"
|
||||
#include "segment2.h"
|
||||
#include "gfx_dimensions.h"
|
||||
#include "config.h"
|
||||
#include "PR/gbi.h"
|
||||
#include "pc/controller/controller_keyboard.h"
|
||||
#include "pc/network/network.h"
|
||||
#include "audio_defines.h"
|
||||
#include "audio/external.h"
|
||||
|
||||
#define CHAT_DIALOG_MAX 96
|
||||
#define CHAT_MESSAGES_MAX 16
|
||||
#define CHAT_LIFE_MAX 400
|
||||
|
||||
struct ChatMessage {
|
||||
u8 dialog[CHAT_DIALOG_MAX];
|
||||
u8 isLocal;
|
||||
u16 life;
|
||||
};
|
||||
|
||||
static char inputMessage[CHAT_DIALOG_MAX] = { 0 };
|
||||
static struct ChatMessage message[CHAT_MESSAGES_MAX] = { 0 };
|
||||
static u8 onMessageIndex = 0;
|
||||
static u8 sInChatInput = FALSE;
|
||||
|
||||
#define CHAT_SCALE 0.5f
|
||||
#define CHAT_SPACE 10.0f
|
||||
#define CHATBOX_PAD_X 0.0215f
|
||||
#define CHATBOX_SCALE_X 0.00385f
|
||||
#define CHATBOX_SCALE_Y 0.115f
|
||||
#define CHATBOX_X 2.0f
|
||||
#define CHATBOX_Y 11.0f
|
||||
#define CHAT_X 4.0f
|
||||
#define CHAT_Y -18.0f
|
||||
|
||||
static void render_chat_message(struct ChatMessage* chatMessage, u8 index) {
|
||||
f32 textWidth = get_generic_dialog_width(chatMessage->dialog);
|
||||
f32 alphaScale = ((f32)chatMessage->life / (f32)(CHAT_LIFE_MAX / 20.0f));
|
||||
alphaScale *= alphaScale;
|
||||
if (alphaScale > 1) { alphaScale = 1; }
|
||||
|
||||
f32 chatBoxWidth = CHATBOX_SCALE_X * textWidth + CHATBOX_PAD_X;
|
||||
create_dl_translation_matrix(MENU_MTX_PUSH, GFX_DIMENSIONS_FROM_LEFT_EDGE(CHATBOX_X), CHATBOX_Y + index * CHAT_SPACE, 0);
|
||||
create_dl_scale_matrix(MENU_MTX_NOPUSH, chatBoxWidth, CHATBOX_SCALE_Y, 1.0f);
|
||||
|
||||
u8 boxR, boxG, boxB;
|
||||
if (chatMessage->isLocal == 2) {
|
||||
boxR = 150;
|
||||
boxG = 150;
|
||||
boxB = 255;
|
||||
} else {
|
||||
f32 rgbScale = (((f32)chatMessage->life - ((f32)CHAT_LIFE_MAX * 0.98f)) / ((f32)CHAT_LIFE_MAX * 0.02f));
|
||||
if (chatMessage->isLocal || rgbScale < 0) { rgbScale = 0; }
|
||||
boxR = 255 * rgbScale;
|
||||
boxG = 255 * rgbScale;
|
||||
boxB = 255 * rgbScale;
|
||||
}
|
||||
|
||||
gDPSetEnvColor(gDisplayListHead++, boxR, boxG, boxB, 110 * alphaScale);
|
||||
gSPDisplayList(gDisplayListHead++, dl_draw_text_bg_box);
|
||||
|
||||
create_dl_scale_matrix(MENU_MTX_NOPUSH, CHAT_SCALE / chatBoxWidth, CHAT_SCALE / CHATBOX_SCALE_Y, 1.0f);
|
||||
|
||||
u8 textR, textG, textB;
|
||||
if (chatMessage->isLocal == 1) {
|
||||
textR = 200;
|
||||
textG = 200;
|
||||
textB = 255;
|
||||
} else if (chatMessage->isLocal == 2) {
|
||||
textR = 0;
|
||||
textG = 0;
|
||||
textB = 0;
|
||||
} else {
|
||||
textR = 255;
|
||||
textG = 255;
|
||||
textB = 255;
|
||||
}
|
||||
|
||||
gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
|
||||
gDPSetEnvColor(gDisplayListHead++, textR, textG, textB, 255 * alphaScale);
|
||||
print_generic_string(CHAT_X, CHAT_Y, chatMessage->dialog);
|
||||
gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
|
||||
|
||||
gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
|
||||
}
|
||||
|
||||
void chat_add_message(char* ascii, u8 isLocal) {
|
||||
message[onMessageIndex].dialog[0] = isLocal ? 0xFD : 0xFA;
|
||||
message[onMessageIndex].dialog[1] = 0x9E;
|
||||
str_ascii_to_dialog(ascii, &message[onMessageIndex].dialog[2], MIN(strlen(ascii), CHAT_DIALOG_MAX - 3));
|
||||
message[onMessageIndex].life = CHAT_LIFE_MAX;
|
||||
message[onMessageIndex].isLocal = isLocal ? 1 : 0;
|
||||
onMessageIndex = (onMessageIndex + 1) % CHAT_MESSAGES_MAX;
|
||||
play_sound(isLocal ? SOUND_MENU_MESSAGE_DISAPPEAR : SOUND_MENU_MESSAGE_APPEAR, gDefaultSoundArgs);
|
||||
}
|
||||
|
||||
static void chat_stop_input(void) {
|
||||
sInChatInput = FALSE;
|
||||
keyboard_stop_text_input();
|
||||
}
|
||||
|
||||
static void chat_send_input(void) {
|
||||
sInChatInput = FALSE;
|
||||
keyboard_stop_text_input();
|
||||
if (strlen(gTextInput) == 0) { return; }
|
||||
chat_add_message(gTextInput, TRUE);
|
||||
network_send_chat(gTextInput);
|
||||
}
|
||||
|
||||
void chat_start_input(void) {
|
||||
sInChatInput = TRUE;
|
||||
keyboard_start_text_input(TIM_SINGLE_LINE, CHAT_DIALOG_MAX - 3, chat_stop_input, chat_send_input);
|
||||
}
|
||||
|
||||
|
||||
void render_chat(void) {
|
||||
u8 count = 0;
|
||||
if (sInChatInput) {
|
||||
struct ChatMessage inputMessage = { 0 };
|
||||
inputMessage.dialog[0] = 0xFD;
|
||||
inputMessage.dialog[1] = 0x9E;
|
||||
str_ascii_to_dialog(gTextInput, &inputMessage.dialog[2], MIN(strlen(gTextInput), CHAT_DIALOG_MAX - 3));
|
||||
inputMessage.isLocal = 2;
|
||||
inputMessage.life = CHAT_LIFE_MAX;
|
||||
render_chat_message(&inputMessage, count++);
|
||||
}
|
||||
|
||||
u8 index = onMessageIndex;
|
||||
for (int i = 0; i < CHAT_MESSAGES_MAX; i++) {
|
||||
if (--index >= CHAT_MESSAGES_MAX) { index = CHAT_MESSAGES_MAX - 1; }
|
||||
if (message[index].life == 0) { continue; }
|
||||
render_chat_message(&message[index], count++);
|
||||
message[index].life--;
|
||||
}
|
||||
}
|
8
src/game/chat.h
Normal file
8
src/game/chat.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#ifndef CHAT_H
|
||||
#define CHAT_H
|
||||
|
||||
void render_chat(void);
|
||||
void chat_add_message(char* ascii, u8 isLocal);
|
||||
void chat_start_input(void);
|
||||
|
||||
#endif
|
|
@ -31,6 +31,7 @@
|
|||
#ifdef EXT_OPTIONS_MENU
|
||||
#include "options_menu.h"
|
||||
#endif
|
||||
#include "chat.h"
|
||||
|
||||
u16 gDialogColorFadeTimer;
|
||||
s8 gLastDialogLineNum;
|
||||
|
@ -415,27 +416,43 @@ void render_multi_text_string(s16 *xPos, s16 *yPos, s8 multiTextID)
|
|||
void str_ascii_to_dialog(const char* string, u8* dialog, u16 length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
switch (string[i]) {
|
||||
case '\'': dialog[i] = 0x3E; break;
|
||||
case '.': dialog[i] = 0x3F; break;
|
||||
case ',': dialog[i] = DIALOG_CHAR_COMMA; break;
|
||||
case '-': dialog[i] = 0x9F; break;
|
||||
case '(': dialog[i] = 0xE1; break;
|
||||
case ')': dialog[i] = 0xE3; break;
|
||||
case '&': dialog[i] = 0xE5; break;
|
||||
case '!': dialog[i] = 0xF2; break;
|
||||
case '%': dialog[i] = 0xF3; break;
|
||||
case '?': dialog[i] = 0xF4; break;
|
||||
case '"': dialog[i] = 0xF6; break; // 0xF5 is opening quote
|
||||
case '~': dialog[i] = 0xF7; break;
|
||||
case '*': dialog[i] = 0xFB; break;
|
||||
case ' ': dialog[i] = DIALOG_CHAR_SPACE; break;
|
||||
case '\n': dialog[i] = DIALOG_CHAR_NEWLINE; break;
|
||||
default: dialog[i] = ASCII_TO_DIALOG(string[i]);
|
||||
case '\'': dialog[i] = 0x3E; break;
|
||||
case '.': dialog[i] = 0x3F; break;
|
||||
case ',': dialog[i] = DIALOG_CHAR_COMMA; break;
|
||||
case '-': dialog[i] = 0x9F; break;
|
||||
case '(': dialog[i] = 0xE1; break;
|
||||
case ')': dialog[i] = 0xE3; break;
|
||||
case '&': dialog[i] = 0xE5; break;
|
||||
case '!': dialog[i] = 0xF2; break;
|
||||
case '%': dialog[i] = 0xF3; break;
|
||||
case '?': dialog[i] = 0xF4; break;
|
||||
case '"': dialog[i] = 0xF6; break; // 0xF5 is opening quote
|
||||
case '~': dialog[i] = 0xF7; break;
|
||||
case '*': dialog[i] = 0xFB; break;
|
||||
case ' ': dialog[i] = DIALOG_CHAR_SPACE; break;
|
||||
case '\n': dialog[i] = DIALOG_CHAR_NEWLINE; break;
|
||||
default: dialog[i] = ((u8)string[i] < 0xF0) ? ASCII_TO_DIALOG(string[i]) : string[i];
|
||||
}
|
||||
}
|
||||
dialog[length] = DIALOG_CHAR_TERMINATOR;
|
||||
}
|
||||
|
||||
f32 get_generic_dialog_width(u8* dialog) {
|
||||
f32 width = 0;
|
||||
u8* d = dialog;
|
||||
while (*d != DIALOG_CHAR_TERMINATOR) {
|
||||
width += (f32)(gDialogCharWidths[*d]);
|
||||
d++;
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
f32 get_generic_ascii_string_width(const char* ascii) {
|
||||
u8 dialog[256] = { DIALOG_CHAR_TERMINATOR };
|
||||
str_ascii_to_dialog(ascii, dialog, strlen(ascii));
|
||||
return get_generic_dialog_width(dialog);
|
||||
}
|
||||
|
||||
void print_generic_ascii_string(s16 x, s16 y, const char* ascii) {
|
||||
u8 dialog[256] = { DIALOG_CHAR_TERMINATOR };
|
||||
str_ascii_to_dialog(ascii, dialog, strlen(ascii));
|
||||
|
@ -3178,6 +3195,8 @@ s16 render_menus_and_dialogs() {
|
|||
|
||||
create_dl_ortho_matrix();
|
||||
|
||||
render_chat();
|
||||
|
||||
if (gMenuMode != -1) {
|
||||
switch (gMenuMode) {
|
||||
case 0:
|
||||
|
|
|
@ -117,6 +117,8 @@ void create_dl_identity_matrix(void);
|
|||
void create_dl_translation_matrix(s8 pushOp, f32 x, f32 y, f32 z);
|
||||
void create_dl_ortho_matrix(void);
|
||||
void str_ascii_to_dialog(const char* string, u8* dialog, u16 length);
|
||||
f32 get_generic_dialog_width(u8* dialog);
|
||||
f32 get_generic_ascii_string_width(const char* ascii);
|
||||
void print_generic_ascii_string(s16 x, s16 y, const char* ascii);
|
||||
void print_generic_string(s16 x, s16 y, const u8 *str);
|
||||
void print_hud_lut_string(s8 hudLUT, s16 x, s16 y, const u8 *str);
|
||||
|
@ -150,5 +152,6 @@ void render_hud_cannon_reticle(void);
|
|||
void reset_red_coins_collected(void);
|
||||
s16 render_menus_and_dialogs(void);
|
||||
s16 render_sync_level_screen(void);
|
||||
void create_dl_scale_matrix(s8 pushOp, f32 x, f32 y, f32 z);
|
||||
|
||||
#endif // INGAME_MENU_H
|
||||
|
|
|
@ -178,7 +178,7 @@ static void connect_menu_on_connection_attempt(void) {
|
|||
static void connect_menu_on_click(void) {
|
||||
gConnectionJoinError[0] = '\0';
|
||||
|
||||
keyboard_start_text_input(TIM_IP, custom_menu_close, connect_menu_on_connection_attempt);
|
||||
keyboard_start_text_input(TIM_IP, MAX_TEXT_INPUT, custom_menu_close, connect_menu_on_connection_attempt);
|
||||
|
||||
// fill in our last attempt
|
||||
if (configJoinPort == 0) { configJoinPort = DEFAULT_PORT; }
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include "pc/gfx/gfx_window_manager_api.h"
|
||||
#include "pc/pc_main.h"
|
||||
#include "engine/math_util.h"
|
||||
#include "menu/file_select.h"
|
||||
#include "game/chat.h"
|
||||
|
||||
// TODO: use some common lookup header
|
||||
#define SCANCODE_BACKSPACE 0x0E
|
||||
|
@ -39,6 +41,7 @@ static u32 keyboard_lastkey = VK_INVALID;
|
|||
|
||||
char gTextInput[MAX_TEXT_INPUT];
|
||||
static bool inTextInput = false;
|
||||
static u8 maxTextInput = 0;
|
||||
|
||||
u8 held_ctrl, held_shift, held_alt;
|
||||
static enum TextInputMode textInputMode;
|
||||
|
@ -110,6 +113,13 @@ bool keyboard_on_key_down(int scancode) {
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
if (scancode == SCANCODE_ENTER) {
|
||||
if (sSelectedFileNum != 0) {
|
||||
chat_start_input();
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
int mapped = keyboard_map_scancode(scancode);
|
||||
keyboard_buttons_down |= mapped;
|
||||
keyboard_lastkey = scancode;
|
||||
|
@ -136,10 +146,11 @@ void keyboard_on_all_keys_up(void) {
|
|||
keyboard_buttons_down = 0;
|
||||
}
|
||||
|
||||
char* keyboard_start_text_input(enum TextInputMode inInputMode, void (*onEscape)(void), void (*onEnter)(void)) {
|
||||
char* keyboard_start_text_input(enum TextInputMode inInputMode, u8 inMaxTextInput, void (*onEscape)(void), void (*onEnter)(void)) {
|
||||
// set text-input events
|
||||
textInputOnEscape = onEscape;
|
||||
textInputOnEnter = onEnter;
|
||||
maxTextInput = inMaxTextInput;
|
||||
|
||||
// clear buffer
|
||||
for (int i = 0; i < MAX_TEXT_INPUT; i++) { gTextInput[i] = '\0'; }
|
||||
|
@ -202,6 +213,7 @@ void keyboard_on_text_input(char* text) {
|
|||
while (*text != '\0') {
|
||||
// make sure we don't overrun the buffer
|
||||
if (i >= MAX_TEXT_INPUT) { break; }
|
||||
if (i >= maxTextInput) { break; }
|
||||
|
||||
// copy over character if we're allowed to input it
|
||||
if (keyboard_allow_character_input(*text)) {
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MAX_TEXT_INPUT 256
|
||||
#define MAX_TEXT_INPUT 255
|
||||
extern char gTextInput[];
|
||||
|
||||
enum TextInputMode {
|
||||
|
@ -23,7 +23,7 @@ bool keyboard_on_key_down(int scancode);
|
|||
bool keyboard_on_key_up(int scancode);
|
||||
void keyboard_on_all_keys_up(void);
|
||||
void keyboard_on_text_input(char* text);
|
||||
char* keyboard_start_text_input(enum TextInputMode, void (*onEscape)(void), void (*onEnter)(void));
|
||||
char* keyboard_start_text_input(enum TextInputMode, u8 inMaxTextInput, void (*onEscape)(void), void (*onEnter)(void));
|
||||
void keyboard_stop_text_input(void);
|
||||
bool keyboard_in_text_input(void);
|
||||
|
||||
|
|
|
@ -49,7 +49,11 @@ static void register_launch_command(void) {
|
|||
}
|
||||
#endif
|
||||
strncat(cmd, " --discord 1", MAX_LAUNCH_CMD - 1);
|
||||
DISCORD_REQUIRE(app.activities->register_command(app.activities, cmd));
|
||||
int rc = app.activities->register_command(app.activities, cmd);
|
||||
if (rc != DiscordResult_Ok) {
|
||||
LOG_ERROR("register command failed %d", rc);
|
||||
return;
|
||||
}
|
||||
LOG_INFO("cmd: %s", cmd);
|
||||
}
|
||||
|
||||
|
|
|
@ -126,6 +126,7 @@ void network_receive(u8* data, u16 dataLength) {
|
|||
case PACKET_RESERVATION: network_receive_reservation(&p); break;
|
||||
case PACKET_SAVE_FILE_REQUEST: network_receive_save_file_request(&p); break;
|
||||
case PACKET_SAVE_FILE: network_receive_save_file(&p); break;
|
||||
case PACKET_CHAT: network_receive_chat(&p); break;
|
||||
case PACKET_CUSTOM: network_receive_custom(&p); break;
|
||||
default: LOG_ERROR("received unknown packet: %d", p.buffer[0]);
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ enum PacketType {
|
|||
PACKET_RESERVATION,
|
||||
PACKET_SAVE_FILE_REQUEST,
|
||||
PACKET_SAVE_FILE,
|
||||
PACKET_CHAT,
|
||||
PACKET_CUSTOM = 255,
|
||||
};
|
||||
|
||||
|
@ -184,4 +185,8 @@ u8 network_register_custom_packet(void (*send_callback)(struct Packet* p, void*
|
|||
void network_send_custom(u8 customId, bool reliable, void* params);
|
||||
void network_receive_custom(struct Packet* p);
|
||||
|
||||
// packet_chat.c
|
||||
void network_send_chat(char* message);
|
||||
void network_receive_chat(struct Packet* p);
|
||||
|
||||
#endif
|
||||
|
|
51
src/pc/network/packets/packet_chat.c
Normal file
51
src/pc/network/packets/packet_chat.c
Normal file
|
@ -0,0 +1,51 @@
|
|||
#include <stdio.h>
|
||||
#include "../network.h"
|
||||
#include "game/chat.h"
|
||||
|
||||
static u8 localChatId = 1;
|
||||
|
||||
// two-player hack: the remoteChatIds stuff is only valid for the one remote player
|
||||
// will need to be extended if MAX_PLAYERS is ever increased
|
||||
#define MAX_CHAT_IDS 16
|
||||
static u8 remoteChatIds[MAX_CHAT_IDS] = { 0 };
|
||||
static u8 onRemoteChatId = 0;
|
||||
|
||||
void network_send_chat(char* message) {
|
||||
u16 messageLength = strlen(message);
|
||||
struct Packet p;
|
||||
packet_init(&p, PACKET_CHAT, true);
|
||||
packet_write(&p, &localChatId, sizeof(u8));
|
||||
packet_write(&p, &messageLength, sizeof(u16));
|
||||
packet_write(&p, message, messageLength * sizeof(u8));
|
||||
|
||||
network_send(&p);
|
||||
localChatId++;
|
||||
}
|
||||
|
||||
void network_receive_chat(struct Packet* p) {
|
||||
u8 remoteChatId = 0;
|
||||
u16 remoteMessageLength = 0;
|
||||
char remoteMessage[255] = { 0 };
|
||||
|
||||
packet_read(p, &remoteChatId, sizeof(u8));
|
||||
packet_read(p, &remoteMessageLength, sizeof(u16));
|
||||
if (remoteMessageLength > 255) { remoteMessageLength = 254; }
|
||||
packet_read(p, &remoteMessage, remoteMessageLength * sizeof(u8));
|
||||
|
||||
// check if remote chat id has already been seen
|
||||
for (u16 i = 0; i < MAX_CHAT_IDS; i++) {
|
||||
if (remoteChatIds[i] == remoteChatId) {
|
||||
// we already saw this message!
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// cache the seen id
|
||||
remoteChatIds[onRemoteChatId] = remoteChatId;
|
||||
onRemoteChatId = (onRemoteChatId + 1) % MAX_CHAT_IDS;
|
||||
|
||||
// add the message
|
||||
chat_add_message(remoteMessage, FALSE);
|
||||
|
||||
return;
|
||||
}
|
Loading…
Reference in a new issue