Skip to content

Integration & Compatibility

Lean's edited this page Aug 20, 2026 · 92 revisions

Introduction

In this section there will be explanations of the classes, events and examples available for you to integrate your mod with LevelUP.

LevelUP works mostly on server side, and all events should be running on the EnumAppSide.Server, for security porpuses, rare situations are controlled by the client side.

Functions will generally have a reference number, so you can easily change and modify the numbers as you wish.

If you need to integrate with your mod and something is missing that you need, you can create a pull request.

Starting: Create your own mod and import the LevelUP.dll in the mod csproj

<ItemGroup>
    <Reference Include="LevelUP">
      <HintPath>$(VINTAGE_STORY)/Mods/LevelUP/LevelUP.dll</HintPath>
      <Private>false</Private>
    </Reference>
</ItemGroup>

Now you have access to the LevelUP Events, check below

LevelUP.Server.ExperienceEvents

  • OnExperienceIncrease
  • Player receives experience, amount is a reference you can modify it to increase or decrease the quantity received

  • OnSubExperienceIncrease
  • Player receives sub experience, amount is a reference you can modify it to increase or decrease the quantity received

  • OnExperienceReduced
  • Player losed experience, amount is a reference you can modify it to increase or decrease the quantity received

  • OnLevelUp
  • Player leveled up, if is a sublevel that leveled up the type variable will come with a "/": "Smithing/Knife"

LevelUP.Server.OverwriteDamageInteractionEvents

  • OnPlayerMeleeDoDamageStart
  • Player does damage with meele weapon (before levelup calculations)

  • OnPlayerMeleeDoDamageFinish
  • Player does damage with meele weapon (after levelup calculations)

  • OnPlayerRangedDoDamageStart
  • Player does damage with ranged weapon (before levelup calculations)

  • OnPlayerRangedDoDamageFinish
  • Player does damage with ranged weapon (after levelup calculations)

  • OnPlayerReceiveDamageStart
  • Player receives damage (before levelup calculation, does not include equipped armors)

  • OnPlayerReceiveDamageFinish
  • Player receives damage (after levelup calculations, includes equipped armors, but can be null if the damage does not belong to a entity)

  • OnPlayerReceiveDamageUnkown
  • Player receives damage from a source that could not be attributed to an attacking entity

  • OnPlayerToolViewStats
  • Client is about to view the tool attack power stats, damage is a reference you can modify

  • OnPlayerArmorViewStats
  • Client is about to view an armor slot's stats

  • OnPlayerArmorReceiveHandleStats
  • Armor slots are about to have their status handled

  • OnPlayerArmorReceiveDamageStat
  • Armor slots are about to absorb damage, damage is a reference you can modify

  • OnPlayerArmorViewStatsPos
  • Same as OnPlayerArmorViewStats but for the positional/off-hand armor slots

  • OnPlayerArmorReceiveHandleStatsPos
  • Same as OnPlayerArmorReceiveHandleStats but for the positional/off-hand armor slots

  • OnPlayerArmorReceiveDamageStatPos
  • Same as OnPlayerArmorReceiveDamageStat but for the positional/off-hand armor slots

LevelUP.Server.LevelBowEvents

  • OnBowDropChanceRefresh
  • Player arrow drop chance refreshed

LevelUP.Server.LevelSlingshotEvents

  • OnSlingshotAimingRefresh
  • Player slingshot aiming refreshed

LevelUP.Server.LevelShieldEvents

  • OnShieldRefreshed
  • Shield status refreshed with player level

LevelUP.Server.LevelKnifeEvents

  • OnKnifeHarvested
  • Player havested entity with a knife

LevelUP.Server.LevelFarmingEvents

  • OnHoeTill
  • Player tilled a soil

  • OnBerryForage
  • Player harvested a berry bush

  • OnHarvestCrop
  • Player harvested any crop

LevelUP.Server.LevelCookingEvents

  • OnCookedSingle
  • Player cooked a single type food

  • OnCookedPot
  • Player cooked a pot

LevelUP.Server.LevelHammerEvents

  • OnHammerItem
  • Player crafted any item using the hammer on anvil

  • OnHammerSmith
  • Player split the item on anvil

LevelUP.Server.LevelPanningEvents

  • OnPanning
  • Player panned

LevelUP.Server.LevelSmithingEvents

  • OnSmithingItem
  • Player crafted any item

  • OnSmithingArmor
  • Player crafted any armor

LevelUP.Server.LevelSmithing

  • ExecuteSmithItemCraftedCalculations
  • Execute the smithing craft calculations for a ItemStack and return it

LevelUP.Server.LevelPickaxeEvents

  • OnMiningOre
  • Player breaked any ore

Note

