Skip to content

Commit

Permalink
Merge upstream
Browse files Browse the repository at this point in the history
  • Loading branch information
ManlyMarco committed Oct 28, 2024
2 parents 9f9e139 + 3f5584d commit fa2fe4e
Show file tree
Hide file tree
Showing 41 changed files with 2,433 additions and 65 deletions.
82 changes: 62 additions & 20 deletions MainGameVR/Caress/CaressController.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using HarmonyLib;
using KKS_VR.Settings;
using Manager;
using UnityEngine;
Expand All @@ -16,10 +18,6 @@ namespace KKS_VR.Caress
/// </summary>
internal class CaressController : ProtectedBehaviour
{
private AibuColliderTracker _aibuTracker;
private Controller _controller;

private Controller.Lock _lock; // may be null but never invalid
// Basic plan:
//
// * Keep track of the potential caress points
Expand All @@ -29,8 +27,13 @@ internal class CaressController : ProtectedBehaviour
// * When the trigger is pulled, initiate caress.
// * Delay releasing of the lock until the trigger is released.

private KoikatuSettings _settings;
private bool _triggerPressed; // Whether the trigger is currently pressed. false if _lock is null.
KoikatuSettings _settings;
Controller _controller;
AibuColliderTracker _aibuTracker;
Undresser _undresser;
Controller.Lock _lock; // may be null but never invalid
bool _triggerPressed; // Whether the trigger is currently pressed. false if _lock is null.
ValueTuple<ChaControl, ChaFileDefine.ClothesKind, Vector3>? _undressing;

public Controller getController()
{
Expand All @@ -48,8 +51,8 @@ protected override void OnAwake()
VRLog.Warn("HSceneProc not found");
return;
}

_aibuTracker = new AibuColliderTracker(proc, transform);
_aibuTracker = new AibuColliderTracker(proc, referencePoint: transform);
_undresser = new Undresser(proc);
}

private void OnDestroy()
Expand All @@ -59,12 +62,13 @@ private void OnDestroy()

protected override void OnUpdate()
{
if (_lock != null && Scene.NowSceneNames[0] == "HPointMove") ReleaseLock();
if (_lock != null)
{
HandleTrigger();
HandleToolChange();
HandleUndress();
}
UpdateLock();
}

protected void OnTriggerEnter(Collider other)
Expand All @@ -75,19 +79,24 @@ protected void OnTriggerEnter(Collider other)
var wasIntersecting = _aibuTracker.IsIntersecting();
if (_aibuTracker.AddIfRelevant(other))
{
UpdateLock();
if (_lock != null && _settings.AutomaticTouching)
if (!wasIntersecting && _aibuTracker.IsIntersecting())
{
var colliderKind = _aibuTracker.GetCurrentColliderKind(out var femaleIndex);
if (HandCtrl.AibuColliderKind.reac_head <= colliderKind)
_controller.StartRumble(new RumbleImpulse(1000));
if (_settings.AutomaticTouching)
{
CaressUtil.SetSelectKindTouch(_aibuTracker.Proc, femaleIndex, colliderKind);
StartCoroutine(CaressUtil.ClickCo());
var colliderKind = _aibuTracker.GetCurrentColliderKind(out int femaleIndex);
if (HandCtrl.AibuColliderKind.reac_head <= colliderKind &&
!CaressUtil.IsSpeaking(_aibuTracker.Proc, femaleIndex))
{
CaressUtil.SetSelectKindTouch(_aibuTracker.Proc, femaleIndex, colliderKind);
StartCoroutine(CaressUtil.ClickCo());
}
}
}

if (!wasIntersecting && _aibuTracker.IsIntersecting()) _controller.StartRumble(new RumbleImpulse(1000));
}

