GameCenter.mm 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #ifdef TORQUE_ALLOW_GAMEKIT
  23. #import "GameCenter.h"
  24. #include "platformiOS.h"
  25. #include "string/stringBuffer.h"
  26. #include "sim/simBase.h"
  27. extern iOSPlatState platState;
  28. IMPLEMENT_CONOBJECT(Achievement);
  29. // Blank constructor for Achievement class
  30. Achievement::Achievement()
  31. {
  32. }
  33. // Called when declared in script or registered
  34. bool Achievement::onAdd()
  35. {
  36. // Perform onAdd routine from parent class(es)
  37. if (!Parent::onAdd())
  38. return false;
  39. // Add to named AchievementSet
  40. // This set is available from anywhere in TorqueScript as AchievementSet
  41. Sim::getAchievementSet()->addObject(this);
  42. // Call onAdd in script!
  43. Con::executef(this, 2, "onAdd", Con::getIntArg(getId()));
  44. // Everything went well, return true
  45. return true;
  46. }
  47. // Called when deleted from script
  48. void Achievement::onRemove()
  49. {
  50. // We call this on this objects namespace so we unlink them after
  51. // Call onRemove in script!
  52. Con::executef(this, 2, "onRemove", Con::getIntArg(getId()));
  53. Parent::onRemove();
  54. }
  55. // Expose all the Achievement fields to TorqueScript
  56. void Achievement::initPersistFields()
  57. {
  58. // The following member variables are the iT2D equivalent of what is found in Game Center
  59. // It is important to note that the actual properties in Game Center are read-only
  60. addField("identifier", TypeString, Offset(mIdentifier, Achievement), "A unique string used to identify the achievement");
  61. addField("title", TypeString, Offset(mTitle, Achievement), "Achievement Title");
  62. addField("achievedDescription", TypeString, Offset(mAchievedDescription, Achievement), "A localized description of the completed achievement");
  63. addField("unachievedDescription", TypeString, Offset(mUnachievedDescription, Achievement), "A localized description of the achievement, to be used when the achievement has not been completed");
  64. addField("hidden", TypeBool, Offset(mHidden, Achievement), "A Boolean value that states whether this achievement should be visible to players");
  65. addField("maximumPoints", TypeS32, Offset(mMaximumPoints, Achievement), "The number of points earned by completing this achievement");
  66. }
  67. // Helper function used to set all the achievement fields
  68. // This is only useful when creating a Achievement in C++
  69. // Currently the only example of this is used in cacheAchievements,
  70. // converting a GKAchievementDescription into a Achievement
  71. void Achievement::setAchievementFields(const char* identifier, const char* title, const char* achievedDescription, const char* unachievedDescription, int maxPoints, bool hidden)
  72. {
  73. mIdentifier = StringTable->insert(identifier);
  74. mTitle = StringTable->insert(title);
  75. mAchievedDescription = StringTable->insert(achievedDescription);
  76. mUnachievedDescription = StringTable->insert(unachievedDescription);
  77. mMaximumPoints = maxPoints;
  78. mHidden = hidden;
  79. }
  80. // Print all the Achievement fields to the console
  81. // This is only useful for debugging purposes
  82. void Achievement::printAchievement()
  83. {
  84. Con::printf("Achievement identifier: %s", mIdentifier);
  85. Con::printf("Achievement title: %s", mTitle);
  86. Con::printf("Achievement description: %s", mAchievedDescription);
  87. Con::printf("Achievement un-description: %s", mUnachievedDescription);
  88. Con::printf("Achievement maximum points: %i", mMaximumPoints);
  89. Con::printf("Achievement hidden: %i", mHidden);
  90. }
  91. // Exposes the Achievement::printAchievement() function to TorqueScript
  92. // only useful for debugging purposes
  93. ConsoleMethod(Achievement, printAchievement, void, 2, 2, "() Prints out the properties of an achievement")
  94. {
  95. object->printAchievement();
  96. }
  97. // Utility function that will determine whether the current iOS support Game Center
  98. BOOL isGameCenterAvailable()
  99. {
  100. // Build a class from a string, using a Game Center specific class: GKLocalPlayer
  101. // http://developer.apple.com/library/ios/#documentation/GameKit/Reference/GKLocalPlayer_Ref/Reference/Reference.html
  102. Class gcClass = (NSClassFromString(@"GKLocalPlayer"));
  103. // This build of iT2D is shooting for a minimum of iOS 4.1
  104. NSString *reqSysVer = @"4.1";
  105. NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
  106. BOOL osVersionSupported = ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending);
  107. return (gcClass && osVersionSupported);
  108. }
  109. // Primary Game Center Objective-C starts here
  110. @implementation GameCenterManager
  111. @synthesize achievementCache;
  112. @synthesize gameCenterViewController;
  113. @synthesize achievementCount;
  114. // Main initialization function for GameCenterManager
  115. // This is exposed to TorqueScript via the initGameCenter() function
  116. - (id) init
  117. {
  118. self = [super init];
  119. if(self != NULL)
  120. {
  121. gameCenterAvailable = NO;
  122. isAuthenticated = NO;
  123. achievementCount = 0;
  124. achievementCache = NULL;
  125. gameCenterViewController = [[UIViewController alloc] init];
  126. gameCenterViewController.view.hidden = true;
  127. }
  128. return self;
  129. }
  130. // Main cleanup function for GameCenterManager
  131. // This is exposed to TorqueScript via the closeGameCenter() function
  132. - (void) dealloc
  133. {
  134. // TODO - Call this from script
  135. gameCenterAvailable = NO;
  136. isAuthenticated = NO;
  137. self.achievementCache = NULL;
  138. if(gameCenterViewController != NULL)
  139. [gameCenterViewController release];
  140. [super dealloc];
  141. }
  142. // This is the second part of initializing Game Center. The init function simply allocates memory.
  143. // This function actually connects and authenticates the current user
  144. // Exposed to TorqueScript via the initGameCenter() function
  145. - (void) authenticateLocalUser
  146. {
  147. // Assume the player is not currently authenticated
  148. isAuthenticated = NO;
  149. // Check to see if this iOS supports Game Center
  150. gameCenterAvailable = isGameCenterAvailable();
  151. // Game Center not supported. Abort and print the error
  152. if(!gameCenterAvailable)
  153. {
  154. Con::printf("Failed to authenticate player. This iOS does not support Game Center");
  155. return;
  156. }
  157. // The iOS supports Game Center, so attempt to authenticate
  158. [[GKLocalPlayer localPlayer] authenticateWithCompletionHandler:^(NSError *error)
  159. {
  160. // Something went wrong with the authentication
  161. // Find out what the error is and print it to the console
  162. if (error != nil)
  163. {
  164. const char* errorMessage = [[error localizedDescription] UTF8String];
  165. Con::printf("Failed to authenticate player. %s", errorMessage);
  166. // Let the scripts know. 0 means it failed
  167. Con::executef(2, "gameCenterAuthenticationChanged", "0");
  168. }
  169. else
  170. {
  171. // Successful authentication occurred
  172. isAuthenticated = YES;
  173. // Start listening for changes in authentication
  174. [self registerForAuthenticationNotification:error];
  175. // Cache the achievements
  176. [self cacheAchievements];
  177. // Let the scripts know. 1 means it succeeded
  178. Con::executef(2, "gameCenterAuthenticationChanged", "1");
  179. }
  180. }];
  181. }
  182. // Helper function. This watches for any changes to the authentication for the Local Player
  183. - (void)registerForAuthenticationNotification: (NSError*) error
  184. {
  185. if(error == NULL)
  186. {
  187. NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
  188. [nc addObserver:self selector:@selector(authenticationChanged) name:GKPlayerAuthenticationDidChangeNotificationName object:nil];
  189. Con::printf("Game Center Authentication has changed");
  190. }
  191. }
  192. // Signaled by registerForAuthenticationNotification
  193. // This function will find out how the authentication changed
  194. - (void) authenticationChanged
  195. {
  196. gameCenterAvailable = isGameCenterAvailable();
  197. if(!gameCenterAvailable)
  198. {
  199. Con::printf("Authentication failed: Game Center is not initialized or supported on this iOS.");
  200. return;
  201. }
  202. // Check to see if player is still authenticated
  203. if([GKLocalPlayer localPlayer].authenticated == YES)
  204. {
  205. isAuthenticated = YES;
  206. Con::printf("Authentication changed successfully");
  207. Con::executef(2, "gameCenterAuthenticationChanged", "1");
  208. }
  209. else
  210. {
  211. Con::printf("Authentication failed for Local Player");
  212. Con::executef(2, "gameCenterAuthenticationChanged", "0");
  213. }
  214. }
  215. // Reload all the scores for a specific leaderboard (determined by category)
  216. // Exposed to TorqueScript via reloadHighScores(%category);
  217. - (void) reloadHighScoresForCategory:(NSString *)category
  218. {
  219. GKLeaderboard* leaderBoard = [[[GKLeaderboard alloc] init] autorelease];
  220. leaderBoard.playerScope = GKLeaderboardPlayerScopeGlobal;
  221. leaderBoard.category = category;
  222. leaderBoard.timeScope = GKLeaderboardTimeScopeAllTime;
  223. leaderBoard.range = NSMakeRange(1, 15);
  224. [leaderBoard loadScoresWithCompletionHandler:^(NSArray *scores, NSError *error)
  225. {
  226. if(error != nil)
  227. {
  228. const char* errorMessage = [[error localizedDescription] UTF8String];
  229. Con::printf("Failed to reload highscores: %s", errorMessage);
  230. // Perform custom error handling here
  231. }
  232. else
  233. {
  234. const char* board = [category UTF8String];
  235. Con::printf("Highscores successfully reloaded for %s", board);
  236. // Perform success code here
  237. }
  238. }];
  239. }
  240. // Responsible for reporting a new score to a leaderboard (determined by category)
  241. // Exposed to TorqueScript via reportScore(%score, %category);
  242. - (void) reportScore:(int64_t)score forCategory:(NSString *)category
  243. {
  244. GKScore* scoreReporter = [[[GKScore alloc] initWithCategory:category] autorelease];
  245. scoreReporter.value = score;
  246. [scoreReporter reportScoreWithCompletionHandler:^(NSError *error)
  247. {
  248. if(error != nil)
  249. {
  250. const char* errorMessage = [[error localizedDescription] UTF8String];
  251. Con::printf("Failed to submit score. %s", errorMessage);
  252. }
  253. else
  254. {
  255. [self reloadHighScoresForCategory:category];
  256. const char* board = [category UTF8String];
  257. Con::printf("Score successfully submitted to %s", board);
  258. // Perform custom success code here (saving, notification, etc)
  259. }
  260. }];
  261. }
  262. // Displays the leaderboard using Game Center's interface
  263. - (void) showLeaderboard
  264. {
  265. // Check to see if local player is signed in
  266. // If not, error out and exit function
  267. if([GKLocalPlayer localPlayer].authenticated == NO)
  268. {
  269. Con::printf("Failed to show leaderboard: Local Player not authenticated");
  270. return;
  271. }
  272. GKLeaderboardViewController *leaderBoardController = [[GKLeaderboardViewController alloc] init];
  273. if(leaderBoardController != nil)
  274. {
  275. leaderBoardController.leaderboardDelegate = self;
  276. UIWindow* window = [UIApplication sharedApplication].keyWindow;
  277. [window addSubview: gameCenterViewController.view];
  278. [gameCenterViewController presentModalViewController:leaderBoardController animated:YES];
  279. }
  280. }
  281. // Called when the user finishes viewing the Leader Board view
  282. - (void) leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController
  283. {
  284. [gameCenterViewController dismissModalViewControllerAnimated:YES];
  285. [viewController.view removeFromSuperview];
  286. [viewController release];
  287. }
  288. // Responsible for submitting achievement progress
  289. // identifier is the unique string for the achievement
  290. // percentComplete determines how far the user has progressed to unlocking the achievement
  291. // Exposed to TorqueScript via submitAchievement(%identifier, %percentComplete);
  292. - (void) submitAchievement:(NSString *)identifier percentComplete:(float)percentComplete
  293. {
  294. GKAchievement* achievement = [self.achievementCache objectForKey:identifier];
  295. if(achievement != NULL)
  296. {
  297. if((achievement.percentComplete >= 100.0) || (achievement.percentComplete >= percentComplete))
  298. {
  299. achievement = NULL;
  300. achievement.percentComplete = percentComplete;
  301. }
  302. }
  303. else
  304. {
  305. achievement = [[[GKAchievement alloc] initWithIdentifier:identifier] autorelease];
  306. achievement.percentComplete = percentComplete;
  307. [self.achievementCache setObject:achievement forKey:achievement.identifier];
  308. }
  309. if(achievement != NULL)
  310. {
  311. [achievement reportAchievementWithCompletionHandler:^(NSError *error)
  312. {
  313. if (error != nil)
  314. {
  315. const char* errorMessage = [[error localizedDescription] UTF8String];
  316. Con::printf("Failed to submit achievement: %s", errorMessage);
  317. // Perform custom error handling here
  318. }
  319. else
  320. {
  321. const char* achievementID = [identifier UTF8String];
  322. Con::printf("Achievement %s successfully submitted", achievementID);
  323. // Perform custom success code here (saving, notification, etc)
  324. }
  325. }];
  326. }
  327. }
  328. // Resets all achievement progress for a user
  329. // Exposed to TorqueScript via resetAchievements();
  330. - (void) resetAchievements
  331. {
  332. self.achievementCache = NULL;
  333. [GKAchievement resetAchievementsWithCompletionHandler:^(NSError *error)
  334. {
  335. if(error != nil)
  336. {
  337. const char* errorMessage = [[error localizedDescription] UTF8String];
  338. Con::printf("Failed to submit achievement: %s", errorMessage);
  339. // Perform custom error handling here
  340. }
  341. else
  342. {
  343. Con::printf("Achievements reset");
  344. // Perform custom success handling here
  345. }
  346. }];
  347. }
  348. // Displays the achievements using Game Center's view
  349. - (void)showAchievements
  350. {
  351. // Check to see if local player is signed in
  352. // If not, error out and exit function
  353. if([GKLocalPlayer localPlayer].authenticated == NO)
  354. {
  355. Con::printf("Failed to show achievements: Local Player not authenticated");
  356. return;
  357. }
  358. GKAchievementViewController *achievements = [[GKAchievementViewController alloc] init];
  359. if (achievements != nil)
  360. {
  361. achievements.achievementDelegate = self;
  362. UIWindow* window = [UIApplication sharedApplication].keyWindow;
  363. [window addSubview: gameCenterViewController.view];
  364. [gameCenterViewController presentModalViewController: achievements animated: YES];
  365. }
  366. }
  367. // Called when the user finishes viewing the achievements view
  368. - (void)achievementViewControllerDidFinish:(GKAchievementViewController *)viewController
  369. {
  370. [gameCenterViewController dismissModalViewControllerAnimated:YES];
  371. [viewController.view removeFromSuperview];
  372. [viewController release];
  373. }
  374. // This extremely important function is used for grabbing all the achievements this app supports
  375. // and exposing it to TorqueScript. This will create new t2DAchievements and populate them
  376. // with data accessed from iTunes Connect. This will only work if you have an iTunes Connect
  377. // account setup, along with a registered app with achievements.
  378. //
  379. // For more information on iTunes Connect, see the following two links:
  380. // iT2D Official Documentation: http://docs.garagegames.com/it2d/official
  381. // iTunes Connect Doc: https://itunesconnect.apple.com/docs/iTunesConnect_DeveloperGuide.pdf
  382. - (void)cacheAchievements
  383. {
  384. if(self.achievementCache == NULL)
  385. {
  386. [GKAchievement loadAchievementsWithCompletionHandler:^(NSArray *achievements, NSError *error)
  387. {
  388. if(error != nil)
  389. {
  390. const char* errorMessage = [[error localizedDescription] UTF8String];
  391. Con::printf("Failed to cache achievements. %s", errorMessage);
  392. // Perform custom caching code here (loading from file, retrying, etc)
  393. }
  394. else
  395. {
  396. NSMutableDictionary* tempCache = [NSMutableDictionary dictionaryWithCapacity: [achievements count]];
  397. for(GKAchievement* achievement in achievements)
  398. {
  399. Con::printf("Achievement progres: %f", achievement.percentComplete);
  400. [tempCache setObject: achievement forKey: achievement.identifier];
  401. }
  402. self.achievementCache = tempCache;
  403. }
  404. }];
  405. }
  406. [GKAchievementDescription loadAchievementDescriptionsWithCompletionHandler:
  407. ^(NSArray *descriptions, NSError *error)
  408. {
  409. // Something went wrong when trying to access the achievement list
  410. // The error should contain some information, so print the localized string
  411. if (error != nil)
  412. {
  413. const char* errorMessage = [[error localizedDescription] UTF8String];
  414. Con::printf("Failed to cache achievements. %s", errorMessage);
  415. }
  416. else
  417. {
  418. // If there are no achievements, the array will be nil
  419. // Otherwise, start iteration through them
  420. if (descriptions != nil)
  421. {
  422. // For each individual achievement in the array
  423. for(GKAchievementDescription* description in descriptions)
  424. {
  425. // Convert the GKAchievementDescription fields to iT2D compatible versions
  426. const char* title = [description.title UTF8String];
  427. const char* identifier = [description.identifier UTF8String];
  428. const char* achievedDescription = [description.achievedDescription UTF8String];
  429. const char* unachieveDescription = [description.unachievedDescription UTF8String];
  430. int maxPoints = description.maximumPoints;
  431. bool hidden = description.hidden;
  432. // First, check to see if this is a duplicate or not
  433. // If it is, skip adding this
  434. if(Sim::getAchievementSet()->findObject(identifier))
  435. {
  436. // Do not allocate memory or add this achievement
  437. // it already exists in our AchievementSet
  438. continue;
  439. }
  440. // Create the new Achievement
  441. Achievement* ach = new Achievement();
  442. // Plug in the properties to the new Achievement
  443. ach->setAchievementFields(identifier, title, achievedDescription, unachieveDescription, maxPoints, hidden);
  444. // Register this Achievement so it can be accessed from TorqueScript
  445. // and properly cleaned up during shut down
  446. ach->registerObject(identifier);
  447. }
  448. }
  449. // Store the number of achievements in GameCenterManager, just in case
  450. achievementCount = [descriptions count];
  451. Con::printf("Achievements cached");
  452. }
  453. }];
  454. }
  455. @end
  456. // The rest of the code consists of exposing the GameCenterManager to TorqueScript
  457. // via global ConsoleFunctions
  458. ConsoleFunction(isGameCenterAvailable, bool, 1, 1, "() Used to determine if current iOS is capable of using Game Center\n"
  459. "@return True if Game Center is supported, false otherwise")
  460. {
  461. return isGameCenterAvailable();
  462. }
  463. ConsoleFunction(initGameCenter, bool, 1, 1, "() Initialize Game Center and authenticate the player\n"
  464. "@return True if Game Center was successfully initialized, false if there was an error")
  465. {
  466. if(isGameCenterAvailable())
  467. {
  468. gameCenterManager = [[GameCenterManager alloc] init];
  469. [gameCenterManager authenticateLocalUser];
  470. }
  471. }
  472. ConsoleFunction(closeGameCenter, void, 1, 1, "() This will cleanup and close Game Center. Should only be called when the game is closed")
  473. {
  474. [gameCenterManager release];
  475. }
  476. ConsoleFunction(showLeaderboard, void, 1, 1, "() Display the leaderboards for this app using Game Center's view")
  477. {
  478. if(isGameCenterAvailable())
  479. {
  480. [gameCenterManager showLeaderboard];
  481. }
  482. else
  483. {
  484. Con::printf("Failed to show leaderboard: Game Center is not supported");
  485. }
  486. }
  487. ConsoleFunction(showAchievements, void, 1, 1, "() Display the achievements for this app using Game Center's view")
  488. {
  489. if(isGameCenterAvailable())
  490. {
  491. [gameCenterManager showAchievements];
  492. }
  493. else
  494. {
  495. Con::printf("Failed to show achievements: Game Center is not supported");
  496. }
  497. }
  498. ConsoleFunction(getAchievementCount, int, 1, 1, "() Gets the number of achievements this app supports from GameCenterManager\n"
  499. "@return Number of achievements obtained by connecting to iTunes Connect")
  500. {
  501. return [gameCenterManager achievementCount];
  502. }
  503. ConsoleFunction(cacheAchievements, void, 1, 1, "() Forces achievement cacheing and populates the AchievementSet")
  504. {
  505. [gameCenterManager cacheAchievements];
  506. }
  507. ConsoleFunction(submitScore, bool, 3, 3, "(int score, string category) Submit a score to a leaderboard, defined by category\n"
  508. "@param score Numerical value of the score being reported\n"
  509. "@param category String containing the unique category of the leaderboard\n"
  510. "@return True if the score was successfully submitted, false if there was an error")
  511. {
  512. // Convert the identifier from a char* to a NSString
  513. NSString* category = [[[NSString alloc] initWithUTF8String:argv[2]] autorelease];
  514. // Convert score from char* to an int
  515. int score = dAtoi(argv[1]);
  516. // Call the GameCenterManager method for reporting a score
  517. [gameCenterManager reportScore:score forCategory:category];
  518. }
  519. ConsoleFunction(reportAchievement, bool, 3, 3, "(string identifier, float percentComplete) Reports achievement progress to Game Center\n"
  520. "@param identifier A unique string used to identify the achievement\n"
  521. "@param percentComplete Floating point value representing the progress of the achievement\n"
  522. "@return True if the achievement succesfully registered, false if there was an error")
  523. {
  524. // Convert the identifier from a char* to a NSString
  525. NSString* identifier = [[[NSString alloc] initWithUTF8String:argv[1]] autorelease];
  526. // Convert the percentComplete from a char* to a float
  527. float percentComplete = dAtof(argv[2]);
  528. // Call the GameCenterManager method for reporting an achievement
  529. [gameCenterManager submitAchievement:identifier percentComplete:percentComplete];
  530. }
  531. ConsoleFunction(reloadHighScores, void, 2, 2, "(string category) Reload the highscore for a leaderboard, specified by category")
  532. {
  533. NSString* category = [[[NSString alloc] initWithUTF8String:argv[1]] autorelease];
  534. [gameCenterManager reloadHighScoresForCategory:category];
  535. }
  536. ConsoleFunction(resetAchievements, bool, 1, 1, "() Reset the achievement progress for the authenticated player. This cannot be undone\n"
  537. "@return True if the achievements were successfully cleared, false if there was an error")
  538. {
  539. [gameCenterManager resetAchievements];
  540. }
  541. #endif //TORQUE_ALLOW_GAMEKIT