Mods / Achievements

Category: #Library #Utility
Author: Nateonus
Side: Both
Created: Dec 20th 2023 at 1:15 PM
Last modified: Mar 22nd at 10:25 PM
Downloads: 1755
Follow Unfollow 59

Latest file for Various v1.19.x:
natsachievements_1.1.0.zip 1-click install


You guessed it - This mod adds achievements. Currently comes with 16 achievements, and contains a super easy API for other modders to add achievements!

Achievements work on singleplayer and multiplayer! If used on a server, unlocking an achievement will be broadcast to all players, if the server's modconfig allows it.

Other mod developers should check out the Api Details section below. You can add achievements for this mod without requiring a dependency!

Press 'L' (configurable) to open your Achievements Journal.

If you wish to support my work, please consider donating a small amount at https://ko-fi.com/nateonus!

The achievements mod can be added to existing worlds without issue.

The following mods support achievements:

Mods with Achievements:

Want your mod listed here? Use the achievements mod API and leave a comment!

Mod Authors - Code Api Details!

The achievements API consists of a single .cs class that can be used to register, unlock, and check achievements.

The great thing about this is that it requires no dependencies on your part. If the user has the achievements mod installed and enabled, your newly added achievements will work. If the user does not have the achievements mod installed and enabled, your newly achievements will just silently not do anything. Error free, and easy to manage.

Step by step instructions:

  1. Create a new .cs class file, named "AchievementsManager".
  2. Replace your new file with the entire contents of the script below.
  3. Achievements should be registered on both sides - So call RegisterAchievement as many times as needed in your mod system's Start function.
  4. The achievementId should be unique across all mods - It is recommended to use something that is tied to your mod name to ensure it is unique, otherwise it will not be correctly registered.
  5. The achievementGraphicCode is a full item or block code. e.g. "gear-rusty" or "clay-fire".
  6. To finish setting up your achievement, add the following entries to your .lang file:
    1. "achievement-{achievementId}": "{Achievement name}"
    2. "achievement-{achievementId}-desc": "{Achievement description}"
    3. (Optional!) "achievement-{achievementId}-desc-undiscovered" : "{Achievement description when not unlocked}"
  7. Achievements can be unlocked or checked on either client side or server side.
    1. Achievements should not be checked before they are unlocked - The unlock function already does this. If the achievement has already been unlocked, nothing happens.
  8. The given script contains no error checking for an invalid achievement id. It is up to you to keep these ids in a static place.
  9. If your achievement appears in the Achievements Journal (L) (Requires Achievements mod installed), you have successfully created an achievement.
AchievementsManager.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Vintagestory.API.Common;

namespace Achievements
{
    internal class AchievementsManager
    {
        static Assembly cachedAchievementsAssembly = null;
        static MethodInfo registerAchievementMethod = null;
        static MethodInfo unlockAchievementMethod = null;
        static MethodInfo isAchievementUnlockedMethod = null;

        public static void RegisterAchievement(string modId, string achievementId, string achievementGraphicCode)
        {
            if (registerAchievementMethod == null)
            {
                Assembly a = GetAchievementsAssembly();
                if (a == null) return;
                Type t = a.GetType("Achievements.AchievementsApi");
                registerAchievementMethod = t.GetMethod("RegisterAchievement");
            }
            registerAchievementMethod.Invoke(null, new object[] { modId, achievementId, achievementGraphicCode });
        }

        public static void UnlockAchievementForPlayer(string achievementId, EntityPlayer player)
        {
            if (unlockAchievementMethod == null)
            {
                Assembly a = GetAchievementsAssembly();
                if (a == null) return;
                Type t = a.GetType("Achievements.AchievementsApi");
                unlockAchievementMethod = t.GetMethod("UnlockAchievementForPlayer");
            }
            unlockAchievementMethod.Invoke(null, new object[] { achievementId, player });
        }

        public static bool IsAchievementUnlockedForPlayer(string achievementId, EntityPlayer player, bool defaultIfNotInstalled = false)
        {
            if (isAchievementUnlockedMethod == null)
            {
                Assembly a = GetAchievementsAssembly();
                if (a == null) return defaultIfNotInstalled;
                Type t = a.GetType("Achievements.AchievementsApi");
                isAchievementUnlockedMethod = t.GetMethod("IsAchievementUnlockedForPlayer");
            }
            return (bool)isAchievementUnlockedMethod.Invoke(null, new object[] { achievementId, player });
        }

