mirror of
https://github.com/hanetzer/sdraw_mods_cvr.git
synced 2025-09-03 10:29:22 +00:00
New mod: Vive Extended Input
Unified UI elements script
This commit is contained in:
parent
14e01e692e
commit
90de21cce8
26 changed files with 900 additions and 919 deletions
|
@ -1,14 +1,15 @@
|
|||
Merged set of MelonLoader mods for ChilloutVR.
|
||||
|
||||
**Table for game build 2023r172:**
|
||||
**Table for game build 2023r172p1:**
|
||||
| Full name | Short name | Latest version | Available in [CVRMA](https://github.com/knah/CVRMelonAssistant) |
|
||||
|:---------:|:----------:|:--------------:| :----------------------------------------------------------------|
|
||||
| [Avatar Motion Tweaker](/ml_amt/README.md) | ml_amt | 1.3.2 [:arrow_down:](../../releases/latest/download/ml_amt.dll)| ✔ Yes |
|
||||
| [Avatar Motion Tweaker](/ml_amt/README.md) | ml_amt | 1.3.3 [:arrow_down:](../../releases/latest/download/ml_amt.dll)| ✔ Yes<br>:hourglass_flowing_sand: Update review |
|
||||
| [Desktop Head Tracking](/ml_dht/README.md)| ml_dht | - | ✔ Yes<br>:warning:Broken |
|
||||
| [Leap Motion Extension](/ml_lme/README.md)| ml_lme | 1.4.2 [:arrow_down:](../../releases/latest/download/ml_lme.dll)| ✔ Yes |
|
||||
| [Pickup Arm Movement](/ml_pam/README.md)| ml_pam | 1.0.7 [:arrow_down:](../../releases/latest/download/ml_pam.dll)| ✔ Yes |
|
||||
| [Leap Motion Extension](/ml_lme/README.md)| ml_lme | 1.4.3 [:arrow_down:](../../releases/latest/download/ml_lme.dll)| ✔ Yes<br>:hourglass_flowing_sand: Update review |
|
||||
| [Pickup Arm Movement](/ml_pam/README.md)| ml_pam | 1.0.8 [:arrow_down:](../../releases/latest/download/ml_pam.dll)| ✔ Yes<br>:hourglass_flowing_sand: Update review |
|
||||
| [Player Movement Copycat](/ml_pmc/README.md)| ml_pmc | 1.0.3 [:arrow_down:](../../releases/latest/download/ml_pmc.dll)| ✔ Yes |
|
||||
| [Player Ragdoll Mod](/ml_prm/README.md)| ml_prm | 1.0.10 [:arrow_down:](../../releases/latest/download/ml_prm.dll)| ✔ Yes<br>:hourglass_flowing_sand: Update review |
|
||||
| [Vive Extended Input](/ml_vei/README.md) | ml_vei | 1.0.0 [:arrow_down:](../../releases/latest/download/ml_vei.dll)| :question: Not planned yet |
|
||||
|
||||
**Archived mods:**
|
||||
| Full name | Short name | Notes |
|
||||
|
|
267
js/ui_elements.js
Normal file
267
js/ui_elements.js
Normal file
|
@ -0,0 +1,267 @@
|
|||
// Modified from original `inp` types, because I have no js knowledge to hook stuff
|
||||
|
||||
if (typeof inp_toggle_mod === 'undefined') {
|
||||
window.inp_toggle_mod = function (_obj, _callbackName) {
|
||||
this.obj = _obj;
|
||||
this.callbackName = _callbackName;
|
||||
this.value = _obj.getAttribute('data-current');
|
||||
this.name = _obj.id;
|
||||
this.type = _obj.getAttribute('data-type');
|
||||
|
||||
var self = this;
|
||||
|
||||
this.mouseDown = function (_e) {
|
||||
self.value = self.value == "True" ? "False" : "True";
|
||||
self.updateState();
|
||||
}
|
||||
|
||||
this.updateState = function () {
|
||||
self.obj.classList.remove("checked");
|
||||
if (self.value == "True") {
|
||||
self.obj.classList.add("checked");
|
||||
}
|
||||
|
||||
engine.call(self.callbackName, self.name, self.value);
|
||||
}
|
||||
|
||||
_obj.addEventListener('mousedown', this.mouseDown);
|
||||
|
||||
this.getValue = function () {
|
||||
return self.value;
|
||||
}
|
||||
|
||||
this.updateValue = function (value) {
|
||||
self.value = value;
|
||||
|
||||
self.obj.classList.remove("checked");
|
||||
if (self.value == "True") {
|
||||
self.obj.classList.add("checked");
|
||||
}
|
||||
}
|
||||
|
||||
this.updateValue(this.value);
|
||||
|
||||
return {
|
||||
name: this.name,
|
||||
value: this.getValue,
|
||||
updateValue: this.updateValue
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof inp_slider_mod === 'undefined') {
|
||||
window.inp_slider_mod = function (_obj, _callbackName) {
|
||||
this.obj = _obj;
|
||||
this.callbackName = _callbackName;
|
||||
this.minValue = parseFloat(_obj.getAttribute('data-min'));
|
||||
this.maxValue = parseFloat(_obj.getAttribute('data-max'));
|
||||
this.percent = 0;
|
||||
this.value = parseFloat(_obj.getAttribute('data-current'));
|
||||
this.dragActive = false;
|
||||
this.name = _obj.id;
|
||||
this.type = _obj.getAttribute('data-type');
|
||||
this.stepSize = _obj.getAttribute('data-stepSize') || 0;
|
||||
this.format = _obj.getAttribute('data-format') || '{value}';
|
||||
|
||||
var self = this;
|
||||
|
||||
if (this.stepSize != 0)
|
||||
this.value = Math.round(this.value / this.stepSize) * this.stepSize;
|
||||
else
|
||||
this.value = Math.round(this.value);
|
||||
|
||||
this.valueLabelBackground = document.createElement('div');
|
||||
this.valueLabelBackground.className = 'valueLabel background';
|
||||
this.valueLabelBackground.innerHTML = this.format.replace('{value}', this.value);
|
||||
this.obj.appendChild(this.valueLabelBackground);
|
||||
|
||||
this.valueBar = document.createElement('div');
|
||||
this.valueBar.className = 'valueBar';
|
||||
this.valueBar.setAttribute('style', 'width: ' + (((this.value - this.minValue) / (this.maxValue - this.minValue)) * 100) + '%;');
|
||||
this.obj.appendChild(this.valueBar);
|
||||
|
||||
this.valueLabelForeground = document.createElement('div');
|
||||
this.valueLabelForeground.className = 'valueLabel foreground';
|
||||
this.valueLabelForeground.innerHTML = this.format.replace('{value}', this.value);
|
||||
this.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / ((this.value - this.minValue) / (this.maxValue - this.minValue)) * 100) + '%;');
|
||||
this.valueBar.appendChild(this.valueLabelForeground);
|
||||
|
||||
this.mouseDown = function (_e) {
|
||||
self.dragActive = true;
|
||||
self.mouseMove(_e, false);
|
||||
}
|
||||
|
||||
this.mouseMove = function (_e, _write) {
|
||||
if (self.dragActive) {
|
||||
var rect = _obj.getBoundingClientRect();
|
||||
var start = rect.left;
|
||||
var end = rect.right;
|
||||
self.percent = Math.min(Math.max((_e.clientX - start) / rect.width, 0), 1);
|
||||
var value = self.percent;
|
||||
value *= (self.maxValue - self.minValue);
|
||||
value += self.minValue;
|
||||
if (self.stepSize != 0) {
|
||||
value = Math.round(value / self.stepSize);
|
||||
self.value = value * self.stepSize;
|
||||
self.percent = (self.value - self.minValue) / (self.maxValue - self.minValue);
|
||||
}
|
||||
else
|
||||
self.value = Math.round(value);
|
||||
|
||||
self.valueBar.setAttribute('style', 'width: ' + (self.percent * 100) + '%;');
|
||||
self.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / self.percent * 100) + '%;');
|
||||
self.valueLabelBackground.innerHTML = self.valueLabelForeground.innerHTML = self.format.replace('{value}', self.value);
|
||||
|
||||
engine.call(self.callbackName, self.name, "" + self.value);
|
||||
self.displayImperial();
|
||||
}
|
||||
}
|
||||
|
||||
this.mouseUp = function (_e) {
|
||||
self.mouseMove(_e, true);
|
||||
self.dragActive = false;
|
||||
}
|
||||
|
||||
_obj.addEventListener('mousedown', this.mouseDown);
|
||||
document.addEventListener('mousemove', this.mouseMove);
|
||||
document.addEventListener('mouseup', this.mouseUp);
|
||||
|
||||
this.getValue = function () {
|
||||
return self.value;
|
||||
}
|
||||
|
||||
this.updateValue = function (value) {
|
||||
if (self.stepSize != 0)
|
||||
self.value = Math.round(value * self.stepSize) / self.stepSize;
|
||||
else
|
||||
self.value = Math.round(value);
|
||||
self.percent = (self.value - self.minValue) / (self.maxValue - self.minValue);
|
||||
self.valueBar.setAttribute('style', 'width: ' + (self.percent * 100) + '%;');
|
||||
self.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / self.percent * 100) + '%;');
|
||||
self.valueLabelBackground.innerHTML = self.valueLabelForeground.innerHTML = self.format.replace('{value}', self.value);
|
||||
self.displayImperial();
|
||||
}
|
||||
|
||||
this.displayImperial = function () {
|
||||
var displays = document.querySelectorAll('.imperialDisplay');
|
||||
for (var i = 0; i < displays.length; i++) {
|
||||
var binding = displays[i].getAttribute('data-binding');
|
||||
if (binding == self.name) {
|
||||
var realFeet = ((self.value * 0.393700) / 12);
|
||||
var feet = Math.floor(realFeet);
|
||||
var inches = Math.floor((realFeet - feet) * 12);
|
||||
displays[i].innerHTML = feet + "'" + inches + '''';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
name: this.name,
|
||||
value: this.getValue,
|
||||
updateValue: this.updateValue
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof inp_dropdown_mod === 'undefined') {
|
||||
window.inp_dropdown_mod = function (_obj, _callbackName) {
|
||||
this.obj = _obj;
|
||||
this.callbackName = _callbackName;
|
||||
this.value = _obj.getAttribute('data-current');
|
||||
this.options = _obj.getAttribute('data-options').split(',');
|
||||
this.name = _obj.id;
|
||||
this.opened = false;
|
||||
this.keyValue = [];
|
||||
this.type = _obj.getAttribute('data-type');
|
||||
|
||||
this.optionElements = [];
|
||||
|
||||
var self = this;
|
||||
|
||||
this.SelectValue = function (_e) {
|
||||
self.value = _e.target.getAttribute('data-key');
|
||||
self.valueElement.innerHTML = _e.target.getAttribute('data-value');
|
||||
self.globalClose();
|
||||
|
||||
engine.call(self.callbackName, self.name, self.value);
|
||||
}
|
||||
|
||||
this.openClick = function (_e) {
|
||||
if (self.obj.classList.contains('open')) {
|
||||
self.obj.classList.remove('open');
|
||||
self.list.setAttribute('style', 'display: none;');
|
||||
} else {
|
||||
self.obj.classList.add('open');
|
||||
self.list.setAttribute('style', 'display: block;');
|
||||
self.opened = true;
|
||||
window.setTimeout(function () { self.opened = false; }, 10);
|
||||
}
|
||||
}
|
||||
|
||||
this.globalClose = function (_e) {
|
||||
if (self.opened) return;
|
||||
self.obj.classList.remove('open');
|
||||
self.list.setAttribute('style', 'display: none;');
|
||||
}
|
||||
|
||||
this.list = document.createElement('div');
|
||||
this.list.className = 'valueList';
|
||||
|
||||
this.updateOptions = function () {
|
||||
self.list.innerHTML = "";
|
||||
for (var i = 0; i < self.options.length; i++) {
|
||||
self.optionElements[i] = document.createElement('div');
|
||||
self.optionElements[i].className = 'listValue';
|
||||
var valuePair = Array.isArray(self.options[i]) ? self.options[i] : self.options[i].split(':');
|
||||
var key = "";
|
||||
var value = "";
|
||||
if (valuePair.length == 1) {
|
||||
key = valuePair[0];
|
||||
value = valuePair[0];
|
||||
} else {
|
||||
key = valuePair[0];
|
||||
value = valuePair[1];
|
||||
}
|
||||
self.keyValue[key] = value;
|
||||
self.optionElements[i].innerHTML = value;
|
||||
self.optionElements[i].setAttribute('data-value', value);
|
||||
self.optionElements[i].setAttribute('data-key', key);
|
||||
self.list.appendChild(self.optionElements[i]);
|
||||
self.optionElements[i].addEventListener('mousedown', self.SelectValue);
|
||||
}
|
||||
|
||||
self.valueElement.innerHTML = self.keyValue[self.value];
|
||||
}
|
||||
|
||||
this.valueElement = document.createElement('div');
|
||||
this.valueElement.className = 'dropdown-value';
|
||||
|
||||
this.updateOptions();
|
||||
|
||||
this.obj.appendChild(this.valueElement);
|
||||
this.obj.appendChild(this.list);
|
||||
this.valueElement.addEventListener('mousedown', this.openClick);
|
||||
document.addEventListener('mousedown', this.globalClose);
|
||||
|
||||
this.getValue = function () {
|
||||
return self.value;
|
||||
}
|
||||
|
||||
this.updateValue = function (value) {
|
||||
self.value = value;
|
||||
self.valueElement.innerHTML = self.keyValue[value];
|
||||
}
|
||||
|
||||
this.setOptions = function (options) {
|
||||
self.options = options;
|
||||
}
|
||||
|
||||
return {
|
||||
name: this.name,
|
||||
value: this.getValue,
|
||||
updateValue: this.updateValue,
|
||||
updateOptions: this.updateOptions,
|
||||
setOptions: this.setOptions
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
[assembly: MelonLoader.MelonInfo(typeof(ml_amt.AvatarMotionTweaker), "AvatarMotionTweaker", "1.3.2", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
|
||||
[assembly: MelonLoader.MelonInfo(typeof(ml_amt.AvatarMotionTweaker), "AvatarMotionTweaker", "1.3.3", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
|
||||
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
|
||||
[assembly: MelonLoader.MelonOptionalDependencies("ml_prm", "ml_pmc")]
|
||||
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
|
||||
|
|
|
@ -78,7 +78,8 @@ namespace ml_amt
|
|||
};
|
||||
ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) =>
|
||||
{
|
||||
ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("menu.js"));
|
||||
ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("ui_elements.js"));
|
||||
ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("ui_menu.js"));
|
||||
foreach(var l_entry in ms_entries)
|
||||
ViewManager.Instance.gameMenuView.View.TriggerEvent("updateModSettingAMT", l_entry.DisplayName, l_entry.GetValueAsString());
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
|
@ -6,7 +6,7 @@
|
|||
<Company>None</Company>
|
||||
<Product>AvatarMotionTweaker</Product>
|
||||
<PackageId>AvatarMotionTweaker</PackageId>
|
||||
<Version>1.3.2</Version>
|
||||
<Version>1.3.3</Version>
|
||||
<Platforms>x64</Platforms>
|
||||
<AssemblyName>ml_amt</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
@ -26,11 +26,15 @@
|
|||
|
||||
<ItemGroup>
|
||||
<None Remove="AvatarMotionTweaker.json" />
|
||||
<None Remove="resources\menu.js" />
|
||||
<None Remove="resources\ui_menu.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="resources\menu.js" />
|
||||
<EmbeddedResource Include="resources\ui_menu.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="..\js\ui_elements.js" Link="resources\ui_elements.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1,245 +0,0 @@
|
|||
// Add settings
|
||||
var g_modSettingsAMT = [];
|
||||
|
||||
engine.on('updateModSettingAMT', function (_name, _value) {
|
||||
for (var i = 0; i < g_modSettingsAMT.length; i++) {
|
||||
if (g_modSettingsAMT[i].name == _name) {
|
||||
g_modSettingsAMT[i].updateValue(_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Modified from original `inp` types, because I have no js knowledge to hook stuff
|
||||
function inp_slider_mod_amt(_obj, _callbackName) {
|
||||
this.obj = _obj;
|
||||
this.callbackName = _callbackName;
|
||||
this.minValue = parseFloat(_obj.getAttribute('data-min'));
|
||||
this.maxValue = parseFloat(_obj.getAttribute('data-max'));
|
||||
this.percent = 0;
|
||||
this.value = parseFloat(_obj.getAttribute('data-current'));
|
||||
this.dragActive = false;
|
||||
this.name = _obj.id;
|
||||
this.type = _obj.getAttribute('data-type');
|
||||
this.stepSize = _obj.getAttribute('data-stepSize') || 0;
|
||||
this.format = _obj.getAttribute('data-format') || '{value}';
|
||||
|
||||
var self = this;
|
||||
|
||||
if (this.stepSize != 0)
|
||||
this.value = Math.round(this.value / this.stepSize) * this.stepSize;
|
||||
else
|
||||
this.value = Math.round(this.value);
|
||||
|
||||
this.valueLabelBackground = document.createElement('div');
|
||||
this.valueLabelBackground.className = 'valueLabel background';
|
||||
this.valueLabelBackground.innerHTML = this.format.replace('{value}', this.value);
|
||||
this.obj.appendChild(this.valueLabelBackground);
|
||||
|
||||
this.valueBar = document.createElement('div');
|
||||
this.valueBar.className = 'valueBar';
|
||||
this.valueBar.setAttribute('style', 'width: ' + (((this.value - this.minValue) / (this.maxValue - this.minValue)) * 100) + '%;');
|
||||
this.obj.appendChild(this.valueBar);
|
||||
|
||||
this.valueLabelForeground = document.createElement('div');
|
||||
this.valueLabelForeground.className = 'valueLabel foreground';
|
||||
this.valueLabelForeground.innerHTML = this.format.replace('{value}', this.value);
|
||||
this.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / ((this.value - this.minValue) / (this.maxValue - this.minValue)) * 100) + '%;');
|
||||
this.valueBar.appendChild(this.valueLabelForeground);
|
||||
|
||||
this.mouseDown = function (_e) {
|
||||
self.dragActive = true;
|
||||
self.mouseMove(_e, false);
|
||||
}
|
||||
|
||||
this.mouseMove = function (_e, _write) {
|
||||
if (self.dragActive) {
|
||||
var rect = _obj.getBoundingClientRect();
|
||||
var start = rect.left;
|
||||
var end = rect.right;
|
||||
self.percent = Math.min(Math.max((_e.clientX - start) / rect.width, 0), 1);
|
||||
var value = self.percent;
|
||||
value *= (self.maxValue - self.minValue);
|
||||
value += self.minValue;
|
||||
if (self.stepSize != 0) {
|
||||
value = Math.round(value / self.stepSize);
|
||||
self.value = value * self.stepSize;
|
||||
self.percent = (self.value - self.minValue) / (self.maxValue - self.minValue);
|
||||
}
|
||||
else
|
||||
self.value = Math.round(value);
|
||||
|
||||
self.valueBar.setAttribute('style', 'width: ' + (self.percent * 100) + '%;');
|
||||
self.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / self.percent * 100) + '%;');
|
||||
self.valueLabelBackground.innerHTML = self.valueLabelForeground.innerHTML = self.format.replace('{value}', self.value);
|
||||
|
||||
engine.call(self.callbackName, self.name, "" + self.value);
|
||||
self.displayImperial();
|
||||
}
|
||||
}
|
||||
|
||||
this.mouseUp = function (_e) {
|
||||
self.mouseMove(_e, true);
|
||||
self.dragActive = false;
|
||||
}
|
||||
|
||||
_obj.addEventListener('mousedown', this.mouseDown);
|
||||
document.addEventListener('mousemove', this.mouseMove);
|
||||
document.addEventListener('mouseup', this.mouseUp);
|
||||
|
||||
this.getValue = function () {
|
||||
return self.value;
|
||||
}
|
||||
|
||||
this.updateValue = function (value) {
|
||||
if (self.stepSize != 0)
|
||||
self.value = Math.round(value * self.stepSize) / self.stepSize;
|
||||
else
|
||||
self.value = Math.round(value);
|
||||
self.percent = (self.value - self.minValue) / (self.maxValue - self.minValue);
|
||||
self.valueBar.setAttribute('style', 'width: ' + (self.percent * 100) + '%;');
|
||||
self.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / self.percent * 100) + '%;');
|
||||
self.valueLabelBackground.innerHTML = self.valueLabelForeground.innerHTML = self.format.replace('{value}', self.value);
|
||||
self.displayImperial();
|
||||
}
|
||||
|
||||
this.displayImperial = function () {
|
||||
var displays = document.querySelectorAll('.imperialDisplay');
|
||||
for (var i = 0; i < displays.length; i++) {
|
||||
var binding = displays[i].getAttribute('data-binding');
|
||||
if (binding == self.name) {
|
||||
var realFeet = ((self.value * 0.393700) / 12);
|
||||
var feet = Math.floor(realFeet);
|
||||
var inches = Math.floor((realFeet - feet) * 12);
|
||||
displays[i].innerHTML = feet + "'" + inches + '''';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
name: this.name,
|
||||
value: this.getValue,
|
||||
updateValue: this.updateValue
|
||||
}
|
||||
}
|
||||
|
||||
// Modified from original `inp` types, because I have no js knowledge to hook stuff
|
||||
function inp_toggle_mod_amt(_obj, _callbackName) {
|
||||
this.obj = _obj;
|
||||
this.callbackName = _callbackName;
|
||||
this.value = _obj.getAttribute('data-current');
|
||||
this.name = _obj.id;
|
||||
this.type = _obj.getAttribute('data-type');
|
||||
|
||||
var self = this;
|
||||
|
||||
this.mouseDown = function (_e) {
|
||||
self.value = self.value == "True" ? "False" : "True";
|
||||
self.updateState();
|
||||
}
|
||||
|
||||
this.updateState = function () {
|
||||
self.obj.classList.remove("checked");
|
||||
if (self.value == "True") {
|
||||
self.obj.classList.add("checked");
|
||||
}
|
||||
|
||||
engine.call(self.callbackName, self.name, self.value);
|
||||
}
|
||||
|
||||
_obj.addEventListener('mousedown', this.mouseDown);
|
||||
|
||||
this.getValue = function () {
|
||||
return self.value;
|
||||
}
|
||||
|
||||
this.updateValue = function (value) {
|
||||
self.value = value;
|
||||
|
||||
self.obj.classList.remove("checked");
|
||||
if (self.value == "True") {
|
||||
self.obj.classList.add("checked");
|
||||
}
|
||||
}
|
||||
|
||||
this.updateValue(this.value);
|
||||
|
||||
return {
|
||||
name: this.name,
|
||||
value: this.getValue,
|
||||
updateValue: this.updateValue
|
||||
}
|
||||
}
|
||||
|
||||
// Add own menu
|
||||
{
|
||||
let l_block = document.createElement('div');
|
||||
l_block.innerHTML = `
|
||||
<div class ="settings-subcategory">
|
||||
<div class ="subcategory-name">Avatar Motion Tweaker</div>
|
||||
<div class ="subcategory-description"></div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Crouch limit: </div>
|
||||
<div class ="option-input">
|
||||
<div id="CrouchLimit" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="75"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Prone limit: </div>
|
||||
<div class ="option-input">
|
||||
<div id="ProneLimit" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="40"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">IK override while flying: </div>
|
||||
<div class ="option-input">
|
||||
<div id="IKOverrideFly" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">IK override while jumping: </div>
|
||||
<div class ="option-input">
|
||||
<div id="IKOverrideJump" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Follow hips on IK override: </div>
|
||||
<div class ="option-input">
|
||||
<div id="FollowHips" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Detect animations emote tag: </div>
|
||||
<div class ="option-input">
|
||||
<div id="DetectEmotes" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Adjusted locomotion mass center: </div>
|
||||
<div class ="option-input">
|
||||
<div id="MassCenter" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
document.getElementById('settings-ik').appendChild(l_block);
|
||||
|
||||
// Update sliders in new menu block
|
||||
let l_sliders = l_block.querySelectorAll('.inp_slider');
|
||||
for (var i = 0; i < l_sliders.length; i++) {
|
||||
g_modSettingsAMT[g_modSettingsAMT.length] = new inp_slider_mod_amt(l_sliders[i], 'MelonMod_AMT_Call_InpSlider');
|
||||
}
|
||||
|
||||
// Update toggles in new menu block
|
||||
let l_toggles = l_block.querySelectorAll('.inp_toggle');
|
||||
for (var i = 0; i < l_toggles.length; i++) {
|
||||
g_modSettingsAMT[g_modSettingsAMT.length] = new inp_toggle_mod_amt(l_toggles[i], 'MelonMod_AMT_Call_InpToggle');
|
||||
}
|
||||
}
|
84
ml_amt/resources/ui_menu.js
Normal file
84
ml_amt/resources/ui_menu.js
Normal file
|
@ -0,0 +1,84 @@
|
|||
// Add settings
|
||||
var g_modSettingsAMT = [];
|
||||
|
||||
engine.on('updateModSettingAMT', function (_name, _value) {
|
||||
for (var i = 0; i < g_modSettingsAMT.length; i++) {
|
||||
if (g_modSettingsAMT[i].name == _name) {
|
||||
g_modSettingsAMT[i].updateValue(_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add own menu
|
||||
{
|
||||
let l_block = document.createElement('div');
|
||||
l_block.innerHTML = `
|
||||
<div class ="settings-subcategory">
|
||||
<div class ="subcategory-name">Avatar Motion Tweaker</div>
|
||||
<div class ="subcategory-description"></div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Crouch limit: </div>
|
||||
<div class ="option-input">
|
||||
<div id="CrouchLimit" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="75"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Prone limit: </div>
|
||||
<div class ="option-input">
|
||||
<div id="ProneLimit" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="40"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">IK override while flying: </div>
|
||||
<div class ="option-input">
|
||||
<div id="IKOverrideFly" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">IK override while jumping: </div>
|
||||
<div class ="option-input">
|
||||
<div id="IKOverrideJump" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Follow hips on IK override: </div>
|
||||
<div class ="option-input">
|
||||
<div id="FollowHips" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Detect animations emote tag: </div>
|
||||
<div class ="option-input">
|
||||
<div id="DetectEmotes" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Adjusted locomotion mass center: </div>
|
||||
<div class ="option-input">
|
||||
<div id="MassCenter" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
document.getElementById('settings-ik').appendChild(l_block);
|
||||
|
||||
// Update sliders in new menu block
|
||||
let l_sliders = l_block.querySelectorAll('.inp_slider');
|
||||
for (var i = 0; i < l_sliders.length; i++) {
|
||||
g_modSettingsAMT[g_modSettingsAMT.length] = new inp_slider_mod(l_sliders[i], 'MelonMod_AMT_Call_InpSlider');
|
||||
}
|
||||
|
||||
// Update toggles in new menu block
|
||||
let l_toggles = l_block.querySelectorAll('.inp_toggle');
|
||||
for (var i = 0; i < l_toggles.length; i++) {
|
||||
g_modSettingsAMT[g_modSettingsAMT.length] = new inp_toggle_mod(l_toggles[i], 'MelonMod_AMT_Call_InpToggle');
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
[assembly: MelonLoader.MelonInfo(typeof(ml_lme.LeapMotionExtension), "LeapMotionExtension", "1.4.2", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
|
||||
[assembly: MelonLoader.MelonInfo(typeof(ml_lme.LeapMotionExtension), "LeapMotionExtension", "1.4.3", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
|
||||
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
|
||||
[assembly: MelonLoader.MelonOptionalDependencies("ml_pmc")]
|
||||
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using ABI_RC.Core.InteractionSystem;
|
||||
using cohtml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
@ -122,7 +121,8 @@ namespace ml_lme
|
|||
};
|
||||
ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) =>
|
||||
{
|
||||
ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("menu.js"));
|
||||
ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("ui_elements.js"));
|
||||
ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("ui_menu.js"));
|
||||
foreach(var l_entry in ms_entries)
|
||||
ViewManager.Instance.gameMenuView.View.TriggerEvent("updateModSettingLME", l_entry.DisplayName, l_entry.GetValueAsString());
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<Platforms>x64</Platforms>
|
||||
<PackageId>LeapMotionExtension</PackageId>
|
||||
<Version>1.4.2</Version>
|
||||
<Version>1.4.3</Version>
|
||||
<Authors>SDraw</Authors>
|
||||
<Company>None</Company>
|
||||
<Product>LeapMotionExtension</Product>
|
||||
|
@ -25,13 +25,13 @@
|
|||
<None Remove="LeapMotionExtension.json" />
|
||||
<None Remove="resources\leapmotion_controller.asset" />
|
||||
<None Remove="resources\leapmotion_hands.asset" />
|
||||
<None Remove="resources\menu.js" />
|
||||
<None Remove="resources\ui_menu.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="resources\leapmotion_controller.asset" />
|
||||
<EmbeddedResource Include="resources\leapmotion_hands.asset" />
|
||||
<EmbeddedResource Include="resources\menu.js" />
|
||||
<EmbeddedResource Include="resources\ui_menu.js" />
|
||||
<EmbeddedResource Include="vendor\LeapSDK\lib\x64\LeapC.dll">
|
||||
<Link>resources/LeapC.dll</Link>
|
||||
</EmbeddedResource>
|
||||
|
@ -91,6 +91,10 @@
|
|||
<ItemGroup>
|
||||
<Folder Include="vendor\LeapCSharp\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="..\js\ui_elements.js" Link="resources\ui_elements.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
<Exec Command="copy /y "$(TargetPath)" "D:\Games\Steam\steamapps\common\ChilloutVR\Mods\"" />
|
||||
|
|
|
@ -1,442 +0,0 @@
|
|||
// Add settings
|
||||
var g_modSettingsLME = [];
|
||||
|
||||
engine.on('updateModSettingLME', function (_name, _value) {
|
||||
for (var i = 0; i < g_modSettingsLME.length; i++) {
|
||||
if (g_modSettingsLME[i].name == _name) {
|
||||
g_modSettingsLME[i].updateValue(_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Modified from original `inp` types, because I have no js knowledge to hook stuff
|
||||
function inp_toggle_mod_lme(_obj, _callbackName) {
|
||||
this.obj = _obj;
|
||||
this.callbackName = _callbackName;
|
||||
this.value = _obj.getAttribute('data-current');
|
||||
this.name = _obj.id;
|
||||
this.type = _obj.getAttribute('data-type');
|
||||
|
||||
var self = this;
|
||||
|
||||
this.mouseDown = function (_e) {
|
||||
self.value = self.value == "True" ? "False" : "True";
|
||||
self.updateState();
|
||||
}
|
||||
|
||||
this.updateState = function () {
|
||||
self.obj.classList.remove("checked");
|
||||
if (self.value == "True") {
|
||||
self.obj.classList.add("checked");
|
||||
}
|
||||
|
||||
engine.call(self.callbackName, self.name, self.value);
|
||||
}
|
||||
|
||||
_obj.addEventListener('mousedown', this.mouseDown);
|
||||
|
||||
this.getValue = function () {
|
||||
return self.value;
|
||||
}
|
||||
|
||||
this.updateValue = function (value) {
|
||||
self.value = value;
|
||||
|
||||
self.obj.classList.remove("checked");
|
||||
if (self.value == "True") {
|
||||
self.obj.classList.add("checked");
|
||||
}
|
||||
}
|
||||
|
||||
this.updateValue(this.value);
|
||||
|
||||
return {
|
||||
name: this.name,
|
||||
value: this.getValue,
|
||||
updateValue: this.updateValue
|
||||
}
|
||||
}
|
||||
|
||||
function inp_slider_mod_lme(_obj, _callbackName) {
|
||||
this.obj = _obj;
|
||||
this.callbackName = _callbackName;
|
||||
this.minValue = parseFloat(_obj.getAttribute('data-min'));
|
||||
this.maxValue = parseFloat(_obj.getAttribute('data-max'));
|
||||
this.percent = 0;
|
||||
this.value = parseFloat(_obj.getAttribute('data-current'));
|
||||
this.dragActive = false;
|
||||
this.name = _obj.id;
|
||||
this.type = _obj.getAttribute('data-type');
|
||||
this.stepSize = _obj.getAttribute('data-stepSize') || 0;
|
||||
this.format = _obj.getAttribute('data-format') || '{value}';
|
||||
|
||||
var self = this;
|
||||
|
||||
if (this.stepSize != 0)
|
||||
this.value = Math.round(this.value / this.stepSize) * this.stepSize;
|
||||
else
|
||||
this.value = Math.round(this.value);
|
||||
|
||||
this.valueLabelBackground = document.createElement('div');
|
||||
this.valueLabelBackground.className = 'valueLabel background';
|
||||
this.valueLabelBackground.innerHTML = this.format.replace('{value}', this.value);
|
||||
this.obj.appendChild(this.valueLabelBackground);
|
||||
|
||||
this.valueBar = document.createElement('div');
|
||||
this.valueBar.className = 'valueBar';
|
||||
this.valueBar.setAttribute('style', 'width: ' + (((this.value - this.minValue) / (this.maxValue - this.minValue)) * 100) + '%;');
|
||||
this.obj.appendChild(this.valueBar);
|
||||
|
||||
this.valueLabelForeground = document.createElement('div');
|
||||
this.valueLabelForeground.className = 'valueLabel foreground';
|
||||
this.valueLabelForeground.innerHTML = this.format.replace('{value}', this.value);
|
||||
this.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / ((this.value - this.minValue) / (this.maxValue - this.minValue)) * 100) + '%;');
|
||||
this.valueBar.appendChild(this.valueLabelForeground);
|
||||
|
||||
this.mouseDown = function (_e) {
|
||||
self.dragActive = true;
|
||||
self.mouseMove(_e, false);
|
||||
}
|
||||
|
||||
this.mouseMove = function (_e, _write) {
|
||||
if (self.dragActive) {
|
||||
var rect = _obj.getBoundingClientRect();
|
||||
var start = rect.left;
|
||||
var end = rect.right;
|
||||
self.percent = Math.min(Math.max((_e.clientX - start) / rect.width, 0), 1);
|
||||
var value = self.percent;
|
||||
value *= (self.maxValue - self.minValue);
|
||||
value += self.minValue;
|
||||
if (self.stepSize != 0) {
|
||||
value = Math.round(value / self.stepSize);
|
||||
self.value = value * self.stepSize;
|
||||
self.percent = (self.value - self.minValue) / (self.maxValue - self.minValue);
|
||||
}
|
||||
else
|
||||
self.value = Math.round(value);
|
||||
|
||||
self.valueBar.setAttribute('style', 'width: ' + (self.percent * 100) + '%;');
|
||||
self.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / self.percent * 100) + '%;');
|
||||
self.valueLabelBackground.innerHTML = self.valueLabelForeground.innerHTML = self.format.replace('{value}', self.value);
|
||||
|
||||
engine.call(self.callbackName, self.name, "" + self.value);
|
||||
self.displayImperial();
|
||||
}
|
||||
}
|
||||
|
||||
this.mouseUp = function (_e) {
|
||||
self.mouseMove(_e, true);
|
||||
self.dragActive = false;
|
||||
}
|
||||
|
||||
_obj.addEventListener('mousedown', this.mouseDown);
|
||||
document.addEventListener('mousemove', this.mouseMove);
|
||||
document.addEventListener('mouseup', this.mouseUp);
|
||||
|
||||
this.getValue = function () {
|
||||
return self.value;
|
||||
}
|
||||
|
||||
this.updateValue = function (value) {
|
||||
if (self.stepSize != 0)
|
||||
self.value = Math.round(value * self.stepSize) / self.stepSize;
|
||||
else
|
||||
self.value = Math.round(value);
|
||||
self.percent = (self.value - self.minValue) / (self.maxValue - self.minValue);
|
||||
self.valueBar.setAttribute('style', 'width: ' + (self.percent * 100) + '%;');
|
||||
self.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / self.percent * 100) + '%;');
|
||||
self.valueLabelBackground.innerHTML = self.valueLabelForeground.innerHTML = self.format.replace('{value}', self.value);
|
||||
self.displayImperial();
|
||||
}
|
||||
|
||||
this.displayImperial = function () {
|
||||
var displays = document.querySelectorAll('.imperialDisplay');
|
||||
for (var i = 0; i < displays.length; i++) {
|
||||
var binding = displays[i].getAttribute('data-binding');
|
||||
if (binding == self.name) {
|
||||
var realFeet = ((self.value * 0.393700) / 12);
|
||||
var feet = Math.floor(realFeet);
|
||||
var inches = Math.floor((realFeet - feet) * 12);
|
||||
displays[i].innerHTML = feet + "'" + inches + '''';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
name: this.name,
|
||||
value: this.getValue,
|
||||
updateValue: this.updateValue
|
||||
}
|
||||
}
|
||||
|
||||
function inp_dropdown_mod_lme(_obj, _callbackName) {
|
||||
this.obj = _obj;
|
||||
this.callbackName = _callbackName;
|
||||
this.value = _obj.getAttribute('data-current');
|
||||
this.options = _obj.getAttribute('data-options').split(',');
|
||||
this.name = _obj.id;
|
||||
this.opened = false;
|
||||
this.keyValue = [];
|
||||
this.type = _obj.getAttribute('data-type');
|
||||
|
||||
this.optionElements = [];
|
||||
|
||||
var self = this;
|
||||
|
||||
this.SelectValue = function (_e) {
|
||||
self.value = _e.target.getAttribute('data-key');
|
||||
self.valueElement.innerHTML = _e.target.getAttribute('data-value');
|
||||
self.globalClose();
|
||||
|
||||
engine.call(self.callbackName, self.name, self.value);
|
||||
}
|
||||
|
||||
this.openClick = function (_e) {
|
||||
if (self.obj.classList.contains('open')) {
|
||||
self.obj.classList.remove('open');
|
||||
self.list.setAttribute('style', 'display: none;');
|
||||
} else {
|
||||
self.obj.classList.add('open');
|
||||
self.list.setAttribute('style', 'display: block;');
|
||||
self.opened = true;
|
||||
window.setTimeout(function () { self.opened = false; }, 10);
|
||||
}
|
||||
}
|
||||
|
||||
this.globalClose = function (_e) {
|
||||
if (self.opened) return;
|
||||
self.obj.classList.remove('open');
|
||||
self.list.setAttribute('style', 'display: none;');
|
||||
}
|
||||
|
||||
this.list = document.createElement('div');
|
||||
this.list.className = 'valueList';
|
||||
|
||||
this.updateOptions = function () {
|
||||
self.list.innerHTML = "";
|
||||
for (var i = 0; i < self.options.length; i++) {
|
||||
self.optionElements[i] = document.createElement('div');
|
||||
self.optionElements[i].className = 'listValue';
|
||||
var valuePair = Array.isArray(self.options[i]) ? self.options[i] : self.options[i].split(':');
|
||||
var key = "";
|
||||
var value = "";
|
||||
if (valuePair.length == 1) {
|
||||
key = valuePair[0];
|
||||
value = valuePair[0];
|
||||
} else {
|
||||
key = valuePair[0];
|
||||
value = valuePair[1];
|
||||
}
|
||||
self.keyValue[key] = value;
|
||||
self.optionElements[i].innerHTML = value;
|
||||
self.optionElements[i].setAttribute('data-value', value);
|
||||
self.optionElements[i].setAttribute('data-key', key);
|
||||
self.list.appendChild(self.optionElements[i]);
|
||||
self.optionElements[i].addEventListener('mousedown', self.SelectValue);
|
||||
}
|
||||
|
||||
self.valueElement.innerHTML = self.keyValue[self.value];
|
||||
}
|
||||
|
||||
this.valueElement = document.createElement('div');
|
||||
this.valueElement.className = 'dropdown-value';
|
||||
|
||||
this.updateOptions();
|
||||
|
||||
this.obj.appendChild(this.valueElement);
|
||||
this.obj.appendChild(this.list);
|
||||
this.valueElement.addEventListener('mousedown', this.openClick);
|
||||
document.addEventListener('mousedown', this.globalClose);
|
||||
|
||||
this.getValue = function () {
|
||||
return self.value;
|
||||
}
|
||||
|
||||
this.updateValue = function (value) {
|
||||
self.value = value;
|
||||
self.valueElement.innerHTML = self.keyValue[value];
|
||||
}
|
||||
|
||||
this.setOptions = function (options) {
|
||||
self.options = options;
|
||||
}
|
||||
|
||||
return {
|
||||
name: this.name,
|
||||
value: this.getValue,
|
||||
updateValue: this.updateValue,
|
||||
updateOptions: this.updateOptions,
|
||||
setOptions: this.setOptions
|
||||
}
|
||||
}
|
||||
|
||||
// Add own menu
|
||||
{
|
||||
let l_block = document.createElement('div');
|
||||
l_block.innerHTML = `
|
||||
<div class ="settings-subcategory">
|
||||
<div class ="subcategory-name">Leap Motion tracking</div>
|
||||
<div class ="subcategory-description"></div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Enable tracking: </div>
|
||||
<div class ="option-input">
|
||||
<div id="Enabled" class ="inp_toggle no-scroll" data-current="false"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Tracking mode: </div>
|
||||
<div class ="option-input">
|
||||
<div id="Mode" class ="inp_dropdown no-scroll" data-options="0:Screentop,1:Desktop,2:HMD" data-current="1"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Desktop offset X: </div>
|
||||
<div class ="option-input">
|
||||
<div id="DesktopX" class ="inp_slider no-scroll" data-min="-100" data-max="100" data-current="0"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Desktop offset Y: </div>
|
||||
<div class ="option-input">
|
||||
<div id="DesktopY" class ="inp_slider no-scroll" data-min="-100" data-max="100" data-current="-45"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Desktop offset Z: </div>
|
||||
<div class ="option-input">
|
||||
<div id="DesktopZ" class ="inp_slider no-scroll" data-min="-100" data-max="100" data-current="30"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Attach to head: </div>
|
||||
<div class ="option-input">
|
||||
<div id="Head" class ="inp_toggle no-scroll" data-current="false"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Head offset X: </div>
|
||||
<div class ="option-input">
|
||||
<div id="HeadX" class ="inp_slider no-scroll" data-min="-100" data-max="100" data-current="0"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Head offset Y: </div>
|
||||
<div class ="option-input">
|
||||
<div id="HeadY" class ="inp_slider no-scroll" data-min="-100" data-max="100" data-current="-30"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Head offset Z: </div>
|
||||
<div class ="option-input">
|
||||
<div id="HeadZ" class ="inp_slider no-scroll" data-min="-100" data-max="100" data-current="15"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Offset angle X: </div>
|
||||
<div class ="option-input">
|
||||
<div id="AngleX" class ="inp_slider no-scroll" data-min="-180" data-max="180" data-current="0"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Offset angle Y: </div>
|
||||
<div class ="option-input">
|
||||
<div id="AngleY" class ="inp_slider no-scroll" data-min="-180" data-max="180" data-current="0"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Offset angle Z: </div>
|
||||
<div class ="option-input">
|
||||
<div id="AngleZ" class ="inp_slider no-scroll" data-min="-180" data-max="180" data-current="0"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Track elbows: </div>
|
||||
<div class ="option-input">
|
||||
<div id="TrackElbows" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Fingers tracking only: </div>
|
||||
<div class ="option-input">
|
||||
<div id="FingersOnly" class ="inp_toggle no-scroll" data-current="false"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Model visibility: </div>
|
||||
<div class ="option-input">
|
||||
<div id="Model" class ="inp_toggle no-scroll" data-current="false"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Visualize hands: </div>
|
||||
<div class ="option-input">
|
||||
<div id="VisualHands" class ="inp_toggle no-scroll" data-current="false"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Interaction input: </div>
|
||||
<div class ="option-input">
|
||||
<div id="Interaction" class ="inp_toggle no-scroll" data-current="false"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Recognize gestures: </div>
|
||||
<div class ="option-input">
|
||||
<div id="Gestures" class ="inp_toggle no-scroll" data-current="false"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Interact gesture threadhold: </div>
|
||||
<div class ="option-input">
|
||||
<div id="InteractThreadhold" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="80"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Grip gesture threadhold: </div>
|
||||
<div class ="option-input">
|
||||
<div id="GripThreadhold" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="40"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
document.getElementById('settings-implementation').appendChild(l_block);
|
||||
|
||||
// Update toggles in new menu block
|
||||
let l_toggles = l_block.querySelectorAll('.inp_toggle');
|
||||
for (var i = 0; i < l_toggles.length; i++) {
|
||||
g_modSettingsLME[g_modSettingsLME.length] = new inp_toggle_mod_lme(l_toggles[i], 'MelonMod_LME_Call_InpToggle');
|
||||
}
|
||||
|
||||
// Update sliders in new menu block
|
||||
let l_sliders = l_block.querySelectorAll('.inp_slider');
|
||||
for (var i = 0; i < l_sliders.length; i++) {
|
||||
g_modSettingsLME[g_modSettingsLME.length] = new inp_slider_mod_lme(l_sliders[i], 'MelonMod_LME_Call_InpSlider');
|
||||
}
|
||||
|
||||
//Update dropdowns in new menu block
|
||||
let l_dropdowns = l_block.querySelectorAll('.inp_dropdown');
|
||||
for (var i = 0; i < l_dropdowns.length; i++) {
|
||||
g_modSettingsLME[g_modSettingsLME.length] = new inp_dropdown_mod_lme(l_dropdowns[i], 'MelonMod_LME_Call_InpDropdown');
|
||||
}
|
||||
}
|
181
ml_lme/resources/ui_menu.js
Normal file
181
ml_lme/resources/ui_menu.js
Normal file
|
@ -0,0 +1,181 @@
|
|||
// Add settings
|
||||
var g_modSettingsLME = [];
|
||||
|
||||
engine.on('updateModSettingLME', function (_name, _value) {
|
||||
for (var i = 0; i < g_modSettingsLME.length; i++) {
|
||||
if (g_modSettingsLME[i].name == _name) {
|
||||
g_modSettingsLME[i].updateValue(_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add own menu
|
||||
{
|
||||
let l_block = document.createElement('div');
|
||||
l_block.innerHTML = `
|
||||
<div class ="settings-subcategory">
|
||||
<div class ="subcategory-name">Leap Motion tracking</div>
|
||||
<div class ="subcategory-description"></div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Enable tracking: </div>
|
||||
<div class ="option-input">
|
||||
<div id="Enabled" class ="inp_toggle no-scroll" data-current="false"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Tracking mode: </div>
|
||||
<div class ="option-input">
|
||||
<div id="Mode" class ="inp_dropdown no-scroll" data-options="0:Screentop,1:Desktop,2:HMD" data-current="1"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Desktop offset X: </div>
|
||||
<div class ="option-input">
|
||||
<div id="DesktopX" class ="inp_slider no-scroll" data-min="-100" data-max="100" data-current="0"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Desktop offset Y: </div>
|
||||
<div class ="option-input">
|
||||
<div id="DesktopY" class ="inp_slider no-scroll" data-min="-100" data-max="100" data-current="-45"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Desktop offset Z: </div>
|
||||
<div class ="option-input">
|
||||
<div id="DesktopZ" class ="inp_slider no-scroll" data-min="-100" data-max="100" data-current="30"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Attach to head: </div>
|
||||
<div class ="option-input">
|
||||
<div id="Head" class ="inp_toggle no-scroll" data-current="false"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Head offset X: </div>
|
||||
<div class ="option-input">
|
||||
<div id="HeadX" class ="inp_slider no-scroll" data-min="-100" data-max="100" data-current="0"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Head offset Y: </div>
|
||||
<div class ="option-input">
|
||||
<div id="HeadY" class ="inp_slider no-scroll" data-min="-100" data-max="100" data-current="-30"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Head offset Z: </div>
|
||||
<div class ="option-input">
|
||||
<div id="HeadZ" class ="inp_slider no-scroll" data-min="-100" data-max="100" data-current="15"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Offset angle X: </div>
|
||||
<div class ="option-input">
|
||||
<div id="AngleX" class ="inp_slider no-scroll" data-min="-180" data-max="180" data-current="0"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Offset angle Y: </div>
|
||||
<div class ="option-input">
|
||||
<div id="AngleY" class ="inp_slider no-scroll" data-min="-180" data-max="180" data-current="0"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Offset angle Z: </div>
|
||||
<div class ="option-input">
|
||||
<div id="AngleZ" class ="inp_slider no-scroll" data-min="-180" data-max="180" data-current="0"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Track elbows: </div>
|
||||
<div class ="option-input">
|
||||
<div id="TrackElbows" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Fingers tracking only: </div>
|
||||
<div class ="option-input">
|
||||
<div id="FingersOnly" class ="inp_toggle no-scroll" data-current="false"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Model visibility: </div>
|
||||
<div class ="option-input">
|
||||
<div id="Model" class ="inp_toggle no-scroll" data-current="false"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Visualize hands: </div>
|
||||
<div class ="option-input">
|
||||
<div id="VisualHands" class ="inp_toggle no-scroll" data-current="false"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Interaction input: </div>
|
||||
<div class ="option-input">
|
||||
<div id="Interaction" class ="inp_toggle no-scroll" data-current="false"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Recognize gestures: </div>
|
||||
<div class ="option-input">
|
||||
<div id="Gestures" class ="inp_toggle no-scroll" data-current="false"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Interact gesture threadhold: </div>
|
||||
<div class ="option-input">
|
||||
<div id="InteractThreadhold" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="80"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Grip gesture threadhold: </div>
|
||||
<div class ="option-input">
|
||||
<div id="GripThreadhold" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="40"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
document.getElementById('settings-implementation').appendChild(l_block);
|
||||
|
||||
// Update toggles in new menu block
|
||||
let l_toggles = l_block.querySelectorAll('.inp_toggle');
|
||||
for (var i = 0; i < l_toggles.length; i++) {
|
||||
g_modSettingsLME[g_modSettingsLME.length] = new inp_toggle_mod(l_toggles[i], 'MelonMod_LME_Call_InpToggle');
|
||||
}
|
||||
|
||||
// Update sliders in new menu block
|
||||
let l_sliders = l_block.querySelectorAll('.inp_slider');
|
||||
for (var i = 0; i < l_sliders.length; i++) {
|
||||
g_modSettingsLME[g_modSettingsLME.length] = new inp_slider_mod(l_sliders[i], 'MelonMod_LME_Call_InpSlider');
|
||||
}
|
||||
|
||||
//Update dropdowns in new menu block
|
||||
let l_dropdowns = l_block.querySelectorAll('.inp_dropdown');
|
||||
for (var i = 0; i < l_dropdowns.length; i++) {
|
||||
g_modSettingsLME[g_modSettingsLME.length] = new inp_dropdown_mod(l_dropdowns[i], 'MelonMod_LME_Call_InpDropdown');
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
[assembly: MelonLoader.MelonInfo(typeof(ml_pam.PickupArmMovement), "PickupArmMovement", "1.0.7", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
|
||||
[assembly: MelonLoader.MelonInfo(typeof(ml_pam.PickupArmMovement), "PickupArmMovement", "1.0.8", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
|
||||
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
|
||||
[assembly: MelonLoader.MelonPriority(1)]
|
||||
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
|
||||
|
|
|
@ -7,6 +7,6 @@ This mod adds arm tracking upon holding pickup in desktop mode.
|
|||
* Put `ml_pam.dll` in `Mods` folder of game
|
||||
|
||||
# Usage
|
||||
Available mod's settings in `Settings - Interactions`:
|
||||
Available mod's settings in `Settings - Interactions - Pickup Arm Movement`:
|
||||
* **Enable hand movement:** enables/disables arm tracking; default value - `true`.
|
||||
* **Grab offset:** offset from pickup grab point; defalut value - `25`.
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using ABI_RC.Core.InteractionSystem;
|
||||
using cohtml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
@ -53,7 +52,8 @@ namespace ml_pam
|
|||
};
|
||||
ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) =>
|
||||
{
|
||||
ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("menu.js"));
|
||||
ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("ui_elements.js"));
|
||||
ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("ui_menu.js"));
|
||||
foreach(var l_entry in ms_entries)
|
||||
ViewManager.Instance.gameMenuView.View.TriggerEvent("updateModSettingPAM", l_entry.DisplayName, l_entry.GetValueAsString());
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<Platforms>x64</Platforms>
|
||||
<PackageId>PickupArmMovement</PackageId>
|
||||
<Version>1.0.7</Version>
|
||||
<Version>1.0.8</Version>
|
||||
<Authors>SDraw</Authors>
|
||||
<Company>None</Company>
|
||||
<Product>PickupArmMovement</Product>
|
||||
|
@ -17,11 +17,15 @@
|
|||
|
||||
<ItemGroup>
|
||||
<None Remove="PickupArmMovement.json" />
|
||||
<None Remove="resources\menu.js" />
|
||||
<None Remove="resources\ui_menu.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="resources\menu.js" />
|
||||
<EmbeddedResource Include="resources\ui_menu.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="..\js\ui_elements.js" Link="resources\ui_elements.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1,209 +0,0 @@
|
|||
// Add settings
|
||||
var g_modSettingsPAM = [];
|
||||
|
||||
engine.on('updateModSettingPAM', function (_name, _value) {
|
||||
for (var i = 0; i < g_modSettingsPAM.length; i++) {
|
||||
if (g_modSettingsPAM[i].name == _name) {
|
||||
g_modSettingsPAM[i].updateValue(_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Modified from original `inp` types, because I have no js knowledge to hook stuff
|
||||
function inp_toggle_mod_pam(_obj, _callbackName) {
|
||||
this.obj = _obj;
|
||||
this.callbackName = _callbackName;
|
||||
this.value = _obj.getAttribute('data-current');
|
||||
this.name = _obj.id;
|
||||
this.type = _obj.getAttribute('data-type');
|
||||
|
||||
var self = this;
|
||||
|
||||
this.mouseDown = function (_e) {
|
||||
self.value = self.value == "True" ? "False" : "True";
|
||||
self.updateState();
|
||||
}
|
||||
|
||||
this.updateState = function () {
|
||||
self.obj.classList.remove("checked");
|
||||
if (self.value == "True") {
|
||||
self.obj.classList.add("checked");
|
||||
}
|
||||
|
||||
engine.call(self.callbackName, self.name, self.value);
|
||||
}
|
||||
|
||||
_obj.addEventListener('mousedown', this.mouseDown);
|
||||
|
||||
this.getValue = function () {
|
||||
return self.value;
|
||||
}
|
||||
|
||||
this.updateValue = function (value) {
|
||||
self.value = value;
|
||||
|
||||
self.obj.classList.remove("checked");
|
||||
if (self.value == "True") {
|
||||
self.obj.classList.add("checked");
|
||||
}
|
||||
}
|
||||
|
||||
this.updateValue(this.value);
|
||||
|
||||
return {
|
||||
name: this.name,
|
||||
value: this.getValue,
|
||||
updateValue: this.updateValue
|
||||
}
|
||||
}
|
||||
|
||||
function inp_slider_mod_pam(_obj, _callbackName) {
|
||||
this.obj = _obj;
|
||||
this.callbackName = _callbackName;
|
||||
this.minValue = parseFloat(_obj.getAttribute('data-min'));
|
||||
this.maxValue = parseFloat(_obj.getAttribute('data-max'));
|
||||
this.percent = 0;
|
||||
this.value = parseFloat(_obj.getAttribute('data-current'));
|
||||
this.dragActive = false;
|
||||
this.name = _obj.id;
|
||||
this.type = _obj.getAttribute('data-type');
|
||||
this.stepSize = _obj.getAttribute('data-stepSize') || 0;
|
||||
this.format = _obj.getAttribute('data-format') || '{value}';
|
||||
|
||||
var self = this;
|
||||
|
||||
if (this.stepSize != 0)
|
||||
this.value = Math.round(this.value / this.stepSize) * this.stepSize;
|
||||
else
|
||||
this.value = Math.round(this.value);
|
||||
|
||||
this.valueLabelBackground = document.createElement('div');
|
||||
this.valueLabelBackground.className = 'valueLabel background';
|
||||
this.valueLabelBackground.innerHTML = this.format.replace('{value}', this.value);
|
||||
this.obj.appendChild(this.valueLabelBackground);
|
||||
|
||||
this.valueBar = document.createElement('div');
|
||||
this.valueBar.className = 'valueBar';
|
||||
this.valueBar.setAttribute('style', 'width: ' + (((this.value - this.minValue) / (this.maxValue - this.minValue)) * 100) + '%;');
|
||||
this.obj.appendChild(this.valueBar);
|
||||
|
||||
this.valueLabelForeground = document.createElement('div');
|
||||
this.valueLabelForeground.className = 'valueLabel foreground';
|
||||
this.valueLabelForeground.innerHTML = this.format.replace('{value}', this.value);
|
||||
this.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / ((this.value - this.minValue) / (this.maxValue - this.minValue)) * 100) + '%;');
|
||||
this.valueBar.appendChild(this.valueLabelForeground);
|
||||
|
||||
this.mouseDown = function (_e) {
|
||||
self.dragActive = true;
|
||||
self.mouseMove(_e, false);
|
||||
}
|
||||
|
||||
this.mouseMove = function (_e, _write) {
|
||||
if (self.dragActive) {
|
||||
var rect = _obj.getBoundingClientRect();
|
||||
var start = rect.left;
|
||||
var end = rect.right;
|
||||
self.percent = Math.min(Math.max((_e.clientX - start) / rect.width, 0), 1);
|
||||
var value = self.percent;
|
||||
value *= (self.maxValue - self.minValue);
|
||||
value += self.minValue;
|
||||
if (self.stepSize != 0) {
|
||||
value = Math.round(value / self.stepSize);
|
||||
self.value = value * self.stepSize;
|
||||
self.percent = (self.value - self.minValue) / (self.maxValue - self.minValue);
|
||||
}
|
||||
else
|
||||
self.value = Math.round(value);
|
||||
|
||||
self.valueBar.setAttribute('style', 'width: ' + (self.percent * 100) + '%;');
|
||||
self.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / self.percent * 100) + '%;');
|
||||
self.valueLabelBackground.innerHTML = self.valueLabelForeground.innerHTML = self.format.replace('{value}', self.value);
|
||||
|
||||
engine.call(self.callbackName, self.name, "" + self.value);
|
||||
self.displayImperial();
|
||||
}
|
||||
}
|
||||
|
||||
this.mouseUp = function (_e) {
|
||||
self.mouseMove(_e, true);
|
||||
self.dragActive = false;
|
||||
}
|
||||
|
||||
_obj.addEventListener('mousedown', this.mouseDown);
|
||||
document.addEventListener('mousemove', this.mouseMove);
|
||||
document.addEventListener('mouseup', this.mouseUp);
|
||||
|
||||
this.getValue = function () {
|
||||
return self.value;
|
||||
}
|
||||
|
||||
this.updateValue = function (value) {
|
||||
if (self.stepSize != 0)
|
||||
self.value = Math.round(value * self.stepSize) / self.stepSize;
|
||||
else
|
||||
self.value = Math.round(value);
|
||||
self.percent = (self.value - self.minValue) / (self.maxValue - self.minValue);
|
||||
self.valueBar.setAttribute('style', 'width: ' + (self.percent * 100) + '%;');
|
||||
self.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / self.percent * 100) + '%;');
|
||||
self.valueLabelBackground.innerHTML = self.valueLabelForeground.innerHTML = self.format.replace('{value}', self.value);
|
||||
self.displayImperial();
|
||||
}
|
||||
|
||||
this.displayImperial = function () {
|
||||
var displays = document.querySelectorAll('.imperialDisplay');
|
||||
for (var i = 0; i < displays.length; i++) {
|
||||
var binding = displays[i].getAttribute('data-binding');
|
||||
if (binding == self.name) {
|
||||
var realFeet = ((self.value * 0.393700) / 12);
|
||||
var feet = Math.floor(realFeet);
|
||||
var inches = Math.floor((realFeet - feet) * 12);
|
||||
displays[i].innerHTML = feet + "'" + inches + '''';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
name: this.name,
|
||||
value: this.getValue,
|
||||
updateValue: this.updateValue
|
||||
}
|
||||
}
|
||||
|
||||
// Add own menu
|
||||
{
|
||||
let l_block = document.createElement('div');
|
||||
l_block.innerHTML = `
|
||||
<div class ="settings-subcategory">
|
||||
<div class ="subcategory-name">Pickup Arm Mover</div>
|
||||
<div class ="subcategory-description"></div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Enable hand movement: </div>
|
||||
<div class ="option-input">
|
||||
<div id="Enabled" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Grab offset: </div>
|
||||
<div class ="option-input">
|
||||
<div id="GrabOffset" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="25"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
document.getElementById('settings-interaction').appendChild(l_block);
|
||||
|
||||
// Update toggles in new menu block
|
||||
let l_toggles = l_block.querySelectorAll('.inp_toggle');
|
||||
for (var i = 0; i < l_toggles.length; i++) {
|
||||
g_modSettingsPAM[g_modSettingsPAM.length] = new inp_toggle_mod_pam(l_toggles[i], 'MelonMod_PAM_Call_InpToggle');
|
||||
}
|
||||
|
||||
// Update sliders in new menu block
|
||||
let l_sliders = l_block.querySelectorAll('.inp_slider');
|
||||
for (var i = 0; i < l_sliders.length; i++) {
|
||||
g_modSettingsPAM[g_modSettingsPAM.length] = new inp_slider_mod_pam(l_sliders[i], 'MelonMod_PAM_Call_InpSlider');
|
||||
}
|
||||
}
|
49
ml_pam/resources/ui_menu.js
Normal file
49
ml_pam/resources/ui_menu.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Add settings
|
||||
var g_modSettingsPAM = [];
|
||||
|
||||
engine.on('updateModSettingPAM', function (_name, _value) {
|
||||
for (var i = 0; i < g_modSettingsPAM.length; i++) {
|
||||
if (g_modSettingsPAM[i].name == _name) {
|
||||
g_modSettingsPAM[i].updateValue(_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add own menu
|
||||
{
|
||||
let l_block = document.createElement('div');
|
||||
l_block.innerHTML = `
|
||||
<div class ="settings-subcategory">
|
||||
<div class ="subcategory-name">Pickup Arm Movement</div>
|
||||
<div class ="subcategory-description"></div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Enable hand movement: </div>
|
||||
<div class ="option-input">
|
||||
<div id="Enabled" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Grab offset: </div>
|
||||
<div class ="option-input">
|
||||
<div id="GrabOffset" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="25"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
document.getElementById('settings-interaction').appendChild(l_block);
|
||||
|
||||
// Update toggles in new menu block
|
||||
let l_toggles = l_block.querySelectorAll('.inp_toggle');
|
||||
for (var i = 0; i < l_toggles.length; i++) {
|
||||
g_modSettingsPAM[g_modSettingsPAM.length] = new inp_toggle_mod(l_toggles[i], 'MelonMod_PAM_Call_InpToggle');
|
||||
}
|
||||
|
||||
// Update sliders in new menu block
|
||||
let l_sliders = l_block.querySelectorAll('.inp_slider');
|
||||
for (var i = 0; i < l_sliders.length; i++) {
|
||||
g_modSettingsPAM[g_modSettingsPAM.length] = new inp_slider_mod(l_sliders[i], 'MelonMod_PAM_Call_InpSlider');
|
||||
}
|
||||
}
|
49
ml_vei/Main.cs
Normal file
49
ml_vei/Main.cs
Normal file
|
@ -0,0 +1,49 @@
|
|||
using ABI_RC.Systems.InputManagement;
|
||||
using ABI_RC.Systems.InputManagement.XR;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ml_vei
|
||||
{
|
||||
public class ViveExtendedInput : MelonLoader.MelonMod
|
||||
{
|
||||
public override void OnInitializeMelon()
|
||||
{
|
||||
Settings.Init();
|
||||
|
||||
HarmonyInstance.Patch(
|
||||
typeof(CVRXRModule).GetMethod("Update_Gestures_Vive", BindingFlags.NonPublic | BindingFlags.Instance),
|
||||
null,
|
||||
new HarmonyLib.HarmonyMethod(typeof(ViveExtendedInput).GetMethod(nameof(OnViveGesturesUpdate_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
|
||||
);
|
||||
}
|
||||
|
||||
static void OnViveGesturesUpdate_Postfix(ref CVRXRModule __instance)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(Settings.Gestures)
|
||||
{
|
||||
float l_mag = ((!__instance.HasEmoteOverride) ? __instance.Primary2DAxis : __instance.EmoteOverride).magnitude;
|
||||
if(__instance.ViveDirectionPressed && (l_mag >= CVRInputManager.VrViveGestureDeadZone))
|
||||
{
|
||||
if(__instance.Grip > 0.5f)
|
||||
{
|
||||
__instance.GestureRaw = -1f;
|
||||
__instance.Gesture = -1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
__instance.GestureRaw = __instance.Trigger;
|
||||
__instance.Gesture = __instance.Trigger;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
4
ml_vei/Properties/AssemblyInfo.cs
Normal file
4
ml_vei/Properties/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,4 @@
|
|||
[assembly: MelonLoader.MelonInfo(typeof(ml_vei.ViveExtendedInput), "ViveExtendedInput", "1.0.0", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
|
||||
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
|
||||
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
|
||||
[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
|
11
ml_vei/README.md
Normal file
11
ml_vei/README.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
# Vive Extended Input
|
||||
This mod changes input behaviour for Vive controllers.
|
||||
|
||||
# Installation
|
||||
* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader)
|
||||
* Get [latest release DLL](../../../releases/latest):
|
||||
* Put `ml_vei.dll` in `Mods` folder of game
|
||||
|
||||
# Usage
|
||||
Available mod's settings in `Settings - Input & Key-Bindings - Vive Extended Input`:
|
||||
* **Disable gestures while moving:** disables gestures while moving and sets gesture according to grip and trigger; grip has higher priority over trigger.
|
26
ml_vei/Scripts.cs
Normal file
26
ml_vei/Scripts.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ml_vei
|
||||
{
|
||||
static class Scripts
|
||||
{
|
||||
public static string GetEmbeddedScript(string p_name)
|
||||
{
|
||||
string l_result = "";
|
||||
Assembly l_assembly = Assembly.GetExecutingAssembly();
|
||||
string l_assemblyName = l_assembly.GetName().Name;
|
||||
|
||||
try
|
||||
{
|
||||
Stream l_libraryStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + p_name);
|
||||
StreamReader l_streadReader = new StreamReader(l_libraryStream);
|
||||
l_result = l_streadReader.ReadToEnd();
|
||||
}
|
||||
catch(Exception) { }
|
||||
|
||||
return l_result;
|
||||
}
|
||||
}
|
||||
}
|
80
ml_vei/Settings.cs
Normal file
80
ml_vei/Settings.cs
Normal file
|
@ -0,0 +1,80 @@
|
|||
using ABI_RC.Core.InteractionSystem;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ml_vei
|
||||
{
|
||||
static class Settings
|
||||
{
|
||||
public enum ModSetting
|
||||
{
|
||||
Gestures = 0
|
||||
}
|
||||
|
||||
public static bool Gestures { get; private set; } = true;
|
||||
|
||||
static MelonLoader.MelonPreferences_Category ms_category = null;
|
||||
static List<MelonLoader.MelonPreferences_Entry> ms_entries = null;
|
||||
|
||||
static public event Action<bool> GesturesChange;
|
||||
|
||||
internal static void Init()
|
||||
{
|
||||
ms_category = MelonLoader.MelonPreferences.CreateCategory("VEI", null, true);
|
||||
|
||||
ms_entries = new List<MelonLoader.MelonPreferences_Entry>()
|
||||
{
|
||||
ms_category.CreateEntry(ModSetting.Gestures.ToString(), Gestures),
|
||||
};
|
||||
|
||||
Load();
|
||||
|
||||
MelonLoader.MelonCoroutines.Start(WaitMainMenuUi());
|
||||
}
|
||||
|
||||
static System.Collections.IEnumerator WaitMainMenuUi()
|
||||
{
|
||||
while(ViewManager.Instance == null)
|
||||
yield return null;
|
||||
while(ViewManager.Instance.gameMenuView == null)
|
||||
yield return null;
|
||||
while(ViewManager.Instance.gameMenuView.Listener == null)
|
||||
yield return null;
|
||||
|
||||
ViewManager.Instance.gameMenuView.Listener.ReadyForBindings += () =>
|
||||
{
|
||||
ViewManager.Instance.gameMenuView.View.BindCall("MelonMod_VEI_Call_InpToggle", new Action<string, string>(OnToggleUpdate));
|
||||
};
|
||||
ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) =>
|
||||
{
|
||||
ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("ui_elements.js"));
|
||||
ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("ui_menu.js"));
|
||||
foreach(var l_entry in ms_entries)
|
||||
ViewManager.Instance.gameMenuView.View.TriggerEvent("updateModSettingVEI", l_entry.DisplayName, l_entry.GetValueAsString());
|
||||
};
|
||||
}
|
||||
|
||||
static void Load()
|
||||
{
|
||||
Gestures = (bool)ms_entries[(int)ModSetting.Gestures].BoxedValue;
|
||||
}
|
||||
|
||||
static void OnToggleUpdate(string p_name, string p_value)
|
||||
{
|
||||
if(Enum.TryParse(p_name, out ModSetting l_setting))
|
||||
{
|
||||
switch(l_setting)
|
||||
{
|
||||
case ModSetting.Gestures:
|
||||
{
|
||||
Gestures = bool.Parse(p_value);
|
||||
GesturesChange?.Invoke(Gestures);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ms_entries[(int)l_setting].BoxedValue = bool.Parse(p_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
12
ml_vei/Utils.cs
Normal file
12
ml_vei/Utils.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using ABI_RC.Core.UI;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ml_vei
|
||||
{
|
||||
static class Utils
|
||||
{
|
||||
static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
static public void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script);
|
||||
}
|
||||
}
|
64
ml_vei/ml_vei.csproj
Normal file
64
ml_vei/ml_vei.csproj
Normal file
|
@ -0,0 +1,64 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<Platforms>x64</Platforms>
|
||||
<PackageId>ViveExtendedInput</PackageId>
|
||||
<Version>1.0.0</Version>
|
||||
<Authors>SDraw</Authors>
|
||||
<Company>None</Company>
|
||||
<Product>ViveExtendedInput</Product>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="PlayerRagdollMod.json" />
|
||||
<None Remove="resources\ui_menu.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="0Harmony">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="Assembly-CSharp">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="cohtml.Net">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Cohtml.Runtime">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="MelonLoader">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Postprocessing.Runtime">
|
||||
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Unity.Postprocessing.Runtime.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.AnimationModule">
|
||||
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AnimationModule.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.CoreModule">
|
||||
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="..\js\ui_elements.js" Link="resources\ui_elements.js" />
|
||||
<EmbeddedResource Include="resources\ui_menu.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
<Exec Command="copy /y "$(TargetPath)" "D:\Games\Steam\steamapps\common\ChilloutVR\Mods\"" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
36
ml_vei/resources/ui_menu.js
Normal file
36
ml_vei/resources/ui_menu.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Add settings
|
||||
var g_modSettingsVEI = [];
|
||||
|
||||
engine.on('updateModSettingVEI', function (_name, _value) {
|
||||
for (var i = 0; i < g_modSettingsVEI.length; i++) {
|
||||
if (g_modSettingsVEI[i].name == _name) {
|
||||
g_modSettingsVEI[i].updateValue(_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add own menu
|
||||
{
|
||||
let l_block = document.createElement('div');
|
||||
l_block.innerHTML = `
|
||||
<div class ="settings-subcategory">
|
||||
<div class ="subcategory-name">Vive Extended Input</div>
|
||||
<div class ="subcategory-description"></div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Disable gestures while moving: </div>
|
||||
<div class ="option-input">
|
||||
<div id="Gestures" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
document.getElementById('settings-input').appendChild(l_block);
|
||||
|
||||
// Update toggles in new menu block
|
||||
let l_toggles = l_block.querySelectorAll('.inp_toggle');
|
||||
for (var i = 0; i < l_toggles.length; i++) {
|
||||
g_modSettingsVEI[g_modSettingsVEI.length] = new inp_toggle_mod(l_toggles[i], 'MelonMod_VEI_Call_InpToggle');
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue