arrow-left

Only this pageAll pages
gitbookPowered by GitBook
1 of 72

Cyber Engine Tweaks

Loading...

Getting Started

Loading...

Loading...

Loading...

Loading...

Loading...

Overlay

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Modding

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Dictionary

Loading...

Loading...

Loading...

Loading...

Other

Loading...

Loading...

External docs

Other Wikis

First Steps

Explaining how to create and run a custom mod

Mod Structurechevron-rightLogs & Debugchevron-rightGood Practiceschevron-right

CET Functions

An overview of CET-Exclusive functions

Eventschevron-rightHotkeyschevron-rightMiscellaneouschevron-rightObserve, Override, NewProxychevron-rightSpecial Typeschevron-rightTweakDBchevron-right

onTweak

This event gets triggered even before onInit, and can be useful for making changes to the TweakDB, as some values can only be changes very early during the loading process.

Miscellaneous

Miscellaneous functions useful for debugging and getting special objects and info.

Debug functionschevron-rightGetters functionschevron-right

Movement / Camera

Movement and camera-related functions

Helpful GUI Mods

A list of mods that you may find helpful for making an ImGui window

CET Overlay Window Builder Tool: https://www.nexusmods.com/cyberpunk2077/mods/12970arrow-up-right

FellowImGui (online tool): https://fellowimgui.devarrow-up-right

onShutdown

Opposing to onInit, this event gets triggered whenever CET unloads all the mods, either due to the game shutting down, or when pressing the "Reload all mods" button in the CET Overlay.

Use this to do any clean-up work for your mod, e.g. despawning objects or removing status effects.

hashtag
Usage Example

init.lua
registerForEvent('onShutdown', function()

    -- cleanup code
    
end)

Installation

Guide to installing the Cyber Engine Tweaks into the game

hashtag
Summary

This page tells you how to install Cyber Engine Tweaks - or you can check the 📺 Video Guide.

hashtag
🔒 Prerequisites

  1. Make sure Cyberpunk 2077 is updated to the latest version

  2. Make sure you installed the latest

  3. Make sure you run the game in Windowed Borderless

hashtag
📋 Installation

  1. Download the latest file. (Do not download the source code)

    • If using Windows, you should right click on the zip file, and check under Properties for a security notice saying "This file came from another computer and might be blocked to help protect this computer" - if that security notice is present, click the checkbox to unblock the contents of the zip file.

hashtag
🎮 Launch the Game

When you first start the game, a window will appear prompting you to choose a keybind to bring up the . Pick any key and save it. It will open with that key from now on.

  • (Optional) To check that everything works you can look at the logs in <cyberpunk install path>/bin/x64/plugins/cyber_engine_tweaks/cyber_engine_tweaks.log

  • (Optional) When installing through Nexus Vortex, make sure to use hardlink deployment.

hashtag
📺 Video Guide

hashtag
What's next?

Game Log

How to use the Cyber Engine Tweaks Game Log

hashtag
Game Log UI

  • This window shows any output done using Redscript's Log/LogChannel function, either coming from the games scripts themselves or from a Redscript mod.

hashtag
Writing Logs

Observe, Override, NewProxy

hashtag
Introduction

Cyber Engine Tweaks allow mods to listen or overwrite game's script functions/methods. There are three kinds of functions:

  • Observe allows to listen for function/method execution

  • allows to overwrite a function/method

  • allows to trigger a function/method callback using game's script system

hashtag
Available Functions

Welcome

Welcome to Cyber Engine Tweaks official Wiki!

Cyber Engine Tweaks is a framework that allows modders to interact with the game's internal functions using . It also provides a user-interface with a console and additional tools to assist developers.

hashtag
GETTING STARTED

For information on how to install Cyber Engine Tweaks, check out the link below.

Uninstall

How to remove Cyber Engine Tweaks

Remove the following folders/files:

  • <cyberpunk install path>

Usage

How to use the Cyber Engine Tweaks Overlay

hashtag
What is the Overlay?

The Cyber Engine Tweaks Overlay is the interface that is used to display and set various settings, and configure mods hotkeys.

Additionally, the most advanced mods will also display their own settings window in this overlay. It contains different tabs:

Console

How to use the Cyber Engine Tweaks Console

hashtag
Console UI

The console tab is where you type commands to use cheats and other useful functions.

FAQ

Frequently asked questions about Cyber Engine Tweaks based mods.

circle-info

hashtag
Clarification:

Bindings

How to use Cyber Engine Tweaks Bindings

hashtag
Hotkeys

The Hotkeys tab is where you manage all the hotkeys of the mods you installed.

circle-info

Settings

How to use Cyber Engine Tweaks Settings

hashtag
Settings UI

The Settings tab is where you change the Overlay open/close hotkey, and toggle the tweaks to Cyberpunk2077's game engine.

circle-info

TweakDB Editor

How to modify the TweakDB via CET

hashtag
Summary

This page documents CET's in-game Tweak Editor.

hashtag

Introduction

An overview of how to use CET to create custom mods

hashtag
Preface

In the early days of Cyberpunk 2077 modding, we noticed a lack of discipline and proper use of the SDK provided by CET. So we strongly recommend you read the documentation and understand what is expected of you and your mods.

Events

Cyber Engine Tweaks has builtin events which can be listened using the registerForEvent() function. These listeners must be registered inside the init.lua file.

Keep in mind that CET events are not game events. For example, doesn't mean the game the initializing, but rather that CET loaded mods and the game's Scripting API.

If you need to listen for game events, take a look at the functions.

onInit

On onInit gets triggered once, when CET loads in all the mods. This event ensures that CET has fully loaded, and you have access to the Scripting API. There can be only one onInit event per mod.

Use it to register any , as well as do any one-off actions needed for your mod.

circle-check

This event is a safe starting point for your code interaction with the game, as it guarantees the access to most of the Scripting API.

onUpdate

This event gets triggered on every frame that the game runs, thus is framerate dependent. The callback function gets the deltaTime passed along with it, which measures the frametime in ms, useful for making e.g. timers.

In here you can put any code that you want to run every frame, although it is best practice to only run your code when needed, e.g. using .

circle-exclamation

Use this event with caution, as it is triggered continuously once the game is launched.

RegisterHotkey

Hotkeys are buttons events triggered on key release. They must be registered using registerHotkey() at root level, outside of any event, in the init.lua file.

circle-exclamation

are not triggered while the player stay pressed on a game's keybind.

CET and Codeware

How to make the most of psiberx's script expansion framework

hashtag

hashtag
Documentation

stringType

Description of string type to use in functions

hashtag
Reflexes

hashtag
Strength

Utilities

A collection of LUA scripting utilities to make your life easier

Snippets & Examples

Ready-to-paste CET code

circle-info

You can also find example mods

Game Functions

Overview of the different functions available to use on the console.

hashtag
Special Types

Helper functions which let you construct internal data types.

hashtag

