# Upgrade Guide

## General Instructions

Key notes on upgrading existing mod for the CET 1.14+:

* Don't use `Game` object and any game types before `onInit`.
* Don't use `GetMod()` before `onInit`.
* Don't pass excessive params to game functions. If the max number of params is 3, then passing 4 params will result in an error.
* Update `Observe()` / `Override()` handlers so they accept `self` as the first param. Previously, some observers lacked the `self` param:

```lua
Observe("PlayerPuppet", "OnAction", function(self, action)
    -- First param self is required now
end)
```

* If your mod stores player reference then unset it when game session ends.&#x20;
  * An unreleased reference can lead to bugs such as disappearing footsteps.
  * Accessing `Game.GetPlayer()` in `onUpdate` will not cause the issue.
  * This can be achieved using next snippet or with `GameSession` / `GameUI` libs:

```lua
Observe("QuestTrackerGameController", "OnUninitialize", function()
    if Game.GetPlayer() == nil then
        Mod.Player = nil -- var containing a reference to the player 
    end
end)
```

* Use corresponding getters for game systems instead of `GetSingleton()`. For example, `Game.GetTeleportationFacility()` instead of  `GetSingleton('gameTeleportationFacility')`.

## New Script API

New scripting capabilities were introduced in CET 1.14 to make it more convenient and simple:

* All game classes are directly accessible by name. For example, `entEntityId`, `PlayerPuppet`.
* All game enums are directly accessible by name. For example, `gamedataStatType.BaseDamage`, `gameGameVersion.Current`.
* Classes can also be accessed by their aliases from redscript. For example, `WeaponObject` instead of `gameweaponObject`.
* Classes have the conventional `.new()` constructor. For example, `MappinData.new()`.
* A constructor can take an array of properties to create and initialize an object in a single statement. For example, `EntityID.new({ hash = 12345 })`.
* Static methods are accessible from classes using the dot. For example, `ScriptedPuppet.IsDefeated(npc)`.
* A static method can be called from an instance if the first parameter is of the same type. For example, `vec4:Length()` instead of `Vector4.Length(vec4)`.
* The overloaded function is resolved based on passed parameters when called by its short name. For example, `StatusEffectHelper.HasStatusEffect(target, gamedataStatusEffectType.Overheat)`.
* Partial `Variant` type support. `ToVariant()` and `FromVariant()` are only applicable to classes.

### Backward Compatibility

All new scripting features are optional. You're not required to rewrite an existing mod using these features.

### Redscript Similarity

The new API allows code that is also valid redscript or very close to its redscript counterpart. Thus, it simplifies the research and the transition to / from the redscript.

For example, this line looks exactly the same in Lua and in the redscript:

```lua
RPGManager.CreateStatModifier(gamedataStatType.BaseDamage, gameStatModifierType.Additive, 50)
```

### Examples

**Constructing objects**

Old API:

```lua
local mappinData = NewObject("gamemappinsMappinData")
mappinData.mappinType = TweakDBID.new("Mappins.DefaultStaticMappin")
mappinData.variant = Enum.new("gamedataMappinVariant", "FastTravelVariant")
mappinData.visibleThroughWalls = true
```

New API:

```lua
local mappinData = MappinData.new()
mappinData.mappinType = "Mappins.DefaultStaticMappin"
mappinData.variant = gamedataMappinVariant.FastTravelVariant
mappinData.visibleThroughWalls = true
```

**Constructor with initialization**

Old API:

```lua
function getStash()
    local stashId = NewObject("entEntityID")
    stashId.hash = 16570246047455160070ULL

    return Game.FindEntityByID(stashId)
end
```

New API:

```lua
function getStash()
    return Game.FindEntityByID(EntityID.new({ hash = 16570246047455160070ULL }))
end
```

**Scripted static call**

Old API:

```lua
Game["PreventionSystem::ShowMessage;GameInstanceStringFloat"]("Message", 5.0)
```

New API:

```lua
PreventionSystem.ShowMessage("Message", 5.0)
```

**Shorthand static call**

Old API:

```lua
Observe("PlayerPuppet", "OnAction", function(action)
    -- Option 1 --
    print(GetSingleton("gameinputScriptListenerAction"):GetName(action))
    -- Option 2 --
    print(action:GetName(action))   
end)
```

New API:

```lua
Observe("PlayerPuppet", "OnAction", function(action)
    -- Option 1 --
    print(ListenerAction.GetName(action))
    -- Option 2 --
    print(action:GetName())   
end)
```

**Working with enums**

Old API:

```lua
print(Enum.new('gameGameVersion', 'Current') == Enum.new('gameGameVersion', 'CP77_GoldMaster'))
print(Enum.new('gameGameVersion', 'Current') == Enum.new('gameGameVersion', 'CP77_Patch_1_2_Hotfix2'))
```

New API:

```lua
print(gameGameVersion.Current == gameGameVersion.CP77_GoldMaster)
print(gameGameVersion.Current == gameGameVersion.CP77_Patch_1_2_Hotfix2)
```

**Variants**

```lua
local message = SimpleScreenMessage.new()
message.message = "Test"
message.isShown = true

local blackboardDefs = Game.GetAllBlackboardDefs()
local blackboardUI = Game.GetBlackboardSystem():Get(blackboardDefs.UI_Notifications)
blackboardUI:SetVariant(
    blackboardDefs.UI_Notifications.OnscreenMessage, 
    ToVariant(message), 
    true
)
```
