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