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