Initial commit
This commit is contained in:
commit
a143b2a9dc
6 files changed
+550
No files matched your search
@@ -0,0 +1,4 @@
|
|||||||
|
bin/
|
||||||
|
obj/
|
||||||
|
dist/
|
||||||
|
*.zip
|
||||||
+253
@@ -0,0 +1,253 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using HarmonyLib;
|
||||||
|
using RedLoader;
|
||||||
|
using RedLoader.Utils;
|
||||||
|
using Sons.Weapon;
|
||||||
|
using SonsSdk;
|
||||||
|
using SonsSdk.Attributes;
|
||||||
|
using TheForest.Utils;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace FullAuto;
|
||||||
|
|
||||||
|
public class FullAuto : SonsMod
|
||||||
|
{
|
||||||
|
internal const float MinRpm = 60f;
|
||||||
|
internal const float MaxRpm = 3000f;
|
||||||
|
|
||||||
|
internal static bool Enabled = true;
|
||||||
|
internal static float Rpm = 900f;
|
||||||
|
internal static bool Verbose;
|
||||||
|
|
||||||
|
private static string _configPath;
|
||||||
|
|
||||||
|
public FullAuto()
|
||||||
|
{
|
||||||
|
HarmonyPatchAll = true;
|
||||||
|
OnUpdateCallback = OnUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnSdkInitialized()
|
||||||
|
{
|
||||||
|
_configPath = Path.Combine(LoaderEnvironment.UserDataDirectory, "FullAuto.txt");
|
||||||
|
Load();
|
||||||
|
RLog.Msg($"FullAuto loaded. Enabled: {Enabled}, RPM: {Rpm}. F10 toggles, console: fullauto");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUpdate()
|
||||||
|
{
|
||||||
|
if (!GameState.IsPlayerControllable)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Input.GetKeyDown(KeyCode.F10))
|
||||||
|
{
|
||||||
|
Enabled = !Enabled;
|
||||||
|
Save();
|
||||||
|
Announce();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[DebugCommand("fullauto")]
|
||||||
|
private static void FullAutoCommand(string args)
|
||||||
|
{
|
||||||
|
var parts = (args ?? string.Empty).Trim().Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
if (parts.Length == 0)
|
||||||
|
{
|
||||||
|
Announce();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (parts[0].ToLowerInvariant())
|
||||||
|
{
|
||||||
|
case "on":
|
||||||
|
Enabled = true;
|
||||||
|
break;
|
||||||
|
case "off":
|
||||||
|
Enabled = false;
|
||||||
|
break;
|
||||||
|
case "toggle":
|
||||||
|
Enabled = !Enabled;
|
||||||
|
break;
|
||||||
|
case "debug":
|
||||||
|
Verbose = !Verbose;
|
||||||
|
SonsTools.ShowMessage($"FullAuto debug: {Verbose}");
|
||||||
|
RLog.Msg($"FullAuto debug: {Verbose}");
|
||||||
|
return;
|
||||||
|
case "rpm":
|
||||||
|
if (parts.Length > 1 && float.TryParse(parts[1], out var value))
|
||||||
|
{
|
||||||
|
Rpm = Mathf.Clamp(value, MinRpm, MaxRpm);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Usage();
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
Usage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Save();
|
||||||
|
Announce();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Usage()
|
||||||
|
{
|
||||||
|
var text = $"Usage: fullauto [on|off|toggle|debug] or fullauto rpm <{MinRpm}-{MaxRpm}>";
|
||||||
|
SonsTools.ShowMessage(text, 5f);
|
||||||
|
RLog.Msg(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void Announce()
|
||||||
|
{
|
||||||
|
var text = $"FullAuto {(Enabled ? "ON" : "OFF")} ({Rpm} RPM)";
|
||||||
|
SonsTools.ShowMessage(text);
|
||||||
|
RLog.Msg(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Load()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!File.Exists(_configPath))
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var line in File.ReadAllLines(_configPath))
|
||||||
|
{
|
||||||
|
var kv = line.Split('=', 2);
|
||||||
|
if (kv.Length != 2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var key = kv[0].Trim().ToLowerInvariant();
|
||||||
|
var val = kv[1].Trim();
|
||||||
|
|
||||||
|
if (key == "enabled" && bool.TryParse(val, out var enabled))
|
||||||
|
Enabled = enabled;
|
||||||
|
else if (key == "rpm" && float.TryParse(val, out var rpm))
|
||||||
|
Rpm = Mathf.Clamp(rpm, MinRpm, MaxRpm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
RLog.Error($"FullAuto could not read config: {e.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Save()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.WriteAllText(_configPath, $"enabled={Enabled}\nrpm={Rpm}\n");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
RLog.Error($"FullAuto could not write config: {e.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(RangedWeaponController), nameof(RangedWeaponController.CheckFireInput))]
|
||||||
|
internal static class CheckFireInputPatch
|
||||||
|
{
|
||||||
|
private const int MaxShotsPerFrame = 4;
|
||||||
|
|
||||||
|
private static readonly HashSet<string> Excluded = new()
|
||||||
|
{
|
||||||
|
"GrenadeWeaponController",
|
||||||
|
"MolotovWeaponController",
|
||||||
|
"TimeBombWeaponController",
|
||||||
|
"SmallRockWeaponController",
|
||||||
|
"RopeGunController"
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly Dictionary<IntPtr, bool> Allowed = new();
|
||||||
|
private static readonly HashSet<string> Reported = new();
|
||||||
|
private static float _nextShot;
|
||||||
|
private static bool _loggedHook;
|
||||||
|
|
||||||
|
private static bool Prefix(RangedWeaponController __instance)
|
||||||
|
{
|
||||||
|
if (!_loggedHook)
|
||||||
|
{
|
||||||
|
_loggedHook = true;
|
||||||
|
RLog.Msg("FullAuto: CheckFireInput hook is firing");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!FullAuto.Enabled || !__instance.IsLocalPlayer() || !IsAllowed(__instance))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!Input.GetMouseButton(0) || !GameState.IsPlayerControllable)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (__instance._isReloading || __instance.IsReloading())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (__instance._mustAimToFire && !__instance.IsAiming)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
var weapon = __instance.GetRangedWeapon();
|
||||||
|
if (!weapon)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
var ammo = weapon.GetAmmo();
|
||||||
|
if (ammo == null || ammo.GetRemainingAmmo() <= 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
var now = Time.time;
|
||||||
|
var interval = 60f / FullAuto.Rpm;
|
||||||
|
|
||||||
|
if (_nextShot < now - interval)
|
||||||
|
_nextShot = now;
|
||||||
|
|
||||||
|
var shots = 0;
|
||||||
|
while (_nextShot <= now && shots < MaxShotsPerFrame && ammo.GetRemainingAmmo() > 0)
|
||||||
|
{
|
||||||
|
Fire(__instance, ammo);
|
||||||
|
_nextShot += interval;
|
||||||
|
shots++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Fire(RangedWeaponController controller, RangedWeapon.Ammo ammo)
|
||||||
|
{
|
||||||
|
var before = ammo.GetRemainingAmmo();
|
||||||
|
|
||||||
|
controller._fireDelay = 0f;
|
||||||
|
controller._nextFireDelay = 0f;
|
||||||
|
controller.FireWeapon();
|
||||||
|
|
||||||
|
if (controller._playFireAudio == RangedWeaponController.FireAudio.OnAnimShootCallback)
|
||||||
|
{
|
||||||
|
controller.TriggerShotFiredAudio();
|
||||||
|
if (controller.ShouldShowMuzzleFlash())
|
||||||
|
controller.OnMuzzleFlash();
|
||||||
|
}
|
||||||
|
|
||||||
|
controller._attackState = RangedWeaponController.AttackState.Idle;
|
||||||
|
|
||||||
|
var after = ammo.GetRemainingAmmo();
|
||||||
|
var name = controller.GetIl2CppType().Name;
|
||||||
|
|
||||||
|
if (Reported.Add(name))
|
||||||
|
RLog.Msg($"FullAuto first shot with {name}: ammo {before} -> {after}, audio {controller._playFireAudio}");
|
||||||
|
|
||||||
|
if (FullAuto.Verbose)
|
||||||
|
RLog.Msg($"FullAuto shot {name}: ammo {before} -> {after}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsAllowed(RangedWeaponController controller)
|
||||||
|
{
|
||||||
|
var ptr = controller.Pointer;
|
||||||
|
if (Allowed.TryGetValue(ptr, out var allowed))
|
||||||
|
return allowed;
|
||||||
|
|
||||||
|
allowed = !Excluded.Contains(controller.GetIl2CppType().Name);
|
||||||
|
Allowed[ptr] = allowed;
|
||||||
|
return allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<AssemblyName>FullAuto</AssemblyName>
|
||||||
|
<RootNamespace>FullAuto</RootNamespace>
|
||||||
|
<Version>1.0.0</Version>
|
||||||
|
<NoWarn>CS0436;CS1701;CS1702;MSB3277;MSB3106</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(GameDir)' == ''">
|
||||||
|
<GameDir>$(SOTF_PATH)</GameDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Target Name="CheckGameDir" BeforeTargets="BeforeBuild">
|
||||||
|
<Error Condition="'$(GameDir)' == ''"
|
||||||
|
Text="GameDir is not set. Run build.ps1, pass -p:GameDir="path", or set SOTF_PATH." />
|
||||||
|
<Error Condition="!Exists('$(GameDir)\_RedLoader\Game\Sons.dll')"
|
||||||
|
Text="No RedLoader interop assemblies at $(GameDir)\_RedLoader\Game. Install RedLoader and launch the game once." />
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="$(GameDir)\_RedLoader\Game\*.dll" Private="false" />
|
||||||
|
<Reference Include="$(GameDir)\_RedLoader\net6\RedLoader.dll" Private="false" />
|
||||||
|
<Reference Include="$(GameDir)\_RedLoader\net6\SonsSdk.dll" Private="false" />
|
||||||
|
<Reference Include="$(GameDir)\_RedLoader\net6\Il2CppInterop.Runtime.dll" Private="false" />
|
||||||
|
<Reference Include="$(GameDir)\_RedLoader\net6\Il2CppInterop.Common.dll" Private="false" />
|
||||||
|
<Reference Include="$(GameDir)\_RedLoader\net6\0Harmony.dll" Private="false" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="manifest.json" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
# FullAuto
|
||||||
|
|
||||||
|
Hold the fire button to shoot any gun full-auto in Sons of the Forest.
|
||||||
|
|
||||||
|
Works with the pistol, revolver, shotgun, rifle and other guns. The fire rate is
|
||||||
|
adjustable and your settings are saved between sessions.
|
||||||
|
|
||||||
|
## Multiplayer
|
||||||
|
|
||||||
|
Client side only. Nothing gets installed on a dedicated server, and other
|
||||||
|
players do not need the mod.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### 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, 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. 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
|
||||||
|
4. Check that `MODS` appears on the main menu, then quit
|
||||||
|
|
||||||
|
### Step 2: Install FullAuto
|
||||||
|
|
||||||
|
1. Download `FullAuto.zip` from the
|
||||||
|
[latest release](https://github.com/JustinOros/sotf-fullauto/releases/latest)
|
||||||
|
2. Extract it into the `Mods` folder inside your game folder
|
||||||
|
3. You should end up with:
|
||||||
|
|
||||||
|
```
|
||||||
|
Mods\FullAuto.dll
|
||||||
|
Mods\FullAuto\manifest.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Equip a gun and hold the fire button.
|
||||||
|
|
||||||
|
| Control | Action |
|
||||||
|
| --- | --- |
|
||||||
|
| F8 | Toggle the mod on or off |
|
||||||
|
| F1 | Open the console |
|
||||||
|
|
||||||
|
Console commands:
|
||||||
|
|
||||||
|
| Command | Action |
|
||||||
|
| --- | --- |
|
||||||
|
| `fullauto` | Show current settings |
|
||||||
|
| `fullauto on` | Enable |
|
||||||
|
| `fullauto off` | Disable |
|
||||||
|
| `fullauto rpm 1200` | Set the fire rate, 60 to 6000 rounds per minute |
|
||||||
|
| `fullauto mode shoot` | Change the fire method, `shoot`, `fire` or `trigger` |
|
||||||
|
|
||||||
|
The default is 900 rpm with mode `shoot`. If a gun does not fire full-auto, try
|
||||||
|
another mode.
|
||||||
|
|
||||||
|
Settings are saved to `UserData\FullAuto.cfg` in your game folder.
|
||||||
|
|
||||||
|
## Excluded weapons
|
||||||
|
|
||||||
|
The bow, slingshot, rope gun and thrown items such as grenades, molotovs, time
|
||||||
|
bombs and rocks keep their normal behavior.
|
||||||
|
|
||||||
|
## Building from source
|
||||||
|
|
||||||
|
Requires the .NET 8 SDK and RedLoader installed with its game assemblies
|
||||||
|
generated.
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\build.ps1 -Install
|
||||||
|
```
|
||||||
|
|
||||||
|
Use `-Package` to build `FullAuto.zip` for a release. Pass `-GameDir "path"` if
|
||||||
|
the game is not found automatically.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
Check `_RedLoader\Latest.log` in your game folder. FullAuto logs a line when it
|
||||||
|
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[string]$GameDir,
|
||||||
|
[switch]$Install,
|
||||||
|
[switch]$Package
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
function Test-Dotnet {
|
||||||
|
$cmd = Get-Command dotnet -ErrorAction SilentlyContinue
|
||||||
|
if (-not $cmd) { return $false }
|
||||||
|
$sdks = & dotnet --list-sdks 2>$null
|
||||||
|
return ($LASTEXITCODE -eq 0 -and $sdks)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Update-Path {
|
||||||
|
$env:Path = [System.Environment]::GetEnvironmentVariable('Path','Machine') + ';' +
|
||||||
|
[System.Environment]::GetEnvironmentVariable('Path','User')
|
||||||
|
$dotnetRoot = Join-Path $env:ProgramFiles 'dotnet'
|
||||||
|
if ((Test-Path $dotnetRoot) -and ($env:Path -notlike "*$dotnetRoot*")) {
|
||||||
|
$env:Path += ";$dotnetRoot"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Install-Dotnet {
|
||||||
|
if (Get-Command winget -ErrorAction SilentlyContinue) {
|
||||||
|
Write-Host 'Installing the .NET 8 SDK with winget' -ForegroundColor Cyan
|
||||||
|
winget install --id Microsoft.DotNet.SDK.8 --accept-source-agreements --accept-package-agreements --silent
|
||||||
|
Update-Path
|
||||||
|
if (Test-Dotnet) { return }
|
||||||
|
Write-Host ' winget install did not make dotnet available, falling back' -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host 'Downloading the official dotnet-install script' -ForegroundColor Cyan
|
||||||
|
$script = Join-Path $env:TEMP 'dotnet-install.ps1'
|
||||||
|
Invoke-WebRequest -Uri 'https://dot.net/v1/dotnet-install.ps1' -OutFile $script -UseBasicParsing
|
||||||
|
|
||||||
|
Write-Host 'Installing the .NET 8 SDK to your user profile' -ForegroundColor Cyan
|
||||||
|
& $script -Channel 8.0 -InstallDir "$env:LOCALAPPDATA\Microsoft\dotnet"
|
||||||
|
$userDotnet = "$env:LOCALAPPDATA\Microsoft\dotnet"
|
||||||
|
$env:Path = "$userDotnet;$env:Path"
|
||||||
|
[Environment]::SetEnvironmentVariable(
|
||||||
|
'Path',
|
||||||
|
"$userDotnet;" + [Environment]::GetEnvironmentVariable('Path','User'),
|
||||||
|
'User')
|
||||||
|
Remove-Item $script -Force -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
function Install-RedLoader {
|
||||||
|
param([string]$Target)
|
||||||
|
|
||||||
|
Write-Host 'Looking up the latest RedLoader release' -ForegroundColor Cyan
|
||||||
|
$release = Invoke-RestMethod 'https://api.github.com/repos/ToniMacaroni/RedLoader/releases/latest' -Headers @{ 'User-Agent' = 'ps' }
|
||||||
|
$asset = $release.assets | Where-Object { $_.name -match '^RedLoader.*\.zip$' } | Select-Object -First 1
|
||||||
|
if (-not $asset) { throw 'Could not find RedLoader.zip in the latest release' }
|
||||||
|
Write-Host " $($release.tag_name) / $($asset.name)"
|
||||||
|
|
||||||
|
$zip = Join-Path $env:TEMP $asset.name
|
||||||
|
Invoke-WebRequest -Uri $asset.browser_download_url -OutFile $zip -UseBasicParsing
|
||||||
|
|
||||||
|
$stage = Join-Path $env:TEMP 'redloader-extract'
|
||||||
|
if (Test-Path $stage) { Remove-Item $stage -Recurse -Force }
|
||||||
|
Expand-Archive -Path $zip -DestinationPath $stage -Force
|
||||||
|
Copy-Item -Path (Join-Path $stage '*') -Destination $Target -Recurse -Force
|
||||||
|
Remove-Item $zip -Force -ErrorAction SilentlyContinue
|
||||||
|
Remove-Item $stage -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
Write-Host ' RedLoader installed' -ForegroundColor Green
|
||||||
|
}
|
||||||
|
|
||||||
|
function Find-GameDir {
|
||||||
|
if ($env:SOTF_PATH) { return $env:SOTF_PATH }
|
||||||
|
$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
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Dotnet)) {
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host 'The .NET 8 SDK is required to build this mod and was not found.' -ForegroundColor Yellow
|
||||||
|
$answer = Read-Host 'Download and install it now? (y/N)'
|
||||||
|
if ($answer -ne 'y') {
|
||||||
|
throw 'Cannot build without the .NET SDK. Get it at https://dotnet.microsoft.com/download'
|
||||||
|
}
|
||||||
|
Install-Dotnet
|
||||||
|
if (-not (Test-Dotnet)) {
|
||||||
|
throw 'The SDK was installed but dotnet is still not on PATH. Reopen PowerShell and rerun this script.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Write-Host ("dotnet SDK: " + (dotnet --version)) -ForegroundColor Green
|
||||||
|
|
||||||
|
if (-not $GameDir) { $GameDir = Find-GameDir }
|
||||||
|
if (-not $GameDir) {
|
||||||
|
throw 'Could not find Sons Of The Forest. Pass -GameDir "path" or set SOTF_PATH.'
|
||||||
|
}
|
||||||
|
Write-Host "Game directory: $GameDir" -ForegroundColor Cyan
|
||||||
|
|
||||||
|
if (-not (Test-Path (Join-Path $GameDir '_RedLoader\net6\SonsSdk.dll'))) {
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host 'RedLoader is not installed in your game folder.' -ForegroundColor Yellow
|
||||||
|
$answer = Read-Host 'Download and install the latest RedLoader release now? (y/N)'
|
||||||
|
if ($answer -ne 'y') {
|
||||||
|
throw 'RedLoader is required. Get it at https://github.com/ToniMacaroni/RedLoader/releases'
|
||||||
|
}
|
||||||
|
Install-RedLoader -Target $GameDir
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path (Join-Path $GameDir '_RedLoader\Game\Sons.dll'))) {
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host 'RedLoader is installed but has not generated its game assemblies yet.' -ForegroundColor Yellow
|
||||||
|
Write-Host 'Launch Sons of the Forest once and wait for the main menu. The first'
|
||||||
|
Write-Host 'launch takes a few minutes while it processes the game files.'
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host 'Then quit the game and run this script again.'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Write-Host 'RedLoader ready' -ForegroundColor Green
|
||||||
|
|
||||||
|
dotnet build -c Release --nologo -p:GameDir="$GameDir"
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw 'Build failed' }
|
||||||
|
|
||||||
|
$dll = Join-Path $PSScriptRoot 'bin\Release\net6\FullAuto.dll'
|
||||||
|
|
||||||
|
if ($Install) {
|
||||||
|
$modsDir = Join-Path $GameDir 'Mods'
|
||||||
|
$modDataDir = Join-Path $modsDir 'FullAuto'
|
||||||
|
New-Item -ItemType Directory -Force -Path $modDataDir | Out-Null
|
||||||
|
Copy-Item $dll -Destination $modsDir -Force
|
||||||
|
Copy-Item (Join-Path $PSScriptRoot 'manifest.json') -Destination $modDataDir -Force
|
||||||
|
Write-Host "Installed to $modsDir" -ForegroundColor Green
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($Package) {
|
||||||
|
$stage = Join-Path $PSScriptRoot 'dist'
|
||||||
|
$zip = Join-Path $PSScriptRoot 'FullAuto.zip'
|
||||||
|
|
||||||
|
if (Test-Path $stage) { Remove-Item $stage -Recurse -Force }
|
||||||
|
New-Item -ItemType Directory -Force -Path (Join-Path $stage 'FullAuto') | Out-Null
|
||||||
|
Copy-Item $dll -Destination $stage -Force
|
||||||
|
Copy-Item (Join-Path $PSScriptRoot 'manifest.json') -Destination (Join-Path $stage 'FullAuto') -Force
|
||||||
|
|
||||||
|
if (Test-Path $zip) { Remove-Item $zip -Force }
|
||||||
|
Compress-Archive -Path (Join-Path $stage '*') -DestinationPath $zip
|
||||||
|
Remove-Item $stage -Recurse -Force
|
||||||
|
|
||||||
|
Write-Host "Packaged $zip" -ForegroundColor Green
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://raw.githubusercontent.com/ToniMacaroni/RedLoader/main/MetadataSchema.json",
|
||||||
|
"id": "FullAuto",
|
||||||
|
"name": "FullAuto",
|
||||||
|
"author": "JustinOros",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Hold fire to shoot any gun full-auto at a configurable rate",
|
||||||
|
"gameVersion": "1.0.0",
|
||||||
|
"platform": "Client",
|
||||||
|
"type": "Mod"
|
||||||
|
}
|
||||||
Reference in new issue
Block a user