600 lines
24 KiB
C#
600 lines
24 KiB
C#
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<Esettings> ElevatorUp = new List<Esettings>(); //Local settings file same as saved data
|
|
public float ScanRadius = 0.8f; //Radius to use when buttons scan for elevator.
|
|
|
|
class SaveData
|
|
{
|
|
public List<Esettings> ElevatorUp = new List<Esettings>();
|
|
}
|
|
|
|
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<SaveData>(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<uint> update = new List<uint>();
|
|
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<Elevator> FindElevator(Vector3 pos, float radius, Vector3 dir)
|
|
{
|
|
var hits = Physics.SphereCastAll(pos, radius, dir);
|
|
var x = new List<Elevator>();
|
|
foreach (var hit in hits)
|
|
{
|
|
var entity = hit.GetEntity()?.GetComponent<Elevator>();
|
|
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<Elevator> 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<Elevator> 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;
|
|
}
|
|
}
|
|
} |