mirror of
https://github.com/Facepunch/Facepunch.Steamworks.git
synced 2025-03-27 12:49:00 +03:00
257 lines
6.8 KiB
C#
257 lines
6.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using SteamNative;
|
|
|
|
namespace Facepunch.Steamworks
|
|
{
|
|
public class Achievements : IDisposable
|
|
{
|
|
internal Client client;
|
|
|
|
public Achievement[] All { get; private set; }
|
|
|
|
public event Action OnUpdated;
|
|
public event Action<Achievement> OnAchievementStateChanged;
|
|
|
|
private List<Achievement> unlockedRecently = new List<Achievement>();
|
|
|
|
internal Achievements( Client c )
|
|
{
|
|
client = c;
|
|
|
|
All = new Achievement[0];
|
|
SteamNative.UserStatsReceived_t.RegisterCallback( c, UserStatsReceived );
|
|
SteamNative.UserStatsStored_t.RegisterCallback( c, UserStatsStored );
|
|
|
|
Refresh();
|
|
}
|
|
|
|
public void Refresh()
|
|
{
|
|
var old = All;
|
|
|
|
All = Enumerable.Range( 0, (int)client.native.userstats.GetNumAchievements() )
|
|
.Select( x =>
|
|
{
|
|
if ( old != null )
|
|
{
|
|
var name = client.native.userstats.GetAchievementName( (uint)x );
|
|
var found = old.FirstOrDefault( y => y.Id == name );
|
|
if ( found != null )
|
|
{
|
|
if ( found.Refresh() )
|
|
{
|
|
unlockedRecently.Add( found );
|
|
}
|
|
return found;
|
|
}
|
|
}
|
|
|
|
return new Achievement( client, x );
|
|
} )
|
|
.ToArray();
|
|
|
|
foreach ( var i in unlockedRecently )
|
|
{
|
|
OnAchievementStateChanged?.Invoke( i );
|
|
}
|
|
|
|
unlockedRecently.Clear();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
client = null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Find an achievement by name. Will be null if not found, or not ready.
|
|
/// </summary>
|
|
public Achievement Find( string identifier )
|
|
{
|
|
return All.FirstOrDefault( x => x.Id == identifier );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unlock an achievement by identifier. If apply is true this will happen as expected
|
|
/// and the achievement overlay will popup etc. If it's false then you'll have to manually
|
|
/// call Stats.StoreStats() to actually trigger it.
|
|
/// </summary>
|
|
public bool Trigger( string identifier, bool apply = true )
|
|
{
|
|
var a = Find( identifier );
|
|
if ( a == null ) return false;
|
|
|
|
return a.Trigger( apply );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reset an achievement by identifier
|
|
/// </summary>
|
|
public bool Reset( string identifier )
|
|
{
|
|
return client.native.userstats.ClearAchievement( identifier );
|
|
}
|
|
|
|
private void UserStatsReceived( UserStatsReceived_t stats, bool isError )
|
|
{
|
|
if ( isError ) return;
|
|
|
|
Refresh();
|
|
|
|
OnUpdated?.Invoke();
|
|
}
|
|
|
|
private void UserStatsStored( UserStatsStored_t stats, bool isError )
|
|
{
|
|
if ( isError ) return;
|
|
|
|
Refresh();
|
|
|
|
OnUpdated?.Invoke();
|
|
}
|
|
}
|
|
|
|
public class Achievement
|
|
{
|
|
private Client client;
|
|
|
|
public string Id { get; private set; }
|
|
public string Name { get; private set; }
|
|
public string Description { get; private set; }
|
|
|
|
/// <summary>
|
|
/// True if unlocked
|
|
/// </summary>
|
|
public bool State { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Should hold the unlock time if State is true
|
|
/// </summary>
|
|
public DateTime UnlockTime { get; private set; }
|
|
|
|
private int iconId { get; set; } = -1;
|
|
private int refreshCount = 0;
|
|
|
|
/// <summary>
|
|
/// If this achievement is linked to a stat this will return the progress.
|
|
/// </summary>
|
|
public float Percentage
|
|
{
|
|
get
|
|
{
|
|
if ( State )
|
|
return 1;
|
|
|
|
float pct = 0;
|
|
|
|
if ( !client.native.userstats.GetAchievementAchievedPercent( Id, out pct ) )
|
|
return -1.0f;
|
|
|
|
return pct;
|
|
}
|
|
}
|
|
|
|
private Image _icon;
|
|
|
|
public Image Icon
|
|
{
|
|
get
|
|
{
|
|
if ( iconId <= 0 ) return null;
|
|
|
|
if ( _icon == null )
|
|
{
|
|
_icon = new Image();
|
|
_icon.Id = iconId;
|
|
}
|
|
|
|
if ( _icon.IsLoaded )
|
|
return _icon;
|
|
|
|
if ( !_icon.TryLoad( client.native.utils ) )
|
|
return null;
|
|
|
|
return _icon;
|
|
}
|
|
}
|
|
|
|
public Achievement( Client client, int index )
|
|
{
|
|
this.client = client;
|
|
|
|
Id = client.native.userstats.GetAchievementName( (uint) index );
|
|
Name = client.native.userstats.GetAchievementDisplayAttribute( Id, "name" );
|
|
Description = client.native.userstats.GetAchievementDisplayAttribute( Id, "desc" );
|
|
|
|
iconId = client.native.userstats.GetAchievementIcon( Id );
|
|
|
|
Refresh();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Make this achievement earned
|
|
/// </summary>
|
|
public bool Trigger( bool apply = true )
|
|
{
|
|
if ( State )
|
|
return false;
|
|
|
|
State = true;
|
|
UnlockTime = DateTime.Now;
|
|
|
|
var r = client.native.userstats.SetAchievement( Id );
|
|
|
|
if ( apply )
|
|
{
|
|
client.Stats.StoreStats();
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reset this achievement to not achieved
|
|
/// </summary>
|
|
public bool Reset()
|
|
{
|
|
State = false;
|
|
UnlockTime = DateTime.Now;
|
|
|
|
return client.native.userstats.ClearAchievement( Id );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Refresh the unlock state. You shouldn't need to call this manually
|
|
/// but it's here if you have to for some reason. Retuns true if state changed (meaning, probably unlocked)
|
|
/// </summary>
|
|
public bool Refresh()
|
|
{
|
|
bool previousState = State;
|
|
|
|
bool state = false;
|
|
uint unlockTime;
|
|
|
|
State = false;
|
|
|
|
if ( client.native.userstats.GetAchievementAndUnlockTime( Id, ref state, out unlockTime ) )
|
|
{
|
|
State = state;
|
|
UnlockTime = Utility.Epoch.ToDateTime( unlockTime );
|
|
}
|
|
|
|
refreshCount++;
|
|
|
|
if ( previousState != State && refreshCount > 1 )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
}
|