using UnityEngine; using System.Collections.Generic; using System; using Oxide.Core; using ProtoBuf; namespace Oxide.Plugins { [Info("AutoElevators", "bmgjet", "1.0.0")] [Description("Replaces elevators placed in rust with with working one.")] public class AutoElevators : RustPlugin { private static SaveData _data; //Save Data for between server restarts private const string USE_PERM = "AutoElevators.use"; //Permission required for /elevator chat commands public List ElevatorUp = new List(); //Local settings file same as saved data public float ScanRadius = 0.8f; //Radius to use when buttons scan for elevator. class SaveData { public List ElevatorUp = new List(); } private void WriteSaveData() => Interface.Oxide.DataFileSystem.WriteObject(Name, _data); private void Init() { permission.RegisterPermission(USE_PERM, this); if (!Interface.Oxide.DataFileSystem.ExistsDatafile(Name)) { Interface.Oxide.DataFileSystem.GetDatafile(Name).Save(); } _data = Interface.Oxide.DataFileSystem.ReadObject(Name); if (_data == null) { WriteSaveData(); } } void OnServerInitialized() { //Delay in loading data for when server restarts some times elevators wouldnt be fully spawned in. timer.Once(5f, () => { ReloadData(); }); } void OnServerSave() { DoSave(); } void DoSave() { _data.ElevatorUp = ElevatorUp; WriteSaveData(); } void Unload() { ClearElevators(); try { DoSave(); _data = null; } catch { } } public void ReloadData() { ClearElevators(); //create new elevators bool flag = false; List update = new List(); foreach (PrefabData pd in World.Serialization.world.prefabs) { if (pd.id == 3845190333) { Elevator el = CreateElevator(pd.position, pd.rotation); if (el == null) { Puts("Fault creating elevator!"); } foreach (Esettings es in _data.ElevatorUp) { //Check for settings if (es.pos == el.transform.position) { //create with set settings update.Add(el.net.ID); _data.ElevatorUp.Add(new Esettings(el.transform.position, el.transform.rotation.eulerAngles, el.net.ID, es.Floors, es.ReturnTime, es.AutoReturn, es.Speed, es.Direction, es.Custompos)); flag = true; break; } } if (!flag) { //create new with default settings _data.ElevatorUp.Add(new Esettings(el.transform.position, el.transform.rotation.eulerAngles, el.net.ID)); } el.SendNetworkUpdateImmediate(); } } //clean up old netids from last session if (flag) { int count = _data.ElevatorUp.Count; for (int c = 0; c < count; c++) { if (!update.Contains(_data.ElevatorUp[c].netid)) { _data.ElevatorUp.Remove(_data.ElevatorUp[c]); c--; count--; } } } Puts("ReloadData"); WriteSaveData(); //sync save data to local data. ElevatorUp = _data.ElevatorUp; } void resetdata() { //Clears data for a fresh start/map Puts("Resettings datafile"); ElevatorUp.Clear(); _data.ElevatorUp.Clear(); WriteSaveData(); ReloadData(); } public void ClearElevators() { //Delete any exsisting elevators int test = 0; foreach (BaseNetworkable bn in BaseNetworkable.serverEntities) { if (bn.prefabID == 3845190333) { //Scan area to make sure not train entance var hits = Physics.SphereCastAll(bn.transform.position, 6f, Vector3.down); bool train = false; foreach (var hit in hits) { if (hit.GetEntity()?.prefabID == 1802909967) { test++; train = true; break; } } if (!train) { //remove elevator if not train entrance since will be replaced by plugin. bn.Kill(); } } } Puts("Train Elevators Found " + test.ToString()); } private List FindElevator(Vector3 pos, float radius, Vector3 dir) { var hits = Physics.SphereCastAll(pos, radius, dir); var x = new List(); foreach (var hit in hits) { var entity = hit.GetEntity()?.GetComponent(); if (entity && !x.Contains(entity)) x.Add(entity); } return x; } public Elevator CreateElevator(Vector3 pos, Vector3 rot) { //Recreate elevators that are workable Elevator newElevator = GameManager.server.CreateEntity("assets/prefabs/deployable/elevator/static/elevator.static.prefab", pos + new Vector3(0, -1, 0), new Quaternion(rot.x, rot.y, rot.z, 0), true) as Elevator; newElevator.Spawn(); newElevator.SetFlag(BaseEntity.Flags.Reserved1, true, false, true); newElevator.SetFlag(Elevator.Flag_HasPower, true); return newElevator; } public Esettings FindServerElevator(uint id) { foreach (Esettings bn in ElevatorUp) { if (bn.netid == id) { return bn; } } return null; } public int FindElevatorIndex(uint id) { int index = 0; foreach (Esettings bn in ElevatorUp) { if (bn.netid == id) { return index; } index++; } return -1; } object OnElevatorMove(Elevator e) { //Elevator buttons trigger this if (e.OwnerID == 0) { ElevatorLogic(e); } return null; } object OnButtonPress(PressButton button, BasePlayer player) { if (button.OwnerID == 0) //Faster exit so doesnt scan player placed buttons { for (int i = 0; i < 2; i++) { Vector3 dir; //Checks up and down path incase button is above or below elevator. if (i == 0) { dir = Vector3.down; } else { dir = Vector3.up; } List e = FindElevator(button.transform.position, ScanRadius, dir); foreach (Elevator elevator in e) { Esettings es = FindServerElevator(elevator.net.ID); if (es != null) { //Found elevator trigger it. ElevatorLogic(elevator, es); return null; } } } } return null; } void ElevatorLogic(Elevator e, Esettings ThisElevator = null) { //Check if its a plugin replaced elevator if (ThisElevator == null) { ThisElevator = FindServerElevator(e.net.ID); } if (ThisElevator != null) { //Change floors if it is //Check if elevator already triggered if (!ThisElevator.up) { GoToFloor(ThisElevator.Floors, e, ThisElevator); } else { //return to base height GoToFloor(1, e, ThisElevator); } } } void GoToFloor(int floor, Elevator e, Esettings eset) { if (e.HasFlag(BaseEntity.Flags.Busy)) { //Already moving so ignore command return; } //Set elevator speed e.LiftSpeedPerMetre = eset.Speed; //Set up base variables Vector3 vector = new Vector3(0, 1, 0); float timeToTravel = 0f; switch (eset.Direction) { //Up Down case 0: vector = e.transform.InverseTransformPoint(e.transform.position + new Vector3(0, floor, 0)); timeToTravel = e.TimeToTravelDistance(Mathf.Abs(e.liftEntity.transform.localPosition.y + vector.y)); LeanTween.moveLocalY(e.liftEntity.gameObject, vector.y, timeToTravel); break; //Forward Back case 1: vector = e.transform.InverseTransformPoint(e.transform.position + new Vector3(floor, 0, 0)); timeToTravel = e.TimeToTravelDistance(Mathf.Abs(e.liftEntity.transform.localPosition.x + vector.x)); LeanTween.moveLocalX(e.liftEntity.gameObject, vector.x, timeToTravel); break; //Side To Side case 2: vector = e.transform.InverseTransformPoint(e.transform.position + new Vector3(0, 0, floor)); timeToTravel = e.TimeToTravelDistance(Mathf.Abs(e.liftEntity.transform.localPosition.z + vector.z)); LeanTween.moveLocalZ(e.liftEntity.gameObject, vector.z, timeToTravel); break; case 3: //Custom position if (!eset.up) { vector = e.transform.InverseTransformPoint(e.transform.position + eset.Custompos); } timeToTravel = e.TimeToTravelDistance(Vector3.Distance(e.transform.position, vector)); LeanTween.moveLocal(e.liftEntity.gameObject, vector, timeToTravel); break; case 4: //Rotate X vector = e.transform.InverseTransformPoint(e.transform.rotation.eulerAngles + new Vector3(floor, 0, 0)); timeToTravel = e.TimeToTravelDistance(Mathf.Abs(e.liftEntity.transform.rotation.eulerAngles.x + vector.x)); if (!eset.up) { LeanTween.rotateX(e.liftEntity.gameObject, vector.x, timeToTravel); } else { LeanTween.rotateX(e.liftEntity.gameObject, 0, timeToTravel); } break; case 5: //Rotate Y vector = e.transform.InverseTransformPoint(e.transform.rotation.eulerAngles + new Vector3(0, floor, 0)); timeToTravel = e.TimeToTravelDistance(Mathf.Abs(e.liftEntity.transform.rotation.eulerAngles.y + vector.y)); if (!eset.up) { LeanTween.rotateY(e.liftEntity.gameObject, vector.y, timeToTravel); } else { LeanTween.rotateY(e.liftEntity.gameObject, 0, timeToTravel); } break; case 6: //Rotate Z vector = e.transform.InverseTransformPoint(e.transform.rotation.eulerAngles + new Vector3(0, 0, floor)); timeToTravel = e.TimeToTravelDistance(Mathf.Abs(e.liftEntity.transform.rotation.eulerAngles.z + vector.z)); if (!eset.up) { LeanTween.rotateZ(e.liftEntity.gameObject, vector.z, timeToTravel); } else { LeanTween.rotateZ(e.liftEntity.gameObject, 0, timeToTravel); } break; case 7: //Throw player off vector = e.transform.InverseTransformPoint(e.transform.position + new Vector3(floor, floor, floor)); timeToTravel = e.TimeToTravelDistance(Mathf.Abs(e.liftEntity.transform.localPosition.y + vector.y)); LeanTween.moveLocalY(e.liftEntity.gameObject, vector.z, timeToTravel); if (!eset.up) { LeanTween.rotateZ(e.liftEntity.gameObject, 180, timeToTravel); } else { LeanTween.rotateZ(e.liftEntity.gameObject, 0, timeToTravel); } break; } e.SendNetworkUpdateImmediate(); //Set busy flag e.SetFlag(global::BaseEntity.Flags.Busy, true, false, true); //set timer to disable busy flag e.Invoke(new Action(e.ClearBusy), timeToTravel); eset.up = !eset.up; //Set up auto return based on delay. if (eset.AutoReturn) { timer.Once(eset.ReturnTime + timeToTravel, () => { if (eset.up) { GoToFloor(1, e, eset); } }); } } //Chat setup commands [ChatCommand("elevator")] private void SetSettings(BasePlayer player, string command, string[] args) { if (player == null || !player.IPlayer.HasPermission(USE_PERM)) return; if (args == null || args.Length == 0 || args.Length == 1 || args.Length >= 3) { player.ChatMessage("Help:\r\nYou must provide settings such as\r\n/elevator arg value\r\nSettings:\r\nfloors int (use neg to go down)\r\nreturn bool (true/flase auto return)\r\ndelay int (sec before return)\r\nspeed float (speed elevator moves)\r\ndirection int (0 = y, 1 = x, 2 = z)\r\nposition f|f|f (Vector to move to)\r\npos get (prints out players vector)\r\nsave yes (saves elevator settings straight away)\r\nreset yes (resets all server elevators)"); return; } if (ElevatorUp.Count == 0) { player.ChatMessage("_data file not found"); return; } if (args[0] == "reset" && args[1] == "yes") { player.ChatMessage("Resetting Data"); resetdata(); return; } if (args[0] == "pos" && args[1] == "get") { player.ChatMessage("You are currently @ " + player.transform.position.ToString()); return; } if (args[0] == "save" && args[1] == "yes") { DoSave(); player.ChatMessage("Settings Saved"); return; } int index = -1; Elevator entity = null; List e = FindElevator(player.transform.position, 0.8f, Vector3.down); foreach (Elevator elevator in e) { Esettings es = FindServerElevator(elevator.net.ID); if (es != null) { index = FindElevatorIndex(elevator.net.ID); entity = elevator; if (es.up) { player.ChatMessage("Elevator must be at its base height to change setting"); return; } break; } } if (entity == null) { player.ChatMessage("No Elevator Found, Stand on it and look down"); return; } if (index >= ElevatorUp.Count || index == -1) { player.ChatMessage("Cant find index"); return; } switch (args[0]) { case "floors": try { int newfloors = int.Parse(args[1]); if (newfloors == 1 || newfloors == 0) { newfloors = 2; } ElevatorUp[index].Floors = newfloors; player.ChatMessage("Changed floors to " + newfloors.ToString()); return; } catch { player.ChatMessage("Must set a number as floors"); return; } case "return": try { bool newreturn = Boolean.Parse(args[1]); ElevatorUp[index].AutoReturn = newreturn; player.ChatMessage("Changed Auto return to " + newreturn.ToString()); return; } catch { player.ChatMessage("Must set true / false"); return; } case "delay": try { int newdelay = int.Parse(args[1]); if (newdelay < 5) { player.ChatMessage("Use a value greater than 5"); return; } ElevatorUp[index].ReturnTime = newdelay; player.ChatMessage("Changed Auto return delay to " + newdelay.ToString() + " sec"); return; } catch { player.ChatMessage("Must set a number as seconds of delay"); return; } case "speed": try { float newspeed = (float)double.Parse(args[1]); if (newspeed <= 0) { player.ChatMessage("Use a value greater than 1"); return; } ElevatorUp[index].Speed = newspeed; player.ChatMessage("Changed elevator speed to " + newspeed.ToString() + " per m"); return; } catch { player.ChatMessage("Must set a float as speed per m"); return; } case "direction": try { int newdirection = int.Parse(args[1]); if (newdirection >= 0 && newdirection <= 7) { ElevatorUp[index].Direction = newdirection; player.ChatMessage("Changed elevator direction"); return; } player.ChatMessage("Use a value of 0-7"); return; } catch { player.ChatMessage("Must set a int as direction 0 = y, 1 = x, 2 = z"); return; } case "position": try { string[] customposition = args[1].Split('|'); ElevatorUp[index].Custompos = new Vector3(float.Parse(customposition[0]), float.Parse(customposition[1]), float.Parse(customposition[2])); player.ChatMessage("Custom positions set"); return; } catch { player.ChatMessage("Please provide a vector floats seperated by | such as x|y|z"); return; } } } public class Esettings { public Esettings() { } public Esettings(Vector3 p, Vector3 r, uint n) { this.pos = p; this.rot = r; this.netid = n; this.Floors = 2; this.ReturnTime = 60; this.AutoReturn = true; this.Speed = 2f; this.Direction = 0; this.Custompos = new Vector3(0, 0, 0); this.up = false; } public Esettings(Vector3 p, Vector3 r, uint n, int f, int rt, bool at, float s, int d, Vector3 c) { this.pos = p; this.rot = r; this.netid = n; this.Floors = f; this.ReturnTime = rt; this.AutoReturn = at; this.Speed = s; this.Direction = d; this.Custompos = c; this.up = false; } public uint netid; public Vector3 pos; public Vector3 rot; public int Floors; public int ReturnTime; public bool AutoReturn; public float Speed; public int Direction; public Vector3 Custompos; public bool up; } } }