Загрузка данных
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using UnityEngine;
using UnityEngine.SceneManagement;
[BepInPlugin("my.cheatmenu", "CheatMenu", "1.4")]
public class Plugin : BasePlugin
{
public static ManualLogSource L;
public override void Load()
{
L = Log;
ClassInjector.RegisterTypeInIl2Cpp<MenuBehaviour>();
AddComponent<MenuBehaviour>();
Log.LogInfo("CheatMenu loaded, press Insert");
}
}
public class MenuBehaviour : MonoBehaviour
{
public MenuBehaviour(IntPtr ptr) : base(ptr) { }
bool open, fly, god, stamina;
float flySpeed = 15f;
Transform player;
CharacterController cc;
Rigidbody rb;
string filter = "";
int scenePage = 0;
float nextApply;
// режим шахеда
bool drone, exploding;
float droneSpeed = 30f, crashSpeed = 20f, spinSpeed = 360f;
float droneStartTime, respawnAt, explodeTime = -100f;
Vector3 startPos;
GameObject flash;
AudioClip boomClip, buzzClip, musicClip;
AudioSource buzzSrc, musicSrc;
string lastHit = "-";
string soundStatus = "звуки не загружены";
string statsStatus = "";
// здоровье и стамина
static Assembly gameAsm;
static readonly Dictionary<string, Type> typeCache = new Dictionary<string, Type>();
static readonly Dictionary<string, double> best = new Dictionary<string, double>();
static readonly HashSet<string> loggedSet = new HashSet<string>();
static readonly string[] HealthWords = { "health", "hp" };
static readonly string[] StaminaWords = { "stamina", "energy" };
static readonly string[] BadWords = { "regen", "rate", "damage", "cost", "drain", "speed", "time", "delay", "max", "min", "percent", "ratio", "fov", "view", "camera", "zoom", "field", "bar", "text", "ui", "image", "slider" };
// ---------- звук ----------
bool ClipOk(AudioClip c)
{
if (c == null) return false;
try { return c.length > 0f; } catch { return false; }
}
void EnsureSounds()
{
if (!ClipOk(boomClip)) boomClip = LoadWav("boom.wav");
if (!ClipOk(boomClip)) { boomClip = MakeBoom(); }
if (!ClipOk(buzzClip)) buzzClip = LoadWav("buzz.wav");
if (!ClipOk(musicClip)) musicClip = LoadWav("music.wav");
soundStatus = "boom:" + (ClipOk(boomClip) ? "ok" : "нет")
+ " buzz:" + (ClipOk(buzzClip) ? "ok" : "нет")
+ " music:" + (ClipOk(musicClip) ? "ok" : "нет");
}
AudioClip MakeClip(string name, float[] d, int ch, int rate)
{
var clip = AudioClip.Create(name, d.Length / ch, ch, rate, false);
clip.SetData((Il2CppStructArray<float>)d, 0);
clip.hideFlags = HideFlags.HideAndDontSave;
return clip;
}
AudioClip MakeBoom()
{
try
{
int rate = 22050, n = rate * 2;
var d = new float[n];
var r = new System.Random(1);
for (int i = 0; i < n; i++)
{
float t = (float)i / rate;
d[i] = ((float)r.NextDouble() * 2f - 1f) * Mathf.Exp(-t * 3f) * 0.9f;
}
return MakeClip("boom_gen", d, 1, rate);
}
catch (Exception e) { Plugin.L.LogInfo("MakeBoom error: " + e.Message); return null; }
}
AudioClip LoadWav(string name)
{
try
{
using (var s = Assembly.GetExecutingAssembly().GetManifestResourceStream(name))
{
if (s == null) { Plugin.L.LogInfo("No embedded sound: " + name); return null; }
var ms = new MemoryStream();
s.CopyTo(ms);
byte[] b = ms.ToArray();
int pos = 12, ch = 1, rate = 44100, bits = 16, fmt = 1, dataPos = -1, dataLen = 0;
while (pos + 8 <= b.Length)
{
string id = Encoding.ASCII.GetString(b, pos, 4);
int size = BitConverter.ToInt32(b, pos + 4);
if (id == "fmt ")
{
fmt = BitConverter.ToInt16(b, pos + 8);
ch = BitConverter.ToInt16(b, pos + 10);
rate = BitConverter.ToInt32(b, pos + 12);
bits = BitConverter.ToInt16(b, pos + 22);
}
else if (id == "data") { dataPos = pos + 8; dataLen = Math.Min(size, b.Length - dataPos); break; }
pos += 8 + size + (size % 2);
}
if (dataPos < 0) { Plugin.L.LogInfo(name + ": no data chunk"); return null; }
if (fmt != 1 && fmt != 65534) { Plugin.L.LogInfo(name + ": unsupported format " + fmt + " (need PCM)"); return null; }
if (bits != 16) { Plugin.L.LogInfo(name + ": need 16-bit, got " + bits); return null; }
int samples = dataLen / 2;
var f = new float[samples];
for (int i = 0; i < samples; i++) f[i] = BitConverter.ToInt16(b, dataPos + i * 2) / 32768f;
var clip = MakeClip(name, f, ch, rate);
Plugin.L.LogInfo("Loaded sound: " + name + " (" + ch + "ch, " + rate + "Hz, " + samples + " samples)");
return clip;
}
}
catch (Exception e) { Plugin.L.LogInfo("LoadWav error (" + name + "): " + e.Message); return null; }
}
AudioSource MakeLoopSource(string name, AudioClip clip, float volume)
{
var g = new GameObject(name);
UnityEngine.Object.DontDestroyOnLoad(g);
var src = g.AddComponent<AudioSource>();
src.clip = clip;
src.loop = true;
src.spatialBlend = 0f;
src.volume = volume;
src.ignoreListenerPause = true;
src.Play();
return src;
}
void KillSource(ref AudioSource src)
{
if (src == null) return;
try { src.Stop(); UnityEngine.Object.Destroy(src.gameObject); } catch { }
src = null;
}
void StopDroneSounds()
{
KillSource(ref buzzSrc);
KillSource(ref musicSrc);
}
void PlayMusic()
{
EnsureSounds();
StopDroneSounds();
if (ClipOk(buzzClip)) buzzSrc = MakeLoopSource("DroneBuzz", buzzClip, 1f);
if (ClipOk(musicClip)) musicSrc = MakeLoopSource("DroneMusic", musicClip, 0.8f);
}
void PlayBoom(Vector3 pos)
{
EnsureSounds();
if (!ClipOk(boomClip)) return;
var g = new GameObject("BoomSound");
UnityEngine.Object.DontDestroyOnLoad(g);
var src = g.AddComponent<AudioSource>();
src.clip = boomClip;
src.spatialBlend = 0f;
src.volume = 1f;
src.ignoreListenerPause = true;
src.Play();
UnityEngine.Object.Destroy(g, boomClip.length + 0.5f);
}
// ---------- основной цикл ----------
void Update()
{
if (Input.GetKeyDown(KeyCode.Insert)) open = !open;
if (Input.GetKeyDown(KeyCode.F6)) SetFly(!fly);
if (Input.GetKeyDown(KeyCode.F8)) SetDrone(!drone);
if (open) { Cursor.visible = true; Cursor.lockState = CursorLockMode.None; }
// обычный полёт
if (fly && !drone && player != null && Camera.main != null && !open)
{
var cam = Camera.main.transform;
Vector3 dir = Vector3.zero;
if (Input.GetKey(KeyCode.W)) dir += cam.forward;
if (Input.GetKey(KeyCode.S)) dir -= cam.forward;
if (Input.GetKey(KeyCode.D)) dir += cam.right;
if (Input.GetKey(KeyCode.A)) dir -= cam.right;
if (Input.GetKey(KeyCode.Space)) dir += Vector3.up;
if (Input.GetKey(KeyCode.LeftControl)) dir -= Vector3.up;
float sp = Input.GetKey(KeyCode.LeftShift) ? flySpeed * 3f : flySpeed;
player.position += dir.normalized * sp * Time.deltaTime;
}
// режим шахеда
if (drone && !exploding && player != null && Camera.main != null && !open)
{
var cam = Camera.main.transform;
Vector3 dir = cam.forward;
if (Input.GetKey(KeyCode.W)) droneSpeed += 25f * Time.deltaTime;
if (Input.GetKey(KeyCode.S)) droneSpeed -= 25f * Time.deltaTime;
droneSpeed = Mathf.Clamp(droneSpeed, 5f, 80f);
float dist = droneSpeed * Time.deltaTime + 0.6f;
bool hit = false;
var hits = Physics.SphereCastAll(player.position, 0.5f, dir, dist);
foreach (var h in hits)
{
if (h.collider == null || h.collider.isTrigger) continue;
if (h.transform == player || h.transform.IsChildOf(player)) continue;
hit = true;
lastHit = h.collider.name;
break;
}
if (hit && Time.time - droneStartTime > 1.5f)
{
if (droneSpeed >= crashSpeed) Explode(player.position);
}
else
{
player.position += dir * droneSpeed * Time.deltaTime;
}
player.Rotate(dir, spinSpeed * Time.deltaTime, Space.World);
}
// сфера взрыва
if (flash != null)
{
float k = (Time.time - explodeTime) / 0.6f;
if (k >= 1f) { UnityEngine.Object.Destroy(flash); flash = null; }
else flash.transform.localScale = Vector3.one * (2f + k * 14f);
}
// возврат на старт
if (exploding && Time.time >= respawnAt)
{
if (player != null) player.position = startPos;
exploding = false;
SetFly(false);
}
if ((god || stamina) && Time.time >= nextApply)
{
nextApply = Time.time + 0.25f;
ApplyStats(0);
}
}
void SetFly(bool value)
{
if (value == fly) return;
fly = value;
if (fly)
{
cc = FindObjectOfType<CharacterController>();
if (cc != null) { player = cc.transform; cc.enabled = false; }
else
{
var go = GameObject.FindWithTag("Player");
rb = go != null ? go.GetComponent<Rigidbody>() : null;
if (rb != null) { player = rb.transform; rb.isKinematic = true; }
}
if (player == null) { fly = false; Plugin.L.LogInfo("Player not found (no CharacterController / Player-tag Rigidbody)"); }
}
else
{
drone = false;
StopDroneSounds();
if (cc != null) cc.enabled = true;
if (rb != null) rb.isKinematic = false;
}
}
void SetDrone(bool value)
{
if (value)
{
SetFly(true);
if (!fly || player == null) return;
startPos = player.position;
droneStartTime = Time.time;
exploding = false;
drone = true;
PlayMusic();
}
else
{
drone = false;
StopDroneSounds();
}
}
void Explode(Vector3 pos)
{
exploding = true;
respawnAt = Time.time + 1.5f;
explodeTime = Time.time;
StopDroneSounds();
Plugin.L.LogInfo("EXPLODE at " + pos + ", hit: " + lastHit);
PlayBoom(pos);
try
{
if (flash != null) UnityEngine.Object.Destroy(flash);
flash = GameObject.CreatePrimitive(PrimitiveType.Sphere);
flash.transform.position = pos;
flash.transform.localScale = Vector3.one * 2f;
var col = flash.GetComponent<Collider>();
if (col != null) UnityEngine.Object.Destroy(col);
var rend = flash.GetComponent<Renderer>();
if (rend != null) rend.material.color = new Color(1f, 0.55f, 0.1f);
}
catch { }
var near = Physics.OverlapSphere(pos, 10f);
foreach (var c in near)
{
if (c == null) continue;
var body = c.attachedRigidbody;
if (body != null && body != rb) body.AddExplosionForce(800f, pos, 10f);
}
}
// ---------- здоровье и стамина ----------
Type ResolveType(MonoBehaviour mb)
{
string n = null;
try { n = mb.GetIl2CppType().FullName; } catch { }
if (n == null) return null;
Type t;
if (typeCache.TryGetValue(n, out t)) return t;
if (gameAsm == null)
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
if (a.GetName().Name == "Assembly-CSharp") { gameAsm = a; break; }
t = gameAsm != null ? gameAsm.GetType(n) : null;
typeCache[n] = t;
return t;
}
static string[] Words(string name)
{
string s = Regex.Replace(name, "([a-z0-9])([A-Z])", "$1 $2");
s = s.Replace("_", " ").ToLower();
return s.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
}
static bool HasAny(string[] words, string[] list)
{
foreach (var w in words) foreach (var l in list) if (w == l) return true;
return false;
}
// mode 0 = применить, mode 1 = записать найденное в лог
void ApplyStats(int mode)
{
int found = 0;
var all = UnityEngine.Object.FindObjectsOfType<MonoBehaviour>();
foreach (var mb in all)
{
if (mb == null) continue;
try
{
var t = ResolveType(mb);
if (t == null) continue;
object obj = Activator.CreateInstance(t, mb.Pointer);
var props = t.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var p in props)
{
if (!p.CanRead || !p.CanWrite) continue;
if (p.PropertyType != typeof(float) && p.PropertyType != typeof(int)) continue;
var w = Words(p.Name);
if (HasAny(w, BadWords)) continue;
bool isH = HasAny(w, HealthWords), isS = HasAny(w, StaminaWords);
if (!isH && !isS) continue;
object cur = p.GetValue(obj);
double curV = cur is float cf ? cf : (cur is int ci ? ci : 0);
if (mode == 1)
{
Plugin.L.LogInfo("FOUND: " + t.FullName + "." + p.Name + " = " + curV);
found++;
continue;
}
if (!(isH && god) && !(isS && stamina)) continue;
string key = t.FullName + "." + p.Name + "#" + mb.Pointer.ToString();
double b;
if (!best.TryGetValue(key, out b) || curV > b) { best[key] = curV; b = curV; }
if (curV < b)
{
if (p.PropertyType == typeof(float)) p.SetValue(obj, (float)b);
else p.SetValue(obj, (int)b);
if (loggedSet.Add(t.FullName + "." + p.Name))
Plugin.L.LogInfo("SET: " + t.FullName + "." + p.Name + " -> " + b);
}
found++;
}
}
catch { }
}
statsStatus = "полей найдено: " + found;
if (mode == 1) Plugin.L.LogInfo("Scan done, fields: " + found);
}
void DumpClasses()
{
var all = UnityEngine.Object.FindObjectsOfType<MonoBehaviour>();
var seen = new HashSet<string>();
foreach (var mb in all)
{
if (mb == null) continue;
try
{
string n = mb.GetIl2CppType().FullName;
if (seen.Add(n) && ResolveType(mb) != null) Plugin.L.LogInfo("CLASS: " + n);
}
catch { }
}
}
// ---------- спавн ----------
void SpawnByName(string name)
{
var all = Resources.FindObjectsOfTypeAll<GameObject>();
foreach (var go in all)
{
if (go == null || go.name != name) continue;
Vector3 pos = Vector3.zero;
if (Camera.main != null) pos = Camera.main.transform.position + Camera.main.transform.forward * 3f;
var copy = UnityEngine.Object.Instantiate(go, pos, Quaternion.identity);
copy.SetActive(true);
return;
}
}
// ---------- интерфейс ----------
void OnGUI()
{
// вспышка взрыва
float fk = (Time.time - explodeTime) / 0.8f;
if (fk >= 0f && fk < 1f)
{
GUI.color = new Color(1f, 0.6f, 0.1f, 1f - fk);
GUI.DrawTexture(new Rect(0, 0, Screen.width, Screen.height), Texture2D.whiteTexture);
GUI.color = Color.white;
}
// строка состояния
if (open || fly || drone || god || stamina)
{
string st = "Полёт:" + (fly ? "да" : "нет") + " Игрок:" + (player != null ? "найден" : "НЕТ")
+ " Шахед:" + (drone ? ("да, v=" + (int)droneSpeed) : "нет") + "\n"
+ "Удар:" + lastHit + " " + soundStatus + "\n" + statsStatus;
GUI.Label(new Rect(Screen.width - 520, 10, 510, 70), st);
}
if (!open) return;
float x = 20, y = 20, w = 320, h = 26;
GUI.Box(new Rect(10, 10, w + 20, 860), "Cheat Menu (Insert)");
y += 20;
bool nf = GUI.Toggle(new Rect(x, y, w, h), fly, " Полёт / ноклип (F6)"); y += h;
if (nf != fly) SetFly(nf);
GUI.Label(new Rect(x, y, w, 20), "Скорость полёта: " + (int)flySpeed); y += 20;
flySpeed = GUI.HorizontalSlider(new Rect(x, y, w, 20), flySpeed, 3f, 100f); y += 26;
bool nd = GUI.Toggle(new Rect(x, y, w, h), drone, " Режим шахеда (F8)"); y += h;
if (nd != drone) SetDrone(nd);
GUI.Label(new Rect(x, y, w, 20), "Скорость дрона: " + (int)droneSpeed + " (W/S)"); y += 20;
droneSpeed = GUI.HorizontalSlider(new Rect(x, y, w, 20), droneSpeed, 5f, 80f); y += 22;
GUI.Label(new Rect(x, y, w, 20), "Порог взрыва: " + (int)crashSpeed); y += 20;
crashSpeed = GUI.HorizontalSlider(new Rect(x, y, w, 20), crashSpeed, 5f, 80f); y += 22;
GUI.Label(new Rect(x, y, w, 20), "Вращение: " + (int)spinSpeed + " град/с"); y += 20;
spinSpeed = GUI.HorizontalSlider(new Rect(x, y, w, 20), spinSpeed, 0f, 1080f); y += 26;
if (GUI.Button(new Rect(x, y, 150, 24), "Тест взрыва"))
{
Vector3 p = Camera.main != null ? Camera.main.transform.position + Camera.main.transform.forward * 5f : Vector3.zero;
Explode(p);
exploding = false;
}
if (GUI.Button(new Rect(x + 170, y, 150, 24), "Тест музыки")) PlayMusic();
y += 28;
if (GUI.Button(new Rect(x, y, w, 24), "Стоп звук")) StopDroneSounds();
y += 32;
god = GUI.Toggle(new Rect(x, y, w, h), god, " Бессмертие"); y += h;
stamina = GUI.Toggle(new Rect(x, y, w, h), stamina, " Бесконечная стамина"); y += h;
if (GUI.Button(new Rect(x, y, w, 24), "Найти поля health/stamina (в лог)")) ApplyStats(1);
y += 28;
if (GUI.Button(new Rect(x, y, w, 24), "Список классов игры (в лог)")) DumpClasses();
y += 36;
GUI.Label(new Rect(x, y, w, h), "Сцены (индекс в билде):"); y += 22;
int count = SceneManager.sceneCountInBuildSettings;
for (int i = scenePage * 4; i < Math.Min(count, scenePage * 4 + 4); i++)
{
string path = SceneUtility.GetScenePathByBuildIndex(i);
string nm = Path.GetFileNameWithoutExtension(path);
if (GUI.Button(new Rect(x, y, w, 24), i + ": " + nm)) SceneManager.LoadScene(i);
y += 26;
}
if (GUI.Button(new Rect(x, y, 150, 24), "< Назад")) scenePage = Math.Max(0, scenePage - 1);
if (GUI.Button(new Rect(x + 170, y, 150, 24), "Вперёд >")) { if ((scenePage + 1) * 4 < count) scenePage++; }
y += 34;
GUI.Label(new Rect(x, y, w, h), "Спавн: введите часть имени"); y += 22;
filter = GUI.TextField(new Rect(x, y, w, 24), filter); y += 28;
if (filter.Length >= 2)
{
int shown = 0;
var all = Resources.FindObjectsOfTypeAll<GameObject>();
foreach (var go in all)
{
if (go == null || shown >= 5) break;
if (go.name.ToLower().Contains(filter.ToLower()))
{
if (GUI.Button(new Rect(x, y, w, 24), go.name)) SpawnByName(go.name);
y += 26; shown++;
}
}
}
}
}