Skip to content

Commit

Permalink
Merge pull request #3 from SpontanCombust/next-gen-port
Browse files Browse the repository at this point in the history
  • Loading branch information
SpontanCombust authored Jul 13, 2023
2 parents c33caf5 + 823e60c commit 74c04db
Show file tree
Hide file tree
Showing 11 changed files with 80 additions and 79 deletions.
68 changes: 28 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,71 +61,59 @@ To see all the possible options that can be used with the parser use the `--help
4. Add generated script file to your mod structure


5. Let the framework know about your settings class
5. Write a convenience function to get your settings object

```ts
public var settings : MyModSettings;
```js
function GetMyModSettings(): MyModSettings
{
var settings : MyModSettings;

mySettings = new MyModSettings in thePlayer;
settings = (MyModSettings)GetSettingsMasterRegistry().GetSettings('MyModSettings');
if (!settings)
{
settings = new MyModSettings in theGame;
GetSettingsMasterRegistry().AddSettings(settings, 'MyModSettings');
}

GetSettingsMasterRegistry().AddSettings(mySettings, 'MyModSettings');
return settings;
}
```
First argument is the settings object itself.
Second argument is the ID you want this object to be identified with.

The `AddSettings()` function needs to be used only once. It is up to you when and where you use it. It can be done with Bootstrap or for example in player's OnSpawned function, your choice. The registry will hold the settings object through the entire time the game runs and even if you try to add these settings multiple times with the same ID given, the framework will detect it and won't add a duplicate.

The `AddSettings()` function needs to be used only once. It is up to you when and where you use it. It can be done with Bootstrap, in player's OnSpawned function or by writing a short function like above that will do this automatically on the first call. The registry will hold the settings object through the entire time the game runs. The second argument of this function is an arbitrary identifier by which you will later be able to retrieve settings object with `GetSettings()` method.

If you don't want to you don't need to store the settings object yourself. You can use registry's GetSettings() method to get access to your settings object.
```ts
// creating the object without assigning it anywhere
GetSettingsMasterRegistry().AddSettings(new MyModSettings in theGame, 'MyModSettings');

...

var mySettings : MyModSettings;

mySettings = (MyModSettings)GetSettingsMasterRegistry().GetSettings('MyModSettings');

if(mySettings)
{
if(mySettings.tab1.option == 1)
{
...
}
}
```

<br>

6. Use the settings object in your mod

```js
if(thePlayer.mySettings.tab1.toggle)
var settings : MyModSettings = GetMyModSettings();

if(settings.tab1.toggle)
{
doSomething();
settings.tab1.sliderInt = 50;
settings.WriteSettings();
}
```
From the moment the settings object is added to the registry it gets updated whenever user changes settings in the menu. You do not need to refresh said object yourself, but if you really need to you can do so by calling `ReadSettings()` on it.
The structure of the generated settings class is meant to resemble the structure of the XML. The class contains variables analogous to XML's Group nodes. Those variables in turn contain variables which are equivalent to individual Var nodes. Their types are appropriate, for example a variable for TOGGLE will be of type bool and a SLIDER variable will be either int or float depending on whether said slider can produce fractions. <br>
Now instead of using CInGameConfigWrapper's methods to access user configuration you can use this dedicated settings object.

It is also possible to change values of these variables and use it to write the data back into game configuration.
```js
mySettings.tab2subtab1.anotherSlider = 0.8;
mySettings.tab2subtab2.anotherToggle = false;

mySettings.WriteSettings();
```
Whenever you fetch the settings object its data is always up-to-date. You do not need to refresh said object yourself, but it is possible with `ReadSettings()` method.
It is also possible to save the data back into user configuration. To do this, after you assign new values to members of the settings object, call the `WriteSettings()` method.

<br>

7. Extend settings master
7. (Optional) Extend settings master

If the basic functionality that framework classes provide is not enough for you you can extend the settings class generated by the parser and use that child class instead.
If the basic functionality that framework classes provided is not enough for you you can extend the settings class generated by the parser and use that child class instead.
The most common usage of this would be overriding `ReadSettings()` method to run arbitrary code whenever the settings class gets updated. To see all the functions available for overriding refer to the [class specification](doc/class_specification.md) or code itself.


