Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
5fd987e
Remove deep destrou
AtonyGit Jul 27, 2026
1e08231
Make achievement icons into virtuals
AtonyGit Jul 27, 2026
23b2840
Update AchievementsAPI.csproj
AtonyGit Jul 28, 2026
458f3d6
Fix save data for inherited achievements
AtonyGit Jul 28, 2026
09a6ff7
Fix rounding for menu
AtonyGit Jul 28, 2026
2cb3611
Remove example tab if other mods have tabs
AtonyGit Jul 28, 2026
5d1e7df
Add support for custom bg sprites on achievements
AtonyGit Jul 28, 2026
d1f5a4e
Add text / icon offset for the achievements
AtonyGit Jul 28, 2026
42a2414
Add support for changing toast visuals
AtonyGit Jul 28, 2026
4a117f9
Hide example tab if unneeded
AtonyGit Jul 28, 2026
6b5c2bb
Add Sub-Icon for achievement context
AtonyGit Jul 28, 2026
b8de5dc
port to ventframework
ThetaHalo Jul 28, 2026
933b1ab
Merge pull request #1 from AtonyGit/master
ThetaHalo Jul 28, 2026
95ff62b
Merge branch 'master' into misc
ThetaHalo Jul 28, 2026
2f09716
Merge pull request #2 from ThetaHalo/misc
ThetaHalo Jul 28, 2026
e840a51
Fix typos
AtonyGit Jul 28, 2026
856de0f
Show achievements tab in local settings menu
AtonyGit Jul 28, 2026
a14ad35
Update AchievementsMenu.cs
AtonyGit Jul 28, 2026
d342085
Fix build error i think
AtonyGit Jul 28, 2026
d7020cd
add linq because this thing sucks
AtonyGit Jul 28, 2026
9604b07
Add improved persistence checks
AtonyGit Jul 28, 2026
2bd4af8
Merge pull request #3 from AtonyGit/master
ThetaHalo Jul 29, 2026
9f6f5b9
Merge branch 'master' into merge
ThetaHalo Jul 29, 2026
195f178
Merge pull request #4 from ThetaHalo/merge
ThetaHalo Jul 29, 2026
f021196
fix
ThetaHalo Jul 29, 2026
a4fa64f
Adjust unlocked logic checks
AtonyGit Jul 29, 2026
02a3672
Update AchievementPersistencePatches.cs
AtonyGit Jul 29, 2026
f676ff2
Merge pull request #5 from AtonyGit/master
ThetaHalo Jul 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,10 @@ obj/
riderModule.iml
/_ReSharper.Caches/
.idea/
*.lock.json
.vs/
*.user
.qodo
*.DS_Store

.fake
36 changes: 33 additions & 3 deletions AchievementsAPI/API/AchievementStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,38 @@ public static void AchievementStorageUpdate(BaseAchievement achievement, bool un
public static void AchievementStorageUpdate(CountAchievement achievement, int progress, bool unlocked)
{
var data = GetData(achievement);
var newProgress = Math.Clamp(progress, 0, achievement.RequiredValue);
data.Unlocked = unlocked;
if (achievement.ProgressPersists) data.Progress = progress;
if (achievement.ProgressPersists is AchPersistence.ThroughoutSessions || data.Unlocked) data.Progress = newProgress;

Save();
}
public static void AchievementDataReset(BaseAchievement achievement, bool? unlocked = false)
{
var data = GetData(achievement);
if (unlocked != null)
{
achievement.Unlocked = unlocked.Value;
data.Unlocked = unlocked.Value;
}

Save();
}

public static void AchievementDataReset(CountAchievement achievement, int progress = 0, bool? unlocked = null)
{
var data = GetData(achievement);
data.Progress = Math.Clamp(progress, 0, achievement.RequiredValue);
if (unlocked != null)
{
achievement.Unlocked = unlocked.Value;
data.Unlocked = unlocked.Value;
}
else
{
data.Unlocked = data.Progress == achievement.RequiredValue;
achievement.Unlocked = data.Progress == achievement.RequiredValue;
}

Save();
}
Expand Down Expand Up @@ -74,7 +104,7 @@ public static AchievementData GetData(CountAchievement achievement)
public static void AchievementStorageGet(AchievementsTab tab)
{
foreach (var propInfo in tab.GetType().GetProperties().Where(x =>
x.PropertyType == typeof(BaseAchievement)))
x.PropertyType.IsAssignableTo(typeof(BaseAchievement))))
{
var achievement = propInfo.GetValue(tab) as BaseAchievement;
if (achievement == null) continue;
Expand All @@ -85,7 +115,7 @@ public static void AchievementStorageGet(AchievementsTab tab)
}

