Hey SoulPixel!
I went through my own code of handling achievements and my hunch is that you need to either
- wait for steam_stats to become available by repeatedly checking Steam,
- or that you also need to check if the steam_user is true, as achievements are tied to users
I don't know about your code structure and layout, but I can show you how it works for my game in principle, maybe that helps you or others too.
So when I start the game I set a whole bunch of global variables, among them flags for the status of Steam. That's not the best way to do this, but it worked for over two years now with my game, so I am not complaining
Code:
// setting global variables in an initialization script that runs when the game launches
global.steam_api_is_ready = false
global.steam_stats_are_ready = false
global.steam_overlay_is_on = false
global.steam_user_online = false
global.steam_persona_name = "Player"
global.steam_user_id = -1
// etc.
After the game starts and is on the first room with the title screen, I have a condition that runs only once with the first step event. Inside, I call two scripts, f_checkSteam() and f_achievementsRead()
Code:
//step event of the title screen object
// ...
if (do_init)
{
if (f_checkSteam())
{
f_achievementsRead()
try_steam_again = false
}
else
{
try_steam_again = true
}
// more init stuff
do_init = false
}
// ...
When
f_checkSteam() returns "false", later down I set an alarm (when
try_steam_again == true) that will try again to call Steam via
f_checkSteam() after a timeout of 30 seconds in my case.
Important: Don't call on Steam too often, like once a second, as too many request will have you go "over quota", meaning that Steamworks won't respond to the caller anymore. That's frustrating during development, but a minor catastrophe when launching your game and players can't upload their highscores/achievements/stats anymore because Steam is blocking them.
I am assuming that you already got code in place for setting and getting your achievements, but for anyone reading this post in the future,I will outline how I do it.
Let's look at the scripts in
do_init now individually.
Here's the boiled down version of my
f_checkSteam()
Code:
/// @function f_checkSteam()
var returnval = true // return value of the script
// simple call to see if steam is running.
global.steam_api_is_ready = steam_initialised();
if (global.steam_api_is_ready)
{
// debug
show_debug_message(scr+"Steam API ready")
// checking for steam stats
global.steam_stats_are_ready = steam_stats_ready();
if (global.steam_stats_are_ready)
{
show_debug_message("Steam stats are ready")
}
else
{
returnval = false;
show_debug_message("Steam stats are NOT ready")
}
global.steam_user_online = steam_is_user_logged_on();
//// if you need to do something with a user's stats, it also goes into this script
//// you would, of course, have to set the respective global variables first
if (global.steam_user_online)
{
show_debug_message(scr+"Steam user logged in")
global.steam_persona_name = steam_get_persona_name();
global.steam_user_id = steam_get_user_steam_id();
var user = global.steam_persona_name;
var steam_id = global.steam_user_id;
show_debug_message("User "+user+" identified with SID "+string(steam_id));
steam_offline = false;
}
else
{
returnval = false;
show_debug_message("Steam User not logged on.");
}
} // end of "if steam api is ready"
else
{
// here go all your fallback settings, e.g.
/*
global.steam_persona_name = "Player";
global.steam_user_id = global.steam_id_fallback_key;
show_debug_message("Steam API not ready. Using persona name "+global.steam_persona_name+", fallback Steam ID: "+string(global.steam_id_fallback_key));
*/
returnval = false;
} ;//end of if steam api ready
return returnval;
So all that happens here is setting the global variables. Once the
steam_stats are ready, my games calls
f_achievementsRead() to read the current value of all achievements into some global variables. Like I said, not the most elegant, but it gets the job done:
The
f_achievementsRead() script:
Code:
/// @func f_achievementsRead()
// Note: I am using global variables as keys so that I only have one string to correct
// should I change an achievement's API name in SteamWorks
// achievement for launching the game:
global.achievement_val_launchGame = steam_get_achievement(global.achievement_key_launchGame);
// in your case this would be something like
global.achievement_val_killFirstSlime = steam_get_achievement("KILL_FIRST_SLIME")
// ...assuming "KILL_FIRST_SLIME" is also the "API Name Progress Stat" within Steamworks.
// etc. for any additional achievements
Now when I want to set an achievement, I just set its global variable, as simple as
Code:
global.achievement_val_launchGame = true
That way I can "collect" a bunch of achievements and submit them to Steam at a later point. This is useful for situations such as this: You want to give your players an achievement for collecting all 5 coins in a level but grant it only "for real" when they also finished the level alive.
So when this should happen, the aptly titled script
f_achievementsWrite() is called and submits all achievements to Steam.
Code:
///@desc f_achievementsWrite()
// for each achievement:
if (!global.achievement_val_launchGame)
{
steam_set_achievement(global.achievement_key_launchGame);
}
// etc.
Sidenote: In my title screen controller object, I also have an async event, "Async - Steam" which deals with upload and download of any relevant leaderboard data, should you have some. If you're interested in how to work with those, I wrote about it
in this thread.