1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 #include <cmath>
4 #include <SDL_mouse.h>
5 #include <SDL_keyboard.h>
6 
7 #include "InMapDraw.h"
8 
9 #include "InMapDrawModel.h"
10 #include "Camera.h"
11 #include "Game.h"
12 #include "GlobalUnsynced.h"
13 #include "Game/Players/Player.h"
14 #include "Game/Players/PlayerHandler.h"
15 #include "Net/Protocol/BaseNetProtocol.h"
16 #include "UI/MiniMap.h"
17 #include "UI/MouseHandler.h"
18 #include "ExternalAI/AILegacySupport.h" // {Point, Line}Marker
19 #include "Map/Ground.h"
20 #include "Map/ReadMap.h"
21 #include "System/Net/UnpackPacket.h"
22 #include "Sim/Misc/TeamHandler.h"
23 #include "System/EventHandler.h"
24 #include "System/EventClient.h"
25 #include "Net/Protocol/NetProtocol.h"
26 #include "System/Log/ILog.h"
27 #include "System/Sound/ISound.h"
28 #include "System/Sound/ISoundChannels.h"
29 
30 
31 CInMapDraw* inMapDrawer = NULL;
32 
33 /**
34  * This simply makes a noice appear when a map point is placed.
35  * We will only receive an even (and thus make a sound) when we are allwoed to
36  * know about it.
37  */
38 class CNotificationPeeper : public CEventClient
39 {
40 public:
CNotificationPeeper()41 	CNotificationPeeper()
42 		: CEventClient("NotificationPeeper", 99, false)
43 	{
44 		blippSound = sound->GetSoundId("MapPoint");
45 	}
46 
WantsEvent(const std::string & eventName)47 	virtual bool WantsEvent(const std::string& eventName) {
48 		return (eventName == "MapDrawCmd");
49 	}
50 
MapDrawCmd(int playerID,int type,const float3 * pos0,const float3 * pos1,const std::string * label)51 	virtual bool MapDrawCmd(int playerID, int type, const float3* pos0, const float3* pos1, const std::string* label) {
52 
53 		if (type == MAPDRAW_POINT) {
54 			const CPlayer* sender = playerHandler->Player(playerID);
55 
56 			// if we happen to be in drawAll mode, notify us now
57 			// even if this message is not intented for our ears
58 			LOG("%s added point: %s", sender->name.c_str(), label->c_str());
59 			eventHandler.LastMessagePosition(*pos0);
60 			Channels::UserInterface->PlaySample(blippSound, *pos0);
61 			minimap->AddNotification(*pos0, OnesVector, 1.0f);
62 		}
63 
64 		return false;
65 	}
66 
67 private:
68 	int blippSound;
69 };
70 
71 
CInMapDraw()72 CInMapDraw::CInMapDraw()
73 	: drawMode(false)
74 	, wantLabel(false)
75 	, lastLeftClickTime(0.0f)
76 	, lastDrawTime(0.0f)
77 	, lastPos(OnesVector)
78 	, allowSpecMapDrawing(true)
79 	, allowLuaMapDrawing(true)
80 	, notificationPeeper(NULL)
81 {
82 	notificationPeeper = new CNotificationPeeper();
83 	eventHandler.AddClient(notificationPeeper);
84 }
85 
86 
~CInMapDraw()87 CInMapDraw::~CInMapDraw()
88 {
89 	eventHandler.RemoveClient(notificationPeeper);
90 	delete notificationPeeper;
91 	notificationPeeper = NULL;
92 }
93 
94 
MousePress(int x,int y,int button)95 void CInMapDraw::MousePress(int x, int y, int button)
96 {
97 	float3 pos = GetMouseMapPos();
98 	if (pos.x < 0)
99 		return;
100 
101 	switch (button) {
102 		case SDL_BUTTON_LEFT: {
103 			if (lastLeftClickTime > gu->gameTime - 0.3f) {
104 				PromptLabel(pos);
105 			}
106 			lastLeftClickTime = gu->gameTime;
107 			break;
108 		}
109 		case SDL_BUTTON_RIGHT: {
110 			SendErase(pos);
111 			break;
112 		}
113 		case SDL_BUTTON_MIDDLE:{
114 			SendPoint(pos, "", false);
115 			break;
116 		}
117 	}
118 
119 	lastPos = pos;
120 }
121 
122 
MouseRelease(int x,int y,int button)123 void CInMapDraw::MouseRelease(int x, int y, int button)
124 {
125 	// TODO implement CInMapDraw::MouseRelease
126 }
127 
128 
MouseMove(int x,int y,int dx,int dy,int button)129 void CInMapDraw::MouseMove(int x, int y, int dx, int dy, int button)
130 {
131 	float3 pos = GetMouseMapPos();
132 	if (pos.x < 0) {
133 		return;
134 	}
135 	if (mouse->buttons[SDL_BUTTON_LEFT].pressed && (lastDrawTime < (gu->gameTime - 0.05f))) {
136 		SendLine(pos, lastPos, false);
137 		lastDrawTime = gu->gameTime;
138 		lastPos = pos;
139 	}
140 	if (mouse->buttons[SDL_BUTTON_RIGHT].pressed && (lastDrawTime < (gu->gameTime - 0.05f))) {
141 		SendErase(pos);
142 		lastDrawTime = gu->gameTime;
143 	}
144 
145 }
146 
147 
GetMouseMapPos()148 float3 CInMapDraw::GetMouseMapPos() // TODO move to some more global place?
149 {
150 	const float dist = CGround::LineGroundCol(camera->GetPos(), camera->GetPos() + (mouse->dir * globalRendering->viewRange * 1.4f), false);
151 	if (dist < 0) {
152 		return float3(-1.0f, 1.0f, -1.0f);
153 	}
154 	float3 pos = camera->GetPos() + (mouse->dir * dist);
155 	pos.ClampInBounds();
156 	return pos;
157 }
158 
GotNetMsg(boost::shared_ptr<const netcode::RawPacket> & packet)159 int CInMapDraw::GotNetMsg(boost::shared_ptr<const netcode::RawPacket>& packet)
160 {
161 	int playerID = -1;
162 
163 	try {
164 		netcode::UnpackPacket pckt(packet, 2);
165 
166 		unsigned char uPlayerID;
167 		pckt >> uPlayerID;
168 		if (!playerHandler->IsValidPlayer(uPlayerID)) {
169 			throw netcode::UnpackPacketException("Invalid player number");
170 		}
171 		playerID = uPlayerID;
172 
173 		unsigned char drawType;
174 		pckt >> drawType;
175 
176 		switch (drawType) {
177 			case MAPDRAW_POINT: {
178 				short int x, z;
179 				pckt >> x;
180 				pckt >> z;
181 				const float3 pos(x, 0, z);
182 				unsigned char fromLua;
183 				pckt >> fromLua;
184 				string label;
185 				pckt >> label;
186 				if (!fromLua || allowLuaMapDrawing) {
187 					inMapDrawerModel->AddPoint(pos, label, playerID);
188 				}
189 				break;
190 			}
191 			case MAPDRAW_LINE: {
192 				short int x1, z1, x2, z2;
193 				pckt >> x1;
194 				pckt >> z1;
195 				pckt >> x2;
196 				pckt >> z2;
197 				const float3 pos1(x1, 0, z1);
198 				const float3 pos2(x2, 0, z2);
199 				unsigned char fromLua;
200 				pckt >> fromLua;
201 				if (!fromLua || allowLuaMapDrawing) {
202 					inMapDrawerModel->AddLine(pos1, pos2, playerID);
203 				}
204 				break;
205 			}
206 			case MAPDRAW_ERASE: {
207 				short int x, z;
208 				pckt >> x;
209 				pckt >> z;
210 				float3 pos(x, 0, z);
211 				inMapDrawerModel->EraseNear(pos, playerID);
212 				break;
213 			}
214 		}
215 	} catch (const netcode::UnpackPacketException& ex) {
216 		LOG_L(L_WARNING, "Got invalid MapDraw: %s", ex.what());
217 		playerID = -1;
218 	}
219 
220 	return playerID;
221 }
222 
223 
SetSpecMapDrawingAllowed(bool state)224 void CInMapDraw::SetSpecMapDrawingAllowed(bool state)
225 {
226 	allowSpecMapDrawing = state;
227 	LOG("[%s] spectator map-drawing is %s", __FUNCTION__, allowSpecMapDrawing? "enabled": "disabled");
228 }
229 
SetLuaMapDrawingAllowed(bool state)230 void CInMapDraw::SetLuaMapDrawingAllowed(bool state)
231 {
232 	allowLuaMapDrawing = state;
233 	LOG("[%s] Lua map-drawing is %s", __FUNCTION__, allowLuaMapDrawing? "enabled": "disabled");
234 }
235 
236 
237 
SendErase(const float3 & pos)238 void CInMapDraw::SendErase(const float3& pos)
239 {
240 	if (!gu->spectating || allowSpecMapDrawing)
241 		net->Send(CBaseNetProtocol::Get().SendMapErase(gu->myPlayerNum, (short)pos.x, (short)pos.z));
242 }
243 
244 
SendPoint(const float3 & pos,const std::string & label,bool fromLua)245 void CInMapDraw::SendPoint(const float3& pos, const std::string& label, bool fromLua)
246 {
247 	if (!gu->spectating || allowSpecMapDrawing)
248 		net->Send(CBaseNetProtocol::Get().SendMapDrawPoint(gu->myPlayerNum, (short)pos.x, (short)pos.z, label, fromLua));
249 }
250 
251 
SendLine(const float3 & pos,const float3 & pos2,bool fromLua)252 void CInMapDraw::SendLine(const float3& pos, const float3& pos2, bool fromLua)
253 {
254 	if (!gu->spectating || allowSpecMapDrawing)
255 		net->Send(CBaseNetProtocol::Get().SendMapDrawLine(gu->myPlayerNum, (short)pos.x, (short)pos.z, (short)pos2.x, (short)pos2.z, fromLua));
256 }
257 
SendWaitingInput(const std::string & label)258 void CInMapDraw::SendWaitingInput(const std::string& label)
259 {
260 	SendPoint(waitingPoint, label, false);
261 
262 	wantLabel = false;
263 	drawMode = false;
264 }
265 
266 
PromptLabel(const float3 & pos)267 void CInMapDraw::PromptLabel(const float3& pos)
268 {
269 	waitingPoint = pos;
270 	game->userWriting = true;
271 	wantLabel = true;
272 	game->userPrompt = "Label: ";
273 	game->ignoreNextChar = true;
274 }
275 
276 
GetPoints(std::vector<PointMarker> & points,int pointsSizeMax,const std::list<int> & teamIDs)277 void CInMapDraw::GetPoints(std::vector<PointMarker>& points, int pointsSizeMax, const std::list<int>& teamIDs)
278 {
279 	pointsSizeMax = std::min(pointsSizeMax, inMapDrawerModel->GetNumPoints());
280 	points.clear();
281 	points.reserve(pointsSizeMax);
282 
283 	const std::list<CInMapDrawModel::MapPoint>* pointsInt = NULL;
284 	std::list<CInMapDrawModel::MapPoint>::const_iterator point;
285 	std::list<int>::const_iterator it;
286 
287 	for (size_t y = 0; (y < inMapDrawerModel->GetDrawQuadY()) && ((int)points.size() < pointsSizeMax); y++) {
288 		for (size_t x = 0; (x < inMapDrawerModel->GetDrawQuadX()) && ((int)points.size() < pointsSizeMax); x++) {
289 			pointsInt = &(inMapDrawerModel->GetDrawQuad(x, y)->points);
290 
291 			for (point = pointsInt->begin(); (point != pointsInt->end()) && ((int)points.size()  < pointsSizeMax); ++point) {
292 				for (it = teamIDs.begin(); it != teamIDs.end(); ++it) {
293 					if (point->GetTeamID() == *it) {
294 						PointMarker pm;
295 						pm.pos   = point->GetPos();
296 						pm.color = teamHandler->Team(point->GetTeamID())->color;
297 						pm.label = point->GetLabel().c_str();
298 						points.push_back(pm);
299 						break;
300 					}
301 				}
302 			}
303 		}
304 	}
305 }
306 
GetLines(std::vector<LineMarker> & lines,int linesSizeMax,const std::list<int> & teamIDs)307 void CInMapDraw::GetLines(std::vector<LineMarker>& lines, int linesSizeMax, const std::list<int>& teamIDs)
308 {
309 	linesSizeMax = std::min(linesSizeMax, inMapDrawerModel->GetNumLines());
310 	lines.clear();
311 	lines.reserve(linesSizeMax);
312 
313 	const std::list<CInMapDrawModel::MapLine>* linesInt = NULL;
314 	std::list<CInMapDrawModel::MapLine>::const_iterator line;
315 	std::list<int>::const_iterator it;
316 
317 	for (size_t y = 0; (y < inMapDrawerModel->GetDrawQuadY()) && ((int)lines.size() < linesSizeMax); y++) {
318 		for (size_t x = 0; (x < inMapDrawerModel->GetDrawQuadX()) && ((int)lines.size() < linesSizeMax); x++) {
319 			linesInt = &(inMapDrawerModel->GetDrawQuad(x, y)->lines);
320 
321 			for (line = linesInt->begin(); (line != linesInt->end()) && ((int)lines.size() < linesSizeMax); ++line) {
322 				for (it = teamIDs.begin(); it != teamIDs.end(); ++it) {
323 					if (line->GetTeamID() == *it) {
324 						LineMarker lm;
325 						lm.pos   = line->GetPos1();
326 						lm.pos2  = line->GetPos2();
327 						lm.color = teamHandler->Team(line->GetTeamID())->color;
328 						lines.push_back(lm);
329 						break;
330 					}
331 				}
332 			}
333 		}
334 	}
335 }
336 
337