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 ¬ification;
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 }