1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 #include "QuitBox.h"
4 
5 #include "MouseHandler.h"
6 #include "Game/GameSetup.h"
7 #include "Game/GlobalUnsynced.h"
8 #include "Game/Players/Player.h"
9 #include "Game/Players/PlayerHandler.h"
10 #include "Rendering/Fonts/glFont.h"
11 #include "Rendering/GL/myGL.h"
12 #include "Sim/Misc/GlobalSynced.h"
13 #include "Sim/Misc/ModInfo.h"
14 #include "Sim/Misc/TeamHandler.h"
15 #include "System/Log/ILog.h"
16 #include "Net/Protocol/NetProtocol.h"
17 #include "System/TimeUtil.h"
18 #include "System/FileSystem/FileSystem.h"
19 #include "System/LoadSave/LoadSaveHandler.h"
20 #include "System/MsgStrings.h"
21 
22 #include <SDL_keycode.h>
23 
24 
25 #define MAX_QUIT_TEAMS (teamHandler->ActiveTeams() - 1)
26 
27 #undef CreateDirectory
28 
CQuitBox()29 CQuitBox::CQuitBox()
30 {
31 	box.x1 = 0.34f;
32 	box.y1 = 0.18f;
33 	box.x2 = 0.66f;
34 	box.y2 = 0.78f;
35 
36 	resignBox.x1=0.02f;
37 	resignBox.y1=0.53f;
38 	resignBox.x2=0.30f;
39 	resignBox.y2=0.57f;
40 
41 	saveBox.x1=0.02f;
42 	saveBox.y1=0.49f;
43 	saveBox.x2=0.30f;
44 	saveBox.y2=0.53f;
45 
46 	giveAwayBox.x1=0.02f;
47 	giveAwayBox.y1=0.44f;
48 	giveAwayBox.x2=0.30f;
49 	giveAwayBox.y2=0.48f;
50 
51 	teamBox.x1=0.02f;
52 	teamBox.y1=0.11f;
53 	teamBox.x2=0.30f;
54 	teamBox.y2=0.43f;
55 
56 	scrollbarBox.x1 = 0.28f;
57 	scrollbarBox.y1 = 0.11f;
58 	scrollbarBox.x2 = 0.30f;
59 	scrollbarBox.y2 = 0.43f;
60 
61 	scrollBox.x1 = 0.28f;
62 	scrollBox.y1 = 0.40f;
63 	scrollBox.x2 = 0.30f;
64 	scrollBox.y2 = 0.43f;
65 
66 	quitBox.x1=0.02f;
67 	quitBox.y1=0.06f;
68 	quitBox.x2=0.30f;
69 	quitBox.y2=0.10f;
70 
71 	cancelBox.x1=0.02f;
72 	cancelBox.y1=0.02f;
73 	cancelBox.x2=0.30f;
74 	cancelBox.y2=0.06f;
75 
76 	startTeam = 0;
77 	numTeamsDisp = (int)((teamBox.y2 - teamBox.y1) / 0.025f);
78 	scrolling = false;
79 	scrollGrab = 0.0f;
80 	hasScroll = MAX_QUIT_TEAMS > numTeamsDisp;
81 
82 	moveBox=false;
83 	noAlliesLeft=true;
84 	shareTeam=0;
85 	// if we have alive allies left, set the shareteam to an undead ally.
86 	for(int team=0;team<teamHandler->ActiveTeams();++team){
87 		if (teamHandler->Team(team)->gaia) continue;
88 		if(team!=gu->myTeam && !teamHandler->Team(team)->isDead)
89 		{
90 			if(shareTeam==gu->myTeam || teamHandler->Team(shareTeam)->isDead)
91 				shareTeam=team;
92 			if(teamHandler->Ally(gu->myAllyTeam, teamHandler->AllyTeam(team))){
93 				noAlliesLeft=false;
94 				shareTeam=team;
95 				break;
96 			}
97 		}
98 	}
99 }
100 
~CQuitBox()101 CQuitBox::~CQuitBox()
102 {
103 }
104 
Draw()105 void CQuitBox::Draw()
106 {
107 	float mx=MouseX(mouse->lastx);
108 	float my=MouseY(mouse->lasty);
109 
110 	glDisable(GL_TEXTURE_2D);
111 	glEnable(GL_BLEND);
112 	glDisable(GL_ALPHA_TEST);
113 
114 	// Large Box
115 	glColor4f(0.2f,0.2f,0.2f,guiAlpha);
116 	DrawBox(box);
117 
118 	// resign Box on mouse over
119 	if(InBox(mx,my,box+resignBox)){
120 		glColor4f(0.7f,0.2f,0.2f,guiAlpha);
121 		DrawBox(box+resignBox);
122 	}
123 	// save Box on mouse over
124 	if(InBox(mx,my,box+saveBox)){
125 		glColor4f(0.7f,0.2f,0.2f,guiAlpha);
126 		DrawBox(box+saveBox);
127 	}
128 	// give away Box on mouse over
129 	if(InBox(mx,my,box+giveAwayBox)){
130 		glColor4f(0.7f,0.2f,0.2f,guiAlpha);
131 		DrawBox(box+giveAwayBox);
132 	}
133 	// cancel Box on mouse over
134 	if(InBox(mx,my,box+cancelBox)){
135 		glColor4f(0.7f,0.2f,0.2f,guiAlpha);
136 		DrawBox(box+cancelBox);
137 	}
138 	// quit Box on mouse over
139 	if(InBox(mx,my,box+quitBox)){
140 		glColor4f(0.7f,0.2f,0.2f,guiAlpha);
141 		DrawBox(box+quitBox);
142 	}
143 
144 
145 	glColor4f(0.2f,0.2f,0.2f,guiAlpha);
146 	DrawBox(box+teamBox);
147 
148 	if (hasScroll) {
149 		glColor4f(0.1f, 0.1f, 0.1f, guiAlpha);
150 		DrawBox(box + scrollbarBox);
151 
152 		float sz = scrollbarBox.y2 - scrollbarBox.y1;
153 		float tsz = sz / (float)(MAX_QUIT_TEAMS);
154 		float psz = tsz * (float)numTeamsDisp;
155 
156 		scrollBox.y2 = scrollbarBox.y2 - startTeam * tsz;
157 		scrollBox.y1 = scrollBox.y2 - psz;
158 
159 		glColor4f(0.8f, 0.8f, 0.8f, guiAlpha);
160 		DrawBox(box + scrollBox);
161 	}
162 
163 	glEnable(GL_TEXTURE_2D);
164 	glColor4f(1,1,0.4f,0.8f);
165 	font->glPrint(box.x1+0.045f,box.y1+0.58f,0.7f, FONT_VCENTER | FONT_SCALE | FONT_NORM,"Do you want to ...");
166 	glColor4f(1,1,1,0.8f);
167 	font->glPrint(box.x1 + resignBox.x1     + 0.025f,
168 	                box.y1 + (resignBox.y1 + resignBox.y2)/2, 1, FONT_VCENTER | FONT_SCALE | FONT_NORM, "Resign");
169 	font->glPrint(box.x1 + saveBox.x1   + 0.025f,
170 	                box.y1 + (saveBox.y1 + saveBox.y2)/2, 1, FONT_VCENTER | FONT_SCALE | FONT_NORM, "Save");
171 	font->glPrint(box.x1 + giveAwayBox.x1   + 0.025f,
172 	                box.y1 + (giveAwayBox.y1 + giveAwayBox.y2)/2, 1, FONT_VCENTER | FONT_SCALE | FONT_NORM, "Give everything to ...");
173 	font->glPrint(box.x1 + cancelBox.x1     + 0.025f,
174 	                box.y1 + (cancelBox.y1 + cancelBox.y2)/2, 1, FONT_VCENTER | FONT_SCALE | FONT_NORM, "Cancel");
175 	font->glPrint(box.x1 + quitBox.x1       + 0.025f,
176 	                box.y1 + (quitBox.y1 + quitBox.y2)/2, 1, FONT_VCENTER | FONT_SCALE | FONT_NORM, "Quit");
177 
178 	int teamPos = 0;
179 	for(int team = startTeam; team < MAX_QUIT_TEAMS && teamPos < numTeamsDisp; ++team, ++teamPos) {
180 		int actualTeam=team;
181 		if (team >= gu->myTeam) {
182 			actualTeam++;
183 		}
184 		if (teamHandler->Team(actualTeam)->gaia) continue;
185 
186 		if (shareTeam == actualTeam) {
187 			glColor4f(1,1,1,0.8f);
188 		} else {
189 			glColor4f(1,1,1,0.4f);
190 		}
191 
192 		std::string teamName = teamHandler->Team(actualTeam)->GetControllerName();
193 		std::string ally, dead;
194 
195 		if (teamHandler->Ally(gu->myAllyTeam, teamHandler->AllyTeam(actualTeam))) {
196 			ally = " <Ally>)";
197 		} else {
198 			ally = " <Enemy>";
199 		}
200 		if (teamHandler->Team(actualTeam)->isDead) {
201 			dead = " <Dead>";
202 		}
203 		if (actualTeam == teamHandler->GaiaTeamID()) {
204 			teamName = "Gaia";
205 			ally   = " <Gaia>";
206 		}
207 		font->glFormat(box.x1 + teamBox.x1 + 0.002f,
208 		                box.y1 + teamBox.y2 - 0.025f - teamPos * 0.025f, 0.7f,  FONT_SCALE | FONT_NORM,
209 		                "Team%i (%s)%s%s", actualTeam,
210 		                teamName.c_str(), ally.c_str(), dead.c_str());
211 	}
212 }
213 
IsAbove(int x,int y)214 bool CQuitBox::IsAbove(int x, int y)
215 {
216 	float mx=MouseX(x);
217 	float my=MouseY(y);
218 	if(InBox(mx,my,box))
219 		return true;
220 	return false;
221 }
222 
GetTooltip(int x,int y)223 std::string CQuitBox::GetTooltip(int x, int y)
224 {
225 	float mx=MouseX(x);
226 	float my=MouseY(y);
227 
228 	if(InBox(mx,my,box+resignBox))
229 		return "Resign the match, remain in the game";
230 	if(InBox(mx,my,box+saveBox))
231 		return "Save the current game state to a file \nfor later reload";
232 	if(InBox(mx,my,box+giveAwayBox))
233 		return "Give away all units and resources \nto the team specified below";
234 	if (hasScroll && InBox(mx, my, box + scrollBox))
235 		return "Scroll the team list here";
236 	if (hasScroll && InBox(mx, my, box + scrollbarBox))
237 		return "Scroll the team list here";
238 	if(InBox(mx,my,box+teamBox))
239 		return "Select which team recieves everything";
240 	if(InBox(mx,my,box+cancelBox))
241 		return "Return to the game";
242 	if(InBox(mx,my,box+quitBox))
243 		return "Forget about the other players and quit";
244 	if(InBox(mx,my,box))
245 		return " ";
246 	return "";
247 }
248 
MousePress(int x,int y,int button)249 bool CQuitBox::MousePress(int x, int y, int button)
250 {
251 	float mx=MouseX(x);
252 	float my=MouseY(y);
253 	if(InBox(mx,my,box)){
254 		moveBox=true;
255 		if(InBox(mx,my,box+resignBox) || InBox(mx,my,box+saveBox) || InBox(mx,my,box+giveAwayBox) || InBox(mx,my,box+teamBox) || InBox(mx,my,box+cancelBox) || InBox(mx,my,box+quitBox) ||
256 				InBox(mx, my, box + scrollbarBox) || InBox(mx, my, box + scrollBox))
257 			moveBox=false;
258 		if (hasScroll && InBox(mx, my, box + scrollBox)) {
259 			scrolling = true;
260 			scrollGrab = (box + scrollBox).y2 - my;
261 		}
262 		else if (hasScroll && InBox(mx, my, box + scrollbarBox)) {
263 			if(my < (box + scrollBox).y1)
264 				*(volatile int *)&startTeam = startTeam + std::min(MAX_QUIT_TEAMS - numTeamsDisp - startTeam, numTeamsDisp);
265 			if(my > (box + scrollBox).y2)
266 				*(volatile int *)&startTeam = startTeam - std::min(startTeam, numTeamsDisp);
267 		}
268 		else if(InBox(mx,my,box+teamBox)){
269 			int team = startTeam + (int)((box.y1 + teamBox.y2 - my) / 0.025f);
270 			if(team>=gu->myTeam)
271 				team++;
272 			if(teamHandler->IsValidTeam(team) && !teamHandler->Team(team)->isDead){
273 				// we don't want to give everything to the enemy if there are allies left
274 				if(noAlliesLeft || (!noAlliesLeft && teamHandler->Ally(gu->myAllyTeam, teamHandler->AllyTeam(team)))){
275 					shareTeam=team;
276 				}
277 			}
278 		}
279 		return true;
280 	}
281 	return false;
282 }
283 
MouseRelease(int x,int y,int button)284 void CQuitBox::MouseRelease(int x,int y,int button)
285 {
286 	float mx=MouseX(x);
287 	float my=MouseY(y);
288 
289 	scrolling = false;
290 	scrollGrab = 0.0f;
291 
292 	if(InBox(mx,my,box+resignBox)
293 	   || (InBox(mx,my,box+saveBox) && !teamHandler->Team(gu->myTeam)->isDead)
294 	   || (InBox(mx,my,box+giveAwayBox) && !teamHandler->Team(shareTeam)->isDead && !teamHandler->Team(gu->myTeam)->isDead)) {
295 		// give away all units (and resources)
296 		if(InBox(mx,my,box+giveAwayBox) && !playerHandler->Player(gu->myPlayerNum)->spectator) {
297 			net->Send(CBaseNetProtocol::Get().SendGiveAwayEverything(gu->myPlayerNum, shareTeam, playerHandler->Player(gu->myPlayerNum)->team));
298 		}
299 		// resign, so self-d all units
300 		if (InBox(mx,my,box+resignBox) && !playerHandler->Player(gu->myPlayerNum)->spectator) {
301 			net->Send(CBaseNetProtocol::Get().SendResign(gu->myPlayerNum));
302 		}
303 		// save current game state
304 		if (InBox(mx,my,box+saveBox)) {
305 			if (FileSystem::CreateDirectory("Saves")) {
306 				std::string timeStr = CTimeUtil::GetCurrentTimeStr();
307 				std::string saveFileName(timeStr + "_" + modInfo.filename + "_" + gameSetup->mapName);
308 				saveFileName = "Saves/" + saveFileName + ".ssf";
309 				if (!FileSystem::FileExists(saveFileName)) {
310 					LOG("Saving game to %s", saveFileName.c_str());
311 					ILoadSaveHandler* ls = ILoadSaveHandler::Create();
312 					ls->mapName = gameSetup->mapName;
313 					ls->modName = modInfo.filename;
314 					ls->SaveGame(saveFileName);
315 					delete ls;
316 				} else {
317 					LOG_L(L_ERROR, "File %s already exists, game NOT saved!",
318 							saveFileName.c_str());
319 				}
320 			}
321 		}
322 	}
323 	else if (InBox(mx, my, box + quitBox)) {
324 		LOG("User exited");
325 		gu->globalQuit = true;
326 	}
327 	// if we're still in the game, remove the resign box
328 	if(InBox(mx,my,box+resignBox) || InBox(mx,my,box+saveBox) || InBox(mx,my,box+giveAwayBox) || InBox(mx,my,box+cancelBox) || InBox(mx,my,box+quitBox)){
329 		delete this;
330 		return;
331 	}
332 	moveBox=false;
333 }
334 
MouseMove(int x,int y,int dx,int dy,int button)335 void CQuitBox::MouseMove(int x, int y, int dx,int dy, int button)
336 {
337 	float mx=MouseX(x);
338 	float my=MouseY(y);
339 
340 	if(scrolling) {
341 		float scr = (box+scrollbarBox).y2 - (my + scrollGrab);
342 		float sz = scrollbarBox.y2 - scrollbarBox.y1;
343 		float tsz = sz / (float)MAX_QUIT_TEAMS;
344 
345 		*(volatile int *)&startTeam = std::max(0, std::min((int)(scr / tsz + 0.5), MAX_QUIT_TEAMS - numTeamsDisp));
346 		return;
347 	}
348 	if(moveBox){
349 		box.x1+=MouseMoveX(dx);
350 		box.x2+=MouseMoveX(dx);
351 		box.y1+=MouseMoveY(dy);
352 		box.y2+=MouseMoveY(dy);
353 	}
354 	if(!(hasScroll && (InBox(mx, my, box + scrollBox) || InBox(mx, my, box + scrollbarBox))) && InBox(mx,my,box+teamBox)){
355 		int team = startTeam + (int)((box.y1 + teamBox.y2 - my) / 0.025f);
356 		if(team>=gu->myTeam)
357 			team++;
358 		if(teamHandler->IsValidTeam(team) && !teamHandler->Team(team)->isDead){
359 			// we don't want to give everything to the enemy if there are allies left
360 			if(noAlliesLeft || (!noAlliesLeft && teamHandler->Ally(gu->myAllyTeam, teamHandler->AllyTeam(team)))){
361 				shareTeam=team;
362 			}
363 		}
364 	}
365 }
366 
367 
KeyPressed(int key,bool isRepeat)368 bool CQuitBox::KeyPressed(int key, bool isRepeat)
369 {
370 	if (key == SDLK_ESCAPE) {
371 		delete this;
372 		return true;
373 	}
374 	return false;
375 }
376