/* ** Command & Conquer Generals(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// // FILE: Chat.cpp ////////////////////////////////////////////////////// // Generals GameSpy chat-related code // Author: Matthew D. Campbell, July 2002 #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine #include "Common/AudioEventRTS.h" #include "Common/INI.h" #include "GameClient/GameText.h" #include "GameClient/GadgetListBox.h" #include "GameClient/LanguageFilter.h" #include "GameClient/GameWindowManager.h" #include "GameNetwork/GameSpy/PeerDefsImplementation.h" #include "GameNetwork/GameSpy/PeerThread.h" #include "GameClient/InGameUI.h" #ifdef _INTERNAL // for occasional debugging... //#pragma optimize("", off) //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes") #endif #define OFFSET(x) (sizeof(Int) * (x)) static const FieldParse GameSpyColorFieldParse[] = { { "Default", INI::parseColorInt, NULL, OFFSET(GSCOLOR_DEFAULT) }, { "CurrentRoom", INI::parseColorInt, NULL, OFFSET(GSCOLOR_CURRENTROOM) }, { "ChatRoom", INI::parseColorInt, NULL, OFFSET(GSCOLOR_ROOM) }, { "Game", INI::parseColorInt, NULL, OFFSET(GSCOLOR_GAME) }, { "GameFull", INI::parseColorInt, NULL, OFFSET(GSCOLOR_GAME_FULL) }, { "GameCRCMismatch", INI::parseColorInt, NULL, OFFSET(GSCOLOR_GAME_CRCMISMATCH) }, { "PlayerNormal", INI::parseColorInt, NULL, OFFSET(GSCOLOR_PLAYER_NORMAL) }, { "PlayerOwner", INI::parseColorInt, NULL, OFFSET(GSCOLOR_PLAYER_OWNER) }, { "PlayerBuddy", INI::parseColorInt, NULL, OFFSET(GSCOLOR_PLAYER_BUDDY) }, { "PlayerSelf", INI::parseColorInt, NULL, OFFSET(GSCOLOR_PLAYER_SELF) }, { "PlayerIgnored", INI::parseColorInt, NULL, OFFSET(GSCOLOR_PLAYER_IGNORED) }, { "ChatNormal", INI::parseColorInt, NULL, OFFSET(GSCOLOR_CHAT_NORMAL) }, { "ChatEmote", INI::parseColorInt, NULL, OFFSET(GSCOLOR_CHAT_EMOTE) }, { "ChatOwner", INI::parseColorInt, NULL, OFFSET(GSCOLOR_CHAT_OWNER) }, { "ChatOwnerEmote", INI::parseColorInt, NULL, OFFSET(GSCOLOR_CHAT_OWNER_EMOTE) }, { "ChatPriv", INI::parseColorInt, NULL, OFFSET(GSCOLOR_CHAT_PRIVATE) }, { "ChatPrivEmote", INI::parseColorInt, NULL, OFFSET(GSCOLOR_CHAT_PRIVATE_EMOTE) }, { "ChatPrivOwner", INI::parseColorInt, NULL, OFFSET(GSCOLOR_CHAT_PRIVATE_OWNER) }, { "ChatPrivOwnerEmote", INI::parseColorInt, NULL, OFFSET(GSCOLOR_CHAT_PRIVATE_OWNER_EMOTE) }, { "ChatBuddy", INI::parseColorInt, NULL, OFFSET(GSCOLOR_CHAT_BUDDY) }, { "ChatSelf", INI::parseColorInt, NULL, OFFSET(GSCOLOR_CHAT_SELF) }, { "AcceptTrue", INI::parseColorInt, NULL, OFFSET(GSCOLOR_ACCEPT_TRUE) }, { "AcceptFalse", INI::parseColorInt, NULL, OFFSET(GSCOLOR_ACCEPT_FALSE) }, { "MapSelected", INI::parseColorInt, NULL, OFFSET(GSCOLOR_MAP_SELECTED) }, { "MapUnselected", INI::parseColorInt, NULL, OFFSET(GSCOLOR_MAP_UNSELECTED) }, { "MOTD", INI::parseColorInt, NULL, OFFSET(GSCOLOR_MOTD) }, { "MOTDHeading", INI::parseColorInt, NULL, OFFSET(GSCOLOR_MOTD_HEADING) }, { NULL, NULL, NULL, 0 } // keep this last }; void INI::parseOnlineChatColorDefinition( INI* ini ) { // parse the ini definition ini->initFromINI( GameSpyColor, GameSpyColorFieldParse ); } Color GameSpyColor[GSCOLOR_MAX] = { GameMakeColor(255,255,255,255), // GSCOLOR_DEFAULT GameMakeColor(255,255, 0,255), // GSCOLOR_CURRENTROOM GameMakeColor(255,255,255,255), // GSCOLOR_ROOM GameMakeColor(128,128,0,255), // GSCOLOR_GAME GameMakeColor(128,128,128,255), // GSCOLOR_GAME_FULL GameMakeColor(128,128,128,255), // GSCOLOR_GAME_CRCMISMATCH GameMakeColor(255, 0, 0,255), // GSCOLOR_PLAYER_NORMAL GameMakeColor(255, 0,255,255), // GSCOLOR_PLAYER_OWNER GameMakeColor(255, 0,128,255), // GSCOLOR_PLAYER_BUDDY GameMakeColor(255, 0, 0,255), // GSCOLOR_PLAYER_SELF GameMakeColor(128,128,128,255), // GSCOLOR_PLAYER_IGNORED GameMakeColor(255,0,0,255), // GSCOLOR_CHAT_NORMAL GameMakeColor(255,128,0,255), // GSCOLOR_CHAT_EMOTE, GameMakeColor(255,255,0,255), // GSCOLOR_CHAT_OWNER, GameMakeColor(128,255,0,255), // GSCOLOR_CHAT_OWNER_EMOTE, GameMakeColor(0,0,255,255), // GSCOLOR_CHAT_PRIVATE, GameMakeColor(0,255,255,255), // GSCOLOR_CHAT_PRIVATE_EMOTE, GameMakeColor(255,0,255,255), // GSCOLOR_CHAT_PRIVATE_OWNER, GameMakeColor(255,128,255,255), // GSCOLOR_CHAT_PRIVATE_OWNER_EMOTE, GameMakeColor(255, 0,255,255), // GSCOLOR_CHAT_BUDDY, GameMakeColor(255, 0,128,255), // GSCOLOR_CHAT_SELF, GameMakeColor( 0,255, 0,255), // GSCOLOR_ACCEPT_TRUE, GameMakeColor(255, 0, 0,255), // GSCOLOR_ACCEPT_FALSE, GameMakeColor(255,255, 0,255), // GSCOLOR_MAP_SELECTED, GameMakeColor(255,255,255,255), // GSCOLOR_MAP_UNSELECTED, GameMakeColor(255,255,255,255), // GSCOLOR_MOTD, GameMakeColor(255,255, 0,255), // GSCOLOR_MOTD_HEADING, }; Bool GameSpyInfo::sendChat( UnicodeString message, Bool isAction, GameWindow *playerListbox ) { RoomType roomType = StagingRoom; if (getCurrentGroupRoom()) roomType = GroupRoom; PeerRequest req; req.text = message.str(); message.trim(); // Echo the user's input to the chat window if (!message.isEmpty()) { if (!playerListbox) { // Public message req.message.isAction = isAction; req.peerRequestType = PeerRequest::PEERREQUEST_MESSAGEROOM; TheGameSpyPeerMessageQueue->addRequest(req); return false; } // Get the selections (is this a private message?) Int maxSel = GadgetListBoxGetListLength(playerListbox); Int *selections; GadgetListBoxGetSelected(playerListbox, (Int *)&selections); if (selections[0] == -1) { // Public message req.message.isAction = isAction; req.peerRequestType = PeerRequest::PEERREQUEST_MESSAGEROOM; TheGameSpyPeerMessageQueue->addRequest(req); return false; } else { // Private message // Construct a list AsciiString names = AsciiString::TheEmptyString; AsciiString tmp = AsciiString::TheEmptyString; AsciiString aStr; // AsciiString buf for translating Unicode entries names.format("%s", TheGameSpyInfo->getLocalName().str()); for (int i=0; igetLocalName())) { tmp.format(",%s", aStr.str()); names.concat(tmp); } } else { break; } } if (!names.isEmpty()) { req.nick = names.str(); req.message.isAction = isAction; req.peerRequestType = PeerRequest::PEERREQUEST_MESSAGEPLAYER; TheGameSpyPeerMessageQueue->addRequest(req); } return true; } } return false; } void GameSpyInfo::addChat( AsciiString nick, Int profileID, UnicodeString msg, Bool isPublic, Bool isAction, GameWindow *win ) { PlayerInfoMap::iterator it = getPlayerInfoMap()->find(nick); if (it != getPlayerInfoMap()->end()) { addChat( it->second, msg, isPublic, isAction, win ); } else { } } void GameSpyInfo::addChat( PlayerInfo p, UnicodeString msg, Bool isPublic, Bool isAction, GameWindow *win ) { Int style; if(isSavedIgnored(p.m_profileID) || isIgnored(p.m_name)) return; Bool isOwner = p.m_flags & PEER_FLAG_OP; Bool isBuddy = getBuddyMap()->find(p.m_profileID) != getBuddyMap()->end(); Bool isMe = p.m_name.compare(TheGameSpyInfo->getLocalName()) == 0; if(!isMe) { if(m_disallowAsainText) { const WideChar *buff = msg.str(); Int length = msg.getLength(); for(Int i = 0; i < length; ++i) { if(buff[i] >= 256) return; } } else if(m_disallowNonAsianText) { const WideChar *buff = msg.str(); Int length = msg.getLength(); Bool hasUnicode = FALSE; for(Int i = 0; i < length; ++i) { if(buff[i] >= 256) { hasUnicode = TRUE; break; } } if(!hasUnicode) return; } if (!isPublic) { AudioEventRTS privMsgAudio("GUIMessageReceived"); if( TheAudio ) { TheAudio->addAudioEvent( &privMsgAudio ); } // end if } } if (isBuddy) { style = GSCOLOR_CHAT_BUDDY; } else if (isPublic && isAction) { style = (isOwner)?GSCOLOR_CHAT_OWNER_EMOTE:GSCOLOR_CHAT_EMOTE; } else if (isPublic) { style = (isOwner)?GSCOLOR_CHAT_OWNER:GSCOLOR_CHAT_NORMAL; } else if (isAction) { style = (isOwner)?GSCOLOR_CHAT_PRIVATE_OWNER_EMOTE:GSCOLOR_CHAT_PRIVATE_EMOTE; } else { style = (isOwner)?GSCOLOR_CHAT_PRIVATE_OWNER:GSCOLOR_CHAT_PRIVATE; } UnicodeString name; name.translate(p.m_name); // filters language // if( TheGlobalData->m_languageFilterPref ) // { TheLanguageFilter->filterLine(msg); // } UnicodeString fullMsg; if (isAction) { fullMsg.format( L"%ls %ls", name.str(), msg.str() ); } else { fullMsg.format( L"[%ls] %ls", name.str(), msg.str() ); } Int index = addText(fullMsg, GameSpyColor[style], win); if (index >= 0) { GadgetListBoxSetItemData(win, (void *)p.m_profileID, index); } } Int GameSpyInfo::addText( UnicodeString message, Color c, GameWindow *win ) { if (TheGameSpyGame && TheGameSpyGame->isInGame() && TheGameSpyGame->isGameInProgress()) { static AudioEventRTS messageFromChatSound("GUIMessageReceived"); TheAudio->addAudioEvent(&messageFromChatSound); TheInGameUI->message(message); } if (!win) { // try to pick up a registered text window if (m_textWindows.empty()) return -1; win = *(m_textWindows.begin()); } Int index = GadgetListBoxAddEntryText(win, message, c, -1, -1); GadgetListBoxSetItemData(win, (void *)-1, index); return index; } void GameSpyInfo::registerTextWindow( GameWindow *win ) { m_textWindows.insert(win); } void GameSpyInfo::unregisterTextWindow( GameWindow *win ) { m_textWindows.erase(win); }