1 // Hyperbolic Rogue -- Achievements
2 // Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
3
4 /** \file achievements.cpp
5 * \brief This file implements routines related to achievements and leaderboards
6 *
7 * This routines are general, i.e., not necessarily Steam-specific.
8 */
9
10 #include "hyper.h"
11 namespace hr {
12
13 #define NUMLEADER 87
14
15 EX bool test_achievements = false;
16
17 EX bool offlineMode = false;
18
19 EX const char* leadernames[NUMLEADER] = {
20 "Score", "Diamonds", "Gold", "Spice", "Rubies", "Elixirs",
21 "Shards100", "Totems", "Daisies", "Statues", "Feathers", "Sapphires",
22 "Hyperstones", "Time to Win-71", "Turns to Win-71",
23 "Time to 10 Hyperstones-120", "Turns to 10 Hyperstones-120", "Orbs of Yendor",
24 "Fern Flowers",
25 "Royal Jellies", "Powerstones", "Silver", "Wine", "Emeralds", "Grimoires",
26 "Holy Grails", "Red Gems", "Pirate Treasures",
27 "Shmup Score", "Shmup Time to Win", "Shmup Knife to Win",
28 "Bomberbird Eggs", // 31
29 "Ambers", // 32
30 "Pearls", // 33
31 "Hypersian Rugs", // 34
32 "Garnets", // 35
33 "Princess Challenge", // 36
34 "Ivory Figurines", // 37
35 "Elemental Gems", // 38
36 "Onyxes", // 39
37 "Yendor Challenge", // 40
38 "Pure Tactics Mode", // 41
39 "Mutant Saplings", // 42
40 "Fulgurites", // 43
41 "Shmup Score 2p", // 44
42 "Coop Shmup Time to Win", // 45
43 "Black Lotuses", // 46
44 "Mutant Fruits", // 47
45 "White Dove Feathers", // 48
46 "Pure Tactics Mode (shmup)", // 49
47 "Pure Tactics Mode (2p)", // 50
48 "Corals", // 51
49 "Thornless Roses", // 52
50 "Chaos Mode", // 53
51 "Tortoise points", // 54
52 "Dragon Scales", // 55
53 "Apples", // 56
54 "Heptagonal Mode", // 57
55 "Sunken Treasures", // 58
56 "Ancient Jewelry", // 59
57 "Golden Eggs", // 60
58 "Multiplayer Score", // 61
59 "Statistics", // 62
60 "Halloween", // 63
61 "Amethysts", // 64
62 "Slime Molds", // 65
63 "Dodecahedra", // 66
64 "Green Grass", // 67
65 "Spinel", // 68
66 "Orb Strategy Score", // 69
67 "Real time to Win-OS", // 70
68 "Turns to Win-OS", // 71
69 "Ancient Weapons", // 72
70 "Forgotten Relics", // 73
71 "Lava Lilies", // 74
72 "Turquoises", // 75
73 "Chrysoberyls", // 76
74 "Tasty Jellies", // 77
75 "Tiger's Eyes", // 78
76 "Torbernites", // 79
77 "Meteorites", // 80
78 "Racing Official Track", // 81
79 "Gold Balls", // 82
80 "Lazurite Figurines", // 83
81 "Water Lilies", // 84
82 "Capon Stones", // 85
83 "Crystal Dice" // 86
84 };
85
86 #define LB_STATISTICS 62
87 #define LB_HALLOWEEN 63
88 #define LB_RACING 81
89
90 EX bool haveLeaderboard(int id);
91 EX int get_currentscore(int id);
92 EX void set_priority_board(int id);
93 EX int get_sync_status();
94 EX bool score_loaded(int id);
95 EX int score_default(int id);
96
97 EX void upload_score(int id, int v);
98
99 string achievementMessage[3];
100 int achievementTimer;
101 /** achievements received this game */
102 EX vector<string> achievementsReceived;
103
104 /** Returns true if the given achievement cannot be obtained in the current mode.
105 * @param flags Mode requested by the achievement.
106 */
wrongMode(char flags)107 EX bool wrongMode(char flags) {
108 if(cheater) return true;
109 if(casual) return true;
110 if(flags == rg::global) return false;
111
112 if(flags != rg::special_geometry) {
113 if(!BITRUNCATED) return true;
114 if(geometry != gNormal) return true;
115 }
116
117 if(shmup::on != (flags == rg::shmup || flags == rg::racing)) return true;
118 if(racing::on != (flags == rg::racing)) return true;
119 if((!!dual::state) != (flags == rg::dualmode)) return true;
120 #if CAP_DAILY
121 if(daily::on != (flags == rg::daily)) return true;
122 #endif
123 if(randomPatternsMode) return true;
124 if(yendor::on) return true;
125 if(peace::on) return true;
126 if(tactic::on) return true;
127 if(dpgen::in) return true;
128 #if CAP_TOUR
129 if(tour::on) return true;
130 #endif
131 if(flags == rg::special_geometry && !ls::single())
132 return true;
133 if(flags != rg::special_geometry && ineligible_starting_land)
134 return true;
135 if(flags == rg::chaos && !ls::std_chaos()) return true;
136 if(flags != rg::chaos && flags != rg::special_geometry && !ls::nice_walls()) return true;
137 if((numplayers() > 1) != (flags == rg::multi)) return true;
138 return false;
139 }
140
141 EX set<string> got_achievements;
142
143 EX void achievement_gain_once(const string& s, char flags IS(0)) {
144 LATE(achievement_gain_once(s, flags));
145 if(got_achievements.count(s)) return;
146 if(wrongMode(flags)) {
147 if(test_achievements) println(hlog, "wrong mode for achievement: ", s);
148 else return;
149 }
150 got_achievements.insert(s);
151 achievement_gain(s.c_str(), flags);
152 }
153
achievement_log(const char * s,char flags)154 EX void achievement_log(const char* s, char flags) {
155
156 if(wrongMode(flags)) {
157 if(test_achievements) println(hlog, "wrong mode for achievement: ", s);
158 return;
159 }
160
161 if(test_achievements) {
162 println(hlog, "got achievement:", s);
163 return;
164 }
165
166 for(int i=0; i<isize(achievementsReceived); i++)
167 if(achievementsReceived[i] == s) return;
168 achievementsReceived.push_back(s);
169
170 #if CAP_SAVE
171 remove_emergency_save();
172
173 FILE *f = fopen(scorefile, "at");
174 if(!f) return;
175
176 int t = (int) (time(NULL) - timerstart);
177
178 time_t timer = time(NULL);
179 char buf[128]; strftime(buf, 128, "%c", localtime(&timer));
180
181 fprintf(f, "ACHIEVEMENT %s turns: %d time: %d at: %d ver: %s c: %d date: %s\n",
182 s, turncount, t, int(timerstart), VER, anticheat::certify(s, turncount, t, (int) timerstart, 0), buf);
183
184 fclose(f);
185 #endif
186 }
187
188 EX void achievement_init();
189 EX string myname();
190 EX void achievement_close();
191
192 /** gain the given achievement.
193 * @param s name of the achievement, e.g., DIAMOND1
194 * @param flags one of the constants from namespace rg. The achievement is only awarded if special modes are matched exactly.
195 */
196 EX void achievement_gain(const char* s, char flags IS(0));
197
198 #if ISSTEAM
199 void improveItemScores();
200 #include "private/hypersteam.cpp"
201 #elif !ISANDROID && !ISIOS
achievement_init()202 void achievement_init() {}
myname()203 string myname() { return "Rogue"; }
achievement_close()204 void achievement_close() {}
205 // gain the achievement with the given name.
206 // flags: 'e' - for Euclidean, 's' - for Shmup, '7' - for heptagonal
207 // Only awarded if special modes are matched exactly.
208 void achievement_gain(const char* s, char flags IS(0)) {
209 achievement_log(s, flags);
210 }
211 #endif
212
213 // gain the achievement for collecting a number of 'it'.
achievement_collection(eItem it)214 EX void achievement_collection(eItem it) {
215 achievement_collection2(it, items[it]);
216 }
217
achievement_collection2(eItem it,int q)218 EX void achievement_collection2(eItem it, int q) {
219 if(cheater && !test_achievements) return;
220 if(casual && !test_achievements) return;
221 if(randomPatternsMode) return;
222 LATE( achievement_collection2(it, q); )
223
224 if(it == itTreat && q == 50 && (geometry == gSphere || geometry == gElliptic) && BITRUNCATED)
225 achievement_gain("HALLOWEEN1", rg::special_geometry);
226
227 if(it == itTreat && q == 100 && (geometry == gSphere || geometry == gElliptic) && BITRUNCATED)
228 achievement_gain("HALLOWEEN2", rg::special_geometry);
229
230 if(q == 1) {
231 if(it == itDiamond) achievement_gain("DIAMOND1");
232 if(it == itRuby) achievement_gain("RUBY1");
233 if(it == itHyperstone) achievement_gain("HYPER1");
234 if(it == itGold) achievement_gain("GOLD1");
235 if(it == itStatue) achievement_gain("STATUE1");
236 if(it == itShard) achievement_gain("MIRROR1");
237 if(it == itBone) achievement_gain("TOTEM1");
238 if(it == itSpice) achievement_gain("SPICE1");
239 if(it == itElixir) achievement_gain("ELIXIR1");
240 if(it == itHell) achievement_gain("DAISY1");
241 if(it == itFeather) achievement_gain("FEATHER1");
242 if(it == itSapphire) achievement_gain("SAPPHIRE1");
243 if(it == itFernFlower) achievement_gain("FERN1");
244 if(it == itRoyalJelly) achievement_gain("JELLY1");
245 if(it == itWine) achievement_gain("WINE1");
246 if(it == itPower) achievement_gain("POWER1");
247 if(it == itEmerald) achievement_gain("EMERALD1");
248 if(it == itSilver) achievement_gain("SILVER1");
249 if(it == itGrimoire) achievement_gain("GRIMOIRE1");
250 if(it == itRedGem) achievement_gain("REDGEM1");
251 if(it == itPirate) achievement_gain("PIRATE1");
252 if(it == itCoast) achievement_gain("COAST1");
253 // if(it == itWhirlpool) achievement_gain("WHIRL1"); // requires escape
254 if(it == itBombEgg) achievement_gain("MINE1");
255 if(it == itPalace) achievement_gain("RUG1");
256 if(it == itFjord) achievement_gain("GARNET1");
257
258 if(it == itIvory) achievement_gain("TOWER1");
259 if(it == itElemental) achievement_gain("ELEMENT1");
260 if(it == itZebra) achievement_gain("ZEBRA1");
261
262 if(it == itMutant) achievement_gain("MUTANT1");
263 if(it == itFulgurite) achievement_gain("FULGUR1");
264
265 if(it == itMutant2) achievement_gain("FRUIT1");
266 if(it == itWindstone) achievement_gain("DOVE1");
267 if(it == itCoral) achievement_gain("CORAL1");
268 if(it == itRose) achievement_gain("ROSE1");
269
270 if(it == itBabyTortoise) achievement_gain("TORTOISE1");
271 if(it == itDragon) achievement_gain("DRAGON1");
272 if(it == itApple) achievement_gain("APPLE1");
273
274 if(it == itKraken) achievement_gain("KRAKEN1");
275 if(it == itBarrow) achievement_gain("BARROW1");
276 if(it == itTrollEgg) achievement_gain("TROLL1");
277
278 if(it == itAmethyst) achievement_gain("MOUNT1");
279 if(it == itSlime) achievement_gain("DUNG1");
280 if(it == itDodeca) achievement_gain("DOD1");
281
282 if(it == itGreenGrass) achievement_gain("PRAIR1");
283 if(it == itBull) achievement_gain("BULL1");
284
285 if(it == itHunting) achievement_gain("TURQ1");
286 if(it == itBlizzard) achievement_gain("BLIZZ1");
287 if(it == itLavaLily) achievement_gain("LILY1");
288 if(it == itTerra) achievement_gain("TERRAC1");
289
290 if(it == itRuins) achievement_gain("RUIN1");
291 if(it == itSwitch) achievement_gain("JELLZ1");
292
293 if(it == itBrownian) achievement_gain("BROWN1");
294 if(it == itVarTreasure) achievement_gain("RADIO1");
295 if(it == itWest) achievement_gain("FREEFALL1");
296
297 if(it == itFrog) achievement_gain("FROG1");
298 if(it == itEclectic) achievement_gain("ECLEC1");
299 if(it == itWet) achievement_gain("WET1");
300
301 if(it == itCursed) achievement_gain("CURSED1");
302 if(it == itDice) achievement_gain("DICE1");
303 }
304
305 // 32
306 if(it == itHolyGrail) {
307 if(q == 1) achievement_gain("GRAIL2");
308 if(PURE && geometry == gNormal)
309 achievement_gain("GRAILH", rg::special_geometry);
310 #if CAP_CRYSTAL
311 if(PURE && cryst && ginf[gCrystal].sides == 8 && ginf[gCrystal].vertex == 4 && !crystal::used_compass_inside)
312 achievement_gain("GRAIL4D", rg::special_geometry);
313 if(BITRUNCATED && cryst && ginf[gCrystal].sides == 8 && ginf[gCrystal].vertex == 3 && !crystal::used_compass_inside)
314 achievement_gain("GRAIL4D2", rg::special_geometry);
315 #endif
316 if(q == 3) achievement_gain("GRAIL3");
317 if(q == 8) achievement_gain("GRAIL4");
318 }
319
320 if(q == (inv::on ? 25 : 10)) {
321 if(it == itDiamond) achievement_gain("DIAMOND2");
322 if(it == itRuby) achievement_gain("RUBY2");
323 if(it == itHyperstone) achievement_gain("HYPER2");
324 if(it == itGold) achievement_gain("GOLD2");
325 if(it == itStatue) achievement_gain("STATUE2");
326 if(it == itShard) achievement_gain("MIRROR2");
327 if(it == itBone) achievement_gain("TOTEM2");
328 if(it == itSpice) achievement_gain("SPICE2");
329 if(it == itElixir) achievement_gain("ELIXIR2");
330 if(it == itHell) achievement_gain("DAISY2");
331 if(it == itFeather) achievement_gain("FEATHER2");
332 if(it == itSapphire) achievement_gain("SAPPHIRE2");
333 if(it == itFernFlower) achievement_gain("FERN2");
334 if(it == itRoyalJelly) achievement_gain("JELLY2");
335 if(it == itWine) achievement_gain("WINE2");
336 if(it == itPower) achievement_gain("POWER2");
337 if(it == itEmerald) achievement_gain("EMERALD2");
338 if(it == itSilver) achievement_gain("SILVER2");
339 if(it == itGrimoire) achievement_gain("GRIMOIRE2");
340 if(it == itRedGem) achievement_gain("REDGEM2");
341 if(it == itPirate) achievement_gain("PIRATE2");
342 if(it == itCoast) achievement_gain("COAST2");
343 if(it == itWhirlpool) achievement_gain("WHIRL2");
344 if(it == itBombEgg) achievement_gain("MINE2");
345 if(it == itPalace) achievement_gain("RUG2");
346 if(it == itFjord) achievement_gain("GARNET2");
347
348 if(it == itIvory) achievement_gain("TOWER2");
349 if(it == itElemental) achievement_gain("ELEMENT2");
350 if(it == itZebra) achievement_gain("ZEBRA2");
351
352 if(it == itMutant) achievement_gain("MUTANT2");
353 if(it == itFulgurite) achievement_gain("FULGUR2");
354
355 if(it == itMutant2) achievement_gain("FRUIT2");
356 if(it == itWindstone) achievement_gain("DOVE2");
357 if(it == itCoral) achievement_gain("CORAL2");
358 if(it == itRose) achievement_gain("ROSE2");
359
360 if(it == itBabyTortoise) achievement_gain("TORTOISE2");
361 if(it == itDragon) achievement_gain("DRAGON2");
362 if(it == itApple) achievement_gain("APPLE2");
363
364 if(it == itKraken) achievement_gain("KRAKEN2");
365 if(it == itBarrow) achievement_gain("BARROW2");
366 if(it == itTrollEgg) achievement_gain("TROLL2");
367
368 if(it == itAmethyst) achievement_gain("MOUNT2");
369 if(it == itSlime) achievement_gain("DUNG2");
370 if(it == itDodeca) achievement_gain("DOD2");
371
372 if(it == itGreenGrass) achievement_gain("PRAIR2");
373 if(it == itBull) achievement_gain("BULL2");
374
375 if(it == itHunting) achievement_gain("TURQ2");
376 if(it == itBlizzard) achievement_gain("BLIZZ2");
377 if(it == itLavaLily) achievement_gain("LILY2");
378 if(it == itTerra) achievement_gain("TERRAC2");
379
380 if(it == itRuins) achievement_gain("RUIN2");
381 if(it == itSwitch) achievement_gain("JELLZ2");
382
383 if(it == itBrownian) achievement_gain("BROWN2");
384 if(it == itVarTreasure) achievement_gain("RADIO2");
385 if(it == itWest) achievement_gain("FREEFALL2");
386
387 if(it == itFrog) achievement_gain("FROG2");
388 if(it == itEclectic) achievement_gain("ECLEC2");
389 if(it == itWet) achievement_gain("WET2");
390
391 if(it == itCursed) achievement_gain("CURSED2");
392 if(it == itDice) achievement_gain("DICE2");
393 }
394
395 if(q == (inv::on ? 50 : 25)) {
396 if(it == itDiamond) achievement_gain("DIAMOND3");
397 if(it == itRuby) achievement_gain("RUBY3");
398 if(it == itHyperstone) achievement_gain("HYPER3");
399 if(it == itGold) achievement_gain("GOLD3");
400 if(it == itStatue) achievement_gain("STATUE3");
401 if(it == itShard) achievement_gain("MIRROR3");
402 if(it == itBone) achievement_gain("TOTEM3");
403 if(it == itSpice) achievement_gain("SPICE3");
404 if(it == itElixir) achievement_gain("ELIXIR3");
405 if(it == itHell) achievement_gain("DAISY3");
406 if(it == itFeather) achievement_gain("FEATHER3");
407 if(it == itSapphire) achievement_gain("SAPPHIRE3");
408 if(it == itFernFlower) achievement_gain("FERN3");
409 if(it == itRoyalJelly) achievement_gain("JELLY3");
410 if(it == itWine) achievement_gain("WINE3");
411 if(it == itPower) achievement_gain("POWER3");
412 if(it == itEmerald) achievement_gain("EMERALD3");
413 if(it == itSilver) achievement_gain("SILVER3");
414 if(it == itGrimoire) achievement_gain("GRIMOIRE3");
415 if(it == itRedGem) achievement_gain("REDGEM3");
416 if(it == itPirate) achievement_gain("PIRATE3");
417 if(it == itCoast) achievement_gain("COAST3");
418 if(it == itWhirlpool) achievement_gain("WHIRL3");
419 if(it == itBombEgg) achievement_gain("MINE3");
420 if(it == itPalace) achievement_gain("RUG3");
421 if(it == itFjord) achievement_gain("GARNET3");
422
423 if(it == itIvory) achievement_gain("TOWER3");
424 if(it == itElemental) achievement_gain("ELEMENT3");
425 if(it == itZebra) achievement_gain("ZEBRA3");
426
427 if(it == itMutant) achievement_gain("MUTANT3");
428 if(it == itFulgurite) achievement_gain("FULGUR3");
429
430 if(it == itMutant2) achievement_gain("FRUIT3");
431 if(it == itWindstone) achievement_gain("DOVE3");
432 if(it == itCoral) achievement_gain("CORAL3");
433 if(it == itRose) achievement_gain("ROSE3");
434
435 if(it == itFulgurite && pureHardcore() && elec::lightningfast == 0)
436 achievement_gain("HARDMETAL");
437
438 if(it == itBabyTortoise) achievement_gain("TORTOISE3");
439 if(it == itDragon) achievement_gain("DRAGON3");
440 if(it == itApple) achievement_gain("APPLE3");
441
442 if(it == itKraken) achievement_gain("KRAKEN3");
443 if(it == itBarrow) achievement_gain("BARROW3");
444 if(it == itTrollEgg) achievement_gain("TROLL3");
445
446 if(it == itAmethyst) achievement_gain("MOUNT3");
447 if(it == itSlime) achievement_gain("DUNG3");
448 if(it == itDodeca) achievement_gain("DOD3");
449
450 if(it == itGreenGrass) achievement_gain("PRAIR3");
451 if(it == itBull) achievement_gain("BULL3");
452
453 if(it == itHunting) achievement_gain("TURQ3");
454 if(it == itBlizzard) achievement_gain("BLIZZ3");
455 if(it == itLavaLily) achievement_gain("LILY3");
456 if(it == itTerra) achievement_gain("TERRAC3");
457
458 if(it == itRuins) achievement_gain("RUIN3");
459 if(it == itSwitch) achievement_gain("JELLZ3");
460
461 if(it == itBrownian) achievement_gain("BROWN3");
462 if(it == itVarTreasure) achievement_gain("RADIO3");
463 if(it == itWest) achievement_gain("FREEFALL3");
464
465 if(it == itFrog) achievement_gain("FROG3");
466 if(it == itEclectic) achievement_gain("ECLEC3");
467 if(it == itWet) achievement_gain("WET3");
468
469 if(it == itCursed) achievement_gain("CURSED3");
470 if(it == itDice) achievement_gain("DICE3");
471 }
472
473 if(q == 50 && !inv::on) {
474 if(it == itDiamond) achievement_gain("DIAMOND4");
475 if(it == itRuby) achievement_gain("RUBY4");
476 if(it == itHyperstone) achievement_gain("HYPER4");
477 if(it == itGold) achievement_gain("GOLD4");
478 if(it == itStatue) achievement_gain("STATUE4");
479 if(it == itShard) achievement_gain("MIRROR4");
480 if(it == itBone) achievement_gain("TOTEM4");
481 if(it == itSpice) achievement_gain("SPICE4");
482 if(it == itElixir) achievement_gain("ELIXIR4");
483 if(it == itHell) achievement_gain("DAISY4");
484 if(it == itFeather) achievement_gain("FEATHER4");
485 if(it == itSapphire) achievement_gain("SAPPHIRE4");
486 if(it == itFernFlower) achievement_gain("FERN4");
487 if(it == itRoyalJelly) achievement_gain("JELLY4");
488 if(it == itWine) achievement_gain("WINE4");
489 if(it == itPower) achievement_gain("POWER4");
490 if(it == itEmerald) achievement_gain("EMERALD4");
491 if(it == itSilver) achievement_gain("SILVER4");
492 if(it == itGrimoire) achievement_gain("GRIMOIRE4");
493 if(it == itRedGem) achievement_gain("REDGEM4");
494 if(it == itPirate) achievement_gain("PIRATE4");
495 if(it == itCoast) achievement_gain("COAST4");
496 if(it == itWhirlpool) achievement_gain("WHIRL4");
497 if(it == itBombEgg) achievement_gain("MINE4");
498 if(it == itPalace) achievement_gain("RUG4");
499 if(it == itFjord) achievement_gain("GARNET4");
500
501 if(it == itIvory) achievement_gain("TOWER4");
502 if(it == itElemental) achievement_gain("ELEMENT4");
503 if(it == itZebra) achievement_gain("ZEBRA4");
504
505 if(it == itMutant) achievement_gain("MUTANT4");
506 if(it == itFulgurite) achievement_gain("FULGUR4");
507
508 if(it == itMutant2) achievement_gain("FRUIT4");
509 if(it == itWindstone) achievement_gain("DOVE4");
510 if(it == itCoral) achievement_gain("CORAL4");
511 if(it == itRose) achievement_gain("ROSE4");
512
513 if(it == itBabyTortoise) achievement_gain("TORTOISE4");
514 if(it == itDragon) achievement_gain("DRAGON4");
515 if(it == itApple) achievement_gain("APPLE4");
516
517 if(it == itKraken) achievement_gain("KRAKEN4");
518 if(it == itBarrow) achievement_gain("BARROW4");
519 if(it == itTrollEgg) achievement_gain("TROLL4");
520
521 if(it == itAmethyst) achievement_gain("MOUNT4");
522 if(it == itSlime) achievement_gain("DUNG4");
523 if(it == itDodeca) achievement_gain("DOD4");
524
525 if(it == itGreenGrass) achievement_gain("PRAIR4");
526 if(it == itBull) achievement_gain("BULL4");
527
528 if(it == itHunting) achievement_gain("TURQ4");
529 if(it == itBlizzard) achievement_gain("BLIZZ4");
530 if(it == itLavaLily) achievement_gain("LILY4");
531 if(it == itTerra) achievement_gain("TERRAC4");
532
533 if(it == itRuins) achievement_gain("RUIN4");
534 if(it == itSwitch) achievement_gain("JELLZ4");
535
536 if(it == itBrownian) achievement_gain("BROWN4");
537 if(it == itVarTreasure) achievement_gain("RADIO4");
538 if(it == itWest) achievement_gain("FREEFALL4");
539
540 if(it == itFrog) achievement_gain("FROG4");
541 if(it == itEclectic) achievement_gain("ECLEC4");
542 if(it == itWet) achievement_gain("WET4");
543
544 if(it == itCursed) achievement_gain("CURSED4");
545 if(it == itDice) achievement_gain("DICE4");
546 }
547
548 if(it == itOrbYendor) {
549 achievement_gain("YENDOR2");
550 if(pureHardcore()) achievement_gain("HARDCORE");
551 if(shmup::on) achievement_gain("SHMUP", rg::shmup);
552 }
553 }
554
555 /** This function awards 'counting' achievements, such as kill 10 monsters at the same time.
556 * @param s name of the group of achievements, e.g. GOLEM.
557 * @param current our score, e.g., the the achievement GOLEM2 will be awared with current >= 5.
558 * @param prev previous value of the score.
559 */
560
achievement_count(const string & s,int current,int prev)561 EX void achievement_count(const string& s, int current, int prev) {
562 if(cheater && !test_achievements) return;
563 if(casual && !test_achievements) return;
564 if(shmup::on) return;
565 if(randomPatternsMode) return;
566 LATE( achievement_count(s, current, prev); )
567
568 if(s == "GOLEM" && current >= 5)
569 achievement_gain("GOLEM2");
570 if(s == "GOLEM" && current >= 10)
571 achievement_gain("GOLEM3");
572 if(s == "STAB" && current >= 1)
573 achievement_gain("STABBER1");
574 if(s == "STAB" && current >= 2)
575 achievement_gain("STABBER2");
576 if(s == "SLASH" && current >= 2)
577 achievement_gain("SLASH2");
578 if(s == "STAB" && current >= 4)
579 achievement_gain("STABBER3");
580 if(s == "MIRRORKILL" && current-prev >= 1)
581 achievement_gain("MIRRORKILL1");
582 if(s == "MIRRORKILL" && current-prev >= 2)
583 achievement_gain("MIRRORKILL2");
584 if(s == "MIRRORKILL" && current-prev >= 3)
585 achievement_gain("MIRRORKILL3");
586 if(s == "FLASH" && current-prev >= 1)
587 achievement_gain("FLASH1");
588 if(s == "FLASH" && current-prev >= 5)
589 achievement_gain("FLASH2");
590 if(s == "FLASH" && current-prev >= 10)
591 achievement_gain("FLASH3");
592 if(s == "LIGHTNING" && current-prev >= 1)
593 achievement_gain("LIGHTNING1");
594 if(s == "LIGHTNING" && current-prev >= 5)
595 achievement_gain("LIGHTNING2");
596 if(s == "LIGHTNING" && current-prev >= 10)
597 achievement_gain("LIGHTNING3");
598 if(s == "MIRAGE" && current >= 35 && geometry == gEuclid)
599 achievement_gain("MIRAGE", rg::special_geometry);
600 if(s == "ORB" && current >= 10)
601 achievement_gain("ORB3");
602 if(s == "BUG" && current >= 1000)
603 achievement_gain("BUG3");
604 if(s == "ELEC" && current >= 10)
605 achievement_gain("ELEC3");
606 }
607
608 int specific_improved = 0;
609 int specific_what = 0;
610
improve_score(int i,eItem what)611 EX void improve_score(int i, eItem what) {
612 if(offlineMode) return;
613 LATE( improve_score(i, what); )
614 #ifdef HAVE_ACHIEVEMENTS
615 if(haveLeaderboard(i)) updateHi(what, get_currentscore(i));
616 if(items[what] && haveLeaderboard(i)) {
617 if(items[what] > get_currentscore(i) && score_loaded(i)) {
618 specific_improved++; specific_what = what;
619 }
620 upload_score(i, items[what]);
621 }
622 #endif
623 }
624
625 // scores for special challenges
achievement_score(int cat,int number)626 EX void achievement_score(int cat, int number) {
627 if(offlineMode) return;
628 #ifdef HAVE_ACHIEVEMENTS
629 if(cheater) return;
630 if(casual) return;
631 LATE( achievement_score(cat, number); )
632 if(cat == LB_HALLOWEEN) {
633 if(geometry != gSphere && geometry != gElliptic)
634 return;
635 }
636 else {
637 if(geometry) return;
638 if(ineligible_starting_land) return;
639 }
640 if(CHANGED_VARIATION) return;
641 if(randomPatternsMode) return;
642 if(dual::state) return;
643 if(shmup::on && cat != LB_PURE_TACTICS_SHMUP && cat != LB_PURE_TACTICS_COOP && cat != LB_RACING) return;
644 if(yendor::on && cat != LB_YENDOR_CHALLENGE) return;
645 if(tactic::on && cat != LB_PURE_TACTICS && cat != LB_PURE_TACTICS_SHMUP && cat != LB_PURE_TACTICS_COOP)
646 return;
647 if(racing::on && cat != LB_RACING) return;
648 upload_score(cat, number);
649 #endif
650 }
651
improveItemScores()652 EX void improveItemScores() {
653 LATE( improveItemScores(); )
654 for(int i=1; i<=12; i++) improve_score(i, eItem(i));
655 improve_score(17, itOrbYendor);
656 improve_score(18, itFernFlower);
657 improve_score(19, itRoyalJelly);
658 improve_score(20, itPower);
659 improve_score(21, itSilver);
660 improve_score(22, itWine);
661 improve_score(23, itEmerald);
662 improve_score(24, itGrimoire);
663 improve_score(25, itHolyGrail);
664 improve_score(26, itRedGem);
665 improve_score(27, itPirate);
666 improve_score(31, itBombEgg);
667 improve_score(32, itCoast);
668 improve_score(33, itWhirlpool);
669 improve_score(34, itPalace);
670 improve_score(35, itFjord);
671
672 improve_score(37, itIvory);
673 improve_score(38, itElemental);
674 improve_score(39, itZebra);
675
676 improve_score(42, itMutant);
677 improve_score(43, itFulgurite);
678
679 if(!isHaunted(cwt.at->land)) improve_score(46, itLotus);
680 improve_score(47, itMutant2);
681 improve_score(48, itWindstone);
682
683 improve_score(51, itCoral);
684 improve_score(52, itRose);
685
686 improve_score(54, itBabyTortoise);
687 improve_score(55, itDragon);
688 improve_score(56, itApple);
689
690 improve_score(58, itKraken);
691 improve_score(59, itBarrow);
692 improve_score(60, itTrollEgg);
693
694 improve_score(64, itAmethyst);
695 improve_score(65, itSlime);
696 improve_score(66, itDodeca);
697
698 improve_score(67, itGreenGrass);
699 improve_score(68, itBull);
700
701 improve_score(72, itTerra);
702 improve_score(73, itBlizzard);
703 improve_score(74, itLavaLily);
704 improve_score(75, itHunting);
705
706 improve_score(76, itRuins);
707 improve_score(77, itSwitch);
708
709 improve_score(78, itBrownian);
710 improve_score(79, itVarTreasure);
711 improve_score(80, itWest);
712
713 improve_score(82, itFrog);
714 improve_score(83, itEclectic);
715 improve_score(84, itWet);
716
717 improve_score(85, itCursed);
718 improve_score(86, itDice);
719 }
720
721 int next_stat_tick;
722
723 /** gain the final achievements.
724 * @param really false: the user is simply looking at the score; true: the game really ended.
725 */
achievement_final(bool really_final)726 EX void achievement_final(bool really_final) {
727 if(offlineMode) return;
728
729 LATE( achievement_final(really_final); )
730
731 #ifdef HAVE_ACHIEVEMENTS
732 if(ticks > next_stat_tick) {
733 upload_score(LB_STATISTICS, time(NULL));
734 next_stat_tick = ticks + 600000;
735 }
736 if(cheater) return;
737 if(casual) return;
738
739 #if CAP_TOUR
740 if(tour::on) return;
741 #endif
742
743 if(randomPatternsMode) return;
744 if(peace::on) return;
745 if(yendor::on) return;
746 if(dual::state) return;
747
748 if(tactic::on) {
749 tactic::record();
750 tactic::unrecord();
751 tactic::uploadScore();
752 return;
753 }
754
755 #if CAP_DAILY
756 if(daily::on) {
757 daily::uploadscore(really_final);
758 return;
759 }
760 #endif
761
762 int specialcode = 0;
763 if(shmup::on) specialcode++;
764 if(ls::std_chaos()) specialcode+=2;
765 else if(!ls::nice_walls()) return;
766 if(PURE) specialcode+=4;
767 if(numplayers() > 1) specialcode+=8;
768 if(inv::on) specialcode+=16;
769
770 if(sphere && specialland == laHalloween) {
771 if(specialcode) return;
772 achievement_score(LB_HALLOWEEN, items[itTreat]);
773 return;
774 }
775
776 if(ineligible_starting_land) return;
777 if(geometry) return;
778 if(NONSTDVAR) return;
779
780 // determine the correct leaderboard ID for 'total score'
781 // or return if no leaderboard for the current mode
782 int sid;
783 switch(specialcode) {
784 case 0: sid = 0; break;
785 case 1: sid = 28; break;
786 case 2: sid = 53; break;
787 case 4: sid = 57; break;
788 case 8: sid = 61; break;
789 case 9: sid = 44; break;
790 case 16: sid = 69; break;
791 default: return;
792 }
793
794 int total_improved = 0;
795 specific_improved = 0;
796 specific_what = 0;
797
798 if(specialcode == 0) improveItemScores();
799
800 int tg = gold();
801 if(tg && haveLeaderboard(sid)) {
802 if(tg > get_currentscore(sid) && score_loaded(sid)) {
803 if(get_currentscore(sid) <= 0) total_improved += 2;
804 total_improved++; // currentscore[sid] = tg;
805 }
806 upload_score(sid, tg);
807 }
808
809 if(total_improved >= 2) {
810 #if !ISANDROID
811 addMessage(XLAT("Your total treasure has been recorded in the " LEADERFULL "."));
812 addMessage(XLAT("Congratulations!"));
813 #endif
814 }
815 else if(total_improved && specific_improved >= 2)
816 addMessage(XLAT("You have improved your total high score and %1 specific high scores!", its(specific_improved)));
817 else if(total_improved && specific_improved)
818 addMessage(XLAT("You have improved your total and '%1' high score!", iinf[specific_what].name));
819 else if(total_improved) {
820 #if !ISANDROID
821 addMessage(XLAT("You have improved your total high score on " LEADER ". Congratulations!"));
822 #endif
823 }
824 else if(specific_improved >= 2)
825 addMessage(XLAT("You have improved %1 of your specific high scores!", its(specific_improved)));
826 else if(specific_improved) {
827 #if !ISANDROID
828 addMessage(XLAT("You have improved your '%1' high score on " LEADER "!", iinf[specific_what].name));
829 #endif
830 }
831 #endif
832 }
833
834 EX bool hadtotalvictory;
835
check_total_victory()836 EX void check_total_victory() {
837 if(!inv::on) return;
838 if(hadtotalvictory) return;
839 if(!items[itOrbYendor]) return;
840 if(!items[itHolyGrail]) return;
841 if(items[itHyperstone] < 50) return;
842 if(!princess::reviveAt) return;
843 LATE( check_total_victory(); )
844 hadtotalvictory = true;
845 achievement_gain("TOTALVICTORY");
846 }
847
848 /** gain the victory achievements.
849 * @param hyper true for the Hyperstone victory, and false for the Orb of Yendor victory.
850 */
achievement_victory(bool hyper)851 EX void achievement_victory(bool hyper) {
852 DEBBI(DF_STEAM, ("achievement_victory"))
853 if(offlineMode) return;
854 #ifdef HAVE_ACHIEVEMENTS
855 if(cheater) return;
856 if(casual) return;
857 if(geometry) return;
858 if(CHANGED_VARIATION) return;
859 if(randomPatternsMode) return;
860 if(hyper && shmup::on) return;
861 if(yendor::on) return;
862 if(peace::on) return;
863 if(tactic::on) return;
864 if(!ls::nice_walls()) return;
865 if(!ineligible_starting_land) return;
866 LATE( achievement_victory(hyper); )
867 DEBB(DF_STEAM, ("after checks"))
868
869 int t = getgametime();
870
871 if(hyper && shmup::on) return;
872 if(hyper && inv::on) return;
873
874 int ih1 = hyper ? 15 : inv::on ? 70 : shmup::on ? (numplayers() > 1 ? 45 : 29) : 13;
875 int ih2 = hyper ? 16 : inv::on ? 71 : shmup::on ? 30 : 14;
876
877 int improved = 0;
878 if(score_loaded(ih1) && score_loaded(ih2)) {
879 if(get_currentscore(ih1) == score_default(ih1) || get_currentscore(ih2) == score_default(ih2))
880 improved += 4;
881
882 if(get_currentscore(ih1) > t) {
883 improved++;
884 }
885
886 if(get_currentscore(ih2) > turncount) {
887 improved+=2;
888 }
889 }
890
891 if(hyper)
892 addMessage(XLAT("You have collected 10 treasures of each type."));
893
894 if(improved) {
895 if(improved >= 4) {
896 if(!hyper) addMessage(XLAT("This is your first victory!"));
897 #if !ISANDROID
898 addMessage(XLAT("This has been recorded in the " LEADERFULL "."));
899 #endif
900 addMessage(XLAT("The faster you get here, the better you are!"));
901 }
902 else if(improved >= 3) {
903 if(shmup::on)
904 addMessage(XLAT("You have improved both your real time and knife count. Congratulations!"));
905 else
906 addMessage(XLAT("You have improved both your real time and turn count. Congratulations!"));
907 }
908 else if(improved == 1)
909 addMessage(XLAT("You have used less real time than ever before. Congratulations!"));
910 else if(improved == 2) {
911 if(shmup::on)
912 addMessage(XLAT("You have used less knives than ever before. Congratulations!"));
913 else
914 addMessage(XLAT("You have used less turns than ever before. Congratulations!"));
915 }
916 }
917
918 DEBB(DF_STEAM, ("uploading scores"))
919 upload_score(ih1, t);
920 upload_score(ih2, turncount);
921 #endif
922 }
923
924 /** call the achievement callbacks */
925 EX void achievement_pump();
926
get_rich_presence_text()927 EX string get_rich_presence_text() {
928
929 #if CAP_DAILY
930 if(daily::on)
931 return "Strange Challenge #" + its(daily::daily_id) + ", score " + its(gold());
932 #endif
933
934 if(tour::on)
935 return "Guided Tour";
936
937 string res;
938 if(geometry != gNormal || !BITRUNCATED)
939 res = res + full_geometry_name() + " ";
940
941 if(land_structure != default_land_structure()) res += land_structure_name(false) + " ";
942 if(shmup::on) res += "shmup ";
943 if(dual::state) res += "dual ";
944 if(randomPatternsMode) res += "random ";
945 if(inv::on) res += "OSM ";
946 if(multi::players > 1) res += "multi ";
947 if(casual) res += "casual ";
948
949 if(cheater || among(cwt.at->land, laCanvas, laCA))
950 return res + "(?)";
951
952 if(yendor::on) {
953 res += "Yendor Challenge: " + yendor::name(yendor::challenge);
954 if(items[itOrbYendor]) res += " (level " + its(items[itOrbYendor]) + ")";
955 return res;
956 }
957
958 if(peace::on) return res + "peaceful";
959
960 if(tactic::on)
961 return res + "PTM: " + linf[specialland].name + " $" + its(gold());
962
963 if(princess::challenge) return res + "Princess Challenge";
964
965 #if CAP_RACING
966 if(racing::on) {
967 using namespace racing;
968 res = res + "racing in " + linf[specialland].name;
969
970 for(int i=0; i<multi::players; i++) {
971 if(race_finish_tick[i])
972 res += racetimeformat(race_finish_tick[i] - race_start_tick);
973 }
974
975 return res;
976 }
977 #endif
978
979 res += linf[cwt.at->land].name;
980 res += ", " + its(gold()) + " $$$";
981
982 return res;
983 }
984
985 #ifndef HAVE_ACHIEVEMENTS
achievement_pump()986 void achievement_pump() {}
987 #endif
988
989 /** display the last achievement gained. */
achievement_display()990 EX void achievement_display() {
991 #ifdef HAVE_ACHIEVEMENTS
992 if(achievementTimer) {
993 int col = (ticks - achievementTimer);
994 if(col > 5000) { achievementTimer = 0; return; }
995 if(col > 2500) col = 5000 - col;
996 col /= 10; col *= 0x10101;
997 displayfr(vid.xres/2, vid.yres/4, 2, vid.fsize * 2, achievementMessage[0], col & 0xFFFF00, 8);
998 int w = 2 * vid.fsize;
999 #if !ISMOBILE
1000 while(w>3 && textwidth(w, achievementMessage[1]) > vid.xres) w--;
1001 #endif
1002 displayfr(vid.xres/2, vid.yres/4 + vid.fsize*2, 2, w, achievementMessage[1], col, 8);
1003 w = vid.fsize;
1004 #if !ISMOBILE
1005 while(w>3 && textwidth(w, achievementMessage[2]) > vid.xres) w--;
1006 #endif
1007 displayfr(vid.xres/2, vid.yres/4 + vid.fsize*4, 2, w, achievementMessage[2], col, 8);
1008 }
1009 #endif
1010 }
1011
isAscending(int i)1012 EX bool isAscending(int i) {
1013 return i == 13 || i == 14 || i == 15 || i == 16 || i == 29 || i == 30 || i == 45;
1014 }
1015
score_default(int i)1016 EX int score_default(int i) {
1017 if(isAscending(i)) return 1999999999;
1018 else return 0;
1019 }
1020
1021 #ifndef HAVE_ACHIEVEMENTS
get_sync_status()1022 EX int get_sync_status() { return 0; }
set_priority_board(int)1023 EX void set_priority_board(int) { }
1024 #endif
1025
1026 }
1027