├── DeppartPrototypeHentaiPlayMod
├── UnityEngine.dll
├── Assembly-CSharp.dll
├── FodyWeavers.xml
├── EventEnum.cs
├── IEventReporter.cs
├── Utils.cs
├── BaseReporter.cs
├── Properties
│ └── AssemblyInfo.cs
├── DeppartPrototypeHentaiPlayMod.csproj
├── HttpReporter.cs
├── ButtPlugReporter.cs
├── FodyWeavers.xsd
└── HentaiPlayMod.cs
├── CHANGELOG.md
├── .github
├── dependabot.yml
└── workflows
│ ├── dotnet.yml
│ └── github-release.yml
├── DeppartPrototypeHentaiPlayMod.sln
├── openapi.yaml
├── LICENSE
├── examples
└── dg-lab-v3.cfg
├── docs
└── dg-lab-v3.md
└── README.md
/DeppartPrototypeHentaiPlayMod/UnityEngine.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ljzd-PRO/DeppartPrototypeHentaiPlayMod/HEAD/DeppartPrototypeHentaiPlayMod/UnityEngine.dll
--------------------------------------------------------------------------------
/DeppartPrototypeHentaiPlayMod/Assembly-CSharp.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ljzd-PRO/DeppartPrototypeHentaiPlayMod/HEAD/DeppartPrototypeHentaiPlayMod/Assembly-CSharp.dll
--------------------------------------------------------------------------------
/DeppartPrototypeHentaiPlayMod/FodyWeavers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/DeppartPrototypeHentaiPlayMod/EventEnum.cs:
--------------------------------------------------------------------------------
1 | namespace DeppartPrototypeHentaiPlayMod
2 | {
3 | public enum EventEnum
4 | {
5 | GameEnter,
6 | GameExit,
7 | InGame,
8 | BulbBroken,
9 | ZombieRun,
10 | EnterLevel1,
11 | Level1Zombie,
12 | EndZombie,
13 | PlayerDied,
14 | Shot
15 | }
16 | }
--------------------------------------------------------------------------------
/DeppartPrototypeHentaiPlayMod/IEventReporter.cs:
--------------------------------------------------------------------------------
1 | namespace DeppartPrototypeHentaiPlayMod
2 | {
3 | public interface IEventReporter
4 | {
5 | void ReportActivateEvent(string eventName);
6 | void ReportDeactivateEvent(string eventName);
7 | void ReportGameEnterEvent();
8 | void ReportGameExitEvent();
9 | void ReportShot();
10 | }
11 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## Changes
2 |
3 | [//]: # (### 🐍 Fix)
4 |
5 | ### 💡 New Features
6 |
7 | - Integrated dependencies into the main assembly DLL file. (Only one DLL file now)
8 |
9 | - - -
10 |
11 | [//]: # (### 🐍 修复)
12 |
13 | ### 💡 新增内容
14 |
15 | - 将依赖项集成到了主程序集 DLL 文件里。(现在只有一个 DLL 文件了)
16 |
17 | **Full Changelog**: https://github.com/Ljzd-PRO/DeppartPrototypeHentaiPlayMod/compare/v1.1.0...v1.1.1
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "nuget" # See documentation for possible values
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "weekly"
12 |
--------------------------------------------------------------------------------
/DeppartPrototypeHentaiPlayMod/Utils.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Mono.Web;
4 |
5 | namespace DeppartPrototypeHentaiPlayMod
6 | {
7 | public static class Utils
8 | {
9 | public static Uri BuildRequestUri(string url, Dictionary query)
10 | {
11 | var queryCollection = HttpUtility.ParseQueryString(string.Empty);
12 | foreach (var kvp in query) queryCollection[kvp.Key] = kvp.Value;
13 | var uriBuilder = new UriBuilder(url)
14 | {
15 | Query = queryCollection.ToString()
16 | };
17 | return uriBuilder.Uri;
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/DeppartPrototypeHentaiPlayMod.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeppartPrototypeHentaiPlayMod", "DeppartPrototypeHentaiPlayMod\DeppartPrototypeHentaiPlayMod.csproj", "{A68AC8D6-276B-4A21-ACED-1255E3708EF5}"
4 | EndProject
5 | Global
6 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
7 | Debug|Any CPU = Debug|Any CPU
8 | Release|Any CPU = Release|Any CPU
9 | EndGlobalSection
10 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
11 | {A68AC8D6-276B-4A21-ACED-1255E3708EF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
12 | {A68AC8D6-276B-4A21-ACED-1255E3708EF5}.Debug|Any CPU.Build.0 = Debug|Any CPU
13 | {A68AC8D6-276B-4A21-ACED-1255E3708EF5}.Release|Any CPU.ActiveCfg = Release|Any CPU
14 | {A68AC8D6-276B-4A21-ACED-1255E3708EF5}.Release|Any CPU.Build.0 = Release|Any CPU
15 | EndGlobalSection
16 | EndGlobal
17 |
--------------------------------------------------------------------------------
/.github/workflows/dotnet.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a .NET project
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
3 |
4 | name: .NET Build
5 |
6 | on:
7 | push:
8 | pull_request:
9 | workflow_dispatch:
10 | workflow_call:
11 |
12 | jobs:
13 | build:
14 | strategy:
15 | matrix:
16 | configuration: [Debug, Release]
17 |
18 | runs-on: windows-latest
19 |
20 | steps:
21 | - uses: actions/checkout@v4
22 |
23 | - name: Setup MSBuild
24 | uses: microsoft/setup-msbuild@v2
25 |
26 | - name: Setup NuGet
27 | uses: NuGet/setup-nuget@v2
28 |
29 | - name: Restore NuGet Packages
30 | run: nuget restore DeppartPrototypeHentaiPlayMod.sln
31 |
32 | - name: Build
33 | run: msbuild DeppartPrototypeHentaiPlayMod.sln /p:Configuration=${{ matrix.configuration }}
34 |
35 | - name: Upload Artifact
36 | uses: actions/upload-artifact@v4
37 | with:
38 | name: ${{ matrix.configuration }}
39 | path: DeppartPrototypeHentaiPlayMod\bin\${{ matrix.configuration }}
40 |
--------------------------------------------------------------------------------
/openapi.yaml:
--------------------------------------------------------------------------------
1 | openapi: "3.0.3"
2 | info:
3 | title: "DeppartPrototypeHentaiPlayMod HttpReporter API"
4 | description: "DeppartPrototypeHentaiPlayMod HttpReporter API"
5 | version: "1.0.0"
6 | servers:
7 | - url: "http://localhost:7788"
8 | paths:
9 | /report:
10 | get:
11 | summary: Report game events
12 | parameters:
13 | - name: event_name
14 | in: query
15 | description: The name of the event
16 | required: true
17 | schema:
18 | type: string
19 | enum:
20 | - GameEnter
21 | - GameExit
22 | - InGame
23 | - BulbBroken
24 | - ZombieRun
25 | - EnterLevel1
26 | - Level1Zombie
27 | - EndZombie
28 | - PlayerDied
29 | - Shot
30 | - name: status
31 | in: query
32 | description: The status of the event
33 | schema:
34 | type: string
35 | enum:
36 | - activate
37 | - deactivate
38 | - name: t
39 | in: query
40 | description: The timestamp of the event
41 | schema:
42 | type: string
43 | format: date-time
44 | responses:
45 | "200":
46 | description: Event reported successfully
47 | "400":
48 | description: Bad request
--------------------------------------------------------------------------------
/.github/workflows/github-release.yml:
--------------------------------------------------------------------------------
1 | name: GitHub Release
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | tags:
7 | - "*"
8 |
9 | permissions:
10 | contents: write
11 |
12 | jobs:
13 | build:
14 | uses: ./.github/workflows/dotnet.yml
15 |
16 | release:
17 | runs-on: ubuntu-latest
18 | needs:
19 | - build
20 |
21 | steps:
22 | - uses: actions/checkout@v4
23 |
24 | - name: Download executable
25 | uses: actions/download-artifact@v4
26 | with:
27 | path: artifact
28 |
29 | - name: Compress Directories
30 | run: |
31 | cd artifact
32 | for dir in */; do
33 | dir_name=$(basename "$dir")
34 | new_dir_name="${dir_name}-$(basename ${{ github.ref }})"
35 | zip_file_name="${new_dir_name}.zip"
36 | mkdir ${new_dir_name}
37 | mkdir ${new_dir_name}/Mods
38 | find ${dir} -type f -name '*.dll' -exec cp {} ${new_dir_name}/Mods \;
39 | cd ${new_dir_name}
40 | zip -r ../${zip_file_name} Mods
41 | cd ..
42 | done
43 | cd ..
44 |
45 | - name: Release
46 | uses: softprops/action-gh-release@v1
47 | if: startsWith(github.ref, 'refs/tags/')
48 | with:
49 | body_path: CHANGELOG.md
50 | files: artifact/*.zip
51 | prerelease: contains(github.ref, 'beta')
52 |
--------------------------------------------------------------------------------
/DeppartPrototypeHentaiPlayMod/BaseReporter.cs:
--------------------------------------------------------------------------------
1 | namespace DeppartPrototypeHentaiPlayMod
2 | {
3 | public class BaseReporter : IEventReporter
4 | {
5 | protected readonly HentaiPlayMod MelonMod;
6 | public bool DisableEventLog = false;
7 |
8 | public BaseReporter(HentaiPlayMod melonMod)
9 | {
10 | MelonMod = melonMod;
11 | }
12 |
13 | public virtual void ReportActivateEvent(string eventName)
14 | {
15 | if (!DisableEventLog)
16 | MelonMod.LoggerInstance.Msg($"ActivateEvent: {eventName}");
17 | }
18 |
19 | public virtual void ReportDeactivateEvent(string eventName)
20 | {
21 | if (!DisableEventLog)
22 | MelonMod.LoggerInstance.Msg($"DeactivateEvent: {eventName}");
23 | }
24 |
25 | public virtual void ReportGameEnterEvent()
26 | {
27 | if (!DisableEventLog)
28 | MelonMod.LoggerInstance.Msg($"Event: {EventEnum.GameEnter.ToString()}");
29 | }
30 |
31 | public virtual void ReportGameExitEvent()
32 | {
33 | if (!DisableEventLog)
34 | MelonMod.LoggerInstance.Msg($"Event: {EventEnum.GameExit.ToString()}");
35 | }
36 |
37 | public virtual void ReportShot()
38 | {
39 | if (!DisableEventLog)
40 | MelonMod.LoggerInstance.Msg($"Event: {EventEnum.Shot.ToString()}");
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2024, Ljzd-PRO (https://github.com/Ljzd-PRO)
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | 1. Redistributions of source code must retain the above copyright notice, this
9 | list of conditions and the following disclaimer.
10 |
11 | 2. Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 |
15 | 3. Neither the name of the copyright holder nor the names of its
16 | contributors may be used to endorse or promote products derived from
17 | this software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 |
--------------------------------------------------------------------------------
/DeppartPrototypeHentaiPlayMod/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("DeppartPrototypeHentaiPlayMod")]
8 | [assembly: AssemblyDescription("Buttplug.io game mod for DeppartPrototype | 适配郊狼 3.0 DG-Lab-V3")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("https://github.com/Ljzd-PRO")]
11 | [assembly: AssemblyProduct("DeppartPrototypeHentaiPlayMod")]
12 | [assembly: AssemblyCopyright("Copyright © 2024, Ljzd-PRO")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("A68AC8D6-276B-4A21-ACED-1255E3708EF5")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("1.1.1")]
35 | [assembly: AssemblyFileVersion("1.1.1.0")]
--------------------------------------------------------------------------------
/examples/dg-lab-v3.cfg:
--------------------------------------------------------------------------------
1 | [HentaiPlay]
2 | # Type of reporter that report events in game (Available: BaseReporter, HttpReporter, ButtPlugReporter)
3 | EventReporterType = "ButtPlugReporter"
4 | # Report URL for HttpReporter
5 | HttpReporterUrl = "http://127.0.0.1:7788/report"
6 | # Time interval for HttpReporter to reporting InGame events
7 | HttpReportInGameInterval = 3000
8 | # Not to report events to Console
9 | DisableEventLog = false
10 | # Websocket URL of ButtPlug server (Intiface Central)
11 | ButtPlugServerUrl = "ws://localhost:12345"
12 |
13 | # Set the ButtPlug vibrate scalar when game events active
14 | # * 郊狼 3.0 虽然电源强度可调范围为 0-200,但默认上限为 100,因此一般此处 0.5 就已经是最大了
15 | ButtPlugActiveVibrateScalar = 0.1
16 |
17 | # Set the ButtPlug vibrate scalar when gun shot
18 | # * 郊狼 3.0 虽然电源强度可调范围为 0-200,但默认上限为 100,因此一般此处 0.5 就已经是最大了
19 | ButtPlugShotVibrateScalar = 0.25
20 |
21 | # Set the ButtPlug vibrate duration when gun shot (Millisecond)
22 | # * 郊狼 3.0 如果持续时间太短反馈不明显,太长则可能跟不上开枪速度
23 | ButtPlugVibrateDuration = 300
24 |
25 | # Set the index of ButtPlug vibrate scalar commands, you can set multiple index or empty as default. (e.g. [0,1])
26 | # * 郊狼 3.0 电源强度 (Vibrate) 命令索引,0, 1 即 A 通道和 B 通道
27 | ButtPlugVibrateCmdIndexList = [ 0, 1, ]
28 |
29 | # Set the additional ButtPlug scalar commands, which called during vibrate (It will set to 0 after vibrate stop)
30 |
31 | # * 郊狼 3.0 波形频率 (Oscillate) 命令索引 2 (A通道)
32 | [[HentaiPlay.ButtPlugAdditionalScalarList]]
33 | Enable = true
34 | ActuatorType = "Oscillate"
35 | Index = 2
36 | Scalar = 0.5
37 |
38 | # * 郊狼 3.0 波形频率 (Oscillate) 命令索引 3 (B 通道)
39 | [[HentaiPlay.ButtPlugAdditionalScalarList]]
40 | Enable = true
41 | ActuatorType = "Oscillate"
42 | Index = 3
43 | Scalar = 0.5
44 |
45 | # * 郊狼 3.0 波形强度 (Inflate) 命令索引 4 (A 通道)
46 | [[HentaiPlay.ButtPlugAdditionalScalarList]]
47 | Enable = true
48 | ActuatorType = "Inflate"
49 | Index = 4
50 | Scalar = 0.5
51 |
52 | # * 郊狼 3.0 波形强度 (Inflate) 命令索引 5 (B 通道)
53 | [[HentaiPlay.ButtPlugAdditionalScalarList]]
54 | Enable = true
55 | ActuatorType = "Inflate"
56 | Index = 5
57 | Scalar = 0.5
58 |
--------------------------------------------------------------------------------
/docs/dg-lab-v3.md:
--------------------------------------------------------------------------------
1 | # DG-Lab-V3 Guide
2 |
3 | 由于 [buttplugio/buttplug](https://github.com/buttplugio/buttplug) 未适配郊狼,且由于其不适合该类产品,buttplug 不接受适配 PR。
4 | 因此需要使用专门适配过的 buttplug 分支,包括 Intiface Central。
5 |
6 | ## 安装
7 |
8 | 1. 前往 [**Ljzd-PRO/buttplug-dg-lab**](https://github.com/Ljzd-PRO/buttplug-dg-lab)
9 | 的 Releases 页面下载最新的 Intiface Central 并安装
10 | 2. 启动 Intiface Central 的引擎服务,在 Devices 页面扫描蓝牙设备并连接上郊狼 3.0 \
11 | 可以测试一下各项控制条是否有作用
12 | 3. 完成主页 [`README.md`](../README.md) 中剩余的其他安装步骤
13 | 4. **为郊狼 3.0 配置 Mod**,可参考已配置好的示例文件 [`examples/dg-lab-v3.cfg`](../examples/dg-lab-v3.cfg) \
14 | 复制其全部内容,替换游戏目录下 `UserData\MelonPreferences.cfg` 配置文件中的 `[HentaiPlay]` 部分
15 | 5. 如果您是通过**手机等其他设备**的 Intiface Central App 连接的郊狼 3.0,也就是**不在运行游戏的电脑**上,
16 | 那么需要修改默认的 `ButtPlugServerUrl` 选项,修改其中的 IP 地址为 Intiface Central 所在设备的 IP 地址,否则将无法连接
17 |
18 | ## 配置说明
19 |
20 | 一些修改了的配置选项,其中与强度相关的参数可以自行调整:
21 |
22 | ```cfg
23 | # Set the ButtPlug vibrate scalar when game events active
24 | # * 郊狼 3.0 虽然电源强度可调范围为 0-200,但默认上限为 100,因此一般此处 0.5 就已经是最大了
25 | ButtPlugActiveVibrateScalar = 0.1
26 |
27 | # Set the ButtPlug vibrate scalar when gun shot
28 | # * 郊狼 3.0 虽然电源强度可调范围为 0-200,但默认上限为 100,因此一般此处 0.5 就已经是最大了
29 | ButtPlugShotVibrateScalar = 0.25
30 |
31 | # Set the ButtPlug vibrate duration when gun shot (Millisecond)
32 | # * 郊狼 3.0 如果持续时间太短反馈不明显,太长则可能跟不上开枪速度
33 | ButtPlugVibrateDuration = 300
34 |
35 | # Set the index of ButtPlug vibrate scalar commands, you can set multiple index or empty as default. (e.g. [0,1])
36 | # * 郊狼 3.0 电源强度 (Vibrate) 命令索引,0, 1 即 A 通道和 B 通道
37 | ButtPlugVibrateCmdIndexList = [ 0, 1, ]
38 |
39 | # Set the additional ButtPlug scalar commands, which called during vibrate (It will set to 0 after vibrate stop)
40 |
41 | # * 郊狼 3.0 波形频率 (Oscillate) 命令索引 2 (A通道)
42 | [[HentaiPlay.ButtPlugAdditionalScalarList]]
43 | Enable = true
44 | ActuatorType = "Oscillate"
45 | Index = 2
46 | Scalar = 0.5
47 |
48 | # * 郊狼 3.0 波形频率 (Oscillate) 命令索引 3 (B 通道)
49 | [[HentaiPlay.ButtPlugAdditionalScalarList]]
50 | Enable = true
51 | ActuatorType = "Oscillate"
52 | Index = 3
53 | Scalar = 0.5
54 |
55 | # * 郊狼 3.0 波形强度 (Inflate) 命令索引 4 (A 通道)
56 | [[HentaiPlay.ButtPlugAdditionalScalarList]]
57 | Enable = true
58 | ActuatorType = "Inflate"
59 | Index = 4
60 | Scalar = 0.5
61 |
62 | # * 郊狼 3.0 波形强度 (Inflate) 命令索引 5 (B 通道)
63 | [[HentaiPlay.ButtPlugAdditionalScalarList]]
64 | Enable = true
65 | ActuatorType = "Inflate"
66 | Index = 5
67 | Scalar = 0.5
68 | ```
69 |
70 | ## 常见问题
71 |
72 | ### 触发各种事件时,Intiface Central 的各项控制条并没有发生变化
73 |
74 | 这是因为 Intiface Central 的控制条位置不会主动更新,您可以观察郊狼设备上的指示灯是否发生变化,或直接用身体体验变化。
75 |
76 | ### 命令索引是怎么得出的,以及为什么 Vibrate, Oscillate 等对应的是电源强度和频率之类的
77 |
78 | Buttplug 协议不适合郊狼这类设备,一些设计主要是配合震动类的设备,因此没有电源强度、脉冲强度、脉宽之类的控制命令。但由于其生态较广,有适配的意义。
79 |
80 | 需要指定命令索引号的原因是,郊狼有两个通道(A, B),因此各种控制命令都会有重复,一个控制 A 通道,另一个控制 B 通道。
81 |
82 | 命令含义和索引号是由该 buttplug 适配分支定义的。
--------------------------------------------------------------------------------
/DeppartPrototypeHentaiPlayMod/DeppartPrototypeHentaiPlayMod.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | Debug
7 | AnyCPU
8 | {A68AC8D6-276B-4A21-ACED-1255E3708EF5}
9 | Library
10 | Properties
11 | DeppartPrototypeHentaiPlayMod
12 | DeppartPrototypeHentaiPlayMod
13 | v4.8.1
14 | 512
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 | Assembly-CSharp.dll
38 |
39 |
40 | ..\packages\LavaGang.MelonLoader.0.6.1\lib\net35\MelonLoader.dll
41 |
42 |
43 | UnityEngine.dll
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | all
52 |
53 |
54 | all
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DeppartPrototype HentaiPlay
2 |
3 | - DeppartPrototype Game Download: https://n4ba.itch.io/deppart
4 | - Buttplug protocol: https://github.com/buttplugio/buttplug
5 | - Intiface® Central: https://intiface.com/central/
6 |
7 | ### About the game DeppartPrototype
8 |
9 | > Deppart is an indie first-person horror game with shooter elements. \
10 | > Be very careful, enemies kill you with one hit. \
11 | > https://n4ba.itch.io/deppart
12 |
13 | ## Feature
14 |
15 | - Buttplug devices will be activated on these situations:
16 | - Gun shot
17 | - Jump-scares
18 | - During battle
19 | - Player died
20 | - Game end
21 | - Mainly use `Vibrate` command, but you can add other scalar commands
22 | - Provide an alternative event reporter instead of buttplug (`HttpReporter`)
23 |
24 | ## Usage
25 |
26 | 1. Install MelonLoader:
27 | - https://melonwiki.xyz/#/README?id=automated-installation
28 | 2. Download latest release and extract:
29 | - https://github.com/Ljzd-PRO/DeppartPrototypeHentaiPlayMod/releases/latest
30 | 3. Place the `Mods` directory under the game path.
31 | 4. Install [Intiface® Central](https://intiface.com/central/)
32 | 5. Launch Intiface® Central, start the engine server.
33 | 6. Launch the game, connect you Buttplug device to Intiface® Central.
34 | 7. (Optional) Configure the mod preference in `UserData\MelonPreferences.cfg` under the game path.
35 | 8. Enjoy the game.
36 |
37 | ## For DG-Lab Users
38 |
39 | 该 Mod 已适配 郊狼 2.0 3.0 即 DG-Lab-V2, V3,但是需要修改 Mod 配置,同时需要使用专门适配的 buttplug 分支。
40 |
41 | - 郊狼 3.0 具体请查看文档:[`docs/dg-lab-v3.md`](docs/dg-lab-v3.md)
42 | - 郊狼 2.0 由于没有设备可测试,无法给出具体配置参考,但可以参考 3.0 进行配置。
43 |
44 | ## Preference
45 |
46 | Some important options:
47 | - ButtPlugServerUrl
48 | - ButtPlugActiveVibrateScalar
49 | - ButtPlugShotVibrateScalar
50 | - ButtPlugVibrateDuration
51 | - ButtPlugVibrateCmdIndexList
52 | - ButtPlugAdditionalScalarList
53 |
54 | ```cfg
55 | [HentaiPlay]
56 | # Type of reporter that report events in game (Available: BaseReporter, HttpReporter, ButtPlugReporter)
57 | EventReporterType = "ButtPlugReporter"
58 | # Report URL for HttpReporter
59 | HttpReporterUrl = "http://127.0.0.1:7788/report"
60 | # Time interval for HttpReporter to reporting InGame events
61 | HttpReportInGameInterval = 3000
62 | # Not to report events to Console
63 | DisableEventLog = false
64 | # Websocket URL of ButtPlug server (Intiface Central)
65 | ButtPlugServerUrl = "ws://localhost:12345"
66 | # Set the ButtPlug vibrate scalar when game events active
67 | ButtPlugActiveVibrateScalar = 0.5
68 | # Set the ButtPlug vibrate scalar when gun shot
69 | ButtPlugShotVibrateScalar = 1.0
70 | # Set the ButtPlug vibrate duration when gun shot (Millisecond)
71 | ButtPlugVibrateDuration = 300
72 | # Set the index of ButtPlug vibrate scalar commands, you can set multiple index or empty as default. (e.g. [0,1])
73 | ButtPlugVibrateCmdIndexList = [ ]
74 | # Set the additional ButtPlug scalar commands, which called during vibrate (It will set to 0 after vibrate stop)
75 |
76 | [[HentaiPlay.ButtPlugAdditionalScalarList]]
77 | Enable = false
78 | ActuatorType = "Oscillate"
79 | Index = 0
80 | Scalar = 0.5
81 |
82 | [[HentaiPlay.ButtPlugAdditionalScalarList]]
83 | Enable = false
84 | ActuatorType = "Inflate"
85 | Index = 0
86 | Scalar = 0.5
87 | ```
88 |
89 | ## About `HttpReporter`
90 |
91 | This is **OPTIONAL**, you can setup an HTTP server to handle events in the mod instead of using buttplug.
92 |
93 | Set the option `EventReporterType` in `UserData\MelonPreferences.cfg` to `"HttpReporter"` if you want to use this.
94 |
95 | ### `HttpReporter` API
96 |
97 | Define in [`openapi.yaml`](openapi.yaml).
98 |
99 | ### Example implementation for the server of `HttpReporter`
100 |
101 | ```python3
102 | import datetime
103 | from enum import StrEnum
104 | from typing import Literal
105 |
106 | from fastapi import FastAPI
107 | from loguru import logger
108 |
109 | app = FastAPI()
110 |
111 |
112 | class EventNameEnum(StrEnum):
113 | GameEnter = "GameEnter"
114 | GameExit = "GameExit"
115 | InGame = "InGame"
116 | BulbBroken = "BulbBroken"
117 | ZombieRun = "ZombieRun"
118 | EnterLevel1 = "EnterLevel1"
119 | Level1Zombie = "Level1Zombie"
120 | EndZombie = "EndZombie"
121 | PlayerDied = "PlayerDied"
122 | Shot = "Shot"
123 |
124 | @app.get("/report")
125 | async def report(
126 | event_name: Literal[
127 | EventNameEnum.GameEnter,
128 | EventNameEnum.GameExit,
129 | EventNameEnum.InGame,
130 | EventNameEnum.BulbBroken,
131 | EventNameEnum.ZombieRun,
132 | EventNameEnum.EnterLevel1,
133 | EventNameEnum.Level1Zombie,
134 | EventNameEnum.EndZombie,
135 | EventNameEnum.PlayerDied,
136 | EventNameEnum.Shot
137 | ],
138 | status: Literal["activate", "deactivate"] = None,
139 | t: datetime.datetime = None
140 | ):
141 | logger.info(f"event_name: {event_name}, "
142 | f"status: {status}, "
143 | f"t: {t.astimezone(tz=None)}, "
144 | f"latency: {datetime.datetime.now(datetime.timezone.utc) - t}")
145 |
146 | ```
147 |
--------------------------------------------------------------------------------
/DeppartPrototypeHentaiPlayMod/HttpReporter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Net.Http;
4 | using System.Reflection;
5 | using System.Threading;
6 | using MelonLoader;
7 |
8 | namespace DeppartPrototypeHentaiPlayMod
9 | {
10 | public class HttpReporter : BaseReporter
11 | {
12 | private readonly HttpClient _httpClient = new HttpClient();
13 | private readonly MelonPreferences_Entry _httpReporterUrlEntry;
14 | private readonly MelonPreferences_Entry _httpReportInGameInterval;
15 | private bool _reportInGameStarted;
16 | private bool _stopReportingInGame;
17 |
18 | public HttpReporter(
19 | HentaiPlayMod melonMod,
20 | MelonPreferences_Entry httpReporterUrlEntry,
21 | MelonPreferences_Entry httpReportInGameInterval
22 | ) : base(melonMod)
23 | {
24 | try
25 | {
26 | Assembly.Load("Mono.HttpUtility");
27 | }
28 | catch (Exception)
29 | {
30 | MelonMod.LoggerInstance.Error("Mono.HttpUtility is required");
31 | throw;
32 | }
33 |
34 | _httpReporterUrlEntry = httpReporterUrlEntry;
35 | _httpReportInGameInterval = httpReportInGameInterval;
36 | }
37 |
38 | private void SendRequest(Dictionary query)
39 | {
40 | new Thread(() =>
41 | {
42 | query["t"] = DateTimeOffset.Now.ToUnixTimeSeconds().ToString();
43 | try
44 | {
45 | _httpClient.GetAsync(Utils.BuildRequestUri(_httpReporterUrlEntry.Value, query)).Wait();
46 | }
47 | catch (Exception e)
48 | {
49 | MelonMod.LoggerInstance.Error($"{nameof(HttpReporter)}: Report failed", e);
50 | }
51 | }).Start();
52 | }
53 |
54 | private void KeepReportingInGame()
55 | {
56 | if (_reportInGameStarted)
57 | return;
58 | var query = new Dictionary
59 | {
60 | { "event_name", EventEnum.InGame.ToString() }
61 | };
62 | new Thread(() =>
63 | {
64 | while (!_stopReportingInGame)
65 | {
66 | query["t"] = DateTimeOffset.Now.ToUnixTimeSeconds().ToString();
67 | try
68 | {
69 | _httpClient.GetAsync(Utils.BuildRequestUri(_httpReporterUrlEntry.Value, query)).Wait();
70 | }
71 | catch (Exception e)
72 | {
73 | MelonMod.LoggerInstance.Error($"{nameof(HttpReporter)}: Report failed", e);
74 | }
75 |
76 | Thread.Sleep(_httpReportInGameInterval.Value);
77 | }
78 | }).Start();
79 | _reportInGameStarted = true;
80 | }
81 |
82 | private void StopReportingInGame()
83 | {
84 | _stopReportingInGame = true;
85 | }
86 |
87 | public override void ReportActivateEvent(string eventName)
88 | {
89 | base.ReportActivateEvent(eventName);
90 | SendRequest(
91 | new Dictionary
92 | {
93 | { "status", "activate" },
94 | { "event_name", eventName }
95 | }
96 | );
97 | }
98 |
99 | public override void ReportDeactivateEvent(string eventName)
100 | {
101 | base.ReportDeactivateEvent(eventName);
102 | SendRequest(
103 | new Dictionary
104 | {
105 | { "status", "deactivate" },
106 | { "event_name", eventName }
107 | }
108 | );
109 | }
110 |
111 | public override void ReportGameEnterEvent()
112 | {
113 | base.ReportGameEnterEvent();
114 | SendRequest(
115 | new Dictionary
116 | {
117 | { "event_name", EventEnum.GameEnter.ToString() }
118 | }
119 | );
120 | KeepReportingInGame();
121 | }
122 |
123 | public override void ReportGameExitEvent()
124 | {
125 | base.ReportGameExitEvent();
126 | SendRequest(
127 | new Dictionary
128 | {
129 | { "event_name", EventEnum.GameExit.ToString() }
130 | }
131 | );
132 | StopReportingInGame();
133 | }
134 |
135 | public override void ReportShot()
136 | {
137 | base.ReportShot();
138 | SendRequest(
139 | new Dictionary
140 | {
141 | { "event_name", EventEnum.Shot.ToString() }
142 | }
143 | );
144 | }
145 | }
146 | }
--------------------------------------------------------------------------------
/DeppartPrototypeHentaiPlayMod/ButtPlugReporter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading;
5 | using Buttplug.Client;
6 | using Buttplug.Client.Connectors.WebsocketConnector;
7 | using Buttplug.Core;
8 | using Buttplug.Core.Messages;
9 | using MelonLoader;
10 |
11 | namespace DeppartPrototypeHentaiPlayMod
12 | {
13 | public struct ButtPlugAdditionalScalar
14 | {
15 | public bool Enable;
16 | public ActuatorType ActuatorType;
17 | public uint Index;
18 | public double Scalar;
19 | }
20 |
21 | public class ButtPlugReporter : BaseReporter
22 | {
23 | private readonly MelonPreferences_Entry _buttPlugActiveVibrateScalar;
24 | private readonly MelonPreferences_Entry _buttPlugAdditionalScalarList;
25 | private readonly ButtplugClient _buttplugClient;
26 | private readonly MelonPreferences_Entry _buttPlugShotVibrateDuration;
27 | private readonly MelonPreferences_Entry _buttPlugShotVibrateScalar;
28 | private readonly MelonPreferences_Entry _buttPlugVibrateCmdIndexList;
29 |
30 | private readonly Dictionary _deviceMutexMap =
31 | new Dictionary();
32 |
33 |
34 | private double _baseVibrateScalar;
35 |
36 | public ButtPlugReporter
37 | (
38 | HentaiPlayMod melonMod,
39 | MelonPreferences_Entry buttPlugActiveVibrateScalar,
40 | MelonPreferences_Entry buttPlugServerUrlEntry,
41 | MelonPreferences_Entry buttPlugShotVibrateScalar,
42 | MelonPreferences_Entry buttPlugVibrateCmdIndexList,
43 | MelonPreferences_Entry buttPlugShotVibrateDuration,
44 | MelonPreferences_Entry buttPlugAdditionalScalarList
45 | ) : base(melonMod)
46 | {
47 | _buttPlugActiveVibrateScalar = buttPlugActiveVibrateScalar;
48 | _buttPlugShotVibrateScalar = buttPlugShotVibrateScalar;
49 | _buttPlugShotVibrateDuration = buttPlugShotVibrateDuration;
50 | _buttPlugAdditionalScalarList = buttPlugAdditionalScalarList;
51 | _buttPlugVibrateCmdIndexList = buttPlugVibrateCmdIndexList;
52 | _buttplugClient = new ButtplugClient(MelonMod.Info.Name);
53 | _buttplugClient.DeviceAdded +=
54 | (sender, args) =>
55 | {
56 | MelonMod.LoggerInstance.Msg($"ButtPlug device added: {args.Device.Name}");
57 | _deviceMutexMap.Add(args.Device, new Mutex());
58 | };
59 | _buttplugClient.DeviceRemoved +=
60 | (sender, args) =>
61 | {
62 | MelonMod.LoggerInstance.Msg($"ButtPlug device removed: {args.Device.Name}");
63 | _deviceMutexMap.Remove(args.Device);
64 | };
65 | _buttplugClient.ScanningFinished += (sender, args) =>
66 | MelonMod.LoggerInstance.Msg($"ButtPlug scanning finished: {args}");
67 | var connector = new ButtplugWebsocketConnector(new Uri(buttPlugServerUrlEntry.Value));
68 | connector.Disconnected +=
69 | (sender, args) => MelonMod.LoggerInstance.Msg($"ButtPlug scanning finished: {args}");
70 | new Thread(() =>
71 | {
72 | try
73 | {
74 | _buttplugClient.ConnectAsync(connector).Wait();
75 | }
76 | catch (ButtplugHandshakeException e)
77 | {
78 | MelonMod.LoggerInstance.Msg("ButtPlug handshake failed", e);
79 | }
80 | catch (AggregateException e)
81 | {
82 | MelonMod.LoggerInstance.Msg("Failed to connect to ButtPlug server", e);
83 | }
84 |
85 | _buttplugClient.StartScanningAsync().Wait();
86 | }).Start();
87 | }
88 |
89 | private void SendCommand(double[] scalars, int interval = 0)
90 | {
91 | foreach (var device in _buttplugClient.Devices)
92 | new Thread(() =>
93 | {
94 | _deviceMutexMap[device].WaitOne();
95 | try
96 | {
97 | foreach (var scalar in scalars)
98 | {
99 | if (_buttPlugVibrateCmdIndexList.Value.Length == 0)
100 | device.VibrateAsync(scalar);
101 | else
102 | device.VibrateAsync(
103 | _buttPlugVibrateCmdIndexList.Value.Select(index => (index, scalar)));
104 | foreach (var cmdInfo in _buttPlugAdditionalScalarList.Value)
105 | if (cmdInfo.Enable)
106 | device.ScalarAsync(
107 | new ScalarCmd.ScalarSubcommand(
108 | cmdInfo.Index,
109 | scalar.Equals(_baseVibrateScalar) && _baseVibrateScalar == 0
110 | ? 0
111 | : cmdInfo.Scalar,
112 | cmdInfo.ActuatorType
113 | )
114 | );
115 | if (interval != 0)
116 | Thread.Sleep(interval);
117 | }
118 | }
119 | catch (ButtplugDeviceException e)
120 | {
121 | MelonMod.LoggerInstance.Msg($"ButtPlug device {device.Name} vibrate failed", e);
122 | }
123 | finally
124 | {
125 | _deviceMutexMap[device].ReleaseMutex();
126 | }
127 | }).Start();
128 | }
129 |
130 | public override void ReportActivateEvent(string eventName)
131 | {
132 | base.ReportActivateEvent(eventName);
133 | var tempEvents = new[]
134 | { EventEnum.BulbBroken.ToString(), EventEnum.ZombieRun.ToString(), EventEnum.EnterLevel1.ToString() };
135 | if (tempEvents.Contains(eventName))
136 | {
137 | SendCommand(new[] { _buttPlugActiveVibrateScalar.Value, _baseVibrateScalar }, 5000);
138 | }
139 | else
140 | {
141 | _baseVibrateScalar = _buttPlugActiveVibrateScalar.Value;
142 | SendCommand(new[] { _baseVibrateScalar });
143 | }
144 | }
145 |
146 | public override void ReportDeactivateEvent(string eventName)
147 | {
148 | base.ReportDeactivateEvent(eventName);
149 | _baseVibrateScalar = 0;
150 | SendCommand(new[] { _baseVibrateScalar });
151 | }
152 |
153 | public override void ReportGameEnterEvent()
154 | {
155 | base.ReportGameEnterEvent();
156 | SendCommand(new[] { _baseVibrateScalar });
157 | }
158 |
159 | public override void ReportGameExitEvent()
160 | {
161 | base.ReportGameExitEvent();
162 | SendCommand(new[] { _baseVibrateScalar });
163 | }
164 |
165 | public override void ReportShot()
166 | {
167 | base.ReportShot();
168 | SendCommand(
169 | new[] { _buttPlugShotVibrateScalar.Value, _baseVibrateScalar },
170 | _buttPlugShotVibrateDuration.Value
171 | );
172 | }
173 | }
174 | }
--------------------------------------------------------------------------------
/DeppartPrototypeHentaiPlayMod/FodyWeavers.xsd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks
13 |
14 |
15 |
16 |
17 | A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.
18 |
19 |
20 |
21 |
22 | A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks
23 |
24 |
25 |
26 |
27 | A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.
28 |
29 |
30 |
31 |
32 | A list of unmanaged 32 bit assembly names to include, delimited with line breaks.
33 |
34 |
35 |
36 |
37 | A list of unmanaged 64 bit assembly names to include, delimited with line breaks.
38 |
39 |
40 |
41 |
42 | The order of preloaded assemblies, delimited with line breaks.
43 |
44 |
45 |
46 |
47 |
48 | This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file.
49 |
50 |
51 |
52 |
53 | Controls if .pdbs for reference assemblies are also embedded.
54 |
55 |
56 |
57 |
58 | Controls if runtime assemblies are also embedded.
59 |
60 |
61 |
62 |
63 | Controls whether the runtime assemblies are embedded with their full path or only with their assembly name.
64 |
65 |
66 |
67 |
68 | Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.
69 |
70 |
71 |
72 |
73 | As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off.
74 |
75 |
76 |
77 |
78 | Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code.
79 |
80 |
81 |
82 |
83 | Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior.
84 |
85 |
86 |
87 |
88 | A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with |
89 |
90 |
91 |
92 |
93 | A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |.
94 |
95 |
96 |
97 |
98 | A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with |
99 |
100 |
101 |
102 |
103 | A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with |.
104 |
105 |
106 |
107 |
108 | A list of unmanaged 32 bit assembly names to include, delimited with |.
109 |
110 |
111 |
112 |
113 | A list of unmanaged 64 bit assembly names to include, delimited with |.
114 |
115 |
116 |
117 |
118 | The order of preloaded assemblies, delimited with |.
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 | 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
127 |
128 |
129 |
130 |
131 | A comma-separated list of error codes that can be safely ignored in assembly verification.
132 |
133 |
134 |
135 |
136 | 'false' to turn off automatic generation of the XML Schema file.
137 |
138 |
139 |
140 |
141 |
--------------------------------------------------------------------------------
/DeppartPrototypeHentaiPlayMod/HentaiPlayMod.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Buttplug.Core.Messages;
5 | using DeppartPrototypeHentaiPlayMod;
6 | using MelonLoader;
7 | using UnityEngine;
8 |
9 | [assembly: MelonInfo(typeof(HentaiPlayMod), "HentaiPlay", "1.1.1", "Ljzd-PRO")]
10 | [assembly: MelonGame("N4bA", "DEPPART prototype")]
11 | [assembly: MelonOptionalDependencies("Mono.HttpUtility")]
12 |
13 | namespace DeppartPrototypeHentaiPlayMod
14 | {
15 | public class HentaiPlayMod : MelonMod
16 | {
17 | private readonly Dictionary _events = new Dictionary
18 | {
19 | { EventEnum.BulbBroken.ToString(), false },
20 | { EventEnum.ZombieRun.ToString(), false },
21 | { EventEnum.EnterLevel1.ToString(), false },
22 | { EventEnum.Level1Zombie.ToString(), false },
23 | { EventEnum.EndZombie.ToString(), false },
24 | { EventEnum.PlayerDied.ToString(), false }
25 | };
26 |
27 | private MelonPreferences_Entry _buttPlugActiveVibrateScalar;
28 | private MelonPreferences_Entry _buttPlugAdditionalScalarList;
29 | private MelonPreferences_Entry _buttPlugServerUrlEntry;
30 | private MelonPreferences_Entry _buttPlugShotVibrateScalar;
31 | private MelonPreferences_Entry _buttPlugVibrateCmdIndexList;
32 | private MelonPreferences_Entry _buttPlugVibrateDuration;
33 | private MelonPreferences_Entry _disableEventLogEntry;
34 |
35 | private IEventReporter _eventReporter;
36 | private MelonPreferences_Entry _eventReporterTypeEntry;
37 | private MelonPreferences_Entry _httpReporterUrlEntry;
38 | private MelonPreferences_Entry _httpReportInGameInterval;
39 | private MelonPreferences_Category _preferencesCategory;
40 |
41 | public HentaiPlayMod()
42 | {
43 | _eventReporter = new BaseReporter(this);
44 | }
45 |
46 | public override void OnInitializeMelon()
47 | {
48 | _preferencesCategory = MelonPreferences.CreateCategory("HentaiPlay");
49 | _eventReporterTypeEntry = _preferencesCategory.CreateEntry
50 | (
51 | "EventReporterType",
52 | nameof(ButtPlugReporter),
53 | description: "Type of reporter that report events in game " +
54 | $"(Available: {nameof(BaseReporter)}, {nameof(HttpReporter)}, {nameof(ButtPlugReporter)})"
55 | );
56 | _httpReporterUrlEntry = _preferencesCategory.CreateEntry
57 | (
58 | "HttpReporterUrl",
59 | "http://127.0.0.1:7788/report",
60 | description: "Report URL for HttpReporter"
61 | );
62 | _httpReportInGameInterval = _preferencesCategory.CreateEntry
63 | (
64 | "HttpReportInGameInterval",
65 | 3000,
66 | description: "Time interval for HttpReporter to reporting InGame events"
67 | );
68 | _disableEventLogEntry = _preferencesCategory.CreateEntry
69 | (
70 | "DisableEventLog",
71 | false,
72 | description: "Not to report events to Console"
73 | );
74 | _buttPlugServerUrlEntry = _preferencesCategory.CreateEntry
75 | (
76 | "ButtPlugServerUrl",
77 | "ws://localhost:12345",
78 | description: "Websocket URL of ButtPlug server (Intiface Central)"
79 | );
80 | _buttPlugActiveVibrateScalar = _preferencesCategory.CreateEntry
81 | (
82 | "ButtPlugActiveVibrateScalar",
83 | 0.5,
84 | description: "Set the ButtPlug vibrate scalar when game events active"
85 | );
86 | _buttPlugShotVibrateScalar = _preferencesCategory.CreateEntry
87 | (
88 | "ButtPlugShotVibrateScalar",
89 | 1.0,
90 | description: "Set the ButtPlug vibrate scalar when gun shot"
91 | );
92 | _buttPlugVibrateDuration = _preferencesCategory.CreateEntry
93 | (
94 | "ButtPlugVibrateDuration",
95 | 300,
96 | description:
97 | "Set the ButtPlug vibrate duration when gun shot (Millisecond)"
98 | );
99 | _buttPlugVibrateCmdIndexList = _preferencesCategory.CreateEntry
100 | (
101 | "ButtPlugVibrateCmdIndexList",
102 | new uint[] { },
103 | description:
104 | "Set the index of ButtPlug vibrate scalar commands, you can set multiple index or empty as default. (e.g. [0,1])"
105 | );
106 | _buttPlugAdditionalScalarList = _preferencesCategory.CreateEntry
107 | (
108 | "ButtPlugAdditionalScalarList",
109 | new[]
110 | {
111 | new ButtPlugAdditionalScalar
112 | {
113 | Enable = false,
114 | ActuatorType = ActuatorType.Oscillate,
115 | Index = 0,
116 | Scalar = 0.5
117 | },
118 | new ButtPlugAdditionalScalar
119 | {
120 | Enable = false,
121 | ActuatorType = ActuatorType.Inflate,
122 | Index = 0,
123 | Scalar = 0.5
124 | }
125 | },
126 | description:
127 | "Set the additional ButtPlug scalar commands, which called during vibrate (It will set to 0 after vibrate stop)"
128 | );
129 | MelonPreferences.Save();
130 | }
131 |
132 | public override void OnLateInitializeMelon()
133 | {
134 | SetupEventReporter();
135 | }
136 |
137 | public override void OnLateUpdate()
138 | {
139 | ReportBulbBroken();
140 | ReportZombieRun();
141 | ReportEnterLevel1();
142 | ReportLevel1Zombie();
143 | ReportEndZombie();
144 | ReportPlayerDied();
145 | }
146 |
147 | public override void OnUpdate()
148 | {
149 | ReportShot();
150 | }
151 |
152 | public override void OnSceneWasInitialized(int buildIndex, string sceneName)
153 | {
154 | _eventReporter.ReportGameEnterEvent();
155 | }
156 |
157 | public override void OnApplicationQuit()
158 | {
159 | _eventReporter.ReportGameExitEvent();
160 | }
161 |
162 | private void SetupEventReporter()
163 | {
164 | var eventReporterType = _eventReporterTypeEntry.Value;
165 | LoggerInstance.Msg($"Using reporter: {eventReporterType}");
166 | switch (eventReporterType)
167 | {
168 | case nameof(BaseReporter):
169 | _eventReporter = new BaseReporter(this);
170 | break;
171 | case nameof(HttpReporter):
172 | _eventReporter = new HttpReporter(
173 | this,
174 | _httpReporterUrlEntry,
175 | _httpReportInGameInterval
176 | );
177 | break;
178 | case nameof(ButtPlugReporter):
179 | try
180 | {
181 | _eventReporter = new ButtPlugReporter
182 | (
183 | this,
184 | _buttPlugActiveVibrateScalar,
185 | _buttPlugServerUrlEntry,
186 | _buttPlugShotVibrateScalar,
187 | _buttPlugVibrateCmdIndexList,
188 | _buttPlugVibrateDuration,
189 | _buttPlugAdditionalScalarList
190 | );
191 | }
192 | catch (Exception e)
193 | {
194 | LoggerInstance.Error($"ButtPlugReporter reporter initialize failed: {e}");
195 | }
196 |
197 | break;
198 | }
199 |
200 | if (_eventReporter is BaseReporter baseReporter) baseReporter.DisableEventLog = _disableEventLogEntry.Value;
201 | }
202 |
203 | private void UpdateEventStatus(string eventName, bool isActivate)
204 | {
205 | if (isActivate && !_events[eventName])
206 | {
207 | _events[eventName] = true;
208 | _eventReporter.ReportActivateEvent(eventName);
209 | }
210 | else if (!isActivate && _events[eventName])
211 | {
212 | _events[eventName] = false;
213 | _eventReporter.ReportDeactivateEvent(eventName);
214 | }
215 | }
216 |
217 | private void ReportBulbBroken()
218 | {
219 | var gameObject = GameObject.Find("/2etaj/shtyki/lampLopnyla");
220 | if (gameObject == null)
221 | {
222 | UpdateEventStatus(EventEnum.BulbBroken.ToString(), false);
223 | return;
224 | }
225 |
226 | UpdateEventStatus(EventEnum.BulbBroken.ToString(), gameObject.activeSelf);
227 | }
228 |
229 | private void ReportZombieRun()
230 | {
231 | var gameObject = GameObject.Find("/z/GameObject");
232 | if (gameObject == null)
233 | {
234 | UpdateEventStatus(EventEnum.ZombieRun.ToString(), false);
235 | return;
236 | }
237 |
238 | UpdateEventStatus(EventEnum.ZombieRun.ToString(), gameObject.activeSelf);
239 | }
240 |
241 | private void ReportEnterLevel1()
242 | {
243 | var gameObject = GameObject.Find("/lvl1");
244 | if (gameObject == null)
245 | {
246 | UpdateEventStatus(EventEnum.EnterLevel1.ToString(), false);
247 | return;
248 | }
249 |
250 | UpdateEventStatus
251 | (
252 | EventEnum.EnterLevel1.ToString(),
253 | gameObject.activeSelf && _events[EventEnum.BulbBroken.ToString()] &&
254 | _events[EventEnum.ZombieRun.ToString()]
255 | );
256 | }
257 |
258 | private void ReportLevel1Zombie()
259 | {
260 | var gameObject = GameObject.Find("/lvl1/z");
261 | if (gameObject == null)
262 | {
263 | UpdateEventStatus(EventEnum.Level1Zombie.ToString(), false);
264 | return;
265 | }
266 |
267 | var level1ZombieExists = gameObject.GetComponentsInChildren()
268 | .FirstOrDefault(child =>
269 | child.name.StartsWith("Ch10_nonPBR") && child.gameObject.activeSelf &&
270 | child.GetComponent().enabled) != null;
271 | UpdateEventStatus(EventEnum.Level1Zombie.ToString(), level1ZombieExists);
272 | }
273 |
274 | private void ReportEndZombie()
275 | {
276 | var gameObject = GameObject.Find("/end/Ch10_nonPBR (11)");
277 | if (gameObject == null)
278 | {
279 | UpdateEventStatus(EventEnum.EndZombie.ToString(), false);
280 | return;
281 | }
282 |
283 | UpdateEventStatus(EventEnum.EndZombie.ToString(), gameObject.activeSelf);
284 | }
285 |
286 | private void ReportPlayerDied()
287 | {
288 | var gameObject = GameObject.Find("/DIE");
289 | if (gameObject == null)
290 | {
291 | UpdateEventStatus(EventEnum.PlayerDied.ToString(), false);
292 | return;
293 | }
294 |
295 | UpdateEventStatus(EventEnum.PlayerDied.ToString(), gameObject.activeSelf);
296 | }
297 |
298 | private void ReportShot()
299 | {
300 | var gameObject = GameObject.Find("/First Person Controller/First Person Camera/Armpist");
301 | if (gameObject == null)
302 | return;
303 | var pistolObject = gameObject.GetComponent();
304 | if (
305 | gameObject.activeSelf &&
306 | !(pistolObject.YBRAL > 0) && !(pistolObject.pl.walking == 2 || pistolObject.stene.vstene) &&
307 | Input.GetButtonDown("SHOOT") && !pistolObject.cantshoot && pistolObject.ammo > 0)
308 | _eventReporter.ReportShot();
309 | }
310 | }
311 | }
--------------------------------------------------------------------------------