_undresser.Enter(other);
UpdateLock();
}
catch (Exception e)
{
Expand All @@ -99,7 +108,9 @@ protected void OnTriggerExit(Collider other)
{
try
{
if (_aibuTracker.RemoveIfRelevant(other)) UpdateLock();
_aibuTracker.RemoveIfRelevant(other);
_undresser.Exit(other);
UpdateLock();
}
catch (Exception e)
{
Expand All @@ -109,7 +120,8 @@ protected void OnTriggerExit(Collider other)

private void UpdateLock()
{
var shouldHaveLock = _aibuTracker.IsIntersecting();
bool shouldHaveLock = (_aibuTracker.IsIntersecting() || _undressing != null) &&
Manager.Scene.NowSceneNames[0] != "HPointMove";
if (shouldHaveLock && _lock == null)
_controller.TryAcquireFocus(out _lock);
else if (!shouldHaveLock && _lock != null && !_triggerPressed) ReleaseLock();
Expand All @@ -129,7 +141,7 @@ private void HandleTrigger()
{
HandCtrlHooks.InjectMouseButtonUp(0);
_triggerPressed = false;
if (!_aibuTracker.IsIntersecting()) ReleaseLock();
UpdateLock();
}
}

Expand All @@ -141,6 +153,35 @@ private void HandleToolChange()
UpdateSelectKindTouch();
HandCtrlHooks.InjectMouseScroll(1f);
}

}

private void HandleUndress()
{
var device = _controller.Input;
var proc = _aibuTracker.Proc;
if (_undressing == null && device.GetPressDown(EVRButtonId.k_EButton_SteamVR_Touchpad))
{
var females = new Traverse(proc).Field<List<ChaControl>>("lstFemale").Value;
var toUndress = _undresser.ComputeUndressTarget(females, out int femaleIndex);
if (toUndress is ChaFileDefine.ClothesKind kind)
{
_undressing = ValueTuple.Create(females[femaleIndex], kind, transform.position);
}
}
if (_undressing is ValueTuple<ChaControl, ChaFileDefine.ClothesKind, Vector3> undressing
&& device.GetPressUp(EVRButtonId.k_EButton_SteamVR_Touchpad))
{
if (0.3f * 0.3f < (transform.position - undressing.Item3).sqrMagnitude)
{
undressing.Item1.SetClothesState((int)undressing.Item2, 3);
}
else
{
undressing.Item1.SetClothesStateNext((int)undressing.Item2);
}
_undressing = null;
}
}

private void ReleaseLock()
Expand All @@ -149,6 +190,7 @@ private void ReleaseLock()
if (_triggerPressed)
HandCtrlHooks.InjectMouseButtonUp(0);
_triggerPressed = false;
_undressing = null;
_lock.Release();
_lock = null;
}
Expand Down
9 changes: 9 additions & 0 deletions MainGameVR/Caress/CaressUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,14 @@ public static IEnumerator ClickCo()
while (!consumed) yield return null;
HandCtrlHooks.InjectMouseButtonUp(0);
}

/// <summary>
/// Is the specified female speaking? Moans are ignored.
/// </summary>
public static bool IsSpeaking(HSceneProc proc, int femaleIndex)
{
return proc.voice.nowVoices[femaleIndex].state == HVoiceCtrl.VoiceKind.voice &&
Manager.Voice.IsPlay(proc.flags.transVoiceMouth[femaleIndex], true);
}
}
}
181 changes: 181 additions & 0 deletions MainGameVR/Caress/Undresser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using VRGIN.Core;
using HarmonyLib;

using static ChaFileDefine;

