13 Commits
4 changed files with 255 additions and 28 deletions

No files matched your search

+55 -7
View File
@@ -1,9 +1,11 @@
using System;
using System.IO;
using System.Text;
using RedLoader;
using RedLoader.Utils;
using Sons.Wearable.Race;
using SonsSdk;
using SonsSdk.Attributes;
using TheForest.Utils;
using UnityEngine;
@@ -27,7 +29,7 @@ public class CharacterSelect : SonsMod
_configPath = Path.Combine(LoaderEnvironment.UserDataDirectory, "CharacterSelect.txt");
Load();
SdkEvents.OnAfterSpawn.Subscribe(OnSpawned);
RLog.Msg($"CharacterSelect loaded. Saved race: {_index} ({(PlayerRace.Race)_index}). F9 cycles.");
RLog.Msg($"CharacterSelect loaded. Saved character: {_index} ({(PlayerRace.Race)_index}). Press F9 or type: character");
}
private void OnSpawned()
@@ -37,32 +39,78 @@ public class CharacterSelect : SonsMod
private void OnUpdate()
{
var system = LocalPlayer.RaceSystem;
if (!system)
if (!LocalPlayer.RaceSystem)
return;
if (!_applied)
{
_applied = true;
Apply(system, false);
ApplyIndex(false);
return;
}
if (Input.GetKeyDown(KeyCode.F9))
{
_index = (_index + 1) % RaceCount;
Apply(system, true);
ApplyIndex(true);
Save();
}
}
private void Apply(PlayerRaceSystem system, bool announce)
[DebugCommand("character")]
private static void CharacterCommand(string args)
{
args = (args ?? string.Empty).Trim();
if (args.Length == 0)
{
ShowList();
return;
}
if (args.Equals("next", StringComparison.OrdinalIgnoreCase))
{
_index = (_index + 1) % RaceCount;
ApplyIndex(true);
Save();
return;
}
if (int.TryParse(args, out var value) && value >= 0 && value < RaceCount)
{
_index = value;
ApplyIndex(true);
Save();
return;
}
SonsTools.ShowMessage("Usage: character [0-7] or character next", 5f);
RLog.Msg("Usage: character [0-7] or character next");
}
private static void ShowList()
{
var sb = new StringBuilder();
sb.AppendLine($"Current: {_index} ({(PlayerRace.Race)_index})");
for (int i = 0; i < RaceCount; i++)
sb.AppendLine($"{i}: {(PlayerRace.Race)i}");
var text = sb.ToString().TrimEnd();
SonsTools.ShowMessage(text, 8f);
RLog.Msg(text);
}
private static void ApplyIndex(bool announce)
{
var system = LocalPlayer.RaceSystem;
if (!system)
return;
var race = (PlayerRace.Race)_index;
system.ApplyRace(race);
if (announce)
SonsTools.ShowMessage($"{_index}: {race}");
RLog.Msg($"Applied race {_index} ({race})");
RLog.Msg($"Applied character {_index} ({race})");
}
private static void Load()
+115
View File
@@ -0,0 +1,115 @@
[CmdletBinding()]
param(
[string]$GameDir
)
$ErrorActionPreference = 'Stop'
function Write-Step {
param([string]$Text)
Write-Host ''
Write-Host $Text -ForegroundColor Cyan
}
function Find-GameDir {
$steam = (Get-ItemProperty 'HKCU:\Software\Valve\Steam' -ErrorAction SilentlyContinue).SteamPath
if (-not $steam) { return $null }
$libs = @($steam)
$vdf = Join-Path $steam 'steamapps\libraryfolders.vdf'
if (Test-Path $vdf) {
Select-String -Path $vdf -Pattern '"path"\s+"(.+?)"' -AllMatches |
ForEach-Object { $_.Matches } |
ForEach-Object { $libs += $_.Groups[1].Value.Replace('\\','\') }
}
foreach ($lib in $libs) {
$candidate = Join-Path $lib 'steamapps\common\Sons Of The Forest'
if (Test-Path (Join-Path $candidate 'SonsOfTheForest.exe')) { return $candidate }
}
return $null
}
function Get-ReleaseZip {
param([string]$Repo, [string]$Pattern, [string]$OutFile)
$release = Invoke-RestMethod "https://api.github.com/repos/$Repo/releases/latest" -Headers @{ 'User-Agent' = 'ps' }
$asset = $release.assets | Where-Object { $_.name -match $Pattern } | Select-Object -First 1
if (-not $asset) { throw "Could not find a download matching $Pattern in the latest $Repo release" }
Write-Host " version $($release.tag_name)"
Invoke-WebRequest -Uri $asset.browser_download_url -OutFile $OutFile -UseBasicParsing
return $asset.name
}
Write-Host ''
Write-Host '====================================' -ForegroundColor Green
Write-Host ' Character Select installer' -ForegroundColor Green
Write-Host '====================================' -ForegroundColor Green
Write-Step 'Looking for Sons of the Forest'
if (-not $GameDir) { $GameDir = Find-GameDir }
if (-not $GameDir -or -not (Test-Path (Join-Path $GameDir 'SonsOfTheForest.exe'))) {
Write-Host ' Could not find the game automatically.' -ForegroundColor Yellow
Write-Host ' Find the folder that contains SonsOfTheForest.exe and paste the path below.'
$GameDir = (Read-Host ' Game folder').Trim('"')
if (-not (Test-Path (Join-Path $GameDir 'SonsOfTheForest.exe'))) {
throw "SonsOfTheForest.exe was not found in $GameDir"
}
}
Write-Host " found: $GameDir" -ForegroundColor Green
$needsFirstLaunch = $false
Write-Step 'Checking for RedLoader'
if (Test-Path (Join-Path $GameDir '_RedLoader\net6\SonsSdk.dll')) {
Write-Host ' already installed' -ForegroundColor Green
}
else {
Write-Host ' not installed, downloading it now'
$rlZip = Join-Path $env:TEMP 'RedLoader.zip'
Get-ReleaseZip -Repo 'ToniMacaroni/RedLoader' -Pattern '^RedLoader.*\.zip$' -OutFile $rlZip | Out-Null
$rlStage = Join-Path $env:TEMP 'redloader-stage'
if (Test-Path $rlStage) { Remove-Item $rlStage -Recurse -Force }
Expand-Archive -Path $rlZip -DestinationPath $rlStage -Force
Copy-Item -Path (Join-Path $rlStage '*') -Destination $GameDir -Recurse -Force
Remove-Item $rlZip -Force -ErrorAction SilentlyContinue
Remove-Item $rlStage -Recurse -Force -ErrorAction SilentlyContinue
Write-Host ' installed' -ForegroundColor Green
$needsFirstLaunch = $true
}
Write-Step 'Downloading Character Select'
$modZip = Join-Path $env:TEMP 'CharacterSelect.zip'
Get-ReleaseZip -Repo 'JustinOros/sotf-character-select' -Pattern '^CharacterSelect\.zip$' -OutFile $modZip | Out-Null
Write-Step 'Installing Character Select'
$modsDir = Join-Path $GameDir 'Mods'
New-Item -ItemType Directory -Force -Path $modsDir | Out-Null
Remove-Item (Join-Path $modsDir 'CharacterSelect.dll') -Force -ErrorAction SilentlyContinue
Remove-Item (Join-Path $modsDir 'CharacterSelect') -Recurse -Force -ErrorAction SilentlyContinue
Expand-Archive -Path $modZip -DestinationPath $modsDir -Force
Remove-Item $modZip -Force -ErrorAction SilentlyContinue
if (-not (Test-Path (Join-Path $modsDir 'CharacterSelect.dll'))) {
throw 'Something went wrong, CharacterSelect.dll is not in the Mods folder'
}
if (-not (Test-Path (Join-Path $modsDir 'CharacterSelect\manifest.json'))) {
throw 'Something went wrong, manifest.json is not in the Mods\CharacterSelect folder'
}
Write-Host ' installed' -ForegroundColor Green
Write-Host ''
Write-Host '====================================' -ForegroundColor Green
Write-Host ' Done' -ForegroundColor Green
Write-Host '====================================' -ForegroundColor Green
Write-Host ''
if ($needsFirstLaunch) {
Write-Host 'RedLoader was installed for the first time, so the next time you start'
Write-Host 'the game it will take a few extra minutes to get ready. That is normal.'
Write-Host ''
}
Write-Host 'Start Sons of the Forest, then:'
Write-Host ' 1. Check that MODS appears on the main menu and lists CharacterSelect'
Write-Host ' 2. Load into a game'
Write-Host ' 3. Press F9 to change your appearance'
Write-Host ''
+84 -20
View File
@@ -1,34 +1,75 @@
# Character Select
Pick your player appearance in Sons of the Forest and keep it across sessions.
Choose your player appearance in Sons of the Forest and keep it across sessions.
The game assigns one of eight player appearances at random when you join a server,
and only remembers it if the world gets saved. This mod lets you choose, and
reapplies your choice every time you spawn.
Sons of the Forest gives you a random appearance out of eight every time you join
a server, and there is no way to choose. This mod lets you pick the one you want
and automatically reapplies it every time your player spawns, so you stay the
same character.
## Notes
## Multiplayer
Client side only. Nothing needs to be installed on a dedicated server.
Client side only. Nothing gets installed on a dedicated server.
Your appearance replicates through normal game networking, so other players see
your choice even if they do not have the mod installed. They will not be able to
change their own appearance without it.
## Requirements
RedLoader
your choice even if they do not have the mod installed.
## Installation
1. Download the latest release
2. Extract into your Sons of the Forest folder so you end up with:
- `Mods/CharacterSelect.dll`
- `Mods/CharacterSelect/manifest.json`
### Step 1: Install RedLoader
[RedLoader](https://github.com/ToniMacaroni/RedLoader/releases/latest) is the mod
loader for Sons of the Forest. The game cannot load any mod without it, including
this one, so install it first. You only have to do this once.
1. Download `RedLoader.zip` from the
[latest RedLoader release](https://github.com/ToniMacaroni/RedLoader/releases/latest)
2. Extract it into your Sons of the Forest folder, the one containing
`SonsOfTheForest.exe`, usually
`C:\Program Files (x86)\Steam\steamapps\common\Sons Of The Forest`
3. You should now see `version.dll` and a `_Redloader` folder sitting next to
`SonsOfTheForest.exe`
4. Launch the game once and wait until you reach the main menu. The first launch
takes a few minutes while RedLoader processes the game files
5. Check that `MODS` appears on the main menu, then quit
Ignore `RedManager.exe` if you see it. It is only a convenience installer and
you do not need it.
### Step 2: Install Character Select
1. Download `CharacterSelect.zip` from the
[latest release](https://github.com/JustinOros/sotf-character-select/releases/latest)
2. Open the `Mods` folder inside your Sons of the Forest install, usually
`C:\Program Files (x86)\Steam\steamapps\common\Sons Of The Forest\Mods`
3. Extract the contents of the zip into that `Mods` folder
When you are done it should look like this:
```
Sons Of The Forest/
Mods/
CharacterSelect.dll
CharacterSelect/
manifest.json
```
## Usage
Press F9 to cycle through the eight appearances. Your choice is saved to
`_RedLoader/UserData/CharacterSelect.txt` and applied automatically on every spawn.
Once you are in the world, press F9 to cycle through the eight appearances. The
current one is shown on screen as you cycle.
If your keyboard has no F keys, or you would rather pick one directly, open the
in game console and use the `character` command:
| Command | What it does |
| --- | --- |
| `character` | Shows your current appearance and the full list |
| `character next` | Moves to the next appearance |
| `character 3` | Switches straight to that appearance |
Your choice is saved to `UserData/CharacterSelect.txt` in your Sons of the Forest
folder and applied automatically every time you spawn.
| Value | Name |
| --- | --- |
@@ -43,5 +84,28 @@ Press F9 to cycle through the eight appearances. Your choice is saved to
## Building
Requires the .NET 8 SDK and a RedLoader install that has been launched once, so
the interop assemblies exist. The script offers to install both if they are missing.
This section is only for those who want to modify the source or build it
yourself.
Requires the [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0) and a
[RedLoader](https://github.com/ToniMacaroni/RedLoader/releases/latest) install
that has been launched at least once, so the interop assemblies exist. The build
script offers to install both if they are missing.
```
.\build.ps1 -Install
```
The game folder is found through Steam. Override it with `-GameDir "path"` or the
`SOTF_PATH` environment variable.
To produce a release zip:
```
.\build.ps1 -Package
```
## Credits
Built against RedLoader by Toni Macaroni. The player race system was found by
reading the source of ArmorMod by clairenheit.
+1 -1
View File
@@ -1,4 +1,4 @@
{
{
"$schema": "https://raw.githubusercontent.com/ToniMacaroni/RedLoader/main/MetadataSchema.json",
"id": "CharacterSelect",
"name": "CharacterSelect",