/bin/x64/plugins/
  • <cyberpunk install path>/bin/x64/global.ini

  • <cyberpunk install path>/bin/x64/LICENSE

  • <cyberpunk install path>/bin/x64/version.dll

  • hashtag
    Notes

    • Removing Cyber Engine Tweaks will not undo any edits made to your character and your save files.

    • If you have added items to your inventory, you will not be able to remove them once you have removed this mod. Please ensure you have done so using the appropriate commands first.

    • Saves made with the patch should be completely fine without it, thought that might change once proper mods start coming out

    "mod" or "mods" on this page refers to mods that are dependent on Cyber Engine Tweaks.
  • Cyber Engine Tweaks will be referred to as CET from now on.

  • hashtag
    I've installed this mod but nothing happens.

    1. Make sure that CET works fine without any mods installed.

    2. Make sure the mod that you are installing is compatible with the latest CET.

    hashtag
    I've made sure the above are true but the mod still won't function.

    You need to bind a hotkey in the Hotkeys tab of the CET overlay. Check how-to here.

    hashtag
    I've bound the hotkeys but the hotkeys do nothing when I press it.

    The hotkeys only work when the CET overlay is closed.

    hashtag
    I've bound the hotkey and I've closed the CET overlay but still, nothing happens.

    You need to press the hotkey combination in the right order. For example Ctrl + Shift + S is different from Shift + Ctrl + S

    hashtag
    I bound the hotkeys and the interface opens but I can't do anything there. There's no cursor in the window.

    You need to open the CET overlay to interact with anything using your mouse.

    hashtag
    This mod supports different languages, but when I change the language every character becomes a ? (question mark).

    You need to change the default font of CET in order for the characters to show properly. You can follow this guide here.

    hashtag
    How do I open .7z .rar .zip files?

    Click here arrow-up-rightfor the ultimate guide.

    hashtag
    Cool

    hashtag
    Intelligence

    hashtag
    TechnicalAbility

    hashtag
    ColdBlood

    hashtag
    Hacking

    hashtag
    Stealth

    hashtag
    Gunslinger

    hashtag
    Kenjutsu

    hashtag
    Assault

    hashtag
    Crafting

    hashtag
    Engineering

    hashtag
    Hacking

    hashtag
    CombatHacking

    hashtag
    Athletics

    hashtag
    Demolition

    hashtag
    Brawling

    hashtag
    StreetCred

    hashtag
    Level

    Override
    NewProxy
    Observechevron-right
    Overridechevron-right
    Debugchevron-right
    Fileschevron-right
    Hookschevron-right
    Tableschevron-right
    in the wiki's github repositoryarrow-up-right
    UI Exampleschevron-right
    Utilitieschevron-right
    hashtag
    Console Buttons

    hashtag
    What the buttons and checkboxes do in the Console tab:

    BUTTON
    DESCRIPTION

    Clear Output

    Clear the output log displayed in the console.

    Auto-Scroll

    Will automatically scroll to the bottom when there is a new output log.

    Reload All Mods

    Reload all installed mods.

    hashtag
    Console Commands

    The console uses Luaarrow-up-right script language, you may want to look up how to use it before proceeding. There are some guides here to get you started:

    Useful commandschevron-right
    🎯First Stepschevron-right
    Note: The "Skip Start Menu", "Suppress Intro Movies", and "Fix MiniMap Flicker" mods shown in this image have been deprecated. See the following comment for more information: https://discord.com/channels/717692382849663036/795037292955172895/1332405506769158236arrow-up-right

    hashtag
    Generated Config file

    See the following section for more information about the configuration file generated using this UI.

    Config filechevron-right
    hashtag
    Getting Started
    • Before you start, it is highly recommended to check out the Modding Resources page

    • If you are using VSCodearrow-up-right, make sure to also pick up the CET VSCode extension

    • Next, it is recommended to read through the Scripts and Mod Structure page, which gives an overview on how to create a mod / run a single script. It also covers the basic elements of a mod

    • After that, you might want to check out the Scripting API page, which explains how you interact with the games systems and code.

    • Once you've made yourself familiar with how a general mod is structured and how to interact with the game, you might want to read up on the different functions offered by CET itself, such as Events, Keybinds, Overrides and more.

    • Check out the Examples section for a collection of small example mods and useful code snippets

    💎Resourceschevron-right
    VS Codechevron-right
    🎯First Stepschevron-right
    📗Scripting APIchevron-right
    📘CET Functionschevron-right
    ✏️Snippets & Exampleschevron-right
    TweakDB

    Methods for interacting with TweakDB, the game's internal data store.

    hashtag
    Misc

    Miscellaneous functions useful for debugging (print to console, dump type information) and getting special objects and info.

    Special Typeschevron-right
    TweakDBchevron-right
    Miscellaneouschevron-right
    // my_script.reds
    LogChannel(n"DEBUG", "My Custom Log");
    circle-info

    This event is also triggered when using "Reload all mods" button in the CET Overlay, as it fires when CET load all mods.

    hashtag
    Usage Example

    Observers
    init.lua
    registerForEvent('onInit', function()
    
        print('Game is loaded')
        
    end)
    
    hashtag
    Usage Example

    Observers
    init.lua
    registerForEvent('onUpdate', function(deltaTime)
    
        print('It has been ' .. deltaTime .. ' ms since the last call')
        
    end)
    
    For example, if the player move forward with "W" and press a Hotkey at the same time, it won't be triggered.

    For this reason, it is recommended to use Inputs instead, as they are always triggered.

    hashtag
    Definition

    hashtag
    Usage Example

    hashtag
    Give money with a hotkey:

    Hotkeys
    registerHotkey(slug, label, callback)
    circle-info

    This page is a stub. If you would like to expand it, please sign uparrow-up-right and get cracking!!

    You can find Codeware's existing documentation at the wiki on psiberx's githubarrow-up-right

    hashtag
    Known problems (and solutions)

    hashtag
    Accessing references

    As of Mar 2024, you can't access rRef or raRef natively via CET:

    This will most likely return nil or crash the game.

    Even if it does not, no reference is kept in memory: you're effectively working on a copy and there is currently no way to pass your changes back to the game.

    Currently, the only way to access resources is via Codewarearrow-up-right, which will do its best to sync your changes with the actually-existing object.

    --
    -- registerHotkey()
    --
    -- @param  string    slug      The internal slug (must be unique in your mod scope)
    -- @param  string    label     The label displayed in CET Bindings
    -- @param  function  callback  The callback function
    --
    registerHotkey('slug', 'label', function()
        
        -- hotkey is released
        
    end)
    init.lua
    registerHotkey('give_money', 'Give Money', function()
        
        Game.AddToInventory('Items.money', 1000)
        
    end)
    -- this does not work
    ComponentParser.GetMeshComponent(Game.GetPlayer(), 't0_000_pwa_fpp__torso')
    Ensure all apps overlays are disabled as the console may not open: Steam, Discord, GOG, Geforce Experience, Windows 10 Game Bar, Rivatuner, Fraps, Afterburner etc...
    You can now either
    • Unzip the download directly into your Cyberpunk 2077 game directory or

    • Unzip to anywhere on your desktop. After unzipping the file, you will see a bin folder.

      • Drag the bin folder to your Cyberpunk 2077 game directory

  • If installed properly, the Cyber Engine Tweaks files will be in the <cyberpunk install path>/bin/x64 folder. As follow:

  • Microsoft Visual C++ Redistributablearrow-up-right
    cet_x_xx_x.ziparrow-up-right
    Overlay
    🚨Troubleshootingchevron-right
    🐧Linux (Proton)chevron-right
    ❔How do I _____?chevron-right

    hashtag
    USAGE

    Learn how to use each Cyber Engine Tweaks tabs.

    hashtag
    MODDING

    Everything you need to know to create your own mod for Cyber Engine Tweaks.

    hashtag
    CONTRIBUTING

    If you wish to contribute to the main repo, try to follow the coding style in the code, otherwise not much to say, don't use code that is not yours unless the license is compatible with MIT.

    Luaarrow-up-right
    💿Installationchevron-right
    ❔How do I _____?chevron-right

    Console

  • Bindings

  • Settings

  • TweakDB

  • Game Log

  • hashtag
    How to open the Overlay?

    Upon the installation of CET, the overlay will appear automatically when you launch the game. You need to assign a hotkey to it before you can move on:

    1. Press the Hotkey button next to the Overlay Key.

    2. Press a key or a key combination on your keyboard after the button becomes BINDING... to bind it.

    3. Now the hotkey has been bound, you can press that key anytime to open/close the Overlay.

    Asign a key for opening/closing the Overlay
    CET does not allow mods to define default hotkeys. Thus, every time you install a new mod, you need to bind a hotkey for it, if it requires one.
    • Binding hotkeys for a mod is the same as how you bind the hotkey for the CET Overlay.

    • You can unbind a hotkey by pressing the UNBIND button behind it.

    • To save the changes, press the Save button.

    • Undefined hotkeys appear in red text. Unsaved changes appear in yellow text.

    Bind/unbind hotkeys for mods

    hashtag
    If you have encountered troubles when using a mod, please read this FAQ page:

    FAQchevron-right
    Wait, this is not what I want!
    • To learn what the TweakDB is, read the page on

    • For an introduction on TweakDB modding with CET, check the Simple Gameplay Modarrow-up-right guide

    • For further information, refer to the TweakDB section on this wiki

    hashtag
    What is the TweakDB editor?

    After opening CET with the key that you bound during the installation, you can find it in the "TweakDB Editor" tab. Here you can browse and update the TweakDatabase in real time.

    circle-exclamation

    Note that changes you made in the editor do not persist across game restarts.

    hashtag
    Definition

    hashtag
    Events Trigger Sequence

    Here is a video showing when events are triggered in real-time:

    hashtag
    Available Events

    onInit
    Observers
    onInitchevron-right
    onUpdatechevron-right
    onDrawchevron-right
    onOverlayOpenchevron-right
    onOverlayClosechevron-right
    onShutdownchevron-right
    onTweakchevron-right

    How do I _____?

    How to do things with CET

    hashtag
    Install CET Mods?

    You can find a very detailed step-by-step guide herearrow-up-right.

    circle-info

    You can find additional guides on how to install mods in the users section of the Cyberpunk 2077 modding wiki, including:

    • A for first-time users

    hashtag
    ... add perk points, level up, add money...?

    For a list of console commands for beginners, check out the page:

    hashtag
    ... create my own mod?

    circle-info

    You can find the api documentation under .

    Checkout the page for your first steps:

    hashtag
    ... do $thing in code?

    Check or , for example

    Logs & Debug

    Explaining where to find logs file and write them

    hashtag
    Individual Mod Logs

    On game launch, Cyber Engine Tweaks generates individual logs files in each mod folder, using their name. eg, <cet_path>/mods/my_mod/my_mod.log.

    hashtag
    What it contains

    This file contains all notices, errors and logs generated by the mod.

    hashtag
    Write the file

    You can write the log file using the functions.

    hashtag
    Watch the file

    You can use to watch files in live with a GUI, or use the following command line in the Windows Command shell:

    hashtag
    CET Console Logs

    Cyber Engine Tweaks has a global log file located in <cet_path>/scripting.log.

    hashtag
    What it contains

    This file contains all logs from the .

    hashtag
    Write the file

    You can write the log file by using the function.

    hashtag
    Watch the file

    You can use to watch files in live with a GUI, or use the following command line in the Windows Command shell:

    Resources

    A collection of helpful links and resources

    • Decompiled game scriptsarrow-up-right: These are the game's scripts decompiled to redscript. Extremely useful for understanding how functions work under the hood, game systems and classes interact with each other and what classes and functions are available. For more information on how to transpile redscript code to .lua code, read up on the Scripting API. Works best if downloaded and opened in some IDE like VSCode that lets you search across all files at once.

    • cp2077-cet-kitarrow-up-right: psiberx's github repository with helper functions and scripts

    • : This is a database containing all the classes and their functions, their relation to each other, their functions, attributes and all the Enum's. Very useful for quickly looking up what functions a class offers and what parameters it takes. Also helpful for understanding which classes inherit from which. Nativedb has more types than decompiled scripts.

    • : A utility that helps you translating your CET mods, by lgomes_gvizstudio on github

    • : A collection of utility scripts, for tracking game states (In Game etc.), setting up timers and timed actions, storing data per game save and more.

    • : A collection of small example mods, such as controlling NPC AI, working with map pins, reading player inputs and more.

    • : Visual overview of the game's classes and structure. Very helpful for understanding the higher level structure.

    • : Discord server with multiple CET related channels, lots of more resources and snippets (Discord search is your best friend)

    • : An extensive tutorial covering how to work with the TweakDB, create a CET mod, override and observe functions and use redscript.

    • : An online sandbox to familiarize yourself with LUA and test some code without having to launch Cyberpunk with CET. Note that any CET/Game functions won't work in it.

    onDraw

    This event works similarly to onUpdate, except that it is used for drawing custom ImGui UI. It gets triggered on every frame that the game runs, thus is framerate dependent.

    circle-exclamation

    Use this event with caution, as it is triggered continuously once the game is launched.

    triangle-exclamation

    Trying to draw ImGui UI outside this callback is prohibited

    hashtag
    Usage Example

    hashtag
    Render an ImGui window at anytime:

    hashtag
    Advanced Example

    hashtag
    Render an ImGui window when the player is sprinting:

    circle-info

    This example use . Check the for further information on its usage.

    onOverlayOpen

    This event get triggered when the CET Overlay gets shown.

    Use this to keep track of the overlays state, and e.g. to only draw your own UI when CETs overlay is visible. Use it in conjunction with onOverlayClose to get a proper on/off switch case.

    hashtag
    Usage Example

    hashtag
    Display a warning message when the is opened:

    hashtag
    Advanced Example

    hashtag
    Render an ImGui window when the is opened:

    circle-info

    This example use the event, which is triggered continuously. Make sure to before any modification.

    onOverlayClose

    This event get triggered when the CET Overlay gets hidden.

    Use this to keep track of the overlays state, and e.g. to only draw your own UI when CETs overlay is visible. Use it in conjunction with onOverlayOpen to get a proper on/off switch case.

    hashtag
    Usage Example

    hashtag
    Display a warning message when the is closed:

    hashtag
    Advanced Example

    hashtag
    Render an ImGui window when the is opened:

    circle-info

    This example use the event, which is triggered continuously. Make sure to before any modification.

    Async / waiting

    Do stuff later without blocking

    hashtag
    Summary

    Published: Feb 18 2024 by Last documented edit: Feb 18 2024 by

    hashtag

    Cyberpunk 2077/
    ├─ bin/
    │  ├─ x64/
    │  │  ├─ global.ini
    │  │  ├─ LICENSE
    │  │  ├─ version.dll
    │  │  ├─ plugins/
    │  │  │  ├─ cyber_engine_tweaks.asi
    │  │  │  ├─ cyber_engine_tweaks/
    │  │  │  │  ├─ fonts/
    │  │  │  │  ├─ scripts/
    │  │  │  │  ├─ tweakdb/
    │  │  │  │  ├─ ThirdParty_LICENSES
    registerForEvent(event, callback)
    --
    -- registerForEvent()
    --
    -- @param  string    event     The event name
    -- @param  function  callback  The callback function
    --
    registerForEvent('event', function()
        
        -- event is triggered
        
    end)
    NativeDBarrow-up-right
    CET translation helperarrow-up-right
    CET Kitarrow-up-right
    Official CET Examplesarrow-up-right
    Cyberpunk 2077 Explorerarrow-up-right
    Cyberpunk 2077 Modding Communityarrow-up-right
    Simple Gameplay Mod Tutorialarrow-up-right
    LUA code playgroundarrow-up-right
    spdlog
    Tailblazer Apparrow-up-right
    CET Console
    print()
    Tailblazer Apparrow-up-right
    observers
    documentation
    CET Overlay
    CET Overlay
    onDraw
    check the documentation
    CET Overlay
    CET Overlay
    onDraw
    check the documentation
    ../
    ├─ cyber_engine_tweaks/
    │  ├─ mods/
    │  │  ├─ my_mod/
    │  │  │  ├─ init.lua
    │  │  │  ├─ my_mod.log <- automatically generated
    <cmd>
    tail -f "C:/path/to/Cyberpunk 2077/bin/x64/plugins/cyber_engine_tweaks/mods/my_mod/my_mod.log"
    ../
    ├─ cyber_engine_tweaks/
    │  ├─ scripting.log
    <cmd>
    tail -f "C:/path/to/Cyberpunk 2077/bin/x64/plugins/cyber_engine_tweaks/scripting.log"
    init.lua
    registerForEvent('onDraw', function()
    
        if ImGui.Begin('Window Title', ImGuiWindowFlags.AlwaysAutoResize) then
            ImGui.Text('Hello World!')
        end
        
        ImGui.End()
        
    end)
    init.lua
    -- set initial switch
    isSprinting = false
    
    -- onInit
    registerForEvent('onInit', function()
        
        -- observe Sprint OnEnter state
        Observe('SprintEvents', 'OnEnter', function(stateContext, scriptInterface)
            isSprinting = true
        end)
        
        -- observe Sprint OnExit state
        Observe('SprintEvents', 'OnExit', function(stateContext, scriptInterface)
            isSprinting = false -- reset switch
        end)
    
    end)
    
    -- onDraw
    registerForEvent('onDraw', function()
        
        -- bail early if not sprinting
        if not isSprinting then
            return
        end
        
        -- draw window
        if ImGui.Begin('Notification', ImGuiWindowFlags.AlwaysAutoResize) then
            ImGui.Text('Nice sprint!')
        end
        
        ImGui.End()
        
    end)
    init.lua
    -- onOverlayOpen
    registerForEvent('onOverlayOpen', function()
        
        -- get player
        local player = Game.GetPlayer()
        
        -- bail early if player doesn't exists
        if not player then
            return
        end
        
        -- display warning message
        player:SetWarningMessage('Overlay is open')
    
    end)
    init.lua
    -- set initial var
    local isOverlayVisible = false
    
    -- onOverlayOpen
    registerForEvent('onOverlayOpen', function()
        isOverlayVisible = true
    end)
    
    -- onOverlayClose
    registerForEvent('onOverlayClose', function()
        isOverlayVisible = false
    end)
    
    -- onDraw
    -- this event is triggered continuously
    registerForEvent('onDraw', function()
        
        -- bail early if overlay is not open
        if not isOverlayVisible then
            return
        end
        
        -- draw ImGui window
        if ImGui.Begin('Window Title', ImGuiWindowFlags.AlwaysAutoResize) then
            ImGui.Text('Hello World!')
        end
        
        ImGui.End()
        
    end)
    init.lua
    -- onOverlayOpen
    registerForEvent('onOverlayOpen', function()
        
        -- get player
        local player = Game.GetPlayer()
        
        -- bail early if player doesn't exists
        if not player then
            return
        end
        
        -- display warning message
        player:SetWarningMessage('Overlay is closed')
    
    end)
    init.lua
    -- set initial var
    local isOverlayVisible = false
    
    -- onOverlayOpen
    registerForEvent('onOverlayOpen', function()
        isOverlayVisible = true
    end)
    
    -- onOverlayClose
    registerForEvent('onOverlayClose', function()
        isOverlayVisible = false
    end)
    
    -- onDraw
    -- this event is triggered continuously
    registerForEvent('onDraw', function()
        
        -- bail early if overlay is not open
        if not isOverlayVisible then
            return
        end
        
        -- draw ImGui window
        if ImGui.Begin('Window Title', ImGuiWindowFlags.AlwaysAutoResize) then
            ImGui.Text('Hello World!')
        end
        
        ImGui.End()
        
    end)
    Add Native Settingsarrow-up-right
    Useful Commands
    Useful commandschevron-right
    nativedb.red4ext.comarrow-up-right
    Modding Introduction
    📖Introductionchevron-right
    Snippets & Examples
    CET Functions
    Logs & Debug
    Hooks
    Observe, Override, NewProxy
    Where to get cron

    You can get Cron.luaarrow-up-right from psiberx's cp2077-cet-kitarrow-up-right on github. Add it to your sources, and then define it as a global:

    hashtag
    How to use cron

    Cron offers a bunch of functions for executing code later. The code you want to execute has to be inside a function (=> callback) and is passed among the arguments.

    For periodically running functions, you need to include logic when to stop the execution.

    hashtag
    Performance impact

    To save processor load, it is best practice to define your callback outside of the function call. This can have a noticable performance impact, so you're gonna want to do this.

    hashtag
    Good example:

    The callback is defined once.

    hashtag
    Bad example:

    The callback is defined every time cron runs.

    hashtag
    Passing function arguments

    To pass arguments to your callback function, you pass them as arguments to cron:

    hashtag
    Cron functions

    hashtag
    Delay

    Execute the callback after X seconds

    hashtag
    Timer

    Executes a function every X seconds.

    triangle-exclamation

    The function will run forever unless you stop it!

    hashtag
    Pausing and resuming a timer

    You can pause a timer by passing its reference to cron:

    🖥️

    Overlay

    🎹

    Console

    🖱️

    Bindings

    🛠️

    Settings

    🗄️

    TweakDB Editor

    ⌛

    Game Log

    📖

    Introduction

    🎯

    First Steps

    💎

    Resources

    📘

    CET Functions

    📒

    Game Functions

    ✏️

    Snippets & Examples

    Change font & language setting

    A guide on how to change the font and font size for Cyber Engine Tweaks and mods based on Cyber Engine Tweaks. And how to display non-English characters.

    Use a text editor to open the config file at <cet install path>/config.json to adjust the settings.

    In this page, we use this example JSON snippet to guide you through adjusting the settings:

    hashtag
    Change font

    Locate this line under the "font" curly brace:

    Add the path of the font you want to change to between the double-quotes. For example, if you want to change the font to Comic Sans, just change this line into:

    Note: You should use slash (/) instead of backslash (\) for the path.

    hashtag
    How to display non-English characters

    Some mods may have implemented multilingual support. But the non-English characters will display as a question mark "?". This is because the default font only contains .

    To display a language that only uses ASCII characters (such as French, German), all you need to do is to change the font to one with a wider range (Literally any font).

    To display a language that uses non-ASCII characters (such as Chinese, Russian), besides changing the font, you also need to change this line in the config file under the "font" curly brace:

    Here is a table of the "language" options and their descriptions.

    options
    Descriptions

    For example, to display Chinese all you need to do is:

    The result (using the Sim Hei / 黑体 font):

    VS Code

    hashtag
    Features

    • Code completion for built-in Cyber Engine Tweaks, Dear ImGui and Cyberpunk 2077 types and functions (except sqlite)

    • Type resolving for Game.GetScriptableSystemsContainer():Get("Type"), NewObject("Type"), and GetSingleton("Type")

    • Contextual suggestions for default values and predefined sets

    hashtag
    Installation

    hashtag
    CET typedef

    1. Download and extract the .

      • The lib files linked above is not the newest version.

      • You can join the and then visit to download the newest version (CET 1.27 / Patch 2.01 Typedefs).

    1. Add next settings to the .vscode\settings.json or in the Settings GUI:

      • NOTE: ensure commas occur after every line

    1. On first use it takes a couple of minutes to parse and cache all definitions. Unfortunately there is no visual indication of this process. When it's done the code assistance will be more responsive.

    hashtag
    Dear ImGui typedef

    To install the typedefs for Dear ImGui, you can manually download the files from GitHub repository and then follow the same installation process in to install them in VSCode.

    hashtag
    Annotations

    hashtag
    Callback parameters

    You can specify the type of the parameters with @param annotation. It's very handy for Observe and Override:

    hashtag
    Unresolved type

    If type of some variable cannot be resolved or ambiguous you can force the type with @type annotation:

    hashtag
    Generic functions

    The type of the result of some functions depends on the parameters passed to the function. If a valid type name is passed as a parameter, then the resulting type must be resolved without custom annotations.

    Getters functions

    hashtag
    GetDisplayResolution()

    Returns the width and height (respectively) of the game window in pixels.

    hashtag
    Definition

    hashtag
    Usage example

    hashtag
    GetMod()

    Returns the return value of the mod with the given name.

    circle-exclamation

    If you don't return anything in your init.lua file, this function will return nil. This will often look like return MyMod:new() at the end of your file. See the for info about how classes can be implemented in Lua.

    hashtag
    Definition

    hashtag
    Usage example

    hashtag
    GetSingleton()

    Returns the singleton for the given type name if it exists, otherwisenil.

    circle-info

    A singleton is the sole instance of a type, often used for global operations.

    hashtag
    Definition

    hashtag
    Usage example

    hashtag
    GetVersion()

    Returns the current CET build's commit hash.

    hashtag
    Definition

    hashtag
    Usage example

    hashtag
    ModArchiveExists()

    Returns whether the specified .archive file is present in the archive/pc/mod folder

    hashtag
    Definition

    hashtag
    Usage example

    hashtag
    Detect a Redscript mod

    This technique can be used to detect whether your Redscript mod is present, from CET side. It can be useful if you are writing a mod with both languages for some reason.

    You can declare a static method without a body. It will do nothing:

    You can now test whether this method is defined from CET side using this:

    You can call IsRedscriptDetected() and it will return true when your Redscript method is detected (meaning your Redscript mod is loaded) or false otherwise.

    circle-info

    Don't forget to change NameOfYourMod in both cases.

    If you're using redscript , and your IsDetected function is inside a module, the name will be mangled to include the full module name and a dot separating it:

    AdjustTransformWithDurations

    ⚠️ All examples in this Observe/Override method ShootEvents.OnEnter to gain access to stateContext.

    hashtag
    AdjustTransform

    Base class for AdjustTransformWithDurations. Seems to do nothing if passed to adjustTransform scriptable parameter. Do not use: your movement will be blocked indefinitely until you reset adjustTransform.

    hashtag
    AdjustTransformWithDurations

    Discovered by void*

    Request to change player trajectory (view angles) and/or player position.

    This code has to be executed in the context where you have access to StateContext for example:

    Function has 3 types of behavior: set trajectory, set position or set both.

    hashtag
    Changing player's trajectory to custom angles

    This code will smoothly point your camera towards {0, 0, 0}.

    chevron-rightHow it looks in-gamehashtag

    hashtag
    Targeting an GameObject for trajectory

    1. Does not change player's pitch.

    2. Seems to target "nearest" targeting component (aka hitbox).

    3. SetRotation is ignored if target is set.

    This function seem to use TargetingSystem to find the "best component" and then calls a LookAt.

    (Look at function and one)

    chevron-rightHow it looks in-gamehashtag

    hashtag
    Moving player

    chevron-rightHow it looks in-gamehashtag

    Mod Structure

    Explaining how to create and run a custom mod

    hashtag
    Summary

    This page will tell you how the CET mod folder structure works. To download a ready-to-go example project, check the wiki's .

    hashtag

    Scripting API

    Explaining how to create and run a custom mod

    triangle-exclamation

    To browse the API, do yourself a favour and use

    hashtag
    Summary

    RegisterInput

    Inputs are buttons events that handle both key press and release states. They must be registered using registerInput() at root level, outside of any event, in the init.lua file.

    The button state (press/release) is defined in the first argument passed to the callback.

    hashtag
    Definition

    TweakDB

    Methods for interacting with TweakDB, the game's internal data store.

    TweakDB is like the game’s database for gameplay features. It’s not actually code itself, but very large amounts of data structures, many of which link together, to make coherent gameplay elements.

    TweakDB has two main elements: Flats, and Records Flats are just like a single piece of data, like a number, a string, a pointer to a record, etc. Records are like a collection of flats. Records have possible types, like a character record or a clothing record. The type of record determines what set of flats the record has.

    An example of these gameplay features nearly entirely contained in TweakDB, would be the damaging quickhacks like Overheat, Contagion, and Short Circuit. The quickhack item to unlock them is in tweakdb, as well as the process of unlocking, making them show up on enemies, the uploading process, the status effect applied, all the special effects possible, as well as the stats to customize all of these things. Almost anything about these quickhacks can be changed in TweakDB.

    circle-info

    NewProxy

    NewProxy is a built-in CET feature you can use to declare callback functions. You can use it to bind a Lua function as a callback with game's script system.

    circle-info

    You can now generate NewProxy using . You need to configure option Clipboard syntax to Lua. You can click on the "copy" button of a function, pick Copy NewProxy and it will copy the code in your clipboard. It only works for classes with their names ending with Listener.

    Linux (Proton)

    Using Cyber Engine Tweaks on Linux

    circle-info

    For a step-by-step guide on installing the basic dependencies to make Cyberpunk run under Linux, see .

    This is a basic guide to using the mod within Proton (aka Linux).

    Please make sure your game works in proton first, before installing this mod.

    Observing weapon hits

    Example code for observing a weapon hit

    hashtag
    Summary

    Created: Oct 08 2025 by Last documented update: Oct 08 2025 by

    This page shows a code sample for reacting to hits on NPCs.

    -- don't overwrite it if it's already defined in the global scope. Or do, up to you.
    Cron = Cron or require('External/Cron.lua')
    local callback = function()
        -- do a little dance, make a little love
    end
    Cron.Every(0.1, callback)
    Cron.Every(0.1, function()
        -- do a little dance, make a little love
    end)
    local callback = function(timer)
        timer.tick = timer.tick +1
        -- do a little dance, make a little love
    end
    Cron.Every(0.1, { tick = 1 }, callback)
    local callback = function() --[[ do a little dance, make a little love ]] end
    Cron.After(0.2, callback)
    local callback = function(timer)
        timer.tick = timer.tick +1
        spdlog.info("Timer is running: " .. tostring(timer.tick))
        -- do a little dance, make a little love
        if timer.tick > 150 then
            Cron.Halt(timer)
        end
    end
    
    -- define outside of function scope to re-use this in Cron.Pause example below
    local timerArg = { tick = 1 }
    
    -- start timer
    Cron.Every(0.2, timerArg, callback)
    -- callback and timer have been defined in the example above
    -- the timer is running
    
    Cron.Pause(timerArg)
    spdlog.info("Timer is paused after " .. tostring(timerArg.tick) .. " ticks")
    
    -- wait 20 seconds, then resume our function
    Cron.After(20, function () Cron.Resume(timerArg) end)
    {
        // other lines
        "font": {
            "base_size": 16.0,
            "language": "Default",
            "oversample_horizontal": 3,
            "oversample_vertical": 1,
            "path": ""
        },
        // other lines
    }
    Lua documentationarrow-up-right
    modulesarrow-up-right
    Setting up the folder
    circle-info

    Cyber Engine Tweaks will be looking for mods inside its/mods/ folder, with each mod having it's own subfolder.

    Create a new folder in the <cet_path>/mods/ folder to get started, e.g. my_mod (snake case recommended)

    hashtag
    Setting up init.lua file

    CET will be looking for an init.lua file inside your mod folder. This is the entry point of your mod, and gets executed when the game is launched.

    circle-info

    Each mod only has read / write access to its own subfolder, so any extra files need to be placed in the same folder or a subfolder.

    hashtag
    Firing up the code

    The init.lua file is the only file that gets automatically loaded, thus should contain all your initialization code.

    Start by printing simple info into the CET Console using the print() function. Then register a listener on the onInit event, and print something else.

    Launch the game, and open the CET Console. You should see your printed text.

    circle-info

    Learn more about Events here. It is an important concept to assimilate for your future mod.

    circle-info

    For communication between mods, use the GetMod() function.

    circle-info

    Tips: Press the "Reload All Mods" button on the CET Overlay after making changes to your mod files to see the effect in live.

    hashtag
    Creating mods

    • To now actually create a complex mod, you'll want to interact with the game's system, modify and create instances of game classes and modify the game's code. For that, head to the Scripting API section

    • Additionally read up on the CET Functions, for information on how to create custom keybinds, Observer and Override game functions, spawn objects and more

    github repoarrow-up-right
    file-download
    9KB
    dump_player.txt
    arrow-up-right-from-squareOpen
    Result of Dump
    This page explains what is what
    circle-exclamation

    This page is still heavily WIP! Perhaps you'd like to work on itarrow-up-right?

    hashtag

    hashtag
    Helpers

    • Helpers make it easy, some listed below

    hashtag
    Game

    • Where can we use it, difference to redscript thing

    hashtag
    GetPlayer

    • What is it in reds, dont store it etc.

    hashtag
    IsDefined

    • Description

    hashtag
    From/ToVariant

    hashtag
    Classes

    • TODO: What names to use

    hashtag
    Creating Instances

    • .new, as constructor

    hashtag
    Member Functions

    These functions are called on Handles. You do not call them on a class, but rather on an instance of a class (not NPCPuppet:isTargetingPlayer(), but npcHandle:isTargetingPlayer())

    hashtag
    Static Functions

    They do not need a handle and are all located in a class, e.g. the Game object:

    hashtag
    Enums

    • How to use, nativeDB link, enumInt

    hashtag
    CName

    • Function to add CNames, string to cname

    hashtag
    Observe and Override

    • Note on how powerful it is etc, link to seperate doc (Observe, Override)

    hashtag
    Blackboard System

    Blackboard is a kind of shared data storage and a framework to access/notify/listen to the data in the storage. Similar to a real blackboard, game objects put their data on the board that then other objects can observe, react to or update the data. E.g. various game state values.

    hashtag
    Handle functions

    They require a handle (more on that later). Assuming you have a player handle:

    hashtag
    Handles

    Handles are a way to pass an object to the function. For example IsNaked makes no sense without telling the engine for which object we want to know this information.

    hashtag
    Singleton

    These handles are static, there is only one in the game, for example the gameTimeSystem is a singleton so there is no need to tell the script engine which one you want. That being said you need a singleton handle so it knows you want to call a function on that system.

    Sample:

    hashtag
    Regular handles

    These handles are not unique, for example, the game contains multiple NPCs so there are as many handles as NPCs. Currently as far as I know we can only get the handle of the player by calling the global function Game.GetPlayer().

    Sample:

    hashtag
    Helpers

    circle-info

    If you need to dump the content of a type, you can use the Dump function like the following:

    You can also call DumpType for a static type you want to know about but don't have an instance of:

    NativeDBarrow-up-right
    hashtag
    Alternative Usage

    You can register an Input and make it behave like a Hotkey. This method is more reactive as it triggers on key press, when a Hotkey is triggered on release.

    hashtag
    Usage Note

    circle-exclamation

    It is important to check the keypress argument inside the callback. Otherwise the code will be executed twice:

    • One time when the key is pressed

    • A second time when released

    hashtag
    Usage Example

    hashtag
    Activate slow motion effect as long as the input key is pressed:

    hashtag
    Advanced Example

    hashtag
    Continuously give money as long as the input key is pressed:

    circle-info

    This example use the onUpdate event, which is triggered continuously. Make sure to check the documentation before any modification.

    To transform a TweakDBID into a gameItemID, call ItemId.FromTDBID(tbdID)

    hashtag
    DebugStats

    Print info about the TweakDB. Displays the number of flats, records, and queries, the size in bytes of flatDataBuffer, and the number of created records.

    hashtag
    Definition

    hashtag
    Usage example

    circle-info

    GameItemIdTo get a TweakDBId's human-readable record name rather than its hash, use id.value!

    hashtag
    GetRecord

    Get a TweakDB record by name or ID.

    hashtag
    Definitions

    hashtag
    Usage example

    hashtag
    GetRecords

    Get a table of all TweakDB records under a given type.

    hashtag
    Definition

    hashtag
    Usage example

    hashtag
    Query

    Get a TweakDB query by name or ID. Returns a table of TweakDBIDs.

    hashtag
    Definitions

    hashtag
    Usage example

    hashtag
    GetFlat

    Get a TweakDB flat by name or ID.

    hashtag
    Definitions

    hashtag
    Usage example

    hashtag
    SetFlat

    Set a TweakDB flat by name or ID and update it. Returns whether the flat was successfully set.

    hashtag
    Definitions

    hashtag
    Usage example

    hashtag
    SetFlatNoUpdate

    Set a TweakDB flat by name or ID without updating it. Returns whether the flat was successfully set.

    hashtag
    Definitions

    hashtag
    Usage example

    hashtag
    Update

    Update (flush data of) a TweakDB record by name, ID, or handle. Returns whether the update was successful.

    hashtag
    Definitions

    hashtag
    Usage example

    hashtag
    CreateRecord

    Create a new TweakDB record. Returns whether the record was created successfully.

    hashtag
    Definitions

    hashtag
    CloneRecord

    Clone an existing record identified by clonedRecordName or clonedRecordID to a new record named recordName or with a TweakDBID of recordID. Returns whether the record was cloned successfully. If a record named recordName or with ID recordID already exists, this method will fail.

    hashtag
    Definitions

    hashtag
    DeleteRecord

    Delete an existing TweakDB record. Returns whether the record was deleted successfully.

    hashtag
    Definitions

    hashtag
    Definition

    CallbackName is an arbitrary name you define. A callback name can be formatted as follow On[Action] (e.g. OnDamaged or also OnPlayerDamaged).

    CallbackDefinition is the signature of the script function (args) and the Lua function (callback) you want to be executed when the callback is triggered.

    The list of arguments must indicate the name of the types to expect. For example with a callback function which receives a String, an Int32 and a reference to a GameObject, it should be defined like this:

    In this case, you can define your function callback like this:

    hashtag
    Find the signature of a function

    The signature of the function depends on the game's script function you want to register a callback for.

    You can use NativeDBarrow-up-right to know the types of arguments to declare. By default, the syntax will be written in Redscript. You can change the option Code syntax in the settings and select Pseudocode · Legacy instead. Basically, it will show you handle:GameObject instead of ref<GameObject> (among other things).

    hashtag
    Use a proxy

    Lets create a proxy:

    After creating the proxy, you can use it to pass the target and function you want to callback. Lets say a game's script is defined as:

    We can call the function RegisterHit to register our callback with our proxy like this:

    Note that the value, when calling listener:Function("OnHit"), is the same we declared in the proxy.

    This way, you can create multiple callback in a proxy and you just need to call listener:Function with the name of the callback you want to use. For example:

    hashtag
    Usage Examples

    This example will be using Codewarearrow-up-right and its system to listen for game eventsarrow-up-right. It will listen for the event Session/Ready and print a message in CET logs.

    NativeDBarrow-up-right
    GetDisplayResolution() -> float, float
    init.lua
    width, height = GetDisplayResolution()
    print(width, height)
    <console>
    > 1920 1080
    GetMod(modName: string) -> object
    init.lua
    mod = GetMod('MyMod')
    returns
    -- any data returned by the mod
    GetSingleton(singletonTypeName: string) -> SingletonReference | nil
    init.lua
    gameTime = GetSingleton("gameTimeSystem")
    
    -- get time
    gameTime:GetGameTime()
    
    -- set time
    gameTime:SetGameTimeByHMS(12, 0, 0) -- 12h0m0s
    returns
    GameTime[ seconds:734466 ]
    GetVersion() -> string
    init.lua
    version = GetVersion()
    
    print(version)
    <console>
    > v1.27.1
    ModArchiveExists(archiveName: string) -> bool
    init.lua
    print(ModArchiveExists('myModFiles.archive'))
    <console>
    > true
    // Redscript side
    public static func IsDetected_NameOfYourMod() -> Void {}
    -- CET side
    function IsRedscriptDetected()
      return Game["IsDetected_NameOfYourMod"] ~= nil
    end
    // Redscript side
    module Module.Name
    public static func IsDetected() -> Void {}
    -- CET side
    function IsRedscriptDetected()
      return Module_Name_IsDetected ~= nil
      -- equivalent syntax without conversion:
      -- return Game["Module.Name.IsDetected"] ~= nil
    end
    Cyberpunk 2077/
    ├─ bin/
    │  ├─ x64/
    │  │  ├─ plugins/
    │  │  │  ├─ cyber_engine_tweaks/
    │  │  │  │  ├─ mods/
    │  │  │  │  │  ├─ mod_1/
    │  │  │  │  │  ├─ mod_2/
    │  │  │  │  │  ├─ mod_3/
    │  │  │  │  │  ├─ my_mod/ <- here
    ../
    ├─ cyber_engine_tweaks/
    │  ├─ mods/
    │  │  ├─ my_mod/
    │  │  │  ├─ init.lua
    init.lua
    -- mod info
    mod = {
        ready = false
    }
    
    -- print on load
    print('My Mod is loaded!')
    
    -- onInit event
    registerForEvent('onInit', function() 
        
        -- set as ready
        mod.ready = true
        
        -- print on initialize
        print('My Mod is initialized!')
        
    end)
    
    -- return mod info 
    -- for communication between mods
    return mod
    player = Game.GetPlayer()
    dmp = Dump(player, false)
    print(dmp)
    type = DumpType("gameItemID", false)
    print(type)
    Game.PrintHealth()
    -- or
    Game.AddToInventory("Items.money", 500)
    print(player:IsNaked())
    print(player:IsMoving())
    player:OnDied()
    gameTime = GetSingleton("gameTimeSystem")
    print(gameTime:GetGameTime())
    gameTime:SetGameTimeByHMS(12,0,0) -- 12h0m0s
    player = Game.GetPlayer()
    if player:IsNaked() then
        player:OnDied() -- kill the player if it's naked
    end
    player = Game.GetPlayer()
    ts = Game.GetTransactionSystem()
    
    tid = TweakDBID.new("Items.money")
    itemid = ItemID.new(tid)
    
    result = ts:GiveItem(player, itemid, 100000)
    if result then
        print("We added " .. tostring(itemid) .. " to the inventory!")
    else
        print("Failed to add " .. tostring(itemid))
    end
    registerInput(slug, label, callback)
    --
    -- registerInput()
    --
    -- @param  string    slug      The internal slug (must be unique in your mod scope)
    -- @param  string    label     The label displayed in CET Bindings
    -- @param  function  callback  The callback function
    --
    registerInput('slug', 'label', function(keypress)
        
        if keypress then
            -- key is pressed
        else
            -- key is released
        end
        
    end)
    registerInput('slug', 'label', function(keypress)
        
        -- bail early on key release
        if not keypress then
            return
        end
        
        -- key is pressed
        
    end)
    registerInput('slug', 'label', function(keypress)
        
        -- this will be called 2 times!
        
    end)
    init.lua
    -- register input
    registerInput('slow_motion', 'Slow Motion', function(keypress)
        
        -- get time system
        local timeSystem = Game.GetTimeSystem()
        
        -- bail early if time system doesn't exists
        if not timeSystem then
            return
        end
        
        -- key is pressed
        if keypress then
            timeSystem:SetTimeDilation('MySlowMo', 0.3)
            
        -- key is released
        else
            timeSystem:UnsetTimeDilation('MySlowMo')
        end
    
    
    end)
    init.lua
    -- set initial switch state
    keep_giving = false
    
    -- register input
    registerInput('give_continuous_money', 'Give Continuous Money', function(keypress)
    
        -- input pressed
        if keypress then
            keep_giving = true -- switch on
    
        -- input released
        else
            keep_giving = false -- switch off
        end
    
    end)
    
    -- onUpdate
    -- this event is triggered continuously
    registerForEvent('onUpdate', function()
        
        -- check switch state
        if keep_giving then
            Game.AddToInventory('Items.money', 20)
        end
    
    end)
    TweakDB:DebugStats() -> nil
    TweakDB:DebugStats()
    TweakDB:GetRecord(recordName: string) -> TweakDBRecord
    TweakDB:GetRecord(recordID: TweakDBID) -> TweakDBRecord
    sticky_frag = TweakDB:GetRecord("Items.GrenadeFragSticky")
    TweakDB:GetRecords(recordTypeName: string) -> table<TweakDBRecord>
    grenades = TweakDB:GetRecords("gamedataGrenade_Record")
    TweakDB:Query(queryName: string) -> table<TweakDBID>
    TweakDB:Query(queryID: TweakDBID) -> table<TweakDBID>
    grenade_ids = TweakDB:Query("Query.GrenadeNoFaction")
    TweakDB:GetFlat(flatName: string) -> object
    TweakDB:GetFlat(flatID: TweakDBID) -> object
    jump_stamina_cost = TweakDB:GetFlat("player.staminaCosts.jump")
    TweakDB:SetFlat(flatName: string, newValue: object) -> boolean
    TweakDB:SetFlat(flatID: TweakDBID, newValue: object) -> boolean
    success = TweakDB:SetFlat("player.staminaCosts.jump", 7.5)
    TweakDB:SetFlatNoUpdate(flatName: string, newValue: object) -> boolean
    TweakDB:SetFlatNoUpdate(flatName: TweakDBID, newValue: object) -> boolean
    success = TweakDB:SetFlatNoUpdate("player.staminaCosts.jump", 7.5)
    TweakDB:Update(recordName: string) -> boolean
    TweakDB:Update(recordID: TweakDBID) -> boolean
    TweakDB:Update(recordHandle: TweakDBRecord) -> boolean
    success = TweakDB:Update("player.staminaCosts.jump")
    TweakDB:CreateRecord(recordName: string, recordTypeName: string) -> boolean
    TweakDB:CreateRecord(recordID: TweakDBID, recordTypeName: string) -> boolean
    TweakDB:CloneRecord(recordName: string, clonedRecordName: string) -> boolean
    TweakDB:CloneRecord(recordName: string, clonedRecordID: TweakDBID) -> boolean
    TweakDB:CloneRecord(recordID: TweakDBID,  clonedRecordName: string) -> boolean
    TweakDB:CloneRecord(recordID: TweakDBID,  clonedRecordID: TweakDBID) -> boolean
    TweakDB:DeleteRecord(recordName: string) -> boolean
    TweakDB:DeleteRecord(recordID: TweakDBID) -> boolean
    listener = NewProxy({
      CallbackName = {
        args = ["ArgType1", "ArgType2", ...],
        callback = function([arg1, arg2, ...]) end
      },
      ...
    })
    args = {"String", "Int32", "handle:GameObject"}
    callback = function(arg1, arg2, arg3)
      -- print("arg1: " .. arg1)
      -- print("arg2: " .. tostring(arg2))
      -- print("arg3: " .. NameToString(arg3:GetClassName()))
    end
    listener = NewProxy({
      OnHit = {
        args = {"handle:GameObject", "Uint32"},
        callback = function(shooter, damage)
          print("Hit by " .. NameToString(shooter:GetClassName()) .. "!")
          print("You lost " .. tostring(damage) .. " HP.")
        end
      }
    })
    // redscript syntax
    public class AwesomePlayer extends PlayerPuppet {
      public func RegisterHit(target: ref<IScriptable>, fn: CName) -> Void;
      public func RegisterShoot(target: ref<IScriptable>, fn: CName) -> Void;
    }
    local awesome = AwesomePlayer.new() -- for example only
    
    awesome:RegisterHit(listener:Target(), listener:Function("OnHit"))
    listener = NewProxy({
      OnHit = {...},
      OnShoot = {
        args = {"handle:GameObject", "Uint32"},
        callback = function(enemy, damage)
          print("You hit " .. NameToString(enemy:GetClassName()) .. "!")
          print("He/She lost " .. tostring(damage) .. " HP.")
        end
      }
    })
    
    -- ...
    
    awesome:RegisterShoot(listener:Target(), listener:Function("OnShoot"))
    local mod = {
      listener = nil
    }
    
    -- Define our function to callback
    function OnReady(event)
      local isMenu = event:IsPreGame()
      
      print("Event \"Session/Ready\" triggered!")
      if isMenu then
        print("Player is in the pre-game menu")
      else
        print("Player is in the game")
      end
    end
    
    registerForEvent('onInit', function()
      -- Create our proxy
      mod.listener = NewProxy({
        OnSessionReady = {
          -- Type is defined in the wiki of Codeware
          args = {"handle:GameSessionEvent"},
          callback = function(event) OnReady(event) end
        }
      })
    
      -- Register our callback to listen for event "Session/Ready".
      local callbackSystem = Game.GetCallbackSystem()
      local target = mod.listener:Target()
      local fn = mod.listener:Function("OnSessionReady")
    
      callbackSystem:RegisterCallback("Session/Ready", target, fn)
    end)
    
    registerForEvent('onShutdown', function()
      -- Unregister our callback before our mod is "removed".
      local target = mod.listener:Target()
      local fn = mod.listener:Function("OnSessionReady")
    
      callbackSystem:UnregisterCallback("Session/Ready", target, fn)
    end)

    Only supports Japanese display

    Korean

    For Korean

    Thai

    For Thai

    Vietnamese

    For Vietnamese

    Cyrillic

    Supports languages that use Cyrillic scriptsarrow-up-right (e.g. Russian, Bulgarian..)

    ChineseFull

    Supports traditional Chinese, simplified Chinese, and Japanese display

    ChineseSimplifiedCommon

    Only supports simplified Chinese display

    ASCII printable charactersarrow-up-right

    Japanese

    You can alos check the pinned messages in the #cet-lua-scripting channelarrow-up-right for any future update.

  • Install the Lua by sumnekoarrow-up-right extension.

  • Locate settings.json in VS code

    • Ctrl + Shift + P

    • C: > Users > (yourname) > AppData > Roaming > Code > User > {} settings.json

  • Cyber Engine Tweaks Lua Libarrow-up-right
    Cyberpunk 2077 Modding Community discord serverarrow-up-right
    this messagearrow-up-right
    Nats-ji/CET_ImGui_lua_type_definesarrow-up-right
    select JSON, not UI
    CET typedef

    Will freeze player in place/in air while it's aiming (same behavior as LookAtarrow-up-right)

    https://nativedb.red4ext.com/ShootEvents#OnEnterarrow-up-right
    thisarrow-up-right
    thisarrow-up-right
    hashtag
    Enabling proton Experimental (Optional)

    While this isn't a required step, it's useful for performance.

    Right click the game in Steam and select 'Properties'. Go to the 'Compatibility' tab and tick 'Force the use of a specific Steam Play compatibility tool'.

    In the dropdown, select 'Proton Experimental'.

    hashtag
    Setting up the mod with ProtonTricks

    Please note - this method requires the use of Steam. For other options, see below.

    First, follow the steps to install the mod. These are in the wikiarrow-up-right.

    Run protontricks 1091500 --gui, you may get an error saying "You are using a 64 bit WINEPREFIX", you can ignore this. Press 'Ok' when this error appears.

    Choose the option 'Select the default wineprefix', then 'Run winecfg'. In the window that opens, select the 'Libraries' tab. In the dropdown on 'New override for library', select version. Click Add, then Apply and Ok.

    You can now exit out of Protontricks and start your game. Check to see if the log exists after you run the game. If it's there, you've successfully installed the mod.

    If you have issues with the mod still not working or the game just crashing, you can try this: re-open Protontricks like you did before, choose the option 'Select the default wineprefix', then 'Install a Windows DLL or component'. Tick vcrun2022 and press Ok. If a window pops up, then go through the steps to install that runtime (just click next really), otherwise just wait until the Protontricks screen comes back. Now exit out of Protontricks and try launching the game. DON'T do this without first checking if the mod works without this step, since installing vcrun2022 has been observed breaking game installs before, preventing the game from ever launching again and requiring a re-install.

    NOTES: You will also need to install d3dcompiler_47 with winetricks to get it to work now, contribution on how to do this would be much appreciated!

    If vcrun2019 breaks your install, it's been reported it can be fixed without reinstalling by deleting the contents of/steamapps/compatdata/1091500.

    hashtag
    Setting up the mod with proton as non-Steam Game

    Please note, this method still requires the use of Steam. You must install Cyberpunk 2077 and then add it to your Steam library by following the official instructionsarrow-up-right.

    Search for the game APPID with: $ protontricks -s name_of_the_game_on_your_steam_lib

    like:

    $ protontricks -s Cyberpunk 2077

    copy the APPID from the terminal then run:

    $ protontricks "APPID" --gui

    From now on, you can follow the Setting up the mod with ProtonTricks instructions.

    hashtag
    Setting up the mod without Steam by using Winetricks

    For users running on the GOG version of the game, via launcher like Lutrisarrow-up-right, Minigalaxyarrow-up-right or GameHubarrow-up-right, those running from the direct GOG Galaxy Windows application through Wine, or those directly using the Game libraries, Protontricks won´t be helpful in your case, as Protontricks will notice that you don´t have the game on Steam.

    Instead, use Winetricks in which Protontricks is derivated from. Please note that you may need to install Winetricksarrow-up-right first, either through the official website or through your distribution's app store.

    Lutris users may perform the following steps to activate the mod:

    Authenticate with GOG Galaxy by clicking on the small profile icon to the right of the GOG entry. You will then be presented with a list of your owned games. Select Cyberpunk and install it - this will install the base version of the game with no other options selected. You may right click on the game icon after installation and choose the option to install the Phantom Liberty DLC if desired. Once installation is complete, select Cyberpunk 2077 and click on the Winetricks 'wine flute' icon along the bottom of the Lutris window. In the expanded menu, select 'Winetricks'.

    Choose the option 'Select the default wineprefix', then 'Run winecfg'. In the window that opens, select the 'Libraries' tab. In the dropdown on 'New override for library', select version. Click Add, then Apply and Ok.

    Alternatively, you may run Winetricks in itself if you use your default prefix, or from your custom prefix via: WINEPREFIX=<Location of your WinePrefix directory> winetricks --gui via the terminal.

    From now on, you can follow the Setting up the mod with ProtonTricks instructions. If you want to verify in Winetricks has correctly taken in account your custom Wine directory, click on Browse files to see if it brings you to the designated folder.

    hashtag
    Thanks to

    • @bundyoarrow-up-right

    • @okamidash (Author)arrow-up-right

    hashtag
    OnWeaponHit

    hashtag
    IsCritical

    hashtag
    IsKill

    local function IsCritical(evt)
      return evt.attackData:HasFlag(hitFlag.CriticalHit)
    end

    Good Practices

    Codes good practices, pitfalls and tips

    hashtag
    LUA

    hashtag
    Gain performance: Early Return

    You're probably used to coding like this:

    In LUA, you can skim a few processing cycles by using the early return style:

    You can gain a significant amount of performance this way, especially when doing this in a loop.

    hashtag
    Fixing/preventing nil access

    LUA throws an exception if it encounters nil in unexpected places. The corresponding error will look like this:

    Open the corresponding file, find the correct line, and check what is being accessed there. It will look like variable.property, or perhaps something will be concatenated to a string ("something something text" .. variable).

    You can assign a default value to the property in question:

    circle-info

    While that won't solve any other problems, it will at least make the error go away.

    hashtag
    Switch in LUA: Lookup Tables

    Who doesn't know the problem? You want to know if your string is A, B, or C, but not D — and LUA doesn't have a switch statement.

    Fortunately, there is a built-in and performant way to

    hashtag
    Performance killers: strings

    String concatenation and comparison can be the difference between a brief stutter and a complete freeze or even crash to desktop. This is not a joke — see for more detail.

    hashtag
    Comparing/Searching

    Lua internalizes strings. That means these two strings will share a single representation in memory:

    The comparison between those two strings will be almost-instant.

    This becomes a problem when comparing strings in a loop (see ):

    Every single pass of the loop will create a memory representation of "This is the same object!" and then discard it again.

    circle-check

    Takeaway:

    If at all possible, define things outside the scope of loops!

    hashtag
    Finding in strings

    Lua's regex implementation is very limited. There is a limitation for pipes. For example, the following example will actually iterate twice after creating internal string representations:

    It is faster to just do this:

    On top of that, string.match will return the entire string if no match is found:

    The alternative:

    circle-check

    Takeaway:

    • Avoid regex

    hashtag
    Concatenation

    circle-info

    For a performance analysis of different kinds of string concatenation, check .

    hashtag
    Scopes

    triangle-exclamation

    Don't do this (30% less performant):

    circle-check

    Do this instead:

    Useful commands

    A collection of commonly requested commands. How do I...

    hashtag
    First Launch

    After installing CET, you can open the console overlay with the corresponding key.

    circle-info

    If you have any problems getting to this stage, please check the page.

    triangle-exclamation

    The commands are case-sensitive!

    Many console commands have changed with 2.0. If something you try doesn't work, check the most up-to-date list under .

    Open the Console tab to run Lua scripts or engine functions. To find out what you can do here, keep reading.

    hashtag
    Spreadsheets

    To find all the codes you can enter, check

    • (source: #cet-support on )

    For the most common commands, see below.

    If you want to find a specific item, look for how to find the hash code for the .

    hashtag
    Items

    Use Game.AddToInventory

    chevron-rightFrequently Required Itemshashtag

    Ammunition

    Crafting Components

    Quickhacks

    hashtag
    Level

    Use Game.SetLevel

    hashtag
    Attributes

    Formerly Game.SetAttribute

    hashtag
    Perk and Skill Points

    Formerly Game.GiveDevPoints

    hashtag
    Vehicles

    All vehicle commands are documented on the VehicleSystem page linked below.

    hashtag
    Teleportation

    hashtag
    Get your coordinates

    You can print V's current coordinates to the CET console with the following command:

    hashtag
    Go somewhere else

    You will need to know the coordinates of where you want to go in the game world, some popular . Replace X, Y, and Z with your coordinates. Coordinates can also be found in the nodeData of .streamingsector files.

    hashtag
    Facts

    triangle-exclamation

    At this time there is no way of triggering or "fixing" stuck quests with the console.

    chevron-rightSpoilers!hashtag

    hashtag
    Secret Ending

    hashtag

    For a more cohesive list of quest facts, check the .

    UI Examples

    This page contains examples for creating UI for your mod using the built-in ImGui library.

    Everything you need to know about Dear ImGui can be found herearrow-up-right and herearrow-up-right.

    circle-check

    You can also take a look at FellowImGuiarrow-up-right. It is a free online tool to create ImGui layouts with an easy to use interface. It also generates Lua code for you. Feel free to come on this Discord channelarrow-up-right if you have issues or feedback to share.

    circle-exclamation

    Any ImGui code needs to be run from inside the callback.

    hashtag
    Basic Window

    hashtag
    Modal/Popup Window

    hashtag
    Combo Box with Selectables

    hashtag
    Checkbox

    hashtag
    Button

    hashtag
    Tooltip

    Default tooltip on a button.

    To customize the tooltip window use BeginTooltip and EndTooltip.

    hashtag
    Multi-column layout

    Creating a multi-column layout is a pain in the ass, but you can go about it like this:

    You can probably use dynamic dimensions somehow. If you find out, please update this guide.

    Cyberpunk 2077 Modding

    Hotkeys

    hashtag
    Introduction

    Cyber Engine Tweaks allow mods to register hotkeys that can be assigned by the player in the , and execute custom code once pressed. There are two kinds of hotkeys:

    • are triggered on key release

    "path": ""
    "path": "C:/Windows/Fonts/comic.ttf"
    "language": "",
    {
        // other lines
        "font": {
            "base_size": 16.0,
            "language": "ChineseFull",
            "oversample_horizontal": 3,
            "oversample_vertical": 1,
            "path": "C:/Windows/Fonts/simhei.ttf"
        },
        // other lines
    }
         "Lua.runtime.version": "LuaJIT",
         "Lua.workspace.preloadFileSize": 15360,
         "Lua.workspace.library": [
             "c:\\path\\to\\cet-lua-lib",
         ], 
    ---@param request PerformFastTravelRequest
    Observe("FastTravelSystem", "OnPerformFastTravelRequest", function(request)
        -- Now request object has type and suitable for code completion
    end)
    ---@type ScriptedPuppet
    local puppet
    local adjustRequest = AdjustTransformWithDurations.new();
    
    -- Setting any duration to 0 or lower means "do not change"
    -- Slide = change player position
    -- Rotation = change player rotation / trajectory / viewing angles
    
    adjustRequest:SetSlideDuration(-1.0); -- Disabling position adjustion
    adjustRequest:SetRotationDuration(1); -- Rotation show be smooth and should happen in 1 second (presumably, second!)
    adjustRequest:SetUseParabolicMotion(true); -- Do not just interpolate movement/rotation, use parabolic formula
    adjustRequest:SetCurve(CName.new("None")); -- Setting curve to "None", no other curves were found in the sources
    adjustRequest:SetRotation(ToEulerAngles {0, 0, 0}:ToQuat()); -- Setting target player's rotation
    
    stateContext:SetTemporaryScriptableParameter("adjustTransform", adjustRequest, true);
    local adjustRequest = AdjustTransformWithDurations.new();
       
    adjustRequest:SetSlideDuration(-1.0); -- Disabling position adjustion
    adjustRequest:SetRotationDuration(1.0); -- Rotation show be smooth and should happen in ~1 second (presumably, seconds!)
    adjustRequest:SetUseParabolicMotion(true); -- Do not just interpolate movement/rotation, use parabolic formula
    adjustRequest:SetCurve(CName.new("None")); -- Setting curve to "None", no other curves were found in the sources
    -- In this case aimAt is a GameObject
    adjustRequest:SetTarget(aimAt); -- Setting target player's rotation
    
    stateContext:SetTemporaryScriptableParameter("adjustTransform", adjustRequest, true);
    local adjustRequest = AdjustTransformWithDurations.new();
            
    adjustRequest:SetSlideDuration(1.0); -- Setting duration of position change.
    adjustRequest:SetRotationDuration(-1.0); -- Disabling rotation adjustion
    adjustRequest:SetUseParabolicMotion(true); -- Do not just interpolate movement/rotation, use parabolic formula
    adjustRequest:SetCurve(CName.new("None")); -- Setting curve to "None", no other curves were found in the sources
    
    -- Getting player's position
    local playerPosition = Game.GetPlayer():GetWorldPosition();
    -- Adding 10 on the vertical axis
    playerPosition.z = playerPosition.z + 10;
    
    adjustRequest:SetPosition(playerPosition);
    
    stateContext:SetTemporaryScriptableParameter("adjustTransform", adjustRequest, true);
    init.lua
    local function OnWeaponHit(this, evt) -- https://nativedb.red4ext.com/gameHitEvent
      
      -- check that the player was the one who hit this npc
      -- https://nativedb.red4ext.com/AttackData
      if not IsDefined(evt.attackData.instigator) or not evt.attackData.instigator:IsPlayer() then return end
      
      local record = evt.attackData.weapon.weaponRecord -- https://nativedb.red4ext.com/gamedataWeaponItem_Record
        
      local id = record:GetID().value -- make sure to stringify the TweakDBID so we can print and search
    
      print("weapon id: " .. id);
    
      if string.find(id, "Items.your_string_here") then
        print("yay")
      end    
    end
    
    registerForEvent("onInit", function()  
      ObserveAfter('NPCPuppet', 'OnHit', OnWeaponHit)
    end)  
    local function IsKill(evt)
      return evt.attackData:HasFlag(hitFlag.Kill)
    end
    prefer String.find() over String.match()
    herearrow-up-right
    Scopes
    herearrow-up-right
    onDraw
    local function myMethod()
      if condition then 
        -- do a little dance, make a little love…
      end
    end
    local function myMethod()
      if not condition then return end
      -- do a little dance, make a little love…
    end
    attempt to access local '<variable name>' (a nil value)
    stack traceback: 
      my_example.lua:1234 in function 'MyFunctionName
    myString = <original string assignment> or ""
    myNumber = <original number assignment> or 0
    myEverythingElse = <original object assignment> or {}
    local string1 = "This is the same object!"
    local string2 = "This is the same object!"
    for (_, mystring) in ipairs(mytable) do
        if mystring == "This is the same object!" then
            -- do something
        end
    end
    local myCompareString = "This is the same object!"
    for (_, mystring) in ipairs(mytable) do
        if mystring == myCompareString then
            -- do something
        end
    end
    if string.find("catastrophe",  "dog|cat") then 
      -- do something
    end
    if string.find("catastrophe",  "dog") or string.find("catastrophe",  "cat") then 
      -- do something
    end
    local match = string.match("catastrophe",  "dog")
    if match ~= "catastrophe" then
      -- do something
    end
    if string.find("catastrophe",  "dog")
      -- do something
    end
    function foo(x)
        for i = 1, 1000000 do
            x = x + math.sin(i)
        end
        return x
    end
    local sin = math.sin
    function foo(x)
        for i = 1, 1000000 do
            x = x + sin(i)
        end
        return x
    end
    ImGui.SetNextWindowPos(100, 500, ImGuiCond.FirstUseEver) -- set window position x, y
    ImGui.SetNextWindowSize(300, 600, ImGuiCond.Appearing) -- set window size w, h
    
    if ImGui.Begin("Unique Window Name") then
        ImGui.Text("Hello World")
        -- more window contents here
    end
    ImGui.End()
    if ImGui.Button("Pop Button", 120, 0) then
          ImGui.OpenPopup("Delete?")
    end
    
    if ImGui.BeginPopupModal("Delete?", true, ImGuiWindowFlags.AlwaysAutoResize) then
        ImGui.Text("This is a popup")
        
        if ImGui.Button("Close") then ImGui.CloseCurrentPopup() end
        ImGui.EndPopup()
    end
    local DropdownOptions = {"1", "2", "3", "4", "5"}
    local DropdownSelected = "1"
    
    if ImGui.BeginCombo("##My Combo Box", DropdownSelected) then -- Remove the ## if you'd like for the title to display above combo box
    
        for _, option in ipairs(DropdownOptions) do
    
            if ImGui.Selectable(option, (option == DropdownSelected)) then
                DropdownSelected = option
                ImGui.SetItemDefaultFocus()
            end
    
        end
    
        ImGui.EndCombo()
    end
    MyMod.myValue = ImGui.Checkbox("Check me", MyMod.myValue)
    if ImGui.Button("Click Me!", 100, 20) then -- Label, width, height - Use -1 as width for button to span available horizontal space
        -- do stuff here when button is clicked
    end
    local pressed
    
    registerForEvent("onUpdate", function()
        if pressed then
            print("You pressed me!")
        end
    end)
    
    registerForEvent("onDraw", function()
        pressed = ImGui.Button("Click me I'm a Sexy Button", 250, 25)
    end)
    local pressed = ImGui.Button("button text")
    if ImGui.IsItemHovered() then
      ImGui.SetTooltip("tooltip text")
    else
      ImGui.SetTooltip(nil)
    end
    local pressed = ImGui.Button("button text")
    if ImGui.IsItemHovered() then
      ImGui.BeginTooltip()
      ImGui.PushTextWrapPos(50)
      ImGui.TextWrapped("tooltip text")
      ImGui.EndTooltip()
    else
      ImGui.SetTooltip(nil)
    end
    ImGui.BeginChild("Column 1", 300, 400) -- this will not display a label
        ImGui.Text('Column 1')
    ImGui.EndChild()
    
    ImGui.BeginChild("Column 2", 300, 400) -- this will not display a label
        ImGui.Text('Column 2')
    ImGui.EndChild()
    Skippy - Mode

    hashtag
    Skippy - Relationship

    hashtag
    Change where Jackie was sent to in the prologue?

    hashtag
    Set Takemura's condition after the parade?

    troubleshooting
    CET Console Commands for Patch 2.xarrow-up-right
    redmodding Discordarrow-up-right
    CET Console Commands for Patch 1.x (Legacy)arrow-up-right
    command below
    coordinates are available here
    Spreadsheets

    Inputs are triggered on key press and release

    Hotkeys and Inputs, while having similar functionalities, are displayed in two separated groups.

    hashtag
    General Notes

    circle-exclamation

    Hotkeys are not triggered while the player stay pressed on a game's keybind.

    For example, if the player move forward with "W" and press a Hotkey at the same time, it won't be triggered.

    For this reason, it is recommended to use Inputs instead, as they are always triggered.

    circle-info

    Remember to register Hotkeys/Inputs at root level, outside of any event.

    circle-info

    Hotkeys & Inputs are displayed in the CET Bindings screen sorted in the order they were registered in the code. If you want to sort hotkeys by label in ascending order for example, you'll have to change the registration code order accordingly.

    circle-info

    Hotkeys & Inputs can be triggered by the user from anywhere, at anytime, as soon as CET loaded your mod (even earlier than onInit event).

    Which means the user might press your hotkey in the Main Game Menu or in Settings Menu. You have to keep that in mind when working with hotkeys, and make sure your script is executed where it is supposed to.

    circle-info

    Hotkeys & Inputs are triggered on top of the game's keybinds. Which means it's possible that player set a hotkey on "R".

    In-game, when the player use "R" to trigger your script, it will also trigger the game's Reload function.

    hashtag
    Available Functions

    CET Binding screen
    Hotkeys
    RegisterHotkeychevron-right
    RegisterInputchevron-right

    Debug functions

    hashtag
    print()

    Prints one or more objects to the CET Console and CET Console Logs file. Multiple objects will be printed separated by a space.

    hashtag
    Definition

    hashtag
    Usage example

    hashtag
    spdlog

    Write a string in the file, eg, <cet_path>/mods/my_mod/my_mod.log.

    There are several spdlog methods, all are identical and have the same output:

    • spdlog.debug()

    • spdlog.trace()

    • spdlog.info()

    hashtag
    Definition

    hashtag
    Usage example

    hashtag
    Usage tips

    It is recommended to use spdlog.info(tostring(my_var)) to make sure the variable is compatible, as boolean, interger and other types will throw an error.

    hashtag
    Dump()

    Returns a JSON-ish string with details about the members of obj:

    • Type name

    • Instance functions

    • Static functions

    The listed functions include their signatures.

    If detailed is true, function descriptions will include the function's full name hash, short name, and short name hash.

    circle-info

    obj must be an instance of a wrapped game type, such as PlayerPuppet which is returned by Game.GetPlayer(). will dump the names of all such types.

    hashtag
    Definition

    hashtag
    Usage example

    hashtag
    GameDump()

    Returns the game's internal debug string for the given object. This string displays the object's properties and their values.

    circle-exclamation

    Beware that this string probably won't have any line breaks, so it's more advisable to open <cet_path

    hashtag
    Definition

    hashtag
    Usage example

    hashtag
    DumpType()

    Like , but takes a type name rather than an object instance. Use to get a list of all types.

    hashtag
    Definition

    hashtag
    Usage example

    hashtag
    DumpAllTypeNames()

    Dump all type names to <cet_path>/cyber_engine_tweaks.log and prints to the the number of types dumped.

    hashtag
    Definition

    hashtag
    Usage example

    Debug

    A debug helper class

    hashtag
    Summary

    This page contains the source code for the debug helper class, along with examples on how to use it.

    If you just want to log something, check Logs & Debug.

    hashtag
    What does this class do?

    • Prints one or multiple objects into

    • Prints one or multiple objects into file

    • Pretty-print table (with indent)

    hashtag
    Source code

    hashtag
    Definitions

    Prints to , similar to function.

    Write in file. eg, <cet_path>/mods/my_mod/my_mod.log. Similar to .

    hashtag
    Usage example

    Clothing Recipes

    This is a preliminary list of item hashes for clothes recipes, provided by Bettik#0331. I have preserved it here so the data doesn't get lost until we can find a proper home for it.

    hashtag
    Confirmed Recipes

    1. Game.AddToInventory("Items.LegendaryBootsRecipe", 1)

    2. Game.AddToInventory("Items.LegendaryCapRecipe", 1)

    3. Game.AddToInventory("Items.LegendaryCasualShoesRecipe", 1)

    4. Game.AddToInventory("Items.LegendaryFormalPantsRecipe", 1)

    5. Game.AddToInventory("Items.LegendaryFormalShoesRecipe", 1)

    6. Game.AddToInventory("Items.LegendaryFormalSkirtRecipe", 1)

    7. Game.AddToInventory("Items.LegendaryGlassesRecipe", 1)

    8. Game.AddToInventory("Items.LegendaryGogglesRecipe", 1)

    9. Game.AddToInventory("Items.LegendaryHatRecipe", 1)

    10. Game.AddToInventory("Items.LegendaryJacketRecipe", 1)

    11. Game.AddToInventory("Items.LegendaryJumpsuitRecipe", 1)

    12. Game.AddToInventory("Items.LegendaryPantsRecipe", 1)

    13. Game.AddToInventory("Items.LegendaryScarfRecipe", 1)

    14. Game.AddToInventory("Items.LegendaryShirtRecipe", 1)

    15. Game.AddToInventory("Items.LegendaryShortsRecipe", 1)

    16. Game.AddToInventory("Items.LegendaryTechRecipe", 1)

    17. Game.AddToInventory("Items.LegendaryTightJumpsuitRecipe", 1)

    18. Game.AddToInventory("Items.LegendaryTShirtRecipe", 1)

    19. Game.AddToInventory("Items.LegendaryVestRecipe", 1)

    20. Game.AddToInventory("Items.LegendaryVisorRecipe", 1)

    21. Game.AddToInventory("Items.EpicBootsRecipe", 1)

    22. Game.AddToInventory("Items.EpicCapRecipe", 1)

    23. Game.AddToInventory("Items.EpicCasualShoesRecipe", 1)

    24. Game.AddToInventory("Items.EpicCoatRecipe", 1)

    25. Game.AddToInventory("Items.EpicFormalJacketRecipe", 1)

    26. Game.AddToInventory("Items.EpicFormalShoesRecipe", 1)

    27. Game.AddToInventory("Items.EpicFormalSkirtRecipe", 1)

    28. Game.AddToInventory("Items.EpicGlassesRecipe", 1)

    29. Game.AddToInventory("Items.EpicHatRecipe", 1)

    30. Game.AddToInventory("Items.EpicHelmetRecipe", 1)

    31. Game.AddToInventory("Items.EpicLooseShirtRecipe", 1)

    32. Game.AddToInventory("Items.EpicMaskRecipe", 1)

    33. Game.AddToInventory("Items.EpicPantsRecipe", 1)

    34. Game.AddToInventory("Items.EpicScarfRecipe", 1)

    35. Game.AddToInventory("Items.EpicShortsRecipe", 1)

    36. Game.AddToInventory("Items.EpicTShirtRecipe", 1)

    37. Game.AddToInventory("Items.EpicUndershirtRecipe", 1)

    38. Game.AddToInventory("Items.EpicVisorRecipe", 1)

    39. Game.AddToInventory("Items.RareBootsRecipe", 1)

    40. Game.AddToInventory("Items.RareCasualShoesRecipe", 1)

    41. Game.AddToInventory("Items.RareCoatRecipe", 1)

    42. Game.AddToInventory("Items.RareFormalJacketRecipe", 1)

    43. Game.AddToInventory("Items.RareFormalPantsRecipe", 1)

    44. Game.AddToInventory("Items.RareFormalShoesRecipe", 1)

    45. Game.AddToInventory("Items.RareGlassesRecipe", 1)

    46. Game.AddToInventory("Items.RareHatRecipe", 1)

    47. Game.AddToInventory("Items.RareHelmetRecipe", 1)

    48. Game.AddToInventory("Items.RareJacketRecipe", 1)

    49. Game.AddToInventory("Items.RareMaskRecipe", 1)

    50. Game.AddToInventory("Items.RarePantsRecipe", 1)

    51. Game.AddToInventory("Items.RareScarfRecipe", 1)

    52. Game.AddToInventory("Items.RareShirtRecipe", 1)

    53. Game.AddToInventory("Items.RareShortsRecipe", 1)

    54. Game.AddToInventory("Items.RareTechRecipe", 1)

    55. Game.AddToInventory("Items.RareTShirtRecipe", 1)

    56. Game.AddToInventory("Items.RareUndershirtRecipe", 1)

    57. Game.AddToInventory("Items.RareVestRecipe", 1)

    58. Game.AddToInventory("Items.UncommonBalaclavaRecipe", 1)

    59. Game.AddToInventory("Items.UncommonBootsRecipe", 1)

    60. Game.AddToInventory("Items.UncommonCapRecipe", 1)

    61. Game.AddToInventory("Items.UncommonCasualShoesRecipe", 1)

    62. Game.AddToInventory("Items.UncommonFormalPantsRecipe", 1)

    63. Game.AddToInventory("Items.UncommonFormalShoesRecipe", 1)

    64. Game.AddToInventory("Items.UncommonFormalSkirtRecipe", 1)

    65. Game.AddToInventory("Items.UncommonHatRecipe", 1)

    66. Game.AddToInventory("Items.UncommonHelmetRecipe", 1)

    67. Game.AddToInventory("Items.UncommonJacketRecipe", 1)

    68. Game.AddToInventory("Items.UncommonMaskRecipe", 1)

    69. Game.AddToInventory("Items.UncommonPantsRecipe", 1)

    70. Game.AddToInventory("Items.UncommonShirtRecipe", 1)

    71. Game.AddToInventory("Items.UncommonTechRecipe", 1)

    72. Game.AddToInventory("Items.UncommonUndershirtRecipe", 1)

    73. Game.AddToInventory("Items.UncommonVestRecipe", 1)

    74. Game.AddToInventory("Items.UncommonVisorRecipe", 1)

    hashtag
    Raw List

    The txt file below contains a dump directly from the game files of all currently known items in the game's TweakDB.

    circle-info

    This list does not contain every item in the game. More will be added as we discover more locations in the game files containing items and recipes.

    Game.AddToInventory("Items.money", 10000) -- Gives 10,000 eurodollars
    Game.AddToInventory("Items.SQ031_Samurai_Jacket", 1) -- Gives you the Replica of Johnny's Samurai Jacket 
    Game.AddToInventory("Ammo.HandgunAmmo", 500)
    Game.AddToInventory("Ammo.RifleAmmo", 700)
    Game.AddToInventory("Ammo.ShotgunAmmo", 100)
    Game.AddToInventory("Ammo.SniperRifleAmmo", 100)
    Game.AddToInventory("Items.CommonMaterial1", 1000)
    Game.AddToInventory("Items.UncommonMaterial1", 1000)
    Game.AddToInventory("Items.RareMaterial1", 1000)
    Game.AddToInventory("Items.EpicMaterial1", 1000)
    Game.AddToInventory("Items.LegendaryMaterial1", 1000)
    Game.AddToInventory("Items.QuickHackUncommonMaterial1", 1000)
    Game.AddToInventory("Items.QuickHackRareMaterial1", 1000)
    Game.AddToInventory("Items.QuickHackEpicMaterial1", 1000)
    Game.AddToInventory("Items.QuickHackLegendaryMaterial1", 1000)
    Game.SetLevel("Level", 60, 1) -- Sets character level to 60
    PlayerDevelopmentSystem.GetInstance(Game.GetPlayer()):GetDevelopmentData(Game.GetPlayer()):SetAttribute("Strength", 15) -- Sets Body to 15
    PlayerDevelopmentSystem.GetInstance(Game.GetPlayer()):GetDevelopmentData(Game.GetPlayer()):AddDevelopmentPoints(5, gamedataDevelopmentPointType.Attribute) -- Attribute (skill) points
    PlayerDevelopmentSystem.GetInstance(Game.GetPlayer()):GetDevelopmentData(Game.GetPlayer()):AddDevelopmentPoints(3, gamedataDevelopmentPointType.Primary) -- Perk points
    print(Game.GetPlayer():GetWorldPosition())
    Game.GetTeleportationFacility():Teleport(GetPlayer(), ToVector4{x=X, y=Y, z=Z, w=1}, ToEulerAngles{roll=0, pitch=0, yaw=0})
    Game.GetQuestsSystem():SetFactStr("sq032_johnny_friend", 1)
    Game.GetQuestsSystem():SetFactStr("mq007_skippy_aim_at_head", 1)
    Game.GetQuestsSystem():SetFactStr("mq007_skippy_goes_emo", 0)
    Game.GetQuestsSystem():SetFactStr("q005_jackie_to_hospital", 0)
    Game.GetQuestsSystem():SetFactStr("q005_jackie_to_mama", 0)
    Game.GetQuestsSystem():SetFactStr("q005_jackie_stay_notell", 1)
    Game.GetQuestsSystem():SetFactStr("q112_takemura_dead", 1)
    file-download
    108KB
    tdbids_items.txt
    arrow-up-right-from-squareOpen
    Raw list of all items
    mana vortex
    mana vortex
    mana vortex
    mana vortex
  • spdlog.warning()

  • spdlog.error()

  • spdlog.critical()

  • Properties
    >/scripting.log
    in a text editor with line wrapping to view the output of this function.
    Individual Mod Logs
    DumpAllTypeNames()
    Dump()
    DumpAllTypeNames()
    CET Console

    Handle boolean, nil, string and other classic types

  • Parse userdata via GameDump() or Dump() if possible

  • Parse CName via NameToString() if possible

  • CET Console
    Individual Mod Logs
    CET Console
    print()
    Individual Mod Logs
    spdlog
    print(obj: object, [obj2: object, [...]]) -> nil
    init.lua
    print('Hey', 'there', 1, 2, true)
    <console>
    > Hey there 1 2 true
    spdlog.info(string: string) -> nil
    init.lua
    spdlog.info('Custom log')
    my_mod.log
    > [02:31:08] Cutom log
    Dump(obj: GameDataType, detailed: bool) -> string
    init.lua
    print(Dump(Game.GetPlayer(), false))
    <console>
    {
        name: PlayerPuppet,
        functions: {
            IsPlayer() => (Bool),
            IsReplacer() => (Bool),
            IsVRReplacer() => (Bool),
            IsJohnnyReplacer() => (Bool),
            IsReplicable() => (Bool),
            GetReplicatedStateClass() => (CName),
            IsCoverModifierAdded() => (Bool),
            IsWorkspotDamageReductionAdded() => (Bool),
            IsWorkspotVisibilityReductionActive() => (Bool),
            GetOverlappedSecurityZones() => (array:gamePersistentID),
            ...
    }
    GameDump(obj: GameDataType) -> string
    init.lua
    print(GameDump(Game.GetPlayer()))
    <console>
    > PlayerPuppet[ customCameraTarget:ECCTV_OnlyOnscreen, renderSceneLayerMask:<Default;Cyberspace>, persistentState:, playerSocket:gamePlayerSocket[ ], ...]
    DumpType(typeName: string, detailed: boolean) -> string
    init.lua
    print(DumpType('PlayerPuppet', false))
    <console>
    {
        name: PlayerPuppet,
        functions: {
            IsPlayer() => (Bool),
            IsReplacer() => (Bool),
            IsVRReplacer() => (Bool),
            IsJohnnyReplacer() => (Bool),
            IsReplicable() => (Bool),
            GetReplicatedStateClass() => (CName),
            IsCoverModifierAdded() => (Bool),
            IsWorkspotDamageReductionAdded() => (Bool),
            IsWorkspotVisibilityReductionActive() => (Bool),
            GetOverlappedSecurityZones() => (array:gamePersistentID),
            ...
    }
    DumpAllTypeNames() -> nil
    init.lua
    DumpAllTypeNames()
    <console>
    > Dumped 26811 types
    <cyber_engine_tweaks.log>
    [operator ()] [4416] handle:gamedataUICharacterCreationAttribute_Record
    [operator ()] [4416] DamageTypeIndicator
    [operator ()] [4416] worldAIDirectorSpawnNode
    [operator ()] [4416] UI_DEV_ScriptableSystemUseNewTooltips
    [operator ()] [4416] array:SDocumentThumbnailWidgetPackage
    [operator ()] [4416] gameIPersistencySystem
    [operator ()] [4416] DelayedComDeviceClose
    [operator ()] [4416] inkSettingsSelectorController
    [operator ()] [4416] array:handle:AICombatSquadScriptInterface
    [operator ()] [4416] animAnimNode_LocomotionAdjusterOnEvent
    [operator ()] [4416] whandle:gamedataLightPreset_Record
    [operator ()] [4416] handle:C4ControllerPS
    ...
    debug = {
    
        -- log
        log = function(...)
    
            spdlog.info(debug.parse(...))
    
        end,
    
        -- print
        print = function(...)
    
            print(debug.parse(...))
    
        end,
    
        -- parse arguments
        parse = function(...)
    
            local args = {}
            for _, v in pairs{debug.null(...)} do
                table.insert(args, debug.parseDeep(v))
            end
    
            local output = ""
            for i, value in pairs(args) do
                output = output .. tostring(value)
                if i ~= #args then
                    output = output .. " "
                end
            end
    
            return output
    
        end,
    
        -- parse deep table
        parseDeep = function(t, max, depth)
    
            -- convert nil to 'nil'
            t = debug.null(t)
    
            if type(t) ~= 'table' then
    
                if type(t) == 'userdata' then
                    t = debug.parseUserData(t)
                end
    
                return t
            end
    
            max = max or 63
            depth = depth or 4
    
            local dumpStr = '{\n'
            local indent = string.rep(' ', depth)
    
            for k, v in pairs(t) do
    
                -- vars
                local ktype = type(k)
                local vtype = type(v)
                local vstr = ''
    
                -- key
                local kstr = ''
                if ktype == 'string' then
                    kstr = string.format('[%q] = ', k)
                end
    
                -- string
                if vtype == 'string' then
                    vstr = string.format('%q', v)
    
                -- table
                elseif vtype == 'table' then
    
                    if depth < max then
                        vstr = debug.parseDeep(v, max, depth + 4)
                    end
    
                -- userdata
                elseif vtype == 'userdata' then
                    vstr = debug.parseUserData(v)
    
                -- thread (do nothing)
                elseif vtype == 'thread' then
    
                -- else
                else
                    vstr = tostring(v)
                end
    
                -- format dump
                if vstr ~= '' then
                    dumpStr = string.format('%s%s%s%s,\n', dumpStr, indent, kstr, vstr)
                end
            end
    
            -- unindent
            local unIndent = indent:sub(1, -5)
    
            -- return
            return string.format('%s%s}', dumpStr, unIndent)
    
        end,
    
        -- parse userdata
        parseUserData = function(t)
    
            local tstr = tostring(t)
    
            if tstr:find('^ToCName{') then
                tstr = NameToString(t)
            elseif tstr:find('^userdata:') or tstr:find('^sol%.') then
    
                local gdump = false
                local ddump = false
                pcall(function() gdump = GameDump(t) end)
                pcall(function() ddump = Dump(t, true) end)
    
                if gdump then
                    tstr = GameDump(t)
                elseif ddump then
                    tstr = ddump
                end
            end
    
            return tstr
    
        end,
    
        -- convert nil into 'nil'
        null = function(...)
    
            local t, n = {...}, select('#', ...)
    
            for k = 1, n do
                local v = t[k]
                if     v == debug.null then t[k] = 'nil'
                elseif v == nil  then t[k] = debug.null
                end
            end
    
            return (table.unpack or unpack)(t, 1, n)
        end,
    
    }
    debug.print(obj: object [, obj2, ...]) -> nil
    debug.log(obj: object [, obj2, ...]) -> nil
    init.lua
    local myTable = {
        var = true,
        enabled = false,
        subTable = {
            player = Game.GetPlayer(),
            stats = Game.GetStatsSystem()
        }
    }
    
    debug.log('myTable:', myTable)
    my_mod.log
    [13:49:03] myTable: {
        ["var"] = true,
        ["enabled"] = false,
        ["subTable"] = {
            ["player"] = PlayerPuppet[ customCameraTarget:ECCTV_OnlyOnscreen, ...]
            ["stats"] = gameStatsSystem[ ],
        },
    }

    Observe

    Observers are builtin CET functions that allow developers to detect when a function/method is executed by the game. They must be registered inside the onInit event, in the init.lua file.

    circle-info

    You can find a list of things to observe arrow-up-rightin the RedScript wiki.

    There are two kinds observers:

    • ObserveBefore() which is triggered right at the moment the function/method is called

    • ObserveAfter() which is triggered once the game finished to execute the function/method

    The provided callback is always filled as follow:

    • The first argument is the current object that execute the function/method. This argument is not present when function/method is static.

    • Others arguments passed to the targeted function/method, if any.

    circle-info

    You can now generate Observable and ObservableAfter using . You need to configure option Clipboard syntax to Lua. You can click on the "copy" button of a function, pick Copy Observable or Copy ObservableAfter and it will copy the code in your clipboard.

    hashtag
    Definition

    circle-info

    Observe() is an alias of ObserveBefore(). They both work the same.

    circle-exclamation

    If you observe a static function, you must define the field 'method' with the full name of the function. Otherwise it won't work. You can find the full name using . See for an example.

    hashtag
    Representation

    Here is a visual representation showing where observers are executed in the game's script:

    In this representation, the observer callback would be filled with the following arguments:

    circle-info

    The self parameter can be renamed at your convenience:

    • _

    In the code above, the observer listens to AimingStateEvents:OnEnter(), which is triggered everytime the player enters in ADS state.

    Additionally, the OnEnter() method is responsible to apply different effects, like the camera zoom, the initial stamina drain etc... Following this logic, this means:

    • Using ObserveBefore() allows to hook in before these effects are applied

    • Using ObserveAfter() guarantees the method finished to apply all the effects

    hashtag
    Usage Examples

    hashtag
    Give money each time the player crouches:

    hashtag
    Give money as long as the player is crouched:

    hashtag
    Observe event send to an (click on function's name in NativeDB to copy the full name in your clipboard):

    hashtag
    Advanced Example

    hashtag
    Give money when the player is crouched and in ADS:

    Override

    circle-exclamation

    It is generally advisable to useObserve whenever possible, and only use Override when absolutely necessary

    Override is a built-in CET function allowing developers to rewrite a game's class method. It must be registered using the Override() function inside the onInit event, in the init.lua file.

    The provided callback is always filled as follows:

    • The first argument is the current object that execute the method. This argument is not present when function/method is static.

    • Others arguments passed to the targeted method, if any.

    • The last argument is the original callable method.

    circle-info

    You can now generate Override using . You need to configure option Clipboard syntax to Lua. You can click on the "copy" button of a function, pick Copy Override and it will copy the code in your clipboard.

    hashtag
    Definition

    circle-exclamation

    If you override a static function, you must define the field 'method' with the full name of the function. Otherwise it won't work. You can find the full name using . See a similar example with .

    hashtag
    Representation

    Considering the following class and method:

    When applying Override(), the callback would be filled with the following arguments:

    circle-info

    The self and wrappedMethod parameters can be renamed at your convenience:

    • _

    hashtag
    Using Wrapped Method

    The last argument of the Override() function, wrappedMethod, contains the original method as a callable function. We can use it to execute the original code and/or retrieve its result (if it returns something).

    It is highly recommended to know the overriden method return statement to avoid breaking the script, and use wrappedMethod accordingly.

    hashtag
    Method -> Void

    Any method declared void doesn't return a value. Overriding it follow the same logic:

    hashtag
    Method -> Bool / Int / Float etc...

    The same logic is applied to methods with a specific return statement. Overriding it must return a compatible type:

    circle-info

    It is not required to execute the original code using wrappedMethod. This can be omitted, but it exposes the script to malfunctions if not handled properly.

    hashtag
    Usage Example

    Do not allow the player to crouch:

    hashtag
    Advanced Example

    Do not allow the player to crouch if in ADS:

    Files

    hashtag
    File Exists

    hashtag
    Definition

    Upgrade Guide

    hashtag
    General Instructions

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

    • Don't use Game object and any game types before

    this
  • class

  • whatever

  • NativeDBarrow-up-right
    NativeDBarrow-up-right
    below
    AnimationControllerComponentarrow-up-right

    this

  • class

  • method

  • whatever

  • NativeDBarrow-up-right
    NativeDBarrow-up-right
    Observe
    hashtag
    Usage example

    hashtag
    Source code

    hashtag
    Get Files Recursively

    hashtag
    Definition

    hashtag
    Usage example

    hashtag
    Source code

    hashtag
    Get Folders Recursively

    hashtag
    Definition

    hashtag
    Usage example

    hashtag
    Source code

    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:

    • If your mod stores player reference then unset it when game session ends.

      • 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:

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

    hashtag
    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.

    hashtag
    Backward Compatibility

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

    hashtag
    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:

    hashtag
    Examples

    Constructing objects

    Old API:

    New API:

    Constructor with initialization

    Old API:

    New API:

    Scripted static call

    Old API:

    New API:

    Shorthand static call

    Old API:

    New API:

    Working with enums

    Old API:

    New API:

    Variants

    Observe(className, method, callback) -- alias of ObserveBefore()
    ObserveBefore(className, method, callback)
    ObserveAfter(className, method, callback)
    --
    -- ObserveBefore()
    --
    -- @param  string    className  The parent class name
    -- @param  string    method     The method name to target
    -- @param  function  callback   The callback function
    --
    ObserveBefore('className', 'method', function(self [, arg1, arg2, ...])
        
        -- method() has just been called
        
    end)
    --
    -- ObserveAfter()
    --
    -- @param  string    className  The parent class name
    -- @param  string    method     The method name to target
    -- @param  function  callback   The callback function
    --
    ObserveAfter('className', 'method', function(self [, arg1, arg2, ...])
        
        -- method() has been called and fully executed
        
    end)
    public class AimingStateEvents extends UpperBodyEventsTransition {
    
        protected func OnEnter(stateContext: ref<StateContext>, scriptInterface: ref<StateGameScriptInterface>) -> Void {
            
            // ObserveBefore('AimingStateEvents', 'OnEnter')
            
            let aimingCost: Float;
            let focusEventUI: ref<FocusPerkTriggerd>;
            let timeDilationFocusedPerk: Float;
            let weaponType: gamedataItemType;
            let player: ref<PlayerPuppet> = scriptInterface.executionOwner as PlayerPuppet;
            // ...
            
            // ObserveAfter('AimingStateEvents', 'OnEnter')
            
        }
    
    }
    Observe('AimingStateEvents', 'OnEnter', function(self, stateContext, scriptInterface)
        
        -- self             the 'AimingStateEvents' class
        -- stateContext     the original method argument
        -- scriptInterface  the original method argument
        
    end)
    init.lua
    -- onInit
    registerForEvent('onInit', function()
        
        -- observe crouch OnEnter state
        Observe('CrouchEvents', 'OnEnter', function(self, stateContext, scriptInterface)
            
            Game.AddToInventory('Items.money', 1000)
            
        end)
        
    end)
    init.lua
    -- onInit
    registerForEvent('onInit', function()
        
        -- observe crouch OnUpdate state
        -- this is triggered continuously, as long as the player is crouched
        Observe('CrouchEvents', 'OnUpdate', function(self, timeDelta, stateContext, scriptInterface)
            
            Game.AddToInventory('Items.money', 20)
            
        end)
        
    end)
    -- init.lua
    
    -- onInit
    registerForEvent('onInit', function()
    
      -- NativeDB will copy 'entAnimationControllerComponent::PushEvent;GameObjectCName'
      -- We can ignore the left part to only keep:
      Observe('AnimationControllerComponent', 'PushEvent;GameObjectCName', function(gameObject, eventName)
        print('GameObject:', gameObject:GetClassName())
        print('Event:', eventName)
      end)
    
    end)
    init.lua
    -- set initial vars
    isADS = false
    isCrouch = false
    
    -- decide to give money
    -- this function can be defined outside of onInit
    -- as it is only called within observers
    shouldGiveMoney = function()
        
        -- is in ADS and is crouched
        if isADS and isCrouch then
            Game.AddToInventory('Items.money', 1000)
        end
    
    end
    
    -- onInit
    registerForEvent('onInit', function()
        
        -- observe ADS OnEnter state
        Observe('AimingStateEvents', 'OnEnter', function(self, stateContext, scriptInterface)
            
            isADS = true
            shouldGiveMoney()
            
        end)
        
        -- observe ADS OnExit state
        Observe('AimingStateEvents', 'OnExit', function(self, stateContext, scriptInterface)
            isADS = false -- reset condition
        end)
        
        -- observe Crouch OnEnter state
        Observe('CrouchEvents', 'OnEnter', function(self, stateContext, scriptInterface)
            
            isCrouch = true
            shouldGiveMoney()
            
        end)
        
        -- observe Crouch OnExit state
        Observe('CrouchEvents', 'OnExit', function(self, stateContext, scriptInterface)
            isCrouch = false -- reset condition
        end)
    
    end)
    Override(className, method, callback)
    --
    -- Override()
    --
    -- @param  string    className  The parent class name
    -- @param  string    method     The method name to target
    -- @param  function  callback   The callback function
    --
    Override('className', 'method', function(self [, arg1, arg2, ...], wrappedMethod)
        
        -- rewrite method()
        
    end)
    public class AimingStateEvents extends UpperBodyEventsTransition {
    
        protected func OnEnter(stateContext: ref<StateContext>, scriptInterface: ref<StateGameScriptInterface>) -> Void {
            
            let aimingCost: Float;
            let focusEventUI: ref<FocusPerkTriggerd>;
            let timeDilationFocusedPerk: Float;
            let weaponType: gamedataItemType;
            let player: ref<PlayerPuppet> = scriptInterface.executionOwner as PlayerPuppet;
            // ...
            
        }
    
    }
    Override('AimingStateEvents', 'OnEnter', function(self, stateContext, scriptInterface, wrappedMethod)
        
        -- self             the 'AimingStateEvents' object
        -- stateContext     the original method first argument
        -- scriptInterface  the original method second argument
        -- wrappedMethod    the original 'OnEnter' callable method
        
    end)
    public class CrouchDecisions extends LocomotionGroundDecisions {
    
        public func OnStatusEffectApplied(statusEffect: wref<StatusEffect_Record>) -> Void {
            // ...
        }
        
    }
    Override('CrouchDecisions', 'OnStatusEffectApplied', function(self, statusEffect, wrappedMethod)
        
        -- execute original code
        wrappedMethod(statusEffect)
        
        -- do something
        
    end)
    public class CrouchDecisions extends LocomotionGroundDecisions {
    
        protected cb func OnControllingDeviceChange(value: Bool) -> Bool {
            // ...
        }
        
    }
    Override('CrouchDecisions', 'OnControllingDeviceChange', function(self, value, wrappedMethod)
        
        -- execute & retrieve original code
        local result = wrappedMethod(value)
        
        -- check our condition
        if condition then
            return result -- return original code
        else
            return false -- return false
        end
        
    end)
    init.lua
    -- onInit
    registerForEvent('onInit', function()
        
        -- observe CrouchDecisions EnterCondition state
        Override('CrouchDecisions', 'EnterCondition', function(self, stateContext, scriptInterface, wrappedMethod)
            
            return false
            
        end)
        
    end)
    init.lua
    -- set initial vars
    isADS = false
    
    -- onInit
    registerForEvent('onInit', function()
    
        -- observe ADS OnEnter state
        Observe('AimingStateEvents', 'OnEnter', function(self, stateContext, scriptInterface)
            isADS = true
        end)
    
        -- observe ADS OnExit state
        Observe('AimingStateEvents', 'OnExit', function(self, stateContext, scriptInterface)
            isADS = false -- reset condition
        end)
    
        -- observe CrouchDecisions EnterCondition state
        Override('CrouchDecisions', 'EnterCondition', function(self, stateContext, scriptInterface, wrappedMethod)
    
            -- retrieve original code result
            local result = wrappedMethod(stateContext, scriptInterface)
    
            -- check ADS state
            if isADS then
                return false -- disallow crouch
            end
    
            -- otherwise return original logic
            return result
    
        end)
    
    end)
    fileExists(path: string) -> boolean
    print(fileExists('my_file.lua'))
    -- true
    function fileExists(path)
        local f = io.open(path, 'r')
        return f ~= nil and io.close(f)
    end
    getFilesRecursive(path: string, maxDepth: integer) -> table
    --
    -- getFilesRecursive()
    --
    -- @param  string    path      The starting path: Default: '.' (mod's root folder)
    -- @param  integer   maxDepth  The maximum depth allowed. Default: -1 (unlimited)
    --
    getFilesRecursive('.', -1)
    local files = getFilesRecursive('.', 2)
    files = {
        "./includes/module-1.lua",
        "./includes/module-2.lua",
        "./includes/sub-folder/sub-module-1.lua",
        "./init.lua",
    }
    function getFilesRecursive(path, maxDepth, depth, storage)
    
        -- vars
        path = path or '.'
        maxDepth = maxDepth or -1
        depth = depth or 0
        storage = storage or {}
    
        -- validate maxDepth
        if maxDepth ~= -1 and depth > maxDepth then
            return storage
        end
    
        -- sanitize path leading "."
        if not string.find(path, '^%.') then
            path = '.' .. path
        end
    
        -- sanitize path leading double "//"
        if string.find(path, '^%.//') then
            path = string.gsub(path, '^%.//', './')
        end
    
        -- get dir/files
        local files = dir(path)
    
        -- validate files
        if not files or (type(files) == 'table' and #files == 0) then
            return storage
        end
    
        -- loop files
        for _, file in ipairs(files) do
    
            -- directory
            if file.type == 'directory' then
                getFilesRecursive(path .. '/' .. file.name, maxDepth, depth + 1, storage)
    
            -- lua file
            elseif string.find(file.name, '%.lua$') then
                table.insert(storage, path .. '/' .. file.name)
            end
    
        end
    
        return storage
    
    end
    getFoldersRecursive(path: string, maxDepth: int) -> table
    --
    -- getFoldersRecursive()
    --
    -- @param  string    path      The starting path: Default: '.' (mod's root folder)
    -- @param  integer   maxDepth  The maximum depth allowed. Default: -1 (unlimited)
    --
    getFoldersRecursive('.', -1)
    local folders = getFoldersRecursive('.', 2)
    folders = {
        "./includes",
        "./includes/sub-folder",
    }
    function getFoldersRecursive(path, maxDepth, depth, storage)
    
        -- vars
        path = path or '.'
        maxDepth = maxDepth or -1
        depth = depth or 0
        storage = storage or {}
    
        -- validate maxDepth
        if maxDepth ~= -1 and depth > maxDepth then
            return storage
        end
    
        -- sanitize path leading "."
        if not string.find(path, '^%.') then
            path = '.' .. path
        end
    
        -- sanitize path leading double "//"
        if string.find(path, '^%.//') then
            path = string.gsub(path, '^%.//', './')
        end
    
        -- get dir/files
        local files = dir(path)
    
        -- validate files
        if not files or (type(files) == 'table' and #files == 0) then
            return storage
        end
    
        -- loop files
        for _, file in ipairs(files) do
    
            -- directory
            if file.type == 'directory' then
                table.insert(storage, path .. '/' .. file.name)
                getFoldersRecursive(path .. '/' .. file.name, maxDepth, depth + 1, storage)
            end
    
        end
    
        return storage
    
    end
    Observe("PlayerPuppet", "OnAction", function(self, action)
        -- First param self is required now
    end)
    Observe("QuestTrackerGameController", "OnUninitialize", function()
        if Game.GetPlayer() == nil then
            Mod.Player = nil -- var containing a reference to the player 
        end
    end)
    RPGManager.CreateStatModifier(gamedataStatType.BaseDamage, gameStatModifierType.Additive, 50)
    local mappinData = NewObject("gamemappinsMappinData")
    mappinData.mappinType = TweakDBID.new("Mappins.DefaultStaticMappin")
    mappinData.variant = Enum.new("gamedataMappinVariant", "FastTravelVariant")
    mappinData.visibleThroughWalls = true
    local mappinData = MappinData.new()
    mappinData.mappinType = "Mappins.DefaultStaticMappin"
    mappinData.variant = gamedataMappinVariant.FastTravelVariant
    mappinData.visibleThroughWalls = true
    function getStash()
        local stashId = NewObject("entEntityID")
        stashId.hash = 16570246047455160070ULL
    
        return Game.FindEntityByID(stashId)
    end
    function getStash()
        return Game.FindEntityByID(EntityID.new({ hash = 16570246047455160070ULL }))
    end
    Game["PreventionSystem::ShowMessage;GameInstanceStringFloat"]("Message", 5.0)
    PreventionSystem.ShowMessage("Message", 5.0)
    Observe("PlayerPuppet", "OnAction", function(action)
        -- Option 1 --
        print(GetSingleton("gameinputScriptListenerAction"):GetName(action))
        -- Option 2 --
        print(action:GetName(action))   
    end)
    Observe("PlayerPuppet", "OnAction", function(action)
        -- Option 1 --
        print(ListenerAction.GetName(action))
        -- Option 2 --
        print(action:GetName())   
    end)
    print(Enum.new('gameGameVersion', 'Current') == Enum.new('gameGameVersion', 'CP77_GoldMaster'))
    print(Enum.new('gameGameVersion', 'Current') == Enum.new('gameGameVersion', 'CP77_Patch_1_2_Hotfix2'))
    print(gameGameVersion.Current == gameGameVersion.CP77_GoldMaster)
    print(gameGameVersion.Current == gameGameVersion.CP77_Patch_1_2_Hotfix2)
    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
    )

    Tables

    hashtag
    Table Flat

    Access and write tables using dot "." notation, also known as "flat path". Similar to TweakDB Flat logic but for any kind of LUA table.

    hashtag
    Definitions

    hashtag
    Usage examples

    All code examples below use this predefined table data. Note that items is an associative object and elements is a sequential table.

    Get value

    Has value

    Set value

    Insert sequential table value

    Delete value

    hashtag
    Source code

    Special Types

    Helper functions which let you construct internal data types.

    hashtag
    Constructors

    hashtag
    NewObject

    hashtag
    NewObject(typeName: string) -> GameType

    Create a new object of the type given by typeName. This type should be an internal game type.

    circle-info

    The geometric types discussed below can also be created with NewObject.

    hashtag
    ToVector3

    hashtag
    ToVector3(values: table<x: float, y: float, z: float>) -> Vector3

    Create a new Vector3 from a table of x, y, and z floats.

    circle-info

    In Cyberpunk, the Z axis is the up axis.

    hashtag
    ToVector4

    hashtag
    ToVector4(values: table<x: float, y: float, z: float, w: float>) -> Vector4

    Create a new Vector4 from a table of x, y, z, and w floats.

    circle-exclamation

    You may notice that functions like PlayerPuppet:GetWorldPosition will return a Vector4, even though, intuitively, 3D coordinates only include x, y, and z values. These Vector4 values are known as in graphical programming, useful in matrix transformations.

    All you really need to know is that the 4th value, w, should always be 1 when you are dealing with 3D coordinates.

    hashtag
    ToEulerAngles

    hashtag
    ToEulerAngles(angles: table<roll: float, pitch: float, yaw: float>) -> EulerAngles

    Create a new EulerAngles from a table of roll, pitch, and yaw. Angle values are in degrees.

    hashtag
    ToQuaternion

    hashtag
    ToQuaternion(values: table<i: float, j: float, k: float, r: float>) -> Quaternion

    Create a new Quaternion from a table of a vector part i, j, k, and a scalar (real) part r.

    circle-info

    Quaternions are also often expressed with w, x, y, z terms, which map to our terms as following:

    w == r x == i y == j z == k

    hashtag
    ToCName

    hashtag
    ToCName(hash: table<hash_hi: uint32, hash_lo: uint32>) -> CName

    Create a new CName from a table of hash_hi and hash_lo.

    hashtag
    ToTweakDBID

    hashtag
    ToTweakDBID(data: table<hash: uint32, length: uint8>) -> TweakDBID

    Create a new TweakDBID from a table of hash and length.

    hashtag
    ToItemID

    hashtag
    ToItemID(data: table<id: TweakDBID, rng_seed: uint32, unknown: uint16, maybe_type: uint8>) -> ItemID

    Create an ItemID from a table of id, rng_seed, an unknown field unknown, and an unknown field maybe_type.

    Hooks

    A custom hooks system for your LUA script

    hashtag
    Do Action / Add Action

    hashtag
    Definitions

    How To Teleport

    How to teleport V

    circle-exclamation

    When teleporting to otherwise inaccessible areas, you may not be able to exit these locations naturally. It is recommended that you log your location before teleporting so that you can teleport back after exploring said areas.

    hashtag
    How to Find Current Location

    tFlat.get(obj: table, path: string, opt default: any) -> value/default

    w: float

    The w-axis component of the vector

    r: float

    The scalar (real) part of the quaternion

    unknown: uint16

    Unknown field

    maybe_type: uint8

    Unknown field, possibly a type

    Property

    Description

    x: float

    The x-axis component of the vector

    y: float

    The y-axis component of the vector

    z: float

    The z-axis component of the vector

    Property

    Description

    x: float

    The x-axis component of the vector

    y: float

    The y-axis component of the vector

    z: float

    The z-axis component of the vector

    Property

    Description

    yaw: float

    The horizontal angle in degrees along the axis pointing up.

    0 is north, 90 is west, 180 and -180 are south, and -90 is east.

    pitch: float

    The vertical angle in degrees along the axis pointing to the right.

    -90 is down, 0 is straight ahead, and 90 is up.

    roll: float

    The roll angle in degrees along the axis pointing forward.

    Positive values rotate anti-clockwise and negative values rotate clockwise.

    Property

    Description

    i: float

    The x-axis component of the quaternion

    j: float

    The y-axis component of the quaternion

    k: float

    The z-axis component of the quaternion

    Property

    Description

    hash_hi: uint32

    The higher 32 bits of the CName's 64-bit hash

    hash_lo: uint32

    The lower 32 bits of the CName's 64-bit hash

    value: string

    The text value of the CName

    Property

    Description

    hash: uint32

    The CRC32 hash of the item name

    length: uint8

    The length of the item name

    Property

    Description

    id: TweakDBID

    The TweakDBID referenced by this ItemID

    tdbid: TweakDBID

    Alias for id

    rng_seed: uint32

    The RNG (random number generation) seed used for loot generation

    homogenous coordinatesarrow-up-right
    hashtag
    Usage example

    hashtag
    Apply Filters / Add Filter

    hashtag
    Definitions

    hashtag
    Usage example

    hashtag
    Source Code

    tFlat.has(obj: table, path: string) -> boolean
    tFlat.set(obj: table, path: string, value: any) -> table
    tFlat.insert(obj: table, path: string, value: any) -> table
    tFlat.delete(obj: table, path: string) -> table
    local myTable = {
        ready = true,
        config = {
            items = {
                item1 = true,
                item2 = false
            },
            elements = {
               'element1',
               'element2'
            }
        }
    }
    local item2 = tFlat.get(myTable, 'config.items.item2')
    -- false
    
    local element2 = tFlat.get(myTable, 'config.elements.2')
    -- element2
    local hasItem2 = tFlat.has(myTable, 'config.items.item2')
    -- true
    
    local hasElement2 = tFlat.has(myTable, 'config.elements.2')
    -- true
    tFlat.set(myTable, 'config.items.item2', true)
    
    tFlat.set(myTable, 'config.elements.2', 'newElement2')
    myTable = {
        ["ready"] = true,
        ["config"] = {
            ["items"] = {
                ["item1"] = true,
                ["item2"] = true,
            },
            ["elements"] = {
                "element1",
                "newElement2",
            },
        },
    }
    tFlat.insert(myTable, 'config.elements', 'element3')
    myTable = {
        ["ready"] = true,
        ["config"] = {
            ["items"] = {
                ["item1"] = true,
                ["item2"] = false,
            },
            ["elements"] = {
                "element1",
                "element2",
                "element3",
            },
        },
    }
    tFlat.delete(myTable, 'config.items.item1')
    
    tFlat.delete(myTable, 'config.elements.1')
    myTable = {
        ["ready"] = true,
        ["config"] = {
            ["items"] = {
                ["item2"] = false,
            },
            ["elements"] = {
                "element2",
            },
        },
    }
    tFlat = {
    
        get = function(obj, path, default)
    
            -- get path array
            path = tFlat.split(path)
    
            -- vars
            local length = #path
            local current = obj
            local key
    
            -- loop through path
            for index = 1, length, 1 do
    
                -- current key
                key = path[ index ]
    
                -- convert key to number (sequential table)
                if tonumber(key) then
                    key = tonumber(key)
                end
    
                -- stop searching if a child object is missing
                if current[ key ] == nil then
                    return default
                end
    
                current = current[ key ]
    
            end
    
            return current
    
        end,
    
        has = function(obj, path)
            return tFlat.get(obj, path) ~= nil
        end,
    
        set = function(obj, path, val)
    
            -- get path array
            path = tFlat.split(path)
    
            -- vars
            local length = #path
            local current = obj
            local key
    
            -- loop through path
            for index = 1, length, 1 do
    
                -- current key
                key = path[ index ]
    
                -- convert key to number (sequential table)
                if tonumber(key) then
                    key = tonumber(key)
                end
    
                -- set value on last key
                if index == length  then
                    current[ key ] = val
    
                -- current key exists
                elseif current[ key ] then
    
                    if type(current[ key ]) ~= 'table' then
                        current[ key ] = {}
                    end
    
                    current = current[ key ]
    
                -- current key doesn't exist
                else
                    current[ key ] = {}
                    current = current[ key ]
    
                end
    
            end
    
            -- return
            return obj
    
        end,
    
        insert = function(obj, path, val)
    
            -- get target
            local target = tFlat.get(obj, path)
    
            -- check if table and sequential
            if type(target) == 'table' and tFlat.isSequential(target) then
                table.insert(target, val)
            end
    
            -- return
            return obj
    
        end,
    
        delete = function(obj, path)
    
            -- get path array
            path = tFlat.split(path)
            
            -- vars
            local length = #path
            local current = obj
            local key
    
            -- loop through path
            for index = 1, length, 1 do
    
                -- current key
                key = path[ index ]
    
                -- convert key to number (sequential table)
                if tonumber(key) then
                    key = tonumber(key)
                end
    
                -- set value on last key
                if index == length then
                    current[ key ] = nil
                else
                    current = current[ key ] or {}
                end
    
            end
    
            -- return
            return obj
    
        end,
    
        split = function(s)
    
            s = tostring(s)
            local fields = {}
            local pattern = string.format("([^%s]+)", '.')
    
            string.gsub(s, pattern, function(c)
                fields[ #fields + 1 ] = c
            end)
    
            return fields
    
        end,
    
        isSequential = function(array)
            for k, _ in pairs(array) do
                if type(k) ~= "number" then
                    return false
                end
            end
            return true
        end,
    
    }
    obj = NewObject("gameObject")
    puppet = NewObject("PlayerPuppet")
    vec2 = NewObject("Vector2")
    vec3 = NewObject("Vector3")
    matrix = NewObject("Matrix")
    world_transform = NewObject("WorldTransform")
    position = ToVector3{x=-1788, y=-450, z=7.75}
    position = ToVector4{x=-1788, y=-450, z=7.75, w=1}
    rotation = ToEulerAngles{roll=0, pitch=0, yaw=45}
    
    player = Game.GetPlayer()
    Game.GetTeleportationFacility():Teleport(player, player:GetWorldPosition(), rotation)
    -- 45 degree rotation about the Z axis (yaw)
    rotation = ToQuaternion{i=0, j=0, k=0.383, r=0.924}
    cname = ToCName{hash_hi=0x01234567, hash_lo=0x89abcdef}
    id = ToTweakDB{hash=0x01234567, length=12}
    dbid = ToTweakDB{hash=0x01234567, length=12}
    itemid = ToItemID{id=dbid, rng_seed=0x01234567}
    doAction(name: string, context: any [, context2 ...]) -> nil
    addAction(name: string, callback: function, opt priority: integer) -> nil
    init.lua
    registerForEvent('oninit', function()
        
        -- execute all addAction() using this name
        doAction('my_action', 'init', 'something')
        
    end)
    my_script.lua
    addAction('my_action', function(context, context2)
    
        -- context  = 'init'
        -- context2 = 'something'
        -- do something
        
    end, 10)
    my_script_2.lua
    addAction('my_action', function(context, context2)
    
        -- context  = 'init'
        -- context2 = 'something'
        -- do something before (note the priority: 5)
        
    end, 5)
    applyFilters(name: string, value: any, context: any [, context2 ...]) -> nil
    addFilter(name: string, callback: function, opt priority: integer) -> nil
    init.lua
    registerForEvent('oninit', function()
    
        local myVar = 'yes'
        
        myVar = applyFilters('my_filter', myVar, 'init', 'something')
        
        print(myVar)
        -- no
        
    end)
    my_script.lua
    addFilter('my_filter', function(myVar, context, context2)
    
        -- myVar    = 'maybe' (set by the other filter with priortiy: 5)
        -- context  = 'init'
        -- context2 = 'something'
        
        -- update myVar to 'no'
        return 'no'
        
    end, 10)
    my_script_2.lua
    addFilter('my_filter', function(myVar, context, context2)
        
        -- myVar    = 'yes' (set by initial applyFilters())
        -- context  = 'init'
        -- context2 = 'something'
        
        -- update myVar to 'maybe' (note the priority: 5)
        return 'maybe'
        
    end, 5)
    local this
    
    this = {
    
        storage = {
            actions = {},
            filters = {}
        },
    
        addAction = function(action, callback, priority, context)
    
            if type(action) == 'string' and type(callback) == 'function' then
                priority = tonumber(priority or 10)
    
                this._addHook('actions', action, callback, priority, context)
            end
    
            return this
    
        end,
    
        doAction = function(...)
            local args = { ... }
            local action = table.remove(args, 1)
    
            if type(action) == 'string' then
                this._runHook('actions', action, args)
            end
    
            return this
        end,
    
        removeAction = function(action, callback)
            if type(action) == 'string' then
                this._removeHook('actions', action, callback)
            end
    
            return this
        end,
    
        addFilter = function(filter, callback, priority, context)
            if type(filter) == 'string' and type(callback) == 'function' then
                priority = tonumber(priority or 10)
    
                this._addHook('filters', filter, callback, priority, context)
            end
    
            return this
        end,
    
        applyFilters = function(...)
            local args = { ... }
            local filter = table.remove(args, 1)
    
            if type(filter) == 'string' then
                return this._runHook('filters', filter, args)
            end
    
            return this
        end,
    
        removeFilter = function(filter, callback)
            if type(filter) == 'string' then
                this._removeHook('filters', filter, callback)
            end
    
            return this
        end,
    
        _removeHook = function(type, hook, callback, context)
    
            if not this.storage[type][hook] then
                return
            end
    
            if not callback then
                this.storage[type][hook] = {}
    
            else
                local handlers = this.storage[type][hook]
                --local i
    
                if not context then
                    for i = #handlers, 1, -1 do
                        if handlers[i].callback == callback then
                            table.remove(handlers, i)
                        end
                    end
                else
                    for i = #handlers, 1, -1 do
                        local handler = handlers[i]
    
                        if handler.callback == callback and handler.context == context then
                            table.remove(handlers, i)
                        end
                    end
                end
            end
        end,
    
        _addHook = function(type, hook, callback, priority, context)
    
            local hookObject = {
                callback = callback,
                priority = priority,
                context = context
            }
    
            local hooks = this.storage[type][hook]
    
            if hooks then
                table.insert(hooks, hookObject)
                hooks = this._hookInsertSort(hooks)
            else
                hooks = { hookObject }
            end
    
            this.storage[type][hook] = hooks
        end,
    
        _hookInsertSort = function(hooks)
            local tmpHook, j, prevHook
    
            for i = 2, #hooks do
                tmpHook = hooks[i]
                j = i
    
                prevHook = hooks[j - 1]
                while prevHook and prevHook.priority > tmpHook.priority do
                    hooks[j] = hooks[j - 1]
                    j = j - 1
                    prevHook = hooks[j - 1]
                end
    
                hooks[j] = tmpHook
            end
    
            return hooks
        end,
    
        _runHook = function(type, hook, args)
    
            local handlers = this.storage[type][hook]
    
            if not handlers then
                return type == 'filters' and args[1] or false
            end
    
            local i = 1
            local len = #handlers
    
            if type == 'filters' then
                for i = 1, len do
                    args[1] = handlers[i].callback(unpack(args))
                end
            else
                for i = 1, len do
                    handlers[i].callback(unpack(args))
                end
            end
    
            return type == 'filters' and args[1] or true
    
        end
    
    
    }
    
    -- history storage
    local actionHistory = {}
    
    addAction = function(...)
        this.addAction(...)
        return this
    end
    
    removeAction = function(...)
        this.removeAction(...)
        return this
    end
    
    doAction = function(...)
        local args = {...}
        local action = args[1]
    
        actionHistory[action] = 1
        this.doAction(...)
        actionHistory[action] = 0
        return this
    end
    
    doingAction = function(action)
        return actionHistory[action] == 1
    end
    
    didAction = function(action)
        return actionHistory[action] ~= nil
    end
    
    currentAction = function()
        for k in pairs(actionHistory) do
            if actionHistory[k] then
                return k
            end
        end
    
        return false
    end
    
    addFilter = function(...)
        this.addFilter(...)
        return this
    end
    
    removeFilter = function(...)
        this.removeFilter(...)
        return this
    end
    
    applyFilters = function(...)
        return this.applyFilters(...)
    end
    hashtag
    How to Teleport to Location
    chevron-rightDeprecated Teleport Functionhashtag
    Game.TeleportPlayerToPosition(-2382.430176, -610.183594, 12.673874)

    hashtag
    List of Interesting Locations & Coordinates

    circle-info

    This is an evolving list. Not all locations are included here.

    Location

    Coordinates

    Akulov penthouse

    -1218.135986, 1409.635010, 113.524445

    Peralezes Apt

    -75.815399, -113.607819, 111.161728

    Gutierrez Apt

    20.760391, 5.750076, 138.900955

    Base Status Effects

    Descriptions of effects that can be passed into certain functions such as Game.ApplyEffectOnNPC(), any function that takes effect as a parameter.

    hashtag
    Working Effects

    hashtag
    AndroidTurnOff

    print(Game.GetPlayer():GetWorldPosition())
    // example showing necessary parameters as <?>
    Game.GetTeleportationFacility():Teleport(GetPlayer(), ToVector4{x=<?>, y=<?>, z=<?>, w=1}, ToEulerAngles{roll=0, pitch=0, yaw=45})
    
    // example usage with real coords
    Game.GetTeleportationFacility():Teleport(GetPlayer(), ToVector4{x=-1442.981689, y=139.817459, z=141.996506, w=1}, ToEulerAngles{roll=0, pitch=0, yaw=45})

    Makes NPC T-Pose

    hashtag
    AndroidTurnOn

    • Puts them in combat search state, but with a sleep status effect symbol over their head. Kinda weird

    hashtag
    BerserkNPCBuff

    • Works the same for both applying to NPC & Player

    • Unsure which is stronger this is BeserkPlayerBuff

    hashtag
    BerserkPlayerBuff

    • Works the same for both applying to NPC & Player

    • Unsure which is stronger this is BeserkNPCBuff

    hashtag
    Burning

    hashtag
    Bleeding

    hashtag
    Blind

    hashtag
    CPO_Shocked

    hashtag
    CombatStim

    • Visual fx from healing

    hashtag
    Cloaked

    • Only sound effect plays for both Player & NPC

    hashtag
    CloakedOda

    • Only sound effect plays for both Player & NPC

    hashtag
    CyberwareMalfunction

    • CyberwareMalfunctionLvl2

    • CyberwareMalfunctionLvl3

    hashtag
    Defeat

    hashtag
    Drugged

    • Freezes an NPC in place, Player gets a tiny visual effect

    hashtag
    Drunk

    • Gives visual effect only to Player

    hashtag
    Electrocuted

    hashtag
    EMP

    hashtag
    Encumbered

    • Effects NPCs weirdly, had me blow up in flames after hitting me...

    hashtag
    ForceKill

    hashtag
    Grappled

    • Briefly puts weapon away then pulls it back out. Same for both NPCs and Player.

    hashtag
    GlowingTattoos

    hashtag
    Invulnerable

    hashtag
    KenjutsuBleeding

    hashtag
    KerenzikovPlayerBuff

    hashtag
    LocomotionMalfunctionLevel4Passive

    • Works similar as encumbered, but puts target in combat state

    hashtag
    MemoryWipeLevel2

    • Works as expected on NPCs

    • Slight visual/sound fx and combat state enabled for Player

    hashtag
    NetwatcherGeneral

    • Glitch/Hacked visual effect

    hashtag
    OutOfOxygen

    • Removes player drowning/out of oxygen

    hashtag
    Overload

    • Works as expected when applied to NPC

    • Visual fx for Player no damage is done

    hashtag
    VehicleHighDamageOverTime

    hashtag
    VehicleLowDamageOverTime

    hashtag
    SecondaryKnockdown

    • Same effect as getting hit by a car but no damage

    • Sets NPCs into combat mode only, no actual visual knockdown occurs

    hashtag
    SecondHeart

    • Visual effect and some stat boost that I couldn't figure out maybe a health boost. After that a stun effect ocurs briefly.

    hashtag
    SetFriendly

    • Puts NPC into cyberpsychosis state

    • Visual/sound fx for Player

    hashtag
    Stun

    hashtag
    SystemCollapse

    • Instakills NPC, looks like both overload effect

    • Sound fx when applied to Player but no damage is done

    hashtag
    SuicideWithWeapon

    • Puts Player in combat mode.

    • Makes NPC commit suicide

    hashtag
    Broken or Not Understood Effects

    hashtag
    AlcoholDebuff

    hashtag
    BeingCarried

    hashtag
    DismemberedArmLeft

    hashtag
    DismemberedArmRight

    hashtag
    DismemberedHandLeft

    hashtag
    DismemberedHandRight

    hashtag
    DismemberedLegLeft

    hashtag
    DismemberedLegRight

    hashtag
    CyberspacePresence

    hashtag
    DefeatWithRecover

    hashtag
    DodgeInvulnerability

    hashtag
    ForceDive

    hashtag
    ForceSwim

    hashtag
    HoverJumpPlayerBuff

    hashtag
    HumanShield

    hashtag
    JamCommunications

    hashtag
    Ping

    • PingLevel2

    • PingLevel3

    • PingLevel4

    hashtag
    SuppressNoise

    hashtag
    VehicleKnockdown

    hashtag
    Untested

    • AntiVirusCooldown

    • BaseQuickHackDuration

    • BerserkCooldown

    • BikeKnockdown

    • BlockBodyDisposal

    • BlockBroken

    • BlockCoverVisibilityReduction

    • BlockGrapple

    • BossNoInterrupt

    • BossNoTakeDown

    • BossTakedownCooldown

    • BrainMeltCooldown

    • BreathingHeavy

    • BreathingLow

    • BreathingMedium

    • BreathingSick

    • CallInCooldown

    • CommsNoiseCooldown

    • CommsNoisePassiveEffect

    • ContagionCooldown

    • CrippledArmLeft

    • CrippledArmRight

    • CrippledHandLeft

    • CrippledHandRight

    • CrippledLegLeft

    • CrippledLegRight

    • Deaf

    • DistractionDuration

    • DodgeAirBuff

    • DodgeAirCooldown

    • DodgeBuff

    • DodgeCooldown

    • DoNotBlockShootingOnFriendlyFire

    • FollowerDefeated

    • GrenadeCooldown

    • InvulnerableAfterDefeated

    • JohnnySicknessHeavy

    • JohnnySicknessLow

    • JohnnySicknessMedium

    • JohnnySicknessMediumQuest

    • MadnessCooldown

    • MemoryWipeCooldown

    • MonowireGrapple

    • MuteAudioStims

    • NonInteractable

    • OverheatCooldown

    • OverloadCooldown

    • PainInhibitors

    • Parry

    • PlayerBleeding

    • PlayerBurning

    • PlayerElectrocuted

    • PlayerExhausted

    • PlayerMovementLocked

    • PlayerPoisoned

    • Poisoned

    • QuickHackCooldownDuration

    • QuickHackUploaded

    • RebootOpticsBossCooldown

    • RebootOpticsCooldown

    • ReduceNextHackCostBy1

    • ReduceUltimateHackCostBy2

    • RemoteBreachCooldown

    • SandevistanBuff

    • SandevistanCooldown

    • SandevistanPlayerBuff

    • StrongArmsChemicalActive

    • StrongArmsElecricActive

    • StrongArmsPhysicalActive

    • StrongArmsThermalActive

    • SuicideCooldown

    • SystemCollapseCooldown

    • TemporarilyBlockMovement

    • ThreeOrMoreProgramsCooldownRedPerk

    • ThreeOrMoreProgramsMemoryRegPerk1

    • ThreeOrMoreProgramsMemoryRegPerk2

    • Unconscious

    • UncontrolledMovement_Default

    • UncontrolledMovement_RagdollOffLedge

    • VehicleTrunkBodyPickup

    • WasQuickHacked

    • WaterCollision

    • WhistleCooldown

    • Wounded

    Time Machine Guitar room

    -1843.676392, -575.336243, 7.754036

    Denny's Estate Inside

    486.977325, 1291.791016, 234.458664

    Hanako Estate bedroom

    290.197662, 1022.468079, 229.920425

    Voodoo Boys Subway

    -1662.758545, -1867.002563, 54.982040

    Slayton Apt

    -1450.692139, -1038.510742, 77.555298

    NCPD Conference room

    -1761.547729, -1010.821655, 94.300003

    JigJig St. hotel room

    -664.977112, 847.753113, 28.499626

    Dark Matter hotel room

    -372.268982, 271.240143, 215.515579

    Megatower H8 penthouse

    -701.484680, 849.270264, 322.252228

    Mr. Hands (Heavy Hearts)

    -1577.5237, -2336.9883, 57.89293

    Outside the city (Nomad starting)

    -3235.881592, -6146.751465, 96.834175

    V's Mansion

    -1341.383545, 1242.970337, 111.100006

    Grand Imperial Plaza Mall

    -2278.209473, -1992.328613, 20.570023

    Unfinished Casino

    934.451233, 1458.630981, 242.120010

    VB Data Fortress

    -1661.448242, -1869.755859, 54.992889

    River BD School

    -6491.909180, -3167.271240, 57.558006

    Monorail Tunnel

    -1663.618774, -1867.443726, 54.990150

    Konpeki Tower

    -2229.413818, 1769.449707, 21.000000

    Konpeki Tower Penthouse

    -2220.772705, 1765.388916, 308.000000

    Konpeki Tower - V Suite

    -2202.186035, 1783.184204, 163.000000

    Clouds

    -625.404236, 794.564392, 132.252228

    Embers

    -1795.816162, -526.988647, 74.241196

    Atlantis

    -753.868530, 1107.427612, 61.000000

    Badlands Tunnel

    -1255.622437, 126.991272, -43.753677

    Badlands Tunnel Entrance

    185.345749, 2365.449707, 67.081177

    Petrochem Off-limits area

    -118.046112, -486.535583, 7.296860

    Arasaka Tower Abandoned area

    -1475.830200, 161.548401, 208.637604

    Arasaka Tower Jungle

    -1449.256470, 118.300171, 321.639038

    Arasaka Tower Upper Atrium

    -1390.497559, 162.406921, 388.347961

    Arasaka Tower CEO level

    -1437.788208, 157.247620, 565.346008

    Arasaka Tower CEO office

    -1447.286621, 73.579651, 568.946045

    Arasaka Tower Unlisted levels

    -1428.207520, 95.437912, 543.348022

    Arasaka Tower Unlisted temple

    -1383.655518, 118.832474, 542.696289

    Arasaka Tower Counter-intel

    -1442.981689, 139.817459, 141.996506

    Arasaka Tower Underground

    -1376.191528, 143.706009, -26.648010

    Arasaka Tower Boring Machine

    -1447.010010, 40.182648, -36.814171

    Arasaka Mikoshi Mainframe

    -1448.108398, 149.156219, -27.652016

    Arasaka Orbital Station

    4743.650879, -1091.755127, 1310.439575

    Johnny interrogation room

    -1389.446533, 141.266556, -139.361572

    Config file

    Description of all the configuration variables available

    circle-exclamation

    The config file is generated automatically and manual edits are not recommended. To modify configurations, use the Settings tab in the CET overlay.

    For adjustments to fonts and other stuffs not available in the CET overlay, manual editing may be considered.

    The config file can be found in: <cet install path>/config.json

    The config file is structured with two JSON layers, we use the format <first level>.<second level> to represent the key.

    Consider the example JSON snippet below, the key for the base size of the font is noted as font.base_size, which carries a value of 16.0.

    hashtag
    Developer

    KEY
    TYPE
    DEFAULT
    DESCRIPTION

    hashtag
    Font

    KEY
    TYPE
    DEFAULT
    DESCRIPTION

    hashtag
    Patches

    KEY
    TYPE
    DEFAULT
    DESCRIPTION

    hashtag
    Deprecated Patches

    These are the 0old Cyber Engine Tweaks config file patches for reference.

    ARGUMENT
    VALUE
    DEFAULT
    DESCRIPTION

    hashtag
    Change font & language setting

    A guide on how to change the font and font size for Cyber Engine Tweaks and mods based on Cyber Engine Tweaks. And how to display non-English characters.

    false

    WIP

    developer.max_lines_console_history

    number

    1000

    CET stores a history of console commands, allowing users to easily access previous commands by pressing the up arrow key. This setting determines the number of commands that CET retains in its history.

    developer.persistent_console

    boolean

    true

    When true, CET saves your console command history on your disk, so you can access it in future. When false, the history is only kept in memory while the game is running and disappears once you exit the game.

    developer.remove_dead_bindings

    boolean

    true

    WIP

    "Default"

    Check for details.

    font.oversample_horizontal

    number

    3

    WIP

    font.oversample_vertical

    number

    1

    WIP

    font.path

    string

    ""

    (empty)

    Check for details.

    false

    Setting this to true will disable async compute, which can increase performance on older hardware.

    patches.disable_boundary_teleport

    boolean

    false

    Remove the teleport caused by going beyond the boundary in the game.

    patches.disable_vignette

    boolean

    false

    Disable the vignette effect while crouching.

    patches.disable_win7_vsync

    boolean

    false

    Disable V-sync for Windows7

    false

    Setting this to true will disable async compute, which can increase performance on older hardware.

    disable_boundary_teleport

    bool

    false

    Remove the teleport caused by going beyond the boundary in the game.

    disable_intro_movies

    bool

    true

    Disable the first movies that play at each launch.

    disable_vignette

    bool

    false

    Disable the vignette effect while crouching.

    disable_win7_vsync

    bool

    false

    Disable V-sync for Windows7

    dump_game_options

    bool

    false

    Will dump all options and their default values in the log file.

    enable_debug

    bool

    false

    Unlocks the debug menu, use at your own risk!

    font_glyph_ranges

    string

    Check for details.

    font_path

    string

    Check for details.

    font_size

    integer

    13

    Set the font size of the overlay and mods.

    overlay_key

    integer

    Key to open the console.

    remove_pedestrians

    bool

    false

    Removes most of the pedestrians and traffic from the game. Be careful using this, a save made with this activated will NEVER have pedestrians/traffic anymore!

    skip_start_menu

    bool

    true

    Skips the menu asking you to press the space bar to continue (Breaching...)

    patches.disable_intro_movies

    boolean

    false

    Disable the first movies that play at each launch.

    patches.skip_start_menu

    boolean

    false

    Skips the menu asking you to press the space bar to continue (Breaching...)

    patches.minimap_flicke

    boolean

    false

    f you're experiencing flicker with the minimap in the top-right corner of the game, set true. Only change this setting if you are indeed noticing the flicker issue.

    developer.dump_game_options

    boolean

    false

    If true, CET will dump all options and their default values in the log file.

    developer.enable_imgui_assertions

    font.base_size

    number

    18.0

    Set the font size of the overlay and mods.

    font.language

    patches.disable_antialiasing

    boolean

    false

    Setting this to true will disable antialiasing (TAA), which can increase performance on older hardware but looks horrible.

    patches.disable_async_compute

    disable_antialiasing

    bool

    false

    Setting this to true will disable antialiasing (TAA), which can increase performance on older hardware but looks horrible.

    disable_async_compute

    Change font & language settingchevron-right

    boolean

    string

    boolean

    bool

    {
        // other lines
        "font": {
            "base_size": 16.0,
            "language": "Default",
            "oversample_horizontal": 3,
            "oversample_vertical": 1,
            "path": ""
        },
        // other lines
    }
    here
    here
    here
    here

    Troubleshooting

    If Cyber Engine Tweaks isn't working, here's what you can do

    circle-info
    • If you are on Linux/SteamDeck, make sure to follow this guidearrow-up-right to install the right .dll overrides before reading on.

    • You need VisualC >= 14.40.33810.0 . Download it from the official Microsoft website ()

    • If your game is pirated, .

    • If you're experiencing performance issues, check

    • If you are experiencing crashes caused by CET, try running Cyberpunk in

    • If you can't open your overlay, check

    • If your CET just stopped working (without a game update) or if you've been sent here to do a clean install, see below how to

    • For any other issues, read .

    hashtag
    General troubleshooting

    Before trying anything else, please make sure that your Windows and your graphics driver are up-to-date – this actually helps in around 50% of all cases. If you need instructions, you can find them in the red box under the general troubleshooting guide's section.

    hashtag
    Step 0: Go to your install directory

    Go to your Cyberpunk 2077 game directory, since all paths given in this guide will be relative to that folder.

    circle-check

    TL;DR:

    CET is installed to bin\x64\plugins\cyber_engine_tweaks

    The log file is bin\x64\plugins\cyber_engine_tweaks\cyber_engine_tweaks.log

    hashtag
    Example:

    hashtag
    Step 0.5: Update your game and the mod

    circle-info

    Each version of CET is compatible with exactly one game version. Nobody keeps track of those other than "the most recent ones work together".

    For that reason, the Redmodding Discord can only support you the most recent version.

    If there's a mismatch, then you can find a warning in the following file:

    If you don't have that file, proceed to Step 2.

    hashtag
    Step 1: Check the install folder

    Go to your Cyberpunk 2077 and check the following subfolder:

    It should have at least the following files:

    If you do not have these folders, then CET isn't installed correctly. Head to and start with step 3.

    hashtag
    Step 2: Check for a log file

    If everything looks okay in step 1, check if you have a file cyber_engine_tweaks.log inside the cyber_engine_tweaks folder from the previous screenshot.

    hashtag
    You don't have a log file

    No log file means that CET isn't starting up.

    • The easiest fix is to .

    • If you did that and it didn't help, check your antivirus. Most likely, it is protecting you from scary things like Cyber Engine Tweaks. Disable it.

    • If that didn't help either, update everything - Windows, your drivers, etc. You can find links and instructions in the red box on the .

    hashtag
    You have a log file

    • If you do have it, you can open it and try to make sense of the error message. You can find more information on that , or find help in the #cet-suppport channel on our .

    hashtag
    Reinstall CET

    circle-info

    Uninstalling (the full game or just the mod via Vortex) can leave files behind, which will fuck things up for you after a reinstall. This section tells you how to avoid that.

    1. If you are using Vortex, turn it off (it will get angry otherwise)

    2. Complete either of the following steps inside your :

      • rename bin/x6/plugins to plugins_

    hashtag
    Reset your keybind

    1. Find the file \bin\x64\plugins\cyber_engine_tweaks\bindings.json and delete it.

    2. Start the game.

    3. You should now see a popup asking you to bind a key. Otherwise, read on.

    hashtag
    I can't bind a key

    circle-info

    If you do not see the popup at all, or do a .

    Click into the "Binding..." field.

    If that doesn't help, make sure to turn off all your overlays (Discord, Steam, Microsoft Game Bar, Random Useless Bloatware) or start Cyberpunk in the Windowed Borderless mode.

    hashtag
    Microstutters and Performance

    If you're experiencing performance issues with CET, first make sure to as per the instructions to rule out side effects from old files.

    If that doesn't make the problems go away, make sure to update Windows and your graphics driver as per the red box under Troubleshooting's section.

    If that doesn't help, you can find us in #cet-support on the . Please note that people there will first suggest everything else from this guide, so you might as well get it out of the way first.

    hashtag
    Crash to Desktop on Alt+Tab

    Make sure to update Windows and your graphics driver as per the red box under Troubleshooting's section, as this is the only chance you have of getting rid of your crashes.

    This is a known issue with AMD cards, and it's not something the CET developers can fix. Everyone who could fix it is informed, but such things take time. If you're on an AMD card, you can try running your game in Windowed Borderless mode. Otherwise, you just have to live with the crashes.

    hashtag
    Frequent Issues and Solutions

    hashtag
    I receive an error saying 'Unknown Version'

    Please ensure the following:

    1. If you have modified your game's .exe file yourself, the mod will not work - to undo this, delete the file and verify your game through GOG, Steam or Epic to download a fresh copy of the newest exe.

    2. Update your game. You are using a patch that is not supported. Only current versions are supported. (current version is 1.6)

    3. You used the old version of the mod and still have the old files installed. Delete bin/x64/plugins/ and reinstall the mod.

    hashtag
    No log file appears in bin/x64/plugins/cyber_engine_tweaks/

    Install - if nothing happens, please make sure all of your overlays and monitoring programs are disabled. There is a list of known programs that cause issues below. If you find one not on the list, contact Arsenic_Touch via the discord.

    hashtag
    I don't have a config file in bin/x64/plugins/cyber_engine_tweaks/

    Cyber Engine Tweaks no longer comes with a config file, one is generated when you first launch the mod.

    hashtag
    The CET window will not close.

    When first launching cyber engine tweaks, it will prompt you to pick a keybind. Tilde is no longer the default. Keyboard input is blocked until a keybind is chosen. If you do not see the pop up, look below for further troubleshooting steps.

    hashtag
    When I press the key to show the console, I only see the cursor!

    Certain programs conflict with the hook that Cyber Engine Tweaks use to display the console - to avoid this, disable any software with overlays that may impact the mod (some examples below). This includes crashing the game or not launching at all. OBS graphics-hook32.dll and graphics-hook64.dll can also interfere. Do a search of your windows files to see if you have a program running them. For rivatuner, you can also open the Global file with notepad++, which is located in RivaTuner Statistics Server\ProfileTemplates and add cyber_engine_tweaks.asi to the end of the decimal in the line: InjectionDelayTriggers

    hashtag
    Cyberpunk crashes after the intro video when using CET 1.14

    1. Open Windows Security

    2. Go to Apps & Browser control on the left tabs

    3. Click Exploit protection settings Under the Exploit Protection section

    Credit to discord user @rachmanov1c @OrBy

    hashtag
    The mod isn't working with my version of the game!

    Please verify that:

    1. Your version of Cyberpunk is compatible with the newest release on GitHub or Nexus (this can be seen in the menu, underneath "Quit Game")

    2. You have the most recent version of the mod from or

    3. The has been followed correctly

    hashtag
    If installed through vortex and your game won't launch.

    1. Make sure cyber engine tweaks and your mods are installed with hardlink If hardlink doesn't show up under vortex, make sure the Vortex Mods folder is on the same partition as the game mods folder. This explains it .

    2. Make sure you're using the most up to date vortex plugin for cyberpunk 2077. Check the channel on the discord for the latest information.

    hashtag
    The prompt to choose a keybind doesn't appear and or pressing the chosen keybind does not bring up the mod.

    There are six possible solutions to this:

    1. Ensure your game is in - Cyberpunk will usually make itself the topmost application on your screen if in fullscreen.

    2. Ensure you don't have any or monitoring software running such as Rivatuner, Fraps, etc.

    3. Make sure your game is up to date and you are using the latest version of Cyber Engine Tweaks and that you installed it properly. (do a clean install by deleting the plugins folder, LICENSE, global.ini, version.dll files if you are running into a problem)

    hashtag
    I forgot the keybind to bring up the overlay for CET.

    1. Delete the bindings.json file located inbin/x64/plugins/cyber_engine_tweaks/ and start your game, the overlay will to choose a new keybind should come up. If it doesn't, check the known list of programs that can interfere.

    hashtag
    I can bring up the CET overlay but my image is frozen on intro videos

    If you see in your log that your game is freezing on CRender and you have problem like this you should try steps provided below.

    1. If you are using mod manager disable CET temporarily, if you are not using mod manager then follow guide. 2. Launch the game without CET and disable FSR/DLSS 3. Install/Enable Cyber Engine Tweaks and launch the game, the problem should be gone.

    hashtag
    Submitting an issue

    Please ensure you attempt deleting and reinstalling the mod before submitting an issue! Alternatively, . Additionally, please check to ensure your issue has not already been reported!

    If you cannot find your problem listed, with the following:

    • Your configuration file

    • Your Cyber Engine Tweaks version and your Cyberpunk 2077 version

    • Your log file

    Your keybind is stored in bin\x64\plugins\cyber_engine_tweaks\bindings.json
    or
  • delete the file bin/x6/plugins/cyber_engine_tweaks.asi

  • move the folder bin/x6/plugins/cyber_engine_tweaks out of plugins (e.g. your Desktop). Do not delete it yet.

  • Download the most recent CET from Nexusarrow-up-right

  • Extract the downloaded archive to your game directory (the bin folder will merge with the one that is already there)

  • Start the game: CET should now ask you to bind a key. If it doesn't:

    1. Update both the game and the mod (double-check if you're sure)

    2. Head to the troubleshooting guidearrow-up-right and follow the steps in the red box at the top

  • Optional: Restore your mods and/or settings Now that CET works, find the old directory from step 2. Move the following files/folders:

    1. Mods: the directory mods from the old cyber_engine_tweaks folder

    2. Settings: The .json files directly inside cyber_engine_tweaks

  • Optional: delete the old folder (or leave it, it's your diskspace)

  • That's it! You're good to go!

  • Do a repair on your installation with no other mod files installed (redscript, cet, et cetera) then install CET as you normally would and check the log file. If it still isn't working, uninstall cyberpunk 2077 and do a complete wipe of the install folder.

    Steamarrow-up-right
  • XBOX Game Bararrow-up-right

  • MSI Afterburner arrow-up-right

  • Rivatunerarrow-up-right

  • GoG Galaxyarrow-up-right

  • Epicarrow-up-right

  • NZXT Camarrow-up-right

  • Ubisoft Connectarrow-up-right

  • In the Systems Settings tab locate Randomize memory allocations (Button-up ASLR) and High-entropy ASLR and change their settings to Use default (On).

  • Restart your computer.

  • You have verified that the log found in bin/x64/plugins/cyber_engine_tweaks/cyber_engine_tweaks.log is 'last modified' at the same time and date as your most recent attempt to launch the game

  • Remove all previous mods in the mods folder and try to launch the game. If it works, add your mods back one by one until you find the one that breaks CET. Some mods can break CET completely or change the overlay colour if they are not up to date.

  • Run the game as administrator. (this works for some people where other methods have failed)

  • Check to make sure your antivirus software is not removing or blocking cyber engine tweaks.

  • Do a complete reinstall of cyberpunk 2077.

  • Your OS version
  • A description of your issue

  • Your install directory

    C:\GOG Games\Cyberpunk 2077\

    Path from the guide

    bin\x64\plugins\cyber_engine_tweaks

    Folder you go to

    C:\GOG Games\Cyberpunk 2077\bin\x64\plugins\cyber_engine_tweaks

    direct linkarrow-up-right
    go herearrow-up-right
    here
    compatibility modearrow-up-right
    general troubleshooting pagearrow-up-right
    herearrow-up-right
    Discordarrow-up-right
    check your CET install
    clean reinstall of CET
    REDmodding Discordarrow-up-right
    thisarrow-up-right
    AMD Radeon arrow-up-right
    Discordarrow-up-right
    Geforce Experiencearrow-up-right
    GitHubarrow-up-right
    Nexus.arrow-up-right
    installation guide
    deploymentarrow-up-right
    morearrow-up-right
    #vortex-supportarrow-up-right
    Windowed Borderless modearrow-up-right
    overlayarrow-up-right
    uninstallationarrow-up-right
    consider asking in the Discord serverarrow-up-right
    submit an issue on GitHub arrow-up-right
    Reset your keybind
    Reinstall CET
    General troubleshooting
    Reinstall CET
    Reinstall CET
    Reinstall CET
    bin\x64\plugins\cyber_engine_tweaks\cyber_engine_tweaks.log
    bin\x64\plugins

    Patch notes

    hashtag
    1.20.0 - 06/08/2022

    Support for patch 1.6

    hashtag
    Added

    • TweakDB.CreateRecord, TweakDB.CloneRecord, and TweakDB.DeleteRecord now support TweakDBID as a parameter type

    hashtag
    Changes and fixes

    • Updated usedhashes.kark to the new URL

    • Moved to OpenResty Lua JIT

    hashtag
    1.19.5 - 05/04/2022

    Support for patch 1.52.1 (made up name, they didn't bother to name it AGAIN)

    hashtag
    1.19.4 - 22/03/2022

    Support for patch 1.52

    hashtag
    1.19.3 - 02/03/2022

    hashtag
    Changes and fixes

    • Fix crash on Windows 7.

    • Fix LoadTexture overwriting the previously loaded texture.

    hashtag
    1.19.2 - 28/02/2022

    Support for patch 1.5 hotfix 2

    hashtag
    Added

    • ImGui.LoadTexture(string path) returns a texture loaded from an image on disk in the mods' directory.

    • ImGui.Image(texture) displays a texture loaded with the LoadTexture function.

      Full Api is as follows, note that you may omit parameters after texture and default values will be used (Texture texture, ImVec2 size, ImVec2 uv0, ImVec2 uv1, ImVec4 tintColor, borderColor)

    hashtag
    Changes and fixes

    • Added stb to load images.

    • Fixed bad screen skip fix.

    hashtag
    1.19.1 - 19/02/2022

    Support for patch 1.5 v2

    hashtag
    Changes and fixes

    • Changed the archive hashes to use the wolvenkit KARK files for TweakDB editing

    • red4ext updated to 1.3.1

    hashtag
    1.19.0 - 16/02/2022

    Support for patch 1.5

    hashtag
    Changes and fixes

    • red4ext updated to 1.3.0

    • mimalloc updated to 2.0.3

    • sqlite3 updated to 3.37.0+200

    Special thanks to WopsS, psiberx and Sombra for their help with this patch!

    hashtag
    1.19.0-rc2 - 16/02/2022

    hashtag
    Known issues

    • exEntitySpawner does not work.

    • No pedestrian patch is not tested.

    hashtag
    Changes and fixes

    • Fix cursor not getting locked to the game.

    • Fix skip start screen patch not working.

    hashtag
    1.19.0-rc - 16/02/2022

    Support for patch 1.5

    hashtag
    Known issues

    • exEntitySpawner does not work.

    • No pedestrian patch is not tested.

    • Feel free to tell us any other issue that may arise on github/discord.

    hashtag
    Changes and fixes

    • mimalloc updated to 2.0.3

    • sqlite3 updated to 3.37.0+200

    hashtag
    1.18.3 - 10/02/2022

    hashtag
    Changes and fixes

    • Improve the d3d12 hook to fix a crash when using ReShade < 5.

    hashtag
    1.18.2 - 09/02/2022

    hashtag
    Changes and fixes

    • Improve the d3d12 hook to avoid conflicts with other overlays.

    hashtag
    1.18.1 - 15/12/2021

    Contains: and

    hashtag
    Changes and fixes

    • Fixed random crashes when overridden method has a script reference parameter.

    • Fixed random crashes when working with game structs.

    • Fixed random crashes for very frequently called overrides (such as GameObject.OnGameAttached and GameObject.OnDetach

    hashtag
    Added

    • Added the ability to add new free flats to TweakDB.

    hashtag
    1.18.0 - 4/12/2021

    Contains: , , and

    hashtag
    Changes and fixes

    • Fix bad allocation causing crashes when using some RTTI types

    • When using Override() the handler receives the original function (or next in the override chain) as the last parameter

    • The wrapped function can be called multiple times

    • If any of the overrides fail then the entire override chain also fails

    • Observers can be called before and after the target function using ObserveBefore() and ObserveAfter()

    • Observe() is an alias for ObserveBefore()

    • All observers are guaranteed to be called even if overrides and other observers fail

    • It's possible to observe and override static class methods

    • Full name should be used when overriding a scripted static method

    • Use weak handle for self in overrides. This should decrease the number of unreleased references and make CET less dependent on garbage collection.

    • Reuse overridden functions on mods reload. This fixes a potential issue with the native function registry, which has a hard limit on the number of registered functions.

    • Support out params in overrides.To properly handle out params in overrides:

    hashtag
    Added

    • Added support for mods deployed as a symlink for the mod directory

    • Added Lua function to check if the mod archive is installed:

    • Use FromVariant(variant) to extract the value

    • Use ToVariant(value) to pack the value when the type is unambiguous

    • Use ToVariant(value, type) when the type is ambiguous or you wanna force the type

    • The type of the array is resolved from the first element when possible

    • Added event for early TweakDB changes:

    hashtag
    1.17.0 - 2/10/2021

    Contains: , , and

    hashtag
    Changes and fixes

    • Fixed directx12 hooking not working when overlays were present.

    • Fixed symlink deployment with Vortex.

    • Drop second implementation of Scripting::ToRED() which was very outdated. This was limiting what overridden functions can return.

    Will output

    hashtag
    Added

    • Stricter check of the passed context when calling the RTTI function. Prevents unwanted crashes when invalid value passed as self / this. In particular, it prevents crashes when a dot is accidentally used instead of a colon.

    • Support for ISerializable strong and weak references. This opens access to some new classes, but not all of them.

    • Workaround for parameterized struct constructor. Previously, fields of a reference type could be corrupted right after construction.

    • Support of nulls for strong and weak references.

    Assignment example:

    Parameter example:

    • IsDefined helper function as in redscript. Checks if reference is not null and valid. Can be used on game object's field and lua variable.

    • EnumInt helper function as in redscript.

    • Support for CRUID type.

    • Support for gamedataLocKeyWrapper type.

    hashtag
    1.16.4 - 14/09/2021

    Support for patch 1.31

    Contains:

    hashtag
    Changes and fixes

    • Updated to Imgui 1.84.2

    hashtag
    1.16.3 - Don't remember when

    Support for patch 1.30 second edition.

    hashtag
    1.16.2 - 27/8/2021

    Contains:

    hashtag
    Changes and fixes

    • Fix disappearing footsteps (1.16.1 regression).

    • Alternative syntax for CName and TweakDBID conversions without .new()

    • Direct access to global functions.

    • Throw Lua errors from RTTI calls as the Lua script will do.

    Will print the full callstack in the log:

    hashtag
    1.16.1 - 23/8/2021

    hashtag
    Changes and fixes

    • Fix channel logs not printing the message.

    • Added a new channel "DEBUG" that always prints even when game logs are disabled.

    • Re-added TweakDB printing.

    hashtag
    1.16.0 - 20/8/2021

    Support for patch 1.30 - huge thanks to everyone who helped with this and all testers!

    Contains: , , , and

    hashtag
    Changes and fixes

    • Fix crash when calling functions that have optional parameters not defaulted.

    • Fix crash when using RegisterInputListener from an observer.

    hashtag
    1.15.0 - 8/8/2021

    Contains: , , , , , , and

    hashtag
    Script API

    • Whitelisted collectgarbage to let scripts force release game references.

    • Added exEntitySpawner to spawn entities easily

    hashtag
    Changes and fixes

    • Fixed a crash that would occur on machines without ASLR enabled by enabling LuaJIT's GC64 option.

    • Fixed nested RTTI calls that would crash the game due to memory release of the previous call's memory.

    • Fix the disable anti AA and async compute patches.

    hashtag
    1.14.0 - 17/06/2021

    Contains: , , , , ,

    hashtag
    Script API

    • 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.

    • Added WorldFunctionalTests

    • Added Imgui.GetStyle()

    • Added support for ResourceAsyncReference. Allows editing of TweakDB props of that type by the mods.

    hashtag
    Changes and fixes

    • Fixed disappearing footsteps issue.

      • The issue still can occur if the mods do not properly release the player reference.

    • The GetMod() is only available after onInit

    hashtag
    Internals

    • Global fallback table is used now for all mod environments. No need to whitelist what's defined there.

    • Aliases like Game['GetPlayer'] = Game['GetPlayer;GameInstance'] aren't needed in autoexec.lua. All global and class functions are automatically resolved by short name now.

    hashtag
    Added

    • Patch for the Minimap flicker

    hashtag
    1.13.0 - 28/04/2021

    Support for patch 1.22

    Fixed

    • Crash when using TweakDB:Update in an Observe/Override callback

    • Direct3d12 command queue could be null sometimes

    • Error logging from Observe/Override callbacks

    Added

    • Strings can now be implicitly cast to a TweakDBID when the scripting framework expects a TweakDBID

    hashtag
    1.12.2 - 14/04/2021

    Support for patch 1.21

    Fixed

    • Lua scripting errors with functions returning some values with out params

    • GameOptions.Toggle() now works

    • Fix falsy "Unknown error" when calling a global that returns nil

    Added

    • Equality comparison to some types (Vector3, Enum, TweakDBID,...)

    • Concatenation for TweakDBID

    hashtag
    1.12.1 - 04/04/2021

    Fixed

    • Regressions with GameOptions

    • Wrong version info returned back by GetVersion()

    • Problems with Console widget history

    Added

    • Modal popups for unsaved changes into Bindings and Settings widget

    • Modal popup on first launch asking user explicitly to bind some hotkey for toggling Overlay

    • Option to enable removing of dead binds (default is on)

    Changed

    • Nicer formatting of headings inside Bindings widget (replace characters that are not alphanumeric by space and autocapitalize each word)

    • Reworked Settings menu (options are now split into two categories - Patches and Dev)

    • Reworked Bindings menu (separated hotkeys and inputs into two categories)

    hashtag
    1.12.0 - 29/03/2021

    Added support for patch 1.2.

    Fixed

    • os.remove and os.rename were not working properly.

    • The debug menu patch would not show the menus.

    • Fixed dofile, loadfile and loadstring.

    Changed

    • Key bindings were improved.

      • Support for scroll wheel.

      • registerInput for single inputs. This handler is similar to registerHotkey, but function now takes one boolean argument denoting if input was pressed/released.

    Added

    • Re-added the SMT patch for AMD CPUs that did not get a performance boost after CDPR's patch.

    • Added Lua's bit32 library.

    • TweakDB additions:

    hashtag
    1.11.4 - 18/02/2021

    Fixed

    • Fix memory allocation errors due to missing argument.

    • Fix Observe/Override sometimes affecting scripts when they shouldn't.

    hashtag
    1.11.3 - 17/02/2021

    • Fix crash when using multiple mods.

    hashtag
    1.11.2 - 17/02/2021

    Fixed

    • Crash on game exit when a reference had not been garbage collected by the lua vm.

    • Missing enum value CellPadding in Imgui Lua (Pull Request: ).

    • Crash when using Override/Observe on pure script functions.

    Changed

    • Memory optimization for calls.

    • Replaced the hacky thread safety used for the Lua VM with TiltedCore's Lockable<T>.

    hashtag
    1.11.1 - 15/02/2021

    Added

    • API changes to support Imgui 1.81 and added missing ColorButton overloads.

    • Override has now 2 overloads to simplify usage, this will completely remove the original function or add one if it doesn't exist:

      • Override(typeName, fullFunctionName, shortFunctionName, function)

    Changed

    • Upgrade Imgui 1.80 to .

    • Fixed Override so that it can override at runtime after scripts have been loaded.

    • Revert Lua caching of functions as the new override method will work regardless.

    Fixed

    • Fix game crash when a script used "require" on a malformed file.

    hashtag
    1.11.0 - 13/02/2021

    Added

    • Override function to either listen to a function or to replace a game function. Despite its name it can also be used to extend a type by adding a new function. Usage is as such:

    The first parameter is the class type, second the full name of the function, the third is the short name (usually they are the same), the fourth boolean is used to specify if this is a replacement of the function or not, if set to true it will replace entirely the original function, if set to false, your function will be called before the original function is called. The last parameter is your handler function, note that the parameters are passed according to the RED script definition.

    Removed

    • Telemetry, the experiment gave us the data we wanted, obviously a lot of people use the mod and we are very happy about that! We are sorry this caused so much drama, it wasn't the intention and quite frankly we still don't really understand why.

    Changed

    • Calling RED functions used to be cached, given that functions can be overridden at any time the cache has been removed, it's a bit slower but shouldn't be visible in the real world.

    hashtag
    1.10.3 - 12/02/2021

    • Using the up and down arrows in the console now retrieves the command history

    hashtag
    1.10.2 - 07/02/2021

    • Added Telemetry so we know how many people use CET (can be disabled in settings), note that we do not store any information we just count the number of players

    hashtag
    1.10.1 - 06/02/2021

    • Fix TweakDBID issues with large arrays

    • Fix TweakDBID crash when using specific functions

    hashtag
    1.10 - 05/02/2021

    Changed

    • Updated to game version 1.12

    • Updated RED4ext.sdk to the latest

    Proper sandboxing ():

    • Each mod is (right now) assigned sqlite3 database implicitly, accessible through db lua object (will be changed later to on-demand through info in metadata file)

    • Introduced proper sandbox with LuaSandbox helper class

    • Bytecode cannot be loaded now (native modules included)

    More ImGui stuff(, , , ):

    • Enabled ImGui keyboard navigation. (ctrl + tab to switch window, arrow keys to navigate)

    • Removed the deprecated power argument in Drag and Slider widgets, replace it with the newer ImGuiSliderFlag (breaking change)

    TweakDB binding ()

    • TweakDB:GetFlat(...)

    • TweakDB:SetFlat(...)

    • TweakDB:GetRecord(...)

    hashtag
    1.9.6 - 30/01/2021

    Changed

    • Major vulnerability in the game's save loader fixed

    hashtag
    1.9.5 - 28/01/2021

    Changed

    • Updated to game version 1.11

    • Updated RED4ext.sdk to the latest

    • Updated to spdlog 1.8.2

    hashtag
    1.9.4 - 26/01/2021

    hashtag
    Changed

    • Replaced pattern search with much faster library

    hashtag
    Fixed

    • Fix issue when resizing game window (eg. resolution change in Options)

    • Fix issue causing game window to stop rendering and freeze on last frame

    • Fix ImGui.CalcTextSize overloads (now, second parameter is removed from all, as it did not make sense for Lua anyway)

    hashtag
    1.9.2 - 23/01/2021

    hashtag
    Added

    • Added jsonsupport to Lua

    hashtag
    1.9.1 - 23/01/2021

    hashtag
    Added

    • Added support for the latest patch 1.10

    • UI with settings will now ask you for a console key on first launch, you can edit it later

    • Mods can now request hotkeys via registerHotkey, the user can pick what key they want to use in the menu

    hashtag
    Fixed

    • Scripting is now handling memory correctly, calling Game.GetPlayer() every frame is fine now

    • Game doesn't crash when exiting anymore

    hashtag
    Removed

    • dofile is now deprecated, use only mods please, it will be removed entirely in the next update

    on game reload).
  • Fixed random crashes for hotkeys and inputs (for example, binding the mouse wheel had a very high chance of causing crashes).

  • Fixed potential issues when CET mods that modify TweakDB are used with TweakDBext mods at the same time.

  • Accept all params in the handler

  • Don't pass out params to the wrapped function

  • Return all results from the handler

  • Fix gamedataTDBIDHelper helper that behaves the same as gamedataTweakDBInterface.

  • Sanitize type names to be a valid Lua identifier. This makes namespaced types added by redscript accessible in Lua.

  • Force garbage collection for overridden functions of inkGameController and it's descendants. In some cases, there seem to be issues with unreleased instances of inkGameController leading to crashes. This was introduced when we started to properly wrap the context in Handle<>.

  • Changed the behavior of observers so that all observers will be called even if an override is defined.

  • Function to add new CNames to the pool

    Register global PlayerPuppet.OnAction handler as for pre patch 1.3.
  • Override function by full name. Allows to observe/override all variants of overloaded function.

  • Fix for TweakDB default values.

  • Fix tooltips that contained C format characters and would crash the game.

  • Improved update hook.

  • xmake install will now install in the game's directory to make it easier for those installing from source.

  • 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.

  • Observe() and Override() now accept both native and scripted type names.

  • Added Game.GetSystemRequestsHandler() as an alternative to GetSingleton("inkMenuScenario"):GetSystemRequestsHandler().

  • Added TweakDB:SetFlats

  • as there is no guarantee that the required mod will be loaded before this event.
  • Types and functions that rely on RTTI are only available after onInit event to prevent some unwanted crashes and unexpected behavior.

  • Improved detection of whether the game is running. Prevents more crashes when exiting the game.

  • Implemented optional parameters support. Prevents some unexpected crashes.

  • Fixed DumpType("Type") returning empty result.

  • Fixed crash when accessing properties of invalid Enum, eg. Enum.new('', '').value.

  • Fixed crash when setting an incompatible value for an object property.

  • Fixed crash when calling function with out parameters of Enum, CName or TweakDBID type.

  • Fixed memory leaks when passing strong or weak references to the function.

  • Fixed memory leaks when invalid parameters passed to the function.

  • Fixed memory leaks for function results and out parameters of certain types.

  • Fixed memory leaks when creating new objects.

  • Fixed memory leaks when setting object properties.

  • Fixed memory leak for arrays with elements of certain types passed as an argument.

  • Fixed a crash when passing an incompatible array as an argument. For example, an array of numbers instead of an array of handles.

  • Fixed memory leak when passing an object instead of an array as an argument. This resulted in a silent crash without calling ResetAllocator.

  • Fixed inconsistent self and random crashes in Observe and Override.

  • Reverted Override() to the previous behavior so when the handler fails, the original game function is not called.

  • Added implicit conversion from Int64/UInt64 to other arithmetic types.

  • Added type safety checks for Int64/UInt64.

  • Added recursive freeing of arrays.

  • Added logging for errors occurred in the module loaded with require(). Should make the transition to the new version less painful.

  • Added implicit class to strong reference conversion.
  • Updated RED4ext to latest version.

    • This fixes the error when trying to use a CName that is None

  • Redundant Lua environment passing
  • 64bits numbers are now correctly interpreted as numbers by Lua

  • Return out params from global function the same way as for instance methods

  • EulerAngles::ToString returning swapped roll and yaw values

  • SQLite3 database not closing on mod reload

  • 3rd party licences missing
  • ImGui.TreePop unavailable in Lua

  • Multiple registerInput handlers unable to be invoked at same time

  • Conversion of 64-bit integral values from object properties

  • Scroll wheel failling to register properly

  • Option to enable ImGui assertions to make sure mods are not breaking something (default is off)

  • Option to toggle ImGui Diagnostic window (default is off, this option is not preserved after restart!)

  • onDraw is not called for mods while CET modal dialog is active (including first launch)

  • First time launch (it should now be more streamlined)

  • Decoupled config.json from bindings.json (overlay key is now located only inside bindings.json and is left out of config.json)

  • Overlay key was moved to Bindings menu

  • Updated TiltedCore to 0.2.2

  • Lua environment would not be applied correctly to some callbacks.

  • Lua to RED engine converter now works correctly with nil values and double.

  • Security issue where scripts could override another script's functions, calling another script is now read only.

  • IsBound and GetBind added to check if action is bound and retrieve string representation of it.

  • TweakDB experienced many changes:

    • All TweakDB functions can now be called with strings instead of TweakDBID. TweakDBID is still accepted.

    • TweakDB:SetFlat now updates records if it already exists (see SetFlatNoUpdate)

    • Performance improved.

  • Lua version changed to 5.1 this can break your mod if you were using post 5.1 language features such as bit operators. This change was done to fix environment issues, we also got a performance boost by switching to LuaJIT instead of the default Lua implementation.

  • Updated Imgui to version 1.82.

  • Updated spdlog to version 1.8.5.

  • Updated sqlite3 to version 3.35.0.

  • CloneRecord, CreateRecord and DeleteRecord.

  • GetRecords, obtain a list of records associated with a type.

  • SetFlatNoUpdate, faster way to set without checking if there is an old record to update. Requires call to TweakDB:Update afterwards to apply changes!

  • TweakDB editor in the UI.

  • Supports loading archivehashes.txt.

  • Can now search through record flats.

  • Crash when reloading mods using Override/Observe.

  • Crash when a malformed lua file was loaded with "require".

  • Lua execution error when a callback used a global variable because environments were not applied.

  • Fix memory leaks when reloading mods using Observe/Override.

  • Override(typeName, functionName, function) - both full and short names are set to functionName

  • Observe has now 2 overloads to simplify usage, this will not remove the original function but will add one if it doesn't exist:

    • Observe(typeName, fullFunctionName, shortFunctionName, function)

    • Observe(typeName, functionName, function) - both full and short names are set to functionName

  • require cache is reset on Reload all mods now

  • require does not support '.' instead of '/' now (only breaking change, functions the same otherwise)

  • All pathing is now relative to mod (breaking change, includes all io functions, dofile() and such included)

  • stripped ImGui, json, io and dofile and such from console sandbox (only needed things left for it in)

  • Added dir() command for mods to list directory contents

  • Errors that happen in Game, game class methods,... should now log into proper environment (to console when executed from console, to mod log when executed from mod)

  • Made the argument open optional in ImGui.Begin(), ImGui.BeginModal(), ImGui.BeginTabItem(), so the close button can be hidden when setting flags.
  • Added bindings arrow-up-rightfor the new tables widget in ImGui 1.8.0

  • Added arrow-up-rightselected mouse utilities back

  • Added bindings for ImDrawlistarrow-up-right

  • TweakDB:Query(...)
  • TweakDB:Update(...)

  • Fix Windows 7 flatlining

    You can now instantiate game objects with NewObject('class name'), do not expect this to work with every class without any issues

  • SQLite3 support was added, you can use the entire API at the moment, we are going to remove all open functions in the near future and replace it with a open that can only open a single database linked to your mod

  • Mods will now log to their own log files, you can also use spdlogto print in your log

  • 634arrow-up-right
    635arrow-up-right
    623arrow-up-right
    625arrow-up-right
    628arrow-up-right
    629arrow-up-right
    617arrow-up-right
    618arrow-up-right
    619arrow-up-right
    620arrow-up-right
    611arrow-up-right
    603arrow-up-right
    581arrow-up-right
    582arrow-up-right
    583arrow-up-right
    585 arrow-up-right
    589arrow-up-right
    568arrow-up-right
    572arrow-up-right
    573arrow-up-right
    574arrow-up-right
    575arrow-up-right
    577arrow-up-right
    578arrow-up-right
    579arrow-up-right
    551arrow-up-right
    552arrow-up-right
    553arrow-up-right
    557arrow-up-right
    558arrow-up-right
    559arrow-up-right
    496arrow-up-right
    1.81arrow-up-right
    #454arrow-up-right
    #452arrow-up-right
    #453arrow-up-right
    #457arrow-up-right
    #460arrow-up-right
    #461arrow-up-right
    -- Get the sticky frag grenade record
    sticky_grenade = TweakDB:GetRecord("Items.GrenadeFragSticky")
    
    -- Create a clone of "Items.GrenadeFragSticky" with the name "Items.GrenadeFragStickyNew"
    TweakDB:CloneRecord(sticky_grenade:GetID() + "New", sticky_grenade:GetID())
    
    -- Get the new record
    sticky_grenade_new = TweakDB:GetRecord("Items.GrenadeFragStickyNew")
    -- or
    sticky_grenade_new = TweakDB:GetRecord(sticky_grenade:GetID() + "New")
    
    -- manipulate the new record as needed
    
    -- delete record many different ways
    TweakDB:DeleteRecord(sticky_grenade_new:GetID())
    TweakDB:DeleteRecord(sticky_grenade:GetID() + "New")
    TweakDB:DeleteRecord("Items.GrenadeFragStickyNew")
    Override('PlayerPuppet', 'SetWarningMessage', function(_, message, wrappedMethod)
        wrappedMethod('[Overridden] ' .. message)
    end)
    Override('PlayerPuppet', 'GetGunshotRange', function(_, originalRangeFunc)
        return originalRangeFunc() + originalRangeFunc()
    end)
    ObserveBefore('PlayerPuppet', 'GetGunshotRange', function()
        print('PlayerPuppet::GetGunshotRange::Before')
    end)
    ObserveAfter('PlayerPuppet', 'GetGunshotRange', function()
        print('PlayerPuppet::GetGunshotRange::After')
    end)
    Override('RPGManager', 'GetFloatItemQuality', function(value)
        print('RPGManager.GetFloatItemQuality(' .. value .. ')')
        return gamedataQuality.Legendary
    end)
    Observe('PlayerPuppet', 'IsSwimming;PlayerPuppet', function(player)
        print('PlayerPuppet.IsSwimming(player)')
    end)
    registerForEvent('onInit', function()
        Override('SpatialQueriesSystem', 'SyncRaycastByCollisionGroup', function(_, a1, a2, a3, a4, a5, a6, wrapped)
            -- Skip a4 here because it's an out param
            local success, result = wrapped(a1, a2, a3, a5, a6)
            print(success, GameDump(result))
    
            -- Return all results
            return success, result
        end)
    end)
    
    registerHotkey('SyncRaycastByCollisionGroup', 'SyncRaycastByCollisionGroup', function()
        local from, forward = Game.GetTargetingSystem():GetCrosshairData(GetPlayer())
        local to = Vector4.new(from.x + forward.x * 100, from.y + forward.y * 100, from.z + forward.z * 100, from.w)
    
        local success, result = Game.GetSpatialQueriesSystem():SyncRaycastByCollisionGroup(from, to, 'Static', false, false)
        print(success, GameDump(result))
    end)
    if not ModArchiveExists("my_mod.archive") then
        print("This mod requires my_mod.archive to be installed")
    end
    ToVariant(Vector4.new(1, 2, 3))
    ToVariant(gamedataQuality.Epic)
    ToVariant("Nam libero tempore")
    ToVariant(16570246047455160070ULL)
    ToVariant(true)
    ToVariant(TweakDBID("Items.money"))
    ToVariant(CName("deflect"))
    ToVariant(LocKey(12345))
    ToVariant(CRUID(1337))
    ToVariant(1, "Int32") -- Same numeric value can be a signed integer
    ToVariant(1, "Uint32") -- or unsigned integer
    ToVariant(1, "Float") -- or float
    ToVariant(nil, "handle:PlayerPuppet") -- Null requires a concrete type
    ToVariant({ "a", "b", "c" }) -- Auto resolved as "array:String"
    ToVariant({ 1, 2, 3 }, "array:Uint32") -- Ambiguous type
    registerForEvent('onTweak', function()
    	TweakDB:SetFlat('PreventionSystem.setup.totalEntitiesLimit', 20)
    end)
    Observe('PlayerPuppet', 'IsPlayer', function() print('Observer 1') end)
    Override('PlayerPuppet', 'IsPlayer', function() print('Override 1') end)
    Override('PlayerPuppet', 'IsPlayer', function() print('Override 2') end)
    Observe('PlayerPuppet', 'IsPlayer', function() print('Observer 2') end)
    Observer 1
    Observer 2
    Override 1
    print(CName("CET").value) -- Empty string
    CName.add("CET")
    print(CName("CET").value) -- CET
    -- Set reference to null
    self.resetConfirmationToken = nil
    -- Omit optional task data
    Game.GetDelaySystem():QueueTask(this, nil, "ResolveGameplayStateTask", gameScriptTaskExecutionStage.PostPhysics)
    print(EnumInt(GameplayTier.Tier2_StagedGameplay)) -- 2ULL
    local dialogLine = scnDialogLineData.new()
    dialogLine.id = CRUID(12345)
    TweakDB:SetFlat("Items.Preset_Overture_Kerry.displayName", LocKey(40475))
    print(TweakDBID("Items.money"))
    print(CName(0))
    print(CName("DEBUG"))
    print(GetPlayer()) -- Equivalent to print(Game.GetPlayer())
    registerForEvent('onInit', function()
        Observe('TimeSystem', 'SetTimeDilation', function(self, reason)
            if reason.value == 'consoleCommand' then
                Game.FloorF('XXX')
            end
        end)
    end)
    registerHotkey('TestError', 'TestError', function()
        Game.GetTimeSystem():SetTimeDilation('consoleCommand', 0.25, 1.0, 'Linear')
    end)
    [error] ...x64\plugins\cyber_engine_tweaks\mods\test-error\init.lua:4: Function 'FloorF' parameter 1 must be Float.
    stack traceback:
        [C]: in function 'FloorF'
        ...x64\plugins\cyber_engine_tweaks\mods\test-error\init.lua:4: in function <...x64\plugins\cyber_engine_tweaks\mods\test-error\init.lua:2>
        [C]: in function 'SetTimeDilation'
        ...x64\plugins\cyber_engine_tweaks\mods\test-error\init.lua:10: in function <...x64\plugins\cyber_engine_tweaks\mods\test-error\init.lua:9>
    spawnPosition = Game.GetPlayer():GetWorldTransform()
    
    -- [Simple]
    -- Spawn 'Character.Judy' just like prevention system
    -- Optionally set appearance to 'judy_diving_suit'
    judyEntityID = exEntitySpawner.SpawnRecord('Character.Judy', spawnPosition)
    judyEntityID = exEntitySpawner.SpawnRecord('Character.Judy', spawnPosition, 'judy_diving_suit')
    
    -- [Advanced]
    -- Spawn base\quest\secondary_characters\judy.ent
    -- Optionally set appearance to 'default'
    -- Optionally set TweakDB record to 'Character.Judy' (VO/Name/Equipment/etc)
    judyEntityID = exEntitySpawner.Spawn([[base\quest\secondary_characters\judy.ent]], spawnPosition)
    judyEntityID = exEntitySpawner.Spawn([[base\quest\secondary_characters\judy.ent]], spawnPosition, 'default')
    judyEntityID = exEntitySpawner.Spawn([[base\quest\secondary_characters\judy.ent]], spawnPosition, 'default', 'Character.Judy')
    
    -- Some time later...
    -- entities are not spawned instantly, FindEntityByID may return nil
    judy = Game.FindEntityByID(judyEntityID)
    
    exEntitySpawner.Despawn(judy)
    TweakDB:SetFlats('Character.Judy', {
      level = 99,
      tags = { 'Immortal' }
    });
    transform = Game.GetPlayer():GetWorldTransform()
    entityID = WorldFunctionalTests.SpawnEntity('base\\quest\\secondary_characters\\judy.ent', transform, '')
    entity = Game.FindEntityByID(entityID)
    WorldFunctionalTests.DespawnEntity(entity)
    Override("PlayerPuppet", "IsA", "IsA", true, function(self, className) print(className); return true end)
    TweakDB
    very detailed step-by-step guide
    General instructions what goes where
    here
    here
    yellow wiki
    game directory
    game directory
    Troubleshooting