The bow/spear aiming-penalty-by-level logic (previously exposed as LevelBowEvents.OnBowAimingRefresh and LevelSpearEvents.OnSpearAimingRefresh) was moved into an internal Harmony patch (LevelUP.Server.OverwriteAimingAccuracy, reading Configuration.BowGetMovePenaltyReductionByLevel/Configuration.SpearGetMovePenaltyReductionByLevel). These events no longer exist and there is currently no public hook to intercept aiming penalty calculations.

LevelUP.Server.Experience

  • GetExperience
  • Returns the player experience from a level type

  • GetSubExperience
  • Returns the player sub experience from a level type and sub level type

  • IncreaseExperience
  • Increase a player experience from a level type and action or amount

  • IncreaseSubExperience
  • Increase a player experience from a level type, sub level type and amount

Level Calculations

You can access all levelup calculation by using the class LevelUP.Configuration

Level Loaded Configuration

You can access all levelup loaded configuration values by using the class LevelUP.Configuration, both client and server is sync with the configs.

Note: the mod no longer ships its default configuration as JSON files under assets/levelup/config/. Defaults are hardcoded directly in LevelUP.Configuration and the effective config is generated/persisted at runtime under the game's data folder (ModConfig/LevelUP/config/...), not inside the mod's assets.

Crafting

LevelUP.Server.LevelSmithing.ExecuteSmithItemCraftedCalculations

This function is called everytime a player craft any item, if for some reason the player use the same item to craft the tool or armor it will be ignored by smithing level, levelup also checks for the attribute "repaired" if is true it will be ignored. (Also put the attribute repaired_by to understand who you are)

LevelUP only changes the durability of item, because if you change the attack power and mining speed it will change for all tools of that type, for example if levelup changes for "game:copper-axe" ALL copper axes will have that status, because of this reason levelup create 2 new attributes for storing this status: attackpower and miningspeed, now the levelup will handle the damage in CollectibleObject.GetAttackPower and the mining speed on CollectibleObject.GetMiningSpeed.

Important

Armor and shield bonuses are no longer driven by base attributes stored on the ItemStack. There is no BaseFlatDamageReduction, BaseRelativeProtection, BaseHealingEffectivness, BaseHungerRate, BaseRangedWeaponsAccuracy, BaseRangedWeaponsSpeed, BaseWalkSpeed, BasePassiveProjectile, BaseActiveProjectile, BasePassive, BaseActive, BaseProjectileDamageAbsorption or BaseDamageAbsorption attribute anymore.

Today these bonuses are calculated purely from the player's current level, read directly from Entity.WatchedAttributes (e.g. LevelUP_Level_LeatherArmor, LevelUP_Level_Shield) and passed through per-level Configuration functions, for example:

  • Armor: Configuration.LeatherArmorRelativeProtectionMultiplyByLevel(level) (and the equivalent function for each armor type: Chain, Brigandine, Lamellar, Plate, Scale)
  • Shield: Configuration.ShieldGetPassiveProjectileByLevel(level), ShieldGetActiveProjectileByLevel(level), ShieldGetPassiveByLevel(level), ShieldGetActiveByLevel(level), ShieldGetProjectileDamageAbsorptionByLevel(level), ShieldGetDamageAbsorptionByLevel(level)

Smithing still writes one attribute back to the crafted ItemStack: LevelUP_Smithing_StatsMultiply (a float, stored as a culture-invariant string), which is an extra multiplier applied on top of the level-based calculation above for items crafted by a smith with bonus stats. There is no per-armor-type "Base*" attribute to set manually anymore.

Watched Attributes

EntityPlayer

LevelUP store some watched attributes so client can easily view their levels. (Changing these watched attributes will have no effect to the server this is only for client visual)

  • LevelUP_{levelType} (long)
  • Stores the experience the player has with the level

  • LevelUP_{levelType}_Sub_{subLevelType} (long)
  • Stores the experience the player has with the sub level

  • LevelUP_Level_{levelType} (int)
  • Stores the current level the player has

  • LevelUP_Level_{levelType}_Sub_{subLevelType} (int)
  • Stores the current sub level the player has

  • LevelUP_{levelType}_MiningSpeed (float)
  • Stores the player LEVEL mining speed increase, so it can handle easily the animation of mining speed

  • LevelUP_Level_{levelType}_RemainingNextLevelPercentage (float)
  • Stores the player remaining in percentage for the next level 0 -> 100

LevelUP.Server.Instance

  • CommunicationChannel
  • Communication channel used to send data to the client

  • UpdatePlayerLevels
  • Force refresh player levels and status

  • ResetPlayerLevels
  • Reduce all player levels, reduce only the level progress (experience towards the next level, not the level itself). overwriteLose is the fraction of that progress that is actually lost: 0.2 -> 20% lose rate, 0.5 -> 50% lose rate, 0.8 -> 80% lose rate. Pass -1 (the default) to use Configuration.hardcoreLosePercentage instead of a custom value.

Custom Level

Register your new level

