diff --git a/Code/Exceptions/RetroReulFuckedUpException.cs b/Code/Exceptions/RetroReulFuckedUpException.cs new file mode 100644 index 0000000..14b9ad8 --- /dev/null +++ b/Code/Exceptions/RetroReulFuckedUpException.cs @@ -0,0 +1,12 @@ +namespace SER.Code.Exceptions; + +public class RetroReulFuckedUpException : DeveloperFuckedUpException +{ + public RetroReulFuckedUpException() : base("retroreul") + { + } + + public RetroReulFuckedUpException(string msg) : base("retroreul", msg) + { + } +} \ No newline at end of file diff --git a/Code/MethodSystem/Methods/AudioMethods/SpeakerExistsMethod.cs b/Code/MethodSystem/Methods/AudioMethods/SpeakerExistsMethod.cs new file mode 100644 index 0000000..c877e3f --- /dev/null +++ b/Code/MethodSystem/Methods/AudioMethods/SpeakerExistsMethod.cs @@ -0,0 +1,23 @@ +using JetBrains.Annotations; +using SER.Code.ArgumentSystem.Arguments; +using SER.Code.ArgumentSystem.BaseArguments; +using SER.Code.MethodSystem.BaseMethods.Synchronous; +using SER.Code.ValueSystem; + +namespace SER.Code.MethodSystem.Methods.AudioMethods; + +[UsedImplicitly] +public class SpeakerExistsMethod : ReturningMethod +{ + public override string Description => "Returns true or false indicating if a speaker with the provided name exists."; + + public override Argument[] ExpectedArguments { get; } = + [ + new TextArgument("speaker name") + ]; + + public override void Execute() + { + ReturnValue = AudioPlayer.TryGet(Args.GetText("speaker name"), out _); + } +} \ No newline at end of file diff --git a/Code/MethodSystem/Methods/DoorMethods/DoorInfoMethod.cs b/Code/MethodSystem/Methods/DoorMethods/DoorInfoMethod.cs index 519bfb2..a313be3 100644 --- a/Code/MethodSystem/Methods/DoorMethods/DoorInfoMethod.cs +++ b/Code/MethodSystem/Methods/DoorMethods/DoorInfoMethod.cs @@ -33,6 +33,8 @@ public class DoorInfoMethod : LiteralValueReturningMethod, IReferenceResolvingMe "isClosed", "isLocked", "isUnlocked", + "isGate", + "isCheckpoint", Option.Enum("name"), "unityName", "remainingHealth", @@ -54,6 +56,8 @@ public override void Execute() "isclosed" => new BoolValue(!door.IsOpened), "islocked" => new BoolValue(door.IsLocked), "isunlocked" => new BoolValue(!door.IsLocked), + "isgate" => new BoolValue(door is Gate), + "ischeckpoint" => new BoolValue(door is CheckpointDoor), "remaininghealth" => new NumberValue(door is BreakableDoor bDoor ? (decimal)bDoor.Health : -1), "maxhealth" => new NumberValue(door is BreakableDoor bDoor ? (decimal)bDoor.MaxHealth : -1), "permissions" => new StaticTextValue(door.Permissions.ToString()), diff --git a/Code/MethodSystem/Methods/DoorMethods/PryGateMethod.cs b/Code/MethodSystem/Methods/DoorMethods/PryGateMethod.cs new file mode 100644 index 0000000..ee28f7c --- /dev/null +++ b/Code/MethodSystem/Methods/DoorMethods/PryGateMethod.cs @@ -0,0 +1,45 @@ +using JetBrains.Annotations; +using LabApi.Features.Wrappers; +using MapGeneration.Distributors; +using SER.Code.ArgumentSystem.Arguments; +using SER.Code.ArgumentSystem.BaseArguments; +using SER.Code.MethodSystem.BaseMethods.Synchronous; + +namespace SER.Code.MethodSystem.Methods.DoorMethods; + +[UsedImplicitly] +public class PryGateMethod : SynchronousMethod +{ + public override string Description => "Pries a gate."; + + public override Argument[] ExpectedArguments => + [ + new DoorArgument("gate"), + new BoolArgument("should play effects") + { + DefaultValue = new(false, "does not play button effects"), + Description = "Whether to play gate button effects when pried." + } + ]; + + public override void Execute() + { + var door = Args.GetDoor("gate"); + var playEffects = Args.GetBool("should play effects"); + + if (door is not Gate gate) return; + + if (gate.IsOpened || gate.ExactState != 0f || gate.Base.IsBeingPried) return; + + if (playEffects) + { + gate.PlayPermissionDeniedAnimation(); + gate.PlayLockBypassDeniedSound(); + } + + // Spawn pickups in case a player goes through the gate. This should not duplicate pickups if the gate gets opened properly later. + SpawnablesDistributorBase.ServerSpawnForAllDoor(gate.Base); + + gate.Pry(); + } +} \ No newline at end of file diff --git a/Code/MethodSystem/Methods/EffectMethods/ClearEffectMethod.cs b/Code/MethodSystem/Methods/EffectMethods/ClearEffectMethod.cs new file mode 100644 index 0000000..f738cf1 --- /dev/null +++ b/Code/MethodSystem/Methods/EffectMethods/ClearEffectMethod.cs @@ -0,0 +1,39 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using JetBrains.Annotations; +using SER.Code.ArgumentSystem.Arguments; +using SER.Code.ArgumentSystem.BaseArguments; +using SER.Code.Helpers; +using SER.Code.MethodSystem.BaseMethods.Synchronous; +using SER.Code.MethodSystem.MethodDescriptors; +using SER.Code.MethodSystem.Structures; + +namespace SER.Code.MethodSystem.Methods.EffectMethods; + +[UsedImplicitly] +public class ClearEffectMethod : SynchronousMethod, IDependOnFramework +{ + public override string Description => "Removes the provided status effect from players."; + + public override Argument[] ExpectedArguments => + [ + new PlayersArgument("players"), + new EnumArgument("effect type") + { DefaultValue = new (null, "Removes all status effects") }, + ]; + + public override void Execute() + { + var players = Args.GetPlayers("players"); + var effectType = Args.GetNullableEnum("effect type"); + + if (effectType.HasValue) + foreach (var plr in players) + Player.Get(plr).DisableEffect(effectType.Value); + else + foreach (var plr in players) + Player.Get(plr).DisableAllEffects(); + } + + public FrameworkBridge.Type DependsOn => FrameworkBridge.Type.Exiled; +} \ No newline at end of file diff --git a/Code/MethodSystem/Methods/EffectMethods/EffectInfoMethod.cs b/Code/MethodSystem/Methods/EffectMethods/EffectInfoMethod.cs new file mode 100644 index 0000000..7c70beb --- /dev/null +++ b/Code/MethodSystem/Methods/EffectMethods/EffectInfoMethod.cs @@ -0,0 +1,50 @@ +using CustomPlayerEffects; +using JetBrains.Annotations; +using SER.Code.ArgumentSystem.Arguments; +using SER.Code.ArgumentSystem.BaseArguments; +using SER.Code.Exceptions; +using SER.Code.MethodSystem.BaseMethods.Synchronous; +using SER.Code.MethodSystem.MethodDescriptors; +using SER.Code.ValueSystem; + +namespace SER.Code.MethodSystem.Methods.EffectMethods; + +[UsedImplicitly] +public class EffectInfoMethod : LiteralValueReturningMethod, IReferenceResolvingMethod +{ + public override string Description => IReferenceResolvingMethod.Desc.Get(this); + + public override Argument[] ExpectedArguments => + [ + new ReferenceArgument("effect"), + new OptionsArgument("info", options: + [ + new("name"), + new("duration"), + new("intensity"), + new("classification"), + new("timeLeft") + ]) + ]; + public override void Execute() + { + var effect = Args.GetReference("effect"); + ReturnValue = Args.GetOption("info") switch + { + "name" => new StaticTextValue(effect.name), + "duration" => new DurationValue(TimeSpan.FromSeconds(effect.Duration)), + "intensity" => new NumberValue(effect.Intensity), + "classification" => new StaticTextValue(effect.Classification.ToString()), + "timeleft" => new DurationValue(TimeSpan.FromSeconds(effect.TimeLeft)), + _ => throw new RetroReulFuckedUpException() + }; + } + + public override TypeOfValue LiteralReturnTypes => new TypesOfValue([ + typeof(TextValue), + typeof(NumberValue), + typeof(DurationValue) + ]); + + public Type ResolvesReference => typeof(StatusEffectBase); +} \ No newline at end of file diff --git a/Code/MethodSystem/Methods/PlayerMethods/GiveEffectMethod.cs b/Code/MethodSystem/Methods/EffectMethods/GiveEffectMethod.cs similarity index 82% rename from Code/MethodSystem/Methods/PlayerMethods/GiveEffectMethod.cs rename to Code/MethodSystem/Methods/EffectMethods/GiveEffectMethod.cs index a386d91..3381a76 100644 --- a/Code/MethodSystem/Methods/PlayerMethods/GiveEffectMethod.cs +++ b/Code/MethodSystem/Methods/EffectMethods/GiveEffectMethod.cs @@ -7,13 +7,13 @@ using SER.Code.MethodSystem.BaseMethods.Synchronous; using SER.Code.MethodSystem.Structures; -namespace SER.Code.MethodSystem.Methods.PlayerMethods; +namespace SER.Code.MethodSystem.Methods.EffectMethods; [UsedImplicitly] public class GiveEffectMethod : SynchronousMethod, IDependOnFramework { public FrameworkBridge.Type DependsOn => FrameworkBridge.Type.Exiled; - + public override string Description => "Adds a provided effect to a player."; public override Argument[] ExpectedArguments => @@ -40,9 +40,11 @@ public override void Execute() var effectType = Args.GetEnum("effect type"); var duration = (float)Args.GetDuration("duration").TotalSeconds; var intensity = (byte)Args.GetInt("intensity"); - - players.ForEach(plr - => Player.Get(plr).EnableEffect(effectType, intensity, duration) - ); + var addDurationIfActive = Args.GetBool("add duration if active"); + + foreach (var plr in players) + { + Player.Get(plr).EnableEffect(effectType, intensity, duration, addDurationIfActive); + } } } \ No newline at end of file diff --git a/Code/MethodSystem/Methods/EffectMethods/HasEffectMethod.cs b/Code/MethodSystem/Methods/EffectMethods/HasEffectMethod.cs new file mode 100644 index 0000000..fcbd284 --- /dev/null +++ b/Code/MethodSystem/Methods/EffectMethods/HasEffectMethod.cs @@ -0,0 +1,39 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using JetBrains.Annotations; +using SER.Code.ArgumentSystem.Arguments; +using SER.Code.ArgumentSystem.BaseArguments; +using SER.Code.Helpers; +using SER.Code.MethodSystem.BaseMethods.Synchronous; +using SER.Code.MethodSystem.Structures; +using SER.Code.ValueSystem; + +namespace SER.Code.MethodSystem.Methods.EffectMethods; + +[UsedImplicitly] +public class HasEffectMethod : LiteralValueReturningMethod, IDependOnFramework +{ + public override TypeOfValue LiteralReturnTypes => new SingleTypeOfValue(typeof(BoolValue)); + + public override string Description => "Returns true or false indicating if the player has the provided effect."; + + public override Argument[] ExpectedArguments => + [ + new PlayerArgument("player"), + new EnumArgument("effect type") + ]; + + public override void Execute() + { + var player = Args.GetPlayer("player"); + var effectType = Args.GetEnum("effect type"); + + if (!Player.Get(player).TryGetEffect(effectType, out var effect)) + ReturnValue = new BoolValue(false); + + // this feels kinda stupid, but you never know... + ReturnValue = new BoolValue(effect.IsEnabled); + } + + public FrameworkBridge.Type DependsOn => FrameworkBridge.Type.Exiled; +} \ No newline at end of file diff --git a/Code/MethodSystem/Methods/ElevatorMethods/ElevatorInfoMethod.cs b/Code/MethodSystem/Methods/ElevatorMethods/ElevatorInfoMethod.cs new file mode 100644 index 0000000..2056786 --- /dev/null +++ b/Code/MethodSystem/Methods/ElevatorMethods/ElevatorInfoMethod.cs @@ -0,0 +1,53 @@ +using JetBrains.Annotations; +using LabApi.Features.Wrappers; +using SER.Code.ArgumentSystem.Arguments; +using SER.Code.ArgumentSystem.BaseArguments; +using SER.Code.Exceptions; +using SER.Code.MethodSystem.BaseMethods.Synchronous; +using SER.Code.MethodSystem.MethodDescriptors; +using SER.Code.ValueSystem; + +namespace SER.Code.MethodSystem.Methods.ElevatorMethods; + +[UsedImplicitly] +public class ElevatorInfoMethod : LiteralValueReturningMethod, IReferenceResolvingMethod +{ + public override string Description => IReferenceResolvingMethod.Desc.Get(this); + public override Argument[] ExpectedArguments => + [ + new ReferenceArgument("elevator"), + new OptionsArgument("info", options: + [ + new("name"), + new("group"), + new("isReady"), + new("isGoingUp"), + new("currentSequence"), + new("allDoorsLockedReason"), + new("anyDoorLockedReason"), + new("isAdminLocked") + ]) + ]; + public override void Execute() + { + var elevator = Args.GetReference("elevator"); + ReturnValue = Args.GetOption("info") switch + { + "name" => new StaticTextValue(elevator.Base.name), + "group" => new StaticTextValue(elevator.Group.ToString()), + "isready" => new BoolValue(elevator.IsReady), + "isgoingup" => new BoolValue(elevator.GoingUp), + "currentsequence" => new StaticTextValue(elevator.CurrentSequence.ToString()), + "alldoorslockedreason" => new StaticTextValue(elevator.AllDoorsLockedReason.ToString()), + "anydoorlockedreason" => new StaticTextValue(elevator.AnyDoorLockedReason.ToString()), + "isadminlocked" => new BoolValue(elevator.DynamicAdminLock), + _ => throw new RetroReulFuckedUpException() + }; + } + + public override TypeOfValue LiteralReturnTypes => new TypesOfValue([ + typeof(TextValue), + typeof(BoolValue) + ]); + public Type ResolvesReference => typeof(Elevator); +} \ No newline at end of file diff --git a/Code/MethodSystem/Methods/ElevatorMethods/SetElevatorTextMethod.cs b/Code/MethodSystem/Methods/ElevatorMethods/SetElevatorTextMethod.cs new file mode 100644 index 0000000..33793e8 --- /dev/null +++ b/Code/MethodSystem/Methods/ElevatorMethods/SetElevatorTextMethod.cs @@ -0,0 +1,29 @@ +using JetBrains.Annotations; +using LabApi.Features.Wrappers; +using SER.Code.ArgumentSystem.Arguments; +using SER.Code.ArgumentSystem.BaseArguments; +using SER.Code.MethodSystem.BaseMethods.Synchronous; +using SER.Code.MethodSystem.MethodDescriptors; + +namespace SER.Code.MethodSystem.Methods.ElevatorMethods; + +[UsedImplicitly] +public class SetElevatorTextMethod : SynchronousMethod, IAdditionalDescription +{ + public override string Description => "Changes the text on the elevator panels between LCZ and HCZ."; + + public override Argument[] ExpectedArguments => + [ + new TextArgument("text") + { + DefaultValue = new(string.Empty, "Resets the text to it's original value."), + } + ]; + + public override void Execute() + { + Decontamination.ElevatorsText = Args.GetText("text"); + } + + public string AdditionalDescription => "An empty text value will reset the elevator panel text to it's original value."; +} \ No newline at end of file diff --git a/Code/MethodSystem/Methods/HealthMethods/RegenerateMethod.cs b/Code/MethodSystem/Methods/HealthMethods/RegenerateMethod.cs new file mode 100644 index 0000000..8979ae2 --- /dev/null +++ b/Code/MethodSystem/Methods/HealthMethods/RegenerateMethod.cs @@ -0,0 +1,27 @@ +using JetBrains.Annotations; +using SER.Code.ArgumentSystem.Arguments; +using SER.Code.ArgumentSystem.BaseArguments; +using SER.Code.MethodSystem.BaseMethods.Synchronous; + +namespace SER.Code.MethodSystem.Methods.HealthMethods; + +[UsedImplicitly] +public class RegenerateMethod : SynchronousMethod +{ + public override string Description => "Adds health regeneration to players."; + + public override Argument[] ExpectedArguments => + [ + new PlayersArgument("players"), + new FloatArgument("regeneration rate"), + new FloatArgument("regeneration duration") + ]; + + public override void Execute() + { + var players = Args.GetPlayers("players"); + var regenerationRate = Args.GetFloat("regeneration rate"); + var regenerationDuration = Args.GetFloat("regeneration duration"); + players.ForEach(plr => plr.AddRegeneration(regenerationRate, regenerationDuration)); + } +} \ No newline at end of file diff --git a/Code/MethodSystem/Methods/ItemMethods/ForceEquipMethod.cs b/Code/MethodSystem/Methods/ItemMethods/ForceEquipMethod.cs new file mode 100644 index 0000000..bbf537f --- /dev/null +++ b/Code/MethodSystem/Methods/ItemMethods/ForceEquipMethod.cs @@ -0,0 +1,34 @@ +using JetBrains.Annotations; +using LabApi.Features.Wrappers; +using SER.Code.ArgumentSystem.Arguments; +using SER.Code.ArgumentSystem.BaseArguments; +using SER.Code.MethodSystem.BaseMethods.Synchronous; + +namespace SER.Code.MethodSystem.Methods.ItemMethods; + +[UsedImplicitly] +public class ForceEquipMethod : SynchronousMethod +{ + public override string Description => "Forces players to equip a provided item."; + + public override Argument[] ExpectedArguments => + [ + new PlayersArgument("players"), + new EnumArgument("item type") + { DefaultValue = new(ItemType.None, "Un-equip held item.") } + ]; + + public override void Execute() + { + var players = Args.GetPlayers("players"); + var itemType = Args.GetEnum("item type"); + + players.ForEach(plr => + { + var item = itemType != ItemType.None + ? Item.Get(plr.Inventory.UserInventory.Items.FirstOrDefault(x => x.Value.ItemTypeId == itemType).Value) + : null; + plr.CurrentItem = item; + }); + } +} \ No newline at end of file diff --git a/Code/MethodSystem/Methods/MapMethods/CreateRagdollMethod.cs b/Code/MethodSystem/Methods/MapMethods/CreateRagdollMethod.cs new file mode 100644 index 0000000..bbc88fb --- /dev/null +++ b/Code/MethodSystem/Methods/MapMethods/CreateRagdollMethod.cs @@ -0,0 +1,102 @@ +using JetBrains.Annotations; +using LabApi.Features.Wrappers; +using PlayerRoles; +using PlayerRoles.Ragdolls; +using PlayerStatsSystem; +using SER.Code.ArgumentSystem.Arguments; +using SER.Code.ArgumentSystem.BaseArguments; +using SER.Code.Exceptions; +using SER.Code.MethodSystem.BaseMethods.Synchronous; +using SER.Code.MethodSystem.MethodDescriptors; +using SER.Code.ValueSystem; +using UnityEngine; + +namespace SER.Code.MethodSystem.Methods.MapMethods; + +[UsedImplicitly] +public class CreateRagdollMethod : SynchronousMethod, ICanError +{ + public override string Description => "Spawns a ragdoll."; + + public override Argument[] ExpectedArguments => + [ + new EnumArgument("role"), + new TextArgument("name"), + new FloatArgument("x position"), + new FloatArgument("y position"), + new FloatArgument("z position"), + new FloatArgument("x size") + { DefaultValue = new(null, "default role x scale") }, + new FloatArgument("y size") + { DefaultValue = new(null, "default role y scale") }, + new FloatArgument("z size") + { DefaultValue = new(null, "default role z scale") }, + new FloatArgument("x rotation") + { DefaultValue = new(0f, null) }, + new FloatArgument("y rotation") + { DefaultValue = new(0f, null) }, + new FloatArgument("z rotation") + { DefaultValue = new(0f, null) }, + new FloatArgument("w rotation") + { DefaultValue = new(1f, null) }, + new AnyValueArgument("damage handler") + { + DefaultValue = new(new CustomReasonDamageHandler(""), "Damage reason will be blank"), + Description = $"Accepts a {nameof(TextValue)} or a {nameof(DamageHandlerBase)} reference." + }, + ]; + + public override void Execute() + { + var role = Args.GetEnum("role"); + var name = Args.GetText("name"); + + var xPosition = Args.GetFloat("x position"); + var yPosition = Args.GetFloat("y position"); + var zPosition = Args.GetFloat("z position"); + + var defaultSize = RagdollManager.GetDefaultScale(role); + + var xSize = Args.GetNullableFloat("x size") ?? defaultSize.x; + var ySize = Args.GetNullableFloat("y size") ?? defaultSize.y; + var zSize = Args.GetNullableFloat("z size") ?? defaultSize.z; + + var xRotation = Args.GetFloat("x rotation"); + var yRotation = Args.GetFloat("y rotation"); + var zRotation = Args.GetFloat("z rotation"); + var wRotation = Args.GetFloat("w rotation"); + + var value = Args.GetAnyValue("damage handler"); + + var position = new Vector3(xPosition, yPosition, zPosition); + var rotation = new Quaternion(xRotation, yRotation, zRotation, wRotation); + var size = new Vector3(xSize, ySize, zSize); + + DamageHandlerBase? damageHandler; + + switch (value) + { + case ReferenceValue referenceValue: + { + if (referenceValue.Value is DamageHandlerBase handler) + damageHandler = handler; + else + throw new ScriptRuntimeError(this, ErrorReasons[1]); + break; + } + case TextValue textValue: + damageHandler = new CustomReasonDamageHandler(textValue.StringRep); + break; + default: + throw new ScriptRuntimeError(this, ErrorReasons[0]); + } + + Ragdoll.SpawnRagdoll(role, position, rotation, damageHandler, name, size); + } + + public string[] ErrorReasons => + [ + $"Damage handler value must be a {nameof(DamageHandlerBase)} reference or a {nameof(TextValue)}.", + $"The provided reference value was not a {nameof(DamageHandlerBase)} reference." + ]; +} \ No newline at end of file diff --git a/Code/MethodSystem/Methods/PlayerMethods/JumpMethod.cs b/Code/MethodSystem/Methods/PlayerMethods/JumpMethod.cs new file mode 100644 index 0000000..d348632 --- /dev/null +++ b/Code/MethodSystem/Methods/PlayerMethods/JumpMethod.cs @@ -0,0 +1,31 @@ +using JetBrains.Annotations; +using SER.Code.ArgumentSystem.Arguments; +using SER.Code.ArgumentSystem.BaseArguments; +using SER.Code.MethodSystem.BaseMethods.Synchronous; +using SER.Code.MethodSystem.MethodDescriptors; + +namespace SER.Code.MethodSystem.Methods.PlayerMethods; + +[UsedImplicitly] +public class JumpMethod : SynchronousMethod, IAdditionalDescription +{ + public override string Description => + "Makes players jump (with modifiable jump strength)."; + + public override Argument[] ExpectedArguments { get; } = + [ + new PlayersArgument("players"), + new FloatArgument("jump strength") + { DefaultValue = new(4.9f, "default jump strength") } + ]; + + public override void Execute() + { + var players = Args.GetPlayers("players"); + var jumpStrength = Args.GetFloat("jump strength"); + + players.ForEach(plr => plr.Jump(jumpStrength)); + } + + public string AdditionalDescription => "This also works for players in the air. Allowing for mid-air jumps."; +} \ No newline at end of file diff --git a/Code/MethodSystem/Methods/PlayerMethods/ShowHitMarkerMethod.cs b/Code/MethodSystem/Methods/PlayerMethods/ShowHitMarkerMethod.cs new file mode 100644 index 0000000..7f070a3 --- /dev/null +++ b/Code/MethodSystem/Methods/PlayerMethods/ShowHitMarkerMethod.cs @@ -0,0 +1,28 @@ +using JetBrains.Annotations; +using SER.Code.ArgumentSystem.Arguments; +using SER.Code.ArgumentSystem.BaseArguments; +using SER.Code.MethodSystem.BaseMethods.Synchronous; + +namespace SER.Code.MethodSystem.Methods.PlayerMethods; + +[UsedImplicitly] +public class ShowHitMarkerMethod : SynchronousMethod +{ + public override string Description => + "Shows a hit marker to players."; + + public override Argument[] ExpectedArguments { get; } = + [ + new PlayersArgument("players"), + new FloatArgument("hitmarker size") + { DefaultValue = new(1f, "default size") } + ]; + + public override void Execute() + { + var players = Args.GetPlayers("players"); + var size = Args.GetFloat("hitmarker size"); + + players.ForEach(plr => plr.SendHitMarker(size)); + } +} \ No newline at end of file diff --git a/Code/MethodSystem/Methods/PlayerMethods/StaminaMethod.cs b/Code/MethodSystem/Methods/PlayerMethods/StaminaMethod.cs new file mode 100644 index 0000000..9e97f96 --- /dev/null +++ b/Code/MethodSystem/Methods/PlayerMethods/StaminaMethod.cs @@ -0,0 +1,63 @@ +using JetBrains.Annotations; +using PlayerRoles.FirstPersonControl; +using SER.Code.ArgumentSystem.Arguments; +using SER.Code.ArgumentSystem.BaseArguments; +using SER.Code.MethodSystem.BaseMethods.Synchronous; + +namespace SER.Code.MethodSystem.Methods.PlayerMethods; + +[UsedImplicitly] +public class StaminaMethod : SynchronousMethod +{ + public override string Description => "Control the stamina of players."; + + public override Argument[] ExpectedArguments => + [ + new OptionsArgument("options", options: + [ + new("add"), + new("remove"), + new("set"), + ]), + new PlayersArgument("players"), + new FloatArgument("stamina value", 0f, 1f) + { + Description = "Stamina is valued from 0 to 1. 0 meaning an empty stamina bar and 1 meaning a full stamina bar." + }, + new BoolArgument("delay stamina regen") + { + Description = "Stops stamina regeneration for a short duration after applying new stamina value, just like at the end of a sprint.", + DefaultValue = new(true, "will delay stamina regeneration for a second when new stamina value is applied.") + } + ]; + + public override void Execute() + { + var players = Args.GetPlayers("players"); + var staminaValue = Args.GetFloat("stamina value"); + + players.ForEach(plr => + { + if (plr?.RoleBase is not IFpcRole currentRole) return; + var newStamina = 0f; + switch (Args.GetOption("options")) + { + case "add": + newStamina = plr.StaminaRemaining + staminaValue; + if (newStamina > 1f) newStamina = 1f; + break; + case "remove": + newStamina = plr.StaminaRemaining - staminaValue; + if (newStamina < 0f) newStamina = 0f; + break; + case "set": + newStamina = staminaValue; + break; + } + + plr.StaminaRemaining = newStamina; + if (Args.GetBool("delay stamina regen")) + currentRole.FpcModule.StateProcessor._regenStopwatch.Restart(); + }); + } +} \ No newline at end of file diff --git a/Code/MethodSystem/Methods/ScriptMethods/ScriptExistsMethod.cs b/Code/MethodSystem/Methods/ScriptMethods/ScriptExistsMethod.cs new file mode 100644 index 0000000..8d40ebb --- /dev/null +++ b/Code/MethodSystem/Methods/ScriptMethods/ScriptExistsMethod.cs @@ -0,0 +1,26 @@ +using JetBrains.Annotations; +using SER.Code.ArgumentSystem.Arguments; +using SER.Code.ArgumentSystem.BaseArguments; +using SER.Code.MethodSystem.BaseMethods.Synchronous; +using SER.Code.ValueSystem; + +namespace SER.Code.MethodSystem.Methods.ScriptMethods; + +[UsedImplicitly] +public class ScriptExistsMethod : ReturningMethod +{ + public override string Description => "Returns true or false indicating if a script with the provided name exists."; + + public override Argument[] ExpectedArguments => + [ + new TextArgument("script name") + ]; + + public override void Execute() + { + var scriptName = Args.GetText("script name"); + ReturnValue = new BoolValue(FileSystem.FileSystem.RegisteredScriptPaths.Any(p => Path.GetFileNameWithoutExtension(p) == scriptName)); + } + + public override TypeOfValue Returns => new SingleTypeOfValue(typeof(BoolValue)); +} \ No newline at end of file diff --git a/Code/MethodSystem/Methods/ServerMethods/ServerInfoMethod.cs b/Code/MethodSystem/Methods/ServerMethods/ServerInfoMethod.cs index 9c396d0..9e7dda6 100644 --- a/Code/MethodSystem/Methods/ServerMethods/ServerInfoMethod.cs +++ b/Code/MethodSystem/Methods/ServerMethods/ServerInfoMethod.cs @@ -19,8 +19,10 @@ public class ServerInfoMethod : ReturningMethod "ip", "port", "name", + "playerCount", "maxPlayers", "tps", + "maxTps", "isVerified") ]; @@ -37,8 +39,10 @@ public override void Execute() "ip" => new StaticTextValue(Server.IpAddress), "port" => new NumberValue(Server.Port), "name" => new StaticTextValue(Server.ServerListName), + "playercount" => new NumberValue(Server.PlayerCount), "maxplayers" => new NumberValue(Server.MaxPlayers), "tps" => new NumberValue((decimal)Server.Tps), + "maxtps" => new NumberValue(Server.MaxTps), "isverified" => new BoolValue(CustomNetworkManager.IsVerified), _ => throw new TosoksFuckedUpException("out of order") }; diff --git a/Code/MethodSystem/Methods/TextMethods/ContainsTextMethod.cs b/Code/MethodSystem/Methods/TextMethods/ContainsTextMethod.cs new file mode 100644 index 0000000..0433416 --- /dev/null +++ b/Code/MethodSystem/Methods/TextMethods/ContainsTextMethod.cs @@ -0,0 +1,27 @@ +using JetBrains.Annotations; +using SER.Code.ArgumentSystem.Arguments; +using SER.Code.ArgumentSystem.BaseArguments; +using SER.Code.MethodSystem.BaseMethods.Synchronous; +using SER.Code.ValueSystem; + +namespace SER.Code.MethodSystem.Methods.TextMethods; + +[UsedImplicitly] +public class ContainsTextMethod : ReturningMethod +{ + public override string Description => "Returns true or false indicating if the provided text contains a provided value."; + + public override Argument[] ExpectedArguments => + [ + new TextArgument("text"), + new TextArgument("text to check for"), + ]; + public override void Execute() + { + var stringToCheck = Args.GetText("text"); + var substringToCheck = Args.GetText("text to check for"); + ReturnValue = new BoolValue(stringToCheck.Contains(substringToCheck)); + } + + public override TypeOfValue Returns => new SingleTypeOfValue(typeof(BoolValue)); +} \ No newline at end of file diff --git a/Code/TokenSystem/Tokens/ExpressionTokens/PlayerExpressionToken.cs b/Code/TokenSystem/Tokens/ExpressionTokens/PlayerExpressionToken.cs index dc00afe..938967b 100644 --- a/Code/TokenSystem/Tokens/ExpressionTokens/PlayerExpressionToken.cs +++ b/Code/TokenSystem/Tokens/ExpressionTokens/PlayerExpressionToken.cs @@ -1,6 +1,8 @@ using LabApi.Features.Wrappers; using PlayerRoles; +using PlayerRoles.FirstPersonControl; using PlayerRoles.PlayableScps.Scp079; +using Respawning.NamingRules; using SER.Code.ArgumentSystem.Arguments; using SER.Code.Extensions; using SER.Code.Helpers.ResultSystem; @@ -67,6 +69,16 @@ public enum PlayerProperty RelativeZ, IsNpc, IsDummy, + IsSpeaking, + IsSpectatable, + IsJumping, + IsGrounded, + Stamina, + MovementState, + RoleColor, + LifeId, + UnitId, + Unit, } public abstract class Info @@ -177,7 +189,17 @@ public class Info(Func handler, string? description) : Info [PlayerProperty.RelativeY] = new Info(plr => (decimal)plr.RelativeRoomPosition().y, "Returns the player's y relative to the current room or 0 if in no room"), [PlayerProperty.RelativeZ] = new Info(plr => (decimal)plr.RelativeRoomPosition().z, "Returns the player's z relative to the current room or 0 if in no room"), [PlayerProperty.IsNpc] = new Info(plr => plr.IsNpc, "True if it's a player without any client connected to it"), - [PlayerProperty.IsDummy] = new Info(plr => plr.IsDummy, null) + [PlayerProperty.IsDummy] = new Info(plr => plr.IsDummy, null), + [PlayerProperty.IsSpeaking] = new Info(plr => plr.IsSpeaking, null), + [PlayerProperty.IsSpectatable] = new Info(plr => plr.IsSpectatable, null), + [PlayerProperty.IsJumping] = new Info(plr => plr.RoleBase is IFpcRole currentRole && currentRole.FpcModule.Motor.JumpController.IsJumping, null), + [PlayerProperty.IsGrounded] = new Info(plr => plr.ReferenceHub.IsGrounded(), null), + [PlayerProperty.Stamina] = new Info(plr => (decimal)plr.StaminaRemaining, "Returns the player's remaining stamina."), + [PlayerProperty.MovementState] = new Info(plr => plr.RoleBase is IFpcRole currentRole ? currentRole.FpcModule.CurrentMovementState.ToString().ToStaticTextValue() : new("None"), "Returns the player's movement state or 'None' if the player is not a first-person role."), + [PlayerProperty.RoleColor] = new Info(plr => plr.RoleBase.RoleColor.ToHex().ToStaticTextValue(), "Returns the hex value of the player's role color."), + [PlayerProperty.LifeId] = new Info(plr => plr.LifeId, null), + [PlayerProperty.UnitId] = new Info(plr => (decimal)plr.UnitId, null), + [PlayerProperty.Unit] = new Info(plr => NamingRulesManager.ClientFetchReceived(plr.Team, plr.UnitId).ToStaticTextValue(), "Returns the player's unit (e.g FOXTROT-03) if player is NTF or Facility Guard, otherwise returns an empty text value."), }; protected override IParseResult InternalParse(BaseToken[] tokens)