foreach (var propInfo in tab.GetType().GetProperties().Where(x =>
x.PropertyType == typeof(CountAchievement)))
x.PropertyType.IsAssignableTo(typeof(CountAchievement))))
{
var achievement = propInfo.GetValue(tab) as CountAchievement;
if (achievement == null) continue;
Expand Down
60 changes: 42 additions & 18 deletions AchievementsAPI/API/AchievementToast.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
using UnityEngine;
using System.Collections;
using Il2CppInterop.Runtime.InteropTypes.Fields;
using Reactor.Utilities;
using Reactor.Utilities.Extensions;
using Rewired.Utils;
using UnityEngine.UI;
using VentLib.Utilities;
using VentLib.Utilities.Extensions;

namespace AchievementsAPI.API;
public class AchievementToast
Expand All @@ -13,12 +12,12 @@ public class AchievementToast

public static void ShowAndDeleteToast(BaseAchievement achievement)
{
Coroutines.Start(CoShowAndDeleteToast(achievement));
Async.Execute(CoShowAndDeleteToast(achievement));
}

public static void ShowAndDeleteToast(CountAchievement achievement, bool unlocked = false)
{
Coroutines.Start(CoShowAndDeleteToast(achievement, unlocked));
Async.Execute(CoShowAndDeleteToast(achievement, unlocked));
}

private static Transform GetOrCreateToast()
Expand All @@ -42,11 +41,17 @@ private static Transform GetOrCreateToast()
}
}

private static void PopulateToast(Transform toast, Sprite icon, System.Reflection.Assembly assembly, string title, string subtitle)
private static void PopulateToast(Transform toast, Sprite icon, System.Reflection.Assembly assembly, string title, string subtitle, Vector3 titleOffset, Vector3 subtitleOffset, Vector3 iconOffset)
{
toast.FindChild("AchievementIcon").gameObject.GetComponent<Image>().sprite = icon;
toast.FindChild("AchievementName").gameObject.GetComponent<TMPro.TextMeshProUGUI>().text = title;
toast.FindChild("AchievementObtainedText").gameObject.GetComponent<TMPro.TextMeshProUGUI>().text = subtitle;
var icoObj = toast.FindChild("AchievementIcon").gameObject.GetComponent<Image>();
icoObj.sprite = icon;
icoObj.transform.localPosition += iconOffset;
var titleObj = toast.FindChild("AchievementName").gameObject.GetComponent<TMPro.TextMeshProUGUI>();
titleObj.text = title;
titleObj.transform.localPosition += titleOffset;
var subtitleObj = toast.FindChild("AchievementObtainedText").gameObject.GetComponent<TMPro.TextMeshProUGUI>();
subtitleObj.text = subtitle;
subtitleObj.transform.localPosition += subtitleOffset;
}

private static IEnumerator CoAnimateAndDestroyToast()
Expand Down Expand Up @@ -74,7 +79,7 @@ private static IEnumerator CoAnimateAndDestroyToast()
yield return null;
}

currentToast.gameObject.DeepDestroy();
currentToast.gameObject.Destroy();
yield break;
}