namespace KKS_VR.Caress
{
/// <summary>
/// An object responsible for determining which clothing item to remove,
/// based on the controller position.
/// </summary>
class Undresser
{
private readonly Dictionary<Collider, ValueTuple<int, InteractionBodyPart>> _knownColliders =
new Dictionary<Collider, ValueTuple<int, InteractionBodyPart>>();
private readonly HashSet<Collider> _currentlyIntersecting = new HashSet<Collider>();

public Undresser(HSceneProc proc)
{
// Populate _knownColliders.
var lstFemale = new Traverse(proc).Field("lstFemale").GetValue<List<ChaControl>>();
for (int i = 0; i < lstFemale.Count; i++)
{
var colliders = lstFemale[i].GetComponentsInChildren<Collider>(includeInactive: true);
foreach (var collider in colliders)
{
if (ColliderBodyPart(collider) is InteractionBodyPart part)
{
_knownColliders.Add(collider, ValueTuple.Create(i, part));
}
}
}
}

public void Enter(Collider collider)
{
if (_knownColliders.ContainsKey(collider))
{
_currentlyIntersecting.Add(collider);
}
}

public void Exit(Collider collider)
{
_currentlyIntersecting.Remove(collider);
}

public ClothesKind? ComputeUndressTarget(List<ChaControl> females, out int femaleIndex)
{
femaleIndex = 0;
if (_currentlyIntersecting.Count == 0)
{
return null;
}
var part = (InteractionBodyPart)9999;
foreach (var collider in _currentlyIntersecting)
{
var item = _knownColliders[collider];
if (item.Item2 < part)
{
femaleIndex = item.Item1;
part = item.Item2;
break;
}
}

var female = females[femaleIndex];
var targets = _itemsForPart[(int)part];
if (part == InteractionBodyPart.Crotch && IsWearingSkirt(female))
{
// Special case: if the character is wearing a skirt, allow
// directly removing the underwear.
targets = _skirtCrotchTargets;
}
foreach (var target in targets)
{
if (!female.IsClothesStateKind((int)target.kind))
{
continue;
}
var state = female.fileStatus.clothesState[(int)target.kind];
if (target.min_state <= state && state <= target.max_state)
{
VRLog.Info($"toUndress: {target.kind}");
return target.kind;
}
}
return null;
}

private static bool IsWearingSkirt(ChaControl female)
{
var objBot = female.objClothes[1];
return objBot != null && objBot.GetComponent<DynamicBone>() != null;
}

/// <summary>
/// A body part the user can interact with. A more specific part gets
/// a lower number.
/// </summary>
private enum InteractionBodyPart
{
Crotch,
Groin,
Breast,
LegL,
LegR,
Forearm,
UpperArm,
Thigh,
Torso,
}

private static readonly UndressTarget[][] _itemsForPart = new[]
{
new[] { Target(ClothesKind.bot), Target(ClothesKind.panst), Target(ClothesKind.shorts) },
new[] { Target(ClothesKind.bot, 0), Target(ClothesKind.panst), Target(ClothesKind.shorts) },
new[] { Target(ClothesKind.top, 0), Target(ClothesKind.bra) },
new[] { Target(ClothesKind.socks), Target(ClothesKind.shorts, 2, 2) },
new[] { Target(ClothesKind.socks) },
new UndressTarget[] { },
new[] { Target(ClothesKind.top) },
new[] { Target(ClothesKind.panst), Target(ClothesKind.bot), Target(ClothesKind.socks) },
new[] { Target(ClothesKind.top) },
};

private static readonly UndressTarget[] _skirtCrotchTargets =
new[] { Target(ClothesKind.panst), Target(ClothesKind.shorts), Target(ClothesKind.bot) };

private static UndressTarget Target(ClothesKind kind, int max_state = 2, int min_state = 0)
{
return new UndressTarget(kind, max_state, min_state);
}

private static InteractionBodyPart? ColliderBodyPart(Collider collider)
{
var name = collider.name;
if (name == "aibu_hit_kokan" ||
name == "aibu_hit_ana")
return InteractionBodyPart.Crotch;
if (name.StartsWith("aibu_hit_siri") ||
name.StartsWith("aibu_reaction_waist"))
return InteractionBodyPart.Groin;
if (name.StartsWith("cf_hit_bust"))
return InteractionBodyPart.Breast;
if (name == "aibu_reaction_legL")
return InteractionBodyPart.LegL;
if (name == "aibu_reaction_legR")
return InteractionBodyPart.LegR;
if (name.StartsWith("cf_hit_wrist"))
return InteractionBodyPart.Forearm;
if (name.StartsWith("cf_hit_arm"))
return InteractionBodyPart.UpperArm;
if (name.StartsWith("aibu_reaction_thigh"))
return InteractionBodyPart.Thigh;
if (name == "cf_hit_spine01" ||
name == "cf_hit_spine03" ||
name == "cf_hit_berry")
return InteractionBodyPart.Torso;
return null;
}

private struct UndressTarget
{
public UndressTarget(ClothesKind k, int m, int mm)
{
kind = k;
max_state = m;
min_state = mm;
}
public ClothesKind kind;
public int max_state;
public int min_state;
}
}
}
3 changes: 2 additions & 1 deletion MainGameVR/Caress/VRMouth.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ private void HandleTriggerEnter(Collider other)

if (_kissCoShouldEnd == null &&
HandCtrl.AibuColliderKind.reac_head <= colliderKind &&
_settings.AutomaticTouchingByHmd)
_settings.AutomaticTouchingByHmd &&
!CaressUtil.IsSpeaking(_aibuTracker.Proc, femaleIndex))
StartCoroutine(TriggerReactionCo(femaleIndex, colliderKind));
}
}
Expand Down
7 changes: 3 additions & 4 deletions MainGameVR/Controls/ButtonsSubtool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,10 @@ public void ButtonDown(AssignableFunction fun)
VR.Input.Mouse.MiddleButtonDown();
break;
case AssignableFunction.LROTATION:
Rotate(-_Settings.RotationAngle);
break;
case AssignableFunction.RROTATION:
// ここでは何もせず、上げたときだけ処理する
Rotate(_Settings.RotationAngle);
break;
case AssignableFunction.SCROLLUP:
StartScroll(1);
Expand Down Expand Up @@ -147,10 +149,7 @@ public void ButtonUp(AssignableFunction fun)
VR.Input.Mouse.MiddleButtonUp();
break;
case AssignableFunction.LROTATION:
Rotate(-_Settings.RotationAngle);
break;
case AssignableFunction.RROTATION:
Rotate(_Settings.RotationAngle);
break;
case AssignableFunction.SCROLLUP:
case AssignableFunction.SCROLLDOWN:
Expand Down
Loading

0 comments on commit fa2fe4e

Please sign in to comment.