Compare commits

...
10 Commits
4 changed files with 193 additions and 11 deletions

No files matched your search

+55 -7
View File
@@ -1,9 +1,11 @@
using System; using System;
using System.IO; using System.IO;
using System.Text;
using RedLoader; using RedLoader;
using RedLoader.Utils; using RedLoader.Utils;
using Sons.Wearable.Race; using Sons.Wearable.Race;
using SonsSdk; using SonsSdk;
using SonsSdk.Attributes;
using TheForest.Utils; using TheForest.Utils;
using UnityEngine; using UnityEngine;
@@ -27,7 +29,7 @@ public class CharacterSelect : SonsMod
_configPath = Path.Combine(LoaderEnvironment.UserDataDirectory, "CharacterSelect.txt"); _configPath = Path.Combine(LoaderEnvironment.UserDataDirectory, "CharacterSelect.txt");
Load(); Load();
SdkEvents.OnAfterSpawn.Subscribe(OnSpawned); 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() private void OnSpawned()
@@ -37,32 +39,78 @@ public class CharacterSelect : SonsMod
private void OnUpdate() private void OnUpdate()
{ {
var system = LocalPlayer.RaceSystem; if (!LocalPlayer.RaceSystem)
if (!system)
return; return;
if (!_applied) if (!_applied)
{ {
_applied = true; _applied = true;
Apply(system, false); ApplyIndex(false);
return; return;
} }
if (Input.GetKeyDown(KeyCode.F9)) if (Input.GetKeyDown(KeyCode.F9))
{ {
_index = (_index + 1) % RaceCount; _index = (_index + 1) % RaceCount;
Apply(system, true); ApplyIndex(true);
Save(); 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; var race = (PlayerRace.Race)_index;
system.ApplyRace(race); system.ApplyRace(race);
if (announce) if (announce)
SonsTools.ShowMessage($"{_index}: {race}"); SonsTools.ShowMessage($"{_index}: {race}");
RLog.Msg($"Applied race {_index} ({race})"); RLog.Msg($"Applied character {_index} ({race})");
} }
private static void Load() 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 ''
+21 -2
View File
@@ -1,5 +1,7 @@
# Character Select # Character Select
[![Downloads](https://img.shields.io/github/downloads/JustinOros/sotf-character-select/total?label=downloads)](https://github.com/JustinOros/sotf-character-select/releases) [![Latest](https://img.shields.io/github/v/release/JustinOros/sotf-character-select?label=latest)](https://github.com/JustinOros/sotf-character-select/releases/latest)
Choose 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.
Sons of the Forest gives you a random appearance out of eight every time you join Sons of the Forest gives you a random appearance out of eight every time you join
@@ -54,13 +56,27 @@ Sons Of The Forest/
manifest.json manifest.json
``` ```
## Updating
Run the installer again. It replaces the old version and keeps your saved
appearance.
## Usage ## Usage
Once you are in the world, press F9 to cycle through the eight appearances. The Once you are in the world, press F9 to cycle through the eight appearances. The
current one is shown on screen as you cycle. current one is shown on screen as you cycle.
Your choice is saved to `_RedLoader/UserData/CharacterSelect.txt` and applied If your keyboard has no F keys, or you would rather pick one directly, press F1
automatically every time you spawn. to open the 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 | | Value | Name |
| --- | --- | | --- | --- |
@@ -75,6 +91,9 @@ automatically every time you spawn.
## Building ## Building
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 Requires the [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0) and a
[RedLoader](https://github.com/ToniMacaroni/RedLoader/releases/latest) install [RedLoader](https://github.com/ToniMacaroni/RedLoader/releases/latest) install
that has been launched at least once, so the interop assemblies exist. The build that has been launched at least once, so the interop assemblies exist. The build
+2 -2
View File
@@ -1,9 +1,9 @@
{ {
"$schema": "https://raw.githubusercontent.com/ToniMacaroni/RedLoader/main/MetadataSchema.json", "$schema": "https://raw.githubusercontent.com/ToniMacaroni/RedLoader/main/MetadataSchema.json",
"id": "CharacterSelect", "id": "CharacterSelect",
"name": "CharacterSelect", "name": "CharacterSelect",
"author": "JustinOros", "author": "JustinOros",
"version": "1.0.0", "version": "1.1.0",
"description": "Pick your player appearance and keep it across sessions", "description": "Pick your player appearance and keep it across sessions",
"gameVersion": "1.0.0", "gameVersion": "1.0.0",
"platform": "Client", "platform": "Client",