Expand All @@ -87,10 +92,18 @@ public static IEnumerator CoShowAndDeleteToast(BaseAchievement achievement)

currentToast = GetOrCreateToast();
PopulateToast(currentToast, achievement.Icon, achievement.Assembly,
title: "Achievement Obtained!",
subtitle: achievement.Name);
"Achievement Obtained!",
achievement.Name,
achievement.ToastTitleOffset,
achievement.ToastObtainedOffset,
achievement.ToastIconOffset);
var img = currentToast.GetComponent<Image>();
if (achievement.ToastBgSprite != null)
{
img.m_Sprite = achievement.ToastBgSprite;
}

yield return Coroutines.Start(CoAnimateAndDestroyToast());
yield return CoAnimateAndDestroyToast();
}

public static IEnumerator CoShowAndDeleteToast(CountAchievement achievement, bool unlocked = false)
Expand All @@ -104,16 +117,27 @@ public static IEnumerator CoShowAndDeleteToast(CountAchievement achievement, boo
if (achievement.Hidden && achievement.HideProgress && !unlocked)
{
PopulateToast(currentToast, achievement.Icon, achievement.Assembly,
title: "Achievement Progressed!",
subtitle: $"Hidden Achievement");
"Achievement Progressed!",
"Hidden Achievement",
achievement.ToastTitleOffset,
achievement.ToastObtainedOffset,
achievement.ToastIconOffset);
}
else
{
PopulateToast(currentToast, achievement.Icon, achievement.Assembly,
title: unlocked ? "Achievement Obtained!" : "Achievement Progressed!",
subtitle: $"{achievement.Name} ({achievement.CurrentValue}/{achievement.RequiredValue})");
unlocked ? "Achievement Obtained!" : "Achievement Progressed!",
$"{achievement.Name} ({achievement.CurrentValue}/{achievement.RequiredValue})",
achievement.ToastTitleOffset,
achievement.ToastObtainedOffset,
achievement.ToastIconOffset);
}
var img = currentToast.GetComponent<Image>();
if (achievement.ToastBgSprite != null)
{
img.m_Sprite = achievement.ToastBgSprite;
}

yield return Coroutines.Start(CoAnimateAndDestroyToast());
yield return CoAnimateAndDestroyToast();
}
}
2 changes: 1 addition & 1 deletion AchievementsAPI/API/AchievementsTab.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public abstract class AchievementsTab
public abstract string Name { get; }

/// <summary>
/// Wether the tab should be selectable in the Achievements Menu. Handy for custom UI's or internal achievements.
/// Whether the tab should be selectable in the Achievements Menu. Handy for custom UI's or internal achievements.
/// </summary>
public virtual bool IsSelectable => true;

Expand Down
87 changes: 79 additions & 8 deletions AchievementsAPI/API/BaseAchievement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,73 @@
/// The achievement's icon's path
/// </summary>
public string IconPath;

/// <summary>
/// Gets or sets the achievement's icon
/// </summary>
public virtual Sprite Icon => _sprite;

/// <summary>
/// Gets the background sprite for the achievement in the achievements menu.
/// </summary>
public virtual Sprite? MenuBgSprite => null;

Check warning on line 32 in AchievementsAPI/API/BaseAchievement.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

/// <summary>
/// Gets the background sprite for the achievement in the toast pop up.
/// </summary>
public virtual Sprite? ToastBgSprite => null;

Check warning on line 37 in AchievementsAPI/API/BaseAchievement.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

/// <summary>
/// Gets the offset for the achievement's icon in the achievements menu.
/// </summary>
public virtual Vector3 MenuIconOffset => new(0, 0);

/// <summary>
/// Gets the offset for the achievement's title in the achievements menu.
/// </summary>
public virtual Vector3 MenuTitleOffset => new(0, 0);

/// <summary>
/// Gets the offset for the achievement's description in the achievements menu.
/// </summary>
public virtual Vector3 MenuDescOffset => new(0, 0);

