1 /*
2 This file is part of Warzone 2100.
3 Copyright (C) 2020 Warzone 2100 Project
4
5 Warzone 2100 is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 Warzone 2100 is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with Warzone 2100; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #include "chat.h"
21 #include "ai.h"
22 #include "lib/netplay/netplay.h"
23 #include "qtscript.h"
24
InGameChatMessage(uint32_t messageSender,char const * messageText)25 InGameChatMessage::InGameChatMessage(uint32_t messageSender, char const *messageText)
26 {
27 sender = messageSender;
28 text = messageText;
29 }
30
isGlobal() const31 bool InGameChatMessage::isGlobal() const
32 {
33 return !toAllies && toPlayers.empty();
34 }
35
shouldReceive(uint32_t playerIndex) const36 bool InGameChatMessage::shouldReceive(uint32_t playerIndex) const
37 {
38 return isGlobal() || toPlayers.find(playerIndex) != toPlayers.end() || (toAllies && aiCheckAlliances(sender, playerIndex));
39 }
40
getReceivers() const41 std::vector<uint32_t> InGameChatMessage::getReceivers() const
42 {
43 std::vector<uint32_t> receivers;
44
45 for (auto playerIndex = 0; playerIndex < game.maxPlayers; playerIndex++)
46 {
47 if (shouldReceive(playerIndex) && openchannels[playerIndex])
48 {
49 receivers.push_back(playerIndex);
50 }
51 }
52
53 return receivers;
54 }
55
formatReceivers() const56 std::string InGameChatMessage::formatReceivers() const
57 {
58 if (isGlobal()) {
59 return _("Global");
60 }
61
62 if (toAllies && toPlayers.empty()) {
63 return _("Allies");
64 }
65
66 auto directs = toPlayers.begin();
67 std::stringstream ss;
68 if (toAllies) {
69 ss << _("Allies");
70 } else {
71 ss << _("private to ");
72 ss << getPlayerName(*directs++);
73 }
74
75 while (directs != toPlayers.end())
76 {
77 auto nextName = getPlayerName(*directs++);
78 ss << (directs == toPlayers.end() ? _(" and ") : ", ");
79 ss << nextName;
80 }
81
82 return ss.str();
83 }
84
sendToHumanPlayers()85 void InGameChatMessage::sendToHumanPlayers()
86 {
87 char formatted[MAX_CONSOLE_STRING_LENGTH];
88 ssprintf(formatted, "%s (%s): %s", getPlayerName(sender), formatReceivers().c_str(), text);
89
90 auto message = NetworkTextMessage(sender, formatted);
91 message.teamSpecific = toAllies && toPlayers.empty();
92
93 if (sender == selectedPlayer || shouldReceive(selectedPlayer)) {
94 printInGameTextMessage(message);
95 }
96
97 if (isGlobal()) {
98 message.enqueue(NETbroadcastQueue());
99 return;
100 }
101
102 for (auto receiver: getReceivers())
103 {
104 if (isHumanPlayer(receiver))
105 {
106 message.enqueue(NETnetQueue(receiver));
107 }
108 }
109 }
110
sendToAiPlayer(uint32_t receiver)111 void InGameChatMessage::sendToAiPlayer(uint32_t receiver)
112 {
113 if (!ingame.localOptionsReceived)
114 {
115 return;
116 }
117
118 uint32_t responsiblePlayer = whosResponsible(receiver);
119
120 if (responsiblePlayer >= MAX_PLAYERS)
121 {
122 debug(LOG_ERROR, "sendToAiPlayer() - responsiblePlayer >= MAX_PLAYERS");
123 return;
124 }
125
126 if (!isHumanPlayer(responsiblePlayer))
127 {
128 debug(LOG_ERROR, "sendToAiPlayer() - responsiblePlayer is not human.");
129 return;
130 }
131
132 NETbeginEncode(NETnetQueue(responsiblePlayer), NET_AITEXTMSG);
133 NETuint32_t(&sender);
134 NETuint32_t(&receiver);
135 NETstring(text, MAX_CONSOLE_STRING_LENGTH);
136 NETend();
137 }
138
sendToAiPlayers()139 void InGameChatMessage::sendToAiPlayers()
140 {
141 for (auto receiver: getReceivers())
142 {
143 if (!isHumanPlayer(receiver))
144 {
145 if (myResponsibility(receiver))
146 {
147 triggerEventChat(sender, receiver, text);
148 }
149 else
150 {
151 sendToAiPlayer(receiver);
152 }
153 }
154 }
155 }
156
addReceiverByPosition(uint32_t playerPosition)157 void InGameChatMessage::addReceiverByPosition(uint32_t playerPosition)
158 {
159 toPlayers.insert(findPlayerIndexByPosition(playerPosition));
160 }
161
addReceiverByIndex(uint32_t playerIndex)162 void InGameChatMessage::addReceiverByIndex(uint32_t playerIndex)
163 {
164 toPlayers.insert(playerIndex);
165 }
166
send()167 void InGameChatMessage::send()
168 {
169 sendToHumanPlayers();
170 sendToAiPlayers();
171 triggerEventChat(sender, sender, text);
172 }
173