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