        static Assembly GetAchievementsAssembly()
        {
            if (cachedAchievementsAssembly != null) return cachedAchievementsAssembly;
            AppDomain cDomain = AppDomain.CurrentDomain;
            Assembly[] assemblies = cDomain.GetAssemblies();
            foreach (Assembly a in assemblies)
            {
                if (a.GetName().Name == "natsachievements")
                {
                    cachedAchievementsAssembly = a;
                    return cachedAchievementsAssembly;
                }
            }
            return null;
        }
    }
}
Mod Authors - Content/JSON Achievements

JSON Achievements are pretty simple to add! The mod comes with a few standard json achievements for you to take a look at.

Achievement files must be added to an "achievements" folder, in the same location as the "blocktypes", "itemtypes", "recipes", etc. folders.

In the "achievements" folder, create a new json file. This file can have any name, and you can store any number of achievements within one file.

The general syntax of an achievement file is:

[
  {
    "enabled": "true",
    "id": "eatcheese",
    "graphicCode": "cheese-waxedcheddar-4slice",
    "criteriaType": "EatAny",
    "criteriaData": [
      "game:cheese-*"
    ]
  },
  {
    "enabled": "true",
    "id": "killtwoheadeddrifter",
    "graphicCode": "creature-drifter-double-headed",
    "criteriaType": "KillAnyEntity",
    "criteriaData": [
      "drifter-double-headed"
    ]
  }
]
  • Enabled - Whether to load this achievement.
  • id - A unique (currently for all mods) identifier for your mod. Used throughout .lang files as well.
  • graphicCode - The block/item code that will show when the achievement is unlocked.
  • criteriaType - The type of trigger for this achievement. The available triggers are currently:
    • CreateAnyFromGridRecipe
    • HaveAnyInInventory
    • HaveAllInInventory
    • KillAnyEntity
    • EatAny
    • DestroyAnyBlock
    • PlaceAnyBlock
  • criteriaData - An array of asset locations - including wildcards that are used as data for the criteria.
    • For example, when using CreateAnyFromGridRecipe, if ANY of the blocks or items in criteriaData are crafted in the grid, the player will unlock the achievement.
    • All criteria types can use one or more asset locations for their criteria data.
    • Note that HaveAllInInventory will not unlock the achievement until all the asset locations are satisfied.
  • orderInList - An optional parameter that controls how achievements are listed in the achievement journal. Achievements are sorted based on lowest to highest from this value. Default is 1000.

When achievements have been added, you will need to register a few properties in your lang file.

"achtabname": "Nat's Achievements",
"achievement-eatcheese": "Lovely cheese, Gromit!",
"achievement-eatcheese-desc": "Eat a slice of cheese.",
  • achtabname - The name of the tab in the achievement log. This will usually just your mod name.
  • achievement-{achievementID} - The name of your achievement.
  • achievement-{achievementID}-desc - The description of your achievement.
  • (optional) achievement-{achievementID}-desc-undiscovered - The description of your achievement when it has not yet been unlocked.

And you have registered your very own achievements! Open the achievement log and you should see your mod's tab containing your new achievements! Put a comment on what mod you used achievements in, and it'll be advertised on this page!

Version For Game version Downloads Release date Changelog Download 1-click mod install*
v1.1.0 563 Mar 22nd at 10:25 PM Show natsachievements_1.1.0.zip Install now
v1.0.2 206 Mar 13th at 6:02 PM Show natsachievements_v1.0.2.zip Install now
v1.0.1 322 Feb 21st at 5:12 PM Show natsachievements_v1.0.1.zip Install now
v1.0.0 618 Dec 20th 2023 at 4:17 PM Show natsachievements_v1.0.0.zip Install now

27 Comments (oldest first | newest first)

💬 JefferZ, 2 days ago

An amazing mod! I had been wondering why there were no achievements in Vintage Story, and now there finally are.

I would like to know if you have plans to add more criteriaType. I'm developing a mod that includes many additional achievements and more criteriaType would be very useful. Also, as a suggestion, it would be great if clicking on an achievement showed the date it was earned, a more detailed description, and some additional information, like the coordinates where you were when you achieved it, for example.

As a token of gratitude, I am attaching the Spanish language file here. It is important to mention that the file name must be "es-es" and not simply "es", since Vintage Story differentiates between the Spanish of Spain and the Spanish of Latin America with "es-es" for Spain and "es-419 "for Latin America. In this case, it should be "es-es", since I am from Spain.

Contents of the language file "es-es"

💬 DejFidOFF, 6 days ago

Nateonus

impossible. Bot delete link which did not goes to https://mods.vintagestory.at/

so that´s why ask you. Need to find another way.

💬 NateonusAuthor, Apr 20th at 10:46 AM

DejFidOFF thanks! You're welcome to upload it to google drive or some other file sharing website and link it as a comment on here :)

💬 DejFidOFF, Apr 18th at 6:04 PM

Nateonus

