# Adding character creator options

## Summary

**Published:** Aug 8 2024 by [Zhincore](mailto:undefined) \
**Last documented edit:** Aug 8 2024 by [Zhincore](mailto:undefined)

This guide will show you how to add your own custom selector to the character creator (CC), the character appearance menu specifically but this knowledge can be reused... hopefully.&#x20;

The guide is a little longer, but that's because I tried to explain my findings, the coding doesn't take too long. Also the guide is "progressive" and each step "upgrades" the custom selector.

{% hint style="warning" %}
**The selector will not be used by the game itself**, it's up to the developer to handle the logic behind it! If you want to add to vanilla CC options, [use methods like CCXL](https://wiki.redmodding.org/cyberpunk-2077-modding/for-mod-creators-theory/core-mods-explained/archivexl/archivexl-character-creator-additions/ccxl-hairs).
{% endhint %}

## Prerequisites

* Some understanding of RedScript, this isn't a beginner guide (kinda).
* You should already know what you want to do with the option (the game won't do it for you).
* [Codeware](https://www.nexusmods.com/cyberpunk2077/mods/7780) - some structs are unavailable without it

## Step 1: Make a selector

### Hooking up

As is the common case with RedScript, we need to hook into something to run our code.

I went for the `characterCreationBodyMorphMenu` class, which manages the character appearance menu. Since I want my option to be on top but bellow the gender/voice selection, I will run my code in the `CreateVoiceOverSwitcher` method. You could also hook into `InitializeList` method (which calls my chosen method) and run your code before (to add on top of the list) or after (to add on the bottom) the wrapped method.

So let's prepare our hook, here is mine:

```swift
@wrapMethod(characterCreationBodyMorphMenu)
public func CreateVoiceOverSwitcher() {
    wrappedMethod(); // Call original method to create vanilla options
    
    // Your code after voice selection
}
```

{% hint style="info" %}
The method `CreateVoiceOverSwitcher` is only called when creating character in new game! If you want your option to be visible in mirror/Ripperdoc hook into the other mentioned method or use [Appearance Change Unlocker](https://www.nexusmods.com/cyberpunk2077/mods/3850)!
{% endhint %}

### Manually Spawned Selector

Let's use the hook to add our selector! We simply need to spawn another `Selector` widget. Update your wrapped method to look something like this:

<pre class="language-swift"><code class="lang-swift">@wrapMethod(characterCreationBodyMorphMenu)
public func CreateVoiceOverSwitcher() {
    wrappedMethod(); // Call original method to create vanilla options
    
<strong>    // Add our custom selector
</strong><strong>    let optionWidget = this.SpawnFromLocal(inkCompoundRef.Get(this.m_optionsList), n"Selector");
</strong>}
</code></pre>

{% hint style="info" %}
You can also spawn the `ColorPicker` if you want selector with thumbnails. That requires more set-up and is outside the scope of this guide.
{% endhint %}

That was easy, right? Now we have this:

<figure><img src="/files/Dbyby8LwyaaOybDRjj4l" alt=""><figcaption><p>The default selector we've just created.</p></figcaption></figure>

Great isn't it? Well... yeah, but it's the default one and doesn't do anything. Let's change it.

## Step 2: Customize the selector

To tell the inkWidget what to show, we need to construct a few data structures.

### Stayin' alive

But before that we need something that will stay alive and keep our data in the memory (because inkWidget won't, it only keeps a weak reference).

So let's go with a `ScriptableSystem`. Simply make a class that extends it (give it a better name, something to do with your mod). Let's also make the attribute that will hold our data.

```swift
class MySystem extends ScriptableSystem {
    // Our hard reference to the option data, so it stays in memory
    private let option: ref<CharacterCustomizationOption>;

    // ...rest of the code
}
```

{% hint style="info" %}
You can also use Codeware's `ScriptableService` if you want to be independent of save. See the RedScript wiki for a comparison: [Scriptables comparison](https://wiki.redmodding.org/redscript/references-and-examples/codeware-callbacks/scriptables-comparison).
{% endhint %}

Now let's construct our stuff. I chose to make a dedicated method for it, so we can call it once we need it. All of string HAVE to be a translation key, you can add custom ones with ArchiveXL. I'm gonna use some random vanilla strings, please don't judge me. Let's add the method to our class (you can name it however you want):

```swift
    public func GetSelectorOption() -> ref<CharacterCustomizationOption> {
        // Instantiate the info class
        let info = new gameuiSwitcherInfo();
        // Set the label of the selector, must be a translation, so either
        // - primary key - e.g. "LocKey#64567"
        // - or secondary key:
        info.localizedName = "Gameplay-RPG-Stats-Attributes-IntelligenceName";
        
        // Add some options to choose from. Again, has to use translations
        // You have to add more than one for the select to be clickable
        let opt1: gameuiSwitcherOption;
        opt1.localizedName = "UI-CharacterCreation-masculine_voice";
        ArrayPush(info.options, opt1);
        
        let opt2: gameuiSwitcherOption;
        opt2.localizedName = "UI-CharacterCreation-feminine_voice";
        ArrayPush(info.options, opt2);
        
        // Instantiate the option data class
        let option = new gameuiCharacterCustomizationOptionImpl();
        // Assign it our info
        option.info = info;
        
        // Save the data to keep it in memory
        this.option = option;
        // And return it
        return option;
    }
```

{% hint style="danger" %}
You may not like the way we create the options here but **DO NOT** call `new gameuiSwitcherOption("...")`!! The struct is incomplete in RedScript and it is not possible to construct it properly. Use the method shown above, maybe in a loop.
{% endhint %}

That's a bit more code, but I commented it for you. You will probably want to customize it anyway. Remember, this method is inside the class we made before!

### Obtaining and assigning the option data

Now we need to call that method and to do that we need to obtain an instance of our Scriptable.

Go back to your hook from Step 1 and add this code to the method (this code will be different if you use `ScritableService` instead of System):&#x20;

```swift
// Obtain our
let gameInstance = this.GetPlayerControlledObject().GetGame();
let mySystem = GameInstance.GetScriptableSystemsContainer(gameInstance).Get(n"MyModule.MySystem") as MySystem;
```

Update the CName and the cast to match your module and class names. With this we can call our method and option the option data:

```swift
let option = mySystem.GetSelectorOption();
```

This option has to be given to the inkWidget's controller which we can do the following way:

```swift
let optionController = optionWidget.GetController() as characterCreationBodyMorphOption;
optionController.SetOption(option);
```

And there we go, your hook method should now look something like this (the line order might differ):

```swift
@wrapMethod(characterCreationBodyMorphMenu)
public func CreateMyCustomSelector() {
    wrappedMethod();

    // Obtain our option
    let gameInstance = this.GetPlayerControlledObject().GetGame();
    let myService = GameInstance.GetScriptableSystemsContainer(gameInstance).Get(n"MyModule.MySystem") as MySystem;
    let option = myService.GetSelectorOption();

    // Add our option
    let optionWidget = this.SpawnFromLocal(inkCompoundRef.Get(this.m_optionsList), n"Selector");
    let optionController = optionWidget.GetController() as characterCreationBodyMorphOption;

    optionController.SetOption(option);
}
```

Now we have this <img src="/files/40mOzAL7XYH6Y8FotElm" alt="" data-size="line"> (again, ignore my choice of strings):

<figure><img src="/files/Bg0o5CO6t5rt4PVobYRh" alt=""><figcaption><p>The selector we just made with custom strings. It's even clickable now!</p></figcaption></figure>

Much better, right? Now what?

## Step 3: Get user's choice

Now we want to know what the user choice! You can do this by registering a callback on the inkWidget's controller. I chose to add the callback to my ScriptableSystem.

I'm gonna store the choice in a persistent attribute which will be store in the player's save and we can retrieve it later.

### Creating the callback

Let's go to our System and let's add our persistent attribute to the top (preferably) of the class:

```swift
private persistent let chosenIndex: Uint32 = 0u;
```

We're storing an index of the option the user chose. By default it's a zero (unsigned). Also add the following method to the class. We will use it as callback, so mark it as such (`cb`).

```swift
public cb func MyOnSliderChange(widget: wref<inkWidget>) {
    let controller = widget.GetController() as characterCreationBodyMorphOption;
    this.chosenIndex = controller.GetSelectorIndex();
}
```

It will receive the selector widget that was changed. From that we get it's controller and from that we get the currently selected index.

### Registering the callback

To make the callback work, we have to register it when creating the widget, so go back to your hook one last time. We already have our System reference so the following is all we need to add to end of the method:

```swift
optionController.RegisterToCallback(n"OnSliderChange", mySystem, n"MyOnSliderChange");
```

You can, again, name the method whatever you want, just remember to change the second CName in the last line.

### Restoring saved value

One last thing remaining is remembering what the user selected last time. We already have the `persistent` attribute which is automagically saved, we just have to use it now.

Go to your option data creation method and add a line to the option creation part to look like this:

<pre class="language-swift"><code class="lang-swift">let option = new gameuiCharacterCustomizationOptionImpl();
<strong>option.currIndex = this.chosenIndex;
</strong>option.info = info;
</code></pre>

With this new line the selector will remember what you've selected last time! Incredible!&#x20;

You can see the whole code from this guide here: <https://gist.github.com/Zhincore/9799a02f55a716283982dcb26e1c9a07>

And with that we're done with the guide, the rest is up to you!


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wiki.redmodding.org/scripting-cyberpunk/how-do-i/inkwidgets/adding-character-creator-options.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
