C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Core.Plugins;
using Oxide.Core.Libraries.Covalence;
using System.Linq;
using System.Collections.Generic;
using Newtonsoft.Json;
using System.Text.RegularExpressions;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Minicopter Drone", "Andrew", "1.2.2")]
[Description("Turns the minicopter into a programmable drone")]
public class MiniCopterDrone : RustPlugin {
static MiniCopterDrone plugin = null;
DroneManager droneManager = null;
static ConfigData config;
static Compiler compiler = null;
const string calibratePerm = "minicopterdrone.calibrate.allowed";
const int maxAvgCharsPerLine = 32;
class ConfigData {
[JsonProperty(PropertyName = "maxProgramInstructions")]
public int maxProgramInstructions;
[JsonProperty(PropertyName = "gridPositionCorrection")]
public Vector3 gridPositionCorrection;
[JsonProperty(PropertyName = "maxInstructionsPerCycle")]
public int maxInstructionsPerCycle;
[JsonProperty(PropertyName = "gridSize")]
public float gridSize;
[JsonProperty(PropertyName = "droneCPUFreq")]
public float droneCPUFreq;
}
protected override void LoadDefaultMessages() {
lang.RegisterMessages(new Dictionary<string, string> {
["must_run_as_player"] = "this command must be run as a player",
//["need_calibrate_perm"] = "error: you need the {0} permission to use this command",
["grid_pos_corr_saved"] = "grid position correction saved",
["calibrate_invalid_arg"] = "invalid argument format <grid_col><grid_row> ex: J13",
["var_already_declared"] = "line {0}: variable already declared ({1})",
["invalid_label"] = "line {0}: invalid label ({1})",
["var_not_declared"] = "line {0}: variable has not been declared ({1})",
["incompat_arg_type"] = "line {0}: incompatible argument type ({1}:{2}) for instruction ({3})",
["invalid_arg"] = "line {0}: invalid argument ({1}) for ({2}) spec: {2} {3}",
["invalid_instruction"] = "line {0}: invalid instruction ({1})",
["wrong_num_args"] = "line {0}: wrong number of arguments for ({1}) spec: {1} {2}",
["over_char_limit"] = "max program length is {0} characters",
["over_instruction_limit"] = "max program length is {0} instructions",
["compiler_error"] = "[error] {0}",
["no_prefab"] = "Error creating prefab \"{0}\""
}, this);
}
protected override void LoadDefaultConfig() {
var config = new ConfigData {
maxProgramInstructions = 512,
gridPositionCorrection = new Vector3(1, 0, 75),
maxInstructionsPerCycle = 32,
gridSize = 146.33f,
droneCPUFreq = 10
};
Config.WriteObject(config, true);
}
void Init() {
config = Config.ReadObject<ConfigData>();
plugin = this;
compiler = new Compiler();
AddCovalenceCommand("minicopterdrone.calibrate", nameof(Calibrate), calibratePerm);
}
void Calibrate(IPlayer player, string command, string[] args) {
if(player.Object == null) {
Puts(lang.GetMessage("must_run_as_player", this));
return;
}
Vector3 pos;
if(args.Length == 1 && TryMapGridToPosition(args[0], out pos, false)) {
var basePlayer = player.Object as BasePlayer;
var actualPos = basePlayer.transform.position;
config.gridPositionCorrection = new Vector3(actualPos.x, 0, actualPos.z) - pos;
Config.WriteObject(config, true);
player.Reply(lang.GetMessage("grid_pos_corr_saved", this, player.Id));
} else {
player.Reply(lang.GetMessage("calibrate_invalid_arg", this, player.Id));
}
}
static void Print(string str) {
Interface.Oxide.LogInfo(str, new object[]{});
}
class DroneManager : MonoBehaviour {
public const string Guid = "918729b6-ca44-46c6-8ad6-1722abff10d4";
Dictionary<uint, Drone> drones = new Dictionary<uint, Drone>();
public static HashSet<int> activeFrequencies = new HashSet<int>();
public static HashSet<int> risingEdgeFrequencies = new HashSet<int>();
public static HashSet<int> fallingEdgeFrequencies = new HashSet<int>();
List<int> removeActiveList = new List<int>();
List<uint> removeDroneList = new List<uint>();
public void Init() {
float period = 1.0f / config.droneCPUFreq;
InvokeRepeating(nameof(CycleDroneCPUs), period, period);
}
void CycleDroneCPUs() {
if(drones.Count == 0) {
return;
}
risingEdgeFrequencies.Clear();
fallingEdgeFrequencies.Clear();
removeActiveList.Clear();
removeDroneList.Clear();
foreach(var freq in RFManager._broadcasters) {
if(freq.Value.Count > 0) {
if(!activeFrequencies.Contains(freq.Key)) {
risingEdgeFrequencies.Add(freq.Key);
activeFrequencies.Add(freq.Key);
}
}
}
foreach(var freq in activeFrequencies) {
List<IRFObject> list;
if(!RFManager._broadcasters.TryGetValue(freq, out list)) {
removeActiveList.Add(freq);
fallingEdgeFrequencies.Add(freq);
} else {
if(list.Count == 0) {
removeActiveList.Add(freq);
fallingEdgeFrequencies.Add(freq);
}
}
}
foreach(var freq in removeActiveList) {
activeFrequencies.Remove(freq);
}
foreach(var value in drones) {
var drone = value.Value;
if(!drone.miniCopterRef.Get(true)) {
removeDroneList.Add(value.Key);
continue;
}
if(drone.active) {
drone.CycleCPU();
}
}
foreach(var id in removeDroneList) {
drones.Remove(id);
}
}
void FixedUpdate() {
foreach(var value in drones) {
var drone = value.Value;
if(!drone.miniCopterRef.Get(true)) {
continue;
}
if(drone.active) {
drone.FixedUpdate();
}
}
}
public Drone AddDrone(MiniCopter miniCopter, StorageContainer storage) {
var drone = new Drone();
drone.miniCopterRef.Set(miniCopter);
drone.instanceId = miniCopter.net.ID;
drone.storage = storage;
drone.manager = this;
drones.Add(drone.instanceId, drone);
return drone;
}
public void OnItemAddedOrRemoved(MiniCopter miniCopter, StorageContainer storage, Item item, bool added) {
Drone drone = null;
if(!drones.TryGetValue(miniCopter.net.ID, out drone)) {
drone = AddDrone(miniCopter, storage);
}
drone.OnItemAddedOrRemoved(storage, item, added);
}
void Reset() {
foreach(var drone in drones) {
drone.Value.Reset();
}
}
}
class DroneCPU {
List<Compiler.Instruction> instructions = new List<Compiler.Instruction>();
public Dictionary<string, int> isrs = new Dictionary<string, int>();
public Queue<string> interrupts = new Queue<string>();
public List<int> picStack = new List<int>();
int pic = 0;
bool abort = false;
string abortReason = null;
Dictionary<string, Compiler.Instruction.Argument> numVariables = new Dictionary<string, Compiler.Instruction.Argument>();
List<float> stack = new List<float>();
public int maxStackSize = 64;
public void Reset() {
pic = 0;
interrupts.Clear();
picStack.Clear();
numVariables.Clear();
}
public void LoadInstructions(List<Compiler.Instruction> instructions) {
this.instructions = instructions;
isrs.Clear();
interrupts.Clear();
picStack.Clear();
numVariables.Clear();
stack.Clear();
pic = 0;
for(int i = 0; i < instructions.Count; i++) {
var instr = instructions[i];
if(instr.name == "isr") {
isrs.Add(instr.args[0].stringValue, i);
} else if(instr.name == "num") {
numVariables.Add(instr.args[0].stringValue, instr.args[0]);
}
}
}
public void Jump(int addr) {
pic = addr;
}
public void Call(int addr) {
picStack.Add(pic);
pic = addr;
}
public bool Ret() {
if(picStack.Count == 0) {
return false;
}
Jump(picStack[picStack.Count - 1]);
picStack.RemoveAt(picStack.Count - 1);
return true;
}
public void Interrupt(string name) {
if(isrs.ContainsKey(name)) {
interrupts.Enqueue(name);
}
}
public bool WriteVariable(string name, float value) {
Compiler.Instruction.Argument arg;
if(numVariables.TryGetValue(name, out arg)) {
arg.floatValue = value;
arg.intValue = (int)value;
return true;
}
return false;
}
public bool HandlePendingInterrupt() {
if(interrupts.Count == 0) {
return false;
}
var isr = interrupts.Dequeue();
int addr;
isrs.TryGetValue(isr, out addr);
Call(addr);
return true;
}
public void Abort(string reason) {
abort = true;
abortReason = reason;
}
public bool Cycle(out Compiler.Instruction instr, out string failReason) {
if(instructions == null || pic < 0 || pic >= instructions.Count) {
instr = null;
failReason = "out of instructions";
return false;
}
if(abort) {
instr = null;
failReason = abortReason;
return false;
}
instr = instructions[pic++];
// if maybe one arg instruction
if(instr.args.Count == 1) {
bool isOneOfThese = true;
switch(instr.name) {
case "jmp":
Jump(instr.args[0].intValue);
break;
case "call":
Call(instr.args[0].intValue);
break;
case "int":
Interrupt(instr.args[0].stringValue);
break;
case "ret":
if(!Ret()) {
failReason = "no address to return from";
return false;
}
break;
case "push":
if(stack.Count == maxStackSize) {
failReason = $"maximum stack size exceeded ({maxStackSize})";
return false;
}
stack.Add(instr.args[0].floatValue);
break;
default:
isOneOfThese = false;
break;
}
if(isOneOfThese) {
failReason = null;
return true;
}
}
// if maybe instruction that assigns to variable as first arg
if(instr.args.Count > 0 && instr.args[0].paramType == Compiler.ParamType.NumVariable) {
bool isOneOfThese = true;
float result = 0;
switch(instr.name) {
case "pop":
if(stack.Count == 0) {
failReason = "stack empty";
return false;
}
result = stack[stack.Count - 1];
stack.RemoveAt(stack.Count - 1);
break;
case "mov":
result = instr.args[1].floatValue;
break;
default:
isOneOfThese = false;
break;
}
if(isOneOfThese) {
instr.args[0].floatValue = result;
instr.args[0].intValue = (int)result;
failReason = null;
return true;
}
}
// if might be one arg math instruction
if(instr.args.Count == 1 && instr.args[0].paramType == Compiler.ParamType.Num) {
var valueArg = instr.args[0];
float result = 0;
bool isOneOfThese = true;
try {
switch(instr.name) {
case "abs":
result = Mathf.Abs(valueArg.floatValue);
break;
case "sign":
result = Mathf.Sign(valueArg.floatValue);
break;
case "sqrt":
result = Mathf.Sqrt(valueArg.floatValue);
break;
case "round":
result = Mathf.Round(valueArg.floatValue);
break;
case "floor":
result = Mathf.Floor(valueArg.floatValue);
break;
case "ceil":
result = Mathf.Ceil(valueArg.floatValue);
break;
default:
isOneOfThese = false;
break;
}
if(isOneOfThese) {
if(!float.IsFinite(result)) {
failReason = $"math error: {instr.name} {String.Join(" ", instr.args.Select(x => x.rawValue))} => {result}";
return false;
}
WriteVariable("rslt", result);
failReason = null;
return true;
}
} catch(ArithmeticException e) {
failReason = e.ToString();
return false;
}
}
// if might be two arg math instruction
if(instr.args.Count == 2 && instr.args[0].paramType == Compiler.ParamType.Num && instr.args[1].paramType == Compiler.ParamType.Num) {
var lhsArg = instr.args[0];
var rhsArg = instr.args[1];
float result = 0;
bool isOneOfThese = true;
try {
switch(instr.name) {
case "add":
result = lhsArg.floatValue + rhsArg.floatValue;
break;
case "sub":
result = lhsArg.floatValue - rhsArg.floatValue;
break;
case "mul":
result = lhsArg.floatValue * rhsArg.floatValue;
break;
case "div":
result = lhsArg.floatValue / rhsArg.floatValue;
break;
case "pow":
result = Mathf.Pow(lhsArg.floatValue, rhsArg.floatValue);
break;
case "min":
result = Mathf.Min(lhsArg.floatValue, rhsArg.floatValue);
break;
case "max":
result = Mathf.Max(lhsArg.floatValue, rhsArg.floatValue);
break;
default:
isOneOfThese = false;
break;
}
if(isOneOfThese) {
if(!float.IsFinite(result)) {
failReason = $"math error: {instr.name} {String.Join(" ", instr.args.Select(x => x.rawValue))} => {result}";
return false;
}
WriteVariable("rslt", result);
failReason = null;
return true;
}
} catch(ArithmeticException e) {
failReason = e.ToString();
return false;
}
}
if(instr.name == "lerp") {
float result = Mathf.Lerp(instr.args[0].floatValue, instr.args[1].floatValue, instr.args[2].floatValue);
WriteVariable("rslt", result);
failReason = null;
return true;
}
// if might be conditional jump instruction
if(instr.args.Count == 3 && instr.args[0].paramType == Compiler.ParamType.Num && instr.args[1].paramType == Compiler.ParamType.Num && instr.args[2].paramType == Compiler.ParamType.Address) {
float lhs = instr.args[0].floatValue;
float rhs = instr.args[1].floatValue;
int addr = instr.args[2].intValue;
bool jump = false;
switch(instr.name) {
case "ja":
jump = Mathf.Approximately(lhs, rhs);
break;
case "je":
jump = lhs == rhs;
break;
case "jne":
jump = lhs != rhs;
break;
case "jna":
jump = !Mathf.Approximately(lhs, rhs);
break;
case "jg":
jump = lhs > rhs;
break;
case "jge":
jump = lhs >= rhs;
break;
case "jl":
jump = lhs < rhs;
break;
case "jle":
jump = lhs <= rhs;
break;
}
if(jump) {
Jump(addr);
failReason = null;
return true;
}
}
failReason = null;
return true;
}
}
class Drone {
public DroneManager manager;
public EntityRef miniCopterRef;
public uint instanceId;
public DroneCPU cpu = new DroneCPU();
public StorageContainer storage = null;
public PIDController.Angular cyclicController = new PIDController.Angular {
p = 0.5f,
i = 0,
d = 1,
dRC = 0.5f
};
public PIDController.Linear throttleController = new PIDController.Linear {
p = 1f,
i = 0,
d = 1f,
spRC = 1f
};
public PIDController.Vector headingController = new PIDController.Vector {
p = 1f,
i = 0,
d = 2.5f,
spRC = 1.0f,
dRC = 0.5f
};
[Flags]
public enum Flag {
EngineOn = 1 << 0,
EngineStarting = 1 << 1,
HasTarget = 1 << 2,
HasTargetAltitude = 1 << 3,
Flying = 1 << 4,
ExecutingInstruction = 1 << 5,
Landing = 1 << 6,
SeekingTarget = 1 << 7,
SeekingTargetFlythrough = 1 << 8,
SeekingTargetAltitude = 1 << 9
}
public Flag flags;
List<Vector3> targetStack = new List<Vector3>();
List<float> targetAltStack = new List<float>();
bool HasFlag(Flag f) {
return (this.flags & f) == f;
}
public void SetFlag(Flag f, bool set) {
if(set) {
this.flags |= f;
return;
}
this.flags &= ~f;
}
public bool active = false;
Compiler.Instruction currentInstruction = null;
float desiredAltitude = 0;
Vector3 target = Vector3.zero;
float sleepTime = -1;
float engineStartTime = -1;
float landingSpeed = 0;
float landingTime;
float desiredPitch = 0;
int waitRF;
public void Reset() {
active = false;
currentInstruction = null;
desiredAltitude = 0;
target = Vector3.zero;
sleepTime = -1;
flags = 0;
landingSpeed = 0;
desiredPitch = 0;
var copter = miniCopterRef.Get(true) as MiniCopter;
engineStartTime = -1;
if(copter) {
StopEngine();
UpdateEngine();
}
targetStack.Clear();
targetAltStack.Clear();
cpu.Reset();
}
float GetTerrainHeight(Vector3 position) {
return Mathf.Max(TerrainMeta.HeightMap.GetHeight(position), TerrainMeta.WaterMap.GetHeight(position));
}
float GetAltitude(Vector3 position) {
return position.y - GetTerrainHeight(position);
}
public void OnItemAddedOrRemoved(StorageContainer storage, Item item, bool added) {
string message;
if(item.info.shortname == "note" && item.text != null) {
var maxProgramCharLength = config.maxProgramInstructions * maxAvgCharsPerLine;
if(item.text.Length > maxProgramCharLength) {
message = string.Format(plugin.lang.GetMessage("over_char_limit", plugin), maxProgramCharLength);
message = string.Format(plugin.lang.GetMessage("compiler_error", plugin), message);
item.text += "\n" + message;
return;
}
var lines = item.text.ToLower().Split(new[] {"\r\n", "\r", "\n", ";"}, StringSplitOptions.RemoveEmptyEntries);
if(lines.Length >= 1) {
if(lines[0] == "#droneasm") {
if(added) {
if(lines.Length < config.maxProgramInstructions) {
Reset();
bool success = compiler.Compile(item.text);
if(!success) {
item.text += "\n" + String.Join("\n", compiler.errors.Select(x => {
return string.Format(plugin.lang.GetMessage("compiler_error", plugin), x);
}));
} else {
cpu.LoadInstructions(compiler.instructions);
active = true;
}
} else {
message = string.Format(plugin.lang.GetMessage("over_instruction_limit", plugin), config.maxProgramInstructions);
message = string.Format(plugin.lang.GetMessage("compiler_error", plugin), message);
item.text += "\n" + message;
}
} else {
Reset();
}
}
}
}
}
void ControlAltitude(float desiredAltitude, float currentAltitude) {
var copter = miniCopterRef.Get(true) as MiniCopter;
var throttleControl = throttleController.Update(desiredAltitude, currentAltitude, Time.fixedDeltaTime);
float bias = (copter.rigidBody.mass * Physics.gravity.magnitude) / copter.engineThrustMax;
throttleControl = Mathf.Clamp(throttleControl + bias, -1.0f, 1.0f);
Vector3 copterUp = copter.transform.rotation * Vector3.up;
Vector3 thrust = copterUp * (copter.engineThrustMax * throttleControl);
copter.rigidBody.AddForce(thrust, ForceMode.Force);
}
void CyclicControl(Vector3 desired) {
var copter = miniCopterRef.Get(true) as MiniCopter;
var pv = copter.transform.rotation * Vector3.up;
var control = cyclicController.Update(desired, pv, Time.fixedDeltaTime);
if(control.magnitude > 1e-6f) {
copter.rigidBody.AddTorque(control, ForceMode.Acceleration);
}
}
Vector3 HeadingControl(Vector3 target, float desiredPitch) {
var copter = miniCopterRef.Get(true) as MiniCopter;
var headingControl = headingController.Update(target, copter.transform.position, Time.fixedDeltaTime);
float pitchAngle = Mathf.Clamp(headingControl.magnitude, -desiredPitch, desiredPitch);
headingControl = headingControl.normalized * pitchAngle;
var axis = Vector3.Cross(Vector3.up, headingControl).normalized;
var rot = Quaternion.AngleAxis(pitchAngle, axis);
return rot * Vector3.up;
}
void TailFinTorque() {
var copter = miniCopterRef.Get(true) as MiniCopter;
var tailForce = Vector3.Project(copter.GetWorldVelocity(), copter.transform.right);
var dot = Vector3.Dot(copter.GetWorldVelocity(), copter.transform.right);
tailForce *= tailForce.magnitude;
copter.rigidBody.AddRelativeTorque(new Vector3(0, tailForce.magnitude * Mathf.Sign(dot), 0), ForceMode.Force);
}
void StartEngine() {
var copter = miniCopterRef.Get(true) as MiniCopter;
if(!HasFlag(Flag.EngineOn) && !HasFlag(Flag.EngineStarting) && !copter.Waterlogged()) {
engineStartTime = Time.fixedTime;
SetFlag(Flag.EngineStarting, true);
copter.EngineStartup();
}
}
void StopEngine() {
var copter = miniCopterRef.Get(true) as MiniCopter;
SetFlag(Flag.EngineOn, false);
SetFlag(Flag.EngineStarting, false);
copter.EngineOff();
}
void UpdateEngine() {
var copter = miniCopterRef.Get(true) as MiniCopter;
if(!copter.HasFuel()) {
StopEngine();
}
if(HasFlag(Flag.EngineStarting) && Time.fixedTime > engineStartTime + 5.0f) {
SetFlag(Flag.EngineOn, true);
SetFlag(Flag.EngineStarting, false);
}
if(HasFlag(Flag.EngineOn)) {
copter.UseFuel(Time.fixedDeltaTime);
if(!copter.HasFlag(MiniCopter.Flag_EngineStart)) {
copter.SetFlag(MiniCopter.Flag_EngineStart, true);
}
}
}
void ResetControl() {
headingController.Reset();
throttleController.Reset();
cyclicController.Reset();
}
void Fly() {
var copter = miniCopterRef.Get(true) as MiniCopter;
if(!copter) {
return;
}
var currentAltitude = GetAltitude(copter.transform.position);
if(HasFlag(Flag.Landing)) {
this.desiredAltitude = Mathf.Max(0, this.desiredAltitude - (this.landingSpeed * Time.fixedDeltaTime));
}
this.target.y = this.desiredAltitude + GetTerrainHeight(this.target);
if(HasFlag(Flag.EngineOn)) {
if(HasFlag(Flag.Flying)) {
if(HasFlag(Flag.HasTargetAltitude)) {
ControlAltitude(desiredAltitude, currentAltitude);
}
Vector3 copterUp = Vector3.up;
if(HasFlag(Flag.HasTarget)) {
copterUp = HeadingControl(this.target, this.desiredPitch);
}
CyclicControl(copterUp);
}
}
}
bool TryGetRFPosition(int freq, Vector3 closestTo, out Vector3 position) {
var list = new List<IRFObject>();
if(RFManager._broadcasters.TryGetValue(freq, out list)) {
if(list.Count == 0) {
position = Vector3.zero;
return false;
}
float closest = float.MaxValue;
Vector3 pos = Vector3.zero;
foreach(var irfObject in list) {
float dist = (irfObject.GetPosition() - closestTo).magnitude;
if(dist <= irfObject.GetMaxRange()) {
if(dist < closest) {
pos = irfObject.GetPosition();
closest = dist;
}
}
}
if(closest != float.MaxValue) {
position = pos;
return true;
}
}
position = Vector3.zero;
return false;
}
public void FixedUpdate() {
MiniCopter copter = miniCopterRef.Get(true) as MiniCopter;
if(!copter) {
Reset();
return;
}
UpdateEngine();
TailFinTorque();
if(!active) {
StopEngine();
return;
}
Fly();
}
public void CycleCPU() {
MiniCopter copter = miniCopterRef.Get(true) as MiniCopter;
if(!copter) {
Reset();
return;
}
var currentAltitude = GetAltitude(copter.transform.position);
foreach(var freq in DroneManager.risingEdgeFrequencies) {
string isrName = "rfa" + freq;
cpu.Interrupt(isrName);
}
foreach(var freq in DroneManager.fallingEdgeFrequencies) {
string isrName = "rfna" + freq;
cpu.Interrupt(isrName);
}
if(HasFlag(Flag.Landing)) {
if(currentAltitude < 2 || (Time.fixedTime - this.landingTime > 5.0f && copter.rigidBody.velocity.magnitude < 0.1f)) {
SetFlag(Flag.Landing, false);
SetFlag(Flag.Flying, false);
cpu.Interrupt("landed");
}
}
if(HasFlag(Flag.HasTarget) && HasFlag(Flag.SeekingTarget)) {
Vector3 offset = this.target - copter.transform.position;
if(offset.magnitude < 1 && copter.rigidBody.velocity.magnitude < 1) {
SetFlag(Flag.SeekingTarget, false);
cpu.Interrupt("at_target");
}
}
if(HasFlag(Flag.HasTarget) && HasFlag(Flag.SeekingTargetFlythrough)) {
Vector3 offset = this.target - copter.transform.position;
if(offset.magnitude < 10.0f) {
SetFlag(Flag.SeekingTargetFlythrough, false);
cpu.Interrupt("almost_at_target");
}
}
if(HasFlag(Flag.HasTargetAltitude) && HasFlag(Flag.SeekingTargetAltitude)) {
if(Mathf.Abs(currentAltitude - this.desiredAltitude) < 1 && copter.rigidBody.velocity.magnitude < 1) {
SetFlag(Flag.SeekingTargetAltitude, false);
cpu.Interrupt("at_altitude");
}
}
// this checks if the executing instruction has finished
if(this.currentInstruction != null && HasFlag(Flag.ExecutingInstruction)) {
switch(this.currentInstruction.name) {
case "sleep":
if(Time.fixedTime > this.sleepTime) {
this.sleepTime = -1;
SetFlag(Flag.ExecutingInstruction, false);
}
break;
case "startengine":
if(HasFlag(Flag.EngineOn)) {
SetFlag(Flag.ExecutingInstruction, false);
}
break;
case "waitrf":
if(DroneManager.activeFrequencies.Contains(this.waitRF)) {
SetFlag(Flag.ExecutingInstruction, false);
}
break;
case "land":
if(!HasFlag(Flag.Landing)) {
SetFlag(Flag.ExecutingInstruction, false);
}
break;
case "flyto":
if(!HasFlag(Flag.SeekingTarget)) {
SetFlag(Flag.ExecutingInstruction, false);
}
break;
case "flythrough":
if(!HasFlag(Flag.SeekingTargetFlythrough)) {
SetFlag(Flag.ExecutingInstruction, false);
}
break;
}
}
if(cpu.HandlePendingInterrupt()) {
SetFlag(Flag.ExecutingInstruction, false);
}
int numInstructionsExecuted = 0;
string failReason;
while(numInstructionsExecuted < config.maxInstructionsPerCycle && !HasFlag(Flag.ExecutingInstruction)) {
// these need to stay synced
this.target.y = this.desiredAltitude + GetTerrainHeight(this.target);
if(!cpu.Cycle(out this.currentInstruction, out failReason)) {
Reset();
return;
}
numInstructionsExecuted++;
switch(this.currentInstruction.name) {
case "sleep":
var seconds = currentInstruction.args[0].floatValue;
if(this.sleepTime == -1) {
this.sleepTime = Time.fixedTime + seconds;
}
SetFlag(Flag.ExecutingInstruction, true);
break;
case "startengine":
if(!HasFlag(Flag.EngineStarting)) {
StartEngine();
SetFlag(Flag.ExecutingInstruction, true);
}
break;
case "stopengine":
StopEngine();
break;
case "target":
var mapCol = currentInstruction.args[0].floatValue;
var mapRow = currentInstruction.args[1].floatValue;
var desiredAltitude = currentInstruction.args[2].floatValue;
Vector3 pos1;
pos1 = MapGridToPosition(mapCol, mapRow);
this.target = pos1;
this.desiredAltitude = desiredAltitude;
SetFlag(Flag.HasTarget, true);
SetFlag(Flag.HasTargetAltitude, true);
ResetControl();
break;
case "targetalt":
this.desiredAltitude = currentInstruction.args[0].floatValue;
SetFlag(Flag.HasTargetAltitude, true);
ResetControl();
break;
case "targetrf":
var freq = currentInstruction.args[0].intValue;
Vector3 pos2;
if(TryGetRFPosition(freq, copter.transform.position, out pos2)) {
this.target = pos2;
this.desiredAltitude = this.target.y - GetTerrainHeight(this.target);
SetFlag(Flag.HasTarget, true);
SetFlag(Flag.HasTargetAltitude, true);
ResetControl();
}
break;
case "targethere":
this.target = copter.transform.position;
this.desiredAltitude = this.target.y - GetTerrainHeight(this.target);
SetFlag(Flag.HasTarget, true);
SetFlag(Flag.HasTargetAltitude, true);
ResetControl();
break;
case "land":
var speed = currentInstruction.args[0].floatValue;
if(HasFlag(Flag.Flying)) {
this.landingSpeed = speed;
this.landingTime = Time.fixedTime;
SetFlag(Flag.Landing, true);
SetFlag(Flag.ExecutingInstruction, true);
}
break;
case "flyto":
case "flythrough":
if(HasFlag(Flag.HasTarget) || HasFlag(Flag.HasTargetAltitude)) {
SetFlag(Flag.Flying, true);
SetFlag(Flag.ExecutingInstruction, true);
if(HasFlag(Flag.HasTarget)) {
SetFlag(Flag.SeekingTarget, true);
}
if(HasFlag(Flag.HasTargetAltitude)) {
SetFlag(Flag.SeekingTargetAltitude, false);
}
}
break;
case "drop":
var slot = currentInstruction.args[0].intValue;
for(int i = 0; i < storage.inventory.itemList.Count; i++) {
var item = storage.inventory.itemList[i];
if(item.position == slot) {
Vector3 randomVector = new Vector3(UnityEngine.Random.Range(-0.5f, 0.5f), 0, UnityEngine.Random.Range(-0.5f, 0.5f));
item.Drop(storage.GetDropPosition() + Vector3.down * 2 + randomVector, storage.GetDropVelocity());
}
}
break;
case "pushtarget":
targetStack.Add(this.target);
if(targetStack.Count > 32) {
Reset();
return;
}
break;
case "poptarget":
if(targetStack.Count == 0) {
Reset();
return;
}
this.target = targetStack[targetStack.Count - 1];
targetStack.RemoveAt(targetStack.Count - 1);
ResetControl();
break;
case "pushtargetalt":
targetAltStack.Add(this.desiredAltitude);
if(targetAltStack.Count > 32) {
Reset();
return;
}
break;
case "poptargetalt":
if(targetAltStack.Count == 0) {
Reset();
return;
}
this.desiredAltitude = targetAltStack[targetAltStack.Count - 1];
targetAltStack.RemoveAt(targetAltStack.Count - 1);
ResetControl();
break;
case "gettarget":
var stackPos = this.currentInstruction.args[0].intValue;
var xVar = this.currentInstruction.args[1].stringValue;
var yVar = this.currentInstruction.args[2].stringValue;
var zVar = this.currentInstruction.args[3].stringValue;
Vector3 targetToUse = Vector3.zero;
if(stackPos == -1) {
targetToUse = this.target;
} else {
if(this.targetStack.Count >= stackPos + 1) {
targetToUse = this.targetStack[stackPos];
}
}
cpu.WriteVariable(xVar, targetToUse.x);
cpu.WriteVariable(yVar, targetToUse.y);
cpu.WriteVariable(zVar, targetToUse.z);
break;
case "getalt":
var stackPos2 = this.currentInstruction.args[0].intValue;
var altVar = this.currentInstruction.args[1].stringValue;
float altToUse = 0;
if(stackPos2 == -1) {
altToUse = this.desiredAltitude;
} else {
if(this.targetAltStack.Count >= stackPos2 + 1) {
altToUse = this.targetAltStack[stackPos2];
}
}
cpu.WriteVariable(altVar, altToUse);
break;
case "settarget":
var stackPos3 = this.currentInstruction.args[0].intValue;
var xVal = this.currentInstruction.args[1].floatValue;
var yVal = this.currentInstruction.args[2].floatValue;
var zVal = this.currentInstruction.args[3].floatValue;
if(stackPos3 == -1) {
this.target = new Vector3(xVal, yVal, zVal);
this.desiredAltitude = this.target.y - GetTerrainHeight(this.target);
SetFlag(Flag.HasTarget, true);
SetFlag(Flag.HasTargetAltitude, true);
ResetControl();
} else {
if(this.targetStack.Count >= stackPos3 + 1) {
this.targetStack[stackPos3] = new Vector3(xVal, yVal, zVal);
this.desiredAltitude = this.target.y - GetTerrainHeight(this.target);
SetFlag(Flag.HasTarget, true);
SetFlag(Flag.HasTargetAltitude, true);
// ... [Code preview truncated due to length. Full source in attached file] ...