Version 4.7 - Radial Menu
Add a new button-"Radial Menu (Z)" instead of "Recall (B)"
This commit is contained in:
@@ -51,6 +51,7 @@ namespace DevourClient
|
||||
public static float exp = 1000f;
|
||||
public static bool _walkInLobby = false;
|
||||
public static bool infinite_mirrors = false;
|
||||
static bool radialMenuEnabled = true;
|
||||
static bool player_esp = false;
|
||||
static bool player_skel_esp = false;
|
||||
static bool player_snapline = false;
|
||||
@@ -188,11 +189,9 @@ namespace DevourClient
|
||||
fly = !fly;
|
||||
}
|
||||
|
||||
// Recall to base position (B key, only if enabled)
|
||||
if (recallEnabled && Input.GetKeyDown(KeyCode.B) && Player.IsInGameOrLobby())
|
||||
{
|
||||
Helpers.RecallHelper.RecallToBase();
|
||||
}
|
||||
// Z-key radial menu logic is managed by RadialMenuManager
|
||||
RadialMenuManager.Enabled = radialMenuEnabled;
|
||||
RadialMenuManager.HandleUpdate();
|
||||
|
||||
if (Player.IsInGameOrLobby())
|
||||
{
|
||||
@@ -607,12 +606,15 @@ namespace DevourClient
|
||||
}
|
||||
}
|
||||
|
||||
if (crosshair && in_game_cache)
|
||||
{
|
||||
const float crosshairSize = 4;
|
||||
// End of EventType.Repaint branch
|
||||
}
|
||||
|
||||
float xMin = (Settings.Settings.width) - (crosshairSize / 2);
|
||||
float yMin = (Settings.Settings.height) - (crosshairSize / 2);
|
||||
if (crosshair && in_game_cache)
|
||||
{
|
||||
const float crosshairSize = 4f;
|
||||
|
||||
float xMin = Settings.Settings.width - (crosshairSize / 2f);
|
||||
float yMin = Settings.Settings.height - (crosshairSize / 2f);
|
||||
|
||||
if (crosshairTexture == null)
|
||||
{
|
||||
@@ -622,7 +624,8 @@ namespace DevourClient
|
||||
GUI.DrawTexture(new Rect(xMin, yMin, crosshairSize, crosshairSize), crosshairTexture);
|
||||
}
|
||||
|
||||
}
|
||||
// Radial menu rendering
|
||||
RadialMenuManager.HandleOnGUI();
|
||||
|
||||
if (Settings.Settings.menu_enable)
|
||||
{
|
||||
@@ -2027,15 +2030,15 @@ namespace DevourClient
|
||||
|
||||
_walkInLobby = GUI.Toggle(new Rect(Settings.Settings.x + 10, Settings.Settings.y + 155, 140, 20), _walkInLobby, MultiLanguageSystem.Translate("Walk In Lobby"));
|
||||
_IsAutoRespawn = GUI.Toggle(new Rect(Settings.Settings.x + 160, Settings.Settings.y + 155, 140, 20), _IsAutoRespawn, MultiLanguageSystem.Translate("Auto Respawn"));
|
||||
|
||||
fly = GUI.Toggle(new Rect(Settings.Settings.x + 10, Settings.Settings.y + 190, 100, 20), fly, MultiLanguageSystem.Translate("Fly"));
|
||||
if (GUI.Button(new Rect(Settings.Settings.x + 120, Settings.Settings.y + 190, 60, 20), Settings.Settings.flyKey.ToString()))
|
||||
|
||||
fly = GUI.Toggle(new Rect(Settings.Settings.x + 10, Settings.Settings.y + 180, 100, 20), fly, MultiLanguageSystem.Translate("Fly"));
|
||||
if (GUI.Button(new Rect(Settings.Settings.x + 120, Settings.Settings.y + 180, 60, 20), Settings.Settings.flyKey.ToString()))
|
||||
{
|
||||
Settings.Settings.flyKey = Settings.Settings.GetKey();
|
||||
}
|
||||
GUI.Label(new Rect(Settings.Settings.x + 20, Settings.Settings.y + 215, 80, 20), MultiLanguageSystem.Translate("Fly Speed") + ":");
|
||||
fly_speed = GUI.HorizontalSlider(new Rect(Settings.Settings.x + 100, Settings.Settings.y + 220, 150, 10), fly_speed, 5f, 20f);
|
||||
GUI.Label(new Rect(Settings.Settings.x + 260, Settings.Settings.y + 215, 50, 20), ((int)fly_speed).ToString());
|
||||
GUI.Label(new Rect(Settings.Settings.x + 20, Settings.Settings.y + 205, 80, 20), MultiLanguageSystem.Translate("Fly Speed") + ":");
|
||||
fly_speed = GUI.HorizontalSlider(new Rect(Settings.Settings.x + 100, Settings.Settings.y + 210, 150, 10), fly_speed, 5f, 20f);
|
||||
GUI.Label(new Rect(Settings.Settings.x + 260, Settings.Settings.y + 205, 50, 20), ((int)fly_speed).ToString());
|
||||
|
||||
spoofLevel = GUI.Toggle(new Rect(Settings.Settings.x + 10, Settings.Settings.y + 250, 200, 20), spoofLevel, MultiLanguageSystem.Translate("Spoof Level"));
|
||||
GUI.Label(new Rect(Settings.Settings.x + 20, Settings.Settings.y + 275, 80, 20), MultiLanguageSystem.Translate("Level") + ":");
|
||||
@@ -2053,8 +2056,8 @@ namespace DevourClient
|
||||
GUI.Label(new Rect(Settings.Settings.x + 260, Settings.Settings.y + 395, 50, 20), ((int)_PlayerSpeedMultiplier).ToString());
|
||||
|
||||
showCoordinates = GUI.Toggle(new Rect(Settings.Settings.x + 10, Settings.Settings.y + 430, 200, 20), showCoordinates, MultiLanguageSystem.Translate("Show Coordinates"));
|
||||
|
||||
recallEnabled = GUI.Toggle(new Rect(Settings.Settings.x + 10, Settings.Settings.y + 455, 200, 20), recallEnabled, MultiLanguageSystem.Translate("Recall (B)"));
|
||||
|
||||
radialMenuEnabled = GUI.Toggle(new Rect(Settings.Settings.x + 10, Settings.Settings.y + 455, 200, 20), radialMenuEnabled, MultiLanguageSystem.Translate("Radial Menu (Z)"));
|
||||
|
||||
// Display player coordinates at the bottom of Misc tab
|
||||
if (showCoordinates && Player.IsInGameOrLobby())
|
||||
|
||||
@@ -2,9 +2,7 @@ using System.Collections.Generic;
|
||||
|
||||
namespace DevourClient.ESP
|
||||
{
|
||||
/// <summary>
|
||||
/// Item ESP configuration management class - dynamically display different item types based on map
|
||||
/// </summary>
|
||||
// Item ESP configuration management class - dynamically display different item types based on map
|
||||
public static class ItemESPConfig
|
||||
{
|
||||
// ESP type enumeration
|
||||
@@ -260,9 +258,6 @@ namespace DevourClient.ESP
|
||||
{ ESPType.Collectables, false }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Get list of ESP types supported by specified map
|
||||
/// </summary>
|
||||
public static List<ESPType> GetMapESPTypes(string sceneName)
|
||||
{
|
||||
// If in menu, return empty list
|
||||
@@ -284,35 +279,23 @@ namespace DevourClient.ESP
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get ESP type enable status
|
||||
/// </summary>
|
||||
public static bool GetESPState(ESPType type)
|
||||
{
|
||||
return espStates.ContainsKey(type) ? espStates[type] : false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set ESP type enable status
|
||||
/// </summary>
|
||||
public static void SetESPState(ESPType type, bool enabled)
|
||||
{
|
||||
if (espStates.ContainsKey(type))
|
||||
espStates[type] = enabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggle ESP type enable status
|
||||
/// </summary>
|
||||
public static void ToggleESPState(ESPType type)
|
||||
{
|
||||
if (espStates.ContainsKey(type))
|
||||
espStates[type] = !espStates[type];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get ESP type display name (for translation key)
|
||||
/// </summary>
|
||||
public static string GetESPTypeName(ESPType type)
|
||||
{
|
||||
switch (type)
|
||||
@@ -420,9 +403,6 @@ namespace DevourClient.ESP
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get corresponding ESP type based on item name
|
||||
/// </summary>
|
||||
public static ESPType? GetESPTypeByItemName(string itemName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(itemName))
|
||||
@@ -617,9 +597,6 @@ namespace DevourClient.ESP
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if ESP should be shown for specified item
|
||||
/// </summary>
|
||||
public static bool ShouldShowESP(string itemName)
|
||||
{
|
||||
ESPType? espType = GetESPTypeByItemName(itemName);
|
||||
@@ -629,9 +606,6 @@ namespace DevourClient.ESP
|
||||
return GetESPState(espType.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset all ESP states
|
||||
/// </summary>
|
||||
public static void ResetAllStates()
|
||||
{
|
||||
var keys = new List<ESPType>(espStates.Keys);
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
using UnityEngine;
|
||||
using MelonLoader;
|
||||
|
||||
namespace DevourClient.Helpers
|
||||
{
|
||||
public static class RecallHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Teleports the local player to the base coordinates of the current map
|
||||
/// </summary>
|
||||
public static void RecallToBase()
|
||||
{
|
||||
try
|
||||
{
|
||||
Il2Cpp.NolanBehaviour nb = Player.GetPlayer();
|
||||
if (nb == null)
|
||||
{
|
||||
MelonLogger.Warning("Player not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
string sceneName = Map.GetActiveScene();
|
||||
Vector3 targetPos = Vector3.zero;
|
||||
string mapName = "";
|
||||
|
||||
// Map coordinates based on scene name
|
||||
switch (sceneName)
|
||||
{
|
||||
case "Devour":
|
||||
case "Anna": // Farmhouse
|
||||
targetPos = new Vector3(5.03f, 4.20f, -50.02f);
|
||||
mapName = "Farm";
|
||||
break;
|
||||
case "Molly": // Asylum
|
||||
targetPos = new Vector3(17.52f, 1.38f, 7.04f);
|
||||
mapName = "Asylum";
|
||||
break;
|
||||
case "Inn":
|
||||
targetPos = new Vector3(3.53f, 0.84f, 2.47f);
|
||||
mapName = "Inn";
|
||||
break;
|
||||
case "Town":
|
||||
targetPos = new Vector3(-63.51f, 10.88f, -12.32f);
|
||||
mapName = "Town";
|
||||
break;
|
||||
case "Slaughterhouse":
|
||||
targetPos = new Vector3(6.09f, 0.70f, -17.58f);
|
||||
mapName = "Slaughterhouse";
|
||||
break;
|
||||
case "Manor":
|
||||
targetPos = new Vector3(3.67f, 1.32f, -23.34f);
|
||||
mapName = "Manor";
|
||||
break;
|
||||
case "Carnival":
|
||||
targetPos = new Vector3(-91.46f, 8.13f, -24.51f);
|
||||
mapName = "Carnival";
|
||||
break;
|
||||
default:
|
||||
MelonLogger.Warning($"Teleport not available for scene: {sceneName}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Teleport player to target position
|
||||
nb.locomotion.SetPosition(targetPos, false);
|
||||
MelonLogger.Msg($"Teleported to {mapName} coordinates: X:{targetPos.x:F2} Y:{targetPos.y:F2} Z:{targetPos.z:F2}");
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
MelonLogger.Error($"Failed to teleport: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,17 +5,13 @@ using UnityEngine;
|
||||
|
||||
namespace DevourClient.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Shared revive utilities. When running as host we mirror DevourX's revive flow.
|
||||
/// </summary>
|
||||
// Shared revive utilities. When running as host we mirror DevourX's revive flow.
|
||||
public static class ReviveHelper
|
||||
{
|
||||
private static readonly Vector3 HostFallbackPosition = new Vector3(0f, -150f, 0f);
|
||||
|
||||
/// <summary>
|
||||
/// Try to revive the provided NolanBehaviour using host-specific logic first,
|
||||
/// then fall back to the standard interactable flow.
|
||||
/// </summary>
|
||||
// Try to revive the provided NolanBehaviour using host-specific logic first,
|
||||
// then fall back to the standard interactable flow.
|
||||
public static bool TryRevive(Il2Cpp.NolanBehaviour target)
|
||||
{
|
||||
if (target == null || target.gameObject == null)
|
||||
@@ -88,11 +84,9 @@ namespace DevourClient.Helpers
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Legacy revive method migrated from StateHelper.BasePlayer.Revive().
|
||||
/// This method preserves the original implementation from StateHelper.
|
||||
/// </summary>
|
||||
/// <param name="targetGameObject">The GameObject of the player to revive</param>
|
||||
// Legacy revive method migrated from StateHelper.BasePlayer.Revive().
|
||||
// This method preserves the original implementation from StateHelper.
|
||||
// targetGameObject: The GameObject of the player to revive.
|
||||
public static void ReviveLegacy(GameObject targetGameObject)
|
||||
{
|
||||
if (targetGameObject == null)
|
||||
|
||||
@@ -207,6 +207,7 @@ namespace DevourClient.Localization.Translations
|
||||
{ "Rat ESP", "老鼠透视" },
|
||||
{ "Region", "区域" },
|
||||
{ "Revive", "复活" },
|
||||
{ "Radial Menu (Z)", "轮盘菜单 (Z)" },
|
||||
{ "Ritual Book", "仪式书" },
|
||||
{ "Rose", "玫瑰" },
|
||||
{ "Ritual Book ESP", "仪式书透视" },
|
||||
@@ -253,8 +254,11 @@ namespace DevourClient.Localization.Translations
|
||||
{ "TP to Azazel", "传送到 Azazel" },
|
||||
{ "TV", "电视" },
|
||||
{ "Teleport Keys", "传送钥匙" },
|
||||
{ "Recall (B)", "回城 (B)" },
|
||||
{ "Teleport to", "传送至" },
|
||||
{ "TP Base", "传送至基地" },
|
||||
{ "TP Altar", "传送至祭坛" },
|
||||
{ "TP Basin", "传送至水池" },
|
||||
{ "TP Fountain", "传送至喷泉" },
|
||||
{ "Ticket", "票券" },
|
||||
{ "Town", "小镇" },
|
||||
{ "TownDoor", "小镇门" },
|
||||
|
||||
@@ -207,6 +207,7 @@ namespace DevourClient.Localization.Translations
|
||||
{ "Rat ESP", "Rat ESP" },
|
||||
{ "Region", "Region" },
|
||||
{ "Revive", "Revive" },
|
||||
{ "Radial Menu (Z)", "Radial Menu (Z)" },
|
||||
{ "Ritual Book", "Ritual Book" },
|
||||
{ "Rose", "Rose" },
|
||||
{ "Ritual Book ESP", "Ritual Book ESP" },
|
||||
@@ -253,8 +254,11 @@ namespace DevourClient.Localization.Translations
|
||||
{ "TP to Azazel", "TP to Azazel" },
|
||||
{ "TV", "TV" },
|
||||
{ "Teleport Keys", "Teleport Keys" },
|
||||
{ "Recall (B)", "Recall (B)" },
|
||||
{ "Teleport to", "Teleport to" },
|
||||
{ "TP Base", "TP Base" },
|
||||
{ "TP Altar", "TP Altar" },
|
||||
{ "TP Basin", "TP Basin" },
|
||||
{ "TP Fountain", "TP Fountain" },
|
||||
{ "Ticket", "Ticket" },
|
||||
{ "Town", "Town" },
|
||||
{ "TownDoor", "TownDoor" },
|
||||
|
||||
@@ -207,6 +207,7 @@ namespace DevourClient.Localization.Translations
|
||||
{ "Rat ESP", "ESP rat" },
|
||||
{ "Region", "Région" },
|
||||
{ "Revive", "Réanimer" },
|
||||
{ "Radial Menu (Z)", "Menu radial (Z)" },
|
||||
{ "Ritual Book", "Livre rituel" },
|
||||
{ "Rose", "Rose" },
|
||||
{ "Ritual Book ESP", "ESP livre rituel" },
|
||||
@@ -253,8 +254,11 @@ namespace DevourClient.Localization.Translations
|
||||
{ "TP to Azazel", "TP vers Azazel" },
|
||||
{ "TV", "Télévision" },
|
||||
{ "Teleport Keys", "Téléporter clés" },
|
||||
{ "Recall (B)", "Retour (B)" },
|
||||
{ "Teleport to", "Téléporter" },
|
||||
{ "TP Base", "TP base" },
|
||||
{ "TP Altar", "TP autel" },
|
||||
{ "TP Basin", "TP bassin" },
|
||||
{ "TP Fountain", "TP fontaine" },
|
||||
{ "Ticket", "Billet" },
|
||||
{ "Town", "Ville" },
|
||||
{ "TownDoor", "Porte de ville" },
|
||||
|
||||
@@ -207,6 +207,7 @@ namespace DevourClient.Localization.Translations
|
||||
{ "Rat ESP", "Ratte-ESP" },
|
||||
{ "Region", "Region" },
|
||||
{ "Revive", "Wiederbeleben" },
|
||||
{ "Radial Menu (Z)", "Radiales Menü (Z)" },
|
||||
{ "Ritual Book", "Ritualbuch" },
|
||||
{ "Rose", "Rose" },
|
||||
{ "Ritual Book ESP", "Ritualbuch-ESP" },
|
||||
@@ -253,8 +254,11 @@ namespace DevourClient.Localization.Translations
|
||||
{ "TP to Azazel", "TP zu Azazel" },
|
||||
{ "TV", "Fernseher" },
|
||||
{ "Teleport Keys", "Schlüssel teleportieren" },
|
||||
{ "Recall (B)", "Zurückrufen (B)" },
|
||||
{ "Teleport to", "Teleportieren" },
|
||||
{ "TP Base", "TP Basis" },
|
||||
{ "TP Altar", "TP Altar" },
|
||||
{ "TP Basin", "TP Becken" },
|
||||
{ "TP Fountain", "TP Brunnen" },
|
||||
{ "Ticket", "Ticket" },
|
||||
{ "Town", "Stadt" },
|
||||
{ "TownDoor", "Stadttür" },
|
||||
|
||||
@@ -207,6 +207,7 @@ namespace DevourClient.Localization.Translations
|
||||
{ "Rat ESP", "ESP ratto" },
|
||||
{ "Region", "Regione" },
|
||||
{ "Revive", "Rianima" },
|
||||
{ "Radial Menu (Z)", "Menu radiale (Z)" },
|
||||
{ "Ritual Book", "Libro rituale" },
|
||||
{ "Rose", "Rosa" },
|
||||
{ "Ritual Book ESP", "ESP libro rituale" },
|
||||
@@ -253,8 +254,11 @@ namespace DevourClient.Localization.Translations
|
||||
{ "TP to Azazel", "TP ad Azazel" },
|
||||
{ "TV", "Televisione" },
|
||||
{ "Teleport Keys", "Teletrasporta chiavi" },
|
||||
{ "Recall (B)", "Richiama (B)" },
|
||||
{ "Teleport to", "Teletrasporta" },
|
||||
{ "TP Base", "TP Base" },
|
||||
{ "TP Altar", "TP Altare" },
|
||||
{ "TP Basin", "TP Bacino" },
|
||||
{ "TP Fountain", "TP Fontana" },
|
||||
{ "Ticket", "Biglietto" },
|
||||
{ "Town", "Città" },
|
||||
{ "TownDoor", "Porta della città" },
|
||||
|
||||
@@ -207,6 +207,7 @@ namespace DevourClient.Localization.Translations
|
||||
{ "Rat ESP", "ネズミESP" },
|
||||
{ "Region", "地域" },
|
||||
{ "Revive", "蘇生" },
|
||||
{ "Radial Menu (Z)", "ラジアルメニュー (Z)" },
|
||||
{ "Ritual Book", "儀式の本" },
|
||||
{ "Rose", "バラ" },
|
||||
{ "Ritual Book ESP", "儀式の本ESP" },
|
||||
@@ -253,8 +254,11 @@ namespace DevourClient.Localization.Translations
|
||||
{ "TP to Azazel", "Azazelへテレポート" },
|
||||
{ "TV", "テレビ" },
|
||||
{ "Teleport Keys", "鍵をテレポート" },
|
||||
{ "Recall (B)", "リコール (B)" },
|
||||
{ "Teleport to", "テレポート" },
|
||||
{ "TP Base", "ベースTP" },
|
||||
{ "TP Altar", "祭壇TP" },
|
||||
{ "TP Basin", "水盤TP" },
|
||||
{ "TP Fountain", "噴水TP" },
|
||||
{ "Ticket", "チケット" },
|
||||
{ "Town", "町" },
|
||||
{ "TownDoor", "町のドア" },
|
||||
|
||||
@@ -207,6 +207,7 @@ namespace DevourClient.Localization.Translations
|
||||
{ "Rat ESP", "쥐 ESP" },
|
||||
{ "Region", "지역" },
|
||||
{ "Revive", "부활" },
|
||||
{ "Radial Menu (Z)", "방사형 메뉴 (Z)" },
|
||||
{ "Ritual Book", "의식서" },
|
||||
{ "Rose", "장미" },
|
||||
{ "Ritual Book ESP", "의식서 ESP" },
|
||||
@@ -253,8 +254,11 @@ namespace DevourClient.Localization.Translations
|
||||
{ "TP to Azazel", "Azazel로 텔레포트" },
|
||||
{ "TV", "TV" },
|
||||
{ "Teleport Keys", "열쇠 텔레포트" },
|
||||
{ "Recall (B)", "리콜 (B)" },
|
||||
{ "Teleport to", "텔레포트" },
|
||||
{ "TP Base", "기지 TP" },
|
||||
{ "TP Altar", "제단 TP" },
|
||||
{ "TP Basin", "대야 TP" },
|
||||
{ "TP Fountain", "분수 TP" },
|
||||
{ "Ticket", "티켓" },
|
||||
{ "Town", "마을" },
|
||||
{ "TownDoor", "마을 문" },
|
||||
|
||||
@@ -207,6 +207,7 @@ namespace DevourClient.Localization.Translations
|
||||
{ "Rat ESP", "ESP rato" },
|
||||
{ "Region", "Região" },
|
||||
{ "Revive", "Reviver" },
|
||||
{ "Radial Menu (Z)", "Menu radial (Z)" },
|
||||
{ "Ritual Book", "Livro ritual" },
|
||||
{ "Rose", "Rosa" },
|
||||
{ "Ritual Book ESP", "ESP livro ritual" },
|
||||
@@ -253,8 +254,11 @@ namespace DevourClient.Localization.Translations
|
||||
{ "TP to Azazel", "TP para Azazel" },
|
||||
{ "TV", "Televisão" },
|
||||
{ "Teleport Keys", "Teletransportar chaves" },
|
||||
{ "Recall (B)", "Recuar (B)" },
|
||||
{ "Teleport to", "Teletransportar" },
|
||||
{ "TP Base", "TP base" },
|
||||
{ "TP Altar", "TP altar" },
|
||||
{ "TP Basin", "TP bacia" },
|
||||
{ "TP Fountain", "TP fonte" },
|
||||
{ "Ticket", "Bilhete" },
|
||||
{ "Town", "Cidade" },
|
||||
{ "TownDoor", "Porta da cidade" },
|
||||
|
||||
@@ -207,6 +207,7 @@ namespace DevourClient.Localization.Translations
|
||||
{ "Rat ESP", "ESP крысы" },
|
||||
{ "Region", "Регион" },
|
||||
{ "Revive", "Воскресить" },
|
||||
{ "Radial Menu (Z)", "Радиальное меню (Z)" },
|
||||
{ "Ritual Book", "Ритуальная книга" },
|
||||
{ "Rose", "Роза" },
|
||||
{ "Ritual Book ESP", "ESP ритуальной книги" },
|
||||
@@ -253,8 +254,11 @@ namespace DevourClient.Localization.Translations
|
||||
{ "TP to Azazel", "ТП к Azazel" },
|
||||
{ "TV", "Телевизор" },
|
||||
{ "Teleport Keys", "Телепорт ключей" },
|
||||
{ "Recall (B)", "Возврат (B)" },
|
||||
{ "Teleport to", "Телепорт" },
|
||||
{ "TP Base", "ТП база" },
|
||||
{ "TP Altar", "ТП алтарь" },
|
||||
{ "TP Basin", "ТП чаша" },
|
||||
{ "TP Fountain", "ТП фонтан" },
|
||||
{ "Ticket", "Билет" },
|
||||
{ "Town", "Город" },
|
||||
{ "TownDoor", "Дверь города" },
|
||||
|
||||
@@ -207,6 +207,7 @@ namespace DevourClient.Localization.Translations
|
||||
{ "Rat ESP", "ESP rata" },
|
||||
{ "Region", "Región" },
|
||||
{ "Revive", "Revivir" },
|
||||
{ "Radial Menu (Z)", "Menú radial (Z)" },
|
||||
{ "Ritual Book", "Libro ritual" },
|
||||
{ "Rose", "Rosa" },
|
||||
{ "Ritual Book ESP", "ESP libro ritual" },
|
||||
@@ -253,8 +254,11 @@ namespace DevourClient.Localization.Translations
|
||||
{ "TP to Azazel", "TP a Azazel" },
|
||||
{ "TV", "Televisión" },
|
||||
{ "Teleport Keys", "Teletransportar llaves" },
|
||||
{ "Recall (B)", "Regresar (B)" },
|
||||
{ "Teleport to", "Teletransportar" },
|
||||
{ "TP Base", "TP base" },
|
||||
{ "TP Altar", "TP altar" },
|
||||
{ "TP Basin", "TP pila" },
|
||||
{ "TP Fountain", "TP fuente" },
|
||||
{ "Ticket", "Boleto" },
|
||||
{ "Town", "Pueblo" },
|
||||
{ "TownDoor", "Puerta de pueblo" },
|
||||
|
||||
@@ -207,6 +207,7 @@ namespace DevourClient.Localization.Translations
|
||||
{ "Rat ESP", "Chuột ESP" },
|
||||
{ "Region", "Khu vực" },
|
||||
{ "Revive", "Hồi sinh" },
|
||||
{ "Radial Menu (Z)", "Menu vòng tròn (Z)" },
|
||||
{ "Ritual Book", "Sách nghi lễ" },
|
||||
{ "Rose", "Hoa hồng" },
|
||||
{ "Ritual Book ESP", "Sách nghi lễ ESP" },
|
||||
@@ -253,8 +254,11 @@ namespace DevourClient.Localization.Translations
|
||||
{ "TP to Azazel", "TP đến Azazel" },
|
||||
{ "TV", "TV" },
|
||||
{ "Teleport Keys", "Phím dịch chuyển" },
|
||||
{ "Recall (B)", "Triệu hồi (B)" },
|
||||
{ "Teleport to", "Dịch chuyển đến" },
|
||||
{ "TP Base", "TP căn cứ" },
|
||||
{ "TP Altar", "TP bàn thờ" },
|
||||
{ "TP Basin", "TP bể nước" },
|
||||
{ "TP Fountain", "TP đài phun nước" },
|
||||
{ "Ticket", "Vé" },
|
||||
{ "Town", "Town" },
|
||||
{ "TownDoor", "TownDoor" },
|
||||
|
||||
@@ -8,17 +8,13 @@ using UnityEngine;
|
||||
|
||||
namespace DevourClient.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Lobby creation and management class
|
||||
/// </summary>
|
||||
// Lobby creation and management class
|
||||
public static class LobbyManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Create game lobby/room
|
||||
/// </summary>
|
||||
/// <param name="regionCode">Region code (e.g.: "eu", "us", "asia", "usw", "sa", "jp", "au", "ru", "in", "kr")</param>
|
||||
/// <param name="lobbyLimit">Maximum player limit (1-64)</param>
|
||||
/// <param name="isPrivate">Whether this is a private room</param>
|
||||
// Create game lobby/room.
|
||||
// regionCode: Region code (e.g.: "eu", "us", "asia", "usw", "sa", "jp", "au", "ru", "in", "kr").
|
||||
// lobbyLimit: Maximum player limit (1-64).
|
||||
// isPrivate: Whether this is a private room.
|
||||
public static void CreateLobby(string regionCode, int lobbyLimit, bool isPrivate)
|
||||
{
|
||||
try
|
||||
@@ -96,9 +92,6 @@ namespace DevourClient.Network
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get PhotonRegion object for specified region
|
||||
/// </summary>
|
||||
private static PhotonRegion GetPhotonRegion(string regionCode)
|
||||
{
|
||||
try
|
||||
@@ -113,9 +106,6 @@ namespace DevourClient.Network
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find Menu controller
|
||||
/// </summary>
|
||||
private static Il2CppHorror.Menu FindMenuController()
|
||||
{
|
||||
try
|
||||
@@ -142,9 +132,6 @@ namespace DevourClient.Network
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if in game
|
||||
/// </summary>
|
||||
private static bool IsInGame()
|
||||
{
|
||||
try
|
||||
@@ -159,9 +146,6 @@ namespace DevourClient.Network
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Force start lobby game (host only)
|
||||
/// </summary>
|
||||
public static void ForceLobbyStart()
|
||||
{
|
||||
try
|
||||
@@ -189,9 +173,6 @@ namespace DevourClient.Network
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show message box
|
||||
/// </summary>
|
||||
public static void ShowMessageBox(string message)
|
||||
{
|
||||
try
|
||||
|
||||
698
DevourClient/RadialMenuManager.cs
Normal file
698
DevourClient/RadialMenuManager.cs
Normal file
@@ -0,0 +1,698 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MelonLoader;
|
||||
using UnityEngine;
|
||||
using DevourClient.Helpers;
|
||||
using DevourClient.Localization;
|
||||
using System.Globalization;
|
||||
|
||||
namespace DevourClient
|
||||
{
|
||||
// Manages the Z-key radial menu: building options, drawing the UI, and executing actions.
|
||||
// Public static methods are called from the appropriate ClientMain lifecycle hooks.
|
||||
internal static class RadialMenuManager
|
||||
{
|
||||
private static bool _active;
|
||||
private static int _selectedIndex = -1;
|
||||
private static readonly List<RadialOption> _options = new List<RadialOption>();
|
||||
private static bool _cursorStateStored;
|
||||
private static bool _prevCursorVisible;
|
||||
private static CursorLockMode _prevCursorLockState;
|
||||
private static bool _enabled = true; // Controls whether the radial menu is enabled
|
||||
|
||||
// Material used to draw radial arcs (GL immediate mode)
|
||||
private static Material _radialMaterial;
|
||||
|
||||
// Public property to enable/disable the radial menu
|
||||
public static bool Enabled
|
||||
{
|
||||
get => _enabled;
|
||||
set => _enabled = value;
|
||||
}
|
||||
|
||||
private enum RadialActionType
|
||||
{
|
||||
None,
|
||||
SpawnItem,
|
||||
TeleportBase,
|
||||
TeleportFixedPoint
|
||||
}
|
||||
|
||||
private class RadialOption
|
||||
{
|
||||
public string Label = string.Empty;
|
||||
public RadialActionType ActionType = RadialActionType.None;
|
||||
public string Payload = string.Empty;
|
||||
}
|
||||
|
||||
// Called from Update: handles input, builds / closes the radial menu and executes actions.
|
||||
public static void HandleUpdate()
|
||||
{
|
||||
if (!_enabled || !Player.IsInGameOrLobby())
|
||||
return;
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.Z))
|
||||
{
|
||||
BuildForCurrentScene();
|
||||
}
|
||||
|
||||
if (Input.GetKeyUp(KeyCode.Z) && _active)
|
||||
{
|
||||
if (_selectedIndex >= 0 && _selectedIndex < _options.Count)
|
||||
{
|
||||
ExecuteOption(_options[_selectedIndex]);
|
||||
}
|
||||
|
||||
_active = false;
|
||||
_selectedIndex = -1;
|
||||
_options.Clear();
|
||||
|
||||
RestoreCursorState();
|
||||
}
|
||||
}
|
||||
|
||||
// Called from OnGUI: draws the radial menu if it is active.
|
||||
public static void HandleOnGUI()
|
||||
{
|
||||
if (!_enabled || !_active || !Player.IsInGameOrLobby())
|
||||
return;
|
||||
|
||||
Draw();
|
||||
}
|
||||
|
||||
private static void BuildForCurrentScene()
|
||||
{
|
||||
_options.Clear();
|
||||
_selectedIndex = -1;
|
||||
|
||||
string sceneName = Helpers.Map.GetActiveScene();
|
||||
if (string.IsNullOrEmpty(sceneName) || sceneName == "Menu")
|
||||
{
|
||||
_active = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_options.Add(new RadialOption
|
||||
{
|
||||
Label = MultiLanguageSystem.Translate("First aid"),
|
||||
ActionType = RadialActionType.SpawnItem,
|
||||
Payload = "SurvivalFirstAid"
|
||||
});
|
||||
|
||||
_options.Add(new RadialOption
|
||||
{
|
||||
Label = MultiLanguageSystem.Translate("Battery"),
|
||||
ActionType = RadialActionType.SpawnItem,
|
||||
Payload = "SurvivalBattery"
|
||||
});
|
||||
|
||||
_options.Add(new RadialOption
|
||||
{
|
||||
Label = MultiLanguageSystem.Translate("TP Base"),
|
||||
ActionType = RadialActionType.TeleportBase,
|
||||
Payload = string.Empty
|
||||
});
|
||||
|
||||
switch (sceneName)
|
||||
{
|
||||
case "Devour":
|
||||
case "Anna":
|
||||
_options.Add(new RadialOption
|
||||
{
|
||||
Label = MultiLanguageSystem.Translate("TP Altar"),
|
||||
ActionType = RadialActionType.TeleportFixedPoint,
|
||||
Payload = "8.57 0.01 -65.19"
|
||||
});
|
||||
|
||||
_options.Add(new RadialOption
|
||||
{
|
||||
Label = MultiLanguageSystem.Translate("Hay"),
|
||||
ActionType = RadialActionType.SpawnItem,
|
||||
Payload = "SurvivalHay"
|
||||
});
|
||||
_options.Add(new RadialOption
|
||||
{
|
||||
Label = MultiLanguageSystem.Translate("Gasoline"),
|
||||
ActionType = RadialActionType.SpawnItem,
|
||||
Payload = "SurvivalGasoline"
|
||||
});
|
||||
break;
|
||||
|
||||
case "Molly":
|
||||
_options.Add(new RadialOption
|
||||
{
|
||||
Label = MultiLanguageSystem.Translate("TP Altar"),
|
||||
ActionType = RadialActionType.TeleportFixedPoint,
|
||||
Payload = "18.12 -8.80 21.06"
|
||||
});
|
||||
|
||||
_options.Add(new RadialOption
|
||||
{
|
||||
Label = MultiLanguageSystem.Translate("Fuse"),
|
||||
ActionType = RadialActionType.SpawnItem,
|
||||
Payload = "SurvivalFuse"
|
||||
});
|
||||
_options.Add(new RadialOption
|
||||
{
|
||||
Label = MultiLanguageSystem.Translate("RottenFood"),
|
||||
ActionType = RadialActionType.SpawnItem,
|
||||
Payload = "SurvivalRottenFood"
|
||||
});
|
||||
break;
|
||||
|
||||
case "Inn":
|
||||
_options.Add(new RadialOption
|
||||
{
|
||||
Label = MultiLanguageSystem.Translate("TP Fountain"),
|
||||
ActionType = RadialActionType.TeleportFixedPoint,
|
||||
Payload = "-3.43 0.06 24.31"
|
||||
});
|
||||
|
||||
_options.Add(new RadialOption
|
||||
{
|
||||
Label = MultiLanguageSystem.Translate("Bleach"),
|
||||
ActionType = RadialActionType.SpawnItem,
|
||||
Payload = "SurvivalBleach"
|
||||
});
|
||||
break;
|
||||
|
||||
case "Town":
|
||||
_options.Add(new RadialOption
|
||||
{
|
||||
Label = MultiLanguageSystem.Translate("TP Altar"),
|
||||
ActionType = RadialActionType.TeleportFixedPoint,
|
||||
Payload = "-56.88 7.17 -34.51"
|
||||
});
|
||||
_options.Add(new RadialOption
|
||||
{
|
||||
Label = MultiLanguageSystem.Translate("Matchbox"),
|
||||
ActionType = RadialActionType.SpawnItem,
|
||||
Payload = "Matchbox-3"
|
||||
});
|
||||
_options.Add(new RadialOption
|
||||
{
|
||||
Label = MultiLanguageSystem.Translate("Gasoline"),
|
||||
ActionType = RadialActionType.SpawnItem,
|
||||
Payload = "SurvivalGasoline"
|
||||
});
|
||||
|
||||
break;
|
||||
|
||||
case "Slaughterhouse":
|
||||
_options.Add(new RadialOption
|
||||
{
|
||||
Label = MultiLanguageSystem.Translate("TP Altar"),
|
||||
ActionType = RadialActionType.TeleportFixedPoint,
|
||||
Payload = "26.68 4.01 -9.27"
|
||||
});
|
||||
|
||||
_options.Add(new RadialOption
|
||||
{
|
||||
Label = MultiLanguageSystem.Translate("Bone"),
|
||||
ActionType = RadialActionType.SpawnItem,
|
||||
Payload = "SurvivalBone"
|
||||
});
|
||||
_options.Add(new RadialOption
|
||||
{
|
||||
Label = MultiLanguageSystem.Translate("Gasoline"),
|
||||
ActionType = RadialActionType.SpawnItem,
|
||||
Payload = "SurvivalGasoline"
|
||||
});
|
||||
break;
|
||||
|
||||
case "Manor":
|
||||
_options.Add(new RadialOption
|
||||
{
|
||||
Label = MultiLanguageSystem.Translate("TP Basin"),
|
||||
ActionType = RadialActionType.TeleportFixedPoint,
|
||||
Payload = "38.93 -4.62 -3.86"
|
||||
});
|
||||
|
||||
_options.Add(new RadialOption
|
||||
{
|
||||
Label = MultiLanguageSystem.Translate("Bleach"),
|
||||
ActionType = RadialActionType.SpawnItem,
|
||||
Payload = "SurvivalBleach"
|
||||
});
|
||||
_options.Add(new RadialOption
|
||||
{
|
||||
Label = MultiLanguageSystem.Translate("Cake"),
|
||||
ActionType = RadialActionType.SpawnItem,
|
||||
Payload = "SurvivalCake"
|
||||
});
|
||||
_options.Add(new RadialOption
|
||||
{
|
||||
Label = MultiLanguageSystem.Translate("Spade"),
|
||||
ActionType = RadialActionType.SpawnItem,
|
||||
Payload = "SurvivalSpade"
|
||||
});
|
||||
break;
|
||||
|
||||
case "Carnival":
|
||||
_options.Add(new RadialOption
|
||||
{
|
||||
Label = MultiLanguageSystem.Translate("TP Altar"),
|
||||
ActionType = RadialActionType.TeleportFixedPoint,
|
||||
Payload = "-114.65 4.07 -4.12"
|
||||
});
|
||||
|
||||
_options.Add(new RadialOption
|
||||
{
|
||||
Label = MultiLanguageSystem.Translate("Coin"),
|
||||
ActionType = RadialActionType.SpawnItem,
|
||||
Payload = "SurvivalCoin"
|
||||
});
|
||||
_options.Add(new RadialOption
|
||||
{
|
||||
Label = MultiLanguageSystem.Translate("MusicBox"),
|
||||
ActionType = RadialActionType.SpawnItem,
|
||||
Payload = "MusicBox-Idle"
|
||||
});
|
||||
_options.Add(new RadialOption
|
||||
{
|
||||
Label = MultiLanguageSystem.Translate("DollHead"),
|
||||
ActionType = RadialActionType.SpawnItem,
|
||||
Payload = "SurvivalDollHead"
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
if (_options.Count == 0)
|
||||
{
|
||||
_active = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_active = true;
|
||||
HideCursorForRadial();
|
||||
}
|
||||
|
||||
private static void Draw()
|
||||
{
|
||||
Event e = Event.current;
|
||||
if (e == null)
|
||||
return;
|
||||
|
||||
bool isRepaint = e.type == EventType.Repaint;
|
||||
|
||||
float centerX = Screen.width / 2f;
|
||||
float centerY = Screen.height / 2f;
|
||||
Vector2 center = new Vector2(centerX, centerY);
|
||||
|
||||
float radiusOuter = 140f;
|
||||
float radiusInner = 40f;
|
||||
|
||||
Vector2 mouse = e.mousePosition;
|
||||
Vector2 dir = mouse - center;
|
||||
float dist = dir.magnitude;
|
||||
|
||||
_selectedIndex = -1;
|
||||
|
||||
if (dist >= radiusInner && dist <= radiusOuter && _options.Count > 0)
|
||||
{
|
||||
// Normalize angles: convert GUI coordinates (y down) to math coordinates (y up)
|
||||
// and treat "up" as 0° increasing clockwise.
|
||||
Vector2 upDir = new Vector2(dir.x, -dir.y);
|
||||
float mathAngle = Mathf.Atan2(upDir.y, upDir.x); // [-PI, PI], 0 is on the right, counterclockwise is positive
|
||||
|
||||
float logicalAngle = (Mathf.PI / 2f) - mathAngle;
|
||||
if (logicalAngle < 0f)
|
||||
{
|
||||
logicalAngle += Mathf.PI * 2f;
|
||||
}
|
||||
|
||||
float logicalSectorAngle = (Mathf.PI * 2f) / _options.Count;
|
||||
int index = Mathf.Clamp(Mathf.FloorToInt(logicalAngle / logicalSectorAngle), 0, _options.Count - 1);
|
||||
_selectedIndex = index;
|
||||
}
|
||||
|
||||
int count = _options.Count;
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
if (isRepaint)
|
||||
{
|
||||
EnsureRadialMaterial();
|
||||
DrawFilledCircle(center, radiusOuter + 6f, new Color(0f, 0f, 0f, 0.55f));
|
||||
}
|
||||
|
||||
// Sector layout and label radius
|
||||
float logicalAnglePerSector = (Mathf.PI * 2f) / count;
|
||||
float radiusFactor;
|
||||
if (count <= 6)
|
||||
radiusFactor = 0.55f;
|
||||
else if (count == 7)
|
||||
radiusFactor = 0.48f;
|
||||
else if (count <= 8)
|
||||
radiusFactor = 0.5f;
|
||||
else
|
||||
radiusFactor = 0.45f;
|
||||
float labelRadius = radiusInner + (radiusOuter - radiusInner) * radiusFactor;
|
||||
|
||||
if (isRepaint)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
float logicalStart = logicalAnglePerSector * i;
|
||||
float logicalEnd = logicalAnglePerSector * (i + 1);
|
||||
|
||||
float displayStart = (Mathf.PI / 2f) - logicalStart;
|
||||
float displayEnd = (Mathf.PI / 2f) - logicalEnd;
|
||||
|
||||
Color sectorColor = (i == _selectedIndex)
|
||||
? new Color(0.15f, 0.7f, 0.3f, 0.8f)
|
||||
: new Color(0.1f, 0.1f, 0.1f, 0.7f);
|
||||
|
||||
DrawFilledSector(center, radiusInner, radiusOuter, displayStart, displayEnd, sectorColor);
|
||||
}
|
||||
}
|
||||
|
||||
GUIStyle labelStyle = new GUIStyle(GUI.skin.label)
|
||||
{
|
||||
alignment = TextAnchor.MiddleCenter,
|
||||
normal = { textColor = Color.white },
|
||||
fontSize = (count <= 6) ? 14 : (count <= 8 ? 12 : 10),
|
||||
wordWrap = true
|
||||
};
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
float logicalMid = logicalAnglePerSector * (i + 0.5f);
|
||||
float displayMid = (Mathf.PI / 2f) - logicalMid;
|
||||
|
||||
float lx = centerX + Mathf.Cos(displayMid) * labelRadius;
|
||||
float ly = centerY - Mathf.Sin(displayMid) * labelRadius;
|
||||
|
||||
float arcLength = logicalAnglePerSector * labelRadius;
|
||||
float arcFactor;
|
||||
float minWidth;
|
||||
float maxWidth;
|
||||
|
||||
if (count <= 6)
|
||||
{
|
||||
arcFactor = 0.8f;
|
||||
minWidth = 60f;
|
||||
maxWidth = 120f;
|
||||
}
|
||||
else if (count == 7)
|
||||
{
|
||||
arcFactor = 0.6f;
|
||||
minWidth = 45f;
|
||||
maxWidth = 75f;
|
||||
}
|
||||
else if (count <= 8)
|
||||
{
|
||||
arcFactor = 0.65f;
|
||||
minWidth = 45f;
|
||||
maxWidth = 80f;
|
||||
}
|
||||
else
|
||||
{
|
||||
arcFactor = 0.55f;
|
||||
minWidth = 40f;
|
||||
maxWidth = 70f;
|
||||
}
|
||||
|
||||
float baseWidth = arcLength * arcFactor;
|
||||
|
||||
float labelWidth = Mathf.Clamp(baseWidth, minWidth, maxWidth);
|
||||
float labelHeight = 32f;
|
||||
|
||||
Rect labelRect = new Rect(lx - labelWidth / 2f, ly - labelHeight / 2f, labelWidth, labelHeight);
|
||||
GUI.Label(labelRect, _options[i].Label, labelStyle);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EnsureRadialMaterial()
|
||||
{
|
||||
if (_radialMaterial != null)
|
||||
return;
|
||||
|
||||
Shader shader = Shader.Find("Hidden/Internal-Colored");
|
||||
if (shader == null)
|
||||
return;
|
||||
|
||||
_radialMaterial = new Material(shader)
|
||||
{
|
||||
hideFlags = HideFlags.HideAndDontSave
|
||||
};
|
||||
_radialMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
|
||||
_radialMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
|
||||
_radialMaterial.SetInt("_Cull", (int)UnityEngine.Rendering.CullMode.Off);
|
||||
_radialMaterial.SetInt("_ZWrite", 0);
|
||||
}
|
||||
|
||||
private static void DrawFilledCircle(Vector2 center, float radius, Color color)
|
||||
{
|
||||
if (_radialMaterial == null)
|
||||
return;
|
||||
|
||||
_radialMaterial.SetPass(0);
|
||||
GL.PushMatrix();
|
||||
GL.LoadPixelMatrix(0, Screen.width, Screen.height, 0);
|
||||
|
||||
GL.Begin(GL.TRIANGLES);
|
||||
GL.Color(color);
|
||||
|
||||
const int steps = 64;
|
||||
for (int i = 0; i < steps; i++)
|
||||
{
|
||||
float t0 = (float)i / steps;
|
||||
float t1 = (float)(i + 1) / steps;
|
||||
|
||||
float ang0 = t0 * Mathf.PI * 2f;
|
||||
float ang1 = t1 * Mathf.PI * 2f;
|
||||
|
||||
float x0 = center.x + Mathf.Cos(ang0) * radius;
|
||||
float y0 = center.y - Mathf.Sin(ang0) * radius;
|
||||
float x1 = center.x + Mathf.Cos(ang1) * radius;
|
||||
float y1 = center.y - Mathf.Sin(ang1) * radius;
|
||||
|
||||
GL.Vertex3(center.x, center.y, 0f);
|
||||
GL.Vertex3(x0, y0, 0f);
|
||||
GL.Vertex3(x1, y1, 0f);
|
||||
}
|
||||
|
||||
GL.End();
|
||||
GL.PopMatrix();
|
||||
}
|
||||
|
||||
private static void DrawFilledSector(Vector2 center, float innerRadius, float outerRadius,
|
||||
float startAngle, float endAngle, Color color)
|
||||
{
|
||||
if (_radialMaterial == null)
|
||||
return;
|
||||
|
||||
_radialMaterial.SetPass(0);
|
||||
GL.PushMatrix();
|
||||
GL.LoadPixelMatrix(0, Screen.width, Screen.height, 0);
|
||||
|
||||
GL.Begin(GL.TRIANGLES);
|
||||
GL.Color(color);
|
||||
|
||||
int steps = Mathf.Max(8, Mathf.CeilToInt(Mathf.Abs(endAngle - startAngle) / (Mathf.PI / 24f)));
|
||||
float delta = (endAngle - startAngle) / steps;
|
||||
|
||||
for (int i = 0; i < steps; i++)
|
||||
{
|
||||
float a0 = startAngle + delta * i;
|
||||
float a1 = startAngle + delta * (i + 1);
|
||||
|
||||
Vector2 o0 = new Vector2(
|
||||
center.x + Mathf.Cos(a0) * outerRadius,
|
||||
center.y - Mathf.Sin(a0) * outerRadius);
|
||||
Vector2 o1 = new Vector2(
|
||||
center.x + Mathf.Cos(a1) * outerRadius,
|
||||
center.y - Mathf.Sin(a1) * outerRadius);
|
||||
|
||||
Vector2 i0 = new Vector2(
|
||||
center.x + Mathf.Cos(a0) * innerRadius,
|
||||
center.y - Mathf.Sin(a0) * innerRadius);
|
||||
Vector2 i1 = new Vector2(
|
||||
center.x + Mathf.Cos(a1) * innerRadius,
|
||||
center.y - Mathf.Sin(a1) * innerRadius);
|
||||
|
||||
GL.Vertex3(o0.x, o0.y, 0f);
|
||||
GL.Vertex3(o1.x, o1.y, 0f);
|
||||
GL.Vertex3(i1.x, i1.y, 0f);
|
||||
|
||||
GL.Vertex3(o0.x, o0.y, 0f);
|
||||
GL.Vertex3(i1.x, i1.y, 0f);
|
||||
GL.Vertex3(i0.x, i0.y, 0f);
|
||||
}
|
||||
|
||||
GL.End();
|
||||
GL.PopMatrix();
|
||||
}
|
||||
|
||||
private static void ExecuteOption(RadialOption option)
|
||||
{
|
||||
if (option == null || option.ActionType == RadialActionType.None)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
switch (option.ActionType)
|
||||
{
|
||||
case RadialActionType.SpawnItem:
|
||||
if (string.IsNullOrEmpty(option.Payload))
|
||||
return;
|
||||
|
||||
ClientMain_HandleItemCarry(option.Payload);
|
||||
break;
|
||||
|
||||
case RadialActionType.TeleportBase:
|
||||
TeleportToBase();
|
||||
break;
|
||||
|
||||
case RadialActionType.TeleportFixedPoint:
|
||||
TeleportToFixedPoint(option.Payload);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MelonLogger.Error($"Radial option execution failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void HideCursorForRadial()
|
||||
{
|
||||
if (!_cursorStateStored)
|
||||
{
|
||||
_prevCursorVisible = Cursor.visible;
|
||||
_prevCursorLockState = Cursor.lockState;
|
||||
_cursorStateStored = true;
|
||||
}
|
||||
|
||||
Cursor.lockState = CursorLockMode.None;
|
||||
Cursor.visible = true;
|
||||
}
|
||||
|
||||
private static void RestoreCursorState()
|
||||
{
|
||||
if (!_cursorStateStored)
|
||||
return;
|
||||
|
||||
Cursor.lockState = _prevCursorLockState;
|
||||
Cursor.visible = _prevCursorVisible;
|
||||
_cursorStateStored = false;
|
||||
}
|
||||
|
||||
// Calls ClientMain.HandleItemCarry via reflection to avoid tight coupling,
|
||||
// and falls back to Hacks.Misc.CarryObject if that fails.
|
||||
private static void ClientMain_HandleItemCarry(string payload)
|
||||
{
|
||||
try
|
||||
{
|
||||
var type = typeof(ClientMain);
|
||||
var method = type.GetMethod("HandleItemCarry",
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
|
||||
if (method != null)
|
||||
{
|
||||
method.Invoke(null, new object[] { payload });
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore and fallback
|
||||
}
|
||||
|
||||
Hacks.Misc.CarryObject(payload);
|
||||
}
|
||||
|
||||
// Teleports the player to a fixed world position.
|
||||
// Payload format: "x y z" using '.' as decimal separator.
|
||||
private static void TeleportToFixedPoint(string payload)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(payload))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
string[] parts = payload.Split(new[] { ' ', '\t', ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length < 3)
|
||||
return;
|
||||
|
||||
float x = float.Parse(parts[0], CultureInfo.InvariantCulture);
|
||||
float y = float.Parse(parts[1], CultureInfo.InvariantCulture);
|
||||
float z = float.Parse(parts[2], CultureInfo.InvariantCulture);
|
||||
|
||||
Il2Cpp.NolanBehaviour nb = Player.GetPlayer();
|
||||
if (nb == null)
|
||||
return;
|
||||
|
||||
Vector3 target = new Vector3(x, y, z);
|
||||
nb.TeleportTo(target, Quaternion.identity);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MelonLogger.Error($"TeleportToFixedPoint failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// Teleports the player to the map-specific base coordinates (former RecallToBase logic).
|
||||
private static void TeleportToBase()
|
||||
{
|
||||
try
|
||||
{
|
||||
Il2Cpp.NolanBehaviour nb = Player.GetPlayer();
|
||||
if (nb == null)
|
||||
{
|
||||
MelonLogger.Warning("Player not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
string sceneName = Helpers.Map.GetActiveScene();
|
||||
Vector3 targetPos = Vector3.zero;
|
||||
string mapName = "";
|
||||
|
||||
switch (sceneName)
|
||||
{
|
||||
case "Devour":
|
||||
case "Anna": // Farmhouse
|
||||
targetPos = new Vector3(5.03f, 4.20f, -50.02f);
|
||||
mapName = "Farm";
|
||||
break;
|
||||
case "Molly": // Asylum
|
||||
targetPos = new Vector3(17.52f, 1.38f, 7.04f);
|
||||
mapName = "Asylum";
|
||||
break;
|
||||
case "Inn":
|
||||
targetPos = new Vector3(3.53f, 0.84f, 2.47f);
|
||||
mapName = "Inn";
|
||||
break;
|
||||
case "Town":
|
||||
targetPos = new Vector3(-63.51f, 10.88f, -12.32f);
|
||||
mapName = "Town";
|
||||
break;
|
||||
case "Slaughterhouse":
|
||||
targetPos = new Vector3(6.09f, 0.70f, -17.58f);
|
||||
mapName = "Slaughterhouse";
|
||||
break;
|
||||
case "Manor":
|
||||
targetPos = new Vector3(3.67f, 1.32f, -23.34f);
|
||||
mapName = "Manor";
|
||||
break;
|
||||
case "Carnival":
|
||||
targetPos = new Vector3(-91.46f, 8.13f, -24.51f);
|
||||
mapName = "Carnival";
|
||||
break;
|
||||
default:
|
||||
MelonLogger.Warning($"Teleport not available for scene: {sceneName}");
|
||||
return;
|
||||
}
|
||||
|
||||
nb.locomotion.SetPosition(targetPos, false);
|
||||
MelonLogger.Msg($"Teleported to {mapName} coordinates: X:{targetPos.x:F2} Y:{targetPos.y:F2} Z:{targetPos.z:F2}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MelonLogger.Error($"Failed to teleport to base: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -103,9 +103,9 @@ If you want to modify and develop the code, please follow the [Building from sou
|
||||
|
||||
4、运行devour → 如果安装成功,你会看到一个windows窗口进行各类安装提示后,自动进入游戏。点击insert键即可打开和关闭devourclient窗口
|
||||
|
||||
**注意:**有些电脑在安装melonloader之后,会出现fatal error的提示,这个我目前并没有碰到过。但是出现这个提示的主要原因,基本是melonloader安装过程中,提取到devour根目录的melonloader文件夹里的文件出现了问题,比较简单的解决办法就是(1)在别人的同系统同位宽(x86,x32)的电脑里拷贝出来他的melonloader文件夹,然后直接粘贴到自己的电脑里。(2)将melonloader文件夹完全删除,然后重装。(3)在直到游戏完全运行,菜单正常工作之前,保持VPN线路通畅。
|
||||
注意:有些电脑在安装melonloader之后,会出现fatal error的提示,这个我目前并没有碰到过。但是出现这个提示的主要原因,基本是melonloader安装过程中,提取到devour根目录的melonloader文件夹里的文件出现了问题,比较简单的解决办法就是(1)在别人的同系统同位宽(x86,x32)的电脑里拷贝出来他的melonloader文件夹,然后直接粘贴到自己的电脑里。(2)将melonloader文件夹完全删除,然后重装。(3)在直到游戏完全运行,菜单正常工作之前,保持VPN线路通畅。
|
||||
|
||||
**注意:**如果在加载时提示 “0 mod”,请检查你的dll文件是否正常,是否已经将dll文件放置到devour的“mods”文件夹中。
|
||||
注意:如果在加载时提示 “0 mod”,请检查你的dll文件是否正常,是否已经将dll文件放置到devour的“mods”文件夹中。
|
||||
|
||||
如果你想要对代码进行修改和开发,请按照下面的 [building from source](#building-from-source) 的步骤,逐步进行
|
||||
|
||||
|
||||
Reference in New Issue
Block a user