/// <summary>
/// Gets the offset for the achievement's sub-icon, if any.
/// </summary>
public virtual Vector3 MenuSubIconOffset => new(0, 0);

/// <summary>
/// The achievement's icon
/// Gets the scale for the achievement's sub-icon, if any.
/// </summary>
public Sprite Icon;
public virtual Vector3 MenuSubIconScale => new(0.5f, 0.5f, 1);

/// <summary>
/// Gets the achievement's sub-icon, if any.
/// </summary>
public virtual Sprite? MenuSubIcon => null;

Check warning on line 67 in AchievementsAPI/API/BaseAchievement.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

/// <summary>
/// Gets the offset for the achievement's "Obtained" text in the toast pop up.
/// </summary>
public virtual Vector3 ToastObtainedOffset => new(0, 0);

/// <summary>
/// Gets the offset for the achievement's title in the toast pop up.
/// </summary>
public virtual Vector3 ToastTitleOffset => new(0, 0);

/// <summary>
/// Gets the offset for the achievement's icon in the toast pop up.
/// </summary>
public virtual Vector3 ToastIconOffset => new(0, 0);

/// <summary>
/// Whether the achievement's background becomes colored.
/// </summary>
public bool RarityOnBgSprite = true;

private Sprite _sprite;
public bool Unlocked;
/// <summary>
/// The achievement's rarity:
Expand All @@ -34,11 +97,11 @@
/// </summary>
public int Rarity;
/// <summary>
/// Wether the achievement is hidden or not (hidden achievements get the default icon and have their name and description set to "Hidden Achievement" until unlocked)
/// Whether the achievement is hidden or not (hidden achievements get the default icon and have their name and description set to "Hidden Achievement" until unlocked)
/// </summary>
public bool Hidden;
/// <summary>
/// Wether to hide the achievement's rarity (if the achievement is hidden)
/// Whether to hide the achievement's rarity (if the achievement is hidden)
/// </summary>
public bool HideRarity;
public Assembly Assembly;
Expand All @@ -56,15 +119,13 @@
if (doStorageUpdate) AchievementStorage.AchievementStorageUpdate(this, true);

}