## Remarks
WitcherScript side of the framework was made using scripts from [Community Patch - Base](https://www.nexusmods.com/witcher3/mods/3652), so you should need to install it as well. Due to this it _may not_ be possible to be used with Enhanced Edition as that does not use CPB. Seperate compatibility versions may appear in the future, but for now if merging modSettingsFramework with your mod setup produces nasty results you may need to hold off with using it for now.
Framework version support:
- 0.3+ for game version 4.03
- 0.2.0 for game version 1.32 (requires [Community Patch - Base](https://www.nexusmods.com/witcher3/mods/3652))


## Documentation
Expand Down
4 changes: 2 additions & 2 deletions doc/class_specification.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Class specification


## [ISettingsGroup](modSettingsFramework/content/scripts/local/settings_group.ws)
## [ISettingsGroup](../modSettingsFramework/content/scripts/local/settings_group.ws)
Abstract base class which is an analogue to settings groups in xml. Child classes generated by the parser store settings variables.

### Public member constants
Expand All @@ -14,7 +14,7 @@ Abstract base class which is an analogue to settings groups in xml. Child classe
- `ResetToDefault() : void` - calls Reset() with defaultPresetIndex


## [`ISettingsMaster`](modSettingsFramework/content/scripts/local/settings_master.ws)
## [`ISettingsMaster`](../modSettingsFramework/content/scripts/local/settings_master.ws)
Abstract base class for the settings class generated by the parser. The child class generated by the parser stores instances of classes extending ISettingsGroup.

### Public member constants
Expand Down
Binary file not shown.
Binary file not shown.
Binary file modified modSettingsFramework/content/scripts/game/r4Game.ws
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class ModDifficultySettings extends ModDifficultySettingsBase
{
super.WriteSettings();

LogChannel('DifficultyMod', "Mod has been written to config");
LogChannel('DifficultyMod', "Mod settings have been written to config");
}

public function ShouldResetSettingsToDefaultOnInit() : bool
Expand All @@ -36,4 +36,18 @@ class ModDifficultySettings extends ModDifficultySettingsBase

LogChannel('DifficultyMod', "Preset " + IntToString(presetIndex) + " has been applied" );
}
}

function GetModDifficultySettings(): ModDifficultySettings
{
var settings : ModDifficultySettings;

settings = (ModDifficultySettings)GetSettingsMasterRegistry().GetSettings('ModDifficultySettings');
if (!settings)
{
settings = new ModDifficultySettings in theGame;
GetSettingsMasterRegistry().AddSettings(settings, 'ModDifficultySettings');
}

return settings;
}
Original file line number Diff line number Diff line change
@@ -1,61 +1,53 @@
exec function settings_difficulty_init()
{
var game: CR4Game;

game = theGame;
if(!game.difficultySettings) {
game.difficultySettings = new ModDifficultySettings in theGame;
GetSettingsMasterRegistry().AddSettings(game.difficultySettings, 'DifficultySettings');
}
}

exec function settings_difficulty_easy()
{
theGame.difficultySettings.general.Reset(0);
GetModDifficultySettings().general.Reset(0);
}

exec function settings_difficulty_default()
{
theGame.difficultySettings.general.ResetToDefault();
GetModDifficultySettings().general.ResetToDefault();
}

exec function settings_difficulty_hard()
{
var game: CR4Game;
var settings : ModDifficultySettings;
settings = GetModDifficultySettings();

game = theGame;
game.difficultySettings.general.enabled = true;
game.difficultySettings.general.healthMultip = 2.0;
game.difficultySettings.general.dmgMultip = 2.0;
settings.general.enabled = true;
settings.general.healthMultip = 2.0;
settings.general.dmgMultip = 2.0;

game.difficultySettings.WriteSettings();
settings.WriteSettings();
}

exec function settings_difficulty_toggle()
{
var game: CR4Game;
var settings : ModDifficultySettings;
settings = GetModDifficultySettings();

game = theGame;
game.difficultySettings.general.enabled = !game.difficultySettings.general.enabled;
game.difficultySettings.WriteSettings();
settings.general.enabled = !settings.general.enabled;
settings.WriteSettings();
}

exec function settings_difficulty_read()
{
theGame.difficultySettings.ReadSettings();
GetModDifficultySettings().ReadSettings();
}

exec function settings_difficulty_write()
{
theGame.difficultySettings.WriteSettings();
GetModDifficultySettings().WriteSettings();
}

exec function settings_difficulty_log()
{
var settings : ModDifficultySettings;
settings = GetModDifficultySettings();

LogChannel('DifficultyMod',
"Enabled: " + theGame.difficultySettings.general.enabled + ", " +
"Health multiplier: " + theGame.difficultySettings.general.healthMultip + ", " +
"Damage multiplier: " + theGame.difficultySettings.general.dmgMultip
"Enabled: " + settings.general.enabled + ", " +
"Health multiplier: " + settings.general.healthMultip + ", " +
"Damage multiplier: " + settings.general.dmgMultip
);
}

2 changes: 1 addition & 1 deletion settings-parser/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "tw3-mod-settings-framework-parser"
version = "0.2.0"
version = "0.3.0"
edition = "2021"

[[bin]]
Expand Down
16 changes: 10 additions & 6 deletions settings-parser/src/var_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ pub enum VarType {
Options,
SliderInt,
SliderFloat
// SubtleSeparator not included as it's just a cosmetic var
}

impl VarType {
pub fn from_display_type(display_type: &str) -> Result<VarType, String> {
pub fn from_display_type(display_type: &str) -> Result<Option<VarType>, String> {
if display_type == "TOGGLE" {
return Ok(VarType::Toggle);
return Ok(Some(VarType::Toggle));
}
if display_type == "OPTIONS" {
return Ok(VarType::Options);
return Ok(Some(VarType::Options));
}
if &display_type[0..6] == "SLIDER" {
let spl: Vec<&str> = display_type.split(';').collect();
Expand Down Expand Up @@ -58,14 +59,17 @@ impl VarType {
}

if (max - min) % div == 0 {
return Ok(VarType::SliderInt)
return Ok(Some(VarType::SliderInt))
}
else {
return Ok(VarType::SliderFloat)
return Ok(Some(VarType::SliderFloat))
}
}
}
if display_type == "SUBTLE_SEPARATOR" {
return Ok(None);
}

return Err(format!("Invalid display type: {}", display_type));
return Err(format!("Unsupported display type: {}", display_type));
}
}
5 changes: 4 additions & 1 deletion settings-parser/src/xml_parsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,10 @@ fn parse_var_node(var_node: &Node, group_id: &str, cli: &CLI) -> Result<Option<S
};

let var_type = match VarType::from_display_type(var_display_type) {
Ok(vt) => vt,
Ok(vto) => match vto {
Some(vt) => vt,
None => return Ok(None),
},
Err(err) => {
println!("Error parsing Var node's display_type in Group {} at {}: {}", group_id, node_pos(var_node), err);
return Ok(None);
Expand Down

0 comments on commit 74c04db

Please sign in to comment.