Annotations

Annotations are special keywords that you can use to change or extend the behavior of existing methods and classes in the base game. You cannot use annotations to modify modded methods or classes.

You cannot use the following annotations on native functions.

You can now generate declaration for annotations using NativeDB. You need to configure option Clipboard syntax to Redscript. You can click on the "copy" button of a function, pick a feature and it will copy the code in your clipboard.

@wrapMethod(class)

You can use this annotation to write a new function that supersedes an existing method with the same name and parameters on a specified class. Your function will execute instead of the original method whenever it is invoked, but you can still access the underlying method by using the wrappedMethod(…) identifier. You would typically invoke the wrapped method in your newly defined function and forward all the arguments unchanged to preserve it's original behavior, but you can also modify the arguments or the final return value if you wish. Furthermore, this annotation can be applied multiple times to chain multiple functions that modify the same method. Each function in the chain wraps around the previous one. This helps to keep mods compatible even if they alter the same methods. For example, multiple mods can use this annotation to append new buttons to the main menu:

@wrapMethod(SingleplayerMenuGameController)
private func PopulateMenuItemList() {
  wrappedMethod(); // This will call the original PopulateMenuItemList
  this.AddMenuItem("BUTTON 1", n"OnDebug");
}

// You can use this annotation to chain multiple functions
@wrapMethod(SingleplayerMenuGameController)
private func PopulateMenuItemList() {
  wrappedMethod(); // This will run the previous function with this annotation
  this.AddMenuItem("BUTTON 2", n"OnDebug");
}

// The result will be two new buttons added to the main menu

When you use wrappedMethod(…) to call the original method, make sure to match the arguments for the correct overload of the method.

@wrapMethod(SingleplayerMenuGameController)
private func PopulateMenuItemList() {
  let menuItemCount: Int32 = 5;
  // this will pass menuItemCount as an argument to the wrong overload of PopulateMenuItemList
  wrappedMethod(menuItemCount);
}

You should usually call wrappedMethod(…) in your wrapper function. However, you can use a conditional statement to decide when to call it if you don’t need it in some situations.

@wrapMethod(SingleplayerMenuGameController)
private func PopulateMenuItemList() {
  let menuItemCount: Int32 = 5;
  if menuItemCount > 5 {
    wrappedMethod(menuItemCount); // wrappedMethod(…) will not run because menuItemCount is not greater than 5
  };
}

@addMethod(class)

You can use this annotation to create a new method and add it to an existing class. For example, you can write a new method to disassemble all junk items in your backpack and add it to the BackpackMainGameController class:

@addMethod(BackpackMainGameController)
private final func DisassembleAllJunkItems() {
    ...
}

@replaceMethod(class)

You can use this annotation to write a new function that has the same name and parameters as an existing method in the game. Your function will replace the original method and change its behavior.

@replaceMethod(CraftingSystem)
private final func ProcessCraftSkill(xpAmount: Int32, craftedItem: StatsObjectID) {
    ...
}

You can only use one @replaceMethod for each method. If you define more than one, only one of them will work. If you want to combine multiple changes to the same method, you should use the @wrapMethod annotation instead.

This annotation is compatible with @wrapMethod. It will always overwrite the original method and it will not affect the functions that wrap around it.

@replaceGlobal()

You can use this annotation to write a new function that has the same name and parameters as an existing global function in the game. Your function will replace the original function and change its behavior.

@replaceGlobal()
public static func CreateExpEvent(amount: Int32, type: gamedataProficiencyType) -> ref<ExperiencePointsEvent> {
    ...
}

@addField(class)

You can use this annotation to create a new field and add it to an existing class. For example, you can add a dummy field of type Int32 to the PlayerPuppet class:

@addField(PlayerPuppet)
let dummy: Int32;

Last updated