LevelUP.Configuration.RegisterNewLevel("MyCustomLevel");
LevelUP.Configuration.RegisterNewLevelTypeEXP("MyCustomLevel", (exp) =>
{
    Console.WriteLine("My custom exp levels calculation");
    return 0;
});
LevelUP.Configuration.RegisterNewEXPLevelType("MyCustomLevel", (level) =>
{
    Console.WriteLine("My custom function to get currently exp from level");
    return 0;
});
LevelUP.Configuration.RegisterNewMaxLevelByLevelTypeEXP("MyCustomLevel", 999);
foreach (string playerClass in LevelUP.Configuration.ClassExperience.Keys)
    LevelUP.Configuration.RegisterNewClassLevel(playerClass, "MyCustomLevel", 1.0f);

Now you can use the function LevelUP.Server.Experience.IncreaseExperience(player, "MyCustomLevel", amount) to increase player experience

Examples

  • Force reduce all experience
public override void StartServerSide(ICoreServerAPI api)
{
    LevelUP.Server.ExperienceEvents.OnExperienceIncrease += LevelUPOnExperienceIncreased;
}

private void LevelUPOnExperienceIncreased(IPlayer player, string type, ref ulong amount)
{
    amount = (ulong)Math.Round(amount * 0.5);
}
  • Notification when player level up
public override void StartServerSide(ICoreServerAPI api)
{
    LevelUP.Server.ExperienceEvents.OnLevelUp += LevelUPOnLevelUp;
}

private void LevelUPOnLevelUp(IPlayer player, string type, ulong exp, int level)
{
    if (player is IServerPlayer serverPlayer)
    {
        serverPlayer.SendMessage(0, "Congratulations another levelup notification", EnumChatType.Notification);
    }
}
  • Shenanigans cannot do melee damage
public override void StartServerSide(ICoreServerAPI api)
{
    LevelUP.Server.OverwriteDamageInteractionEvents.OnPlayerMeleeDoDamageFinish += LevelUPOnPlayerMeleeDoDamageFinish
}

private void LevelUPOnPlayerMeleeDoDamageFinish(IPlayer player, DamageSource damageSource, ref float damage)
{
    if (player.PlayerName == "Shenanigans")
    {
        damage = 0;
    }
}
  • Disable axe levels when a player reach 1000 blocks
private static ICoreServerAPI serverAPI;
public override void StartServerSide(ICoreServerAPI api)
{
    serverAPI = api
    LevelUP.Server.ExperienceEvents.OnExperienceIncrease += LevelUPOnExperienceIncreased;
}

private void LevelUPOnExperienceIncreased(IPlayer player, string type, ref ulong amount)
{
    var posX = player.Entity.Pos.X - serverAPI.World.DefaultSpawnPosition.X;
    var posZ = player.Entity.Pos.X - serverAPI.World.DefaultSpawnPosition.Z;

    if (posX > 1000)
        LevelUP.Configuration.enableLevelAxe = false;
    if (posZ > 1000)
        LevelUP.Configuration.enableLevelAxe = false;
}
  • Custom Level Create any icon in your project assets/levelup/textures/levelname.png, if icon is not provided will not be show in level view
public static int GlobalGetLevelByEXP(ulong exp)
{
    double baseExp = 100.0;
    double multiplier = 0.1;

    if (multiplier <= 1.0)
    {
        return (int)(exp / baseExp);
    }

    double expDouble = exp;

    double level = Math.Log((expDouble * (multiplier - 1) / baseExp) + 1) / Math.Log(multiplier);

    return Math.Max(0, (int)Math.Floor(level));
}

public override void StartServerSide(ICoreServerAPI api)
{

    foreach (string playerClass in LevelUP.Configuration.ClassExperience.Keys)
        LevelUP.Configuration.RegisterNewClassLevel(playerClass, "Global", 1.0f);
    LevelUP.Configuration.RegisterNewLevel("Global");
    LevelUP.Configuration.RegisterNewLevelTypeEXP("Global", GlobalGetLevelByEXP);
    LevelUP.Configuration.RegisterNewMaxLevelByLevelTypeEXP("Global", 999);
    LevelUP.Server.ExperienceEvents.OnExperienceIncrease += LevelUPOnPlayerExperienceIncrease;
}

private void LevelUPOnPlayerExperienceIncrease(IPlayer player, string type, ref ulong amount)
{
    if (type != "Global")
    {
        // Increase global levels
        LevelUP.Server.Experience.IncreaseExperience(player, "Global", amount);
    }
}
  • Custom Armor Type The best example you can get is literally on the mod codes LevelLeatherArmor.cs, all you need is inside it, you can copy all the code change the name to the type you want. of course you need to call Patch() and Init() functions in serverside and change the configurations. (Instance.api is ICoreServerAPI)

  • Custom Weapon Type The same way as the Custom Armor Type you can simple get any example in LevelAxe.cs