1 /*
2  Copyright (c) 2013 yvt
3  based on code of pysnip (c) Mathias Kaerlev 2011-2012.
4 
5  This file is part of OpenSpades.
6 
7  OpenSpades is free software: you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation, either version 3 of the License, or
10  (at your option) any later version.
11 
12  OpenSpades is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with OpenSpades.  If not, see <http://www.gnu.org/licenses/>.
19 
20  */
21 
22 #include <cstdlib>
23 
24 #include "Client.h"
25 
26 #include <Core/Settings.h>
27 #include <Core/Strings.h>
28 #include <Core/Strings.h>
29 
30 #include "IAudioChunk.h"
31 #include "IAudioDevice.h"
32 
33 #include "CTFGameMode.h"
34 #include "GameMap.h"
35 #include "IGameMode.h"
36 #include "TCGameMode.h"
37 #include "World.h"
38 #include "GameProperties.h"
39 
40 #include "CenterMessageView.h"
41 #include "ChatWindow.h"
42 #include "ClientUI.h"
43 
44 #include "NetClient.h"
45 
46 DEFINE_SPADES_SETTING(cg_centerMessage, "2");
47 
48 namespace spades {
49 	namespace client {
50 
51 #pragma mark - Server Packet Handlers
52 
LocalPlayerCreated()53 		void Client::LocalPlayerCreated() {
54 			freeCameraState.position = world->GetLocalPlayer()->GetEye();
55 			weapInput = WeaponInput();
56 			playerInput = PlayerInput();
57 			keypadInput = KeypadInput();
58 
59 			toolRaiseState = .0f;
60 		}
61 
JoinedGame()62 		void Client::JoinedGame() {
63 			// Note: A local player doesn't exist yet
64 
65 			// Prepare the spectate mode
66 			followCameraState.enabled = false;
67 			freeCameraState.position = MakeVector3(256, 256, 30);
68 			freeCameraState.velocity = MakeVector3(0, 0, 0);
69 		}
70 
PlayerCreatedBlock(spades::client::Player * p)71 		void Client::PlayerCreatedBlock(spades::client::Player *p) {
72 			SPADES_MARK_FUNCTION();
73 
74 			if (!IsMuted()) {
75 				Handle<IAudioChunk> c =
76 				  audioDevice->RegisterSound("Sounds/Weapons/Block/Build.opus");
77 				audioDevice->Play(c, p->GetEye() + p->GetFront(), AudioParam());
78 			}
79 		}
80 
TeamCapturedTerritory(int teamId,int terId)81 		void Client::TeamCapturedTerritory(int teamId, int terId) {
82 			TCGameMode::Territory *ter =
83 			  static_cast<TCGameMode *>(world->GetMode())->GetTerritory(terId);
84 			int old = ter->ownerTeamId;
85 			std::string msg;
86 			std::string teamName =
87 			  chatWindow->TeamColorMessage(world->GetTeam(teamId).name, teamId);
88 			if (old < 2) {
89 				std::string otherTeam = chatWindow->TeamColorMessage(world->GetTeam(old).name, old);
90 				msg = _Tr("Client", "{0} captured {1}'s territory", teamName, otherTeam);
91 			} else {
92 				msg = _Tr("Client", "{0} captured an neutral territory", teamName);
93 			}
94 			chatWindow->AddMessage(msg);
95 
96 			if ((int)cg_centerMessage != 0) {
97 				teamName = world->GetTeam(teamId).name;
98 				if (old < 2) {
99 					std::string otherTeam = world->GetTeam(old).name;
100 					msg = _Tr("Client", "{0} captured {1}'s Territory", teamName, otherTeam);
101 				} else {
102 					msg = _Tr("Client", "{0} captured an Neutral Territory", teamName);
103 				}
104 				NetLog("%s", msg.c_str());
105 				centerMessageView->AddMessage(msg);
106 			}
107 
108 			if (world->GetLocalPlayer() && !IsMuted()) {
109 				if (teamId == world->GetLocalPlayer()->GetTeamId()) {
110 					Handle<IAudioChunk> chunk =
111 					  audioDevice->RegisterSound("Sounds/Feedback/TC/YourTeamCaptured.opus");
112 					audioDevice->PlayLocal(chunk, AudioParam());
113 				} else {
114 					Handle<IAudioChunk> chunk =
115 					  audioDevice->RegisterSound("Sounds/Feedback/TC/EnemyCaptured.opus");
116 					audioDevice->PlayLocal(chunk, AudioParam());
117 				}
118 			}
119 		}
120 
PlayerCapturedIntel(spades::client::Player * p)121 		void Client::PlayerCapturedIntel(spades::client::Player *p) {
122 			std::string msg;
123 			{
124 				std::string holderName = chatWindow->TeamColorMessage(p->GetName(), p->GetTeamId());
125 
126 				std::string otherTeamName = chatWindow->TeamColorMessage(
127 				  world->GetTeam(1 - p->GetTeamId()).name, 1 - p->GetTeamId());
128 				msg = _Tr("Client", "{0} captured {1}'s intel", holderName, otherTeamName);
129 				chatWindow->AddMessage(msg);
130 			}
131 
132 			if ((int)cg_centerMessage != 0) {
133 				std::string holderName = p->GetName();
134 				std::string otherTeamName = world->GetTeam(1 - p->GetTeamId()).name;
135 				msg = _Tr("Client", "{0} captured {1}'s Intel.", holderName, otherTeamName);
136 				NetLog("%s", msg.c_str());
137 				centerMessageView->AddMessage(msg);
138 			}
139 
140 			if (world->GetLocalPlayer() && !IsMuted()) {
141 				if (p->GetTeamId() == world->GetLocalPlayer()->GetTeamId()) {
142 					Handle<IAudioChunk> chunk =
143 					  audioDevice->RegisterSound("Sounds/Feedback/CTF/YourTeamCaptured.opus");
144 					audioDevice->PlayLocal(chunk, AudioParam());
145 				} else {
146 					Handle<IAudioChunk> chunk =
147 					  audioDevice->RegisterSound("Sounds/Feedback/CTF/EnemyCaptured.opus");
148 					audioDevice->PlayLocal(chunk, AudioParam());
149 				}
150 			}
151 		}
152 
PlayerPickedIntel(spades::client::Player * p)153 		void Client::PlayerPickedIntel(spades::client::Player *p) {
154 			std::string msg;
155 			{
156 				std::string holderName = chatWindow->TeamColorMessage(p->GetName(), p->GetTeamId());
157 				std::string otherTeamName = chatWindow->TeamColorMessage(
158 				  world->GetTeam(1 - p->GetTeamId()).name, 1 - p->GetTeamId());
159 				msg = _Tr("Client", "{0} picked up {1}'s intel", holderName, otherTeamName);
160 				chatWindow->AddMessage(msg);
161 			}
162 
163 			if ((int)cg_centerMessage != 0) {
164 				std::string holderName = p->GetName();
165 				std::string otherTeamName = world->GetTeam(1 - p->GetTeamId()).name;
166 				msg = _Tr("Client", "{0} picked up {1}'s Intel.", holderName, otherTeamName);
167 				NetLog("%s", msg.c_str());
168 				centerMessageView->AddMessage(msg);
169 			}
170 
171 			if (!IsMuted()) {
172 				Handle<IAudioChunk> chunk =
173 				  audioDevice->RegisterSound("Sounds/Feedback/CTF/PickedUp.opus");
174 				audioDevice->PlayLocal(chunk, AudioParam());
175 			}
176 		}
177 
PlayerDropIntel(spades::client::Player * p)178 		void Client::PlayerDropIntel(spades::client::Player *p) {
179 			std::string msg;
180 			{
181 				std::string holderName = chatWindow->TeamColorMessage(p->GetName(), p->GetTeamId());
182 				std::string otherTeamName = chatWindow->TeamColorMessage(
183 				  world->GetTeam(1 - p->GetTeamId()).name, 1 - p->GetTeamId());
184 				msg = _Tr("Client", "{0} dropped {1}'s intel", holderName, otherTeamName);
185 				chatWindow->AddMessage(msg);
186 			}
187 
188 			if ((int)cg_centerMessage != 0) {
189 				std::string holderName = p->GetName();
190 				std::string otherTeamName = world->GetTeam(1 - p->GetTeamId()).name;
191 				msg = _Tr("Client", "{0} dropped {1}'s Intel", holderName, otherTeamName);
192 				NetLog("%s", msg.c_str());
193 				centerMessageView->AddMessage(msg);
194 			}
195 		}
196 
PlayerDestroyedBlockWithWeaponOrTool(spades::IntVector3 blk)197 		void Client::PlayerDestroyedBlockWithWeaponOrTool(spades::IntVector3 blk) {
198 			Vector3 origin = {blk.x + .5f, blk.y + .5f, blk.z + .5f};
199 
200 			if (!map->IsSolid(blk.x, blk.y, blk.z))
201 				return;
202 			;
203 
204 			Handle<IAudioChunk> c = audioDevice->RegisterSound("Sounds/Misc/BlockDestroy.opus");
205 			if (!IsMuted()) {
206 				audioDevice->Play(c, origin, AudioParam());
207 			}
208 
209 			uint32_t col = map->GetColor(blk.x, blk.y, blk.z);
210 			IntVector3 colV = {(uint8_t)col, (uint8_t)(col >> 8), (uint8_t)(col >> 16)};
211 
212 			EmitBlockDestroyFragments(blk, colV);
213 		}
214 
PlayerDiggedBlock(spades::IntVector3 blk)215 		void Client::PlayerDiggedBlock(spades::IntVector3 blk) {
216 			Vector3 origin = {blk.x + .5f, blk.y + .5f, blk.z + .5f};
217 
218 			Handle<IAudioChunk> c = audioDevice->RegisterSound("Sounds/Misc/BlockDestroy.opus");
219 			if (!IsMuted()) {
220 				audioDevice->Play(c, origin, AudioParam());
221 			}
222 
223 			for (int z = blk.z - 1; z <= blk.z + 1; z++) {
224 				if (z < 0 || z > 61)
225 					continue;
226 				if (!map->IsSolid(blk.x, blk.y, z))
227 					continue;
228 				uint32_t col = map->GetColor(blk.x, blk.y, z);
229 				IntVector3 colV = {(uint8_t)col, (uint8_t)(col >> 8), (uint8_t)(col >> 16)};
230 
231 				EmitBlockDestroyFragments(IntVector3::Make(blk.x, blk.y, z), colV);
232 			}
233 		}
234 
PlayerLeaving(spades::client::Player * p)235 		void Client::PlayerLeaving(spades::client::Player *p) {
236 			// Choose the next player if a follow cam is active on this player
237 			if (FollowsNonLocalPlayer(GetCameraMode()) && &GetCameraTargetPlayer() == p) {
238 				FollowNextPlayer(false);
239 
240 				// Still unable to find a substitute?
241 				if (&GetCameraTargetPlayer() == p) {
242 					// Turn off the follow cam mode
243 					followCameraState.enabled = false;
244 				}
245 			}
246 
247 			{
248 				std::string msg;
249 				msg = _Tr("Client", "Player {0} has left",
250 				          chatWindow->TeamColorMessage(p->GetName(), p->GetTeamId()));
251 				chatWindow->AddMessage(msg);
252 			}
253 			{
254 				std::string msg;
255 				msg = _Tr("Client", "Player {0} has left", p->GetName());
256 
257 				auto col = p->GetTeamId() < 2 ? world->GetTeam(p->GetTeamId()).color
258 				                              : IntVector3::Make(255, 255, 255);
259 
260 				NetLog("%s", msg.c_str());
261 				scriptedUI->RecordChatLog(
262 				  msg, MakeVector4(col.x / 255.f, col.y / 255.f, col.z / 255.f, 0.8f));
263 			}
264 			RemoveCorpseForPlayer(p->GetId());
265 		}
266 
PlayerJoinedTeam(spades::client::Player * p)267 		void Client::PlayerJoinedTeam(spades::client::Player *p) {
268 			std::string teamName = world->GetTeam(p->GetTeamId()).name;
269 
270 			if (p->GetTeamId() >= 2) {
271 				teamName = _Tr("Client", "Spectator");
272 			}
273 
274 			{
275 				std::string msg;
276 				msg = _Tr("Client", "{0} joined {1} team", p->GetName(),
277 				          chatWindow->TeamColorMessage(teamName, p->GetTeamId()));
278 				chatWindow->AddMessage(msg);
279 			}
280 			{
281 				std::string msg;
282 				msg = _Tr("Client", "{0} joined {1} team", p->GetName(), teamName);
283 
284 				auto col = p->GetTeamId() < 2 ? world->GetTeam(p->GetTeamId()).color
285 				                              : IntVector3::Make(255, 255, 255);
286 
287 				NetLog("%s", msg.c_str());
288 				scriptedUI->RecordChatLog(
289 				  msg, MakeVector4(col.x / 255.f, col.y / 255.f, col.z / 255.f, 0.8f));
290 			}
291 		}
292 
PlayerSpawned(Player * p)293 		void Client::PlayerSpawned(Player *p) {
294 			if (net->GetGameProperties()->clearCorpseOnRespawn) {
295 				RemoveCorpseForPlayer(p->GetId());
296 			}
297 		}
298 
GrenadeDestroyedBlock(spades::IntVector3 blk)299 		void Client::GrenadeDestroyedBlock(spades::IntVector3 blk) {
300 			for (int x = blk.x - 1; x <= blk.x + 1; x++)
301 				for (int y = blk.y - 1; y <= blk.y + 1; y++)
302 					for (int z = blk.z - 1; z <= blk.z + 1; z++) {
303 						if (z < 0 || z > 61 || x < 0 || x >= 512 || y < 0 || y >= 512)
304 							continue;
305 						if (!map->IsSolid(x, y, z))
306 							continue;
307 						uint32_t col = map->GetColor(x, y, z);
308 						IntVector3 colV = {(uint8_t)col, (uint8_t)(col >> 8), (uint8_t)(col >> 16)};
309 
310 						EmitBlockDestroyFragments(IntVector3::Make(x, y, z), colV);
311 					}
312 		}
313 
TeamWon(int teamId)314 		void Client::TeamWon(int teamId) {
315 			std::string msg;
316 			msg = chatWindow->TeamColorMessage(world->GetTeam(teamId).name, teamId);
317 			msg = _Tr("Client", "{0} wins!", msg);
318 			chatWindow->AddMessage(msg);
319 
320 			msg = world->GetTeam(teamId).name;
321 			msg = _Tr("Client", "{0} Wins!", msg);
322 			NetLog("%s", msg.c_str());
323 			centerMessageView->AddMessage(msg);
324 
325 			scriptedUI->RecordChatLog(msg, MakeVector4(1.f, 1.f, 1.f, 0.8f));
326 
327 			if (world->GetLocalPlayer()) {
328 				if (teamId == world->GetLocalPlayer()->GetTeamId()) {
329 					Handle<IAudioChunk> chunk =
330 					  audioDevice->RegisterSound("Sounds/Feedback/Win.opus");
331 					audioDevice->PlayLocal(chunk, AudioParam());
332 				} else {
333 					Handle<IAudioChunk> chunk =
334 					  audioDevice->RegisterSound("Sounds/Feedback/Lose.opus");
335 					audioDevice->PlayLocal(chunk, AudioParam());
336 				}
337 			}
338 		}
339 
MarkWorldUpdate()340 		void Client::MarkWorldUpdate() {
341 			upsCounter.MarkFrame();
342 		}
343 	}
344 }
345