1 /*-------------------------------------------------------------------------------
2 
3 	BARONY
4 	File: ui_general.cpp
5 	Desc: contains code for game interface
6 
7 	Copyright 2013-2016 (c) Turning Wheel LLC, all rights reserved.
8 	See LICENSE for details.
9 
10 -------------------------------------------------------------------------------*/
11 
12 
13 #include "ui.hpp"
14 #include "../scores.hpp"
15 #ifdef WINDOWS
16 #include <shellapi.h>
17 #endif
18 UIToastNotificationManager_t UIToastNotificationManager;
19 
drawNotifications(bool isMoviePlaying,bool beforeFadeout)20 void UIToastNotificationManager_t::drawNotifications(bool isMoviePlaying, bool beforeFadeout)
21 {
22 	if ( !bIsInit )
23 	{
24 		return;
25 	}
26 
27 	int cardPosY = 110; // update the card y values if number of notifications change.
28 	for ( auto& card : allNotifications )
29 	{
30 		card.skipDrawingCardThisTick = false;
31 	}
32 
33 	bool bFirstDockedCard = true;
34 
35 	for ( auto& card : allNotifications )
36 	{
37 		if ((isMoviePlaying || !intro)
38 			&& !(card.actionFlags & UIToastNotification::ActionFlags::UI_NOTIFICATION_REMOVABLE))
39 		{
40 			continue;
41 		}
42 
43 		bool docked = false;
44 		if ( !(card.actionFlags & UIToastNotification::ActionFlags::UI_NOTIFICATION_REMOVABLE) )
45 		{
46 			docked = (card.getCardState() == UIToastNotification::CardState::UI_CARD_STATE_DOCKED);
47 		}
48 
49 		card.setPosY(cardPosY);
50 		SDL_Rect newPosition;
51 		card.getDimensions(newPosition.x, newPosition.y, newPosition.w, newPosition.h);
52 		// stack next card higher than the previous
53 		if ( docked )
54 		{
55 			if ( !bFirstDockedCard )
56 			{
57 				card.skipDrawingCardThisTick = true;
58 			}
59 			else
60 			{
61 				cardPosY += (newPosition.h + 8);
62 			}
63 			bFirstDockedCard = false;
64 		}
65 		else
66 		{
67 			cardPosY += (newPosition.h + 8);
68 		}
69 	}
70 
71 	int maxAchievementCards = 3;
72 	if ( cardPosY >= yres )
73 	{
74 		maxAchievementCards = 1;
75 	}
76 	int currentNumAchievementCards = 0;
77 	for ( auto& card : allNotifications )
78 	{
79 		if ( (isMoviePlaying || !intro)
80 			&& !(card.actionFlags & UIToastNotification::ActionFlags::UI_NOTIFICATION_REMOVABLE))
81 		{
82 			continue;
83 		}
84 
85 		if ( card.cardType == UIToastNotification::CardType::UI_CARD_ACHIEVEMENT )
86 		{
87 			// achievements are drawn AFTER the fadeout
88 			if ( beforeFadeout )
89 			{
90 				// if fading, then skip this card.
91 				if ( fadealpha > 0 )
92 				{
93 					continue;
94 				}
95 				// if we aren't fading, then draw BEFORE - makes sure the cursor is available
96 			}
97 			else
98 			{
99 				if ( fadealpha == 0 )
100 				{
101 					continue;
102 				}
103 				// if we are fading, then draw AFTER - we don't see the cursor anyway
104 			}
105 		}
106 		else
107 		{
108 			// all other cards are drawn BEFORE the fadeout
109 			if ( !beforeFadeout )
110 			{
111 				continue;
112 			}
113 		}
114 
115 		if ( card.cardType == UIToastNotification::CardType::UI_CARD_ACHIEVEMENT )
116 		{
117 			if ( currentNumAchievementCards >= maxAchievementCards )
118 			{
119 				card.cardForceTickUpdate(); // don't draw, but don't expire these.
120 				continue;
121 			}
122 			++currentNumAchievementCards;
123 		}
124 
125 		if ( card.skipDrawingCardThisTick )
126 		{
127 			continue;
128 		}
129 
130 		card.init();
131 		card.draw();
132 	}
133 
134 	for ( size_t c = 0; c < allNotifications.size(); ++c )
135 	{
136 		auto& card = allNotifications[c];
137 		if (card.getCardState() == UIToastNotification::CardState::UI_CARD_STATE_REMOVED)
138 		{
139 			allNotifications.erase(allNotifications.begin() + c);
140 			--c;
141 		}
142 	}
143 }
144 
addNotification(SDL_Surface * image)145 UIToastNotification* UIToastNotificationManager_t::addNotification(SDL_Surface* image)
146 {
147 	if ( !bIsInit )
148 	{
149 		init();
150 	}
151 
152 	SDL_Surface* surf = getImage(image);
153 	allNotifications.push_back(UIToastNotification(surf));
154 	auto& notification = allNotifications.back();
155 	return &notification;
156 }
157 
openURLTryWithOverlay(std::string url,bool forceSystemBrowser)158 void openURLTryWithOverlay(std::string url, bool forceSystemBrowser)
159 {
160 	bool useSystemBrowser = false;
161 	if ( !forceSystemBrowser )
162 	{
163 #ifdef STEAMWORKS
164 		if ( SteamUtils()->IsOverlayEnabled() )
165 		{
166 			SteamFriends()->ActivateGameOverlayToWebPage(url.c_str());
167 		}
168 		else
169 		{
170 			useSystemBrowser = true;
171 		}
172 #else
173 		useSystemBrowser = true;
174 #endif
175 	}
176 	else
177 	{
178 		useSystemBrowser = true;
179 	}
180 
181 	if ( useSystemBrowser )
182 	{
183 #ifdef WINDOWS
184 		ShellExecute(NULL, TEXT("open"), TEXT(url.c_str()), NULL, NULL, 0);
185 #endif // WINDOWS
186 #ifdef APPLE
187 		//TODO: Mac equivalent.
188 		system(std::string("open " + url).c_str());
189 #endif // APPLE
190 #ifdef LINUX
191 		system(std::string("xdg-open " + url).c_str());
192 #endif // LINUX
193 	}
194 }
195 
communityLinkAction()196 void communityLinkAction()
197 {
198 	openURLTryWithOverlay("https://discord.gg/P55tcYD");
199 	/*UIToastNotification* n = UIToastNotificationManager.getNotificationSingle(UIToastNotification::CardType::UI_CARD_COMMUNITY_LINK);
200 	if ( n )
201 	{
202 		n->showMainCard();
203 		n->updateCardEvent(false, true);
204 	}*/
205 }
206 
promoLinkAction()207 void promoLinkAction()
208 {
209 	openURLTryWithOverlay("https://www.kickstarter.com/projects/turningwheel/barony-for-the-nintendo-switch", true);
210 	achievementObserver.updateGlobalStat(STEAM_GSTAT_PROMO_INTERACT);
211 }
212 
createCommunityNotification()213 void UIToastNotificationManager_t::createCommunityNotification()
214 {
215 	if ( !UIToastNotificationManager.getNotificationSingle(UIToastNotification::CardType::UI_CARD_COMMUNITY_LINK) )
216 	{
217 		UIToastNotification* n = UIToastNotificationManager.addNotification(nullptr);
218 		n->setHeaderText(std::string("Join the community!"));
219 		n->setMainText(std::string("Find co-op allies and\nchat in real-time on the\nofficial Barony Discord!"));
220 		n->setSecondaryText(std::string("Overlay is disabled -\nVisit discord.gg/P55tcYD\nin your browser to join!"));
221 		n->setActionText(std::string("Join"));
222 		n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_ACTION_BUTTON);
223 		n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_AUTO_HIDE);
224 		n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_CLOSE);
225 		n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_RESET_TEXT_TO_MAIN_ON_HIDE);
226 		n->cardType = UIToastNotification::CardType::UI_CARD_COMMUNITY_LINK;
227 		n->buttonAction = &communityLinkAction;
228 		n->setIdleSeconds(8);
229 	}
230 }
231 
createPromoNotification()232 void UIToastNotificationManager_t::createPromoNotification()
233 {
234 	if ( !bIsInit )
235 	{
236 		init();
237 	}
238 	if ( !UIToastNotificationManager.getNotificationSingle(UIToastNotification::CardType::UI_CARD_PROMO) )
239 	{
240 		UIToastNotification* n = UIToastNotificationManager.addNotification(promoLink1);
241 		n->setHeaderText(std::string("The Barony Kickstarter is live!"));
242 		n->setMainText(std::string(" Help bring Barony to the Switch, plus\nsplit-screen, native controller support\nand new UI to all versions of the game!"));
243 		n->setSecondaryText(std::string(""));
244 		n->setActionText(std::string("Visit Page"));
245 		n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_ACTION_BUTTON);
246 		n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_AUTO_HIDE);
247 		n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_CLOSE);
248 		n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_RESET_TEXT_TO_MAIN_ON_HIDE);
249 		n->cardType = UIToastNotification::CardType::UI_CARD_PROMO;
250 		n->buttonAction = &promoLinkAction;
251 		SDL_Rect d;
252 		n->getDimensions(d.x, d.y, d.w, d.h);
253 
254 		int imagew = 1920;
255 		int imageh = 1080;
256 		double imagescale = 0.2;
257 
258 		n->setImageDimensions(1920, 1080);
259 		n->setImageScale(imagescale, imagescale);
260 
261 		SDL_Rect padding;
262 		padding.x = 0;
263 		padding.y = imageh * imagescale + 8;
264 		padding.w = 0;
265 		padding.h = TTF12_HEIGHT * 4 + 8;
266 		n->setPadding(padding);
267 
268 		d.x = 48;
269 		d.w = d.x + imagew * imagescale + 8;
270 		d.h = imageh * imagescale + 16 + padding.h;
271 		n->setDimensions(d);
272 		n->setActionButtonOffsetY(-4);
273 		n->setActionButtonOffsetW(-224);
274 		n->setIdleSeconds(120);
275 	}
276 }
277 
truncateMainText(std::string & str)278 void truncateMainText(std::string& str)
279 {
280 	if ( str.length() > 24 )
281 	{
282 		for ( size_t c, offset = 0;;)
283 		{
284 			size_t lastoffset = offset;
285 			for ( c = lastoffset + 1; c < str.size(); ++c )
286 			{
287 				if ( str[c] == ' ' )
288 				{
289 					break;
290 				}
291 			}
292 			offset = c;
293 			if ( offset > 24 && lastoffset )
294 			{
295 				str[lastoffset] = '\n';
296 				break;
297 			}
298 			if ( offset >= str.size() )
299 			{
300 				break;
301 			}
302 		}
303 	}
304 }
305 
createAchievementNotification(const char * name)306 void UIToastNotificationManager_t::createAchievementNotification(const char* name)
307 {
308 	UIToastNotification* n = nullptr;
309 	if ( n = getNotificationAchievementSingle(name) )
310 	{
311 		if ( n->actionFlags & UIToastNotification::ActionFlags::UI_NOTIFICATION_STATISTIC_UPDATE )
312 		{
313 			return; // don't create a new achievement card, since we have a statistic update present.
314 		}
315 	}
316 
317 	SDL_Surface* achievementImage = nullptr;
318 	{
319 		std::string imgName = name + std::string(".png");
320 		auto it = achievementImages.find(imgName.c_str());
321 		if (it != achievementImages.end())
322 		{
323 			achievementImage = it->second;
324 		}
325 	}
326 	const char* achievementName = "";
327 	{
328 		auto it = achievementNames.find(name);
329 		if (it != achievementNames.end())
330 		{
331 			achievementName = it->second.c_str();
332 		}
333 	}
334 
335 	n = UIToastNotificationManager.addNotification(achievementImage);
336 	n->setHeaderText(std::string("Achievement Unlocked!"));
337 
338 	std::string achStr = std::string(achievementName);
339 	truncateMainText(achStr);
340 	n->setMainText(achStr);
341 
342 	n->setAchievementName(name);
343 	n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_REMOVABLE);
344 	n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_AUTO_HIDE);
345 	n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_CLOSE);
346 	n->cardType = UIToastNotification::CardType::UI_CARD_ACHIEVEMENT;
347 	n->setIdleSeconds(5);
348 }
349 
createStatisticUpdateNotification(const char * name,int currentValue,int maxValue)350 void UIToastNotificationManager_t::createStatisticUpdateNotification(const char* name, int currentValue, int maxValue)
351 {
352 	UIToastNotification* n = nullptr;
353 	if ( n = getNotificationAchievementSingle(name) )
354 	{
355 		n->setStatisticMaxValue(maxValue);
356 		n->updateCardStatisticEvent(currentValue);
357 		return;
358 	}
359 
360 	bool unlocked = (currentValue >= maxValue);
361 
362 	SDL_Surface* achievementImage = nullptr;
363 	{
364 		std::string imgName = name + std::string("_l.png");
365 		if ( unlocked )
366 		{
367 			imgName = name + std::string(".png");
368 		}
369 		auto it = achievementImages.find(imgName.c_str());
370 		if ( it != achievementImages.end() )
371 		{
372 			achievementImage = it->second;
373 		}
374 	}
375 	const char* achievementName = "";
376 	{
377 		auto it = achievementNames.find(name);
378 		if ( it != achievementNames.end() )
379 		{
380 			achievementName = it->second.c_str();
381 		}
382 	}
383 
384 	n = UIToastNotificationManager.addNotification(achievementImage);
385 	if ( unlocked )
386 	{
387 		n->setHeaderText(std::string("Achievement Unlocked!"));
388 	}
389 	else
390 	{
391 		n->setHeaderText(std::string("Achievement Updated!"));
392 	}
393 
394 	std::string achStr = std::string(achievementName);
395 	truncateMainText(achStr);
396 	n->setMainText(achStr);
397 	n->setAchievementName(name);
398 	n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_STATISTIC_UPDATE);
399 	n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_REMOVABLE);
400 	n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_AUTO_HIDE);
401 	n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_CLOSE);
402 	n->cardType = UIToastNotification::CardType::UI_CARD_ACHIEVEMENT;
403 	n->setStatisticCurrentValue(currentValue);
404 	n->setStatisticMaxValue(maxValue);
405 	n->setIdleSeconds(5);
406 }