Hello o/ I really like that mod, using it at our server. I translete achievements to the Czech language. Is it possible to add that to this translate to the mod? Where I can catch you with the file? Thank you.

 

💬 SpearAndFang, Apr 1st at 1:59 AM

Nateonus
I have added achievements to one of my mods.  Can I get it listed above please and thank you?
Primitive Survival

And thanks for the mod...very cool!

💬 NateonusAuthor, Mar 23rd at 8:27 PM

Shinji170981 Ahh, interesting.

I'm going to say that it's likely those mods that are causing an issue, but you've bought up an issue. In the next mod version, I shall add a command to manually unlock an achievement, for those ones that are not possible.

💬 Shinji170981, Mar 23rd at 7:24 PM

Nateonus sry, i forgot to mention that i am on 1.19.3 and i used a regular clay oven. I am using Culinary Artillery and Expanded Foods, which might have an impact on it.

Besides that, i am notsure what else it could be.

💬 NateonusAuthor, Mar 23rd at 5:55 PM

Shinji170981 Interesting...

What version are you using, and what oven did you use to bake the bread? Do you use any other mods that may alter ovens or bread?

💬 Shinji170981, Mar 23rd at 5:31 PM

For some reason, the "Bread baking" achievement doesn't work. I tried it with regular spelt dough. Tried to bake part baked, baked and charred but none of them triggered the achievement.

💬 Brady_The, Mar 23rd at 3:39 AM

It works flawlessly now. You even added an in-game option to change the keybind. Cheers!

💬 NateonusAuthor, Mar 22nd at 10:26 PM

Brady_The no need for concern, it wasn't your fault. I have fixed it! Please redownload the newest version and it should work.

The issue is due to the cairosharp DLL being packed inside the zip file.

💬 Brady_The, Mar 22nd at 10:22 PM

It's probably something on my end. I have to look into it further to see how, why, and what, but if I am honest I am currently in a bit of a VS exhaustion phase, so troubleshooting might take a while.

💬 Petalwing, Mar 22nd at 5:27 PM

This is so rad! I downloaded the second I saw this. Super excited about this mod's future and hoping other mods join in. I'm surprised that I haven't seen an achievement mod before now. Thanks. :)

In reference to Brady_The's experience, I thought I'd add some info for game version 1.19.4, mod v1.1.0, running 50 other mods.

Upon load, I got the chat message and opened the achievement journal. Everything seems to be in working order on my end. The Stone Age achievement triggered after knapping a knife. There's also a config file available. I noticed that 1.19.5 just dropped, but I didn't upgrade yet. The logs didn't have any errors. 

💬 Brady_The, Mar 21st at 2:58 PM

@Nateonus Got it. I only play Singleplayer and did some testing on a newly created world with only this mod enabled. Still nothing. I use a different control scheme, with "L" bound to an action. I assume this shouldn't break anything, though.

The only hints I currently have, are the following:

  • The config file wasn't created. Redownloading and reenabling didn't trigger anything.
  • The log writes following error:

21.3.2024 15:52:04 [Notification] Mods, sorted by dependency: game, natsachievements, creative, survival
21.3.2024 15:52:04 [Error] [natsachievements] An exception was thrown when trying to load assembly:
21.3.2024 15:52:04 [Error] [natsachievements] Exception: Assembly with same name is already loaded
at System.Runtime.Loader.AssemblyLoadContext.g____PInvoke|5_0(IntPtr ptrNativeAssemblyBinder, UInt16* ilPath, UInt16* niPath, ObjectHandleOnStack retAssembly)
at System.Runtime.Loader.AssemblyLoadContext.LoadFromAssemblyPath(String assemblyPath)
at System.Reflection.Assembly.LoadFrom(String assemblyFile)
at Vintagestory.Common.ModAssemblyLoader.LoadFrom(String path) in VintagestoryLib\Common\API\ModAssemblyLoader.cs:line 34
at Vintagestory.Common.ModContainer.<>c__DisplayClass35_0.b__0(String path) in VintagestoryLib\Common\API\ModContainer.cs:line 449
at System.Linq.Enumerable.SelectListIterator`2.MoveNext()
at System.Linq.Enumerable.WhereEnumerableIterator`1.ToList()
at Vintagestory.Common.ModContainer.LoadAssembly(ModCompilationContext compilationContext, ModAssemblyLoader loader) in VintagestoryLib\Common\API\ModContainer.cs:line 448

The Seashells achievement troubled me a bit. I didn't want to take too much creative license in the translation, but the literal translation didn't translate too well. I am glad you like it! :-D

💬 NateonusAuthor, Mar 21st at 9:50 AM

Brady_The Hey Brady, thanks for the comment!

