diff --git a/.gitignore b/.gitignore
index bc78471..2e90c7a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -482,3 +482,9 @@ $RECYCLE.BIN/
# Vim temporary swap files
*.swp
+
+# VS Code
+.vscode
+
+# Build artifacts
+Packages/
diff --git a/Editor/HookahAssetBuilder.cs b/Editor/HookahAssetBuilder.cs
new file mode 100644
index 0000000..a9e4794
--- /dev/null
+++ b/Editor/HookahAssetBuilder.cs
@@ -0,0 +1,158 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+using System.Windows;
+
+public class HookahAssetBuilder
+{
+ static readonly string ModManagerProfilesPath =
+ @"C:\Users\user\AppData\Roaming\com.kesomannen.gale\lethal-company\profiles";
+ static readonly string ModPackProfileName = "HookahPlace Test";
+ static readonly string ModPackModName = "HookahPlace-DEV";
+ static readonly string ModPackProfilePath = $@"{ModManagerProfilesPath}/{ModPackProfileName}";
+ static readonly string ModPackHookahPlacePath = @$"{ModPackProfilePath}/BepInEx/plugins/{ModPackModName}";
+
+ static readonly string ModProjectPath = @"D:\Code\LC-Decompiled\Mods\HookahPlace";
+ static readonly string DllName = "Ratijas.HookahPlace.dll";
+ static readonly string DllSourcePath = $@"{ModProjectPath}/HookahPlace/bin/Debug/netstandard2.1/{DllName}";
+ static readonly string[] DllDestinationPaths = new string[]
+ {
+ // relative to Unity project
+ $@"Assets/LethalCompany/Mods/plugins/HookahPlace/Dependencies/{ModPackModName}",
+ ModPackHookahPlacePath,
+ };
+
+ static readonly string AssetBundlesPath = "Assets/LethalCompany/Mods/plugins/HookahPlace/AssetBundles";
+ static readonly string[] AssetBundlesNames = new string[]
+ {
+ "hookahplaceasset",
+ "hookahunlockableassets",
+ };
+ static readonly string[] AssetBundlesDestinationPaths = new string[]
+ {
+ @$"{ModPackHookahPlacePath}/Assets",
+ @$"{ModProjectPath}/HookahPlace/res",
+ };
+
+ [MenuItem("HookahPlace/All")]
+ public static void DoAll()
+ {
+ CompileScripts();
+ BuildAssetBundles();
+ InstallAssetBundles();
+ LaunchGame();
+ }
+
+ [MenuItem("HookahPlace/1. Compile Scripts")]
+ public static void CompileScripts()
+ {
+ var psi = new ProcessStartInfo
+ {
+ FileName = "dotnet",
+ Arguments = "build",
+ UseShellExecute = false,
+ };
+
+ var proc = Process.Start(psi);
+ proc.WaitForExit();
+
+ if (proc.ExitCode != 0)
+ {
+ UnityEngine.Debug.Log($"Process exited with non-zero code: {proc.ExitCode}");
+ // proc.StandardOutput
+ return;
+ }
+
+ foreach (var destinationPath in DllDestinationPaths)
+ {
+ Directory.CreateDirectory(destinationPath);
+
+ var sourceFileName = DllSourcePath;
+ var destFileName = $"{destinationPath}/{DllName}";
+ FileCopy(sourceFileName, destFileName);
+ }
+ }
+
+ [MenuItem("HookahPlace/2. Build Asset Bundles")]
+ public static void BuildAssetBundles()
+ {
+ UnityEngine.Debug.Log("Building asset bundles for HookahPlace...");
+
+ Directory.CreateDirectory(AssetBundlesPath);
+
+ BuildPipeline.BuildAssetBundles(AssetBundlesPath, BuildAssetBundleOptions.ChunkBasedCompression, BuildTarget.StandaloneWindows);
+
+ if (Application.isBatchMode)
+ {
+ EditorApplication.Exit(0);
+ }
+ }
+
+ [MenuItem("HookahPlace/3. Install Asset Bundles")]
+ public static void InstallAssetBundles()
+ {
+ UnityEngine.Debug.Log("Installing asset bundles for HookahPlace...");
+
+ foreach (var destinationPath in AssetBundlesDestinationPaths)
+ {
+ Directory.CreateDirectory(destinationPath);
+
+ foreach (var name in AssetBundlesNames)
+ {
+ var sourceFileName = $"{AssetBundlesPath}/{name}";
+ var destFileName = $"{destinationPath}/{name}";
+ FileCopy(sourceFileName, destFileName);
+ }
+ }
+ }
+
+ [MenuItem("HookahPlace/4. Launch Game")]
+ public static void LaunchGame()
+ {
+ UnityEngine.Debug.Log($"Launching Gale profile with profile '{ModPackProfileName}'...");
+
+ string BepInExPreloaderDllPath = $@"{ModPackProfilePath}/BepInEx/core/BepInEx.Preloader.dll";
+
+ string[] args = new string[]
+ {
+ @"C:\Program Files (x86)\Steam\steam.exe",
+ "-applaunch",
+ "1966720",
+ "--doorstop-enabled",
+ "true",
+ "--doorstop-target-assembly",
+ BepInExPreloaderDllPath,
+ };
+
+ var psi = new ProcessStartInfo
+ {
+ FileName = args[0],
+ Arguments = string.Join(" ", args.Skip(1).Select(QuoteArgument)),
+ UseShellExecute = false,
+ };
+
+ Process.Start(psi);
+
+ GUIUtility.systemCopyBuffer = "hookah";
+ }
+
+ static void FileCopy(string sourceFileName, string destFileName)
+ {
+ UnityEngine.Debug.Log($"File.Copy {sourceFileName} => {destFileName}");
+ File.Copy(sourceFileName, destFileName, overwrite: true);
+ }
+
+ static string QuoteArgument(string arg)
+ {
+ if (string.IsNullOrEmpty(arg))
+ return "\"\"";
+
+ if (arg.IndexOfAny(new char[] { ' ', '\t', '"' }) == -1)
+ return arg;
+
+ return "\"" + arg.Replace("\"", "\\\"") + "\"";
+ }
+}
diff --git a/Editor/README.md b/Editor/README.md
new file mode 100644
index 0000000..80b460e
--- /dev/null
+++ b/Editor/README.md
@@ -0,0 +1 @@
+This folder contains C# scripts for Unity project with the decompiled game.
diff --git a/HookahPlace/DEVELOPMENT.md b/HookahPlace/DEVELOPMENT.md
new file mode 100644
index 0000000..51bf7d1
--- /dev/null
+++ b/HookahPlace/DEVELOPMENT.md
@@ -0,0 +1,5 @@
+# Development
+
+- Keep in sync versions of DawnLib in `thunderstore.toml` and `*.csproj`.
+- Build AssetBundles in Unity, and copy them to the `res` folder.
+- Build in the `Release` configuration to create the zip
diff --git a/HookahPlace/HookahPlace.csproj b/HookahPlace/HookahPlace.csproj
index 0798e29..50330a8 100644
--- a/HookahPlace/HookahPlace.csproj
+++ b/HookahPlace/HookahPlace.csproj
@@ -4,13 +4,12 @@
Ratijas.HookahPlace
HookahPlace
-
- 1.0.0
+ 4.1.9
netstandard2.1
- CRLib._ModTemplate
+ HookahPlace
true
latest
@@ -37,16 +36,15 @@
true
+
embedded
-
- $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)'))=./
+
+
+ $(UserProfile)=~,$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)'))=$(PackageId)/
- 0.4.*
+ 0.7.3
@@ -58,7 +56,7 @@
-
+
diff --git a/HookahPlace/HookahPlace.csproj.user.template b/HookahPlace/HookahPlace.csproj.user.template
index 1b05b60..5866c8f 100644
--- a/HookahPlace/HookahPlace.csproj.user.template
+++ b/HookahPlace/HookahPlace.csproj.user.template
@@ -1,17 +1,11 @@
-
.../Lethal Company/BepInEx/plugins/
-
-
.../Lethal Comapny/BepInEx/plugins/MMHOOK/
-
-
-
-
diff --git a/HookahPlace/README.md b/HookahPlace/README.md
index b59b1a7..2fb385a 100644
--- a/HookahPlace/README.md
+++ b/HookahPlace/README.md
@@ -1,12 +1,21 @@
-# TODO before release:
-- `thunderstore.toml`:
- - **Important**: Set `namespace` (this is your author name) and `description`
- - Set `websiteUrl`
- - Update `DawnLib` version
- - Note that the name of your mod is determined by the name of the project, and the version is determined in your `.csproj`
-- `.csproj`:
- - Update `DawnLib` version
-- `HookahPlaceKeys.cs`:
- - Look in this file
-- Update this README! (it gets used to generate your mods readme)
-- Finally, build in the `Release` configuration to create the zip
\ No newline at end of file
+# Hookah Place
+
+_Relaxing hookah as a ship upgrade_
+
+Welcome to your very own Lethal Hookah Place!
+
+Become a ship-mom (or a ship-dad) and smoke shisha while guiding your crew over a walkie-talkie. Let 'em know you're having a good time! 🗣💯ðŸ’
+
+
+
+## Store
+
+The mod adds a ship decor/furniture to the store called "Hookah".
+
+The store rotates between random 4-6 entries every quota. To make the hookah always available in the store, use another mod like [`StoreRotationConfig` by `pacoito`](https://thunderstore.io/c/lethal-company/p/pacoito/StoreRotationConfig/) with the `stockAll` option toggled ON.
+
+## Made by Ratijas
+
+Also check out my other mod, 🎵 [Muzika Gromche — The ultimate Jester party music mod](https://thunderstore.io/c/lethal-company/p/Ratijas/MuzikaGromche/)!
+
+Hookah model made in Blender: [HookahPlace.blend](https://ratijas.me/share/public/LC/HookahPlace/HookahPlace.blend)
diff --git a/HookahPlace/Thunderstore/CHANGELOG.md b/HookahPlace/Thunderstore/CHANGELOG.md
index 6185211..d1bf2b3 100644
--- a/HookahPlace/Thunderstore/CHANGELOG.md
+++ b/HookahPlace/Thunderstore/CHANGELOG.md
@@ -1,2 +1,3 @@
-# 1.0.0
-Inital release
\ No newline at end of file
+# 4.1.9
+
+Inital release
diff --git a/HookahPlace/Thunderstore/HookahPlace-demo-1.jpg b/HookahPlace/Thunderstore/HookahPlace-demo-1.jpg
new file mode 100644
index 0000000..ee9d7e7
Binary files /dev/null and b/HookahPlace/Thunderstore/HookahPlace-demo-1.jpg differ
diff --git a/HookahPlace/Thunderstore/icon.png b/HookahPlace/Thunderstore/icon.png
new file mode 100644
index 0000000..6cf3ba6
Binary files /dev/null and b/HookahPlace/Thunderstore/icon.png differ
diff --git a/HookahPlace/Thunderstore/thunderstore.toml b/HookahPlace/Thunderstore/thunderstore.toml
index 72defb0..b0192c2 100644
--- a/HookahPlace/Thunderstore/thunderstore.toml
+++ b/HookahPlace/Thunderstore/thunderstore.toml
@@ -2,13 +2,16 @@
schemaVersion = "0.0.1"
[package]
-namespace = ""
-description = ""
-websiteUrl = ""
+namespace = "Ratijas"
+name = "HookahPlace"
+versionNumber = "4.1.9"
+description = "Relaxing hookah as a ship upgrade"
+websiteUrl = "https://git.vilunov.me/ratijas/HookahPlace"
containsNsfwContent = false
+
[package.dependencies]
-BepInEx-BepInExPack = "5.4.2100"
-TeamXiaolan-DawnLib = "0.4.0"
+BepInEx-BepInExPack = "5.4.2304"
+TeamXiaolan-DawnLib = "0.7.3"
[build]
icon = "./icon.png"
@@ -29,6 +32,7 @@ target = "/"
[publish]
repository = "https://thunderstore.io"
-communities = [ "lethal-company", ]
+communities = [ "lethal-company" ]
[publish.categories]
-lethal-company = [ "mods", "tools", "libraries", "clientside", "serverside" ]
\ No newline at end of file
+# https://thunderstore.io/api/experimental/community/lethal-company/category/
+lethal-company = [ "mods", "furniture", "clientside", "serverside" ]
diff --git a/HookahPlace/res/ASSETBUNDLES HERE b/HookahPlace/res/ASSETBUNDLES HERE
deleted file mode 100644
index e69de29..0000000
diff --git a/HookahPlace/res/hookahplaceasset b/HookahPlace/res/hookahplaceasset
new file mode 100644
index 0000000..873d629
Binary files /dev/null and b/HookahPlace/res/hookahplaceasset differ
diff --git a/HookahPlace/res/hookahunlockableassets b/HookahPlace/res/hookahunlockableassets
new file mode 100644
index 0000000..e779806
Binary files /dev/null and b/HookahPlace/res/hookahunlockableassets differ
diff --git a/HookahPlace/src/Content/ExampleContentHandler.cs b/HookahPlace/src/Content/ExampleContentHandler.cs
deleted file mode 100644
index 1fe0a70..0000000
--- a/HookahPlace/src/Content/ExampleContentHandler.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using Dawn;
-using Dusk;
-
-namespace HookahPlace.Content;
-
-// REMOVE THIS FILE IF NOT USING DUSK
-public class ExampleContentHandler : ContentHandler
-{
- public ExampleContentHandler(DuskMod mod) : base(mod)
- {
- RegisterContent("content bundle name here", out DefaultBundle? bundle);
- }
-}
\ No newline at end of file
diff --git a/HookahPlace/src/Content/HookahContentHandler.cs b/HookahPlace/src/Content/HookahContentHandler.cs
new file mode 100644
index 0000000..26a96d0
--- /dev/null
+++ b/HookahPlace/src/Content/HookahContentHandler.cs
@@ -0,0 +1,19 @@
+using Dusk;
+
+namespace HookahPlace.Content;
+
+public class HookahContentHandler : ContentHandler
+{
+ const int HOOKAH_PRICE = 130;
+
+ public HookahContentHandler(DuskMod mod) : base(mod)
+ {
+ NukeDawnLibConfig.NukeUnlockable(HookahPlace.Config, "HookahUnlockable", "Hookah", HOOKAH_PRICE);
+
+ if (!RegisterContent("hookahunlockableassets", out DefaultBundle? bundle, forceEnabled: true) || bundle == null)
+ {
+ HookahPlace.Logger.LogError($"Failed to register content");
+ return;
+ }
+ }
+}
diff --git a/HookahPlace/src/Content/NukeDawnLibConfig.cs b/HookahPlace/src/Content/NukeDawnLibConfig.cs
new file mode 100644
index 0000000..d24402e
--- /dev/null
+++ b/HookahPlace/src/Content/NukeDawnLibConfig.cs
@@ -0,0 +1,22 @@
+using BepInEx.Configuration;
+using Dusk;
+
+namespace HookahPlace.Content;
+
+internal static class NukeDawnLibConfig
+{
+ static internal void NukeUnlockable(ConfigFile config, string heading, string name, int cost)
+ {
+ heading = $"{heading} Options";
+ var costName = $"{name} | Cost";
+ var disclaimer = "Sorry, this is not configurable. This is a dirty hack to suppress DawnLib/DuskMod from allowing any configurability here.";
+
+ var ctx = new ConfigContext(config, heading);
+
+ var enabledEntry = ctx.Bind("Enabled", disclaimer, true);
+ enabledEntry.Value = true;
+
+ var costEntry = ctx.Bind(costName, disclaimer, cost);
+ costEntry.Value = cost;
+ }
+}
diff --git a/HookahPlace/src/HookahBehaviour.cs b/HookahPlace/src/HookahBehaviour.cs
new file mode 100644
index 0000000..873e18b
--- /dev/null
+++ b/HookahPlace/src/HookahBehaviour.cs
@@ -0,0 +1,78 @@
+using Unity.Netcode;
+using UnityEngine;
+using UnityEngine.Rendering.HighDefinition;
+
+namespace HookahPlace;
+
+class HookahBehaviour : NetworkBehaviour
+{
+ public Transform? smokePosition;
+ public GameObject? smokePrefab;
+
+ public Color[] Palette = [];
+
+ private Color? ApplyLaterSmokeColor = null;
+ private LocalVolumetricFog? ApplyLaterSmokeVolumetricFog;
+
+ // Hookah smoke animation emits an event that invokes this method.
+ // The prefab that it spawns self-destructs at the end of its animation.
+ //
+ // The fog works through LocalVolumetricFog components.
+ // Inside the ship and under any roof outdoors there are placed FogExclusionZone objects,
+ // with Blending set to Override which essentially removes the fog in those zones.
+ //
+ // The solution is to set your Hookah smoke blending to Additive (to preserve seamless transitions when fading out),
+ // but crank up priority to something unreasonably high like 9001, so that it adds to that Override layer.
+ public void SpawnSmoke()
+ {
+ if (smokePosition == null || smokePrefab == null)
+ {
+ return;
+ }
+ var ship = transform.parent.parent;
+ var smoke = Instantiate(smokePrefab, smokePosition.position, Quaternion.identity, ship);
+ var fog = smoke.GetComponentInChildren();
+ // Randomize smoke color.
+ // Clients set a client-side random color just in case, but it should be overridden by server rpc shortly.
+ var index = Random.RandomRangeInt(0, Palette.Length);
+ Color randomColor = Palette[index];
+ if (IsServer)
+ {
+ SetSmokeColorClientRpc(randomColor);
+ ApplySmokeColor(fog, randomColor);
+ }
+ else if (ApplyLaterSmokeColor is { } serverColor)
+ {
+ ApplySmokeColor(fog, serverColor);
+ ApplyLaterSmokeColor = null;
+ }
+ else
+ {
+ // color has not arrived from server (yet), for now set local random color
+ ApplySmokeColor(fog, randomColor);
+ ApplyLaterSmokeVolumetricFog = fog;
+ }
+ }
+
+ [Rpc(SendTo.NotServer)]
+ public void SetSmokeColorClientRpc(Color color)
+ {
+ if (ApplyLaterSmokeVolumetricFog is { } fog)
+ {
+ ApplySmokeColor(fog, color);
+ ApplyLaterSmokeVolumetricFog = null;
+ }
+ else
+ {
+ // smoke has not been spawned (yet), remember the color for later.
+ ApplyLaterSmokeColor = color;
+ }
+ }
+
+ private static void ApplySmokeColor(LocalVolumetricFog fog, Color color)
+ {
+ var parameters = fog.parameters;
+ parameters.albedo = color;
+ fog.parameters = parameters;
+ }
+}
diff --git a/HookahPlace/src/HookahPlace.cs b/HookahPlace/src/HookahPlace.cs
index de984f9..4fac385 100644
--- a/HookahPlace/src/HookahPlace.cs
+++ b/HookahPlace/src/HookahPlace.cs
@@ -4,6 +4,9 @@ using System.Reflection;
using UnityEngine;
using Dawn;
using Dawn.Utils;
+using HarmonyLib;
+using Dusk;
+using BepInEx.Configuration;
namespace HookahPlace;
@@ -12,16 +15,18 @@ namespace HookahPlace;
public class HookahPlace : BaseUnityPlugin
{
internal new static ManualLogSource Logger { get; private set; } = null!;
+ internal new static ConfigFile Config { get; private set; } = null!;
internal static PersistentDataContainer PersistentData { get; private set; } = null!;
+ internal static DuskMod Mod { get; private set; } = null!;
private void Awake()
{
Logger = base.Logger;
+ Config = base.Config;
- // Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), MyPluginInfo.PLUGIN_GUID) // uncomment if using Harmony to patch
-
+ Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), MyPluginInfo.PLUGIN_GUID);
// Example Persistent Data Container Usage
// You can do anything you want with this DataContainer, there are a few additonal ones under the DawnLib class that pertain to actual save files
@@ -31,6 +36,10 @@ public class HookahPlace : BaseUnityPlugin
// if you want to do config migration you should use DawnLib.GetCurrentInstallSave instead.
PersistentData.Set(HookahPlaceKeys.LastVersion, MyPluginInfo.PLUGIN_VERSION);
+ AssetBundle mainBundle = AssetBundleUtils.LoadBundle(Assembly.GetExecutingAssembly(), "hookahplaceasset");
+ Mod = DuskMod.RegisterMod(this, mainBundle);
+ Mod.RegisterContentHandlers();
+
Logger.LogInfo($"{MyPluginInfo.PLUGIN_GUID} v{MyPluginInfo.PLUGIN_VERSION} has loaded!");
}
}
diff --git a/HookahPlace/src/HookahPlaceKeys.cs b/HookahPlace/src/HookahPlaceKeys.cs
index b2f44ba..fe56de2 100644
--- a/HookahPlace/src/HookahPlaceKeys.cs
+++ b/HookahPlace/src/HookahPlaceKeys.cs
@@ -12,8 +12,6 @@ namespace HookahPlace;
// If you contain lots of keys, you might want to use child classes e.g: HookahPlaceKeys.Items.MyItem
// Note: the use of `partial` here is because of the DawnLib SourceGenerator, which you might not be using.
public static partial class HookahPlaceKeys {
- // You may want to update this namespace. It should be "Snake case", e.g: FacilityMeltdown's namespace should be `facility_meltdown`
- // You can also shorten it if you want, e.g: `meltdown`
public const string Namespace = "hookah_place";
// Data Keys