public BaseAchievement(string name, string description, string iconPath, int rarity = 0, bool hidden = false, bool hideRarity = true, Assembly? assembly = null)
{
Name = name;
Description = description;
IconPath = iconPath;
Assembly = assembly ?? Assembly.GetCallingAssembly();
Icon = SpriteTools.LoadSpriteFromPath(IconPath, Assembly, 100);
_sprite = SpriteTools.LoadSpriteFromPath(IconPath, Assembly, 100);
Id = Assembly.GetName().Name + "_" + Name;
Rarity = rarity;
Hidden = hidden;
Expand All @@ -75,7 +136,17 @@
Name = name;
Description = description;
Assembly = assembly ?? Assembly.GetCallingAssembly();
Icon = icon;
_sprite = icon;
Id = Assembly.GetName().Name + "_" + Name;
Rarity = rarity;
Hidden = hidden;
HideRarity = hideRarity;
}
public BaseAchievement(string name, string description, int rarity = 0, bool hidden = false, bool hideRarity = true, Assembly? assembly = null)
{
Name = name;
Description = description;
Assembly = assembly ?? Assembly.GetCallingAssembly();
Id = Assembly.GetName().Name + "_" + Name;
Rarity = rarity;
Hidden = hidden;
Expand Down
26 changes: 20 additions & 6 deletions AchievementsAPI/API/CountAchievement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,28 @@ public class CountAchievement : BaseAchievement
/// </summary>
public int RequiredValue;
/// <summary>
/// Defines if the progress persists between games.
/// Defines how the progress persists between games.
/// </summary>
public bool ProgressPersists;
public AchPersistence ProgressPersists;
/// <summary>
/// Wether to hide the achievement's progress (if the achievement is hidden)
/// Whether to hide the achievement's progress (if the achievement is hidden)
/// </summary>
public bool HideProgress;
public CountAchievement(string name, string description, string iconPath, int currentValue, int requiredValue, bool progressPersists = true, int rarity = 0, bool hidden = false, bool hideRarity = true, bool hideProgress = false) : base(name, description, iconPath, rarity, hidden, hideRarity, System.Reflection.Assembly.GetCallingAssembly())
public CountAchievement(string name, string description, string iconPath, int currentValue, int requiredValue, AchPersistence progressPersists = AchPersistence.ThroughoutSessions, int rarity = 0, bool hidden = false, bool hideRarity = true, bool hideProgress = false) : base(name, description, iconPath, rarity, hidden, hideRarity, System.Reflection.Assembly.GetCallingAssembly())
{
CurrentValue = currentValue;
RequiredValue = requiredValue;
ProgressPersists = progressPersists;
HideProgress = hideProgress;
}
public CountAchievement(string name, string description, Sprite icon, int currentValue, int requiredValue, bool progressPersists = true, int rarity = 0, bool hidden = false, bool hideRarity = true, bool hideProgress = false) : base(name, description, icon, rarity, hidden, hideRarity, System.Reflection.Assembly.GetCallingAssembly())
public CountAchievement(string name, string description, Sprite icon, int currentValue, int requiredValue, AchPersistence progressPersists = AchPersistence.ThroughoutSessions, int rarity = 0, bool hidden = false, bool hideRarity = true, bool hideProgress = false) : base(name, description, icon, rarity, hidden, hideRarity, System.Reflection.Assembly.GetCallingAssembly())
{
CurrentValue = currentValue;
RequiredValue = requiredValue;
ProgressPersists = progressPersists;
HideProgress = hideProgress;
}
public CountAchievement(string name, string description, int currentValue, int requiredValue, AchPersistence progressPersists = AchPersistence.ThroughoutSessions, int rarity = 0, bool hidden = false, bool hideRarity = true, bool hideProgress = false) : base(name, description, rarity, hidden, hideRarity, System.Reflection.Assembly.GetCallingAssembly())
{
CurrentValue = currentValue;
RequiredValue = requiredValue;
Expand All @@ -54,7 +61,6 @@ public void Increment(int count, bool showOnUI = true)
/// <param name="showOnUI">Shows an unlock animation on the hud.</param>
public void SetValue(int value, bool showOnUI = true)
{

CurrentValue = value;
if (showOnUI && !Unlocked) AchievementToast.ShowAndDeleteToast(this, Unlocked);
if (CurrentValue >= RequiredValue)
Expand All @@ -67,4 +73,12 @@ public void SetValue(int value, bool showOnUI = true)
AchievementStorage.AchievementStorageUpdate(this, value, Unlocked);

}
}

public enum AchPersistence
{
ThroughoutSessions,
ThroughoutRounds,
ResetOnRoundStart,
ResetOnMeetingStart
}
10 changes: 9 additions & 1 deletion AchievementsAPI/AchievementsAPI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,19 @@
<VersionPrefix>1.1.3</VersionPrefix>
<VersionSuffix>dev</VersionSuffix>
<Description>Achievements API for Among Us</Description>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<!-- <Authors>your name</Authors> -->
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<IncludeContentInPack>true</IncludeContentInPack>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Reactor" Version="2.5.0-ci.371"/>
<None Include="../LICENSE" Pack="true" PackagePath="" />
<None Include="../README.md" Pack="true" PackagePath="" />
<Reference Include="../VFC/VentFrameworkContinued.dll">
<IncludeAssets>runtime; compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</Reference>
<PackageReference Include="BepInEx.Unity.IL2CPP" Version="6.0.0-be.735" Private="false" ExcludeAssets="runtime;native"/>
<PackageReference Include="AmongUs.GameLibs.Steam" Version="2026.3.31" PrivateAssets="all" />

Expand Down
Loading
Loading