To clarify a few things: The default key for opening the journal is 'L'. When you start a game, do you receive a chat message that notifies you of this? If not, the mod may not be loading correctly.

Are you using any other mods that would conflict with this key?

Are you playing on a server that doesn't have the mod?

Regarding rebinding the key, this must be done in the external config file, not the controls menu. This is located in your userdata folder. Mine is found in: "C:\Users\Nat\AppData\Roaming\VintagestoryData\ModConfig", and is called "achievementsconfig".

You should see a line that says "AchievementsJournalMenuKey": 94. This number can be changed to change what key is used. Some suggestions:

  • P - Change to 98.
  • O - Change to 97.
  • I - Change to 91.
  • B - Change to 84.
  • For a specific letter key, start from A = 83 and offset that value for what letter you want to use.

Thanks for the translation file! I love "Fisherman Fritze fished fresh... mussel shells" as an entry. I'll pop it in the next version and give you a special thanks!

💬 Brady_The, Mar 21st at 3:45 AM

As an achievement enjoyer this is good stuff!

I wanted to have a quick look, but unfortunately I am not even able to open the Achievement Journal; neither am I able to rebind the key due to a missing entry in the controls menu.

Game version: 1.19.3

Mod version: 1.1.0

I didn't want to come empty-handed, so have a language file: https://drive.proton.me/urls/BGF4TN7PCC#vs1wmHkKKXRV

Cheers!

💬 NateonusAuthor, Mar 21st at 12:39 AM

Added a description of how to create achievements using JSON! Check out the "Mod Authors - Content/JSON Achievements" section!

💬 NateonusAuthor, Mar 20th at 11:59 PM

Achievements have been updated to 1.1.0! This rather large update adds a few more achievements, the ability to view achievements by mod, and allows achievements to be added through JSON code for content mods!

More details on how to achieve that will be coming imminently, so if you're interested check back soon!

💬 NateonusAuthor, Mar 17th at 12:44 PM

NiclAss that'd need a loooot of work and changes - It'd be an entire system of registering events through a JSON system to make it versatile enough.

What sort of events would you want to be able to call if I did do that?

💬 NiclAss, Mar 15th at 7:54 AM

Any chance for a content mod api via json file? 

Makes this mod unusable for content mods atm. 

💬 NateonusAuthor, Feb 21st at 5:12 PM

Hi all, I've released a version for 1.19 with a couple of bug fixes too.

💬 dotyerts, Jan 9th at 3:18 PM

Seeing this error on repeat every second in console for days, surviving every reboot

 

[Server Fatal] System.NullReferenceException: Object reference not set to an instance of an object.
at Achievements.AchievementsPatches.CheckForDoubleHeadedDrifter(EntityAgent __instance, EnumDespawnReason reason, DamageSource damageSourceForDeath)
at Vintagestory.GameContent.EntityBehaviorDespawn.OnGameTick(Single deltaTime) in C:\Users\Tyron\Documents\vintagestory\game\VSEssentials\Entity\Behavior\BehaviorDespawn.cs:line 105
at Vintagestory.API.Common.Entities.Entity.OnGameTick(Single dt) in C:\Users\Tyron\Documents\vintagestory\game\VintagestoryApi\Common\Entity\Entity.cs:line 852
at Vintagestory.API.Common.EntityAgent.OnGameTick(Single dt) in C:\Users\Tyron\Documents\vintagestory\game\VintagestoryApi\Common\Entity\EntityAgent.cs:line 528
at Vintagestory.Server.ServerSystemEntitySimulation.TickEntities(Single dt) in C:\Users\Tyron\Documents\vintagestory\game\VintagestoryLib\Server\Systems\World\EntitySimulation.cs:line 326
at Vintagestory.Server.ServerSystemEntitySimulation.OnServerTick(Single dt) in C:\Users\Tyron\Documents\vintagestory\game\VintagestoryLib\Server\Systems\World\EntitySimulation.cs:line 165
at Vintagestory.Server.ServerMain.Process() in C:\Users\Tyron\Documents\vintagestory\game\VintagestoryLib\Server\ServerMain.cs:line 881

💬 Ruyeex, Dec 20th 2023 at 3:49 PM

It could be a vanilla thing tbh

💬 Vilderos, Dec 20th 2023 at 2:54 PM

yay! I have been waiting for something like this :D

💬 ATree, Dec 20th 2023 at 2:16 PM

Very cool! :D

💬 NateonusAuthor, Dec 20th 2023 at 2:09 PM

Wellaaron Thank you! I'm hoping to add in a fair few more achievements, let me know if you think of any you'd like to see!

💬 Wellaaron, Dec 20th 2023 at 2:06 PM

awesome ! i love achievements purely for a checklist of what i've done to progress, amazing mod!

(edit comment delete)