C#:
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("MLRSDriveable", "YourName", "1.2.0")]
[Description("Позволяет игрокам ездить на MLRS. Использует подтверждённые методы API.")]
public class MLRSDriveable : RustPlugin
{
private const string PermUse = "mlrsdriveable.use";
private const string PermSpawn = "mlrsdriveable.spawn";
private const string PermAdmin = "mlrsdriveable.admin";
// prefab подтверждён авто-определением
private const string MLRSPrefab = "assets/content/vehicles/mlrs/mlrs.entity.prefab";
// Словари: кто в каком MLRS едет
private readonly Dictionary<ulong, MLRS> _drivingPlayers = new Dictionary<ulong, MLRS>();
private readonly Dictionary<NetworkableId, BasePlayer> _mlrsDrivers = new Dictionary<NetworkableId, BasePlayer>();
#region Инициализация
void Init()
{
permission.RegisterPermission(PermUse, this);
permission.RegisterPermission(PermSpawn, this);
permission.RegisterPermission(PermAdmin, this);
}
void Unload()
{
foreach (var pair in new Dictionary<ulong, MLRS>(_drivingPlayers))
{
BasePlayer player = null;
foreach (var p in BasePlayer.activePlayerList)
if (p.userID == pair.Key) { player = p; break; }
if (player != null)
ExitMLRS(player, pair.Value);
}
_drivingPlayers.Clear();
_mlrsDrivers.Clear();
}
#endregion
#region Hooks
void OnEntityKill(MLRS mlrs)
{
if (mlrs == null) return;
if (_mlrsDrivers.TryGetValue(mlrs.net.ID, out var driver))
{
_drivingPlayers.Remove(driver.userID);
_mlrsDrivers.Remove(mlrs.net.ID);
SendReply(driver, lang.GetMessage("MLRSDestroyed", this, driver.UserIDString));
}
}
object OnPlayerInput(BasePlayer player, InputState input)
{
if (player == null || input == null) return null;
if (_drivingPlayers.ContainsKey(player.userID))
HandleDriving(player, input);
return null;
}
#endregion
#region Команды
[ChatCommand("mlrs")]
void CmdMLRS(BasePlayer player, string cmd, string[] args)
{
if (!permission.UserHasPermission(player.UserIDString, PermUse))
{
SendReply(player, lang.GetMessage("NoPermission", this, player.UserIDString));
return;
}
string sub = args.Length > 0 ? args[0].ToLower() : "";
switch (sub)
{
case "enter": case "войти":
TryEnterNearestMLRS(player);
break;
case "exit": case "выйти":
TryExitMLRS(player);
break;
case "spawn": case "спавн":
if (permission.UserHasPermission(player.UserIDString, PermSpawn))
SpawnMLRS(player);
else
SendReply(player, lang.GetMessage("NoPermission", this, player.UserIDString));
break;
default:
SendReply(player, lang.GetMessage("Help", this, player.UserIDString));
break;
}
}
#endregion
#region Посадка / Высадка
void TryEnterNearestMLRS(BasePlayer player)
{
if (_drivingPlayers.ContainsKey(player.userID))
{
SendReply(player, lang.GetMessage("AlreadyDriving", this, player.UserIDString));
return;
}
var mlrs = FindNearestMLRS(player.transform.position, 5f);
if (mlrs == null)
{
SendReply(player, lang.GetMessage("NoMLRSNearby", this, player.UserIDString));
return;
}
if (_mlrsDrivers.ContainsKey(mlrs.net.ID))
{
SendReply(player, lang.GetMessage("MLRSOccupied", this, player.UserIDString));
return;
}
EnterMLRS(player, mlrs);
}
void TryExitMLRS(BasePlayer player)
{
if (!_drivingPlayers.TryGetValue(player.userID, out var mlrs))
{
SendReply(player, lang.GetMessage("NotDriving", this, player.UserIDString));
return;
}
ExitMLRS(player, mlrs);
}
void EnterMLRS(BasePlayer player, MLRS mlrs)
{
_drivingPlayers[player.userID] = mlrs;
_mlrsDrivers[mlrs.net.ID] = player;
try
{
// MLRS сам является BaseMountable — MountPlayer(BasePlayer, Vector3)
mlrs.MountPlayer(player);
Puts($"[MLRSDriveable] {player.displayName} сел в MLRS через MountPlayer");
}
catch (Exception ex)
{
Puts($"[MLRSDriveable] MountPlayer ошибка: {ex.Message} — fallback");
FallbackEnter(player, mlrs);
}
SendReply(player, lang.GetMessage("Entered", this, player.UserIDString));
SendReply(player, lang.GetMessage("Controls", this, player.UserIDString));
}
void FallbackEnter(BasePlayer player, MLRS mlrs)
{
// Позиция в кабине: чуть вперёд и выше центра MLRS
Vector3 seatPos = mlrs.transform.position
+ mlrs.transform.forward * 0.5f
+ Vector3.up * 2.3f;
try
{
player.transform.SetParent(mlrs.transform);
player.MovePosition(seatPos, true); // true = forceUpdateTriggers
Puts($"[MLRSDriveable] Fallback посадка: {player.displayName} → {seatPos}");
}
catch (Exception ex)
{
Puts($"[MLRSDriveable] Fallback ошибка: {ex.Message}");
player.transform.position = seatPos;
}
}
void ExitMLRS(BasePlayer player, MLRS mlrs)
{
if (player == null) return;
_drivingPlayers.Remove(player.userID);
if (mlrs != null) _mlrsDrivers.Remove(mlrs.net.ID);
try
{
// DismountPlayer подтверждён авто-определением
mlrs?.DismountPlayer(player);
Puts($"[MLRSDriveable] {player.displayName} вышел через DismountPlayer");
}
catch (Exception ex)
{
Puts($"[MLRSDriveable] DismountPlayer ошибка: {ex.Message} — fallback");
FallbackExit(player, mlrs);
}
SendReply(player, lang.GetMessage("Exited", this, player.UserIDString));
}
void FallbackExit(BasePlayer player, MLRS mlrs)
{
try { player.transform.SetParent(null); } catch { }
if (mlrs != null)
{
Vector3 exitPos = mlrs.transform.position + mlrs.transform.right * 3f + Vector3.up * 0.5f;
try { player.MovePosition(exitPos, true); }
catch { player.transform.position = exitPos; }
}
}
#endregion
#region Движение
void HandleDriving(BasePlayer player, InputState input)
{
if (!_drivingPlayers.TryGetValue(player.userID, out var mlrs)) return;
if (mlrs == null || mlrs.IsDestroyed) { ExitMLRS(player, null); return; }
// E — выйти
if (input.WasJustPressed(BUTTON.USE))
{
ExitMLRS(player, mlrs);
return;
}
bool w = input.IsDown(BUTTON.FORWARD);
bool s = input.IsDown(BUTTON.BACKWARD);
bool a = input.IsDown(BUTTON.LEFT);
bool d = input.IsDown(BUTTON.RIGHT);
if (!w && !s && !a && !d) return;
float speed = 5f; // м/с
float rotSpeed = 45f; // градусов/с
// Rigidbody не найден — двигаем через transform
if (w) mlrs.transform.position += mlrs.transform.forward * speed * Time.deltaTime;
if (s) mlrs.transform.position -= mlrs.transform.forward * speed * Time.deltaTime;
if (a) mlrs.transform.Rotate(Vector3.up, -rotSpeed * Time.deltaTime, Space.World);
if (d) mlrs.transform.Rotate(Vector3.up, rotSpeed * Time.deltaTime, Space.World);
// Синхронизируем позицию со всеми клиентами
mlrs.SendNetworkUpdateImmediate();
// Обновляем позицию игрока вместе с машиной
if (player.HasParent()) return; // уже прикреплён через MountPlayer — не нужно
Vector3 newSeat = mlrs.transform.position + mlrs.transform.forward * 0.5f + Vector3.up * 2.3f;
try { player.MovePosition(newSeat, false); } catch { }
}
#endregion
#region Спавн
void SpawnMLRS(BasePlayer player)
{
Vector3 pos = player.transform.position + player.transform.forward * 6f;
Quaternion rot = Quaternion.Euler(0, player.transform.eulerAngles.y, 0);
var ent = GameManager.server.CreateEntity(MLRSPrefab, pos, rot);
if (ent == null)
{
SendReply(player, lang.GetMessage("SpawnFailed", this, player.UserIDString));
Puts($"[MLRSDriveable] CreateEntity вернул null для prefab: {MLRSPrefab}");
return;
}
ent.Spawn();
SendReply(player, lang.GetMessage("Spawned", this, player.UserIDString));
Puts($"[MLRSDriveable] {player.displayName} заспавнил MLRS на {pos}");
}
#endregion
#region Хелперы
MLRS FindNearestMLRS(Vector3 pos, float radius)
{
MLRS nearest = null;
float minDist = float.MaxValue;
foreach (var entity in BaseNetworkable.serverEntities)
{
var mlrs = entity as MLRS;
if (mlrs == null) continue;
float dist = Vector3.Distance(pos, mlrs.transform.position);
if (dist < radius && dist < minDist) { minDist = dist; nearest = mlrs; }
}
return nearest;
}
#endregion
#region Локализация
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["NoPermission"] = "❌ Нет прав для этой команды.",
["Help"] = "📋 /mlrs enter — сесть | /mlrs exit — выйти | /mlrs spawn — заспавнить",
["NoMLRSNearby"] = "❌ Нет MLRS рядом (радиус 5м).",
["MLRSOccupied"] = "❌ Этот MLRS уже занят.",
["AlreadyDriving"] = "❌ Ты уже управляешь MLRS. Используй /mlrs exit.",
["NotDriving"] = "❌ Ты не управляешь MLRS.",
["Entered"] = "✅ Ты сел в MLRS!",
["Controls"] = "🎮 W/S — движение | A/D — поворот | E — выйти",
["Exited"] = "✅ Ты вышел из MLRS.",
["MLRSDestroyed"] = "💥 MLRS уничтожен!",
["Spawned"] = "✅ MLRS заспавнен перед тобой!",
["SpawnFailed"] = "❌ Не удалось заспавнить MLRS. Проверь консоль.",
}, this, "ru");
lang.RegisterMessages(new Dictionary<string, string>
{
["NoPermission"] = "❌ You don't have permission.",
["Help"] = "📋 /mlrs enter — mount | /mlrs exit — dismount | /mlrs spawn — spawn",
["NoMLRSNearby"] = "❌ No MLRS nearby (5m radius).",
["MLRSOccupied"] = "❌ This MLRS is already occupied.",
["AlreadyDriving"] = "❌ You are already driving an MLRS. Use /mlrs exit.",
["NotDriving"] = "❌ You are not driving an MLRS.",
["Entered"] = "✅ You entered the MLRS!",
["Controls"] = "🎮 W/S — move | A/D — turn | E — exit",
["Exited"] = "✅ You exited the MLRS.",
["MLRSDestroyed"] = "💥 Your MLRS was destroyed!",
["Spawned"] = "✅ MLRS spawned in front of you!",
["SpawnFailed"] = "❌ Failed to spawn MLRS. Check console.",
}, this, "en");
}
#endregion
}
}