diff --git a/.github/workflows/block-pr-to-master.yml b/.github/workflows/block-pr-to-master.yml new file mode 100644 index 00000000..bbc1ad35 --- /dev/null +++ b/.github/workflows/block-pr-to-master.yml @@ -0,0 +1,37 @@ +name: Block external PRs to master +on: + pull_request_target: + branches: [ "master" ] + types: [opened, reopened] + +permissions: + pull-requests: write + issues: write + +jobs: + close-pr: + if: > + github.event.pull_request.author_association != 'OWNER' && + github.event.pull_request.author_association != 'MEMBER' && + github.event.pull_request.author_association != 'COLLABORATOR' + runs-on: ubuntu-latest + steps: + - name: Debug - Afficher le statut + run: echo "Le statut de l'auteur de cette PR est ${{ github.event.pull_request.author_association }}" + + - name: Close the PR + uses: actions/github-script@v6 + with: + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: If you wish to contribute, please target the `dev` branch.' + }) + github.rest.pulls.update({ + pull_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + state: 'closed' + }) diff --git a/.github/workflows/buildtoserver.yml b/.github/workflows/buildtoserver.yml new file mode 100644 index 00000000..42b086c5 --- /dev/null +++ b/.github/workflows/buildtoserver.yml @@ -0,0 +1,53 @@ +name: Build .NET Solution + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + workflow_dispatch: + +env: + OUTPUT_FOLDER_PATH: /home/grosserv/.config/EXILED/Plugins/ + EXILED_REFERENCES: ${{ github.workspace }}/references + EXILED_DEV_REFERENCES: ${{ github.workspace }}/devreferences + SERVER_LOCATION: /home/grosserv/SCPSL/ + OTHER_DEPENDENCIES: /home/grosserv/OtherDependencies/ +jobs: + build: + runs-on: self-hosted + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Restore NuGet packages + run: | + SLN=$(find . -name "*.sln" | head -n 1) + nuget restore "$SLN" + + + - name : Fill Exiled References + run: | + mkdir ${{ env.EXILED_REFERENCES }} + mkdir ${{ env.EXILED_DEV_REFERENCES }} + find ${{env.SERVER_LOCATION}}/SCPSL_Data/Managed/ -name "*.dll" | xargs cp -t ${{env.EXILED_REFERENCES}} + find ${{env.OTHER_DEPENDENCIES}} -name "*.dll" | xargs cp -t ${{env.EXILED_REFERENCES}} + + - name: Build solution + run: | + export EXILED_REFERENCES=${{ env.EXILED_REFERENCES }} + export EXILED_DEV_REFERENCES=${{ env.EXILED_DEV_REFERENCES }} + SLN=$(find . -name "*.sln" | head -n 1) + msbuild "$SLN" /p:Configuration=Release + + - name: Copy plugin to server + run: | + DLL=$(find . -path "*/bin/Release/*KruacentExiled.dll" | head -n 1) + + if [ -z "$DLL" ]; then + echo "DLL not found!" + exit 1 + fi + cp "$DLL" $OUTPUT_FOLDER_PATH/ + diff --git a/.gitignore b/.gitignore index 344f050a..ec239c70 100644 --- a/.gitignore +++ b/.gitignore @@ -32,7 +32,7 @@ bld/ [Oo]ut/ [Ll]og/ [Ll]ogs/ - +lib # Visual Studio 2015/2017 cache/options directory diff --git a/KruacentE.BlackoutNDoor/API/Features/Controller.cs b/KruacentE.BlackoutNDoor/API/Features/Controller.cs deleted file mode 100644 index d5b03951..00000000 --- a/KruacentE.BlackoutNDoor/API/Features/Controller.cs +++ /dev/null @@ -1,182 +0,0 @@ -using Exiled.API.Enums; -using Exiled.API.Features; -using Exiled.API.Features.Doors; -using MEC; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace BlackoutKruacent.API.Features -{ - internal class Controller - { - /// - /// Select a random zone and close and lock all door of the zone - /// - public IEnumerator RandomDoorStuck() - { - yield return Timing.WaitForOneFrame; - var zone = SelectZone(); - Log.Debug($"DoorStuck in {zone}"); - yield return Timing.WaitUntilFalse(() => Cassie.IsSpeaking); - CassieVoiceLine(zone, false); - yield return Timing.WaitUntilFalse(() => Cassie.IsSpeaking); - yield return Timing.WaitForSeconds(5); - // if the zone is light and there is only 30s left then skip - if (!(zone == ZoneType.LightContainment && Map.DecontaminationState == DecontaminationState.Countdown)) - { - List doorList = Door.List - .Where(d => !new[] { DoorType.ElevatorGateA, DoorType.ElevatorGateB, DoorType.ElevatorLczA, DoorType.ElevatorLczB, DoorType.ElevatorNuke, DoorType.ElevatorScp049, DoorType.UnknownElevator }.Contains(d.Type)) - .ToList(); - foreach (Door door in doorList) - { - if (door.Zone == zone) - { - door.IsOpen = false; - door.ChangeLock(DoorLockType.Lockdown2176); - Timing.CallDelayed(MainPlugin.Instance.Config.DurationMalfunction / 2, () => door.IsOpen = true); - Timing.CallDelayed(MainPlugin.Instance.Config.DurationMalfunction, () => door.Unlock()); - if (UnityEngine.Random.value < .5f) - { - door.IsOpen = false; - } - } - } - } - } - - /// - /// Select a random zone and blackout it - /// - public IEnumerator RandomBlackout() - { - yield return Timing.WaitForOneFrame; - - var zone = SelectZone(); - yield return Timing.WaitUntilFalse(() => Cassie.IsSpeaking); - CassieVoiceLine(zone, true); - yield return Timing.WaitUntilFalse(() => Cassie.IsSpeaking); - yield return Timing.WaitForSeconds(5); - Log.Debug($"BlackOut in {zone}"); - - - Map.TurnOffAllLights(MainPlugin.Instance.Config.DurationMalfunction, zone); - } - - - private void CassieVoiceLine(ZoneType zone,bool isBlackout) - { - if (isBlackout) - { - switch (zone) - { - case ZoneType.LightContainment: - Cassie.MessageTranslated("Warning Failure Of All Lights In Light Containment Zone in 5 seconds", - "Warning Failure Of All Lights In Light Containment Zone in 5 seconds", false, false); - break; - case ZoneType.HeavyContainment: - Cassie.MessageTranslated("Warning Failure Of All Lights In Heavy Containment Zone in 5 seconds", - "Warning Failure Of All Lights In Heavy Containment Zone in 5 seconds", false, false); - break; - case ZoneType.Entrance: - Cassie.MessageTranslated("Warning Failure Of All Lights In Entrance Zone in 5 seconds", - "Warning Failure Of All Lights In Entrance Zone in 5 seconds", false, false); - break ; - case ZoneType.Surface: - Cassie.MessageTranslated("Warning Failure Of All Lights In Surface in 5 seconds", - "Warning Failure Of All Lights In Surface in 5 seconds", false, false); - break ; - case ZoneType.Unspecified: - Cassie.MessageTranslated("Warning Failure Of All Lights In All Of The Facility in 5 seconds", - "Warning Failure Of All Lights In All Of The Facility in 5 seconds", false, false); - break; - } - } - else - { - switch (zone) - { - case ZoneType.LightContainment: - Cassie.MessageTranslated("Door system malfunction in Light Containment Zone in 5 seconds", - "Door system malfunction in Light containment zone in 5 seconds", false,false); - break; - case ZoneType.HeavyContainment: - Cassie.MessageTranslated("Door system malfunction in Heavy Containment Zone in 5 seconds", - "Door system malfunction in Heavy zone in 5 seconds", false, false); - break; - case ZoneType.Entrance: - Cassie.MessageTranslated("Door system malfunction in Entrance zone in 5 seconds", - "Door system malfunction in Entrance zone in 5 seconds", false, false); - break; - case ZoneType.Surface: - Cassie.MessageTranslated("Door system malfunction in Surface Zone in 5 seconds", - "Door system malfunction in Surface zone in 5 seconds", false, false); - break; - case ZoneType.Unspecified: - Cassie.MessageTranslated("Door system malfunction in All of the facility in 5 seconds", - "Door system malfunction in All of the facility in 5 seconds", false, false); - break; - } - } - } - - /// - /// Select a random zone - /// If the nuke is detonated return only SurfaceZone - /// - /// a zone - private ZoneType SelectZone() - { - ZoneType z = ZoneType.Unspecified; - float random = UnityEngine.Random.value; - if (Warhead.IsDetonated) - { - z= ZoneType.Surface; - } - if (!Map.IsLczDecontaminated) - { - z= GetZoneType(GetProbabilityIndex(MainPlugin.Instance.Config.ChancePreConta, random)); - } - else - { - z= GetZoneType(GetProbabilityIndex(MainPlugin.Instance.Config.ChancePostConta, random)); - } - Log.Debug($"zone={z}"); - return z; - } - - private int GetProbabilityIndex(float[] probabilities, float randomValue) - { - float cumulative = 0f; - - for (int i = 0; i < probabilities.Length; i++) - { - cumulative += probabilities[i]; - if (randomValue <= cumulative) - { - return i; - } - } - - throw new ArgumentException("Random value is outside the range of probabilities."); - } - - private ZoneType GetZoneType(int index) - { - switch (index) - { - case 0: - return ZoneType.LightContainment; - case 1: - return ZoneType.HeavyContainment; - case 2: - return ZoneType.Entrance; - case 3: - return ZoneType.Surface; - default: - case 4: - return ZoneType.Unspecified; - } - } - } -} diff --git a/KruacentE.BlackoutNDoor/BlackoutKruacent.csproj b/KruacentE.BlackoutNDoor/BlackoutKruacent.csproj deleted file mode 100644 index 3fa6f206..00000000 --- a/KruacentE.BlackoutNDoor/BlackoutKruacent.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net48 - - - - - - - - - C:\Program Files (x86)\Steam\steamapps\common\SCP Secret Laboratory Dedicated Server\SCPSL_Data\Managed\Assembly-CSharp-firstpass.dll - - - C:\Program Files (x86)\Steam\steamapps\common\SCP Secret Laboratory Dedicated Server\SCPSL_Data\Managed\System.ComponentModel.Composition.dll - - - C:\Program Files (x86)\Steam\steamapps\common\SCP Secret Laboratory Dedicated Server\SCPSL_Data\Managed\UnityEngine.CoreModule.dll - - - - diff --git a/KruacentE.BlackoutNDoor/BlackoutKruacent.sln b/KruacentE.BlackoutNDoor/BlackoutKruacent.sln deleted file mode 100644 index 53bbd945..00000000 --- a/KruacentE.BlackoutNDoor/BlackoutKruacent.sln +++ /dev/null @@ -1,22 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.12.35506.116 d17.12 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlackoutKruacent", "BlackoutKruacent.csproj", "{347FC6BF-9742-4687-A244-85028B70D158}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {347FC6BF-9742-4687-A244-85028B70D158}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {347FC6BF-9742-4687-A244-85028B70D158}.Debug|Any CPU.Build.0 = Debug|Any CPU - {347FC6BF-9742-4687-A244-85028B70D158}.Release|Any CPU.ActiveCfg = Release|Any CPU - {347FC6BF-9742-4687-A244-85028B70D158}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/KruacentE.BlackoutNDoor/Config.cs b/KruacentE.BlackoutNDoor/Config.cs deleted file mode 100644 index b0d43fc4..00000000 --- a/KruacentE.BlackoutNDoor/Config.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Exiled.API.Interfaces; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BlackoutKruacent -{ - public class Config : IConfig - { - public bool IsEnabled { get; set; } = true; - public bool Debug { get; set; } = false; - - [Description("the minimum interval between 2 malfunctions")] - public int MinInterval { get; set; } = 300; - [Description("the maximum interval between 2 malfunctions")] - public int MaxInterval { get; set; } = 600; - [Description("chance of having a Blackout at the start of the game")] - public double InitialChanceBO { get; set; } = 0.5; - [Description("the duration of a malfunction")] - public int DurationMalfunction { get; set; } = 30; - - public float[] ChancePreConta = { .2f, .3f, .3f, .15f, .05f }; - public float[] ChancePostConta = { 0, .4f, .4f, .15f, .05f }; - } -} diff --git a/KruacentE.BlackoutNDoor/Handlers/ServerHandler.cs b/KruacentE.BlackoutNDoor/Handlers/ServerHandler.cs deleted file mode 100644 index 6984114f..00000000 --- a/KruacentE.BlackoutNDoor/Handlers/ServerHandler.cs +++ /dev/null @@ -1,57 +0,0 @@ -using BlackoutKruacent.API.Features; -using Exiled.API.Features; -using Exiled.Events.EventArgs.Server; -using MEC; -using PluginAPI.Events; -using System.Collections.Generic; - -namespace BlackoutKruacent.Handlers -{ - internal class ServerHandler - { - internal double ChanceBO { get; set; } = MainPlugin.Instance.Config.InitialChanceBO; - private Controller controller; - internal ServerHandler(Controller con) - { - controller = con; - } - public void OnRoundStarted() - { - Timing.RunCoroutine(Update()); - } - - private IEnumerator Update() - { - Log.Debug("startUpdate"); - var wait = UnityEngine.Random.Range(MainPlugin.Instance.Config.MinInterval, MainPlugin.Instance.Config.MaxInterval); - Log.Debug($"waiting for {wait}"); - yield return Timing.WaitForSeconds(wait); - while (true) - { - var a = UnityEngine.Random.value; - Log.Debug("random =" + a); - if (a <= ChanceBO) - { - Log.Debug("BlackOut"); - CoroutineHandle coroutine = Timing.RunCoroutine(controller.RandomBlackout()); - yield return Timing.WaitUntilDone(coroutine); - } - else - { - Log.Debug("DoorStuck"); - CoroutineHandle coroutine = Timing.RunCoroutine(controller.RandomDoorStuck()); - yield return Timing.WaitUntilDone(coroutine); - } - wait = UnityEngine.Random.Range(MainPlugin.Instance.Config.MinInterval, MainPlugin.Instance.Config.MaxInterval); - Log.Debug($"waiting : {wait}"); - yield return Timing.WaitForSeconds(wait); - yield return Timing.WaitUntilFalse(() => Warhead.IsInProgress); - - ChanceBO = -(1 / 60) * Round.ElapsedTime.TotalMinutes + 0.5; - Log.Debug($"new ChanceBO = {ChanceBO}"); - } - Log.Debug("end"); - } - - } -} diff --git a/KruacentE.BlackoutNDoor/MainPlugin.cs b/KruacentE.BlackoutNDoor/MainPlugin.cs deleted file mode 100644 index 757224a1..00000000 --- a/KruacentE.BlackoutNDoor/MainPlugin.cs +++ /dev/null @@ -1,47 +0,0 @@ -using BlackoutKruacent.API.Features; -using BlackoutKruacent.Handlers; -using Exiled.API.Features; -using System.ComponentModel; -using Server = Exiled.Events.Handlers.Server; - -namespace BlackoutKruacent -{ - public class MainPlugin : Plugin - { - internal static MainPlugin Instance; - private Controller _c; - private ServerHandler _server; - - public override void OnEnabled() - { - Instance = this; - _c = new Controller(); - this.RegisterEvent(); - } - public override void OnDisabled() - { - - Instance = null; - _c = null; - this.UnregisterEvent(); - } - - private void RegisterEvent() - { - _server = new ServerHandler(_c); - Server.RoundStarted += _server.OnRoundStarted; - - } - private void UnregisterEvent() - { - Server.RoundStarted -= _server.OnRoundStarted; - - _server = null; - } - - - - - - } -} diff --git a/KruacentE.GlobalEventFramework/Config.cs b/KruacentE.GlobalEventFramework/Config.cs deleted file mode 100644 index 8ac73e5b..00000000 --- a/KruacentE.GlobalEventFramework/Config.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Exiled.API.Interfaces; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace GEFExiled -{ - internal class Config : IConfig - { - - public bool IsEnabled { get; set; } = true; - public bool Debug { get; set; } = true; - - - - - - } -} diff --git a/KruacentE.GlobalEventFramework/GEFE.Examples/Config.cs b/KruacentE.GlobalEventFramework/GEFE.Examples/Config.cs deleted file mode 100644 index 04bcd01f..00000000 --- a/KruacentE.GlobalEventFramework/GEFE.Examples/Config.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Exiled.API.Interfaces; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace GEFExiled.GEFE.Examples -{ - public class Config : IConfig - { - public bool IsEnabled { get; set; } = true; - public bool Debug { get; set; } = true; - } -} diff --git a/KruacentE.GlobalEventFramework/GEFE.Examples/GE/Shuffle.cs b/KruacentE.GlobalEventFramework/GEFE.Examples/GE/Shuffle.cs deleted file mode 100644 index 256b3c0d..00000000 --- a/KruacentE.GlobalEventFramework/GEFE.Examples/GE/Shuffle.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System.Collections.Generic; -using Exiled.API.Features; -using GlobalEventFrameworkEXILED.API.Utils; -using InventorySystem; -using System.Linq; -using Exiled.API.Features.Items; -using Exiled.API.Extensions; -using Exiled.API.Enums; -using InventorySystem.Items.Firearms.Attachments.Components; -using Exiled.API.Structs; -using MEC; -using GEFExiled.GEFE.API.Features; - -namespace GEFExiled.GEFE.Examples.GE -{ - public class Shuffle : GlobalEvent - { - /// - public override int Id { get; set; } = 31; - /// - public override string Name { get; set; } = "Shuffle"; - /// - public override string Description { get; set; } = "et ça fait roomba café dans le scp"; - /// - public override double Weight { get; set; } = 1; - private Player[] players = Player.List.ToArray(); - private List[] inventories; - /// - public override IEnumerator Start() - { - Coroutine.LaunchCoroutine(Update()); - yield return 0; - } - - public Shuffle() - { - inventories = new List[players.Length]; - for (int i = 0; i < players.Length; i++) - { - inventories[i] = players[i].Items.ToList(); - } - } - private IEnumerator Update() - { - while (!Round.IsEnded) - { - yield return Timing.WaitForSeconds(UnityEngine.Random.Range(120, 240)); - - - } - } - //sprainte 9 fèr lé non ki rest - private void Shuffling(int decale) - { - for (int i = 0; i < players.Length; i++) - { - - } - } - - private void ChangeInventory(int pid) - { - var fire = new Dictionary>(); - players[pid].ClearItems(); - foreach (Item item in inventories[pid]) - { - //is a firearm - if (item is Firearm firearm) - { - fire.Add(firearm, firearm.AttachmentIdentifiers); - players[pid].AddItem(fire); - } - else - { - players[pid].AddItem(item); - } - } - } - } -} diff --git a/KruacentE.GlobalEventFramework/GEFE.Examples/GE/Speed.cs b/KruacentE.GlobalEventFramework/GEFE.Examples/GE/Speed.cs deleted file mode 100644 index a5697b44..00000000 --- a/KruacentE.GlobalEventFramework/GEFE.Examples/GE/Speed.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Exiled.API.Enums; -using Exiled.API.Features; -using GEFExiled.GEFE.API.Features; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace GEFExiled.GEFE.Examples.GE -{ - public class Speed : GlobalEvent - { - public override int Id { get; set; } = 30; - public override string Name { get; set; } = "Speed"; - public override string Description { get; set; } = "Gas! gas! gas!"; - public override double Weight { get; set; } = 1; - - public override IEnumerator Start() - { - - yield return 0; - } - - - public override void SubscribeEvent() - { - Exiled.Events.Handlers.Server.RoundStarted += OnRoundStarted; - Exiled.Events.Handlers.Server.RespawnedTeam +=; - } - - public override void UnsubscribeEvent() - { - Exiled.Events.Handlers.Server.RoundStarted -= OnRoundStarted; - } - - private void OnRoundStarted() - { - foreach(Player p in Player.List) - { - p.EnableEffect(EffectType.MovementBoost,100,99999999f); - } - } - - } -} diff --git a/KruacentE.GlobalEventFramework/GEFE.Examples/GE/SystemMalfunction.cs b/KruacentE.GlobalEventFramework/GEFE.Examples/GE/SystemMalfunction.cs deleted file mode 100644 index 3cd247f6..00000000 --- a/KruacentE.GlobalEventFramework/GEFE.Examples/GE/SystemMalfunction.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Exiled.API.Features; -using GEFExiled.GEFE.API.Features; -using GlobalEventFrameworkEXILED.API.Utils; -using MEC; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace GEFExiled.GEFE.Examples.GE -{ - - public class SystemMalfunction : GlobalEvent - { - public override int Id { get; set; } = 1; - public override string Name { get; set; } = "System Malfunction"; - public override string Description { get; set; } = "On dirait que les systèmes informatiques sont défaillants"; - public override double Weight { get; set; } = 1; - - public override IEnumerator Start() - { - Coroutine.LaunchCoroutine(EarlyNuke()); - yield return 0; - } - - private IEnumerator EarlyNuke() - { - int timeNuke = UnityEngine.Random.Range(15, 30); - Log.Debug($"the nuke will detonate in {timeNuke}min"); - yield return Timing.WaitUntilTrue(() => timeNuke == Round.ElapsedTime.TotalMinutes); - Warhead.Start(); - Log.Debug($"kaboom"); - } - } -} diff --git a/KruacentE.GlobalEventFramework/GEFE.Examples/MainPlugin.cs b/KruacentE.GlobalEventFramework/GEFE.Examples/MainPlugin.cs deleted file mode 100644 index 70832aef..00000000 --- a/KruacentE.GlobalEventFramework/GEFE.Examples/MainPlugin.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Exiled.API.Features; -using MEC; -using GEFExiled.GEFE.API.Features; - -namespace GEFExiled.GEFE.Examples -{ - public class MainPlugin : Plugin - { - - public override void OnEnabled() - { - - - - Timing.CallDelayed(10f, () => { - GlobalEvent.RegisterItems(); - }); - base.OnEnabled(); - } - } -} diff --git a/KruacentE.GlobalEventFramework/GEFE/API/Features/GlobalEvent.cs b/KruacentE.GlobalEventFramework/GEFE/API/Features/GlobalEvent.cs deleted file mode 100644 index 7c202975..00000000 --- a/KruacentE.GlobalEventFramework/GEFE/API/Features/GlobalEvent.cs +++ /dev/null @@ -1,70 +0,0 @@ -using Exiled.API.Features; -using System.Collections.Generic; -using System.Linq; -using MEC; -using GEFExiled.GEFE.API.Interfaces; -using Exiled.CustomItems.API.Features; -using System.Reflection; -using Exiled.API.Features.Attributes; -using Exiled.API.Interfaces; -using System.Collections; -using System; -using Exiled.API.Extensions; -using Exiled.API.Features.Pools; - -namespace GEFExiled.GEFE.API.Features -{ - public class GlobalEvent : IGlobalEvent - { - /// - /// A list of Active GlobalEvents - /// - public static List ActiveGlobalEvents => ActiveGE.ToList(); - internal static List ActiveGE { get; } = new List(); - internal static Dictionary GlobalEvents { get; set; } = new Dictionary(); - /// - /// A list of all registered GlobalEvents - /// - public static List GlobalEventsList => GlobalEvents.Values.ToList(); - public static HashSet Registered { get; } = new HashSet(); - public virtual int Id { get; set; } = -1; - public virtual string Name { get; set; } = "GE NOT SET"; - public virtual string Description { get; set; } = "DESC NOT SET"; - public virtual double Weight { get; set; } = 1; - - public static void Register(IGlobalEvent globalEvent) - { - Log.Debug("REGISTERING" + globalEvent.Name); - if (GlobalEvents.ContainsKey(globalEvent.Id)) - { - Log.Warn($"id already used for {GlobalEvents.TryGetValue(globalEvent.Id, out IGlobalEvent geAlready)}"); - Log.Warn("Trying to attribute a new id..."); - int key = 0; - while (GlobalEvents.ContainsKey(key)) - { - key++; - } - globalEvent.Id = key; - Log.Warn($"new id of {globalEvent.Name} : {globalEvent.Id}"); - } - GlobalEvents.Add(globalEvent.Id, globalEvent); - Log.Info($"{globalEvent.Name} is registered"); - - } - - public virtual IEnumerator Start() - { - Log.Error($"{GetType().Name} Start is NOT overrided"); - yield return Timing.WaitForSeconds(30f); - } - - public virtual void SubscribeEvent() - { - Log.Error($"{GetType().Name} SubscribeEvent is NOT overrided"); - } - public virtual void UnsubscribeEvent() - { - Log.Error($"{GetType().Name} UnsubscribeEvent is NOT overrided"); - } - } -} diff --git a/KruacentE.GlobalEventFramework/GEFE/API/Interfaces/IGlobalEvent.cs b/KruacentE.GlobalEventFramework/GEFE/API/Interfaces/IGlobalEvent.cs deleted file mode 100644 index 11c0a535..00000000 --- a/KruacentE.GlobalEventFramework/GEFE/API/Interfaces/IGlobalEvent.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using MEC; - -namespace GEFExiled.GEFE.API.Interfaces -{ - public interface IGlobalEvent - { - /// - /// the UNIQUE id of the Global Event - /// - int Id { get; set; } - /// - /// Name used in the logs on the RA - /// - string Name { get; set; } - - /// - /// The description that will be shown to the player when the round start - /// - string Description { get; set; } - - /// - /// The chance this GE will be choosed at the start of a round - /// - double Weight { get; set; } - - /// - /// Is launched at the start of a round - /// - IEnumerator Start(); - - void SubscribeEvent(); - - void UnsubscribeEvent(); - } -} diff --git a/KruacentE.GlobalEventFramework/GEFE/API/Utils/Coroutine.cs b/KruacentE.GlobalEventFramework/GEFE/API/Utils/Coroutine.cs deleted file mode 100644 index 2d17f72f..00000000 --- a/KruacentE.GlobalEventFramework/GEFE/API/Utils/Coroutine.cs +++ /dev/null @@ -1,19 +0,0 @@ -using MEC; -using System.Collections.Generic; - -namespace GEFExiled.API.Utils -{ - public static class Coroutine - { - public static readonly List _coroutine = new List(); - - //CoroutineHandle coroutine - public static CoroutineHandle LaunchCoroutine(IEnumerator coroutine) - { - CoroutineHandle a = Timing.RunCoroutine(coroutine); - - _coroutine.Add(a); - return a; - } - } -} diff --git a/KruacentE.GlobalEventFramework/GEFE/Commands/List.cs b/KruacentE.GlobalEventFramework/GEFE/Commands/List.cs deleted file mode 100644 index 0aba91bf..00000000 --- a/KruacentE.GlobalEventFramework/GEFE/Commands/List.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace KruacentExiled.KruacentE.GlobalEventFramework.GEFE.Commands -{ - using Exiled.API.Features; - using Exiled.API.Features.Pickups; - using System; - using CommandSystem; - using System.Runtime.InteropServices.WindowsRuntime; - using GEFExiled.GEFE.API.Features; - using GEFExiled.GEFE.API.Interfaces; - - public class List : ICommand - { - public string Command { get; } = "list"; - public string[] Aliases { get; } = new string[] { "l", "ls" }; - public string Description { get; } = "get the list of all Global Events"; - - public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) - { - string result = "List of all global event ([o] if it's active in the round ; [ ] otherwise) : "; - foreach (IGlobalEvent ge in GlobalEvent.GlobalEvents.Values) - { - - if (GlobalEvent.ActiveGlobalEvents.Contains(ge)) - { - result += "[o]"; - } - else - { - result += "[ ]"; - } - result += $" {ge.Id} : {ge.Name} : {ge.Description}"; - } - response = result; - return true; - } - } -} diff --git a/KruacentE.GlobalEventFramework/GEFE/Commands/ParentCommandGEFE.cs b/KruacentE.GlobalEventFramework/GEFE/Commands/ParentCommandGEFE.cs deleted file mode 100644 index c20ba3cf..00000000 --- a/KruacentE.GlobalEventFramework/GEFE/Commands/ParentCommandGEFE.cs +++ /dev/null @@ -1,34 +0,0 @@ -using GEFExiled.Commands; - -namespace KruacentExiled.KruacentE.GlobalEventFramework.GEFE.Commands -{ - using CommandSystem; - using System; - - [CommandHandler(typeof(RemoteAdminCommandHandler))] - public class ParentCommandGEFE : ParentCommand - { - public ParentCommandGEFE() - { - LoadGeneratedCommands(); - } - public override string Command { get; } = "globalevent"; - public override string Description { get; } = "the parent command to check the Global Events"; - public override string[] Aliases { get; } = { "ge" }; - - public override void LoadGeneratedCommands() - { - RegisterCommand(new List()); - } - - protected override bool ExecuteParent(ArraySegment arguments, ICommandSender sender, out string response) - { - - response = ""; - return true; - } - - - } - -} \ No newline at end of file diff --git a/KruacentE.GlobalEventFramework/GEFE/Handlers/ServerHandler.cs b/KruacentE.GlobalEventFramework/GEFE/Handlers/ServerHandler.cs deleted file mode 100644 index f5999932..00000000 --- a/KruacentE.GlobalEventFramework/GEFE/Handlers/ServerHandler.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Exiled.API.Features; -using Exiled.Events.EventArgs.Server; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using MEC; - -namespace GEFExiled.Handlers -{ - internal class ServerHandler - { - MainPlugin _plugin; - CoroutineHandle _coroutineHandle; - public ServerHandler(MainPlugin mainPlugin) - { - this._plugin = mainPlugin; - } - public void OnRoundStarted() - { - Log.Debug("starting round"); - _plugin.ChooseGE(); - Log.Debug("end starting round"); - } - - public void OnEndingRound(EndingRoundEventArgs ev) - { - Log.Debug("ending round"); - this._plugin.StopCoroutines(); - } - } -} diff --git a/KruacentE.GlobalEventFramework/GEFExiled.csproj b/KruacentE.GlobalEventFramework/GEFExiled.csproj deleted file mode 100644 index 08b7aa74..00000000 --- a/KruacentE.GlobalEventFramework/GEFExiled.csproj +++ /dev/null @@ -1,30 +0,0 @@ - - - - net48 - - - - - - - - - ..\..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\SCP Secret Laboratory Dedicated Server\SCPSL_Data\Managed\Assembly-CSharp-firstpass.dll - - - C:\Program Files (x86)\Steam\steamapps\common\SCP Secret Laboratory Dedicated Server\SCPSL_Data\Managed\Mirror.dll - - - C:\Program Files (x86)\Steam\steamapps\common\SCP Secret Laboratory Dedicated Server\SCPSL_Data\Managed\UnityEngine.dll - - - C:\Program Files (x86)\Steam\steamapps\common\SCP Secret Laboratory Dedicated Server\SCPSL_Data\Managed\UnityEngine.CoreModule.dll - - - - - - - - diff --git a/KruacentE.GlobalEventFramework/MainPlugin.cs b/KruacentE.GlobalEventFramework/MainPlugin.cs deleted file mode 100644 index aefea34b..00000000 --- a/KruacentE.GlobalEventFramework/MainPlugin.cs +++ /dev/null @@ -1,141 +0,0 @@ -using Exiled.API.Features; -using System; -using System.Collections.Generic; -using System.Linq; -using MEC; -using Exiled.API.Enums; -using Player = Exiled.API.Features.Player; -using GEFExiled.GEFE.API.Features; -using GEFExiled.GEFE.API.Interfaces; -using GEFExiled.GEFE.Examples.GE; -namespace GEFExiled -{ - internal class MainPlugin : Plugin - { - public override PluginPriority Priority => PluginPriority.Highest; - - internal Handlers.ServerHandler _server; - - internal static MainPlugin _instance; - - public static List coroutineHandles = new List(); - public override void OnEnabled() - { - _instance = this; - GlobalEvent.Register(new SystemMalfunction()); - GlobalEvent.Register(new Shuffle()); - - RegisterEvents(); - base.OnEnabled(); - } - - public override void OnDisabled() - { - UnregisterEvents(); - Timing.KillCoroutines(); - base.OnDisabled(); - _instance = null; - } - - private void RegisterEvents() - { - _server = new Handlers.ServerHandler(this); - - Exiled.Events.Handlers.Server.RoundStarted += _server.OnRoundStarted; - Exiled.Events.Handlers.Server.EndingRound += _server.OnEndingRound; - } - - private void UnregisterEvents() - { - Exiled.Events.Handlers.Server.RoundStarted -= _server.OnRoundStarted; - Exiled.Events.Handlers.Server.EndingRound -= _server.OnEndingRound; - - _server = null; - } - - public void Show() - { - foreach (Player player in Player.List) - { - Exiled.API.Features.Broadcast b = new Exiled.API.Features.Broadcast(); - b.Content = ShowText(); - b.Duration = 10; - player.Broadcast(b); - } - } - - private String ShowText() - { - String result = "Global Events: "; - for (int i = 0; i < GlobalEvent.ActiveGE.Count(); i++) - { - result += GlobalEvent.ActiveGE[i].Description; - if (GlobalEvent.ActiveGE.Count() > 1) - { - result += ","; - } - } - return result; - } - - public void ChooseGE() - { - Log.Debug("Choosine ge"); - List activeGE = ChooseRandomGE(); - Log.Debug("et go les ga c la fin du randomge"); - - foreach (IGlobalEvent ge in activeGE) - { - Log.Debug($"GE : {ge.Name}"); - var a = Timing.RunCoroutine(ge.Start()); - Log.Debug("fin start corutn"); - coroutineHandles.Add(a); //crash - Log.Debug("fin add corutn"); - } - Log.Debug("fin choosein ge"); - } - - - private List ChooseRandomGE(int nbGE = 1) - { - List result = new List(); - List gelist = GlobalEvent.GlobalEventsList.ToList(); - - double totalWeight = GlobalEvent.GlobalEventsList.Sum(x => x.Weight); - double randomWeight = UnityEngine.Random.value * totalWeight; - double cumulativeWeight = 0.0; - - - for (int i = 0; i < nbGE; i++) - { - Log.Debug("start foreach"); - bool found = false; - foreach (IGlobalEvent ge in gelist) - { - cumulativeWeight += ge.Weight; - - if (randomWeight <= cumulativeWeight) - { - Log.Debug("hon hon hon le add du if"); - result.Add(ge); - break; - } - } - if (!found) - { - Log.Debug("nsm on a pa trouvé"); - result.Add(gelist.Last()); - gelist.Remove(gelist.Last()); - } - - } - Log.Debug("fin :D"); - return result; - } - - public void StopCoroutines() - { - Timing.KillCoroutines(); - } - } -} diff --git a/KruacentE.Items/ArmeKruacent.csproj b/KruacentE.Items/ArmeKruacent.csproj deleted file mode 100644 index bf48e06e..00000000 --- a/KruacentE.Items/ArmeKruacent.csproj +++ /dev/null @@ -1,35 +0,0 @@ - - - - net48 - - - - - - - - - C:\Program Files (x86)\Steam\steamapps\common\SCP Secret Laboratory Dedicated Server\SCPSL_Data\Managed\Assembly-CSharp-firstpass.dll - - - ..\lib\CustomItems.dll - - - C:\Program Files (x86)\Steam\steamapps\common\SCP Secret Laboratory Dedicated Server\SCPSL_Data\Managed\Mirror.dll - - - C:\Program Files (x86)\Steam\steamapps\common\SCP Secret Laboratory Dedicated Server\SCPSL_Data\Managed\System.ComponentModel.Composition.dll - - - C:\Program Files (x86)\Steam\steamapps\common\SCP Secret Laboratory Dedicated Server\SCPSL_Data\Managed\UnityEngine.dll - - - C:\Program Files (x86)\Steam\steamapps\common\SCP Secret Laboratory Dedicated Server\SCPSL_Data\Managed\UnityEngine.CoreModule.dll - - - C:\Program Files (x86)\Steam\steamapps\common\SCP Secret Laboratory Dedicated Server\SCPSL_Data\Managed\UnityEngine.PhysicsModule.dll - - - - diff --git a/KruacentE.Items/Items/AdrenalineDrogue.cs b/KruacentE.Items/Items/AdrenalineDrogue.cs deleted file mode 100644 index 4beae8c3..00000000 --- a/KruacentE.Items/Items/AdrenalineDrogue.cs +++ /dev/null @@ -1,271 +0,0 @@ -using System.Collections.Generic; -using Exiled.API.Enums; -using Exiled.API.Features.Attributes; -using Exiled.API.Features.Spawn; -using Exiled.CustomItems.API.Features; -using MEC; -using Exiled.Events.EventArgs.Player; -using Player = Exiled.Events.Handlers.Player; -using Exiled.API.Features; -using Exiled.API.Extensions; -using UnityEngine; - -/// -[CustomItem(ItemType.Adrenaline)] -public class AdrenalineDrogue : CustomItem -{ - /// - public override uint Id { get; set; } = 19; - - /// - public override string Name { get; set; } = "DA-020"; - - /// - public override string Description { get; set; } = "La bonne drogue là, si vous le prenez vous êtes ienb pendant 20 secondes puis vous vous sentez pas bien !"; - - /// - public override float Weight { get; set; } = 0.65f; - - public List joueursSCP = new List(); - - /// - public override SpawnProperties SpawnProperties { get; set; } = new SpawnProperties() - { - Limit = 1, - DynamicSpawnPoints = new List - { - new DynamicSpawnPoint() - { - Chance = 100, - Location = SpawnLocationType.Inside079Secondary, - }, - new DynamicSpawnPoint() - { - Chance = 30, - Location = SpawnLocationType.InsideHid, - }, - new DynamicSpawnPoint() - { - Chance = 30, - Location = SpawnLocationType.InsideHczArmory, - }, - new DynamicSpawnPoint() - { - Chance = 20, - Location = SpawnLocationType.InsideIntercom, - }, - new DynamicSpawnPoint() - { - Chance = 30, - Location = SpawnLocationType.InsideNukeArmory - }, - new DynamicSpawnPoint() - { - Chance = 1, - Location = SpawnLocationType.Inside914 - }, - new DynamicSpawnPoint() - { - Chance = 2, - Location = SpawnLocationType.Inside330 - } - }, - }; - - /// - protected override void SubscribeEvents() - { - Player.UsedItem += OnUsingItem; - base.SubscribeEvents(); - } - - /// - protected override void UnsubscribeEvents() - { - Player.UsedItem -= OnUsingItem; - base.UnsubscribeEvents(); - } - - private void OnUsingItem(UsedItemEventArgs ev) - { - if (TryGet(ev.Item, out var result)) - { - if(result.Id == 19) - { - Timing.CallDelayed(0.5f, () => - { - Timing.RunCoroutine(EffectAttribution(ev.Player)); - }); - } - - } - } - - private IEnumerator EffectAttribution(Exiled.API.Features.Player joueur) - { - /* EFFET DE LA DROGUE */ - joueur.ShowHint("Vous êtes actuellement sous effet de la cocaïne liquide !"); - joueur.EnableEffect(EffectType.MovementBoost, 40, true); - joueur.EnableEffect(EffectType.RainbowTaste, 30, true); - joueur.EnableEffect(EffectType.InsufficientLighting, 30, true); - joueur.EnableEffect(EffectType.BodyshotReduction, 30, true); - joueur.EnableEffect(EffectType.Vitality, 30, true); - joueur.EnableEffect(EffectType.Ghostly, 30, true); - joueur.Health = 173; - - yield return Timing.WaitForSeconds(30); - - joueur.ShowHint("Mince vous êtes perdu chez le papi Rian !"); - joueur.Health = 9420; - - joueur.IsGodModeEnabled = true; - joueur.EnableEffect(EffectType.Flashed, 2, 2); - joueur.Teleport(RoomType.Pocket); - yield return Timing.WaitForSeconds(6); - - - joueur.EnableEffect(EffectType.Flashed, 2, 2); - joueur.Teleport(Room.Random()); - joueur.Handcuff(); - yield return Timing.WaitForSeconds(20); - - joueur.Health = 1; - - joueur.EnableEffect(EffectType.SeveredHands, 4); - yield return Timing.WaitForSeconds(4); - joueur.DisableAllEffects(); - - - foreach (Exiled.API.Features.Player unJoueur in Exiled.API.Features.Player.List) - { - if (unJoueur.IsScp) - { - joueursSCP.Add(unJoueur); - } - } - - joueur.EnableEffect(EffectType.Flashed, 2, 2); - if (joueursSCP.Count > 0) - { - joueur.Teleport(joueursSCP[UnityEngine.Random.Range(0, joueursSCP.Count)]); - } else - { - joueur.Teleport(Room.Random()); - } - - yield return Timing.WaitForSeconds(10); - - joueur.Teleport(Room.Random()); - - joueur.RemoveHandcuffs(); - joueur.IsGodModeEnabled = false; - joueur.MaxHealth = 65; - joueur.Heal(joueur.MaxHealth); - joueur.DisplayNickname = "Sou Hiyori"; - - joueur.DisableAllEffects(); - joueur.EnableEffect(EffectType.SilentWalk, 10); - joueur.EnableEffect(EffectType.MovementBoost, 35); - - - yield return Timing.WaitForSeconds(UnityEngine.Random.Range(10, 20)); - - if (joueur.IsAlive) - { - int randomNumber = UnityEngine.Random.Range(1, 6); - - switch (randomNumber) - { - case 1: - Log.Debug(joueur.Nickname + " a changé d'apparence !"); - joueur.PlayShieldBreakSound(); - - joueur.ChangeAppearance(joueursSCP[0].Role); - joueur.DisplayNickname = joueursSCP[0].Nickname; - - Exiled.API.Features.Server.FriendlyFire = true; - - joueur.Mute(); - yield return Timing.WaitForSeconds(15); - joueur.UnMute(); - break; - case 2: - Log.Debug("Ne saute pas"); - joueur.ShowHint("Ne saute pas !"); - - yield return Timing.WaitForSeconds(5); - - float duration = 300f; - float interval = 0.1f; - - float elapsedTime = 0f; - - while (elapsedTime < duration) - { - if (joueur.IsJumping) - { - int randomSaute = UnityEngine.Random.Range(0, 1); - if(randomSaute <= 0) - { - joueur.Explode(); - } else - { - joueur.ShowHint("Tu es joueur ! Voila une recompense"); - joueur.AddItem(ItemType.Coin); - } - } - - yield return Timing.WaitForSeconds(interval); - - elapsedTime += interval; - } - break; - case 3: - Log.Debug("Muet"); - joueur.ShowHint("Vous avez perdu votre langue ! (esperons que celui-ci repousse)"); - joueur.Mute(); - yield return Timing.WaitForSeconds(UnityEngine.Random.Range(30, 100)); - joueur.ShowHint("Je crois que c'est bon, ça a repoussé !"); - joueur.UnMute(); - break; - case 4: - joueur.ShowHint("Vous êtes devenu du caoutchouc !"); - Exiled.API.Features.TeslaGate.IgnoredPlayers.Add(joueur); - joueur.SetScale(new Vector3(1.5f, 0.5f, 1.7f), Exiled.API.Features.Player.List); - break; - case 5: - Log.Debug("Let's go party"); - foreach (var player in Exiled.API.Features.Player.List) - { - player.ShowHint(joueur.Nickname + " à commencer une fête d'anniversaire !"); - } - - float duration2 = 30f; - float interval2 = 0.7f; - - float elapsedTime2 = 0f; - - while (elapsedTime2 < duration2) - { - float r = UnityEngine.Random.Range(0f, 1f); - float g = UnityEngine.Random.Range(0f, 1f); - float b = UnityEngine.Random.Range(0f, 1f); - - Exiled.API.Features.Map.ChangeLightsColor(new UnityEngine.Color(r, g, b)); - - yield return Timing.WaitForSeconds(interval2); - - elapsedTime2 += interval2; - } - - Exiled.API.Features.Map.ResetLightsColor(); - break; - case 6: - Log.Debug("Paper"); - joueur.ShowHint("Bienvenue dans le monde des papiers. Évite les ciseaux !"); - joueur.SetScale(new Vector3(1f, 0.5f, 1f), Exiled.API.Features.Player.List); - break; - } - } - } -} \ No newline at end of file diff --git a/KruacentE.Items/Items/Mine.cs b/KruacentE.Items/Items/Mine.cs deleted file mode 100644 index 35f792cd..00000000 --- a/KruacentE.Items/Items/Mine.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Exiled.CustomItems.API.Features; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ArmeKruacent.Items -{ - - //une mine - /*public class Mine : CustomGrenade - { - - } - */ -} diff --git a/KruacentE.Items/Items/TPGrenada.cs b/KruacentE.Items/Items/TPGrenada.cs deleted file mode 100644 index 34814ec7..00000000 --- a/KruacentE.Items/Items/TPGrenada.cs +++ /dev/null @@ -1,132 +0,0 @@ - -using System; -using System.Collections.Generic; -using System.ComponentModel; -using Exiled.API.Enums; -using Exiled.API.Features; -using Exiled.API.Features.Attributes; -using Exiled.API.Features.Pools; -using Exiled.API.Features.Spawn; -using Exiled.CustomItems.API.Features; -using Exiled.Events.EventArgs.Map; -using PlayerRoles; -using UnityEngine; - -namespace ArmeKruacent.Items -{ - //grenade qui tp - public class TPGrenada : CustomGrenade - { - private List effectedPlayers = new List(); - public override uint Id { get; set; } = 20; - public override string Name { get; set; } = "Teleportation Grenade"; - public override string Description { get; set; } = "This grenade does 0 damage but teleport nearby players in a random place (does work in other dimension ;3 )"; - public override float Weight { get; set; } = 0.65f; - public override float FuseTime { get; set; } = 1.5f; - public override bool ExplodeOnCollision { get; set; } = true; - public float DamageModifier { get; set; } = 0.05f; - public override SpawnProperties SpawnProperties { get; set; } = new SpawnProperties() - { - Limit = 5, - DynamicSpawnPoints = new List - { - new DynamicSpawnPoint() - { - Chance = 50, - Location = SpawnLocationType.InsideLocker, - }, - new DynamicSpawnPoint() - { - Chance = 50, - Location = SpawnLocationType.InsideHczArmory, - }, - new DynamicSpawnPoint() - { - Chance =2, - Location = SpawnLocationType.Inside914, - }, - new DynamicSpawnPoint() - { - Chance=50, - Location = SpawnLocationType.Inside049Armory, - }, - new DynamicSpawnPoint() - { - Chance=50, - Location = SpawnLocationType.InsideLczArmory, - } - }, - - }; - - [Description("What roles will not be able to be affected by Implosion Grenades. Keeping SCP-173 on this list is highly recommended.")] - public HashSet BlacklistedRoles { get; set; } = new HashSet() { RoleTypeId.Scp173, RoleTypeId.Scp106,RoleTypeId.Scp049, RoleTypeId.Scp096,RoleTypeId.Scp3114,RoleTypeId.Scp0492,RoleTypeId.Scp939 }; - - protected override void OnExploding(ExplodingGrenadeEventArgs ev) - { - ev.IsAllowed = false; - List copiedList = new List(); - foreach (Player player in ev.TargetsToAffect) - { - copiedList.Add(player); - } - - ev.TargetsToAffect.Clear(); - - effectedPlayers = ListPool.Pool.Get(); - foreach (Player player in copiedList) - { - if (BlacklistedRoles.Contains(player.Role)) - continue; - try - { - - bool line = Physics.Linecast(ev.Projectile.Transform.position, player.Position); - if (line) - { - effectedPlayers.Add(player); - player.Teleport(RandomRoom()); - } - } - catch (Exception exception) - { - Log.Error($"{nameof(OnExploding)} error: {exception}"); - } - } - } - - - - private Room RandomRoom() - { - Room room = Room.Random(); - if (Warhead.IsDetonated) - { - return Room.Random(ZoneType.Surface); - } - - if(Map.IsLczDecontaminated) - { - float random = UnityEngine.Random.value; - Log.Debug($"random={random}"); - if (random <= 0.33f) - { - return Room.Random(ZoneType.HeavyContainment); - } - if(random > 0.33f && random <= 0.66f) - { - return Room.Random(ZoneType.Entrance); - } - return Room.Random(ZoneType.Surface); - } - Log.Debug($"roomZone={room.Zone}"); - return room; - } - } - - //bonbon quand tu manges ça donne une armes random - /*public class RandomWeaponCandy : CustomItem - { - - }*/ -} diff --git a/KruacentE.Items/MainPlugin.cs b/KruacentE.Items/MainPlugin.cs deleted file mode 100644 index 42d98d4e..00000000 --- a/KruacentE.Items/MainPlugin.cs +++ /dev/null @@ -1,25 +0,0 @@ - -using Exiled.API.Features; -using Exiled.CustomItems.API.Features; - -namespace ArmeKruacent -{ - public class MainPlugin : Plugin - { - - public override void OnEnabled() - { - - CustomItem.RegisterItems(); - - base.OnEnabled(); - } - - public override void OnDisabled() - { - CustomItem.UnregisterItems(); - - base.OnDisabled(); - } - } -} \ No newline at end of file diff --git a/KruacentExiled.csproj b/KruacentExiled.csproj deleted file mode 100644 index 6a605df2..00000000 --- a/KruacentExiled.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net48 - - - - - - - - - C:\Program Files (x86)\Steam\steamapps\common\SCP Secret Laboratory Dedicated Server\SCPSL_Data\Managed\Assembly-CSharp-firstpass.dll - - - C:\Program Files (x86)\Steam\steamapps\common\SCP Secret Laboratory Dedicated Server\SCPSL_Data\Managed\UnityEngine.dll - - - C:\Program Files (x86)\Steam\steamapps\common\SCP Secret Laboratory Dedicated Server\SCPSL_Data\Managed\UnityEngine.CoreModule.dll - - - - diff --git a/KruacentE.Items/Config.cs b/KruacentExiled/IExiledTranslation.cs similarity index 51% rename from KruacentE.Items/Config.cs rename to KruacentExiled/IExiledTranslation.cs index c064de87..5edf884d 100644 --- a/KruacentE.Items/Config.cs +++ b/KruacentExiled/IExiledTranslation.cs @@ -5,11 +5,10 @@ using System.Text; using System.Threading.Tasks; -namespace ArmeKruacent +namespace KruacentExiled { - public class Config : IConfig + internal interface IExiledTranslation { - public bool IsEnabled { get; set; } = true; - public bool Debug { get; set; } = false; + public abstract ITranslation Translation { get; } } } diff --git a/KruacentExiled/KE.CustomRoles/API/Core/Positions/UltraPosition.cs b/KruacentExiled/KE.CustomRoles/API/Core/Positions/UltraPosition.cs new file mode 100644 index 00000000..4574a1f7 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/API/Core/Positions/UltraPosition.cs @@ -0,0 +1,20 @@ +using HintServiceMeow.Core.Enum; +using KE.Utils.API.Displays.DisplayMeow.Placements; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.API.Core.Positions +{ + public class UltraPosition : HintPosition + { + public override float Xposition => 740; + + public override float Yposition => 280; + public override string Name => "Ultra"; + + public override HintAlignment HintAlignment => HintAlignment.Right; + } +} diff --git a/KruacentExiled/KE.CustomRoles/API/Features/Abilities/BaseCompAbility.cs b/KruacentExiled/KE.CustomRoles/API/Features/Abilities/BaseCompAbility.cs new file mode 100644 index 00000000..c1bf3e46 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/API/Features/Abilities/BaseCompAbility.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.CustomRoles.API.Features.Abilities +{ + public abstract class BaseCompAbility : MonoBehaviour + { + + public abstract bool Active { get; } + + public abstract void ToggleActive(); + } +} diff --git a/KruacentExiled/KE.CustomRoles/API/Features/Abilities/EmptyAbility.cs b/KruacentExiled/KE.CustomRoles/API/Features/Abilities/EmptyAbility.cs new file mode 100644 index 00000000..e1a7f84d --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/API/Features/Abilities/EmptyAbility.cs @@ -0,0 +1,25 @@ +using Exiled.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.API.Features.Abilities +{ + /// + /// an ability used to show text + /// + public abstract class EmptyAbility : KEAbilities + { + + public sealed override float Cooldown => 1; + protected override void GuiReady(StringBuilder sb, Player player) + { + + } + + + + } +} diff --git a/KruacentExiled/KE.CustomRoles/API/Features/Abilities/KEAbilityCost.cs b/KruacentExiled/KE.CustomRoles/API/Features/Abilities/KEAbilityCost.cs new file mode 100644 index 00000000..9f9241aa --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/API/Features/Abilities/KEAbilityCost.cs @@ -0,0 +1,45 @@ +using Exiled.API.Features; +using KE.CustomRoles.Abilities.FireAbilities; +using KE.Utils.API.CustomStats; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.API.Features.Abilities +{ + public abstract class KEAbilityCost : KEAbilities + { + + public abstract int Cost { get; } + public virtual string CostName { get; } = string.Empty; + protected sealed override bool AbilityUsed(Player player) + { + bool result = CanLaunchAbility(player); + if (result) + { + result = LaunchedAbility(player); + } + return result; + } + + protected virtual bool LaunchedAbility(Player player) + { + return true; + } + + public abstract bool CanLaunchAbility(Player player); + + + protected override void AbilityGui(StringBuilder sb,Player player) + { + base.AbilityGui(sb,player); + sb.Append("("); + sb.Append(Cost); + sb.Append(CostName); + sb.Append(")"); + sb.Append(" "); + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/API/Features/Abilities/KEAbilityLimited.cs b/KruacentExiled/KE.CustomRoles/API/Features/Abilities/KEAbilityLimited.cs new file mode 100644 index 00000000..f07f316f --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/API/Features/Abilities/KEAbilityLimited.cs @@ -0,0 +1,74 @@ +using Exiled.API.Features; +using KE.CustomRoles.Abilities.FireAbilities; +using KE.Utils.API.CustomStats; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.API.Features.Abilities +{ + public abstract class KEAbilityLimited : KEAbilities + { + + public abstract int Uses { get; } + + private Dictionary uses =new Dictionary(); + + public override void AddAbility(Player player) + { + uses[player] = Uses; + base.AddAbility(player); + } + protected sealed override bool AbilityUsed(Player player) + { + bool result = false; + int use = GetUse(player); + if (use > 0) + { + uses[player]--; + result = LaunchedAbility(player); + } + if(use <= 0) + { + RemoveAbility(player); + } + + return result; + } + + protected virtual bool LaunchedAbility(Player player) + { + return true; + } + + + public int GetUse(Player player) + { + if(!uses.TryGetValue(player,out int result)) + { + return -1; + } + + return result; + } + + + + protected override void AbilityGui(StringBuilder sb,Player player) + { + base.AbilityGui(sb, player); + GuiUses(sb, player); + } + + + protected void GuiUses(StringBuilder sb, Player player) + { + sb.Append("("); + sb.Append(uses[player]); + sb.Append(" left)"); + sb.Append(" "); + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/API/Features/Abilities/NeedCompAbility.cs b/KruacentExiled/KE.CustomRoles/API/Features/Abilities/NeedCompAbility.cs new file mode 100644 index 00000000..b5b2ae9c --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/API/Features/Abilities/NeedCompAbility.cs @@ -0,0 +1,47 @@ +using Exiled.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.CustomRoles.API.Features.Abilities +{ + public abstract class NeedCompAbility : KEAbilities where T : BaseCompAbility + { + + + protected virtual bool AddCompIfMissing { get; } = false; + + protected sealed override bool AbilityUsed(Player player) + { + bool result = CanLaunchAbility(player,out T comp); + if (result) + { + result = LaunchedAbility(player,comp); + } + return result; + } + + protected abstract bool LaunchedAbility(Player player, T component); + + public virtual bool CanLaunchAbility(Player player, out T component) + { + if(!player.GameObject.TryGetComponent(out component)) + { + if (AddCompIfMissing) + { + component = player.GameObject.AddComponent(); + } + else + { + return false; + } + } + + return component.Active; + } + + } +} diff --git a/KruacentExiled/KE.CustomRoles/API/Features/Abilities/ToggleableAbility.cs b/KruacentExiled/KE.CustomRoles/API/Features/Abilities/ToggleableAbility.cs new file mode 100644 index 00000000..2032a441 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/API/Features/Abilities/ToggleableAbility.cs @@ -0,0 +1,51 @@ +using Exiled.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.CustomRoles.API.Features.Abilities +{ + public abstract class ToggleableAbility : KEAbilities + { + + public abstract Color ColorOn { get; } + private string colorOn = string.Empty; + public abstract Color ColorOff { get; } + private string colorOff = string.Empty; + + public abstract bool GetState(Player player); + + protected override void Gui(StringBuilder sb, Player player) + { + sb.Append(""); + AbilityGui(sb, player); + GuiReady(sb, player); + GuiArrow(sb, player); + sb.Append(""); + } + + + + } +} diff --git a/KruacentExiled/KE.CustomRoles/API/Features/CustomSCP.cs b/KruacentExiled/KE.CustomRoles/API/Features/CustomSCP.cs new file mode 100644 index 00000000..09cf8420 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/API/Features/CustomSCP.cs @@ -0,0 +1,79 @@ +using Exiled.API.Features; +using Exiled.API.Features.Core.UserSettings; +using KE.Utils.API.Features.SCPs; +using KE.Utils.API.Settings; +using KE.Utils.API.Settings.SettingsCategories; +using System.Collections.Generic; +using System.Linq; + +namespace KE.CustomRoles.API.Features +{ + public abstract class CustomSCP : KECustomRole + { + public const int MinValue = -5; + public const int MaxValue = 5; + public const int DefaultValue = 0; + + private SliderSetting sliderSetting; + public abstract bool IsSupport { get; } + + protected abstract int SettingId { get; } + private static HeaderSetting header = null; + private static int HeaderId => MainPlugin.Configs.HeaderId; + + private static SettingsCategory category; + public static IEnumerable All => Registered.Where(c => c is CustomSCP).Cast(); + public override void Init() + { + base.Init(); + if (SpawnChance <= 0) + { + return; + } + + + if (category is null) + { + header = new HeaderSetting(HeaderId, "SCP Spawn Preferences",string.Empty,true); + category = new SettingsCategory(header, 1001, new List()); + } + + sliderSetting= new SliderSetting(SettingId, GetTranslation("en", TranslationKeyName), MinValue, MaxValue, DefaultValue, true); + + category.Settings.Add(sliderSetting); + } + + + public override void Destroy() + { + SettingBase.Unregister(); + base.Destroy(); + } + + protected override void RoleAdded(Player player) + { + SCPTeam.AddSCP(player.ReferenceHub); + base.RoleAdded(player); + } + + + public int GetPreferences(Player player) + { + if(sliderSetting is null) + { + Log.Error("slider setting is null in custom scp"); + return -6; + } + + + if (!SettingBase.TryGetSetting(player, sliderSetting.Id, out var setting)) return -6; + return (int) setting.SliderValue; + } + + public override bool IsAvailable(Player player) + { + return false; + } + + } +} diff --git a/KruacentExiled/KE.CustomRoles/API/Features/GlobalCustomRole.cs b/KruacentExiled/KE.CustomRoles/API/Features/GlobalCustomRole.cs new file mode 100644 index 00000000..32aba97f --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/API/Features/GlobalCustomRole.cs @@ -0,0 +1,140 @@ +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using Exiled.API.Features.Pools; +using Exiled.CustomRoles.API.Features; +using Exiled.Events.EventArgs.Player; +using InventorySystem.Configs; +using KE.CustomRoles.API.Interfaces; +using KE.Utils.API.Displays.DisplayMeow; +using LiteNetLib4Mirror.Open.Nat; +using MEC; +using PlayerRoles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.CustomRoles.API.Features +{ + + public abstract class GlobalCustomRole : KECustomRole + { + public sealed override RoleTypeId Role { get; set; } = RoleTypeId.None; + public abstract SideEnum Side { get; set; } + public override bool KeepInventoryOnSpawn { get; set; } = true; + + public override string Name + { + get + { + return Side.ToString().ToUpper() + "_" + InternalName.RemoveSpaces(); + } + } + + public override void AddRole(Player player) + { + SideEnum side = SideClass.Get(player.Role.Side); + + if (side != Side) + { + Log.Error($"tried to give a global custom role to a player in the wrong side ({side} instead of {Side})"); + return; + } + + + base.AddRole(player); + } + + protected override void AttributeHealth(Player player) + { + player.MaxHealth *= MaxHealthMultiplicator; + player.Health = player.MaxHealth; + } + + + + + public sealed override int MaxHealth { get; set; } + public virtual float MaxHealthMultiplicator { get; set; } = 1; + + protected override void ShowMessage(Player player) + { + StringBuilder sb = StringBuilderPool.Pool.Get(); + sb.Append(""); + IColor color = this as IColor; + if (color != null) + { + sb.Append(""); + } + + + sb.Append(GetTranslation(player,TranslationKeyName)); + + if (color != null) + { + sb.Append(""); + } + + if (player.IsScp) + { + sb.Append(player.Role.Name); + } + + + sb.AppendLine(""); + + if (MainPlugin.SettingHandler.GetDescriptionsSettings(player)) + { + sb.AppendLine(GetTranslation(player, TranslationKeyDesc)); + } + + + float delay = MainPlugin.SettingHandler.GetTime(player); + + DisplayHandler.Instance.AddHint(MainPlugin.CRHint, player, sb.ToString(), delay); + StringBuilderPool.Pool.Return(sb); + } + + + + public override bool IsAvailable(Player player) + { + //if (player.Role == RoleTypeId.Scp106) return false; + if (CurrentNumberOfSpawn >= Limit) return false; + return SideClass.Get(player.Role.Side) == Side; + } + + public override bool RoleCheck(RoleTypeId role) + { + return SideClass.Get(role.GetSide()) == Side; + } + } + + public enum SideEnum + { + Human, + SCP, + None, + } + + public static class SideClass + { + public static SideEnum Get(Side side) + { + return side switch + { + Side.Scp => SideEnum.SCP, + Side.Tutorial | Side.Mtf | Side.ChaosInsurgency => SideEnum.Human, + Side.None => SideEnum.None, + _ => SideEnum.None, + }; + } + } + +} diff --git a/KruacentExiled/KE.CustomRoles/API/Features/KEAbilities.cs b/KruacentExiled/KE.CustomRoles/API/Features/KEAbilities.cs new file mode 100644 index 00000000..e72ed34d --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/API/Features/KEAbilities.cs @@ -0,0 +1,769 @@ +using Exiled.API.Features; +using Exiled.API.Features.Pools; +using HintServiceMeow.Core.Enum; +using HintServiceMeow.Core.Extension; +using HintServiceMeow.Core.Models.Arguments; +using HintServiceMeow.Core.Models.Hints; +using KE.CustomRoles.Abilities.FireAbilities; +using KE.CustomRoles.API.HintPositions; +using KE.CustomRoles.API.Interfaces; +using KE.CustomRoles.API.Interfaces.Ability; +using KE.CustomRoles.Settings; +using KE.Utils.API; +using KE.Utils.API.Displays.DisplayMeow; +using KE.Utils.API.Displays.DisplayMeow.Placements; +using KE.Utils.API.Displays.Feeds; +using KE.Utils.API.Features; +using KE.Utils.API.Translations; +using MEC; +using PlayerRoles.FirstPersonControl.Thirdperson; +using PlayerRoles.PlayableScps.HUDs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using UnityEngine; + +namespace KE.CustomRoles.API.Features +{ + /// + /// Our version of the + /// + public abstract class KEAbilities + { + #region static stuff + private static HashSet registered = new HashSet(); + public static HashSet Registered => registered; + #endregion + + #region abstract stuff + public abstract string Name { get; } + + public string TranslationKeyName => Name+"_"+ AbilityNameKey; + public string TranslationKeyDesc => Name+"_"+ AbilityDescriptionKey; + + /// + /// in seconds + /// + public abstract float Cooldown { get; } + #endregion + + private Dictionary LastUsed = new Dictionary(); + private static Dictionary TypeToAbility { get; } = new Dictionary(); + private static Dictionary NameToAbility { get; } = new Dictionary(); + public HashSet Players { get; } = new HashSet(); + public IReadOnlyCollection Selected => selected; + private HashSet selected = new HashSet(); + private HashSet blockedPlayer = new HashSet(); + private HashSet playerWithActiveAbility = new HashSet(); + + + + public static Dictionary> PlayersAbility { get;} = new Dictionary>(); + + + + + protected const string AbilityNameKey = "Name"; + protected const string AbilityDescriptionKey = "Desc"; + + public const string AbilityTranslationId = "Ability"; + protected KEAbilities() + { + + } + + + private bool activated = false; + private void OneTimeInit() + { + if (activated) return; + TranslationHub.Add(AbilityTranslationId, "en", "AbilityReady", "READY"); + TranslationHub.Add(AbilityTranslationId, "fr", "AbilityReady", "PRÊT"); + + + activated = true; + } + + public virtual void Init() + { + if (NameToAbility.ContainsKey(Name)) + { + KEAbilities other = NameToAbility[Name]; + Log.Warn($"{GetType().FullName} have the same name as {other.GetType().FullName}. Skipping..."); + return; + } + + + TypeToAbility.Add(GetType(), this); + NameToAbility.Add(Name, this); + + OneTimeInit(); + InternalSubscribeEvent(); + + var translate = SetTranslation(); + + + TranslationHub.Add(AbilityTranslationId, translate); + } + + + protected abstract Dictionary> SetTranslation(); + + public static string GetTranslation(Player player, string key) + { + return TranslationHub.Get(player, AbilityTranslationId, key); + } + + + public static void ShowEffectHint(Player player,string translationKey) + { + + string text = GetTranslation(player, translationKey); + + float delay = MainPlugin.SettingHandler.GetTime(player); + DisplayHandler.Instance.AddHint(MainPlugin.CREffect, player, text, delay); + } + + public static void ShowEffectHint(Player player, string text, float delay) + { + delay = MainPlugin.SettingHandler.GetTime(player); + DisplayHandler.Instance.AddHint(MainPlugin.CREffect, player, text, delay); + } + + + public static void TranslationFeed(Player player, string key) + { + HintFeed.AddFeed(player, GetTranslation(player, key)); + } + public void Destroy() + { + InternalUnsubcribeEvent(); + } + + private void InternalSubscribeEvent() + { + + SubscribeEvents(); + } + + private void InternalUnsubcribeEvent() + { + UnsubscribeEvents(); + } + + + + + + protected virtual void SubscribeEvents() + { + + } + + protected virtual void UnsubscribeEvents() + { + + } + + /// + /// + /// + /// + /// return true if the ability was used; returns false if the ability was not used and need to be refunded + protected virtual bool AbilityUsed(Player player) + { + return true; + } + + protected virtual void AbilityAdded(Player player) + { + + } + protected virtual void AbilityRemoved(Player player) + { + + } + + + public void ShowAbility(Player player) + { + float time = MainPlugin.SettingHandler.GetAbilityTime(player); + + StringBuilder sb = StringBuilderPool.Pool.Get(); + + sb.Append(""); + + if(this is IDynamicName dynamicName) + { + dynamicName.GetName(sb, player); + } + else + { + sb.Append(GetTranslation(player, TranslationKeyName)); + } + + sb.AppendLine(""); + + + if(this is IDynamicDescription dynamicDescription) + { + dynamicDescription.GetDescription(sb, player); + } + else + { + sb.Append(GetTranslation(player, TranslationKeyDesc)); + } + + DisplayHandler.Instance.AddHint(MainPlugin.AbilitiesDesc, player, sb.ToString(), time); + StringBuilderPool.Pool.Return(sb); + } + + public void SelectAbility(Player player, bool hide = false) + { + if (selected.Add(player)) + { + Log.Debug($"player {player.Nickname} selected ability {this}"); + foreach (KEAbilities abilities in Registered.Where(a => a != this && a.Players.Contains(player))) + { + abilities.UnselectAbility(player); + } + if (!hide) + { + ShowAbility(player); + } + + } + } + + public void UnselectAbility(Player player) + { + if (selected.Remove(player)) + { + KELog.Debug($"player {player.Nickname} unselected ability {this}"); + } + + } + + public void RemoveAbility(Player player) + { + + if (Players.Contains(player)) + { + KELog.Debug($"player {player.Nickname} lost {this}"); + PlayersAbility[player].Remove(this); + Players.Remove(player); + UnselectAbility(player); + AbilityRemoved(player); + } + } + public virtual void AddAbility(Player player) + { + + bool result = Players.Add(player); + Log.Debug($"player {player.Nickname} got {this} ({result})"); + if (result) + { + if(!PlayersAbility.TryGetValue(player,out var _)) + { + PlayersAbility.Add(player, new List()); + } + PlayersAbility[player].Add(this); + InitHints(player); + + + AbilityAdded(player); + } + + } + public void UseAbility(Player player) + { + if (AbilityUsed(player)) + { + if (this is IDuration durationAbility) + { + this.playerWithActiveAbility.Add(player); + Timing.CallDelayed(durationAbility.Duration, () => + { + durationAbility.ActionAfterAbility(player); + this.playerWithActiveAbility.Remove(player); + }); + } + + LastUsed[player] = DateTime.Now; + } + } + + public bool IsAbilityActive(Player player) + { + return this.playerWithActiveAbility.Contains(player); + } + + public virtual bool Check(Player player) + { + if(player != null) + { + return Players.Contains(player); + } + return false; + } + + public bool CanUse(Player player, out string result) + { + if (player == null) + { + result = "player null"; + return false; + } + if (!Players.Contains(player)) + { + result = "cannot use this"; + return false; + } + if (this.blockedPlayer.Contains(player)) + { + result = "blocked"; + return false; + } + + if (!LastUsed.ContainsKey(player)) + { + result = "never used"; + return true; + } + + DateTime dateTime = LastUsed[player] + TimeSpan.FromSeconds(Cooldown); + if (DateTime.Now > dateTime) + { + result = "ok"; + return true; + } + result = "in cooldown"; + return false; + } + + + + public static void UseSelected(Player player) + { + if(TryGetSelected(player,out var ability)) + { + Log.Debug("got selected " + ability.Name); + if (ability.CanUse(player, out string _)) + { + Log.Debug("can use"); + ability.UseAbility(player); + } + } + } + + public static bool TryAddToPlayer(Type typeAbility,Player player) + { + if (!TypeToAbility.TryGetValue(typeAbility, out var ability)) return false; + + + ability.AddAbility(player); + return true; + } + public static bool TryAddToPlayer(string name, Player player) + { + if (!TryGet(name,out var ability)) return false; + + + ability.AddAbility(player); + return true; + } + + public static void RemoveAllSelect(Player player) + { + if (!PlayersAbility.ContainsKey(player)) return; + + foreach(KEAbilities ability in PlayersAbility[player]) + { + ability.selected.Remove(player); + } + + } + + public static void SelectFirstAbility(Player player,bool hide = false) + { + if(PlayersAbility.TryGetValue(player,out var list)) + { + list[0].SelectAbility(player); + + } + + } + + public static void Remove(string name,Player player) + { + KEAbilities abilities = Get(name); + + if(abilities != null) + { + abilities.RemoveAbility(player); + } + + } + + public static void TryRemoveFromPlayer(Player player) + { + RemoveAllSelect(player); + foreach(KEAbilities abilities in Registered) + { + if (abilities.Players.Contains(player)) + { + abilities.RemoveAbility(player); + } + } + } + + public static void TemporaryRemoveAbilities(Player player) + { + foreach(KEAbilities ability in PlayersAbility[player]) + { + ability.blockedPlayer.Add(player); + } + } + + public static void ReaffectRemovedAbilities(Player player) + { + foreach (KEAbilities ability in PlayersAbility[player]) + { + ability.blockedPlayer.Remove(player); + } + } + + + public bool IsSelected(Player player) + { + return Selected.Contains(player); + } + + + + #region register + private bool TryRegister() + { + + + if (!Registered.Contains(this)) + { + Registered.Add(this); + Init(); + Log.Debug("registered " + Name); + return true; + } + + + + return false; + } + + + public static IEnumerable Register(Assembly assembly =null) + { + IEnumerable abilities = ReflectionHelper.GetObjects(assembly); + List result = abilities.ToList(); + + foreach(KEAbilities ability in abilities) + { + + if (!ability.TryRegister()) + { + Log.Warn("couldn't register KEability " + ability); + result.Remove(ability); + } + + } + registered = result.ToHashSet(); + return result; + + } + + + private bool TryUnregister() + { + Destroy(); + if (Registered.Remove(this)) + { + Log.Debug("unregistered " + this); + return true; + } + Log.Warn(this + " was not registered"); + return false; + } + + public static IEnumerable Unregister() + { + List list = new List(); + foreach (KEAbilities item in Registered) + { + item.TryUnregister(); + list.Add(item); + } + + return list; + } + #endregion + + #region getters + + public static KEAbilities Get(string name) + { + foreach(KEAbilities kEAbilities in Registered) + { + if (name == kEAbilities.Name) + { + return kEAbilities; + } + } + + return null; + } + + public static bool TryGet(string name, out KEAbilities ability) + { + ability = Get(name); + return ability != null; + } + + public static KEAbilities GetSelected(Player player) + { + foreach(KEAbilities ability in Registered) + { + if (ability.IsSelected(player)) + { + return ability; + } + } + return null; + } + + public static bool TryGetSelected(Player player, out KEAbilities ability) + { + ability = GetSelected(player); + return ability != null; + } + + + + + #endregion + + #region gui + + + + protected virtual void GuiReady(StringBuilder sb, Player player) + { + + if (CanUse(player, out var output)) + { + sb.Append("["); + sb.Append(GetTranslation(player, "AbilityReady")); + sb.Append("]"); + } + else + { + DateTime dateTime = LastUsed[player] + TimeSpan.FromSeconds(Cooldown); + sb.Append("["); + sb.Append(Math.Round((dateTime - DateTime.Now).TotalSeconds, 0)); + sb.Append("s]"); + } + } + + protected void GuiArrow(StringBuilder sb, Player player) + { + if (IsSelected(player)) + { + string arrow = SettingHandler.Instance.GetArrow(player); + if (string.IsNullOrEmpty(arrow)) + { + arrow = SettingHandler.baseArrow; + } + sb.Append(arrow); + } + } + + protected void GuiAbilityName(StringBuilder sb, Player player) + { + if(this is IDynamicName dynamic) + { + dynamic.GetName(sb, player); + } + else + { + sb.Append(GetTranslation(player, TranslationKeyName)); + } + + + sb.Append(" "); + } + + /// + /// The ability gui without the arrow or the ready + /// + protected virtual void AbilityGui(StringBuilder sb, Player player) + { + GuiAbilityName(sb, player); + + } + + /// + /// The full ability gui + /// + protected virtual void Gui(StringBuilder sb,Player player) + { + IConditional conditional = this as IConditional; + + if(conditional != null) + { + if (conditional.CheckCondition(player)) + { + sb.Append(""); + } + else + { + sb.Append(""); + } + } + else + { + sb.Append(""); + } + + AbilityGui(sb, player); + GuiReady(sb, player); + GuiArrow(sb, player); + + sb.Append(""); + } + + + public static Dictionary> PlayersHints { get; } = new Dictionary>(); + public static Dictionary AddonHints { get; } = new Dictionary(); + public const int InitialAbilitySlot = 5; + private void InitHints(Player player) + { + + + if (!PlayersHints.TryGetValue(player, out var _)) + { + PlayersHints.Add(player, new List()); + for (int i = 0; i < InitialAbilitySlot; i++) + { + AbilitiesPosition position = AbilitiesPosition.GetIndex(i); + AbstractHint hint = DisplayHandler.Instance.CreateAuto(player, arg => UpdateHint(arg), position.HintPlacement); + PlayersHints[player].Add(hint); + AddonHints.Add(hint, DisplayHandler.Instance.CreateAuto(player, arg => UpdateAddon(arg,hint), AddonAbilitiesPosition.Get(position).HintPlacement,HintSyncSpeed.Slow)); + } + + + + } + + } + + private static HashSet addonAbilitiesexception = new HashSet(); + + /// + /// + /// + /// + /// the other hint + /// + private static string UpdateAddon(AutoContentUpdateArg arg,AbstractHint hint) + { + Player player = Player.Get(arg.PlayerDisplay.ReferenceHub); + int index = PlayersHints[player].FindIndex(h => h == hint); + + if (PlayersAbility[player].TryGet(index, out KEAbilities ability)) + { + if (ability is ICustomIcon icon && !addonAbilitiesexception.Contains(ability)) + { + StringBuilder sb = StringBuilderPool.Pool.Get(); + try + { + sb.Append(icon.IconName.RawString); + } + catch(KeyNotFoundException) + { + addonAbilitiesexception.Add(ability); + Log.Error("Icon for ability "+ ability.Name+ " is missing"); + return " "; + } + return StringBuilderPool.Pool.ToStringReturn(sb); + } + } + return " "; + } + + + + + + private static string UpdateHint(AutoContentUpdateArg arg) + { + Player player = Player.Get(arg.PlayerDisplay.ReferenceHub); + int index = PlayersHints[player].FindIndex(h => h == arg.Hint); + + + + if (PlayersAbility[player].TryGet(index,out KEAbilities ability)) + { + + StringBuilder sb = StringBuilderPool.Pool.Get(); + ability.Gui(sb,player); + return StringBuilderPool.Pool.ToStringReturn(sb); + } + + + + return " "; + } + + + public static string UpdateGUI(Player player) + { + StringBuilder builder = StringBuilderPool.Pool.Get(); + + + List allAbilities = PlayersAbility[player]; + + for (int i = 0; i < allAbilities.Count; i++) + { + + + KEAbilities ability = allAbilities[i]; + ability.Gui(builder,player); + + + } + + string msg = builder.ToString(); + StringBuilderPool.Pool.Return(builder); + return msg; + } + + + + + + public static void ShowAbilityHint(Player player, string text) + { + float delay = MainPlugin.SettingHandler.GetTime(player); + DisplayHandler.Instance.AddHint(MainPlugin.AbilitiesDesc, player, text, delay); + } + + + #endregion + + public override string ToString() + { + return Name; + } + + } +} diff --git a/KruacentExiled/KE.CustomRoles/API/Features/KECustomRole.cs b/KruacentExiled/KE.CustomRoles/API/Features/KECustomRole.cs new file mode 100644 index 00000000..55778ccc --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/API/Features/KECustomRole.cs @@ -0,0 +1,805 @@ +using CustomPlayerEffects; +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Items; +using Exiled.API.Features.Pools; +using Exiled.CustomItems.API.Features; +using Exiled.CustomRoles; +using Exiled.CustomRoles.API; +using Exiled.CustomRoles.API.Features; +using Exiled.Events.EventArgs.Player; +using Exiled.Events.EventArgs.Server; +using HintServiceMeow.Core.Models.Hints; +using InventorySystem.Configs; +using KE.CustomRoles.API.HintPositions; +using KE.CustomRoles.API.Interfaces; +using KE.CustomRoles.Events.EventArgs; +using KE.Utils.API.Displays.DisplayMeow; +using KE.Utils.API.Displays.DisplayMeow.Placements; +using KE.Utils.API.Translations; +using MEC; +using PlayerRoles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Sockets; +using System.Reflection; +using System.Text; +using UnityEngine; +using YamlDotNet.Core.Tokens; + +namespace KE.CustomRoles.API.Features +{ + public abstract class KECustomRole : CustomRole + { + public const float TimeAttributingInventory = .25f; + public sealed override uint Id { get; set; } = 0; + public override string Name + { + get + { + return Role.ToString().ToUpper() + "_" + InternalName.RemoveSpaces(); + } + set + { + + } + } + + public sealed override bool RemovalKillsPlayer + { + get + { + return false; + } + set + { + + } + } + + public virtual string InternalName => GetType().Name; + + public sealed override string Description { get; set; } = string.Empty; + + public static new HashSet Registered { get; } = new HashSet(); + + public sealed override string CustomInfo { get; set; } + + /// + /// the max number of people who can have this role in a round + /// + public virtual int Limit { get; set; } = 1; + /// + /// the number of people who had this role in a round + /// + public int CurrentNumberOfSpawn { get; set; } = 0; + + public static void ResetNumberOfSpawn() + { + + foreach(KECustomRole cr in Registered) + { + cr.CurrentNumberOfSpawn = 0; + } + + } + + + protected abstract Dictionary> SetTranslation(); + + + + public abstract override RoleTypeId Role { get; } + + /// + /// + /// + public virtual HashSet Abilities { get; } + + public sealed override bool IgnoreSpawnSystem { get; set; } = true; + public sealed override List CustomAbilities { get; set; } = null; + protected override void ShowMessage(Player player) + { + //string msg = MainPlugin.Translations.GettingNewRole; + //msg = msg.Replace("%Name%", PublicName).Replace("%Desc%",Description); + StringBuilder sb =StringBuilderPool.Pool.Get(); + sb.Append(""); + IColor color = this as IColor; + if (color != null) + { + sb.Append(""); + } + + sb.Append(GetTranslation(player,TranslationKeyName)); + + if (color != null) + { + sb.Append(""); + } + sb.AppendLine(""); + + if (MainPlugin.SettingHandler.GetDescriptionsSettings(player)) + { + sb.AppendLine(GetTranslation(player, TranslationKeyDesc)); + + if(this is IHealable heal) + { + sb.AppendLine(GetTranslation(player, TranslationHealable)); + } + + } + + + float delay = MainPlugin.SettingHandler.GetTime(player); + + DisplayHandler.Instance.AddHint(MainPlugin.CRHint, player, sb.ToString(), delay); + StringBuilderPool.Pool.Return(sb); + } + + public void Show(Player player) + { + ShowMessage(player); + } + + + private static Dictionary typeLookupTable = new Dictionary(); + private static Dictionary stringLookupTable = new Dictionary(); + + protected const string CustomRoleNameKey = "Name"; + protected const string CustomRoleDescriptionKey = "Desc"; + public string TranslationKeyName => Name + "_" + CustomRoleNameKey; + public string TranslationKeyDesc => Name + "_" + CustomRoleDescriptionKey; + + public const string CustomRoleTranslationId = "CustomRole"; + public override void Init() + { + typeLookupTable.Add(GetType(), this); + stringLookupTable.Add(Name, this); + OneTimeInit(); + InternalSubscribeEvents(); + SubscribeEvents(); + + var translate = SetTranslation(); + + + TranslationHub.Add(CustomRoleTranslationId, translate); + + Log.Debug("adding keys to "+Name); + } + + private bool activated = false; + + private const string TranslationGUI = "CustomRoleGUI"; + private const string TranslationHealable = "CustomRoleHealable"; + private static Dictionary> SetStaticTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationGUI] = "Current Role", + [TranslationHealable] = "This Custom Role is healable!", + }, + ["fr"] = new Dictionary() + { + [TranslationGUI] = "Role", + [TranslationHealable] = "Ce custom role peut être enlevé avec un object!", + }, + }; + } + + private void OneTimeInit() + { + if (activated) return; + TranslationHub.Add(CustomRoleTranslationId, SetStaticTranslation()); + + + activated = true; + } + public static string GetTranslation(Player player, string key) + { + return TranslationHub.Get(player, CustomRoleTranslationId, key); + } + public static string GetTranslation(string lang, string key) + { + return TranslationHub.Get(lang, CustomRoleTranslationId, key); + } + + public override void Destroy() + { + typeLookupTable.Remove(GetType()); + stringLookupTable.Remove(Name); + InternalUnsubscribeEvents(); + UnsubscribeEvents(); + } + + protected virtual void InternalSubscribeEvents() + { + if(this is IHealable) + { + Exiled.Events.Handlers.Player.UsedItem += OnUsedItem; + } + + if(this is IEffectImmunity) + { + Exiled.Events.Handlers.Player.ReceivingEffect += OnReceivingEffect; + } + + } + + + protected virtual void InternalUnsubscribeEvents() + { + if (this is IHealable) + { + Exiled.Events.Handlers.Player.UsedItem -= OnUsedItem; + } + if (this is IEffectImmunity) + { + Exiled.Events.Handlers.Player.ReceivingEffect -= OnReceivingEffect; + } + } + + + private void OnUsedItem(UsedItemEventArgs ev) + { + if (!Check(ev.Player)) return; + IHealable healable = this as IHealable; + if(healable.HealItem is null || healable.HealItem.Count == 0) + { + Log.Warn("no healable item found for" + Name); + return; + } + + + if (healable.HealItem.Contains(ev.Item.Type)) + { + RemoveRole(ev.Player); + } + } + + + private void OnReceivingEffect(ReceivingEffectEventArgs ev) + { + if (!Check(ev.Player)) return; + if (!ev.IsAllowed) return; + + IEffectImmunity effectImmunity = this as IEffectImmunity; + + if (effectImmunity.ImmuneEffects is null || effectImmunity.ImmuneEffects.Count == 0) + { + Log.Warn("no healable item found for" + Name); + return; + } + if (effectImmunity.ImmuneEffects.Contains(ev.Effect.GetEffectType())) + { + ev.IsAllowed = false; + } + + } + + + public static void SpawnStartRound(Misc.Features.Spawn.SpawnedEventArgs ev) + { + GiveRandomRole(Player.List.Except(ev.CustomRoles)); + } + + public static void RespawnCustomRole(RespawnedTeamEventArgs ev) + { + GiveRandomRole(ev.Players); + } + + public static void ShowCustomRole(VerifiedEventArgs ev) + { + Timing.CallDelayed(1, delegate + { + Player player = ev.Player; + DisplayHandler.Instance.CreateAuto(player, arg => CurrentRole(player), CurrentCustomRolePosition.HintPlacement); + }); + + + } + + + + + public static string CurrentRole(Player player) + { + StringBuilder sb = StringBuilderPool.Pool.Get(); + + if (HasCustomRole(player)) + { + KECustomRole kECustomRole = Get(player).First(); + + sb.Append(GetTranslation(player, TranslationGUI)); + sb.AppendLine(" : "); + sb.AppendLine(); + sb.Append(""); + sb.Append(GetTranslation(player,kECustomRole.TranslationKeyName)); + sb.Append(""); + } + else if (player.IsDead) + { + Player spectating = GetSpectatingPlayer(player); + KECustomRole customRole = null; + if (spectating != null) + { + customRole = Get(spectating).FirstOrDefault(); + } + + + if(customRole != null) + { + sb.Append(GetTranslation(player, TranslationGUI)); + sb.AppendLine(" : "); + sb.AppendLine(); + sb.Append(""); + sb.Append(GetTranslation(player,customRole.TranslationKeyName)); + sb.Append(""); + } + } + + string result = StringBuilderPool.Pool.ToStringReturn(sb); + + if (string.IsNullOrEmpty(result)) + { + result = " "; + } + + + return result; + } + + private static Player GetSpectatingPlayer(Player spectator) + { + + foreach(Player player in Player.Enumerable) + { + if (player.CurrentSpectatingPlayers.Contains(spectator)) + { + return player; + } + } + + return null; + + + } + + + protected void ShowEffectHint(Player player, string text) + { + float delay = MainPlugin.SettingHandler.GetTime(player); ; + DisplayHandler.Instance.AddHint(MainPlugin.CREffect, player, text, delay); + } + protected void ShowEffectHint(Player player, string text, float delay) + { + DisplayHandler.Instance.AddHint(MainPlugin.CREffect, player, text, delay); + } + + + + + + #region addrole + public override void AddRole(Player player) + { + Player player2 = player; + Log.Debug(Name + ": Adding role to " + player2.Nickname + "."); + Log.Debug(Name + ": old role type " + player2.Role.Type + "."); + Log.Debug(Name + ": new role type " + Role + "."); + + + ReceivingCustomRoleEventArgs ev1 = new ReceivingCustomRoleEventArgs(player2,this); + + Events.Handlers.KECustomRole.OnReceivingCustomRole(ev1); + + if (!ev1.IsAllowed) + { + Log.Debug(Name + ": role cancelled by plugin"); + return; + } + + foreach(KECustomRole cr in Get(player)) + { + cr.RemoveRole(player); + } + + + + CurrentNumberOfSpawn++; + + + + + if (Role != RoleTypeId.None) + { + Log.Debug("new role exist"); + if (KeepPositionOnSpawn) + { + if (KeepInventoryOnSpawn) + { + player2.Role.Set(Role, SpawnReason.ForceClass, RoleSpawnFlags.None); + } + else + { + player2.Role.Set(Role, SpawnReason.ForceClass, RoleSpawnFlags.AssignInventory); + } + } + else if (KeepInventoryOnSpawn && player2.IsAlive) + { + player2.Role.Set(Role, SpawnReason.ForceClass, RoleSpawnFlags.UseSpawnpoint); + } + else + { + player2.Role.Set(Role, SpawnReason.ForceClass, RoleSpawnFlags.AssignInventory); + } + } + + + + TrackedPlayers.Add(player2); + + Timing.CallDelayed(TimeAttributingInventory, delegate + { + ClearInventory(player2); + GiveInventory(player2); + GiveAmmo(player2); + }); + + + AttributeHealth(player2); + AttributeScale(player2); + + SetCustomInfo(player2); + + SetAbilities(player2); + + SetSpawn(player2); + + + ShowMessage(player2); + + RoleAdded(player2); + player2.UniqueRole = Name; + player2.TryAddCustomRoleFriendlyFire(Name, CustomRoleFFMultiplier); + ReceivedCustomRoleEventArgs ev2 = new ReceivedCustomRoleEventArgs(player2, this); + + Events.Handlers.KECustomRole.OnReceivedCustomRole(ev2); + + } + + + public static readonly HintPosition CurrentCustomRolePosition = new CurrentCustomRolePosition(); + + + + protected virtual void ClearInventory(Player player) + { + if (!KeepInventoryOnSpawn) + { + player.ClearInventory(); + } + } + + protected virtual void GiveInventory(Player player) + { + + for (int i = 0; i < Inventory.Count; i++) + { + string item = Inventory[i]; + Log.Debug(Name + ": Adding " + item + " to inventory."); + if (TryAddItem(player, item) && CustomItem.TryGet(item, out CustomItem customItem) && customItem is CustomWeapon customWeapon && player.CurrentItem is Firearm firearm && !customWeapon.Attachments.IsEmpty()) + { + firearm.AddAttachment(customWeapon.Attachments); + Log.Debug(Name + ": Applied attachments to " + item + "."); + } + } + } + + protected virtual void GiveAmmo(Player player) + { + if (Ammo.Count > 0) + { + AmmoType[] values = EnumUtils.Values; + foreach (AmmoType ammoType in values) + { + if (ammoType != 0) + { + player.SetAmmo(ammoType, (ushort)(Ammo.ContainsKey(ammoType) ? Ammo[ammoType] == ushort.MaxValue ? InventoryLimits.GetAmmoLimit(ammoType.GetItemType(), player.ReferenceHub) : Ammo[ammoType] : 0)); + } + } + } + } + + protected virtual void AttributeHealth(Player player) + { + player.Health = MaxHealth; + player.MaxHealth = MaxHealth; + } + + protected virtual void AttributeScale(Player player) + { + player.Scale = Scale; + } + + protected virtual void SetSpawn(Player player) + { + Vector3 spawnPosition = GetSpawnPosition(); + if (spawnPosition != Vector3.zero) + { + player.Position = spawnPosition; + } + } + + protected virtual void SetCustomInfo(Player player) + { + //MirrorExtensions.SetPlayerInfoForTargetOnly() + + string name = GetTranslation("legacy", TranslationKeyName); + + player.CustomInfo = player.CustomName; + if (!string.IsNullOrEmpty(name)) + { + player.CustomInfo += "\n" + name; + } + player.InfoArea &= ~(PlayerInfoArea.Nickname | PlayerInfoArea.Role); + } + + protected virtual void SetAbilities(Player player) + { + KEAbilities.TryRemoveFromPlayer(player); + + if (Abilities != null) + { + foreach (string name in Abilities) + { + if(!KEAbilities.TryAddToPlayer(name, player)) + { + Log.Error("couldn't find ability : " + name); + } + + } + KEAbilities.SelectFirstAbility(player,true); + } + } + + +#endregion + + + public override void RemoveRole(Player player) + { + base.RemoveRole(player); + if (Abilities != null) + { + KEAbilities.TryRemoveFromPlayer(player); + } + } + + /// + /// + /// + /// + /// true if the player can have this CR ; false otherwise + public virtual bool IsAvailable(Player player) + { + if (CurrentNumberOfSpawn >= Limit) return false; + return RoleCheck(player.Role); + } + + + public virtual bool RoleCheck(RoleTypeId role) + { + return Role == role; + } + + + /// + /// The chance of having this role, NOT the chance to have a role + /// + public new virtual float SpawnChance { get; set; } = 100; + + public static new KECustomRole Get(string fullinternalname) + { + return stringLookupTable[fullinternalname]; + } + + + public static bool TryGet(string fullinternalname,out KECustomRole customrole) + { + customrole = Get(fullinternalname); + return customrole != null; + } + + public static KECustomRole Get(RoleTypeId roleid,string name) + { + return Get(roleid.ToString().ToUpper() + "_" + name); + } + public static bool Get(RoleTypeId roleid, string name, out KECustomRole customrole) + { + customrole = Get(roleid, name); + return customrole != null; + } + + + public static bool HasCustomRole(Player player) + { + if (player is null) return false; + + + foreach(KECustomRole customRole in Registered) + { + if (customRole.Check(player)) + { + return true; + } + } + return false; + + } + + public static IEnumerable Get(Player player) + { + List cr = new List(); + foreach(KECustomRole ke in Registered) + { + if (ke.Check(player)) + { + cr.Add(ke); + } + } + return cr; + } + + #region Spawn + + /// + /// The chance to get a at the start or a respawn + /// + public static float Chance + { + get + { + return chance; + } + set + { + chance = Mathf.Clamp(value, 0f, 100f); + } + } + + private static float chance = 100; + + private static KECustomRole AssignRole(Dictionary roleChances) + { + float totalWeight = roleChances.Values.Sum(); + float randomValue = UnityEngine.Random.Range(0f, totalWeight); + + foreach (var role in roleChances) + { + randomValue -= role.Value; + if (randomValue <= 0) + return role.Key; + } + + return null; + } + + public static Dictionary GetAvailableCustomRole(Player player) + { + return Registered.Where(cr => cr.IsAvailable(player)).ToDictionary(c => c, c => c.SpawnChance); + } + + public static void GiveRandomRole(Player player) + { + if (player == null) + return; + if (UnityEngine.Random.Range(0f, 100f) > Chance) + { + Log.Debug("no luck"); + return; + } + + if(HasCustomRole(player)) + { + Log.Debug("already got a cr"); + return; + } + + + KECustomRole cr = AssignRole(GetAvailableCustomRole(player)); + Log.Debug($"{player.Id} : {cr?.Name}"); + + cr?.AddRole(player); + } + + + public static void GiveRandomRole(IEnumerable players) + { + foreach (Player p in players) + { + GiveRandomRole(p); + } + } + #endregion + + + + + + #region Register + + public bool TryRegister() + { + + if (!Registered.Contains(this)) + { + if (Registered.Any((KECustomRole r) => r.Name == Name)) + { + Log.Warn($"{Name} has tried to register with the same Name as another role: {Name}. It will not be registered!"); + return false; + } + + Registered.Add(this); + Init(); + return true; + } + + + return false; + } + public bool TryUnregister() + { + Destroy(); + if (!Registered.Remove(this)) + { + Log.Warn($"Cannot unregister {Name} [{Role}], it hasn't been registered yet."); + return false; + } + + return true; + } + + public static IEnumerable Register() + { + List list = new List(); + Type[] types = Assembly.GetCallingAssembly().GetTypes(); + foreach (Type type in types) + { + if (!type.IsAbstract && type.IsSubclassOf(typeof(KECustomRole))) + { + KECustomRole customRole = (KECustomRole)Activator.CreateInstance(type); + if (customRole.TryRegister()) + { + list.Add(customRole); + } + } + } + return list; + } + + + public static IEnumerable Unregister() + { + List list = new List(); + foreach (KECustomRole item in Registered) + { + item.TryUnregister(); + list.Add(item); + } + + return list; + } + + #endregion + } +} diff --git a/KruacentExiled/KE.CustomRoles/API/Features/KECustomRoleMultipleRole.cs b/KruacentExiled/KE.CustomRoles/API/Features/KECustomRoleMultipleRole.cs new file mode 100644 index 00000000..ccbb24ed --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/API/Features/KECustomRoleMultipleRole.cs @@ -0,0 +1,44 @@ +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using PlayerRoles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.API.Features +{ + public abstract class KECustomRoleMultipleRole : KECustomRole + { + + public override string Name + { + get + { + return "MULTIPLE_" + InternalName.RemoveSpaces(); + } + } + public override sealed RoleTypeId Role => RoleTypeId.None; + public override sealed int MaxHealth { get; set; }= -1; + + public abstract HashSet Roles { get; } + + + + + + protected override void AttributeHealth(Player player) + { + + } + + + public override bool RoleCheck(RoleTypeId role) + { + return Roles.Contains(role); + } + + } +} diff --git a/KruacentExiled/KE.CustomRoles/API/Features/RecyclableSettingId.cs b/KruacentExiled/KE.CustomRoles/API/Features/RecyclableSettingId.cs new file mode 100644 index 00000000..322c6907 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/API/Features/RecyclableSettingId.cs @@ -0,0 +1,78 @@ +using Exiled.API.Features; +using Exiled.API.Features.Core.UserSettings; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.API.Features +{ + public struct RecyclableSettingId : IEquatable + { + public const int MinThreshold = 16; + public readonly int Value; + + private static Queue FreeIds = new Queue(); + private static int _autoIncrement; + + private static int Min => MainPlugin.Configs.CustomScpSliderRangeMin; + private static int Max => MainPlugin.Configs.CustomScpSliderRangeMax; + + public RecyclableSettingId(SettingBase setting) + { + Value = setting.Id; + } + + /*public RecyclableSettingId() + { + int num = MinThreshold; + int value; + if (FreeIds.Count >= num) + { + value = FreeIds.Dequeue(); + } + else + { + value = _autoIncrement++ + Min; + if(value > Max) + { + throw new ArgumentOutOfRangeException("ID out of range, please increase the CustomScpSliderRangeMax in the plugins settings"); + } + } + Log.Debug("creating id =" + value); + Value = value; + + }*/ + + + public void Destroy() + { + if(Value != 0) + { + FreeIds.Enqueue(Value); + } + } + + + + public bool Equals(RecyclableSettingId other) + { + return other.Value == this.Value; + } + public override bool Equals(object obj) + { + if (obj is RecyclableSettingId other) + { + return Equals(other); + } + return false; + } + + public override int GetHashCode() + { + return Value; + } + + } +} diff --git a/KruacentExiled/KE.CustomRoles/API/HintPositions/AbilitiesPosition.cs b/KruacentExiled/KE.CustomRoles/API/HintPositions/AbilitiesPosition.cs new file mode 100644 index 00000000..bbcfdb8e --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/API/HintPositions/AbilitiesPosition.cs @@ -0,0 +1,47 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using HintServiceMeow.Core.Enum; +using KE.CustomRoles.API.Features; +using KE.Utils.API.Displays.DisplayMeow.Placements; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.API.HintPositions +{ + public class AbilitiesPosition : HintPosition + { + + + public const float BaseYPosition = 900; + public const float Increments = -50; + private float yposition = BaseYPosition; + public override float Xposition => -300; + + public override float Yposition => yposition; + + private int index; + public int Index => index; + + private static List nonalloc = new List(KEAbilities.InitialAbilitySlot); + + + public static AbilitiesPosition GetIndex(int index) + { + if (!nonalloc.TryGet(index, out AbilitiesPosition position)) + { + position = new AbilitiesPosition() + { + yposition = BaseYPosition - index * 50, + index = index + }; + } + Log.Debug($"get index {index} y pos=" + position.Yposition); + return position; + } + + public override HintAlignment HintAlignment => HintAlignment.Left; + } +} diff --git a/KruacentExiled/KE.CustomRoles/API/HintPositions/AddonAbilitiesPosition.cs b/KruacentExiled/KE.CustomRoles/API/HintPositions/AddonAbilitiesPosition.cs new file mode 100644 index 00000000..0e2f40d2 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/API/HintPositions/AddonAbilitiesPosition.cs @@ -0,0 +1,45 @@ +using Exiled.API.Features; +using HintServiceMeow.Core.Enum; +using KE.CustomRoles.API.Features; +using KE.Utils.API.Displays.DisplayMeow.Placements; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.API.HintPositions +{ + public class AddonAbilitiesPosition : HintPosition + { + + + private float yposition = AbilitiesPosition.BaseYPosition; + public override float Xposition => -350; + + public override float Yposition => yposition; + + + private static List nonalloc = new List(KEAbilities.InitialAbilitySlot); + + + public static AddonAbilitiesPosition Get(AbilitiesPosition abilitiesPosition) + { + if (abilitiesPosition is null) throw new ArgumentNullException(); + int index = abilitiesPosition.Index; + + if (!nonalloc.TryGet(index, out AddonAbilitiesPosition position)) + { + position = new AddonAbilitiesPosition() + { + yposition = abilitiesPosition.Yposition+5 + }; + } + Log.Debug($"addon get {index} y pos=" + position.Yposition); + return position; + + } + + public override HintAlignment HintAlignment => HintAlignment.Left; + } +} diff --git a/KruacentExiled/KE.CustomRoles/API/HintPositions/CurrentCustomRolePosition.cs b/KruacentExiled/KE.CustomRoles/API/HintPositions/CurrentCustomRolePosition.cs new file mode 100644 index 00000000..e818cbbe --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/API/HintPositions/CurrentCustomRolePosition.cs @@ -0,0 +1,21 @@ +using HintServiceMeow.Core.Enum; +using KE.Utils.API.Displays.DisplayMeow.Placements; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.API.HintPositions +{ + public class CurrentCustomRolePosition : HintPosition + { + public override float Xposition => 600; + + public override float Yposition => 1030; + + public override HintAlignment HintAlignment => HintAlignment.Center; + + public override string Name => "CurrentRole"; + } +} diff --git a/KruacentExiled/KE.CustomRoles/API/Interfaces/Ability/IConditional.cs b/KruacentExiled/KE.CustomRoles/API/Interfaces/Ability/IConditional.cs new file mode 100644 index 00000000..682e4ba1 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/API/Interfaces/Ability/IConditional.cs @@ -0,0 +1,15 @@ +using Exiled.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.API.Interfaces.Ability +{ + public interface IConditional + { + + bool CheckCondition(Player player); + } +} diff --git a/KruacentExiled/KE.CustomRoles/API/Interfaces/Ability/IDuration.cs b/KruacentExiled/KE.CustomRoles/API/Interfaces/Ability/IDuration.cs new file mode 100644 index 00000000..e68a58c5 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/API/Interfaces/Ability/IDuration.cs @@ -0,0 +1,15 @@ +using Exiled.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.API.Interfaces.Ability +{ + internal interface IDuration + { + float Duration { get; } + void ActionAfterAbility(Player player); + } +} diff --git a/KruacentExiled/KE.CustomRoles/API/Interfaces/Ability/IDynamicDescription.cs b/KruacentExiled/KE.CustomRoles/API/Interfaces/Ability/IDynamicDescription.cs new file mode 100644 index 00000000..8914727f --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/API/Interfaces/Ability/IDynamicDescription.cs @@ -0,0 +1,17 @@ +using Exiled.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.API.Interfaces.Ability +{ + public interface IDynamicDescription + { + + + void GetDescription(StringBuilder sb, Player player); + + } +} diff --git a/KruacentExiled/KE.CustomRoles/API/Interfaces/Ability/IDynamicName.cs b/KruacentExiled/KE.CustomRoles/API/Interfaces/Ability/IDynamicName.cs new file mode 100644 index 00000000..1b4c31df --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/API/Interfaces/Ability/IDynamicName.cs @@ -0,0 +1,15 @@ +using Exiled.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.API.Interfaces.Ability +{ + public interface IDynamicName + { + + void GetName(StringBuilder sb, Player player); + } +} diff --git a/KruacentExiled/KE.CustomRoles/API/Interfaces/IColor.cs b/KruacentExiled/KE.CustomRoles/API/Interfaces/IColor.cs new file mode 100644 index 00000000..18493e58 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/API/Interfaces/IColor.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + + +namespace KE.CustomRoles.API.Interfaces +{ + public interface IColor + { + + Color32 Color { get; } + } +} diff --git a/KruacentExiled/KE.CustomRoles/API/Interfaces/ICustomIcon.cs b/KruacentExiled/KE.CustomRoles/API/Interfaces/ICustomIcon.cs new file mode 100644 index 00000000..189f0071 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/API/Interfaces/ICustomIcon.cs @@ -0,0 +1,17 @@ +using KE.Utils.API.GifAnimator; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.API.Interfaces +{ + public interface ICustomIcon + { + + public abstract TextImage IconName { get; } + + + } +} diff --git a/KruacentExiled/KE.CustomRoles/API/Interfaces/IEffectImmunity.cs b/KruacentExiled/KE.CustomRoles/API/Interfaces/IEffectImmunity.cs new file mode 100644 index 00000000..58786df8 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/API/Interfaces/IEffectImmunity.cs @@ -0,0 +1,15 @@ +using Exiled.API.Enums; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.API.Interfaces +{ + public interface IEffectImmunity + { + + public abstract HashSet ImmuneEffects { get; } + } +} diff --git a/KruacentExiled/KE.CustomRoles/API/Interfaces/IHealable.cs b/KruacentExiled/KE.CustomRoles/API/Interfaces/IHealable.cs new file mode 100644 index 00000000..d7c207e9 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/API/Interfaces/IHealable.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.API.Interfaces +{ + public interface IHealable + { + + public abstract HashSet HealItem { get; } + } +} diff --git a/KruacentExiled/KE.CustomRoles/API/Interfaces/ISCPPreferences.cs b/KruacentExiled/KE.CustomRoles/API/Interfaces/ISCPPreferences.cs new file mode 100644 index 00000000..a3d74e4e --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/API/Interfaces/ISCPPreferences.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.API.Interfaces +{ + + /// + /// SCP custom role implementing this interface will allow them to have a preference slider like the vanilla scps + /// + public interface ISCPPreferences + { + + + } +} diff --git a/KruacentExiled/KE.CustomRoles/Abilities/Airstrike.cs b/KruacentExiled/KE.CustomRoles/Abilities/Airstrike.cs new file mode 100644 index 00000000..7114e33a --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Abilities/Airstrike.cs @@ -0,0 +1,106 @@ +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using Exiled.API.Features.Items; +using Exiled.API.Features.Pickups.Projectiles; +using Exiled.API.Features.Toys; +using Exiled.CustomRoles.API.Features; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.Interfaces; +using KE.CustomRoles.API.Interfaces.Ability; +using MapGeneration; +using MEC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using Light = Exiled.API.Features.Toys.Light; + +namespace KE.CustomRoles.Abilities +{ + public class Airstrike : KEAbilities, ICustomIcon, IConditional + { + public override string Name { get; } = "AirStrike"; + + public const string TranslationSomethingHere = "AirStrikeSomethingHere"; + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Airstrike", + [TranslationKeyDesc] = "Don't overuse it or your co-op will not be happy", + [TranslationSomethingHere] = "Something is in the way", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Bombardement", + [TranslationKeyDesc] = "Ne l'utilise pas trop sinon ton coop sera pas content", + [TranslationSomethingHere] = "Quelque chose gêne", + } + }; + } + public override float Cooldown { get; } = 60f; + + public float height = 1; + public Utils.API.GifAnimator.TextImage IconName => MainPlugin.Instance.icons["Airstrike"]; + + + protected override bool AbilityUsed(Player player) + { + if (CheckValid(player, true)) + { + return false; + } + SetPosition.TryGetTarget(player, out Vector3 target); + var l = Light.Create(target, null, null, true, Color.red); + + ExplosiveGrenade grenade = (ExplosiveGrenade)Item.Create(ItemType.GrenadeHE, player); + grenade.ScpDamageMultiplier = 1; + grenade.FuseTime = 10; + Timing.CallDelayed(1.5f, () => + { + Projectile gre = grenade.SpawnActive(target + (height - .5f) * Vector3.up); + //explode on collision + gre.GameObject.AddComponent().Init(player.GameObject, gre.Base); + l.Destroy(); + }); + + return base.AbilityUsed(player); + } + + + + private bool CheckValid(Player player, bool showMessage) + { + if (!SetPosition.TryGetTarget(player, out Vector3 target)) + { + if (showMessage) + { + ShowEffectHint(player, SetPosition.TranslationNoTarget); + } + return false; + } + + Physics.Linecast(target, target + height * Vector3.up, out RaycastHit hit); + if (hit.collider != null) + { + if (showMessage) + { + ShowEffectHint(player, TranslationSomethingHere); + } + return false; + } + + return true; + } + + public bool CheckCondition(Player player) + { + return CheckValid(player, false); + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/Abilities/Convert.cs b/KruacentExiled/KE.CustomRoles/Abilities/Convert.cs new file mode 100644 index 00000000..84cea815 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Abilities/Convert.cs @@ -0,0 +1,105 @@ +/*using DrawableLine; +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using Exiled.API.Features.Toys; +using Exiled.CustomRoles.API.Features; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.Interfaces; +using MEC; +using PlayerRoles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.CustomRoles.Abilities +{ + public class Convert : KEAbilities, ICustomIcon + { + public override string Name { get; } = "Convert"; + + protected override Dictionary> SetTranslation() + { + return new() + { + ["en"] = new() + { + [TranslationKeyName] = "Convert", + [TranslationKeyDesc] = "Convert a zombie to your team", + ["ConvertNobody"] = "But nobody's here", + ["ConvertSameTeam"] = "I know you don't like them, but they're in your team", + ["ConvertNonZombie"] = "That ain't a zombie", + ["ConvertSuccess"] = "New friend acquired!", + }, + ["fr"] = new() + { + [TranslationKeyName] = "Convert", + [TranslationKeyDesc] = "Converti un zombie à la bonne foi", + ["ConvertNobody"] = "Mais personne n'est venu", + ["ConvertSameTeam"] = "Je sais que tu l'aimes pas mais il est bien avec toi", + ["ConvertNonZombie"] = "C'est pas un zombie ça", + ["ConvertSuccess"] = "Ami obtenu!", + } + }; + } + + public override float Cooldown { get; } = 10*60f; + + public float MaxDistance { get; set; } = 15f; + + public Utils.API.GifAnimator.TextImage IconName => MainPlugin.Instance.icons["Convert"]; + protected override bool AbilityUsed(Player player) + { + Vector3 start = player.CameraTransform.position+ player.CameraTransform.forward*.2f; + Vector3 end = start + player.CameraTransform.forward * 5f; + + Vector3 basePosition = player.Position + player.CameraTransform.rotation * Vector3.forward; + DrawableLines.IsDebugModeEnabled = MainPlugin.Instance.Config.Debug; + DrawableLines.ServerGenerateLine(10f,null,start, end); + + + + + if (!Physics.Linecast(start, end, out RaycastHit hit)) return false; + + + Player playerHit = Player.Get(hit.collider); + + if (playerHit == null || playerHit == player) + { + MainPlugin.ShowEffectHint(player, "But nobody's here"); + return false; + } + + + if (playerHit.Role.Side == player.Role.Side) + { + MainPlugin.ShowEffectHint(player, "I know you don't like them but they're in your team"); + return false; + } + + if (playerHit.IsScp && playerHit.Role != RoleTypeId.Scp0492) + { + MainPlugin.ShowEffectHint(player, "That ain't a zombie"); + return false; + } + + + if (playerHit.IsScp) + { + playerHit.Role.Set(player.Role, RoleSpawnFlags.AssignInventory); + } + else + { + playerHit.Role.Set(player.Role, RoleSpawnFlags.None); + } + + MainPlugin.ShowEffectHint(player, "New friend acquired!"); + return base.AbilityUsed(player); + } + + } +} +*/ \ No newline at end of file diff --git a/KruacentExiled/KE.CustomRoles/Abilities/EmptyAbilities/NumberCheckpointEmptyAbility.cs b/KruacentExiled/KE.CustomRoles/Abilities/EmptyAbilities/NumberCheckpointEmptyAbility.cs new file mode 100644 index 00000000..13424b4e --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Abilities/EmptyAbilities/NumberCheckpointEmptyAbility.cs @@ -0,0 +1,58 @@ +using Exiled.API.Features; +using KE.CustomRoles.API.Features.Abilities; +using KE.CustomRoles.API.Interfaces.Ability; +using KE.CustomRoles.CR.Scientist; +using KE.Utils.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.Abilities.EmptyAbilities +{ + public class NumberCheckpointEmptyAbility : EmptyAbility, IDynamicName + { + public override string Name { get; } = "NumberCheckpoints"; + + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Remaining Checkpoint%S% : %remain%/%total%", + [TranslationKeyDesc] = "", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Checkpoint%S% restant : %remain%/%total% ", + [TranslationKeyDesc] = "", + } + }; + } + + public void GetName(StringBuilder sb, Player player) + { + int remaining = ZoneManager.GetNumberCheckpoints(player); + int total = ZoneManager.DoorToOpen.Count; + KELog.Debug("remainig"+remaining); + + if(remaining > 0) + { + string n = GetTranslation(player, TranslationKeyName).Replace("%remain%", remaining.ToString()).Replace("%total%", total.ToString()); + if(remaining > 1) + { + n = n.Replace("%S%", "s"); + } + else + { + n = n.Replace("%S%", string.Empty); + } + + sb.Append(n); + } + } + + } +} diff --git a/KruacentExiled/KE.CustomRoles/Abilities/Explode.cs b/KruacentExiled/KE.CustomRoles/Abilities/Explode.cs new file mode 100644 index 00000000..768a9473 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Abilities/Explode.cs @@ -0,0 +1,97 @@ +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using Exiled.API.Features.Items; +using Exiled.API.Features.Pools; +using Exiled.CustomRoles.API.Features; +using InventorySystem.Items.ThrowableProjectiles; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.Interfaces; +using KE.Utils.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.CustomRoles.Abilities +{ + public class Explode : KEAbilities, ICustomIcon + { + public override string Name { get; } = "Explode"; + + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary () + { + [TranslationKeyName] = "Explode", + [TranslationKeyDesc] = "You got an explosive belt", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Explosion", + [TranslationKeyDesc] = "Tu as une ceinture d'explosif autour de toi", + } + }; + } + + + public override float Cooldown { get; } = 4*60f; + + public Utils.API.GifAnimator.TextImage IconName => MainPlugin.Instance.icons["Explode"]; + + private HashSet GrenadesSerials = new HashSet(); + protected override void SubscribeEvents() + { + GrenadesSerials = HashSetPool.Pool.Get(); + Items.API.Events.ExplodeEvent.ExplodeDestructible += ExplodeEvent_ExplodeDestructible; + + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + + Items.API.Events.ExplodeEvent.ExplodeDestructible -= ExplodeEvent_ExplodeDestructible; + HashSetPool.Pool.Return(GrenadesSerials); + base.UnsubscribeEvents(); + } + + protected override bool AbilityUsed(Player player) + { + ExplosiveGrenade grenade = (ExplosiveGrenade)Item.Create(ItemType.GrenadeHE); + + + grenade.FuseTime = 0.2f; + grenade.SpawnActive(player.Position); + + + //idk why it's the next one but it is + ushort serial = (ushort) (grenade.Serial + 1); + + GrenadesSerials.Add(serial); + + return base.AbilityUsed(player); + } + + private void ExplodeEvent_ExplodeDestructible(Items.API.Events.OnExplodeDestructibleEventsArgs obj) + { + + ushort serial = obj.ExplosionGrenade.Info.Serial; + KELog.Debug("explode serial " + serial); + + + + if (!GrenadesSerials.Contains(serial)) return; + + obj.Damage /=3f; + + GrenadesSerials.Remove(serial); + + KELog.Debug("explode with "+ obj.Damage); + + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/Abilities/FireAbilities/BlindingFlash.cs b/KruacentExiled/KE.CustomRoles/Abilities/FireAbilities/BlindingFlash.cs new file mode 100644 index 00000000..c56e151a --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Abilities/FireAbilities/BlindingFlash.cs @@ -0,0 +1,47 @@ +using Exiled.API.Features; +using Exiled.API.Features.Items; +using Exiled.API.Features.Pickups.Projectiles; +using System.Collections.Generic; + +namespace KE.CustomRoles.Abilities.FireAbilities +{ + public class BlindingFlash : FireAbilityBase + { + public override string Name { get; } = "BlindingFlash"; + + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Blinding Flash", + [TranslationKeyDesc] = "Spawns an active flashbang at your feet", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Le soleil", + [TranslationKeyDesc] = "IS THAT A MOTHERFUCKER RED FLOOD REFERENCE??????????????", + } + }; + } + public override float Cooldown { get; } = 30f; + + public override int Cost => 50; + + + + protected override bool LaunchedAbility(Player player) + { + FlashGrenade flashbangProjectile = Item.Create(ItemType.GrenadeFlash); + + flashbangProjectile.FuseTime = .1f; + flashbangProjectile.SpawnActive(player.Position, player); + return true; + } + + + + } + +} diff --git a/KruacentExiled/KE.CustomRoles/Abilities/FireAbilities/FireAbilityBase.cs b/KruacentExiled/KE.CustomRoles/Abilities/FireAbilities/FireAbilityBase.cs new file mode 100644 index 00000000..673728bb --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Abilities/FireAbilities/FireAbilityBase.cs @@ -0,0 +1,47 @@ +using Exiled.API.Extensions; +using Exiled.API.Features; +using KE.CustomRoles.API.Features.Abilities; +using KE.Utils.API.CustomStats; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.Abilities.FireAbilities +{ + public abstract class FireAbilityBase : KEAbilityCost + { + public override bool CanLaunchAbility(Player player) + { + + if(player.GameObject.TryGetComponent(out var stat)) + { + if (stat.TryGetModule(out var firestat)) + { + Log.Debug($"cur = {firestat.CurValue} / {Cost}"); + if (firestat.CurValue > Cost) + { + Log.Debug("enough"); + firestat.AddAmount(-Cost); + return true; + } + else + { + Log.Debug("not enough"); + return false; + } + } + + } + + + Log.Debug("no fire stat"); + return true; + + } + + + + } +} diff --git a/KruacentExiled/KE.CustomRoles/Abilities/FireAbilities/FireStat.cs b/KruacentExiled/KE.CustomRoles/Abilities/FireAbilities/FireStat.cs new file mode 100644 index 00000000..be0781ed --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Abilities/FireAbilities/FireStat.cs @@ -0,0 +1,88 @@ +using Exiled.API.Features; +using KE.CustomRoles.API.Features; +using KE.Utils.API.CustomStats; +using KE.Utils.API.CustomStats.GUI; +using System.Linq; +using UnityEngine; + +namespace KE.CustomRoles.Abilities.FireAbilities +{ + public class FireStat : CustomStatBar, ICustomRoleStat + { + + public float BaseCapacity => MaxValue; + public override float CurValue { get; set; } + + public override float MinValue => 0; + + private float maxvalue = 100; + public override float MaxValue + { + get + { + return maxvalue; + } + set + { + maxvalue = value; + } + } + + public float FireRegen { get; set; } = 3f; + + public override Color ColorBar => new Color32(252, 186, 3,byte.MaxValue); + public override Color ColorText => new Color32(255, 255, 255, byte.MaxValue); + + public string CustomRole => "SCP106_SCP457"; + + public override int Width => 80; + + public override char Segment => '|'; + + + + + public override void ClassChanged() + { + maxvalue = 100f; + CurValue = 0f; + } + + public override void Init(ReferenceHub ply) + { + base.Init(ply); + StatBarPosition = new Utils.API.Displays.DisplayMeow.Placements.SCP106StatBarPosition(); + + } + + public override bool Check() + { + return false; + Player player = Player.Get(Hub); + if (!KECustomRole.Get(player).Any(role => KECustomRole.Get(CustomRole) == role)) + { + return false; + } + + return base.Check(); + } + + public override void Update() + { + float num = FireRegen * Time.deltaTime; + if (num > 0f) + { + if (CurValue < MaxValue) + { + CurValue = Mathf.MoveTowards(CurValue, MaxValue, num); + } + } + else if (CurValue > 0f) + { + CurValue += num; + } + base.Update(); + } + + } +} diff --git a/KruacentExiled/KE.CustomRoles/Abilities/FireAbilities/Fireball.cs b/KruacentExiled/KE.CustomRoles/Abilities/FireAbilities/Fireball.cs new file mode 100644 index 00000000..f026511c --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Abilities/FireAbilities/Fireball.cs @@ -0,0 +1,168 @@ +using Discord; +using Exiled.API.Features; +using Exiled.API.Features.Doors; +using Exiled.API.Features.Roles; +using Exiled.API.Features.Toys; +using Exiled.API.Interfaces; +using Exiled.Events.EventArgs.Player; +using Exiled.Events.Patches.Events.Player; +using KE.CustomRoles.API.Features; +using MEC; +using PlayerStatsSystem; +using System; +using System.Collections.Generic; +using UnityEngine; +using Light = Exiled.API.Features.Toys.Light; + +namespace KE.CustomRoles.Abilities.FireAbilities +{ + public class Fireball : FireAbilityBase + { + public override string Name { get; } = "Fireball"; + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Fireball", + [TranslationKeyDesc] = "I cast Fireball", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Boule de feur", + [TranslationKeyDesc] = "Attends j'ai fait une faute là non?", + } + }; + } + public override int Cost => 10; + + public override float Cooldown { get; } = 0f; + + + public static readonly CustomReasonDamageHandler BallDamage = new CustomReasonDamageHandler("Burned to death", 25, string.Empty); + public const int MAX_BALLS = 5; + private static readonly Color ballColor = new Color(2, 1.08f, 0, .75f); + private Dictionary _activeBalls = new Dictionary(); + + protected override bool LaunchedAbility(Player player) + { + if (!_activeBalls.ContainsKey(player)) + { + _activeBalls.Add(player, 0); + } + + + if (_activeBalls[player] >= MAX_BALLS) + { + ShowEffectHint(player, "too much balls"); + return true; + } + + + _activeBalls[player]++; + Timing.RunCoroutine(LaunchingAttack(player)); + return true; + + } + + + private float smooth = .01f; + + private IEnumerator LaunchingAttack(Player player) + { + Vector3 initpos = player.Position; + Quaternion direction = player.ReferenceHub.PlayerCameraReference.rotation; + + + Log.Debug(direction.eulerAngles); + bool attackTouchedSomething = false; + + Light light = Light.Create(initpos, direction.eulerAngles, null, false); + light.Color = ballColor; + light.Intensity = 1f; + Primitive primitive = Primitive.Create(initpos, direction.eulerAngles, null, false); + primitive.Collidable = false; + primitive.Color = ballColor; + primitive.Spawn(); + light.Spawn(); + Vector3 nextPos; + + int fallback = Mathf.CeilToInt(100/ smooth); + Log.Debug("fallback=" + fallback); + while (!attackTouchedSomething && fallback > 0) + { + nextPos = primitive.Position + primitive.Rotation * new Vector3(0, 0, 10 * smooth); + + + if (Physics.Linecast(primitive.Position, nextPos, out RaycastHit hit)) + { + //spawn mtf looking at central gate + if (hit.collider.gameObject.name != "VolumeOverrideTunnel") + { + attackTouchedSomething = true; + } + + } + + Collider[] colliders = Physics.OverlapSphere(primitive.Position, .5f); + + + + foreach (Collider collider in colliders) + { + + attackTouchedSomething = attackTouchedSomething || ProcessHit(player, collider); + + } + + + + + + + + Log.Debug($"current pos = {primitive.Position} next pos = {nextPos}"); + yield return Timing.WaitForSeconds(smooth); + primitive.Position = nextPos; + light.Position = nextPos; + fallback--; + } + _activeBalls[player]--; + primitive.Destroy(); + light.Destroy(); + + + } + + + private bool ProcessHit(Player attacker,Collider collider) + { + bool result = false; + Player playerhit = Player.Get(collider); + if (playerhit != null && playerhit.Role.Side != attacker.Role.Side) + { + playerhit.Hurt(BallDamage); + attacker.ShowHitMarker(); + result = true; + + } + + Door doorhit = Door.Get(collider.gameObject); + if (doorhit != null && doorhit is IDamageableDoor damageable && !damageable.IsDestroyed) + { + damageable.Break(); + attacker.ShowHitMarker(); + result = true; + } + + return result; + } + + + + + + } + +} diff --git a/KruacentExiled/KE.CustomRoles/Abilities/ForceOpen.cs b/KruacentExiled/KE.CustomRoles/Abilities/ForceOpen.cs new file mode 100644 index 00000000..7ee97b0c --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Abilities/ForceOpen.cs @@ -0,0 +1,128 @@ +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using Exiled.API.Features.Doors; +using Exiled.CustomRoles.API.Features; +using Exiled.Events.EventArgs.Player; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.Features.Abilities; +using KE.CustomRoles.API.Interfaces; +using PlayerRoles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.CustomRoles.Abilities +{ + public class ForceOpen : KEAbilityLimited, ICustomIcon + { + public override string Name { get; } = "ForceOpen"; + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Force open", + [TranslationKeyDesc] = "Force open a door", + ["ForceOpenFail"] = "You failed opening the door and lost %HP% HP!", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Forcer une porte", + [TranslationKeyDesc] = "Force une porte (grosse description)", + ["ForceOpenFail"] = "Tu as raté d'ouvrir la porte et a perdu %HP% HP!", + } + }; + } + public Utils.API.GifAnimator.TextImage IconName => MainPlugin.Instance.icons["ForceOpen"]; + public override float Cooldown { get; } = 30; + public override int Uses { get; } = 5; + + private Dictionary abilityActivated = new Dictionary(); + public static readonly TimeSpan MaxTime = new TimeSpan(0, 0, 30); + + protected override bool LaunchedAbility(Player player) + { + + abilityActivated[player] = DateTime.Now; + return base.LaunchedAbility(player); + } + + private void InteractingDoor(InteractingDoorEventArgs ev) + { + Player player = ev.Player; + if (ev.IsAllowed) return; + if (!abilityActivated.ContainsKey(player)) return; + if (DateTime.Now > abilityActivated[player] + MaxTime) return; + + if (ev.Door.IsOpen) return; + if (ev.Door.DoorLockType >= DoorLockType.Lockdown079) return; + + + int successRate; + int damage; + + if (ev.Door is Gate) + { + successRate = 100; + damage = 20; + } + else if (ev.Door.Type.IsCheckpoint()) + { + successRate = 50; + damage = 10; + } + else + { + successRate = 50; + damage = 5; + } + + + int proba = UnityEngine.Random.Range(0, 101); + + if (proba <= successRate) + { + if(ev.Door is Gate gate) + { + gate.TryPry(player); + } + else + { + ev.IsAllowed = true; + } + } + else + { + + string text = GetTranslation(player, "ForceOpenFail").Replace("%HP%",damage.ToString()); + ShowEffectHint(player, text,0f); + player.Hurt(damage, "Door too stronk"); + } + + + abilityActivated.Remove(player); + + + } + + + protected override void SubscribeEvents() + { + Exiled.Events.Handlers.Player.InteractingDoor += InteractingDoor; + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Player.InteractingDoor -= InteractingDoor; + base.UnsubscribeEvents(); + } + + } +} diff --git a/KruacentExiled/KE.CustomRoles/Abilities/RedMist/EgoAbility.cs b/KruacentExiled/KE.CustomRoles/Abilities/RedMist/EgoAbility.cs new file mode 100644 index 00000000..d25606f5 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Abilities/RedMist/EgoAbility.cs @@ -0,0 +1,67 @@ +using Exiled.API.Features; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.Features.Abilities; +using KE.CustomRoles.API.Interfaces.Ability; +using KE.CustomRoles.CR.MTF.RedMist; +using KE.Utils.API.Features; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.CustomRoles.Abilities.RedMist +{ + public abstract class EgoAbility : NeedCompAbility, IConditional + { + + + protected sealed override bool AddCompIfMissing => false; + protected abstract NeedActive NeedEGOActive { get; } + + public override bool CanLaunchAbility(Player player, out EGO comp) + { + + + + bool active = base.CanLaunchAbility(player,out comp); + + if (CheckItem(player)) + { + return false; + } + + + if (NeedEGOActive == NeedActive.NeedActive) + { + return active; + } + + if (NeedEGOActive == NeedActive.NeedNotActive) + { + return !active; + } + + return true; + } + + protected static bool CheckItem(Player player) + { + return player.CurrentItem is null || player.CurrentItem.Type != ItemType.SCP1509; + } + + public bool CheckCondition(Player player) + { + return CanLaunchAbility(player, out _); + } + + public enum NeedActive + { + NeedActive, + NeedNotActive, + Either, + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/Abilities/RedMist/GreaterSplitHorizontal/AttackGreaterSplitComp.cs b/KruacentExiled/KE.CustomRoles/Abilities/RedMist/GreaterSplitHorizontal/AttackGreaterSplitComp.cs new file mode 100644 index 00000000..922defbf --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Abilities/RedMist/GreaterSplitHorizontal/AttackGreaterSplitComp.cs @@ -0,0 +1,294 @@ +using CustomPlayerEffects; +using DrawableLine; +using Exiled.API.Enums; +using Exiled.API.Features; +using HintServiceMeow.Core.Enum; +using KE.Utils.API.Displays.DisplayMeow; +using KE.Utils.API.Displays.DisplayMeow.Placements; +using KE.Utils.API.Features; +using MEC; +using PlayerRoles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TMPro; +using UnityEngine; + +namespace KE.CustomRoles.Abilities.RedMist.GreaterSplitHorizontal +{ + public class AttackGreaterSplitComp : MonoBehaviour + { + + private static bool Debug => MainPlugin.Instance.Config.Debug; + private Player player; + private bool currentUsing; + private bool attacking; + private GreaterSplitHorizontal ability; + public const float TimeAttack = 3; + private readonly float StartTimeAttack; + private readonly float EndTimeAttack; + + private const float HighPowerRequirement = .1f; + private const float MedPowerRequirement = .3f; + + + public AttackGreaterSplitComp() + { + + StartTimeAttack = Mathf.Abs(TimeAttack) / 2f; + EndTimeAttack = -StartTimeAttack; + + } + private static HintPosition Position = new DebugPosition(); + public void Init(Player player, GreaterSplitHorizontal ability) + { + this.player = player; + this.ability = ability; + currentUsing = false; + attacking = false; + + if (Debug) + { + DisplayHandler.Instance.CreateAuto(player, (args) => GetDebug(), Position.HintPlacement, HintSyncSpeed.Fastest); + } + } + + private void OnDestroy() + { + + if (player != null) + { + DisplayHandler.Instance.RemoveHint(player, Position.HintPlacement); + } + + } + + + private string GetDebug() + { + float power = GetPower(time); + return "time = " + time + "\npower = " + power + '(' + GetPowerDebug(power) + ')'; + + } + + private string GetPowerDebug(float power) + { + if (power <= StartTimeAttack * HighPowerRequirement) + { + return "high"; + } + else if (power <= StartTimeAttack * MedPowerRequirement) + { + return "medium"; + } + else + { + return "low"; + } + } + + + private float time; + public void StartAttack() + { + currentUsing = true; + time = StartTimeAttack; + } + + + public bool OnTriggerAttack() + { + if (currentUsing) + { + attacking = true; + + } + return attacking; + } + + + private void Update() + { + try + { + UpdateAttack(); + } + catch (Exception e) + { + Log.Error(e); + } + } + + private void UpdateAttack() + { + if (!currentUsing) return; + + + time -= Timing.DeltaTime; + + + + if (time <= EndTimeAttack || attacking) + { + if (ability.Check(player)) + { + float power = GetPower(time); + + LaunchedAttack(power); + } + attacking = false; + currentUsing = false; + } + } + + + public float GetPower(float time) + { + return Mathf.Abs(time); + } + + private void LaunchedAttack(float power) + { + + if (player == null || player.GameObject == null) return; + KELog.Debug($"power (0~{StartTimeAttack})= {power}"); + + float angle = 1f; + float range = 1f; + bool checkBack = false; + + + if (power <= StartTimeAttack * HighPowerRequirement) + { + angle = 60f; + range = 7f; + checkBack = true; + KELog.Debug("high power"); + } + else if (power <= StartTimeAttack * MedPowerRequirement) + { + angle = 60f; + range = 7f; + KELog.Debug("med power"); + } + else + { + angle = 30f; + range = 5f; + KELog.Debug("low power"); + } + + + + + + + Vector3 direction = player.Transform.forward.NormalizeIgnoreY(); + Vector3 directionBack = -player.Transform.forward.NormalizeIgnoreY(); + + + + Vector3 position = player.Position; + + if (Debug) + { + CheckPoint(position, position, direction, range, angle); + if (checkBack) + { + CheckPoint(position, position, directionBack, range, angle); + } + } + + + foreach (Player target in Player.List) + { + + Vector3 targetPosition = target.Position; + KELog.Debug("fornt"); + + + + if (!CheckPoint(targetPosition, position, direction, range, angle) & !(checkBack && CheckPoint(targetPosition, position, directionBack, range, angle))) + { + continue; + } + KELog.Debug("player " + target.Nickname); + if (player == target) + { + continue; + } + + + KELog.Debug("linecast"); + if (Physics.Linecast(position, targetPosition, out var hitInfo, PlayerRolesUtils.AttackMask)) + { + continue; + } + + KELog.Debug("add damage"); + + + target.Hurt(GreaterSplitHorizontal.Damage, DamageType.Scp1509); + + } + + } + + public const float height = 1f; + private bool CheckPoint(Vector3 point, Vector3 center, Vector3 direction, float size, float halfAngle) + { + Vector2 position = new Vector2(point.x - center.x, point.z - center.z); + Vector3 playerPosition = player.Position; + float sqrMag = position.sqrMagnitude; + + float rad = halfAngle * Mathf.Deg2Rad; + + Vector2 dir = new Vector2(direction.x, direction.z); + + dir /= Mathf.Sqrt(dir.sqrMagnitude); + Vector2 leftDir = new Vector2(dir.x * Mathf.Cos(rad) - dir.y * Mathf.Sin(rad), dir.x * Mathf.Sin(rad) + dir.y * Mathf.Cos(rad)); + Vector2 rightDir = new Vector2(dir.x * Mathf.Cos(-rad) - dir.y * Mathf.Sin(-rad), dir.x * Mathf.Sin(-rad) + dir.y * Mathf.Cos(-rad)); + + Vector3 leftPoint = center + new Vector3(leftDir.x, 0, leftDir.y) * size; + Vector3 rightPoint = center + new Vector3(rightDir.x, 0, rightDir.y) * size; + + Vector3 frontPoint = playerPosition + direction * size; + if (Debug) + { + DrawableLines.IsDebugModeEnabled = true; + DrawableLines.GenerateLine(10, Color.yellow, playerPosition, leftPoint, frontPoint, rightPoint, player.Position); + } + + if (sqrMag <= 0.0001f) + return false; + + float radius = Mathf.Sqrt(sqrMag); + if (radius > size) + return false; + + + + Vector2 pointDir = position / radius; + + float cosThreshold = Mathf.Cos(halfAngle * Mathf.Deg2Rad); + + float dot = Vector2.Dot(dir, pointDir); + + + + + + KELog.Debug("center =" + center); + KELog.Debug("left =" + leftPoint); + KELog.Debug("rightPoint =" + rightPoint); + + + + return dot >= cosThreshold && point.y < center.y + height && point.y > center.y - height; + } + + } + +} diff --git a/KruacentExiled/KE.CustomRoles/Abilities/RedMist/GreaterSplitHorizontal/DebugPosition.cs b/KruacentExiled/KE.CustomRoles/Abilities/RedMist/GreaterSplitHorizontal/DebugPosition.cs new file mode 100644 index 00000000..3a2091a6 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Abilities/RedMist/GreaterSplitHorizontal/DebugPosition.cs @@ -0,0 +1,18 @@ +using HintServiceMeow.Core.Enum; +using KE.Utils.API.Displays.DisplayMeow.Placements; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.Abilities.RedMist.GreaterSplitHorizontal +{ + public class DebugPosition : HintPosition + { + public override float Xposition => 800; + public override float Yposition => 400; + public override HintAlignment HintAlignment => HintAlignment.Center; + public override string Name => "DebugGreaterSplit"; + } +} diff --git a/KruacentExiled/KE.CustomRoles/Abilities/RedMist/GreaterSplitHorizontal/GreaterSplitHorizontal.cs b/KruacentExiled/KE.CustomRoles/Abilities/RedMist/GreaterSplitHorizontal/GreaterSplitHorizontal.cs new file mode 100644 index 00000000..7ff68b7c --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Abilities/RedMist/GreaterSplitHorizontal/GreaterSplitHorizontal.cs @@ -0,0 +1,135 @@ +using DrawableLine; +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.CreditTags.Features; +using Exiled.Events.EventArgs.Scp1509; +using HintServiceMeow.Core.Enum; +using InventorySystem.Items.MicroHID.Modules; +using KE.CustomRoles.CR.MTF.RedMist; +using KE.CustomRoles.CR.SCP; +using KE.Utils.API.Displays.DisplayMeow; +using KE.Utils.API.Displays.DisplayMeow.Placements; +using KE.Utils.API.Features; +using MEC; +using PlayerRoles; +using System; +using System.Collections.Generic; +using UnityEngine; +namespace KE.CustomRoles.Abilities.RedMist.GreaterSplitHorizontal +{ + public class GreaterSplitHorizontal : EgoAbility + { + public const string FailEgo = "GreaterSplitFailEGO"; + public const string FailWeapon = "GreaterSplitFailWeapon"; + + + public override string Name { get; } = "GreaterSplitHorizontal"; + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Greater Split : Horizontal", + [TranslationKeyDesc] = "Manifest your E.G.O, gain powerful buff but rapid health drain.\nCan be deactivated anytime", + [FailEgo] = "You need to manifest your E.G.O. first", + [FailWeapon] = "You need your weapon", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "todo", + [TranslationKeyDesc] = "todo", + [FailEgo] = "todo", + [FailWeapon] = "todo", + } + }; + } + public override float Cooldown { get; } = 0f; + + protected override NeedActive NeedEGOActive => NeedActive.NeedActive; + + public const float Damage = 200; + + + public static readonly LayerMasks Mask = LayerMasks.Scp173Teleport | LayerMasks.Glass; + protected override void AbilityAdded(Player player) + { + + if (!player.GameObject.TryGetComponent(out var comp)) + { + comp = player.GameObject.AddComponent(); + } + comp.Init(player, this); + + + base.AbilityAdded(player); + } + + protected override void AbilityRemoved(Player player) + { + if (player.GameObject.TryGetComponent(out var comp)) + { + UnityEngine.Object.Destroy(comp); + } + base.AbilityRemoved(player); + } + + + protected override void SubscribeEvents() + { + Exiled.Events.Handlers.Scp1509.TriggeringAttack += OnTriggeringAttack; + + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Scp1509.TriggeringAttack -= OnTriggeringAttack; + base.UnsubscribeEvents(); + } + + + + + + private void OnTriggeringAttack(TriggeringAttackEventArgs ev) + { + Player player = ev.Player; + if (!Check(player)) return; + if (!ev.IsAllowed) return; + if (player.GameObject.TryGetComponent(out var comp)) + { + ev.IsAllowed = !comp.OnTriggerAttack(); + + } + ev.Scp1509.NextResurrectTime = float.MaxValue; + } + + + + + + protected override bool LaunchedAbility(Player player,EGO ego) + { + KELog.Debug("check weapopgn"); + if(player.CurrentItem is null || player.CurrentItem.Type != ItemType.SCP1509) + { + ShowEffectHint(player, FailWeapon); + return false; + } + + + if (player.GameObject.TryGetComponent(out var comp)) + { + comp.StartAttack(); + } + + + return true; + } + + + + + } +} diff --git a/KruacentExiled/KE.CustomRoles/Abilities/RedMist/OnRush.cs b/KruacentExiled/KE.CustomRoles/Abilities/RedMist/OnRush.cs new file mode 100644 index 00000000..e258ae51 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Abilities/RedMist/OnRush.cs @@ -0,0 +1,178 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.DamageHandlers; +using InventorySystem.Items.MicroHID.Modules; +using KE.CustomRoles.CR.MTF.RedMist; +using KE.Utils.API.Features; +using PlayerRoles; +using System.Collections.Generic; +using UnityEngine; +namespace KE.CustomRoles.Abilities.RedMist +{ + public class OnRush : EgoAbility + { + public override string Name { get; } = "OnRush"; + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "On Rush", + [TranslationKeyDesc] = "Manifest your E.G.O, gain powerful buff but rapid health drain.\nCan be deactivated anytime", + ["OnRushFailEGO"] = "You need to manifest your E.G.O. first", + ["OnRushFailWeapon"] = "You need your weapon", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "todo", + [TranslationKeyDesc] = "todo", + ["OnRushFailEGO"] = "todo", + ["OnRushFailWeapon"] = "todo", + } + }; + } + public override float Cooldown { get; } = 120f; + + protected override NeedActive NeedEGOActive => NeedActive.Either; + + public const float Damage = 100; + + public const float MaxDistance = 5; + + + private Collider[] NonAlloc = new Collider[64]; + + public static readonly LayerMasks Mask = LayerMasks.Scp173Teleport | LayerMasks.Glass; + protected override bool LaunchedAbility(Player player,EGO ego) + { + + + KELog.Debug("check weapopgn"); + + + + if(player.CurrentItem is null || player.CurrentItem.Type != ItemType.SCP1509) + { + //show OnRushFailWeapon + return false; + } + + + float size = 1; + + Vector3 feetposition = player.Position- (Vector3.up*player.Scale.y)/2 + player.CameraTransform.forward*.1f; + Vector3 position = player.Position + player.CameraTransform.forward*.1f; + + + DrawSphere(position, size, Color.green); + DrawSphere(feetposition, size, Color.green); + Vector3 direction = player.CameraTransform.forward; + + direction = direction.NormalizeIgnoreY(); + + + Vector3 end = position + direction * MaxDistance; + Vector3 feetend = feetposition + direction * MaxDistance; + DrawSphere(end, size, Color.yellow); + DrawSphere(feetend, size, Color.yellow); + Vector3 teleport = end; + + Vector3 wallPoint = end; + + + + + if (Physics.Raycast(position, direction,out RaycastHit wallHit, MaxDistance, (int)Mask) + | Physics.Raycast(feetposition, direction, out RaycastHit wallHitFeet, MaxDistance, (int)Mask)) + { + Draw.Sphere(wallHit.point, Quaternion.identity, Vector3.one / 8f, Color.blue, 10, Player.Enumerable); + Draw.Sphere(wallHitFeet.point, Quaternion.identity, Vector3.one / 8f, Color.blue, 10, Player.Enumerable); + + if(Vector3.Distance(wallHit.point,position) <= Vector3.Distance(wallHitFeet.point, feetposition)) + { + teleport = wallHit.point + direction * (-1f); + } + else + { + teleport = wallHitFeet.point + direction * (-1f); + } + Vector3 directionToPoint = teleport - player.Position; + bool behind = Vector3.Dot(direction, directionToPoint) < 0f; + + + wallPoint = teleport; + if (behind) + { + teleport = Vector3.zero; + } + + + + } + + int detect = Physics.OverlapCapsuleNonAlloc(position, wallPoint, size, NonAlloc, HitregUtils.DetectionMask); + + for (int i = 0;i < detect;i++) + { + Collider collider = NonAlloc[i]; + + //KELog.Debug("hit collider ="+collider); + + if (collider.TryGetComponent(out var destructible) && + (!Linecast(position, destructible?.CenterOfMass ?? Vector3.zero, out var hitInfo, PlayerRolesUtils.AttackMask) + || collider == hitInfo.collider)) + { + Player target = Player.Get(collider); + + if(target is null || target != player) + { + DrawSphere(collider.transform.position,.2f, Color.red); + } + + + if (destructible != null) + { + PlayerStatsSystem.DamageHandlerBase handler = new GenericDamageHandler(target, player, Damage, DamageType.Scp1509, new DamageHandlerBase.CassieAnnouncement("")).Base; + destructible.Damage(Damage, handler, destructible.CenterOfMass); + } + } + } + + DrawSphere(teleport,.25f, Color.magenta); + + if(teleport != Vector3.zero) + { + player.Teleport(teleport); + } + + return true; + } + + + private static bool Debug => MainPlugin.Configs.Debug; + public static bool Linecast(Vector3 start,Vector3 end,out RaycastHit hit,int layerMask) + { + if (Debug) + { + //Draw.Line(start, end, Color.red, 10, Player.Enumerable); + } + + + + return Physics.Linecast(start, end, out hit, layerMask); + + } + + public static void DrawSphere(Vector3 position, float size,Color color) + { + if (Debug) + { + Draw.Sphere(position, Quaternion.identity, Vector3.one * size, color, 10, Player.Enumerable); + } + + } + + + } +} diff --git a/KruacentExiled/KE.CustomRoles/Abilities/RedMist/Spear/Spear.cs b/KruacentExiled/KE.CustomRoles/Abilities/RedMist/Spear/Spear.cs new file mode 100644 index 00000000..950447ff --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Abilities/RedMist/Spear/Spear.cs @@ -0,0 +1,65 @@ +using DrawableLine; +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.DamageHandlers; +using InventorySystem.Items.MicroHID.Modules; +using KE.CustomRoles.CR.MTF.RedMist; +using KE.Utils.API.Features; +using PlayerRoles; +using System.Collections.Generic; +using UnityEngine; +using static PlayerRoles.PlayableScps.VisionInformation; + +namespace KE.CustomRoles.Abilities.RedMist.Spear +{ + public class Spear : EgoAbility + { + public const string FailWeapon = "SpearFailWeapon"; + public override string Name { get; } = "Spear"; + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Spear", + [TranslationKeyDesc] = "Manifest your E.G.O, gain powerful buff but rapid health drain.\nCan be deactivated anytime", + [FailWeapon] = "You need your weapon", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "todo", + [TranslationKeyDesc] = "todo", + [FailWeapon] = "todo", + } + }; + } + public override float Cooldown { get; } = 0f; + + protected override NeedActive NeedEGOActive => NeedActive.Either; + + public const float Damage = 200; + + + + + protected override bool LaunchedAbility(Player player, EGO ego) + { + KELog.Debug("check weapopgn"); + if (player.CurrentItem is null || player.CurrentItem.Type != ItemType.SCP1509) + { + ShowEffectHint(player, FailWeapon); + return false; + } + + SpearProjectile.Create(player, player.CameraTransform.forward); + + + return true; + } + + + + + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.CustomRoles/Abilities/RedMist/Spear/SpearComp.cs b/KruacentExiled/KE.CustomRoles/Abilities/RedMist/Spear/SpearComp.cs new file mode 100644 index 00000000..05786b39 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Abilities/RedMist/Spear/SpearComp.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.CustomRoles.Abilities.RedMist.Spear +{ + public class SpearComp : MonoBehaviour + { + + + + } +} diff --git a/KruacentExiled/KE.CustomRoles/Abilities/RedMist/Spear/SpearProjectile.cs b/KruacentExiled/KE.CustomRoles/Abilities/RedMist/Spear/SpearProjectile.cs new file mode 100644 index 00000000..1899f355 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Abilities/RedMist/Spear/SpearProjectile.cs @@ -0,0 +1,153 @@ +using DrawableLine; +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Toys; +using KE.Utils.API.Features; +using PlayerRoles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TMPro; +using UnityEngine; +using Utils; + +namespace KE.CustomRoles.Abilities.RedMist.Spear +{ + public class SpearProjectile : MonoBehaviour + { + public static float Damage => Spear.Damage; + private Primitive _primitive; + private Vector3 _direction; + private Player _player; + + public const float Speed = 30; + + public const float TimeBeforeMoving = 2f; + private float timeBeforeMove = 0f; + + public void Init(Primitive primitive,Player player, Vector3 direction) + { + _player = player; + _direction = direction; + _primitive = primitive; + DrawableLines.IsDebugModeEnabled = true; + + } + + public const byte Fallback = 200; + private byte fallback = 0; + private const float SpeedRotation = 90f; + + private static readonly Vector3 SizeProjectile = new Vector3(1,.2f,.2f); + public static SpearProjectile Create(Player player, Vector3 direction) + { + var prim = Primitive.Create(player.Position, null, SizeProjectile, false); + prim.MovementSmoothing = 0; + prim.Collidable = false; + prim.Spawn(); + + SpearProjectile spear = prim.GameObject.AddComponent(); + + spear.Init(prim,player, direction); + + return spear; + + } + + + private Collider[] NonAlloc = new Collider[64]; + private void Update() + { + if(fallback >= Fallback || _player == null || _player.GameObject == null) + { + Destroy(); + return; + } + + if (timeBeforeMove <= TimeBeforeMoving) + { + timeBeforeMove += Time.deltaTime; + _primitive.Rotation = _primitive.Rotation * Quaternion.Euler(0, SpeedRotation* Time.deltaTime, 0); + return; + } + + _primitive.Rotation.SetLookRotation(_direction); + + + UpdateDetect(); + UpdateMovement(); + + } + + private void UpdateMovement() + { + + Vector3 position = base.transform.position; + float checkDistance = base.transform.localScale.z; + + DrawableLines.GenerateLine(.1f,Color.blue,position, position + _direction * checkDistance); + + if (Physics.Raycast(position, _direction, checkDistance, (int)LayerMasks.AttackMask)) + { + KELog.Debug("hit wall"); + + Destroy(); + return; + + } + + _primitive.Position = _primitive.Position + _direction * Speed * Time.deltaTime; + fallback++; + } + + + private void UpdateDetect() + { + Vector3 position = base.transform.position; + float radius = _primitive.Scale.x /2f; + int detect = Physics.OverlapSphereNonAlloc(position, radius, NonAlloc, (int)LayerMasks.Hitbox); + + + + DrawableLines.GenerateSphere(position, radius, duration:.1f); + + KELog.Debug("detect=" + detect); + + for (int i = 0; i < detect; i++) + { + Collider collider = NonAlloc[i]; + + if (Player.TryGet(collider, out Player target)) + { + KELog.Debug("collider layer=" + collider.gameObject.layer); + + if (target == _player) + { + continue; + } + + if (HitboxIdentity.IsDamageable(_player.ReferenceHub, target.ReferenceHub)) + { + KELog.Debug("hurt "+ target.Nickname); + target.Hurt(_player, Damage, Exiled.API.Enums.DamageType.Scp1509); + Destroy(); + } + + + } + } + } + + + public void Destroy() + { + KELog.Debug($"destroyed ({fallback}/{Fallback})"); + + _primitive?.Destroy(); + Destroy(this); + } + + } +} diff --git a/KruacentExiled/KE.CustomRoles/Abilities/RedMist/ToggleEGO.cs b/KruacentExiled/KE.CustomRoles/Abilities/RedMist/ToggleEGO.cs new file mode 100644 index 00000000..8bad74a5 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Abilities/RedMist/ToggleEGO.cs @@ -0,0 +1,74 @@ +using Exiled.API.Features; +using KE.CustomRoles.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using KE.CustomRoles.CR.MTF.RedMist; +using KE.CustomRoles.API.Features.Abilities; +using UnityEngine; +namespace KE.CustomRoles.Abilities.RedMist +{ + public class ToggleEGO : ToggleableAbility + { + + public override Color ColorOn => Color.red; + public override Color ColorOff => Color.white; + + public override string Name { get; } = "ToggleEGO"; + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Toggle E.G.O.", + [TranslationKeyDesc] = "Manifest your E.G.O, gain powerful buff but rapid health drain.\nCan be deactivated anytime", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "todo", + [TranslationKeyDesc] = "todo", + } + }; + } + public override float Cooldown { get; } = 0f; + + public override bool GetState(Player player) + { + if (!player.ReferenceHub.gameObject.TryGetComponent(out var ego)) + { + return false; + } + + return ego.Active; + } + protected override bool AbilityUsed(Player player) + { + if(!player.ReferenceHub.gameObject.TryGetComponent(out var ego)) + { + return false; + } + + ego.ToggleActive(); + + + + return base.AbilityUsed(player); + } + + + protected override void SubscribeEvents() + { + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + base.UnsubscribeEvents(); + } + + + } +} diff --git a/KruacentExiled/KE.CustomRoles/Abilities/SCP049C/Small.cs b/KruacentExiled/KE.CustomRoles/Abilities/SCP049C/Small.cs new file mode 100644 index 00000000..62265928 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Abilities/SCP049C/Small.cs @@ -0,0 +1,52 @@ +using Exiled.API.Features; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.Interfaces; +using MEC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.CustomRoles.Abilities.SCP049C +{ + public class Small : KEAbilities + { + public override string Name { get; } = "Small"; + + + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Small", + [TranslationKeyDesc] = "Get small for 30 seconds", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Potit", + [TranslationKeyDesc] = "Devient plus petit pendant 30 secondes", + } + }; + } + + public override float Cooldown { get; } = 120f; + + protected override bool AbilityUsed(Player player) + { + + Vector3 oldScale = player.Scale; + player.Scale = new Vector3(oldScale.x, 0.8f, oldScale.z); + + Timing.CallDelayed(30, () => + { + player.Scale = oldScale; + }); + + return base.AbilityUsed(player); + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/Abilities/SCP049C/ToggleHighJump.cs b/KruacentExiled/KE.CustomRoles/Abilities/SCP049C/ToggleHighJump.cs new file mode 100644 index 00000000..dc1d8d63 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Abilities/SCP049C/ToggleHighJump.cs @@ -0,0 +1,68 @@ +using Exiled.API.Features; +using HintServiceMeow.UI.Utilities; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.Features.Abilities; +using KE.CustomRoles.CR.CustomSCPs.SCP049C.UnlockableAbilities.Tier1; +using MEC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.CustomRoles.Abilities.SCP049C +{ + public class ToggleHighJump : ToggleableAbility + { + public override string Name { get; } = "ToggleHighJump"; + + + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Toggle Big Jump", + [TranslationKeyDesc] = "", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Active/désactive grand saut", + [TranslationKeyDesc] = "", + } + }; + } + + public override float Cooldown { get; } = 2f; + + public override Color ColorOn => Color.green; + + public override Color ColorOff => Color.red; + + + + protected override bool AbilityUsed(Player player) + { + if (!player.GameObject.TryGetComponent(out var comp)) + { + player.GameObject.AddComponent(); + return true; + } + comp.IsActive = !comp.IsActive; + + return base.AbilityUsed(player); + } + + public override bool GetState(Player player) + { + if (!player.GameObject.TryGetComponent(out var comp)) + { + return false; + } + + return comp.IsActive; + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/Abilities/SetPosition.cs b/KruacentExiled/KE.CustomRoles/Abilities/SetPosition.cs new file mode 100644 index 00000000..b4cfffbc --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Abilities/SetPosition.cs @@ -0,0 +1,179 @@ +using Exiled.API.Features; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.Interfaces; +using KE.Utils.API.Features; +using KE.Utils.API.KETextToy; +using MEC; +using System.Collections.Generic; +using UnityEngine; + +namespace KE.CustomRoles.Abilities +{ + public class SetPosition : KEAbilities, ICustomIcon + { + public override string Name { get; } = "SetPosition"; + + public const string TranslationNoTarget = "SetPositionNoTarget"; + public const string TranslationTooFar = "SetPositionTooFar"; + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Set Position", + [TranslationKeyDesc] = "Select the current position for another ability", + [TranslationNoTarget] = "No target set", + [TranslationTooFar] = "Position destroyed : too far away", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Selection de position", + [TranslationKeyDesc] = "Selectionne la position pour une autre abilité", + [TranslationNoTarget] = "Pas de position mise", + [TranslationTooFar] = "Position détruite : trop loin", + } + }; + } + + public override float Cooldown { get; } = 5f; + public Utils.API.GifAnimator.TextImage IconName => MainPlugin.Instance.icons[Name]; + + + + protected override bool AbilityUsed(Player player) + { + + Vector3 position = player.Position; + + if(SetPositionPosition.TryGet(player,out var setPosition)) + { + setPosition.Destroy(); + } + + + + + + FollowingTextToy followingTextToy = new FollowingTextToy(new List() { player }, position, Quaternion.identity, Vector3.one); + + new SetPositionPosition(player,position, followingTextToy); + + + + followingTextToy.OnlyMoveY = true; + followingTextToy.Text = "↓"; + + Log.Debug("set position at " + position); + return base.AbilityUsed(player); + } + + + + + + protected override void AbilityRemoved(Player player) + { + if(SetPositionPosition.TryGet(player,out var position)) + { + position.Destroy(); + } + base.AbilityRemoved(player); + } + + public static bool TryGetTarget(Player p, out Vector3 target) + { + return SetPositionPosition.TryGetTarget(p, out target); + } + + private class SetPositionPosition + { + public const float RefreshRate = 1f; + public const float MaxDistance = 25; + private static Dictionary SelectedTarget = new Dictionary(); + public Player Player { get; private set; } + public Vector3 Position { get; } + public FollowingTextToy Follow { get; } + private readonly CoroutineHandle handle; + + public SetPositionPosition(Player player,Vector3 position, FollowingTextToy follow) + { + Position = position; + Follow = follow; + Player = player; + handle = Timing.RunCoroutine(CheckPosition()); + SelectedTarget.Add(Player, this); + } + + public static void TryDestroyFollowingTextToy(Player player) + { + if (SelectedTarget.ContainsKey(player)) + { + SetPositionPosition setposition = SelectedTarget[player]; + + if (setposition.Follow != null) + { + setposition.Follow.Destroy(); + } + } + } + + public static bool TryGet(Player player, out SetPositionPosition position) + { + return SelectedTarget.TryGetValue(player, out position); + } + + + public void Destroy() + { + Follow.Destroy(); + SelectedTarget.Remove(Player); + Timing.KillCoroutines(handle); + Player = null; + } + + private bool ToDestroy = false; + private IEnumerator CheckPosition() + { + while(Player != null && !ToDestroy) + { + float distance = (Position - Player.Position).sqrMagnitude; + + yield return Timing.WaitForSeconds(RefreshRate); + + if (distance >= MaxDistance * MaxDistance) + { + KELog.Debug("SetPosition to destroy "+ Position); + ToDestroy = true; + } + + + } + + if (ToDestroy) + { + + TranslationFeed(Player, SetPosition.TranslationTooFar); + KELog.Debug($"SetPosition ({Position}) destroyed : too far"); + Destroy(); + } + } + + + public static bool TryGetTarget(Player player, out Vector3 target) + { + target = Vector3.zero; + bool result = SelectedTarget.TryGetValue(player, out SetPositionPosition position); + if (result) + { + target = position.Position; + } + + return result; + } + } + + + + } +} diff --git a/KruacentExiled/KE.CustomRoles/Abilities/SimulateDeath.cs b/KruacentExiled/KE.CustomRoles/Abilities/SimulateDeath.cs new file mode 100644 index 00000000..ce284a85 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Abilities/SimulateDeath.cs @@ -0,0 +1,67 @@ +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.Interfaces; +using KE.CustomRoles.API.Interfaces.Ability; +using MEC; +using PlayerRoles.FirstPersonControl.Thirdperson.Subcontrollers; +using PlayerStatsSystem; +using System.Collections.Generic; +using UnityEngine; + +namespace KE.CustomRoles.Abilities +{ + public class SimulateDeath : KEAbilities, ICustomIcon, IDuration + { + public override string Name { get; } = "SimulateDeath"; + + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Play dead", + [TranslationKeyDesc] = "Simulate your own death", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Faire le mort", + [TranslationKeyDesc] = "T'es talent de mime te permettent de simuler la mort", + } + }; + } + + public Utils.API.GifAnimator.TextImage IconName => MainPlugin.Instance.icons[Name]; + public override float Cooldown => 60f; + public float Duration => 10f; + + private Ragdoll ragdoll; + private Vector3 pScale; + private Vector3 pPos; + + protected override bool AbilityUsed(Player player) + { + Dictionary deathTranslation = DeathTranslations.TranslationsById; + + this.pScale = player.Scale; + this.pPos = player.Position; + this.ragdoll = Ragdoll.CreateAndSpawn(player.Role, player.DisplayNickname, deathTranslation[(byte)UnityEngine.Random.Range(0, deathTranslation.Count)].DeathscreenTranslation, player.Position, player.ReferenceHub.PlayerCameraReference.rotation, player); + + player.EnableEffect(EffectType.Invisible, this.Duration); + player.EnableEffect(EffectType.Ensnared, this.Duration); + player.EnableEffect(EffectType.AmnesiaItems, this.Duration); + player.Scale = new Vector3(0.1f, 0.1f, 0.1f); + + return base.AbilityUsed(player); + } + + public void ActionAfterAbility(Player player) + { + this.ragdoll.Destroy(); + player.Scale = this.pScale; + player.Position = this.pPos; + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/Abilities/Teleportation.cs b/KruacentExiled/KE.CustomRoles/Abilities/Teleportation.cs new file mode 100644 index 00000000..58e1ad0f --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Abilities/Teleportation.cs @@ -0,0 +1,127 @@ +using Exiled.API.Extensions; +using Exiled.API.Features; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.Interfaces; +using KE.CustomRoles.API.Interfaces.Ability; +using MapGeneration; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace KE.CustomRoles.Abilities +{ + public class Teleportation : KEAbilities, ICustomIcon, IConditional + { + public override string Name { get; } = "Teleportation"; + + public const string TranslationLift = "TeleportationLift"; + public const string TranslationLcz = "TeleportationLcz"; + public const string TranslationDifferentZone = "TeleportationDifferentZone"; + + + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Teleportation", + [TranslationKeyDesc] = $"You lose {Damage} HP per teleportation, can't be used in lifts", + [TranslationLift] = "can't teleport in lifts", + [TranslationLcz] = "The target is inaccessible", + [TranslationDifferentZone] = "The target is inaccessible", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Téléportation", + [TranslationKeyDesc] = $"Tu perds {Damage} HP/téléportation, ne peux pas être utilisé dans les ascenseurs", + [TranslationLift] = "Impossible de se téléporter dans un ascenseur", + [TranslationLcz] = "Position inaccessible", + [TranslationDifferentZone] = "Position inaccessible", + } + }; + } + + public Utils.API.GifAnimator.TextImage IconName => MainPlugin.Instance.icons[Name]; + public override float Cooldown { get; } = 130f; + public static float Damage { get; set; } = 60; + + protected override bool AbilityUsed(Player player) + { + + if (!CheckValid(player, true)) + { + return false; + } + SetPosition.TryGetTarget(player, out Vector3 target); + player.Hurt(Damage, Exiled.API.Enums.DamageType.Asphyxiation); + + if(UnityEngine.Random.Range(1f, 100f) < 5) + { + if (Player.Enumerable.Count() > 1) + { + player.Teleport(Player.Enumerable.GetRandomValue(p => p != player)); + } + } + else + { + player.Position = target; + } + return base.AbilityUsed(player); + + } + + + private bool CheckValid(Player player,bool showMessage) + { + if (!SetPosition.TryGetTarget(player, out Vector3 target)) + { + if (showMessage) + { + ShowEffectHint(player, SetPosition.TranslationNoTarget); + } + + return false; + } + + + + if (Lift.Get(target) != null) + { + if (showMessage) + { + ShowEffectHint(player, TranslationLift); + } + + return false; + } + + + if (target.GetZone() == FacilityZone.LightContainment && Exiled.API.Features.Map.IsLczDecontaminated) + { + if (showMessage) + { + ShowEffectHint(player, TranslationLcz); + } + + + return false; + } + + if (target.GetZone() != player.Zone.GetZone()) + { + if (showMessage) + { + ShowEffectHint(player, TranslationDifferentZone); + } + return false; + } + return true; + } + + public bool CheckCondition(Player player) + { + return CheckValid(player, false); + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.CustomRoles/Abilities/Thief.cs b/KruacentExiled/KE.CustomRoles/Abilities/Thief.cs new file mode 100644 index 00000000..d306c4fa --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Abilities/Thief.cs @@ -0,0 +1,133 @@ +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Items; +using Exiled.CustomItems.API.Features; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.Interfaces; +using KE.Items.API.Features; +using KE.Utils.API.Displays.DisplayMeow; +using KE.Utils.API.Features; +using KE.Utils.API.Features.SCPs; +using KE.Utils.API.GifAnimator; +using LiteNetLib4Mirror.Open.Nat; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace KE.CustomRoles.Abilities +{ + public class Thief : KEAbilities, ICustomIcon + { + public override string Name { get; } = "Thief"; + + public const string NoPlayer = "ThiefNoPlayer"; + public const string Fail = "ThiefFail"; + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Steal", + [TranslationKeyDesc] = "Steal a random item from a player in the same room", + [NoPlayer] = "No player to steal from", + [Fail] = "I think this is a skill issue! Congrats!", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Voler", + [TranslationKeyDesc] = "Vole un object aléatoire à un joueur dans la même pièce", + [NoPlayer] = "Personne à qui voler", + [Fail] = "Wow faut get good là", + } + }; + } + + public override float Cooldown { get; } = 60f; + + public TextImage IconName => MainPlugin.Instance.icons[Name]; + + protected override bool AbilityUsed(Player player) + { + + IEnumerable sameRoom = Player.Enumerable.Where(p => p.CurrentRoom == player.CurrentRoom && !p.IsInventoryEmpty && p != player); + + + + KELog.Debug("scp"); + Player scp = GetClosest(sameRoom.Where(p => SCPTeam.IsSCP(p.ReferenceHub)), player.Position); + + + if(scp != null) + { + KELog.Debug("steal scp"); + Steal(player, scp); + return true; + } + + KELog.Debug("enemy"); + Player enemy = GetClosest(sameRoom.Where(p => HitboxIdentity.IsEnemy(player.ReferenceHub, p.ReferenceHub)),player.Position); + + + if(enemy != null) + { + KELog.Debug("steal enemy"); + Steal(player, enemy); + return true; + } + + KELog.Debug("other"); + Player ally = GetClosest(sameRoom, player.Position); + + + if (ally != null) + { + KELog.Debug("steal ally"); + Steal(player, ally); + return true; + } + + MainPlugin.ShowEffectHint(player, "no player to steal from"); + return false; + } + + private void Steal(Player player, Player thiefed) + { + + Item item; + + if(player.CurrentItem is null || UnityEngine.Random.Range(0f,100f) < 50f) + { + item = thiefed.Items.GetRandomValue(); + } + else + { + item = player.CurrentItem; + } + + + if(KECustomItem.TryGet(item,out CustomItem ci)) + { + ci.Give(player); + } + else + { + Item newitem = item.Clone(); + newitem.Give(player); + } + + thiefed.RemoveItem(item); + } + + + + + private Player GetClosest(IEnumerable players,Vector3 center) + { + return players.OrderBy(p => Vector3.Distance(center, p.Position)).FirstOrDefault(); + } + + + + } +} diff --git a/KruacentExiled/KE.CustomRoles/Abilities/Trade.cs b/KruacentExiled/KE.CustomRoles/Abilities/Trade.cs new file mode 100644 index 00000000..b0a7acc3 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Abilities/Trade.cs @@ -0,0 +1,72 @@ +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using Exiled.CustomRoles.API.Features; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.CustomRoles.Abilities +{ + public class Trade : KEAbilities, ICustomIcon + { + public override string Name { get; } = "Trade"; + public Utils.API.GifAnimator.TextImage IconName => MainPlugin.Instance.icons[Name]; + public override float Cooldown { get; } = 1f; + + public static float MaxHealthPercent = .1f; + + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Trade", + [TranslationKeyDesc] = "Your link with casinos permit your to gain more coins in exchange of items", + ["TradeNoItem"] = "No item?", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Trade", + [TranslationKeyDesc] = "T'es lien avec le casino t'as permis d'avoir un peu plus de pièce en échange d'un objet", + ["TradeNoItem"] = "Pas d'objet", + } + }; + } + + protected override bool AbilityUsed(Player player) + { + if (player.CurrentItem != null) + { + player.RemoveItem(player.CurrentItem); + } + else + { + + + ShowEffectHint(player, "no items?"); + return false; + /* + float newHealth = player.MaxHealth - player.MaxHealth * MaxHealthPercent; + if (newHealth > 0) + { + player.Health = Mathf.Min(player.Health, newHealth); + } + else + { + player.Kill("The casino always win"); + return base.AbilityUsed(player); + }*/ + } + + player.AddItem(ItemType.Coin); + return base.AbilityUsed(player); + } + + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/ChaosInsurgency/LeRusse.cs b/KruacentExiled/KE.CustomRoles/CR/ChaosInsurgency/LeRusse.cs new file mode 100644 index 00000000..ba62946f --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/ChaosInsurgency/LeRusse.cs @@ -0,0 +1,183 @@ +using AdminToys; +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Items; +using Exiled.API.Features.Toys; +using Exiled.CustomItems.API.Features; +using Exiled.Events.EventArgs.Player; +using KE.CustomRoles.API.Features; +using MEC; +using Mirror; +using PlayerRoles; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using VoiceChat.Codec; +using VoiceChat.Networking; + +namespace KE.CustomRoles.CR.ChaosInsurgency +{ + public class Russe : KECustomRole + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Russian", + [TranslationKeyDesc] = "RUSH B or A, i dont rember sooo good luck", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Russe", + [TranslationKeyDesc] = "RUSH B or A, i dont rember sooo good luck", + }, + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "Russe", + [TranslationKeyDesc] = "RUSH B or A, i dont rember sooo good luck", + } + }; + } + + public override int MaxHealth { get; set; } = 100; + public override RoleTypeId Role { get; set; } = RoleTypeId.ChaosRifleman; + + public float DamageToLootbox { get; set; } = 500f; + + private readonly Dictionary _playerDamage = new Dictionary(); + + private static object[] _lootPool = null; + + public override List Inventory { get; set; } = new List() + { + $"{ItemType.GunRevolver}", $"{ItemType.GunA7}", $"{ItemType.ArmorHeavy}", + $"{ItemType.GrenadeHE}", $"{ItemType.GrenadeFlash}", $"{ItemType.Radio}", + $"{ItemType.KeycardChaosInsurgency}" + }; + + public override Dictionary Ammo { get; set; } = new Dictionary() + { + { AmmoType.Ammo44Cal, 19 }, { AmmoType.Nato762, 60 } + }; + + + protected override void SubscribeEvents() + { + Exiled.Events.Handlers.Player.Hurting += OnDealingDamage; + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Player.Hurting -= OnDealingDamage; + base.UnsubscribeEvents(); + } + + + protected override void RoleAdded(Player player) + { + _playerDamage[player] = 0f; + + } + + protected override void RoleRemoved(Player player) + { + + + _playerDamage.Remove(player); + } + + private void OnDealingDamage(HurtingEventArgs ev) + { + if (ev.Attacker == null || !Check(ev.Attacker) || ev.Player == ev.Attacker) return; + + if (!_playerDamage.ContainsKey(ev.Attacker)) _playerDamage[ev.Attacker] = 0f; + _playerDamage[ev.Attacker] += ev.Amount; + + if (_playerDamage[ev.Attacker] >= DamageToLootbox) + { + _playerDamage[ev.Attacker] = 0f; + SpawnLootBox(ev.Player.Position, ev.Attacker); + } + } + + private void SpawnLootBox(Vector3 pos, Player owner) + { + Primitive box = Primitive.Create(PrimitiveType.Cube, pos + Vector3.up * 0.5f, Vector3.zero, new Vector3(0.5f, 0.5f, 0.5f), true); + box.Color = Color.black; + Timing.RunCoroutine(LootBoxAnimation(box, owner)); + } + + private IEnumerator LootBoxAnimation(Primitive box, Player p) + { + Exiled.API.Features.Toys.Light boxLight = Exiled.API.Features.Toys.Light.Create(box.Position + Vector3.up * 0.5f, Vector3.zero, Vector3.one, true); + boxLight.Intensity = 40f; + boxLight.Range = 8f; + + Color[] csRarities = { Color.blue, Color.magenta, Color.red }; + + try + { + for (int i = 0; i < 10; i++) + { + if (box == null) yield break; + + Color color = csRarities[Random.Range(0, csRarities.Length)]; + box.Color = color; + boxLight.Color = color; + + yield return Timing.WaitForSeconds(0.2f); + } + + object reward = GetRandomGambleReward(); + + if (reward is CustomItem custom) + { + custom.Spawn(box.Position); + } + else if (reward is ItemType baseItem) + { + Item.Create(baseItem).CreatePickup(box.Position); + } + } + finally + { + if (box != null) box.Destroy(); + Timing.CallDelayed(0.5f, () => { if (boxLight != null) boxLight.Destroy(); }); + } + } + + + private object GetRandomGambleReward() + { + if (_lootPool == null) + { + List tempPool = new List(); + + foreach (ItemType item in System.Enum.GetValues(typeof(ItemType))) + { + if (item == ItemType.None || item == ItemType.MicroHID || item == ItemType.Jailbird || + item == ItemType.ParticleDisruptor || item == ItemType.GunSCP127) continue; + + if (item.ToString().Contains("Ammo")) continue; + + tempPool.Add(item); + } + + if (CustomItem.Registered != null) + { + foreach (var custom in CustomItem.Registered) + { + tempPool.Add(custom); + } + } + + _lootPool = tempPool.ToArray(); + } + + return _lootPool[Random.Range(0, _lootPool.Length)]; + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.CustomRoles/CR/ChaosInsurgency/Negotiator.cs b/KruacentExiled/KE.CustomRoles/CR/ChaosInsurgency/Negotiator.cs new file mode 100644 index 00000000..2f850d8e --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/ChaosInsurgency/Negotiator.cs @@ -0,0 +1,98 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using Exiled.CustomRoles.API.Features; +using Exiled.Events.EventArgs.Player; +using KE.CustomRoles.API.Features; +using KE.Items.API.Features; +using MEC; +using PlayerRoles; +using System.Collections.Generic; +using UnityEngine; + +namespace KE.CustomRoles.CR.ChaosInsurgency +{ + public class Negotiator : KECustomRole + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Negotiator", + [TranslationKeyDesc] = "You're immune to friendly fire and you can convert zombie into your team, isn't that nice?", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Negociateur", + [TranslationKeyDesc] = "T'es immunisé au tire allié et tu peux convertir des zombies", + }, + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "Negotiator", + [TranslationKeyDesc] = "You're immune to friendly fire and you can convert zombie into your team, isn't that nice?", + }, + }; + } + public override int MaxHealth { get; set; } = 100; + public override RoleTypeId Role { get; set; } = RoleTypeId.ChaosRifleman; + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = false; + + public override float SpawnChance { get; set; } = 100; + + + public override List Inventory { get; set; } = new List() + { + "KeycardChaosInsurgency", + "GunAK", + "Medkit", + "Painkillers", + "ArmorCombat", + "Radio" + }; + + public override Dictionary Ammo { get; set; } = new Dictionary() + { + { AmmoType.Ammo12Gauge, 7 }, { AmmoType.Nato762, 90 } + }; + + + protected override void SubscribeEvents() + { + Exiled.Events.Handlers.Player.Hurting += OnHurting; + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Player.Hurting -= OnHurting; + base.UnsubscribeEvents(); + } + + protected override void GiveInventory(Player player) + { + base.GiveInventory(player); + + KECustomItem.TryGive(player, "Friend Maker", false); + + } + + + private void OnHurting(HurtingEventArgs ev) + { + if (!Check(ev.Player)) return; + if (!ev.IsAllowed) return; + if (ev.Attacker is null) return; + + if(ev.Attacker.Role.Side == ev.Player.Role.Side) + { + ev.IsAllowed = false; + } + + } + + + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/ClassD/DBoyInShape.cs b/KruacentExiled/KE.CustomRoles/CR/ClassD/DBoyInShape.cs new file mode 100644 index 00000000..e3b5f18b --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/ClassD/DBoyInShape.cs @@ -0,0 +1,68 @@ +using CustomPlayerEffects; +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using Exiled.CustomRoles.API.Features; +using Exiled.Events.EventArgs.Map; +using Exiled.Events.EventArgs.Player; +using KE.CustomRoles.API.Features; +using MEC; +using PlayerRoles; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.Experimental.GlobalIllumination; +using Utils.NonAllocLINQ; + +namespace KE.CustomRoles.CR.ClassD +{ + public class DBoyInShape : KECustomRole + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "DBoyInShape", + [TranslationKeyDesc] = "You're strong enough to open any door", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "DBoyInShape", + [TranslationKeyDesc] = "Dammmmnnnnnnn les gates", + }, + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "DBoyInShape", + [TranslationKeyDesc] = "Dammmmnnnnnnn les gates", + } + }; + } + + public override RoleTypeId Role { get; set; } = RoleTypeId.ClassD; + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = true; + public override float SpawnChance { get; set; } = 100; + public override int MaxHealth { get; set; } = 100; + + public const byte SpeedReduction = 15; + + public override HashSet Abilities => new HashSet() + { + "ForceOpen" + }; + + protected override void RoleAdded(Player player) + { + //player.EnableEffect(EffectType.Slowness, SpeedReduction,-1 ); + } + + protected override void RoleRemoved(Player player) + { + //player.DisableEffect(EffectType.Slowness); + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.CustomRoles/CR/ClassD/Enfant.cs b/KruacentExiled/KE.CustomRoles/CR/ClassD/Enfant.cs new file mode 100644 index 00000000..96a14048 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/ClassD/Enfant.cs @@ -0,0 +1,56 @@ +using CommandSystem.Commands.RemoteAdmin; +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using InventorySystem; +using InventorySystem.Items.Usables.Scp330; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.Interfaces; +using PlayerRoles; +using System.Collections.Generic; +using UnityEngine; + +namespace KE.CustomRoles.CR.ClassD +{ + public class Enfant : KECustomRole, IColor + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Kid", + [TranslationKeyDesc] = "do not the kid \nyou start with a rainbow candy (for real this time) \nyou're a bit smaller", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Enfant", + [TranslationKeyDesc] = "do not the kid \ntu commences avec un bonbon arc-en-ciel (pour de vrai cette fois) \n t'es un peu plus petit que la normal", + }, + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "Enfant", + [TranslationKeyDesc] = "do not the kid \ntu commences avec un bonbon arc-en-ciel (pour de vrai cette fois) \n t'es un peu plus petit que la normal", + } + }; + } + + + public override int MaxHealth { get; set; } = 100; + public override RoleTypeId Role { get; set; } = RoleTypeId.ClassD; + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = true; + public Color32 Color => new Color32(255, 192, 203, 0); + + public override float SpawnChance { get; set; } = 100; + public override Vector3 Scale { get; set; } = new Vector3(1, 0.75f, 1); + + + protected override void GiveInventory(Player player) + { + player.ReferenceHub.GrantCandy(CandyKindID.Rainbow, InventorySystem.Items.ItemAddReason.StartingItem); + base.GiveInventory(player); + } + } + +} diff --git a/KruacentExiled/KE.CustomRoles/CR/ClassD/Mime.cs b/KruacentExiled/KE.CustomRoles/CR/ClassD/Mime.cs new file mode 100644 index 00000000..e31cdc53 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/ClassD/Mime.cs @@ -0,0 +1,62 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using Exiled.Events.EventArgs.Player; +using InventorySystem.Items.Usables.Scp330; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.Interfaces; +using PlayerRoles; +using PlayerRoles.Spectating; +using System.Collections.Generic; +using UnityEngine; + +namespace KE.CustomRoles.CR.ClassD +{ + public class Mime : KECustomRole, IColor + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Mime", + [TranslationKeyDesc] = "you make almost no sound while walking \nand you're flat", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Mime", + [TranslationKeyDesc] = "tu fais très peu de bruit quand tu marches\net t'es tout plat", + }, + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "Mime", + [TranslationKeyDesc] = "Tu ne peux pas parler\nmais tu fais très peu de bruit quand tu marches\net t'es tout plat", + } + }; + } + + public override int MaxHealth { get; set; } = 100; + public override RoleTypeId Role { get; set; } = RoleTypeId.ClassD; + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = true; + public override float SpawnChance { get; set; } = 100; + public override Vector3 Scale { get; set; } = new Vector3(0.5f, 1, 1); + public Color32 Color => new Color32(255, 74, 74, 0); + + protected override void RoleAdded(Player player) + { + player.EnableEffect(EffectType.SilentWalk, -1, true); + } + + protected override void RoleRemoved(Player player) + { + player.DisableEffect(EffectType.SilentWalk); + } + + public override HashSet Abilities { get; } = new HashSet() + { + "SimulateDeath" + }; + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/RemainingPlayerPosition.cs b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/RemainingPlayerPosition.cs new file mode 100644 index 00000000..215ea7c2 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/RemainingPlayerPosition.cs @@ -0,0 +1,28 @@ +using HintServiceMeow.Core.Enum; +using KE.Utils.API.Displays.DisplayMeow.Placements; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.CR.CustomSCPs +{ + public class RemainingPlayerPosition : HintPosition + { + public override float Xposition => 1820; + + public override float Yposition => 60; + + public override HintAlignment HintAlignment => HintAlignment.Center; + } + + public class LogoPosition : HintPosition + { + public override float Xposition => 1700; + + public override float Yposition => 60; + + public override HintAlignment HintAlignment => HintAlignment.Center; + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP035.cs b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP035.cs new file mode 100644 index 00000000..cec73a86 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP035.cs @@ -0,0 +1,340 @@ +using Cassie; +using CustomPlayerEffects; +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Pickups; +using Exiled.API.Features.Pools; +using Exiled.CustomItems.API.Features; +using Exiled.Events.EventArgs.Player; +using Exiled.Events.EventArgs.Scp1509; +using Exiled.Events.EventArgs.Server; +using HintServiceMeow.Core.Models.Arguments; +using HintServiceMeow.Core.Utilities; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.CR.MTF; +using KE.Items.API.Features; +using KE.Utils.API.Displays.DisplayMeow; +using KE.Utils.API.Displays.DisplayMeow.Placements; +using KE.Utils.API.Features.SCPs; +using MEC; +using PlayerRoles; +using PlayerRoles.FirstPersonControl.Thirdperson; +using System; +using System.Collections.Generic; +using System.Text; +using UnityEngine; +using VoiceChat.Networking; + +namespace KE.CustomRoles.CR.CustomSCPs +{ + public class SCP035 : CustomSCP + { + public override bool IsSupport => false; + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "SCP-035", + [TranslationKeyDesc] = "Kill every humans!\nYou can't pick up the Micro-HID and anything made with it, but you take 3 time less damage by these weapon.", + ["SCP035CantPickup"] = "A strange force called 'game balance' \nprevents you from picking up this item.", + ["SCP035CantUse"] = "A strange force called 'game balance' \nprevents you from using this item.", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "SCP-035", + [TranslationKeyDesc] = "Tue tous les humains!\nTu peux pas prendre d'arme spécial mais tu prends 3 fois moins de dégât de ces armes", + ["SCP035CantPickup"] = "Une force étrange qui s'appelle 'équilibre' \nt'empêches de prendre cet objet.", + ["SCP035CantUse"] = "Une force étrange qui s'appelle 'équilibre' \nt'empêches d'utiliser cet objet.", + }, + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "SCP-035", + [TranslationKeyDesc] = "You can't pickup the Micro-HID and anything made with it, but you take 3 time less damage by these weapon.\nKill every humans", + ["SCP035CantPickup"] = "A strange force called \'game balance\' \nprevents you from picking up this item", + ["SCP035CantUse"] = "A strange force called 'game balance' \nprevents you from using this item.", + } + }; + } + + public override int MaxHealth { get; set; } = 1200; + protected override int SettingId => 10002; + + public override RoleTypeId Role { get; set; } = RoleTypeId.Tutorial; + + // 035 can't pickup these items + public HashSet BlacklistedPickup = new HashSet() + { + ItemType.Jailbird, + ItemType.ParticleDisruptor, + ItemType.MicroHID, + ItemType.Painkillers, + ItemType.Medkit, + ItemType.SCP500, + }; + public HashSet BlacklistedUsing = new HashSet() + { + ItemType.Painkillers, + ItemType.Medkit, + ItemType.SCP500, + }; + public HashSet WhitelistUsing = new HashSet() + { + ItemType.SCP330, + ItemType.SCP1853, + ItemType.SCP1509, + }; + + // 035 can't be damaged by these + public HashSet BlacklistedDamage = new HashSet() + { + DamageType.Jailbird, + DamageType.ParticleDisruptor, + DamageType.MicroHid + }; + + protected override void SubscribeEvents() + { + Exiled.Events.Handlers.Player.Hurting += OnHurting; + Exiled.Events.Handlers.Player.Dying += OnDying; + Exiled.Events.Handlers.Player.SearchingPickup += OnSearchingPickup; + Exiled.Events.Handlers.Server.EndingRound += OnEndingRound; + Exiled.Events.Handlers.Scp1509.Resurrecting += OnResurrecting; + Exiled.Events.Handlers.Player.UsingItem += OnUsingItem; + Exiled.Events.Handlers.Player.VoiceChatting += OnVoiceChatting; + + base.SubscribeEvents(); + } + + + protected override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Player.Hurting -= OnHurting; + Exiled.Events.Handlers.Player.Dying -= OnDying; + Exiled.Events.Handlers.Player.SearchingPickup -= OnSearchingPickup; + Exiled.Events.Handlers.Server.EndingRound -= OnEndingRound; + Exiled.Events.Handlers.Scp1509.Resurrecting -= OnResurrecting; + Exiled.Events.Handlers.Player.UsingItem -= OnUsingItem; + base.UnsubscribeEvents(); + } + private static HintPosition position = new RemainingPlayerPosition(); + private static HintPosition logoposition = new LogoPosition(); + private void OnDying(DyingEventArgs ev) + { + if (!Check(ev.Player)) return; + Log.Debug("cassie message"); + + Exiled.API.Features.Cassie.CustomScpTermination("SCP 0 3 5", ev.DamageHandler); + + } + + private void OnVoiceChatting(VoiceChattingEventArgs ev) + { + Player player = ev.Player; + + + VoiceMessage msg = ev.VoiceMessage; + + if(msg.Channel == VoiceChat.VoiceChatChannel.ScpChat) + { + msg.Channel = VoiceChat.VoiceChatChannel.RoundSummary; + + foreach (Player scp035 in Player.List) + { + if (scp035 != player && Check(scp035)) + { + scp035.Connection.Send(msg); + } + } + } + + + + if (Check(player)) + { + msg.Channel = VoiceChat.VoiceChatChannel.RoundSummary; + + foreach (ReferenceHub hub in SCPTeam.SCPs) + { + if (hub != player.ReferenceHub) + { + hub.connectionToClient.Send(msg); + } + + } + ev.IsAllowed = false; + } + } + + protected override void RoleAdded(Player player) + { + PlayerDisplay dis = PlayerDisplay.Get(player); + DisplayHandler.Instance.CreateAuto(player, (args) => GetPlayers(args), position.HintPlacement,HintServiceMeow.Core.Enum.HintSyncSpeed.Normal); + DisplayHandler.Instance.CreateAuto(player, (args) => GetLogo(args), logoposition.HintPlacement,HintServiceMeow.Core.Enum.HintSyncSpeed.Normal); + + + + + + player.Position = RoleTypeId.Scp049.GetRandomSpawnLocation().Position; + player.EnableEffect(100, 0, false); + base.RoleAdded(player); + } + + protected override void RoleRemoved(Player player) + { + DisplayHandler.Instance.RemoveHint(player, position.HintPlacement); + DisplayHandler.Instance.RemoveHint(player, logoposition.HintPlacement); + + player.DisableEffect(); + base.RoleRemoved(player); + } + + + + + + private string GetPlayers(AutoContentUpdateArg arg) + { + + if (!Check(Player.Get(arg.PlayerDisplay.ReferenceHub))) + return string.Empty; + return "" + Mathf.Clamp(RoundSummary.singleton.TargetCount,0,9).ToString() + ""; + //👤8 + } + + private string GetLogo(AutoContentUpdateArg arg) + { + + if (!Check(Player.Get(arg.PlayerDisplay.ReferenceHub))) + return string.Empty; + return "👤"; + } + + + + + + private void OnUsingItem(UsingItemEventArgs ev) + { + Player player = ev.Player; + if (!Check(player)) return; + + if (BlacklistedUsing.Contains(ev.Item.Type)) + { + + + ShowEffectHint(player, GetTranslation(player, "SCP035CantUse")); + ev.IsAllowed = false; + return; + } + } + + private void OnSearchingPickup(SearchingPickupEventArgs ev) + { + Player player = ev.Player; + Pickup pickup = ev.Pickup; + if (!Check(ev.Player)) return; + + + + CustomItem item = null; + + CustomItem.TryGet(pickup, out item); + + + if(item != null) + { + + KECustomItem kECustomItem = item as KECustomItem; + + if (kECustomItem.Name == "TrueDivinePills" || kECustomItem.Name == "Divine Pills") + { + ShowEffectHint(player, GetTranslation(player, "SCP035CantPickup")); + ev.IsAllowed = false; + return; + } + } + + + if (pickup.Type.IsScp() && !WhitelistUsing.Contains(pickup.Type)) + { + ShowEffectHint(player, GetTranslation(player, "SCP035CantPickup")); + ev.IsAllowed = false; + return; + } + + if (pickup.Type == ItemType.GunSCP127) + { + ShowEffectHint(player, GetTranslation(player, "SCP035CantPickup")); + ev.IsAllowed = false; + return; + } + + + + + + if (BlacklistedPickup.Contains(pickup.Type)) + { + ShowEffectHint(player, GetTranslation(player, "SCP035CantPickup")); + ev.IsAllowed = false; + return; + + } + } + + private void OnHurting(HurtingEventArgs ev) + { + if (Check(ev.Player)) + { + if (BlacklistedDamage.Contains(ev.DamageHandler.Type)) + { + if (ev.IsAllowed) + { + ev.DamageHandler.Damage /= 3; + } + + return; + } + + if (ev.Attacker != null && ev.Attacker.Role.Side == Side.Scp) + { + ev.IsAllowed = false; + return; + } + } + + if (Check(ev.Attacker)) + { + if (ev.Player.Role.Side == Side.Scp) + { + ev.IsAllowed = Server.FriendlyFire || ev.Attacker.IsFriendlyFireEnabled; + return; + } + } + + + + } + + private void OnResurrecting(ResurrectingEventArgs ev) + { + if (!Check(ev.Player)) return; + ev.NewRole = RoleTypeId.Scp0492; + Timing.CallDelayed(1, () => ev.Target.Health = 100); + } + + public void OnEndingRound(EndingRoundEventArgs ev) + { + /*if (TrackedPlayers.Count <= 0) return; + + if (ev.ClassList.mtf_and_guards != 0 || ev.ClassList.scientists != 0) ev.IsAllowed = false; + else if (ev.ClassList.class_ds != 0 || ev.ClassList.chaos_insurgents != 0) ev.IsAllowed = false; + else if (ev.ClassList.scps_except_zombies + ev.ClassList.zombies > 0) ev.IsAllowed = true; + else ev.IsAllowed = true;*/ + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/Positions/SCP049CLevelPosition.cs b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/Positions/SCP049CLevelPosition.cs new file mode 100644 index 00000000..0b880bfc --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/Positions/SCP049CLevelPosition.cs @@ -0,0 +1,20 @@ +using HintServiceMeow.Core.Enum; +using KE.Utils.API.Displays.DisplayMeow.Placements; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.CR.CustomSCPs.SCP049C.Positions +{ + public class SCP049CLevelPosition : HintPosition + { + public override float Xposition => 1060; + + public override float Yposition => 1030; + + public override HintAlignment HintAlignment => HintAlignment.Center; + public override string Name => "SCP049CLevel"; + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/Positions/SCP049CUnlockablePosition.cs b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/Positions/SCP049CUnlockablePosition.cs new file mode 100644 index 00000000..a6a5bce6 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/Positions/SCP049CUnlockablePosition.cs @@ -0,0 +1,41 @@ +using HintServiceMeow.Core.Enum; +using KE.Utils.API.Displays.DisplayMeow.Placements; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.CR.CustomSCPs.SCP049C.Positions +{ + internal class SCP049CUnlockableTitlePosition : HintPosition + { + public override float Xposition => 0; + + public override float Yposition => 360; + + public override HintAlignment HintAlignment => HintAlignment.Center; + public override string Name => "SCP049CUnlockableTitle"; + } + + + internal class SCP049CUnlockableLeftPosition : HintPosition + { + public override float Xposition => -400; + + public override float Yposition => 450; + + public override HintAlignment HintAlignment => HintAlignment.Center; + public override string Name => "SCP049CUnlockableLeft"; + } + + internal class SCP049CUnlockableRightPosition : HintPosition + { + public override float Xposition => 400; + + public override float Yposition => 450; + + public override HintAlignment HintAlignment => HintAlignment.Center; + public override string Name => "SCP049CUnlockableRight"; + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/RagdollArrowComp.cs b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/RagdollArrowComp.cs new file mode 100644 index 00000000..b9e66095 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/RagdollArrowComp.cs @@ -0,0 +1,74 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.DamageHandlers; +using KE.Utils.API.Features; +using KE.Utils.API.KETextToy; +using NorthwoodLib.Pools; +using PlayerRoles.Ragdolls; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.CustomRoles.CR.CustomSCPs.SCP049C +{ + public class RagdollArrowComp : MonoBehaviour + { + + private FollowingTextToy arrow = null; + + private Ragdoll ragdoll; + + public const string arrowText = "↓"; + + public void Init(Ragdoll ragdoll) + { + this.ragdoll = ragdoll; + + } + + + private void Update() + { + if (ragdoll.IsExpired && arrow == null && ragdoll.DamageHandler is PlayerStatsSystem.AttackerDamageHandler) + { + CreateArrow(); + } + } + + private void OnDestroy() + { + arrow?.Destroy(); + } + + public void SetColor(Color color) + { + if (arrow == null) return; + StringBuilder sb = StringBuilderPool.Shared.Rent(); + + sb.Append(""); + sb.Append(arrowText); + sb.Append(""); + + + arrow.Text = sb.ToString(); + + StringBuilderPool.Shared.Return(sb); + + } + + private void CreateArrow() + { + KELog.Debug("arrow"); + arrow = new FollowingTextToy(SCP049CRole.instance.TrackedPlayers, ragdoll.Position+Vector3.up, Quaternion.identity, Vector3.one*2); + + arrow.OnlyMoveY = true; + SetColor(Color.white); + arrow.Toy.Spawn(); + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/SCP049CGUI.cs b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/SCP049CGUI.cs new file mode 100644 index 00000000..fb961441 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/SCP049CGUI.cs @@ -0,0 +1,152 @@ +using Exiled.API.Features; +using HintServiceMeow.Core.Enum; +using HintServiceMeow.Core.Models.HintContent; +using KE.CustomRoles.CR.CustomSCPs.SCP049C.Positions; +using KE.CustomRoles.CR.CustomSCPs.SCP049C.UnlockableAbilities; +using KE.Utils.API.Displays.DisplayMeow; +using KE.Utils.API.Displays.DisplayMeow.Placements; +using NorthwoodLib.Pools; +using PlayerRoles.FirstPersonControl.Thirdperson; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.CR.CustomSCPs.SCP049C +{ + public class SCP049CGUI : IDisposable + { + + private SCP049CLevelSystem _lvl; + + private Player player => Player.Get(_lvl.Hub); + public static readonly HintPosition HintTitlePosition = new SCP049CUnlockableTitlePosition(); + public static readonly HintPosition HintLeftPosition = new SCP049CUnlockableLeftPosition(); + public static readonly HintPosition HintRightPosition = new SCP049CUnlockableRightPosition(); + + + private int timeRemaining => _lvl.TimeRemaining; + public SCP049CGUI(SCP049CLevelSystem lvl) + { + _lvl = lvl; + } + + public Unlockable[] UnlockableOnScreen { get; private set; } + public void CreateHints(List ability) + { + UnlockableOnScreen = ability.ToArray(); + CreateAutoIfNotExist(player, HintTitlePosition, (arg) => Title(player),HintSyncSpeed.Normal); + CreateAutoIfNotExist(player, HintLeftPosition, (arg) => GetContent(player, HintLeftPosition.HintPlacement), HintSyncSpeed.Fastest); + CreateAutoIfNotExist(player, HintRightPosition, (arg) => GetContent(player, HintRightPosition.HintPlacement), HintSyncSpeed.Fastest); + } + + private void CreateAutoIfNotExist(Player player,HintPosition hintPosition, AutoContent.TextUpdateHandler update, HintSyncSpeed hintSyncSpeed) + { + if (!DisplayHandler.Instance.HasHint(player, hintPosition.HintPlacement)) + { + DisplayHandler.Instance.CreateAuto(player, update, hintPosition.HintPlacement, hintSyncSpeed); + } + } + + public void DestroyHints() + { + DisplayHandler.Instance.RemoveHint(player, HintTitlePosition.HintPlacement); + DisplayHandler.Instance.RemoveHint(player, HintLeftPosition.HintPlacement); + DisplayHandler.Instance.RemoveHint(player, HintRightPosition.HintPlacement); + UnlockableOnScreen = null; + } + + + private const string TitleMessage = "Choose an ability! (%seconds%s)\n use Right and Left (Server settings)"; + + + private string Title(Player player) + { + return TitleMessage.Replace("%seconds%", timeRemaining.ToString()); + } + + + public const int FontSize = 15; + + private string GetContent(Player player,HintPlacement hintPlacement) + { + StringBuilder sb = StringBuilderPool.Shared.Rent(); + string msg; + try + { + + int abilityShowingIndex; + + if (hintPlacement.XCoordinate < 0) + { + abilityShowingIndex = 0; + } + else + { + abilityShowingIndex = 1; + } + + + + + Unlockable unlockable= UnlockableOnScreen[abilityShowingIndex]; + + bool flag = _lvl.Selected == abilityShowingIndex; + + if (flag) + { + sb.AppendLine(""); + } + else + { + sb.AppendLine(""); + } + + + sb.AppendLine(unlockable.GetName(player.ReferenceHub)); + if (flag) + { + sb.AppendLine(""); + } + else + { + sb.AppendLine(""); + } + + sb.Append(""); + sb.Append(unlockable.GetDescription(player.ReferenceHub)); + sb.Append(""); + } + catch(Exception e) + { + Log.Error(e); + } + finally + { + msg = sb.ToString(); + + StringBuilderPool.Shared.Return(sb); + if (string.IsNullOrEmpty(msg)) + { + msg = " "; + } + } + return msg; + + } + + + + + public void Dispose() + { + if (player != null) + { + DestroyHints(); + } + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/SCP049CLevelSystem.cs b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/SCP049CLevelSystem.cs new file mode 100644 index 00000000..878e5f28 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/SCP049CLevelSystem.cs @@ -0,0 +1,333 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using KE.CustomRoles.CR.CustomSCPs.SCP049C.Positions; +using KE.CustomRoles.CR.CustomSCPs.SCP049C.UnlockableAbilities; +using KE.CustomRoles.Settings; +using KE.Utils.API; +using KE.Utils.API.Displays.DisplayMeow; +using KE.Utils.API.Displays.DisplayMeow.Placements; +using KE.Utils.API.Features; +using MEC; +using NorthwoodLib.Pools; +using PlayerRoles.FirstPersonControl; +using PlayerRoles.Ragdolls; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace KE.CustomRoles.CR.CustomSCPs.SCP049C +{ + public class SCP049CLevelSystem : MonoBehaviour + { + private int level; + public int Level + { + get + { + return level; + } + set + { + level = value; + } + } + + public const int MaxLevel = 3; + + + private int currentkill = 0; + private int objective = 0; + + + public int CurrentKill => currentkill; + public int KillObjective => objective; + + public bool MaxLevelReached + { + get + { + return Level >= MaxLevel; + } + } + + private ReferenceHub _hub; + + public ReferenceHub Hub => _hub; + private SCP049CGUI gui; + public SCP049CGUI GUI => gui; + + public List CurrentAbilities { get; private set; } + + private static HashSet abilities; + + private static IReadOnlyCollection Abilities + { + get + { + if(abilities == null) + { + abilities = ReflectionHelper.GetObjects().ToHashSet(); + } + return abilities; + } + } + + + public void Awake() + { + _hub = ReferenceHub.GetHub(base.gameObject); + CurrentAbilities = new List(); + currentkill = 0; + currentTime = 0; + Level = 0; + objective = GetNbKillPerTier(0); + + gui = new SCP049CGUI(this); + + SettingHandler.RightPressed += SettingHandler_RightPressed; + SettingHandler.LeftPressed += SettingHandler_LeftPressed; + + + } + + private int selected = 0; + public int Selected => selected; + private const int maxPerScreen = 2; + private void SettingHandler_LeftPressed(Player obj) + { + if (obj.ReferenceHub != _hub) return; + if (!CurrentlyChoosing) return; + + selected = Mathf.Abs((selected - 1) % maxPerScreen); + } + + private void SettingHandler_RightPressed(Player obj) + { + if (obj.ReferenceHub != _hub) return; + if (!CurrentlyChoosing) return; + + + selected = (selected + 1) % maxPerScreen; + } + + public void OnDestroy() + { + SettingHandler.RightPressed -= SettingHandler_RightPressed; + SettingHandler.LeftPressed -= SettingHandler_LeftPressed; + + GUI.Dispose(); + gui = null; + DisableAll(); + } + + + + public void AddKill() + { + KELog.Debug("add kill"); + currentkill++; + + if(!MaxLevelReached && currentkill >= KillObjective) + { + currentkill = 0; + + AddLevel(); + } + + } + + public static int GetNbKillPerTier(int tier) + { + //int x = tier + 1; + + //double result = ((-1) * Math.Pow(x, 2)) + (6*x)-1; + + //return (int)Math.Ceiling(result); + + if(tier == 2) + { + return 4; + } + + return 2; + + } + + + public void DisableAll() + { + foreach (Unlockable ability in CurrentAbilities) + { + ability.Remove(_hub); + } + } + + public void AddLevel() + { + List ability = ListPool.Shared.Rent(); + + Level++; + objective = GetNbKillPerTier(Level); + foreach (Unlockable possibleAbility in Abilities) + { + if(possibleAbility.Tier == Level) + { + ability.Add(possibleAbility); + } + } + + Log.Debug("nbaiblit"+ability.Count); + Timing.RunCoroutine(GiveNewUnlockable(ability)); + + + } + + + + + + + + + + + + private int currentTime = 0; + private const int maxTime = 17; + public int TimeRemaining => maxTime-currentTime; + + private bool currentlyChoosing = false; + public bool CurrentlyChoosing => currentlyChoosing; + private IEnumerator GiveNewUnlockable(List ability) + { + + Unlockable choosen = null; + + + GUI.CreateHints(ability); + currentTime = 0; + selected = 0; + currentlyChoosing = true; + while (currentTime < maxTime) + { + yield return Timing.WaitForSeconds(1); + currentTime++; + } + + try + { + currentlyChoosing = false; + + choosen = GUI.UnlockableOnScreen[selected]; + GUI.DestroyHints(); + + if (choosen != null) + { + choosen.Grant(_hub); + CurrentAbilities.Add(choosen); + KELog.Debug(CurrentAbilities.Count); + } + } + catch(Exception e) + { + Log.Error(e); + } + finally + { + ListPool.Shared.Return(ability); + } + } + + + private Collider[] NonAlloc = new Collider[8]; + private Ragdoll ragdoll = null; + + private float cooldown = 0f; + private float timeOnCorpse = 10f; + + internal void Update() + { + if (ragdoll != null) + { + UpdateTime(); + } + else + { + GetNearRagdoll(); + } + + } + + public const float MaxDistance = 2; + + private void UpdateTime() + { + + if(ragdoll.GameObject == null) + { + Reset(); + return; + } + + if (Vector3.Distance(ragdoll.Position,_hub.transform.position) > MaxDistance) + { + Reset(); + return; + } + + + cooldown += Time.deltaTime; + + + if(ragdoll.GameObject.TryGetComponent(out var comp)) + { + comp.SetColor(Color.red); + + if (cooldown > timeOnCorpse) + { + ragdoll.Destroy(); + AddKill(); + Reset(); + } + } + + + + } + + + private void Reset() + { + if(ragdoll != null && ragdoll.GameObject.TryGetComponent(out var comp)) + { + comp.SetColor(Color.white); + } + + ragdoll = null; + cooldown = 0; + } + + + private void GetNearRagdoll() + { + int num = Physics.OverlapSphereNonAlloc(_hub.GetPosition(), MaxDistance, NonAlloc, (int)LayerMasks.Ragdoll); + + for (int i = 0; i < num; i++) + { + if (NonAlloc[i].TryGetComponent(out var r)) + { + Ragdoll ragdoll = Ragdoll.Get(r); + + + if (ragdoll.IsExpired) + { + this.ragdoll = ragdoll; + } + + } + } + } + + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/SCP049CRole.cs b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/SCP049CRole.cs new file mode 100644 index 00000000..09d39ca4 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/SCP049CRole.cs @@ -0,0 +1,162 @@ +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.Events.EventArgs.Player; +using Exiled.Events.EventArgs.Scp049; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.CR.CustomSCPs.SCP049C.Positions; +using KE.CustomRoles.CR.CustomSCPs.SCP049C.UnlockableAbilities.Tier2; +using KE.Utils.API.Displays.DisplayMeow; +using LabApi.Events.Arguments.Scp049Events; +using NorthwoodLib.Pools; +using PlayerRoles; +using PlayerStatsSystem; +using System.Collections.Generic; +using System.Text; + +namespace KE.CustomRoles.CR.CustomSCPs.SCP049C +{ + public class SCP049CRole : CustomSCP + { + public override bool IsSupport => false; + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "SCP049-C", + [TranslationKeyDesc] = "A modified SCP049 instance which does not create any SCP049-2\ninstead consuming the body to gain powerful abilities\nDoctor's call gives 300 Hume Shield\nExpired body can still be consumed but need more time to do so (stay near the arrow until it disappear (10s))", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "SCP049-C", + [TranslationKeyDesc] = "A modified SCP049 instance which does not create any SCP049-2\ninstead consuming the body to gain powerful abilities\nDoctor's call gives 300 Hume Shield\nExpired body can still be consumed but need more time to do so (stay near the arrow until it disappear (10s))", + }, + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "SCP049-C", + [TranslationKeyDesc] = "A modified SCP049 instance which does not create any SCP049-2\ninstead consuming the body to gain powerful abilities\nDoctor's call gives 300 Hume Shield\nExpired body can still be consumed but need more time to do so (stay near the arrow until it disappear (10s))", + } + }; + } + + public override RoleTypeId Role => RoleTypeId.Scp049; + + public override string InternalName => "C"; + public override int MaxHealth { get; set; } = 2500; + public override float SpawnChance { get; set; } = 100; + protected override int SettingId => 10003; + + internal static SCP049CRole instance = null; + + public override void Init() + { + instance = this; + base.Init(); + } + + public static readonly SCP049CLevelPosition HintPosition = new SCP049CLevelPosition(); + + protected override void RoleAdded(Player player) + { + player.ReferenceHub.gameObject.AddComponent(); + if (!DisplayHandler.Instance.HasHint(player, HintPosition.HintPlacement)) + { + DisplayHandler.Instance.CreateAuto(player, (arg) => GetContent(player), HintPosition.HintPlacement); + } + + + player.Position = RoleTypeId.Scp939.GetRandomSpawnLocation().Position; + base.RoleAdded(player); + } + + + + public string GetContent(Player player) + { + if(!player.GameObject.TryGetComponent(out var comp)) + { + return " "; + } + + StringBuilder sb = StringBuilderPool.Shared.Rent(); + bool flag = comp.MaxLevelReached; + + if (!flag) + { + sb.Append("Tier : "); + sb.AppendLine(comp.Level.ToString()); + } + else + { + sb.AppendLine("Max Tier"); + } + + sb.Append("Kills : "); + sb.Append(comp.CurrentKill); + if (!flag) + { + sb.Append("/"); + sb.Append(comp.KillObjective); + } + return StringBuilderPool.Shared.ToStringReturn(sb); + } + + protected override void RoleRemoved(Player player) + { + DisplayHandler.Instance.RemoveHint(player, HintPosition.HintPlacement); + + + if(player.ReferenceHub.gameObject.TryGetComponent(out var lvl)) + { + UnityEngine.Object.Destroy(lvl); + } + + + base.RoleRemoved(player); + } + + protected override void SubscribeEvents() + { + Exiled.Events.Handlers.Scp049.FinishingRecall += OnFinishingRecall; + LabApi.Events.Handlers.Scp049Events.UsedDoctorsCall += OnUsedDoctorsCall; + Exiled.Events.Handlers.Player.SpawnedRagdoll += OnSpawnedRagdoll; + Exiled.Events.Handlers.Player.Hurting += DeflectDamageUnlockable.OnHurting; + base.SubscribeEvents(); + } + protected override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Scp049.FinishingRecall -= OnFinishingRecall; + LabApi.Events.Handlers.Scp049Events.UsedDoctorsCall -= OnUsedDoctorsCall; + Exiled.Events.Handlers.Player.SpawnedRagdoll -= OnSpawnedRagdoll; + Exiled.Events.Handlers.Player.Hurting -= DeflectDamageUnlockable.OnHurting; + base.UnsubscribeEvents(); + } + private void OnFinishingRecall(FinishingRecallEventArgs ev) + { + if (!Check(ev.Player)) return; + if (!ev.Player.ReferenceHub.gameObject.TryGetComponent(out var lvl)) return; + ev.IsAllowed = false; + ev.Ragdoll.Destroy(); + + lvl.AddKill(); + + + } + + private void OnUsedDoctorsCall(Scp049UsedDoctorsCallEventArgs ev) + { + if (!Check(ev.Player)) return; + ev.Player.GetStatModule().AddAmount(300f); + + } + private void OnSpawnedRagdoll(SpawnedRagdollEventArgs ev) + { + RagdollArrowComp comp = ev.Ragdoll.GameObject.AddComponent(); + comp.Init(ev.Ragdoll); + } + + + + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/Tier1/HigherJumpComp.cs b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/Tier1/HigherJumpComp.cs new file mode 100644 index 00000000..8cbcf928 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/Tier1/HigherJumpComp.cs @@ -0,0 +1,65 @@ +using Exiled.API.Features; +using Exiled.API.Features.Roles; +using Mirror; +using PlayerRoles.FirstPersonControl; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using Utf8Json.Internal.DoubleConversion; + +namespace KE.CustomRoles.CR.CustomSCPs.SCP049C.UnlockableAbilities.Tier1 +{ + internal class HigherJumpComp : MonoBehaviour + { + + private Player _player; + private static readonly Vector3 _jumpgravity = UnityEngine.Vector3.up * -3; + + private void Awake() + { + _player = Player.Get(base.gameObject); + IsActive = false; + } + + public bool IsActive { get; set; } + + private void Update() + { + + if(_player.Role is FpcRole fpc) + { + + if (IsActive) + { + + Vector3 vector = (fpc.Velocity.y < 0f) ? FpcGravityController.DefaultGravity : _jumpgravity; + if (fpc.Gravity != vector) + { + fpc.Gravity = vector; + } + } + else + { + fpc.Gravity = FpcGravityController.DefaultGravity; + } + } + + } + + private void OnDestroy() + { + if(_player.Role is FpcRole fpc) + { + fpc.Gravity = FpcGravityController.DefaultGravity; + } + } + + public void Destroy() + { + Destroy(this); + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/Tier1/JumpUnlockable.cs b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/Tier1/JumpUnlockable.cs new file mode 100644 index 00000000..d8b15f06 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/Tier1/JumpUnlockable.cs @@ -0,0 +1,44 @@ +using Exiled.API.Features; +using Exiled.API.Features.Roles; +using KE.CustomRoles.Abilities.SCP049C; +using KE.CustomRoles.API.Features; +using PlayerRoles.FirstPersonControl; +using System; +using UnityEngine; +using LabPlayer = LabApi.Features.Wrappers.Player; +namespace KE.CustomRoles.CR.CustomSCPs.SCP049C.UnlockableAbilities.Tier1 +{ + internal class JumpUnlockable : UnlockableAbility + { + public override byte Tier => 1; //1 + + public override KEAbilities Ability => KEAbilities.Get("ToggleHighJump"); + + public override string GetName(ReferenceHub hub) + { + return "Jump"; + } + public override string GetDescription(ReferenceHub hub) + { + return "You can now jump higher\n(high enough to go up Gate B)"; + } + public override void Grant(ReferenceHub hub) + { + + if (!hub.gameObject.TryGetComponent(out _)) + { + hub.gameObject.AddComponent(); + } + base.Grant(hub); + } + + public override void Remove(ReferenceHub hub) + { + if (hub.gameObject.TryGetComponent(out var comp)) + { + comp.Destroy(); + } + base.Remove(hub); + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/Tier1/MoreShieldUnlockable.cs b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/Tier1/MoreShieldUnlockable.cs new file mode 100644 index 00000000..4df1182d --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/Tier1/MoreShieldUnlockable.cs @@ -0,0 +1,40 @@ +using Exiled.API.Features; +using PlayerRoles.PlayableScps.HumeShield; +using PlayerStatsSystem; +using System; +using UnityEngine; +using LabPlayer = LabApi.Features.Wrappers.Player; +namespace KE.CustomRoles.CR.CustomSCPs.SCP049C.UnlockableAbilities.Tier1 +{ + internal class MoreShieldUnlockable : Unlockable + { + public override byte Tier => 1; + public override string GetName(ReferenceHub hub) + { + return "More shield"; + } + public const float Shield = 1100f; + public override string GetDescription(ReferenceHub hub) + { + return $"Set your max Shield at {Shield}\ninstead of {hub.playerStats.GetModule().MaxValue}"; + } + public override void Grant(ReferenceHub hub) + { + LabPlayer lab = LabPlayer.Get(hub); + + lab.MaxHumeShield = 1100; + } + + public override void Remove(ReferenceHub hub) + { + LabPlayer lab = LabPlayer.Get(hub); + IHumeShieldProvider.GetForHub(hub, out _, out var hsMax, out _, out _); + + lab.MaxHumeShield = hsMax; + + lab.HumeShield = Mathf.Min(lab.HumeShield, lab.MaxHumeShield); + + + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/Tier2/DeflectDamageUnlockable.cs b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/Tier2/DeflectDamageUnlockable.cs new file mode 100644 index 00000000..e8c3d9e2 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/Tier2/DeflectDamageUnlockable.cs @@ -0,0 +1,84 @@ +using Exiled.API.Features; +using Exiled.API.Features.Roles; +using Exiled.Events.EventArgs.Player; +using KE.Utils.API.Features; +using PlayerRoles.FirstPersonControl; +using System; +using System.Collections.Generic; +using UnityEngine.Windows.Speech; +using LabPlayer = LabApi.Features.Wrappers.Player; +namespace KE.CustomRoles.CR.CustomSCPs.SCP049C.UnlockableAbilities.Tier2 +{ + internal class DeflectDamageUnlockable : Unlockable + { + public override byte Tier => 2; + public override string GetName(ReferenceHub hub) + { + return "Deflect Damages"; + } + public override string GetDescription(ReferenceHub hub) + { + return "1 chance sur 8 d'annuler un dégât (.5s par hp sauvé de cooldown)"; + } + + + private static Dictionary cooldowns = new Dictionary(); + + public override void Grant(ReferenceHub hub) + { + cooldowns.Add(hub, null); + + } + + public override void Remove(ReferenceHub hub) + { + + cooldowns.Remove(hub); + } + + + internal static void OnHurting(HurtingEventArgs ev) + { + + ReferenceHub hub = ev.Player.ReferenceHub; + if (ev.IsAllowed && cooldowns.ContainsKey(hub)) + { + LastDamage lastDamage = cooldowns[hub]; + + if(lastDamage is null || lastDamage.IsDeflectable()) + { + ev.IsAllowed = false; + cooldowns[hub] = new LastDamage(ev.Amount); + } + } + } + + private class LastDamage + { + public float DamageAmount; + public DateTime Time; + public LastDamage(float damageAmount) + { + DamageAmount = damageAmount; + Time = DateTime.Now; + } + + + public bool IsDeflectable() + { + return DateTime.Now > (Time + GetCooldown(DamageAmount)); + } + + + public static TimeSpan GetCooldown(float damage) + { + return TimeSpan.FromSeconds(damage * .5f); + } + + } + } + + + + +} diff --git a/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/Tier2/SmallUnlockable.cs b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/Tier2/SmallUnlockable.cs new file mode 100644 index 00000000..5714e9a6 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/Tier2/SmallUnlockable.cs @@ -0,0 +1,26 @@ +using CustomPlayerEffects; +using Exiled.API.Features; +using InventorySystem.Items.Usables.Scp1344; +using KE.CustomRoles.API.Features; +using KE.Utils.API.Features; +using System; +using LabPlayer = LabApi.Features.Wrappers.Player; +namespace KE.CustomRoles.CR.CustomSCPs.SCP049C.UnlockableAbilities.Tier2 +{ + internal class SmallUnlockable : UnlockableAbility + { + public override byte Tier => 2; + + public override KEAbilities Ability => KEAbilities.Get("Small"); + + public override string GetName(ReferenceHub hub) + { + return "Smol"; + } + public override string GetDescription(ReferenceHub hub) + { + return "Gain a new ability to be small for 30 seconds (60s of cooldown)"; + } + + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/Tier3/GogglesNoWarningUnlockable.cs b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/Tier3/GogglesNoWarningUnlockable.cs new file mode 100644 index 00000000..1a1fa900 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/Tier3/GogglesNoWarningUnlockable.cs @@ -0,0 +1,34 @@ +using CustomPlayerEffects; +using Exiled.API.Features; +using InventorySystem.Items.Usables.Scp1344; +using System; +using LabPlayer = LabApi.Features.Wrappers.Player; +namespace KE.CustomRoles.CR.CustomSCPs.SCP049C.UnlockableAbilities.Tier3 +{ + internal class GogglesNoWarningUnlockable : Unlockable + { + public override byte Tier => 3; //3 + public override string GetName(ReferenceHub hub) + { + return "SCP-1344"; + } + public override string GetDescription(ReferenceHub hub) + { + return "See through walls like when using SCP-1344"; + } + public override void Grant(ReferenceHub hub) + { + LabPlayer lab = LabPlayer.Get(hub); + + lab.EnableEffect(); + + } + + public override void Remove(ReferenceHub hub) + { + LabPlayer lab = LabPlayer.Get(hub); + + lab.DisableEffect(); + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/Tier3/SpeedUnlockable.cs b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/Tier3/SpeedUnlockable.cs new file mode 100644 index 00000000..5514545b --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/Tier3/SpeedUnlockable.cs @@ -0,0 +1,50 @@ +using CustomPlayerEffects; +using Exiled.API.Features; +using InventorySystem.Items.Usables.Scp1344; +using KE.Utils.API.Features; +using KE.Utils.Extensions; +using System; +using LabPlayer = LabApi.Features.Wrappers.Player; +namespace KE.CustomRoles.CR.CustomSCPs.SCP049C.UnlockableAbilities.Tier3 +{ + internal class SpeedUnlockable : Unlockable + { + public override byte Tier => 3; + + public override string GetName(ReferenceHub hub) + { + return "Speed"; + } + public override string GetDescription(ReferenceHub hub) + { + return $"Become {Intensity}% faster but lose all of the other abilities"; + } + + + public const byte Intensity = 50; + + public override void Grant(ReferenceHub hub) + { + LabPlayer lab = LabPlayer.Get(hub); + + MovementBoost move = lab.GetEffect(); + + Player.Get(lab).AddLevelEffect(Intensity); + + hub.GetComponent().DisableAll(); + + + } + + public override void Remove(ReferenceHub hub) + { + LabPlayer lab = LabPlayer.Get(hub); + MovementBoost move = lab.GetEffect(); + + Player.Get(lab).AddLevelEffect(-Intensity); + + + + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/Unlockable.cs b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/Unlockable.cs new file mode 100644 index 00000000..8b11119b --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/Unlockable.cs @@ -0,0 +1,25 @@ +using Exiled.API.Features; +using KE.CustomRoles.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.CR.CustomSCPs.SCP049C.UnlockableAbilities +{ + public abstract class Unlockable + { + + public abstract byte Tier { get; } + + public abstract string GetName(ReferenceHub hub); + public abstract string GetDescription(ReferenceHub hub); + + public abstract void Grant(ReferenceHub hub); + + + public abstract void Remove(ReferenceHub hub); + + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/UnlockableAbility.cs b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/UnlockableAbility.cs new file mode 100644 index 00000000..629a297d --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP049C/UnlockableAbilities/UnlockableAbility.cs @@ -0,0 +1,36 @@ +using Exiled.API.Features; +using KE.CustomRoles.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.CR.CustomSCPs.SCP049C.UnlockableAbilities +{ + public abstract class UnlockableAbility : Unlockable + { + + + + public abstract KEAbilities Ability { get; } + + public override void Grant(ReferenceHub hub) + { + Player player = Player.Get(hub); + Ability.AddAbility(player); + + if(!KEAbilities.TryGetSelected(player, out _)) + { + KEAbilities.SelectFirstAbility(player); + } + + } + + public override void Remove(ReferenceHub hub) + { + Ability.RemoveAbility(Player.Get(hub)); + } + + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP457.cs b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP457.cs new file mode 100644 index 00000000..0552b100 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/CustomSCPs/SCP457.cs @@ -0,0 +1,209 @@ +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using Exiled.API.Features.Doors; +using Exiled.API.Features.Roles; +using Exiled.API.Features.Toys; +using Exiled.API.Interfaces; +using Exiled.Events.Commands.Reload; +using Exiled.Events.EventArgs.Scp106; +using KE.CustomRoles.Abilities.FireAbilities; +using KE.CustomRoles.API.Features; +using KE.Utils.API; +using MEC; +using PlayerRoles; +using PlayerStatsSystem; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using Light = Exiled.API.Features.Toys.Light; + +namespace KE.CustomRoles.CR.CustomSCPs +{ + public class SCP457 : CustomSCP + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "SCP-457", + [TranslationKeyDesc] = "You do passive damage around you", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "SCP-457", + [TranslationKeyDesc] = "j'ai dit pas trop cuite", + }, + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "SCP-457", + [TranslationKeyDesc] = "You do passive damage around you", + } + }; + } + public override int MaxHealth { get; set; } = 5000; + public override RoleTypeId Role { get; set; } = RoleTypeId.Scp106; + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = false; + public override bool IsSupport { get; } = false; + + public override float SpawnChance { get; set; } = 0; + + private Dictionary _inside = new Dictionary(); + private Dictionary> _handles = new Dictionary>(); + + + public static float DamageRefreshRate = 5f; + public static readonly Color FlameColor = new Color(2, 1.08f, 0); + + public Collider[] SphereNonAlloc = new Collider[32]; + + + public override HashSet Abilities => new HashSet() + { + "Fireball", + "BlindingFlash" + }; + + protected override int SettingId => 10001; + + protected override void RoleAdded(Player player) + { + Log.Debug("adding role 457"); + _inside.Add(player, null); + _handles.Add(player, new HashSet()); + + _handles[player].Add(Timing.RunCoroutine(InsideLight(player))); + _handles[player].Add(Timing.RunCoroutine(PassiveDamage(player))); + + if(player.Role is Scp106Role role) + { + role.Vigor = role.VigorComponent.MaxValue; + } + } + + private IEnumerator InsideLight(Player player) + { + Light light = Light.Create(); + light.Intensity = .5f; + _inside[player] = light; + light.Position = player.Position; + light.Color = FlameColor; + light.Spawn(); + while (true) + { + light.Position = player.Position; + PassiveDamage(player); + yield return Timing.WaitForOneFrame; + } + } + + + + private IEnumerator PassiveDamage(Player scp) + { + while (scp.IsAlive) + { + + + int num = Physics.OverlapSphereNonAlloc(scp.Position, 5, SphereNonAlloc); + + if (num > 0) + { + for (int i = 0; i < num; i++) + { + Player player = Player.Get(SphereNonAlloc[i]); + if (player is null) continue; + if (!HitboxIdentity.IsDamageable(scp.ReferenceHub, player.ReferenceHub)) continue; + + if(Physics.Linecast(player.Position, scp.Position, out var hitinfo)) + { + float damage = -(hitinfo.distance / 3) + 10; + player.EnableEffect(Exiled.API.Enums.EffectType.Burned, DamageRefreshRate, true); + player.Hurt(damage, Fireball.BallDamage.RagdollInspectText); + scp.CustomHumeShieldStat.AddAmount(damage); + } + + } + } + yield return Timing.WaitForSeconds(DamageRefreshRate); + } + + } + + protected override void RoleRemoved(Player player) + { + Log.Debug("remove role 457"); + + + if (_inside.TryGetValue(player, out var l)) + { + l.Destroy(); + _inside.Remove(player); + } + + + + + + if (_handles.TryGetValue(player, out var handle)) + { + foreach (var c in handle) + { + Timing.KillCoroutines(c); + } + _handles.Remove(player); + } + + } + + public override bool IsAvailable(Player player) + { + return false; + } + + + private void OnStalking(StalkingEventArgs ev) + { + if (!Check(ev.Player)) return; + ev.IsAllowed = false; + } + + private void OnTP(TeleportingEventArgs ev) + { + if (!Check(ev.Player)) return; + ev.IsAllowed = false; + + } + + + protected override void SubscribeEvents() + { + Exiled.Events.Handlers.Scp106.Stalking += OnStalking; + Exiled.Events.Handlers.Scp106.Teleporting += OnTP; + Exiled.Events.Handlers.Scp106.Attacking += OnAttacking; + base.SubscribeEvents(); + + } + + protected override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Scp106.Stalking -= OnStalking; + Exiled.Events.Handlers.Scp106.Teleporting -= OnTP; + Exiled.Events.Handlers.Scp106.Attacking -= OnAttacking; + base.UnsubscribeEvents(); + } + + + private void OnAttacking(AttackingEventArgs ev) + { + if (!Check(ev.Player)) return; + + ev.IsAllowed = false; + } + + + + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/Guard/ChiefGuard.cs b/KruacentExiled/KE.CustomRoles/CR/Guard/ChiefGuard.cs new file mode 100644 index 00000000..bfa51549 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/Guard/ChiefGuard.cs @@ -0,0 +1,58 @@ +using Exiled.API.Enums; +using Exiled.API.Features.Attributes; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.Interfaces; +using PlayerRoles; +using System.Collections.Generic; +using UnityEngine; + +namespace KE.CustomRoles.CR.Guard +{ + public class ChiefGuard : KECustomRole, IColor + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Chief Guard", + [TranslationKeyDesc] = "you got a private card and a crossvec", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Chef des gardes", + [TranslationKeyDesc] = "T'as une carte de private \net un crossvec", + } + ,["legacy"] = new Dictionary() + { + [TranslationKeyName] = "Chef des gardes", + [TranslationKeyDesc] = "T'as une carte de private \net un crossvec", + } + }; + } + public override int MaxHealth { get; set; } = 100; + public override RoleTypeId Role { get; set; } = RoleTypeId.FacilityGuard; + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = false; + + public override float SpawnChance { get; set; } = 100; + + public override List Inventory { get; set; } = new List() + { + $"{ItemType.Radio}", + $"{ItemType.ArmorLight}", + $"{ItemType.GunCrossvec}", + $"{ItemType.Medkit}", + $"{ItemType.Flashlight}", + $"{ItemType.KeycardMTFPrivate}" + }; + + public override Dictionary Ammo { get; set; } = new Dictionary() + { + { AmmoType.Nato556, 120} + }; + + public Color32 Color => new Color32(112, 195, 255, 0); + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/Guard/Guard914.cs b/KruacentExiled/KE.CustomRoles/CR/Guard/Guard914.cs new file mode 100644 index 00000000..d5e6ec97 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/Guard/Guard914.cs @@ -0,0 +1,196 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Spawn; +using KE.CustomRoles.API.Features; +using LabApi.Events.Arguments.Scp914Events; +using LabApi.Features.Wrappers; +using PlayerRoles; +using System.Collections.Generic; +using Player = Exiled.API.Features.Player; +using Item = Exiled.API.Features.Items.Item; +using LabPlayer = LabApi.Features.Wrappers.Player; +using Interactables.Interobjects.DoorUtils; +using UnityEngine; +using InventorySystem; +using MEC; +using InventorySystem.Items; +using Exiled.API.Features.Items.Keycards; + +namespace KE.CustomRoles.CR.Guard +{ + public class Guard914 : KECustomRole + { + + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Guard of SCP-914", + [TranslationKeyDesc] = "You are The guard of SCP-914 \nYou start at SCP-914 \nbut someone tampered with your card\nand also fuck you", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Garde de SCP-914", + [TranslationKeyDesc] = "Tu es Le garde de SCP-914 \nTu commences à 914 \nmais on a traffiqué ta carte \net ntm aussi", + } + , + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "Garde de SCP-914", + [TranslationKeyDesc] = "Tu es Le garde de SCP-914 \nTu commences à 914 \nmais on a traffiqué ta carte \net ntm aussi", + } + }; + } + public override int MaxHealth { get; set; } = 100; + public override RoleTypeId Role { get; set; } = RoleTypeId.FacilityGuard; + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = false; + + public override SpawnProperties SpawnProperties { get; set; } = new SpawnProperties() + { + DynamicSpawnPoints = new List() + { + new DynamicSpawnPoint() + { + Location = Exiled.API.Enums.SpawnLocationType.Inside914, + Chance = 100, + } + } + }; + + public override float SpawnChance { get; set; } = 100; + + public override List Inventory { get; set; } = new List() + { + $"{ItemType.ArmorLight}", + $"{ItemType.Radio}", + $"{ItemType.GrenadeFlash}", + $"{ItemType.Medkit}", + $"{ItemType.GunFSP9}", + }; + public override Dictionary Ammo { get; set; } = new Dictionary() + { + { AmmoType.Nato9, 60} + }; + + public override void Init() + { + base.Init(); + storedSerials = new HashSet(); + } + + protected override void RoleAdded(Player player) + { + Timing.CallDelayed(.25f, delegate + { + CreateFakeGuardCard(player); + }); + base.RoleAdded(player); + } + + protected override void SubscribeEvents() + { + LabApi.Events.Handlers.Scp914Events.ProcessedPlayer += OnProcessedPlayer; + base.SubscribeEvents(); + } + + + protected override void UnsubscribeEvents() + { + LabApi.Events.Handlers.Scp914Events.ProcessedPlayer -= OnProcessedPlayer; + base.UnsubscribeEvents(); + } + + public static KeycardItem CreateFakeGuardCard(LabPlayer player) + { + Log.Debug("guard"); + KeycardItem item = KeycardItem.CreateCustomKeycardMetal( + player, "Guard Keycard?", "Ofc. " + player.Nickname, "SECURITY GAURD", new KeycardLevels(0, 0, 1), + new Color32(0, 0, 0, 255), new Color32(0, 0, 0, 255), new Color32(255, 255, 255, 255), + 1, ""); + + storedSerials.Add(item.Serial); + + return item; + } + + public static KeycardItem CreateFakeOperativeCard(LabPlayer player) + { + Log.Debug("operative"); + KeycardItem item = KeycardItem.CreateCustomKeycardTaskForce( + player, "MTF operative Keycard?", "Pvt. " + player.Nickname, new KeycardLevels(0, 0, 2), + new Color32(60, 100, 150, 255), new Color32(157, 136, 43, 255), + "", 1); + storedSerials.Add(item.Serial); + + return item; + + } + public static KeycardItem CreateFakeCaptainCard(LabPlayer player) + { + Log.Debug("captain"); + KeycardItem item = KeycardItem.CreateCustomKeycardTaskForce( + player, "MTF captain Keycard?", "Cpt. " + player.Nickname, new KeycardLevels(0, 0, 3), + new Color32(35, 50, 150, 255), new Color32(157, 136, 43, 255), + "", 2); + storedSerials.Add(item.Serial); + + return item; + } + + + + private static HashSet storedSerials; + private void OnProcessedPlayer(Scp914ProcessedPlayerEventArgs ev) + { + if (ev.KnobSetting != Scp914.Scp914KnobSetting.Fine) return; + Player player = ev.Player; + + if (player.CurrentItem is null) return; + + Item item = Item.Get(player.CurrentItem.Serial); + if (item is null) return; + + if (storedSerials.Contains(item.Serial)) + { + ItemBase itemBase = null; + bool equipped = player.CurrentItem != null && player.CurrentItem.Serial == item.Serial; + player.RemoveItem(item); + storedSerials.Remove(item.Serial); + + if (!(item is CustomKeycardItem keycard)) + { + Log.Error("not a custom keycard"); + return; + } + + + + switch (keycard.KeycardLevels.Admin) + { + case 1: + itemBase = CreateFakeOperativeCard(player).Base; + break; + case 2: + itemBase = CreateFakeCaptainCard(player).Base; + break; + case 3: + default: + itemBase = player.Inventory.ServerAddItem(ItemType.KeycardJanitor, ItemAddReason.Scp914Upgrade); + break; + } + if(equipped) + { + player.Inventory.ServerSelectItem(itemBase.ItemSerial); + } + + + } + + } + + + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/Guard/Introvert.cs b/KruacentExiled/KE.CustomRoles/CR/Guard/Introvert.cs new file mode 100644 index 00000000..09d11917 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/Guard/Introvert.cs @@ -0,0 +1,123 @@ +using CustomPlayerEffects; +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using Exiled.API.Features.Spawn; +using KE.CustomRoles.API.Features; +using MEC; +using PlayerRoles; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace KE.CustomRoles.CR.Guard +{ + internal class Introvert : KECustomRole + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Introvert", + [TranslationKeyDesc] = "your better by yourself", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Introverti", + [TranslationKeyDesc] = "Tu n'aimes pas trop les humains", + }, + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "Introvert", + [TranslationKeyDesc] = "Tu n'aimes pas trop les humains", + } + }; + } + public override int MaxHealth { get; set; } = 100; + public override RoleTypeId Role { get; set; } = RoleTypeId.FacilityGuard; + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = false; + + public override float SpawnChance { get; set; } = 100; + + public override List Inventory { get; set; } = new List() + { + $"{ItemType.KeycardGuard}", + $"{ItemType.GunFSP9}", + $"{ItemType.Medkit}", + $"{ItemType.GrenadeFlash}", + $"{ItemType.GrenadeFlash}", + $"{ItemType.ArmorLight}", + }; + + public override Dictionary Ammo { get; set; } = new Dictionary() + { + { AmmoType.Nato556, 60} + }; + + public override void Init() + { + _enabled = new Dictionary(); + base.Init(); + } + + + protected override void RoleAdded(Player player) + { + _enabled[player] = false; + //Timing.RunCoroutine(BuffAlone(player)); + base.RoleAdded(player); + } + + + protected override void RoleRemoved(Player player) + { + SyncBuff(player, false); + _enabled.Remove(player); + base.RoleRemoved(player); + } + + + private const byte MovementBoostIntensity = 5; + + private Dictionary _enabled; + + + private IEnumerator BuffAlone(Player player) + { + + while (Check(player)) + { + yield return Timing.WaitForSeconds(1f); + SyncBuff(player, player.CurrentRoom.Players.Count(p => p != player) == 0); + } + } + + private void SyncBuff(Player player,bool alone) + { + MovementBoost boost = player.GetEffect(); + + if (alone && !_enabled[player]) + { + Log.Debug("add buff to "+player.Nickname); + + boost.Intensity += 5; + _enabled[player] = true; + } + + + if (!alone && _enabled[player]) + { + Log.Debug("remove buff to " + player.Nickname); + boost.Intensity -= 5; + _enabled[player] = false; + } + + + } + + + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/Human/Alzheimer.cs b/KruacentExiled/KE.CustomRoles/CR/Human/Alzheimer.cs new file mode 100644 index 00000000..1e2b9c2b --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/Human/Alzheimer.cs @@ -0,0 +1,99 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using Exiled.API.Features.Items; +using Exiled.CustomRoles.API.Features; +using Exiled.Events.EventArgs.Player; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.Interfaces; +using KE.Utils.API.Translations.Events; +using KE.Utils.Extensions; +using MEC; +using PlayerRoles; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Pool; + +namespace KE.CustomRoles.CR.Human +{ + public class Alzheimer : GlobalCustomRole, IColor, IHealable + { + public override SideEnum Side { get; set; } = SideEnum.Human; + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Old man", + [TranslationKeyDesc] = "I'm old", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Vieux", + [TranslationKeyDesc] = "Je suis vieux", + }, + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "Vieux", + [TranslationKeyDesc] = "POV Mishima", + } + }; + } + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = false; + public override float SpawnChance { get; set; } = 100; + + public Color32 Color => new Color32(112,112,112,0); + + public HashSet HealItem => new HashSet() { ItemType.SCP500 }; + private Dictionary handles; + + protected override void SubscribeEvents() + { + handles = DictionaryPool.Get(); + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + base.UnsubscribeEvents(); + DictionaryPool.Release(handles); + } + + protected override void RoleAdded(Player player) + { + handles[player] = Timing.RunCoroutine(Teleport(player)); + } + + protected override void RoleRemoved(Player player) + { + if(handles.TryGetValue(player, out var handle)) + { + Timing.KillCoroutines(handle); + } + } + private IEnumerator Teleport(Player player) + { + while (Check(player)) + { + + yield return Timing.WaitForSeconds(UnityEngine.Random.Range(300f, 600f)); + EffectPlayer(player); + + } + } + + + + private void EffectPlayer(Player player) + { + if (player is null) return; + player.EnableEffect(EffectType.Flashed, 1, 5); + player.EnableEffect(EffectType.Invisible, 1, 6); + player.Teleport(player.Zone.RandomSafeRoom()); + } + + + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/Human/Asthmatique.cs b/KruacentExiled/KE.CustomRoles/CR/Human/Asthmatique.cs new file mode 100644 index 00000000..41878a42 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/Human/Asthmatique.cs @@ -0,0 +1,59 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using Exiled.CustomRoles.API.Features; +using Exiled.Events.EventArgs.Player; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.Interfaces; +using PlayerRoles; +using System.Collections.Generic; +using UnityEngine; +using Utils.NonAllocLINQ; + +namespace KE.CustomRoles.CR.Human +{ + public class Asthmatique : GlobalCustomRole, IColor, IHealable, IEffectImmunity + { + public override SideEnum Side { get; set; } = SideEnum.Human; + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Asthmatic", + [TranslationKeyDesc] = "Stamina halfed\nbut better accuracy", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Asthmatique", + [TranslationKeyDesc] = "T'as stamina est réduit de moitié\nMais tu vises mieux", + }, + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "Asthmatique", + [TranslationKeyDesc] = "T'as stamina est réduit de moitié\nMais tu vises mieux", + } + }; + } + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = false; + public override float SpawnChance { get; set; } = 100; + public Color32 Color => new Color32(191, 255, 0, 0); + public HashSet HealItem => new HashSet() { ItemType.SCP500 }; + + public HashSet ImmuneEffects => new HashSet() {EffectType.Poisoned}; + + protected override void RoleAdded(Player player) + { + player.EnableEffect(EffectType.Scp1853, -1, true); + player.EnableEffect(EffectType.Exhausted, -1, true); + } + + protected override void RoleRemoved(Player player) + { + player.DisableEffect(EffectType.Scp1853); + player.DisableEffect(EffectType.Exhausted); + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/Human/Crazy.cs b/KruacentExiled/KE.CustomRoles/CR/Human/Crazy.cs new file mode 100644 index 00000000..32aa4fb8 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/Human/Crazy.cs @@ -0,0 +1,164 @@ +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using Exiled.API.Features.Doors; +using Exiled.CustomRoles.API.Features; +using KE.CustomRoles.API.Features; +using KE.Utils.Extensions; +using MEC; +using NetworkManagerUtils.Dummies; +using PlayerRoles; +using PlayerStatsSystem; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace KE.CustomRoles.CR.Human +{ + /// + /// Behaviour of the crazy custom role, with the name and the weight (the higher the weight, the more it will happen) + /// + public enum CrazyBehaviour + { + Jump, + Shoot, + Vision, + Crazying + } + + internal class Crazy : GlobalCustomRole + { + private static CoroutineHandle _coroutines; + private static CoroutineHandle _crazyingCoroutine; + public override SideEnum Side { get; set; } = SideEnum.Human; + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Crazy", + [TranslationKeyDesc] = "Crazy? I Was Crazy Once. They Locked Me In A Room. A Rubber Room. A Rubber Room With Rats. And Rats Make Me Crazy", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Fou de la facilité", + [TranslationKeyDesc] = "Je pense que le traitement que t'as eu à la fondation t'as pas aidé", + }, + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "Fou de la facilité", + [TranslationKeyDesc] = "Je pense que le traitement que t'as eu à la fondation t'as pas aidé", + }, + }; + } + + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = false; + public override float SpawnChance { get; set; } = 100; + + private readonly Dictionary WeightDictionnary = new Dictionary() + { + { CrazyBehaviour.Jump, 3 }, + { CrazyBehaviour.Shoot, 3 }, + { CrazyBehaviour.Vision, 2 }, + { CrazyBehaviour.Crazying, 1 } + }; + private List WeightedList; + private float EFFECT_INTERVAL => UnityEngine.Random.Range(180, 300); + + protected override void RoleAdded(Player player) + { + PreparationEffect(); + _coroutines = Timing.RunCoroutine(ApplyEffect(player)); + } + + protected override void RoleRemoved(Player player) + { + Timing.KillCoroutines(_coroutines); + } + + private void PreparationEffect() + { + this.WeightedList = new List(); + + foreach (var cb in WeightDictionnary) + { + this.WeightedList.AddRange(Enumerable.Repeat(cb.Key, cb.Value)); + } + } + + private IEnumerator ApplyEffect(Player player) + { + while (Check(player)) + { + yield return Timing.WaitForSeconds(this.EFFECT_INTERVAL); + CrazyBehaviour behaviour = this.WeightedList.GetRandomValue(); + + switch (behaviour) + { + case CrazyBehaviour.Jump: + player.IsJumping = true; + break; + case CrazyBehaviour.Shoot: + Player.List.ToList().Where(p => p != player).ToList().ForEach(p => p.PlayGunSound(player.Position, FirearmType.FRMG0)); + player.PlayShieldBreakSound(); + break; + case CrazyBehaviour.Vision: + //Vision(player); + break; + case CrazyBehaviour.Crazying: + _crazyingCoroutine = Timing.RunCoroutine(Crazying(player)); + break; + default: + break; + } + } + } + + private void Vision(Player player) + { + List scpRole = new List { RoleTypeId.Scp3114, RoleTypeId.Scp173, RoleTypeId.Scp096, RoleTypeId.Scp106, RoleTypeId.Scp049, RoleTypeId.Scp939 }; + + DummyUtils.SpawnDummy(Player.List.ToList().Where(p => p.IsScp).First().DisplayNickname); // Setting name of the dummy + Player bot = Player.List.ToList().Where(p => p.IsNPC).First(); // Getting the bot + bot.Role.Set(RoleTypeId.Tutorial); + bot.ChangeAppearance(scpRole.GetRandomValue()); + bot.Health = 5000; + + bot.Position = player.Position + (-player.CameraTransform.forward) * 2; + player.PlayGunSound(bot.Position, FirearmType.ParticleDisruptor); + Timing.CallDelayed(2f, () => bot.Kick("")); + } + + private IEnumerator Crazying(Player player) + { + float duration = 15f; + float timer = 0f; + + while (timer < duration) + { + float randomYaw = UnityEngine.Random.Range(-180f, 180f); + float randomPitch = UnityEngine.Random.Range(-30f, 30f); + + Quaternion crazyRot = Quaternion.Euler(randomPitch, randomYaw, 0f); + player.Rotation = crazyRot; + + for (int i = 0; i < 5; i++) + { + float shakeYaw = UnityEngine.Random.Range(-5f, 5f); + float shakePitch = UnityEngine.Random.Range(-5f, 5f); + + Quaternion shakeRot = Quaternion.Euler(shakePitch, shakeYaw, 0f); + player.Rotation *= shakeRot; + + yield return Timing.WaitForSeconds(0.05f); + timer += 0.05f; + } + } + + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.CustomRoles/CR/Human/Curiosophile.cs b/KruacentExiled/KE.CustomRoles/CR/Human/Curiosophile.cs new file mode 100644 index 00000000..16ad6ec4 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/Human/Curiosophile.cs @@ -0,0 +1,147 @@ +/* +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using Exiled.CustomRoles.API.Features; +using Exiled.Events.EventArgs.Map; +using Exiled.Events.EventArgs.Player; +using KE.CustomRoles.API; +using MEC; +using PlayerRoles; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using Utils.NonAllocLINQ; + +namespace KE.CustomRoles.CR.Human +{ + internal class Curiosophile : GlobalCustomRole + { + public override SideEnum Side { get; set; } = SideEnum.Human; + public override string Name { get; set; } = "Curiosophile"; + public override string Description { get; set; } = "Tu veux récuperer tous ce que tu vois !"; + public override uint Id { get; set; } = 1059; + public override string CustomInfo { get; set; } = "Curiosophile"; + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = true; + public override float SpawnChance { get; set; } = 100; + + private readonly List LowQualityItem = + [ + ItemType.GunA7, + ItemType.GunCOM15, + ItemType.Flashlight, + ItemType.Coin, + ItemType.KeycardJanitor, + ItemType.KeycardGuard, + ]; + + private readonly List HighQualityItem = + [ + ItemType.MicroHID, + ItemType.Jailbird, + ItemType.GunFRMG0, + ItemType.ArmorHeavy, + ItemType.ParticleDisruptor, + ItemType.Adrenaline, + ItemType.SCP1344, + ItemType.SCP500, + ItemType.AntiSCP207, + ItemType.KeycardMTFCaptain, + ItemType.KeycardO5, + ItemType.KeycardFacilityManager, + ]; + + private readonly List BuffEffect = + [ + EffectType.Vitality, + EffectType.RainbowTaste, + EffectType.BodyshotReduction, + EffectType.AntiScp207 + ]; + + private readonly List NerfEffect = + [ + EffectType.Deafened, + EffectType.AmnesiaVision, + EffectType.InsufficientLighting, + EffectType.Stained, + EffectType.Blurred + ]; + + private Dictionary activeEffect = new Dictionary(); + + protected override void SubscribeEvents() + { + Exiled.Events.Handlers.Player.ItemAdded += PickupItem; + Exiled.Events.Handlers.Player.ItemRemoved += ThrowItem; + } + + protected override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Player.ItemAdded -= PickupItem; + Exiled.Events.Handlers.Player.ItemRemoved -= ThrowItem; + } + + public void PickupItem(ItemAddedEventArgs ev) + { + if (!Check(ev.Player)) return; + + CheckupInventory(ev.Player); + } + + public void ThrowItem(ItemRemovedEventArgs ev) + { + if (!Check(ev.Player)) return; + + CheckupInventory(ev.Player); + } + + private void CheckupInventory(Player p) + { + IEnumerable playerItems = p.Items.Select(item => item.Type); + + int buffedItemCount = playerItems.Intersect(LowQualityItem).Count(); + int nerfedItemCount = playerItems.Intersect(HighQualityItem).Count(); + + + + ApplyRandomBuffEffects(buffedItemCount, p); + ApplyRandomNerfEffects(nerfedItemCount, p); + } + + private void ApplyRandomBuffEffects(int buffedItemCount, Player p) + { + if (buffedItemCount > 0) + { + Log.Info("Buff Effect Applied"); + int effectsToApply = Mathf.Min(buffedItemCount, BuffEffect.Count); + + for (int i = 0; i < effectsToApply; i++) + { + var randomBuff = BuffEffect[UnityEngine.Random.Range(0, BuffEffect.Count)]; + p.EnableEffect(randomBuff, -1); + } + } + } + + private void ApplyRandomNerfEffects(int nerfedItemCount, Player p) + { + if (nerfedItemCount > 0) + { + Log.Info("Nerf Effect Applied"); + + int effectsToApply = Mathf.Min(nerfedItemCount, NerfEffect.Count); + + for (int i = 0; i < effectsToApply; i++) + { + var randomNerf = NerfEffect[UnityEngine.Random.Range(0, NerfEffect.Count)]; + p.EnableEffect(randomNerf, -1); + } + } + } + } +} +*/ \ No newline at end of file diff --git a/KruacentExiled/KE.CustomRoles/CR/Human/Diabetique.cs b/KruacentExiled/KE.CustomRoles/CR/Human/Diabetique.cs new file mode 100644 index 00000000..8a540db5 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/Human/Diabetique.cs @@ -0,0 +1,74 @@ +using CustomPlayerEffects; +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using Exiled.API.Features.Pickups; +using Exiled.CustomRoles.API.Features; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.Interfaces; +using MEC; +using PlayerRoles; +using System.Collections.Generic; +using UnityEngine; +using Utils.NonAllocLINQ; + +namespace KE.CustomRoles.CR.Human +{ + public class Diabetique : GlobalCustomRole, IColor, IHealable, IEffectImmunity + { + public override SideEnum Side { get; set; } = SideEnum.Human; + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Diabetic", + [TranslationKeyDesc] = "Fucking type 1. 1", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Diabétique", + [TranslationKeyDesc] = "T'as mangé le crambleu au pomme de mael", + }, + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "Diabetique", + [TranslationKeyDesc] = "T'as mangé le crambleu au pomme de mael", + } + + }; + } + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = false; + public override float SpawnChance { get; set; } = 100; + public HashSet HealItem => new HashSet() { ItemType.SCP500 }; + public Color32 Color => new Color32(255, 255, 0,0); + + public HashSet ImmuneEffects => new HashSet() { EffectType.Poisoned }; + + protected override void RoleAdded(Player player) + { + + Timing.CallDelayed(KECustomRole.TimeAttributingInventory, () => + { + if (player.IsInventoryFull) + { + Pickup.CreateAndSpawn(ItemType.Medkit, player.Position); + } + else + { + player.AddItem(ItemType.Medkit); + } + + }); + + + player.EnableEffect(EffectType.Scp207, -1, true); + } + protected override void RoleRemoved(Player player) + { + player.DisableEffect(EffectType.Scp207); + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/Human/Enderman.cs b/KruacentExiled/KE.CustomRoles/CR/Human/Enderman.cs new file mode 100644 index 00000000..0cc123b8 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/Human/Enderman.cs @@ -0,0 +1,48 @@ +using Exiled.API.Features.Attributes; +using Exiled.CustomRoles.API.Features; +using KE.CustomRoles.Abilities; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.Interfaces; +using PlayerRoles; +using System.Collections.Generic; +using UnityEngine; + +namespace KE.CustomRoles.CR.Human +{ + internal class Enderman : GlobalCustomRole, IColor + { + public override SideEnum Side { get; set; } = SideEnum.Human; + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Enderman", + [TranslationKeyDesc] = "Great job you're now overpowered", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Enderman", + [TranslationKeyDesc] = "Tu peux te téléporter ! T tro for enféte", + }, + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "Enderman", + [TranslationKeyDesc] = "Tu peux te téléporter ! T tro for enféte", + } + + }; + } + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = true; + public override float SpawnChance { get; set; } = 100; + public override HashSet Abilities { get; } = new HashSet() + { + "Teleportation", + "SetPosition" + }; + + public Color32 Color => new Color32(142, 37, 190, 0); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.CustomRoles/CR/Human/Hitman.cs b/KruacentExiled/KE.CustomRoles/CR/Human/Hitman.cs new file mode 100644 index 00000000..fbdd26d2 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/Human/Hitman.cs @@ -0,0 +1,242 @@ +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using Exiled.CustomRoles.API.Features; +using Exiled.Events.EventArgs.Player; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.Interfaces; +using MEC; +using PlayerRoles; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace KE.CustomRoles.CR.Human +{ + /* public class Hitman : GlobalCustomRole, IColor + { + private static CoroutineHandle _coroutines; + public override SideEnum Side { get; set; } = SideEnum.Human; + public override string Description { get; set; } = "pov sou hiyori de Your Turn To Die -Death Game By Majority-"; + public override string PublicName { get; set; } = string.Empty; + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = true; + public override float SpawnChance { get; set; } = 0; + + public Color32 Color => new Color32(54, 54, 54,0); + + private Player TargetPlayer; + private Player TheHitman; + + private void NerfHitman() + { + Log.Info($"Hitman Nerf Applied to {this.TheHitman.Nickname}"); + this.TheHitman.MaxHealth = 70; + this.TheHitman.Health -= 30; + + this.TheHitman.EnableEffect(EffectType.Slowness, -1,true); + } + + private void BuffHitman() + { + Log.Info($"Hitman Buff Applied to {this.TheHitman.Nickname}"); + this.TheHitman.MaxHealth = 130; + this.TheHitman.Health += 30; + + this.TheHitman.EnableEffect(EffectType.MovementBoost, -1, true); + } + + protected override void SetCustomInfo(Player player) + { + + } + + public override bool IsAvailable(Player player) + { + return Player.List.Count(p=> p != player && !p.IsScp) > 0; + } + + protected override void RoleAdded(Player player) + { + Log.Info("Role full name : " + player.Role.Name); + this.CustomInfo = player.Role.Name; + + this.TheHitman = player; + // Target cannot be NPC, SCP or the Hitman + this.TargetPlayer = Player.List.Where(x => x.IsHuman && x != player && !x.IsNPC).GetRandomValue(); + + if(TargetPlayer == null) + { + Log.Warn("no other human player"); + return; + } + + + + Log.Debug($"Target showing to Hitman, target is : {TargetPlayer.Nickname}"); + ShowEffectHint(player, $"The Target is {TargetPlayer.Nickname}"); + + if (this.TheHitman.Role.Side == this.TargetPlayer.Role.Side && !Server.FriendlyFire) + { + bool success = this.TheHitman.TryAddCustomRoleFriendlyFire(this.TargetPlayer.Role.Type.ToString(), this.TargetPlayer.Role.Type, 1); + Log.Info("Custom FF : " + success); + } + + _coroutines = Timing.RunCoroutine(CheckRooms()); + } + + protected override void RoleRemoved(Player player) + { + Timing.KillCoroutines(_coroutines); + } + + protected override void SubscribeEvents() + { + Exiled.Events.Handlers.Player.Escaped += TargetEscaped; + Exiled.Events.Handlers.Player.Died += TargetDie; + } + + protected override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Player.Escaped -= TargetEscaped; + Exiled.Events.Handlers.Player.Died -= TargetDie; + } + + public void TargetEscaped(EscapedEventArgs ev) + { + if (ev.Player != this.TargetPlayer) return; + + + Log.Info($"Target {this.TargetPlayer.Nickname} escaped"); + ShowEffectHint(TheHitman, $"The target ({this.TargetPlayer.Nickname}) has escaped, you lost the contract"); + + + NerfHitman(); + + this.TargetPlayer = null; + } + + public void TargetDie(DiedEventArgs ev) + { + if (ev.Player != this.TargetPlayer) return; + + Log.Info($"{ev.Attacker} has killed {ev.Player}"); + + // Hitman killed the target + if (ev.Player == this.TargetPlayer && ev.Attacker == this.TheHitman) + { + ShowEffectHint(TheHitman, $"You killed the target ({this.TargetPlayer.Nickname}), you achieved the contract"); + BuffHitman(); + + this.TargetPlayer = null; + } + else + { + ShowEffectHint(TheHitman, $"The target ({this.TargetPlayer.Nickname}) is dead, you lost the contract"); + NerfHitman(); + + this.TargetPlayer = null; + } + + if (this.TheHitman.Role.Side == this.TargetPlayer.Role.Side && !Server.FriendlyFire) + { + this.TheHitman.TryRemoveCustomeRoleFriendlyFire(this.TargetPlayer.Role.Type.ToString()); + } + } + + + + + const float CHECK_INTERVAL = 15f; + + private IEnumerator CheckRooms() + { + List<(int distance, string message, bool hasLogged)> proximityAlerts = new List<(int, string, bool)> + { + (0, "Sweat drips down your forehead...", false), + (3, "Your heart beats out of your chest...", false), + (5, "Your lungs tighten up...", false), + (999, "You feel dizzy...", false) + }; + + while (true) + { + if (this.TargetPlayer == null) + { + Log.Info("Target player is null. Stopping CheckRooms coroutine."); + yield break; + } + + yield return Timing.WaitForSeconds(CHECK_INTERVAL); + + for (int i = 0; i < proximityAlerts.Count; i++) + { + var (distance, message, hasLogged) = proximityAlerts[i]; + + if (distance == 0 && this.TheHitman.CurrentRoom == this.TargetPlayer.CurrentRoom) + { + if (!hasLogged) + { + Log.Info(this.TargetPlayer + " " + message); + + ShowEffectHint(TargetPlayer, message); + ResetLogs(ref proximityAlerts); + proximityAlerts[i] = (distance, message, true); + } + break; + } + else if (distance == 999 && this.TheHitman.Zone == this.TargetPlayer.Zone) + { + if (!hasLogged) + { + ShowEffectHint(TargetPlayer, message); + Log.Info(this.TargetPlayer + " " + message); + ResetLogs(ref proximityAlerts); + proximityAlerts[i] = (distance, message, true); + } + break; + } + else if (AreRoomsWithinDepth(this.TheHitman, this.TargetPlayer, distance)) + { + if (!hasLogged) + { + ShowEffectHint(TargetPlayer, message); + Log.Info(this.TargetPlayer + " " + message); + ResetLogs(ref proximityAlerts); + proximityAlerts[i] = (distance, message, true); + } + break; + } + } + } + } + + private bool AreRoomsWithinDepth(Player p1, Player p2, int depth) + { + HashSet nearbyRooms = GetNearbyRooms(p1, depth); + return nearbyRooms.Contains(p2.CurrentRoom); + } + + private HashSet GetNearbyRooms(Player p, int depth) + { + HashSet rooms = new HashSet(p.CurrentRoom.NearestRooms); + for (int i = 0; i < depth; i++) + { + HashSet newRooms = new HashSet(rooms.SelectMany(r => r.NearestRooms)); + rooms.UnionWith(newRooms); + } + return rooms; + } + + // Reinitialise tous logs sauf celui qui vient d'être actiév + private void ResetLogs(ref List<(int distance, string message, bool hasLogged)> proximityAlerts) + { + for (int i = 0; i < proximityAlerts.Count; i++) + { + var (distance, message, _) = proximityAlerts[i]; + proximityAlerts[i] = (distance, message, false); + } + } + }*/ +} diff --git a/KruacentExiled/KE.CustomRoles/CR/Human/MaladroitVoleur.cs b/KruacentExiled/KE.CustomRoles/CR/Human/MaladroitVoleur.cs new file mode 100644 index 00000000..1a8fc50d --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/Human/MaladroitVoleur.cs @@ -0,0 +1,102 @@ +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using Exiled.CustomRoles.API.Features; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.Interfaces; +using KE.Utils.API.Translations.Events; +using MEC; +using PlayerRoles; +using System; +using System.Collections.Generic; +using System.Drawing.Design; +using System.Linq; +using UnityEngine; +using UnityEngine.Pool; +using Utils.NonAllocLINQ; + +namespace KE.CustomRoles.CR.Human +{ + public class MaladroitVoleur : GlobalCustomRole, IColor + { + + public override SideEnum Side { get; set; } = SideEnum.Human; + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Butter Finger Thief", + [TranslationKeyDesc] = "Be careful of \"your\" items!", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Maladroit Voleur", + [TranslationKeyDesc] = "Fais attention à \"tes\" objets !", + }, + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "Maladroit Voleur", + [TranslationKeyDesc] = "Fais attention à \"tes\" objets !", + } + }; + } + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = false; + public override float SpawnChance { get; set; } = 100; + + public Color32 Color => new Color32(211, 110, 112, 0); + + public override HashSet Abilities { get; } = new HashSet() + { + "Thief" + }; + private Dictionary handles; + protected override void SubscribeEvents() + { + handles = DictionaryPool.Get(); + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + base.UnsubscribeEvents(); + DictionaryPool.Release(handles); + } + protected override void RoleAdded(Player player) + { + Timing.RunCoroutine(ThrowingItem(player)); + } + protected override void RoleRemoved(Player player) + { + if (handles.TryGetValue(player, out var handle)) + { + Timing.KillCoroutines(handle); + } + } + + public const float LowerBound = 90f; + public const float UpperBound = 120f; + + private IEnumerator ThrowingItem(Player player) + { + yield return Timing.WaitForSeconds(UnityEngine.Random.Range(LowerBound, UpperBound)); + while (Check(player)) + { + EffectPlayer(player); + yield return Timing.WaitForSeconds(UnityEngine.Random.Range(LowerBound, UpperBound)); + } + } + + + private void EffectPlayer(Player player) + { + if (UnityEngine.Random.Range(0f, 100f) > 50f) + { + player.DropHeldItem(); + } + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.CustomRoles/CR/Human/Pacifist.cs b/KruacentExiled/KE.CustomRoles/CR/Human/Pacifist.cs new file mode 100644 index 00000000..75ac3eaf --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/Human/Pacifist.cs @@ -0,0 +1,217 @@ +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Items; +using Exiled.API.Features.Pickups; +using Exiled.CustomItems.API.Features; +using Exiled.Events.EventArgs.Item; +using Exiled.Events.EventArgs.Player; +using KE.CustomRoles.API.Features; +using KE.Items.API.Features; +using KE.Utils.API.Features; +using MEC; +using PlayerRoles; +using System.Collections.Generic; + +namespace KE.CustomRoles.CR.Human +{ + public class Pacifist : KECustomRoleMultipleRole + { + public const string TranslationCantPickup = "PacifistCantPickup"; + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Pacifist", + [TranslationKeyDesc] = "You're incapable of violence.\nRemove when escaping and bring more people", + [TranslationCantPickup] = "Picking up this item make you sick", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Pacifiste", + [TranslationKeyDesc] = "T'es idées empêche quelconque violence.\nS'enlève quand tu t'échappes et ramène plus de renfort", + [TranslationCantPickup] = "Juste la vue de cet objet te rend malade", + }, + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "Pacifiste", + [TranslationKeyDesc] = "T'es idées empêche quelconque violence.\nS'enlève quand tu t'échappes et ramène plus de renfort", + }, + }; + } + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = false; + public override float SpawnChance { get; set; } = 100; + + public override HashSet Roles => new HashSet() { RoleTypeId.Scientist, RoleTypeId.ClassD }; + + + protected override void SubscribeEvents() + { + Exiled.Events.Handlers.Item.DisruptorFiring += OnDisruptorFiring; + Exiled.Events.Handlers.Item.Swinging += OnSwinging; + Exiled.Events.Handlers.Item.ChargingJailbird += OnChargingJailbird; + Exiled.Events.Handlers.Player.Shooting += OnShooting; + Exiled.Events.Handlers.Player.Escaping += OnEscaping; + Exiled.Events.Handlers.Player.PickingUpItem += OnPickingUpItem; + Exiled.Events.Handlers.Player.ItemAdded += OnItemAdded; + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Item.DisruptorFiring -= OnDisruptorFiring; + Exiled.Events.Handlers.Item.Swinging -= OnSwinging; + Exiled.Events.Handlers.Item.ChargingJailbird -= OnChargingJailbird; + Exiled.Events.Handlers.Player.Shooting -= OnShooting; + Exiled.Events.Handlers.Player.Escaping -= OnEscaping; + Exiled.Events.Handlers.Player.PickingUpItem -= OnPickingUpItem; + Exiled.Events.Handlers.Player.ItemAdded -= OnItemAdded; + base.UnsubscribeEvents(); + } + + private void OnItemAdded(ItemAddedEventArgs ev) + { + Player player = ev.Player; + if (!Check(player)) return; + Item item = ev.Item; + + if (IsViolent(item)) + { + ShowEffectHint(player, GetTranslation(player, TranslationCantPickup)); + Timing.CallDelayed(.01f, () => + { + player.DropItem(item); + }); + } + } + + + private void OnPickingUpItem(PickingUpItemEventArgs ev) + { + Player player = ev.Player; + if (!ev.IsAllowed) return; + if (!Check(player)) return; + + bool isViolent = IsViolent(ev.Pickup); + ev.IsAllowed = !isViolent; + + KELog.Debug("violent " + isViolent); + if (isViolent) + { + ShowEffectHint(player, GetTranslation(player, TranslationCantPickup)); + } + } + + private bool IsViolent(Pickup pickup) + { + bool result = IsViolent(pickup.Type); + if (KECustomItem.TryGet(pickup, out CustomItem ci)) + { + if (ci is KECustomItem kecr) + { + result = KECustomItem.IsConsideredViolent(kecr); + } + + } + return result; + } + + private bool IsViolent(Item item) + { + bool result = IsViolent(item.Type); + if (KECustomItem.TryGet(item, out CustomItem ci)) + { + if (ci is KECustomItem kecr) + { + result = KECustomItem.IsConsideredViolent(kecr); + } + + } + return result; + } + + private bool IsViolent(ItemType itemtype) + { + if (itemtype.IsWeapon()) + { + return true; + } + + if (itemtype.GetCategory() == ItemCategory.Firearm) + { + return true; + } + + ProjectileType projectileType = itemtype.GetProjectileType(); + if (projectileType == ProjectileType.FragGrenade || projectileType == ProjectileType.Scp018) + { + return true; + } + return false; + } + + + private void OnDisruptorFiring(DisruptorFiringEventArgs ev) + { + if (Check(ev.Attacker)) + { + ev.IsAllowed = false; + } + + } + private void OnSwinging(SwingingEventArgs ev) + { + if (Check(ev.Player)) + { + ev.IsAllowed = false; + } + } + private void OnChargingJailbird(ChargingJailbirdEventArgs ev) + { + if (Check(ev.Player)) + { + ev.IsAllowed = false; + } + } + + private void OnShooting(ShootingEventArgs ev) + { + if (Check(ev.Player)) + { + ev.IsAllowed = false; + } + } + + private void OnEscaping(EscapingEventArgs ev) + { + Player escape = ev.Player; + if (Check(escape) && ev.IsAllowed) + { + RemoveRole(escape); + + Player respawned = Player.Enumerable.GetRandomValue(p => p.IsDead); + + + if (respawned != null) + { + respawned.Role.Set(ev.NewRole, Exiled.API.Enums.SpawnReason.Respawn, RoleSpawnFlags.All); + GiveRandomRole(respawned); + + Timing.CallDelayed(1f, () => + { + KELog.Debug(respawned); + }); + } + + + + } + } + + + + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/MTF/Pilot.cs b/KruacentExiled/KE.CustomRoles/CR/MTF/Pilot.cs new file mode 100644 index 00000000..5eb62445 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/MTF/Pilot.cs @@ -0,0 +1,65 @@ +using Exiled.API.Enums; +using Exiled.API.Features.Attributes; +using Exiled.CustomRoles.API.Features; +using KE.CustomRoles.Abilities; +using KE.CustomRoles.API.Features; +using PlayerRoles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.CR.MTF +{ + public class Pilot : KECustomRole + { + public override int MaxHealth { get; set; } = 90; + public override RoleTypeId Role { get; set; } = RoleTypeId.NtfPrivate; + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = false; + + public override float SpawnChance { get; set; } = 100; + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Pilot", + [TranslationKeyDesc] = "So I haveth a Laser Pointere", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Pilote", + [TranslationKeyDesc] = "Je suis pilote!", + }, + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "Pilot", + [TranslationKeyDesc] = "So I haveth a Laser Pointere", + }, + }; + } + + public override List Inventory { get; set; } = new List() + { + $"{ItemType.GunCrossvec}", + $"{ItemType.KeycardMTFOperative}", + $"{ItemType.Radio}", + $"{ItemType.ArmorCombat}", + $"{ItemType.Medkit}" + }; + + public override Dictionary Ammo { get; set; } = new Dictionary() + { + { AmmoType.Nato9, 100} + }; + + public override HashSet Abilities { get; } = new HashSet() + { + "SetPosition", + "AirStrike" + }; + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/MTF/RedMist/EGO.cs b/KruacentExiled/KE.CustomRoles/CR/MTF/RedMist/EGO.cs new file mode 100644 index 00000000..d10083c9 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/MTF/RedMist/EGO.cs @@ -0,0 +1,181 @@ +using CustomPlayerEffects; +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.DamageHandlers; +using Exiled.Events.EventArgs.Player; +using KE.CustomRoles.API.Features.Abilities; +using KE.Utils.API.Features; +using KE.Utils.API.Interfaces; +using KE.Utils.Extensions; +using PlayerRoles; +using PlayerStatsSystem; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.CustomRoles.CR.MTF.RedMist +{ + public class EGO : BaseCompAbility, IUsingEvents + { + + + public override bool Active + { + get + { + return active; + } + } + + private bool active; + + + public ReferenceHub Hub { get; private set; } + + private const float Damage = 2; + + private CustomReasonDamageHandler damage; + + internal void Awake() + { + Hub = ReferenceHub.GetHub(base.gameObject); + active = false; + + damage = new CustomReasonDamageHandler("drained", Damage); + + SubscribeEvents(); + } + + + internal void OnDestroy() + { + UnsubscribeEvents(); + } + + public void SubscribeEvents() + { + Exiled.Events.Handlers.Player.ReceivingEffect += OnReceivingEffect; + Exiled.Events.Handlers.Player.Hurt += OnHurt; + } + private void OnHurt(HurtEventArgs ev) + { + Player player = Player.Get(Hub); + if (player != ev.Attacker) return; + + if (Active) + { + IncreaseObjective(); + } + } + + public void UnsubscribeEvents() + { + Exiled.Events.Handlers.Player.ReceivingEffect -= OnReceivingEffect; + Exiled.Events.Handlers.Player.Hurt -= OnHurt; + } + + private void OnReceivingEffect(ReceivingEffectEventArgs ev) + { + Player player = Player.Get(Hub); + if (ev.Player != player) return; + StatusEffectBase effect = ev.Effect; + + if (Active && effect is Poisoned) + { + effect.DisableEffect(); + } + } + + + private float cooldown = 0; + private float objective = .2f; + private float baseObjective = .2f; + + private void UpdateDamage() + { + if (Hub is null) + { + KELog.Debug("null"); + return; + } + + if (!Hub.IsAlive()) + { + KELog.Debug(Hub?.nicknameSync.DisplayName + " is dead"); + Destroy(this); + return; + } + + if (Active) + { + cooldown += Time.deltaTime; + KELog.Debug(cooldown); + if (cooldown >= objective) + { + Hub.playerStats.DealDamage(damage); + cooldown = 0; + objective = baseObjective; + } + } + + + } + + public void IncreaseObjective() + { + objective += baseObjective*10; + } + + + public void Update() + { + UpdateDamage(); + } + + + public override void ToggleActive() + { + active = !Active; + KELog.Debug("toggle now active? "+ Active); + Effect(); + } + + + private static readonly Dictionary effects = new Dictionary() + { + { EffectType.MovementBoost,25 }, + { EffectType.Scp1853,2 }, + }; + + public const byte MovementBoostIntensity = 25; + public const byte SCP1853Intensity = 2; + private void Effect() + { + Player player = Player.Get(Hub); + + if (Active) + { + foreach(var kvp in effects) + { + player.AddLevelEffect(kvp.Key, kvp.Value); + } + if (player.IsEffectActive()) + { + player.DisableEffect(); + } + } + else + { + foreach (var kvp in effects) + { + player.AddLevelEffect(kvp.Key, -kvp.Value); + } + } + + + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/MTF/RedMist/RedMist.cs b/KruacentExiled/KE.CustomRoles/CR/MTF/RedMist/RedMist.cs new file mode 100644 index 00000000..cf0e38e3 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/MTF/RedMist/RedMist.cs @@ -0,0 +1,123 @@ +using Exiled.API.Features; +using Exiled.API.Features.Items; +using Exiled.Events.EventArgs.Player; +using Exiled.Events.EventArgs.Scp1509; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.Interfaces; +using PlayerRoles; +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace KE.CustomRoles.CR.MTF.RedMist +{ + public class RedMistRole : KECustomRole, IColor + { + public override string InternalName => "RedMist"; + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "The Red Mist", + [TranslationKeyDesc] = "todo", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "The Red Mist", + [TranslationKeyDesc] = "todo", + }, + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "The Red Mist", + [TranslationKeyDesc] = "todo", + } + }; + } + public override int MaxHealth { get; set; } = 200; + public override RoleTypeId Role { get; set; } = RoleTypeId.ClassD; + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = false; + public Color32 Color => new Color32(255, 192, 203, 0); + public override float SpawnChance { get; set; } = 0; + + //You're the legendary Fixer, the Red Mist. This role comes with multiple strengths such as increased speed (1.5 to 2 times the normal speed ) , more health (200 health), you spawn with a machete scp but better, The Mimicry also you can't use guns. + //The Mimicry is a weapon that deals 100 damage per normal attacks and instead of inspecting you do a devastating vertical slash (30sec cd) doing 400 damage. + //You have 2 abilities one is to manifest your E.G.O, an armor that boosts your abilities but drains your health over time if you don't do damage (you have life steal). + //The second one is only useable when you manifested your E.G.O, and horizontal slash doing 600 damage and hitting everyone in the room (60sec cd) + //If you let people on your team die you get weaker, you are a protector afterall. + + //faster, 200hp, machete mais 200 hp de dégats (75 pour les humains) + //+ego : quick heal drain pause when attacking, 80 damage reduction faster + //forward slash : damage everything on its path max distance of a room + + public override HashSet Abilities { get; } = new HashSet() + { + "ToggleEGO", + "Spear", + "OnRush", + "GreaterSplitHorizontal", + }; + + protected override void GiveInventory(Player player) + { + try + { + + //ci + Item i = player.AddItem(ItemType.SCP1509); + + } + catch (Exception e) + { + Log.Error(e); + } + + + } + + protected override void RoleAdded(Player player) + { + if (!player.ReferenceHub.TryGetComponent(out _)) + { + Log.Debug("adding comp"); + player.ReferenceHub.gameObject.AddComponent(); + } + base.RoleAdded(player); + } + + protected override void RoleRemoved(Player player) + { + if (player.ReferenceHub.gameObject.TryGetComponent(out var ego)) + { + UnityEngine.Object.Destroy(ego); + } + base.RoleRemoved(player); + } + + protected override void SubscribeEvents() + { + Exiled.Events.Handlers.Scp1509.Resurrecting += OnResurrecting; + + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Scp1509.Resurrecting -= OnResurrecting; + base.UnsubscribeEvents(); + } + + private void OnResurrecting(ResurrectingEventArgs ev) + { + Player player = ev.Player; + if (!Check(player)) return; + + ev.IsAllowed = false; + } + + + + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/MTF/Tank.cs b/KruacentExiled/KE.CustomRoles/CR/MTF/Tank.cs new file mode 100644 index 00000000..85d56f1d --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/MTF/Tank.cs @@ -0,0 +1,103 @@ +using Exiled.API.Enums; +using Exiled.API.Features.Attributes; +using Exiled.Events.EventArgs.Player; +using MEC; +using PlayerRoles; +using System.Collections.Generic; +using UnityEngine; +using System; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.Interfaces; +using Exiled.API.Features; + +namespace KE.CustomRoles.CR.MTF +{ + public class Tank : KECustomRole, IColor + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Tank", + [TranslationKeyDesc] = "The more bullet you got, the slower you are", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Tank", + [TranslationKeyDesc] = "Tu es ralenti selon ton nombre de balle", + }, + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "Tank", + [TranslationKeyDesc] = "Tu es débuff mais ta force de tir est démultiplié (fais attention à tes balles)", + } + }; + } + + public override int MaxHealth { get; set; } = 200; + public override RoleTypeId Role { get; set; } = RoleTypeId.NtfSergeant; + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = false; + public Color32 Color => new Color32(255, 192, 203,0); + public override float SpawnChance { get; set; } = 100; + public override Vector3 Scale { get; set; } = new Vector3(1.15f, 1f, 1.15f); + + public override List Inventory { get; set; } = new List() + { + $"{ItemType.GunLogicer}", + $"{ItemType.GunFRMG0}", + $"{ItemType.Radio}", + $"{ItemType.GrenadeHE}", + $"{ItemType.Adrenaline}", + $"{ItemType.KeycardMTFCaptain}", + $"{ItemType.Painkillers}", + $"{ItemType.ArmorHeavy}" + }; + + public override Dictionary Ammo { get; set; } = new Dictionary() + { + { AmmoType.Nato762, 200}, + { AmmoType.Nato556, 200} + }; + + + + protected override void SubscribeEvents() + { + Exiled.Events.Handlers.Player.Shooting += Shooting; + base.SubscribeEvents(); + } + + /// + protected override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Player.Shooting -= Shooting; + base.UnsubscribeEvents(); + } + + private void Shooting(ShootingEventArgs ev) + { + if (!Check(ev.Player)) return; + + Timing.CallDelayed(0.5f, () => + { + EffectAttribution(ev.Player); + }); + } + + private void EffectAttribution(Player player) + { + int nbMunition = player.GetAmmo(AmmoType.Nato762) / 100; + byte nbMunitionByte = (byte)Mathf.Clamp(nbMunition,byte.MinValue,byte.MaxValue); + + if (UnityEngine.Random.Range(0, 1) > 0.5f) + { + player.DisableEffect(EffectType.Slowness); + player.EnableEffect(EffectType.Slowness, nbMunitionByte, 99999, false); + } + + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/MTF/Terroriste/Terroriste.cs b/KruacentExiled/KE.CustomRoles/CR/MTF/Terroriste/Terroriste.cs new file mode 100644 index 00000000..d596b2ec --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/MTF/Terroriste/Terroriste.cs @@ -0,0 +1,81 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.Interfaces; +using PlayerRoles; +using System.Collections.Generic; +using UnityEngine; + +namespace KE.CustomRoles.CR.MTF.Terroriste +{ + public class Terroriste : KECustomRole, IColor + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Terrorist", + [TranslationKeyDesc] = "Kaboom!", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Terroriste", + [TranslationKeyDesc] = "Ne fait pas exploser la facilité \ntu commences avec des grenades", + }, + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "Terroriste", + [TranslationKeyDesc] = "Ne fait pas exploser la facilité \ntu commences avec des grenades", + }, + }; + } + public override int MaxHealth { get; set; } = 100; + public override RoleTypeId Role { get; set; } = RoleTypeId.NtfSergeant; + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = false; + public Color32 Color => new Color32(105, 52, 22, 0); + + public override float SpawnChance { get; set; } = 100; + public override List Inventory { get; set; } = new List() + { + $"{ItemType.GrenadeHE}", + $"{ItemType.GrenadeHE}", + $"{ItemType.ArmorCombat}", + $"{ItemType.GunE11SR}", + $"{ItemType.Adrenaline}", + $"{ItemType.KeycardMTFOperative}", + $"{ItemType.Radio}" + }; + + public override Dictionary Ammo { get; set; } = new Dictionary() + { + { AmmoType.Nato556, 100} + }; + + public override HashSet Abilities { get; } = new HashSet() + { + "Explode" + }; + + + protected override void RoleAdded(Player player) + { + player.GameObject.AddComponent(); + + + base.RoleAdded(player); + } + + protected override void RoleRemoved(Player player) + { + if(player.GameObject.TryGetComponent(out var comp)) + { + Object.Destroy(comp); + } + base.RoleRemoved(player); + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/MTF/Terroriste/TerroristeLight.cs b/KruacentExiled/KE.CustomRoles/CR/MTF/Terroriste/TerroristeLight.cs new file mode 100644 index 00000000..9cb179db --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/MTF/Terroriste/TerroristeLight.cs @@ -0,0 +1,95 @@ +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Toys; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using Light = Exiled.API.Features.Toys.Light; + +namespace KE.CustomRoles.CR.MTF.Terroriste +{ + internal class TerroristeLight : MonoBehaviour + { + private ReferenceHub _hub; + private Light light; + private Primitive prim; + + private void Start() + { + _hub = ReferenceHub.GetHub(base.gameObject); + CreateLight(); + } + + private void CreateLight() + { + light = Light.Create(null, null, null, false); + light.Transform.parent = _hub.transform; + light.Transform.localPosition = Vector3.down /3f + Vector3.back/4f; + + + light.Color = Color.red; + light.LightType = LightType.Spot; + light.Intensity = intensity; + light.Transform.localRotation = Quaternion.LookRotation(-_hub.transform.forward); + light.SpotAngle = 111; + light.MovementSmoothing = 0; + light.Spawn(); + + //prim = Primitive.Create(null, null, null, false); + //prim.Transform.parent = _hub.transform; + //prim.Transform.localPosition = Vector3.down / 3f + Vector3.back / 5f; + //prim.Transform.localScale = new(.5f, 1, .5f); + //prim.Type = PrimitiveType.Cube; + //prim.MovementSmoothing = 0; + //prim.Spawn(); + } + + + private void OnDestroy() + { + light.Destroy(); + prim?.Destroy(); + } + + + + private int timer = 0; + private const int objective = 100; + + + private float intensity = 2; + + public void Update() + { + + if(timer >= objective) + { + ToggleLight(); + timer = 0; + } + timer++; + } + + + public void ToggleLight() + { + if(light.Intensity == 0) + { + light.Intensity = intensity; + } + else + { + light.Intensity = 0; + } + + + } + + + + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/SCP/Paper.cs b/KruacentExiled/KE.CustomRoles/CR/SCP/Paper.cs new file mode 100644 index 00000000..8818ce02 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/SCP/Paper.cs @@ -0,0 +1,39 @@ +using Exiled.API.Features.Attributes; +using KE.CustomRoles.API.Features; +using PlayerRoles; +using System.Collections.Generic; +using UnityEngine; + +namespace KE.CustomRoles.CR.SCP +{ + public class Paper : GlobalCustomRole + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Paper", + [TranslationKeyDesc] = "uh oh. paper jam", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Paper", + [TranslationKeyDesc] = "uh oh. paper jam", + }, + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "Paper", + [TranslationKeyDesc] = "uh oh. paper jam", + }, + }; + } + public override SideEnum Side { get; set; } = SideEnum.SCP; + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = false; + + public override float SpawnChance { get; set; } = 100; + public override Vector3 Scale { get; set; } = new Vector3(0.3f, 1, 1); + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/SCP/SCP173/Frozen.cs b/KruacentExiled/KE.CustomRoles/CR/SCP/SCP173/Frozen.cs new file mode 100644 index 00000000..59df478d --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/SCP/SCP173/Frozen.cs @@ -0,0 +1,132 @@ +using Exiled.API.Features.Items; +using InventorySystem.Items.Usables.Scp244; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.Interfaces; +using LabApi.Events.Arguments.PlayerEvents; +using LabApi.Events.Arguments.Scp173Events; +using LabApi.Features.Wrappers; +using MEC; +using PlayerRoles; +using PlayerStatsSystem; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using Item = Exiled.API.Features.Items.Item; +using Player = Exiled.API.Features.Player; +using Scp244Pickup = Exiled.API.Features.Pickups.Scp244Pickup; + +namespace KE.CustomRoles.CR.SCP.SCP173 +{ + public class Frozen : KECustomRole, IColor + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Frozen SCP-173", + [TranslationKeyDesc] = "Instead of Tantrum you drop a SCP-244.\nKilling anyone with hypothermia gives Hume Shield", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "SCP-173 Glacé", + [TranslationKeyDesc] = "Au lieu de chier tu poses un SCP-244.\nTué quelqu'un qui est en hypothermie donne du shield (dans le jeu hein)", + }, + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "Frozen SCP-173", + [TranslationKeyDesc] = "Instead of Tantrum you drop a SCP-244.\nKilling anyone with hypothermia gives Hume Shield", + }, + }; + } + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = false; + public override float SpawnChance { get; set; } = 100; + public override int MaxHealth { get; set; } = 4500; + + public override RoleTypeId Role => RoleTypeId.Scp173; + + public Color32 Color => new Color32(120, 205, 255, 0); + + + protected override void SubscribeEvents() + { + + LabApi.Events.Handlers.Scp173Events.CreatedTantrum += OnCreatedTantrum; + LabApi.Events.Handlers.PlayerEvents.Death += OnDeath; + base.SubscribeEvents(); + } + + + + protected override void UnsubscribeEvents() + { + LabApi.Events.Handlers.Scp173Events.CreatedTantrum -= OnCreatedTantrum; + LabApi.Events.Handlers.PlayerEvents.Death -= OnDeath; + base.UnsubscribeEvents(); + } + + + private void OnDeath(PlayerDeathEventArgs ev) + { + if (ev.Player == null || ev.Player.ReferenceHub == null) return; + if (ev.Attacker == null || ev.Attacker.ReferenceHub == null) return; + Player player = Player.Get(ev.Player); + + if (!Check(ev.Attacker)) return; + + + if (ev.DamageHandler is ScpDamageHandler scpDamageHandler) + { + Player attacker = Player.Get(scpDamageHandler.Attacker.Hub); + if (Check(attacker)) + { + + Vector3 oldpos = ev.OldPosition; + + bool isInAPrimed = _primed.Any(pickup => pickup.FogPercentForPoint(oldpos) > 0); + + if (isInAPrimed) + { + attacker.HumeShield = Mathf.Min(attacker.MaxHumeShield, attacker.HumeShield + 400f); + } + + } + + } + } + + private HashSet _primed = new HashSet(); + + + private void OnCreatedTantrum(Scp173CreatedTantrumEventArgs ev) + { + if (!Check(ev.Player)) return; + + TantrumHazard tantrum = ev.Tantrum; + float time = tantrum.LiveDuration; + + + Vector3 position = tantrum.SyncedPosition + Vector3.up; + tantrum.Destroy(); + + Scp244 scp244 = (Scp244)Item.Create(ItemType.SCP244a); + scp244.Primed = true; + Scp244Pickup scp244Pickup = (Scp244Pickup)scp244.CreatePickup(position); + + + _primed.Add(scp244Pickup.Base); + + Timing.CallDelayed(time, () => + { + scp244Pickup.Destroy(); + _primed.Remove(scp244Pickup.Base); + }); + + + } + + + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/SCP/SCP939/Ultra.cs b/KruacentExiled/KE.CustomRoles/CR/SCP/SCP939/Ultra.cs new file mode 100644 index 00000000..442204f7 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/SCP/SCP939/Ultra.cs @@ -0,0 +1,84 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using HintServiceMeow.Core.Models.Arguments; +using KE.CustomRoles.API.Core.Positions; +using KE.CustomRoles.API.Features; +using KE.Utils.API.Displays.DisplayMeow; +using KE.Utils.API.Displays.DisplayMeow.Placements; +using MEC; +using PlayerRoles; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace KE.CustomRoles.CR.SCP.SCP939 +{ + public class Ultra : KECustomRole + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Ultra SCP-939", + [TranslationKeyDesc] = "You can sense where people are located", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Ultra SCP-939", + [TranslationKeyDesc] = "Tu sais où est tout le monde", + }, + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "Ultra SCP-939", + [TranslationKeyDesc] = "You can sense where people are located", + }, + }; + } + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = false; + public override float SpawnChance { get; set; } = 100; + public override int MaxHealth { get; set; } = 2700; + + public override RoleTypeId Role => RoleTypeId.Scp939; + + public const float RefreshRate = 20; + public const int SizeText = 20; + + public static readonly HintPosition UltraPosition = new UltraPosition(); + protected override void RoleAdded(Player player) + { + Timing.CallDelayed(1f, () => + { + DisplayHandler.Instance.CreateAuto(player, (arg) => PlayerInZone(arg), UltraPosition.HintPlacement); + }); + } + private string PlayerInZone(AutoContentUpdateArg arg) + { + + if (!TrackedPlayers.Contains(Player.Get(arg.PlayerDisplay.ReferenceHub))) + { + return string.Empty; + } + + string result = $""; + int nbPlayer; + foreach (ZoneType zone in Enum.GetValues(typeof(ZoneType))) + { + nbPlayer = GetPlayerInZone(zone); + if (nbPlayer > 0 || MainPlugin.Instance.Config.Debug) + { + result += zone.ToString() + " : " + nbPlayer + "\n"; + } + } + result += ""; + return result; + } + + private int GetPlayerInZone(ZoneType zone) + { + return Player.List.Count(p => !p.IsScp && p.Zone == zone); + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/SCP/Small.cs b/KruacentExiled/KE.CustomRoles/CR/SCP/Small.cs new file mode 100644 index 00000000..6fff5fc8 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/SCP/Small.cs @@ -0,0 +1,40 @@ +using Exiled.API.Features.Attributes; +using Exiled.CustomRoles.API.Features; +using KE.CustomRoles.API.Features; +using PlayerRoles; +using System.Collections.Generic; +using UnityEngine; + +namespace KE.CustomRoles.CR.SCP +{ + public class Small : GlobalCustomRole + { + public override SideEnum Side { get; set; } = SideEnum.SCP; + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Small", + [TranslationKeyDesc] = "u smoll", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Petit", + [TranslationKeyDesc] = "t poti", + }, + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "Small", + [TranslationKeyDesc] = "u smoll", + }, + }; + } + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = false; + + public override float SpawnChance { get; set; } = 100; + public override Vector3 Scale { get; set; } = new Vector3(1, .75f, 1); + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/Scientist/GambleAddict.cs b/KruacentExiled/KE.CustomRoles/CR/Scientist/GambleAddict.cs new file mode 100644 index 00000000..8d803fc2 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/Scientist/GambleAddict.cs @@ -0,0 +1,56 @@ +using Exiled.API.Features.Attributes; +using Exiled.CustomRoles.API.Features; +using KE.CustomRoles.Abilities; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.Interfaces; +using PlayerRoles; +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace KE.CustomRoles.CR.Scientist +{ + public class GambleAddict : KECustomRole, IColor + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Gamble Addict", + [TranslationKeyDesc] = "you got 2 coins\ngood luck", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Accro du casino", + [TranslationKeyDesc] = "T'as trade ton kit et ta carte contre 2 pièces \nfais en bon usage", + }, + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "Gamble Addict", + [TranslationKeyDesc] = "T'as trade ton kit et ta carte contre 2 pièces \nfais en bon usage", + }, + }; + } + public override int MaxHealth { get; set; } = 100; + public override RoleTypeId Role { get; set; } = RoleTypeId.Scientist; + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = false; + + public override float SpawnChance { get; set; } = 100; + + public override List Inventory { get; set; } = new List() + { + $"{ItemType.Coin}", + $"{ItemType.Coin}", + }; + + public override HashSet Abilities { get; } = new HashSet() + { + "Trade" + }; + + public Color32 Color => new Color32(0, 105, 59,0); + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/Scientist/ZoneManager.cs b/KruacentExiled/KE.CustomRoles/CR/Scientist/ZoneManager.cs new file mode 100644 index 00000000..96a585c7 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/Scientist/ZoneManager.cs @@ -0,0 +1,160 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using Exiled.API.Features.Doors; +using Exiled.API.Features.Items; +using Exiled.API.Features.Spawn; +using Exiled.Events.EventArgs.Player; +using InventorySystem.Items.Keycards; +using KE.CustomRoles.API.Features; +using PlayerRoles; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace KE.CustomRoles.CR.Scientist +{ + public class ZoneManager : KECustomRole + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Zone Manager", + [TranslationKeyDesc] = "Open all of the checkpoint to get a better card!", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Zone Manager", + [TranslationKeyDesc] = "Incroyable tu peux avoir une promotion alors fais ton boulot et ouvre tous ces checkpoints et tu pourras sortir d'ici!", + }, + ["legacy"] = new Dictionary() + { + [TranslationKeyName] = "Zone Manager", + [TranslationKeyDesc] = "Incroyable tu peux avoir une promotion alors fais ton boulot et ouvre tous ces checkpoints et tu pourras sortir d'ici!", + } + }; + } + public override int MaxHealth { get; set; } = 100; + public override RoleTypeId Role { get; set; } = RoleTypeId.Scientist; + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = false; + public Color32 Color => new Color32(4, 52, 50, 0); + + + public override float SpawnChance { get; set; } = 100; + + public override SpawnProperties SpawnProperties { get; set; } = new SpawnProperties() + { + DynamicSpawnPoints = new List() + { + new DynamicSpawnPoint() + { + Location = SpawnLocationType.Inside127Lab, + Chance = 100, + } + } + }; + + + public override HashSet Abilities { get; } = new HashSet() + { + "NumberCheckpoints" + }; + + public override List Inventory { get; set; } = new List() + { + $"{ItemType.Medkit}", + $"{ItemType.Adrenaline}", + $"{ItemType.KeycardZoneManager}" + }; + public static readonly IReadOnlyCollection DoorToOpen = new List() + { + DoorType.CheckpointLczA, + DoorType.CheckpointLczB, + DoorType.CheckpointEzHczA, + DoorType.CheckpointEzHczB + }.AsReadOnly(); + + + private static Dictionary> objectives = new Dictionary>(); + private static Dictionary flag = new Dictionary(); + + protected override void SubscribeEvents() + { + + Exiled.Events.Handlers.Player.InteractingDoor += OnInteractingDoor; + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Player.InteractingDoor -= OnInteractingDoor; + base.UnsubscribeEvents(); + } + + protected override void RoleAdded(Player player) + { + objectives.Add(player, new HashSet(DoorToOpen)); + flag.Add(player, false); + } + protected override void RoleRemoved(Player player) + { + objectives.Remove(player); + flag.Remove(player); + } + + + public static int GetNumberCheckpoints(Player player) + { + if (!objectives.ContainsKey(player)) + { + return -1; + } + + return objectives[player].Count; + } + + private void OnInteractingDoor(InteractingDoorEventArgs ev) + { + Player player = ev.Player; + if (!Check(player)) return; + objectives[player].Remove(ev.Door.Type); + + if (CheckDoors(player)) + { + KEAbilities.Remove("NumberCheckpoints", player); + bool equipped; + + if(player.CurrentItem != null) + { + equipped = player.CurrentItem.Type == ItemType.KeycardZoneManager; + } + else + { + equipped = false; + } + + Item zoneKeycard = player.Items.Where(p => p.Type == ItemType.KeycardZoneManager).ElementAtOrDefault(0); + zoneKeycard?.Destroy(); + + flag[player] = true; + Item engineerKeycard = player.AddItem(ItemType.KeycardFacilityManager); + if (equipped) + { + player.CurrentItem = engineerKeycard; + } + } + + } + + private bool CheckDoors(Player p) + { + if (flag[p]) return false; + return objectives[p].Count == 0; + } + + } +} diff --git a/KruacentExiled/KE.CustomRoles/CR/Scientist/scp202.cs b/KruacentExiled/KE.CustomRoles/CR/Scientist/scp202.cs new file mode 100644 index 00000000..1aaf03d3 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/CR/Scientist/scp202.cs @@ -0,0 +1,79 @@ +/*using KE.CustomRoles.API.Features; +using Exiled.API.Features; +using UnityEngine; +using Exiled.Events.EventArgs.Player; +using KE.CustomRoles.API.Interfaces; +using PlayerRoles; +using System.Collections.Generic; + +using Exiled.API.Extensions; + +namespace KE.CustomRoles.CR.Scientist +{ + public class SCP202 : KECustomRole, IColor + { + + public override int MaxHealth { get; set; } = 100; + + protected override Dictionary> SetTranslation() + { + return new() + { + ["en"] = new() + { + [TranslationKeyName] = "SCP-202", + [TranslationKeyDesc] = "? emsizan-itna'd te emsinummoc itna'd eiv enu sèrpa\n7691 lirva 91 el trom te engoloc à 6781 reivnaj 5 el én\n,etarcoméd neitérhc itrap ud eitrap tnasiaf 5691 à 9491 ed reilecnahC\n?elaidnom erreug ednoces al sèrpa dnamella reilecnahc re1 el darnoK erid xuev uT ?drannoC", + }, + ["fr"] = new() + { + [TranslationKeyName] = "SCP-202", + [TranslationKeyDesc] = "? emsizan-itna'd te emsinummoc itna'd eiv enu sèrpa\n7691 lirva 91 el trom te engoloc à 6781 reivnaj 5 el én\n,etarcoméd neitérhc itrap ud eitrap tnasiaf 5691 à 9491 ed reilecnahC\n?elaidnom erreug ednoces al sèrpa dnamella reilecnahc re1 el darnoK erid xuev uT ?drannoC", + } + }; + } + + public override RoleTypeId Role { get; set; } = RoleTypeId.Scientist; + public override bool KeepRoleOnDeath { get; set; } = false; + public override bool KeepRoleOnChangingRole { get; set; } = true; + + public override float SpawnChance { get; set; } = 100; + public Color32 Color => new Color32(191, 255, 183, 0); + + public static readonly Vector3 scale = new(-1,1,1); + + protected override void SubscribeEvents() + { + Exiled.Events.Handlers.Player.UsedItem += OnUsedItem; + base.SubscribeEvents(); + } + protected override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Player.UsedItem -= OnUsedItem; + base.UnsubscribeEvents(); + } + + protected override void ClearInventory(Player player) + { + //empty to keep the inventory + } + + private void OnUsedItem(UsedItemEventArgs ev) + { + if (!Check(ev.Player)) return; + if (ev.Item.Type == ItemType.SCP500) + { + RemoveRole(ev.Player); + } + } + protected override void RoleAdded(Player player) + { + player.SetFakeScale(scale, Player.Enumerable); + } + + protected override void RoleRemoved(Player player) + { + player.SetFakeScale(player.Scale, Player.Enumerable); + + } + } +}*/ \ No newline at end of file diff --git a/KruacentExiled/KE.CustomRoles/Commands/KECR/Give.cs b/KruacentExiled/KE.CustomRoles/Commands/KECR/Give.cs new file mode 100644 index 00000000..a9438a95 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Commands/KECR/Give.cs @@ -0,0 +1,98 @@ +using CommandSystem; +using Exiled.API.Features; +using Exiled.API.Features.Pools; +using Exiled.CustomRoles.API.Features; +using Exiled.Permissions.Extensions; +using KE.CustomRoles.API.Features; +using RemoteAdmin; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.Commands.KECR +{ + internal class Give : ICommand + { + public string Command => "give"; + + public string[] Aliases => new string[] { "g" }; + + public string Description => ""; + + public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) + { + try + { + if (!sender.CheckPermission("customroles.give")) + { + response = "Permission Denied, required: customroles.give"; + return false; + } + + if (arguments.Count == 0) + { + response = "give [Nickname/PlayerID/UserID/all/*]"; + return false; + } + + if (!KECustomRole.TryGet(arguments.At(0), out KECustomRole customRole) || customRole is null) + { + response = "Custom role " + arguments.At(0) + " not found!"; + return false; + } + KECustomRole kecr = customRole; + + if (arguments.Count == 1) + { + if (sender is PlayerCommandSender sender2) + { + Player player = Player.Get(sender2); + kecr.AddRole(player); + response = kecr.Name + " given to " + player.Nickname + "."; + return true; + } + + response = "Failed to provide a valid player."; + return false; + } + + string text = string.Join(" ", arguments.Skip(1)); + if (text == "*" || text == "all") + { + List list = ListPool.Pool.Get(Player.List); + foreach (Player item in list) + { + kecr.AddRole(item); + } + + response = "Custom role " + kecr.Name + " given to all players."; + ListPool.Pool.Return(list); + return true; + } + + IEnumerable processedData = Player.GetProcessedData(arguments, 1); + if (processedData.IsEmpty()) + { + response = "Cannot find player! Try using the player ID!"; + return false; + } + + foreach (Player item2 in processedData) + { + customRole.AddRole(item2); + } + + response = $"Customrole {customRole.Name} given to {processedData.Count()} players!"; + return true; + } + catch (Exception message) + { + Log.Error(message); + response = "Error"; + return false; + } + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/Commands/KECR/ImageCommand.cs b/KruacentExiled/KE.CustomRoles/Commands/KECR/ImageCommand.cs new file mode 100644 index 00000000..97f9f291 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Commands/KECR/ImageCommand.cs @@ -0,0 +1,73 @@ +using CommandSystem; +using Exiled.API.Features; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.HintPositions; +using KE.Utils.API.Commands; +using KE.Utils.API.Displays.DisplayMeow; +using KE.Utils.API.GifAnimator; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.Commands.KECR +{ + [CommandHandler(typeof(RemoteAdminCommandHandler))] + public class ImageCommand : ICommand + { + public string Command => "img"; + + public string[] Aliases => new string[0]; + + public string Description => "image"; + + public string[] Usage => new string[0]; + + public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) + { + response = "ok"; + try + { + + string arg1 = arguments.At(0); + + Log.Debug(arg1); + if (string.IsNullOrEmpty(arg1)) + { + response = "arg null "+ arg1; + return false; + } + + + if (!float.TryParse(arg1, out float size)) + { + response = "float not found"; + return false; + } + + + try + { + TextImage imaeg = new TextImage(Image.FromFile(MainPlugin.ImageLocation + "/Explode.png"), size); + + DisplayHandler.Instance.AddHint(AbilitiesPosition.GetIndex(0).HintPlacement, Player.Get(sender), imaeg.RawString, 30); + } + catch (Exception e2) + { + Log.Error(e2); + } + + } + catch (Exception e) + { + Log.Error(e); + } + + + + return true; + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/Commands/KECR/KEParentCommand.cs b/KruacentExiled/KE.CustomRoles/Commands/KECR/KEParentCommand.cs new file mode 100644 index 00000000..d9f0c14d --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Commands/KECR/KEParentCommand.cs @@ -0,0 +1,37 @@ +using CommandSystem; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.Commands.KECR +{ + [CommandHandler(typeof(RemoteAdminCommandHandler))] + [CommandHandler(typeof(GameConsoleCommandHandler))] + public class KEParentCommand : ParentCommand + { + + public KEParentCommand() + { + LoadGeneratedCommands(); + } + public override string Command => "kecustomrole"; + + public override string[] Aliases => new string[1] { "kecr" }; + + public override string Description => string.Empty; + + public override void LoadGeneratedCommands() + { + RegisterCommand(new Lists.List()); + RegisterCommand(new Give()); + } + + protected override bool ExecuteParent(ArraySegment arguments, ICommandSender sender, out string response) + { + response = "Usage : list"; + return true; + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/Commands/KECR/Lists/Abilities.cs b/KruacentExiled/KE.CustomRoles/Commands/KECR/Lists/Abilities.cs new file mode 100644 index 00000000..1eb70134 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Commands/KECR/Lists/Abilities.cs @@ -0,0 +1,40 @@ +using CommandSystem; +using Exiled.API.Features; +using Exiled.API.Features.Pools; +using KE.CustomRoles.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.Commands.KECR.Lists +{ + public class Abilities : ICommand + { + public string Command => "Ability"; + + public string[] Aliases => new string[1] { "a" }; + + public string Description => "list abilities"; + + public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) + { + if(KEAbilities.Registered.Count == 0) + { + response = "no ability found"; + return false; + } + StringBuilder sb = StringBuilderPool.Pool.Get(); + foreach(KEAbilities ability in KEAbilities.Registered.OrderBy(a => a.Name)) + { + sb.Append('[') + .Append(ability.Name) + .AppendLine("]"); + } + + response = StringBuilderPool.Pool.ToStringReturn(sb); + return true; + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/Commands/KECR/Lists/List.cs b/KruacentExiled/KE.CustomRoles/Commands/KECR/Lists/List.cs new file mode 100644 index 00000000..dc4be7ee --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Commands/KECR/Lists/List.cs @@ -0,0 +1,40 @@ +using CommandSystem; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.Commands.KECR.Lists +{ + public class List : ParentCommand + { + public List() + { + LoadGeneratedCommands(); + } + public override string Command => "list"; + + public override string[] Aliases => new string[] { "l" }; + + public override string Description => ""; + + public override void LoadGeneratedCommands() + { + RegisterCommand(Registered.Instance); + RegisterCommand(new Abilities()); + } + + protected override bool ExecuteParent(ArraySegment arguments, ICommandSender sender, out string response) + { + if (arguments.IsEmpty() && TryGetCommand(Registered.Instance.Command, out var command)) + { + command.Execute(arguments, sender, out response); + response = response + "\nTo view all abilities registered use command: " + string.Join(" ", arguments.Array) + " abilities"; + return true; + } + response = "Usage : registered, abilities"; + return false; + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/Commands/KECR/Lists/Registered.cs b/KruacentExiled/KE.CustomRoles/Commands/KECR/Lists/Registered.cs new file mode 100644 index 00000000..38016304 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Commands/KECR/Lists/Registered.cs @@ -0,0 +1,49 @@ +using CommandSystem; +using Exiled.API.Features.Pools; +using KE.CustomRoles.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.Commands.KECR.Lists +{ + public class Registered : ICommand + { + public static Registered Instance = new Registered(); + public string Command => "registered"; + + public string[] Aliases => new string[] { "r" }; + + public string Description => ""; + + private Registered() { } + + public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) + { + if (KECustomRole.Registered.Count == 0) + { + response = "no role found"; + return false; + } + + StringBuilder sb = StringBuilderPool.Pool.Get(); + string[] name; + sb.AppendLine(); + foreach (KECustomRole cr in KECustomRole.Registered.OrderBy(a => a.Name)) + { + name = cr.Name.Split('_'); + sb.Append('[') + .Append('(') + .Append(name[0]) + .Append(") ") + .Append(name[1]) + .AppendLine("]"); + } + + response = StringBuilderPool.Pool.ToStringReturn(sb); + return true; + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/Config.cs b/KruacentExiled/KE.CustomRoles/Config.cs new file mode 100644 index 00000000..75facbad --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Config.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Exiled.API.Interfaces; + +namespace KE.CustomRoles +{ + public class Config : IConfig + { + public bool IsEnabled { get; set; } = true; + public bool Debug { get; set; } = true; + + public int HeaderId { get; set; } = 1000; + + [Description("the minimum id the custom scp preference will use (Note: the header will always be the first)")] + public int CustomScpSliderRangeMin { get; set; } = 10000; + public int CustomScpSliderRangeMax { get; set; } = 10500; + } +} diff --git a/KruacentExiled/KE.CustomRoles/Events/EventArgs/ReceivedCustomRoleEventArgs.cs b/KruacentExiled/KE.CustomRoles/Events/EventArgs/ReceivedCustomRoleEventArgs.cs new file mode 100644 index 00000000..abd081fd --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Events/EventArgs/ReceivedCustomRoleEventArgs.cs @@ -0,0 +1,30 @@ +using Exiled.API.Features; +using Exiled.Events.EventArgs.Interfaces; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.Events.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.Events.EventArgs +{ + public class ReceivedCustomRoleEventArgs : IExiledEvent, IPlayerEvent, IKECustomRole + { + + public Player Player { get; } + + public KECustomRole KECustomRole { get; } + + public ReceivedCustomRoleEventArgs(Player player,KECustomRole kECustomRole) + { + Player = player; + KECustomRole = kECustomRole; + + } + + + + } +} diff --git a/KruacentExiled/KE.CustomRoles/Events/EventArgs/ReceivingCustomRoleEventArgs.cs b/KruacentExiled/KE.CustomRoles/Events/EventArgs/ReceivingCustomRoleEventArgs.cs new file mode 100644 index 00000000..6f7ee243 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Events/EventArgs/ReceivingCustomRoleEventArgs.cs @@ -0,0 +1,34 @@ +using Exiled.API.Features; +using Exiled.Events.EventArgs.Interfaces; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.Events.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.Events.EventArgs +{ + public class ReceivingCustomRoleEventArgs : IExiledEvent, IPlayerEvent, IDeniableEvent, IKECustomRole + { + + public Player Player { get; } + + + public bool IsAllowed { get; set; } + + public KECustomRole KECustomRole { get; } + + public ReceivingCustomRoleEventArgs(Player player, KECustomRole kECustomRole, bool isAllowed = true) + { + Player = player; + KECustomRole = kECustomRole; + IsAllowed = isAllowed; + + } + + + + } +} diff --git a/KruacentExiled/KE.CustomRoles/Events/Handlers/KECustomRole.cs b/KruacentExiled/KE.CustomRoles/Events/Handlers/KECustomRole.cs new file mode 100644 index 00000000..2c4fac24 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Events/Handlers/KECustomRole.cs @@ -0,0 +1,27 @@ +using KE.CustomRoles.Events.EventArgs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.Events.Handlers +{ + public static class KECustomRole + { + + public static event Action ReceivingCustomRole = delegate { }; + public static event Action ReceivedCustomRole = delegate { }; + + + public static void OnReceivingCustomRole(ReceivingCustomRoleEventArgs ev) + { + ReceivingCustomRole?.Invoke(ev); + } + public static void OnReceivedCustomRole(ReceivedCustomRoleEventArgs ev) + { + ReceivedCustomRole?.Invoke(ev); + } + + } +} diff --git a/KruacentExiled/KE.CustomRoles/Events/Interfaces/IKECustomRole.cs b/KruacentExiled/KE.CustomRoles/Events/Interfaces/IKECustomRole.cs new file mode 100644 index 00000000..dc108dbe --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Events/Interfaces/IKECustomRole.cs @@ -0,0 +1,15 @@ +using KE.CustomRoles.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.CustomRoles.Events.Interfaces +{ + public interface IKECustomRole + { + public KECustomRole KECustomRole { get; } + + } +} diff --git a/KruacentExiled/KE.CustomRoles/MainPlugin.cs b/KruacentExiled/KE.CustomRoles/MainPlugin.cs new file mode 100644 index 00000000..cc73287e --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/MainPlugin.cs @@ -0,0 +1,139 @@ +using Exiled.API.Features; +using Exiled.API.Features.Core.UserSettings; +using Exiled.API.Interfaces; +using Exiled.CustomRoles.API.Features; +using Exiled.Events.EventArgs.Map; +using Exiled.Events.EventArgs.Player; +using Exiled.Events.EventArgs.Server; +using HarmonyLib; +using KE.CustomRoles.Abilities.FireAbilities; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.API.HintPositions; +using KE.CustomRoles.Settings; +using KE.Misc.Features.Spawn; +using KE.Utils.API.CustomStats; +using KE.Utils.API.Displays.DisplayMeow; +using KE.Utils.API.Features.SCPs; +using KE.Utils.API.GifAnimator; +using KruacentExiled; +using MEC; +using Microsoft.Win32; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.ComTypes; + + +namespace KE.CustomRoles +{ + public class MainPlugin : KEPlugin + { + public override string Name => "KE.CustomRoles"; + public override string Prefix => "KE.CR"; + public static MainPlugin Instance; + public static Config Configs => (Config) Instance?.Config; + public static readonly HintPlacement CRHint = new HintPlacement(0, 750); + public static readonly HintPlacement CREffect = new HintPlacement(700, 300); + public static readonly HintPlacement AbilitiesDesc = new HintPlacement(0, 900); + public static readonly HintPlacement Abilities = new HintPlacement(0, 850,HintServiceMeow.Core.Enum.HintAlignment.Left); + public static readonly HintPlacement RightHPbars = new HintPlacement(55, 1000,HintServiceMeow.Core.Enum.HintAlignment.Left); + private SettingHandler _settingHandler; + internal static SettingHandler SettingHandler => Instance?._settingHandler; + + private Harmony Harmony; + + internal Dictionary icons; + + public static readonly string ImageLocation = Paths.Configs + "/Img/"; + + public override IConfig Config => config; + private IConfig config = null; + public override void OnEnabled() + { + Instance = this; + config = KruacentExiled.MainPlugin.Instance.Config.CustomRoleConfig; + + _settingHandler = new SettingHandler(); + Utils.API.Settings.GlobalSettings.GlobalSettingsHandler.Instance.TryLoad(); + Utils.API.Settings.GlobalSettings.GlobalSettingsHandler.Instance.SubscribeEvents(); + + CustomPlayerStat.AddModule(); + CustomStatsEvents.SubscribeEvents(); + icons = new Dictionary(); + + CustomTeamEvents.SubscribeEvents(); + + + LoadImage(); + + Harmony = new Harmony(Name); + Harmony.PatchAll(); + SettingHandler.SubscribeEvents(); + KEAbilities.Register(KruacentExiled.MainPlugin.Instance.Assembly); + KECustomRole.Register(); + SubscribeEvents(); + } + + public override void OnDisabled() + { + + KECustomRole.Unregister(); + SettingHandler.UnsubscribeEvents(); + Harmony.UnpatchAll(); + + KEAbilities.Unregister(); + UnsubscribeEvents(); + CustomTeamEvents.UnsubscribeEvents(); + CustomStatsEvents.UnsubscribeEvents(); + Utils.API.Settings.GlobalSettings.GlobalSettingsHandler.Instance.UnsubscribeEvents(); + _settingHandler = null; + Instance = null; + } + + public void SubscribeEvents() + { + Misc.Features.Spawn.Spawn.OnAssigned += KECustomRole.SpawnStartRound; + Exiled.Events.Handlers.Server.RespawnedTeam += KECustomRole.RespawnCustomRole; + Exiled.Events.Handlers.Server.WaitingForPlayers += KECustomRole.ResetNumberOfSpawn; + Exiled.Events.Handlers.Player.Verified += KECustomRole.ShowCustomRole; + } + public void UnsubscribeEvents() + { + Misc.Features.Spawn.Spawn.OnAssigned -= KECustomRole.SpawnStartRound; + Exiled.Events.Handlers.Server.RespawnedTeam -= KECustomRole.RespawnCustomRole; + Exiled.Events.Handlers.Server.WaitingForPlayers -= KECustomRole.ResetNumberOfSpawn; + Exiled.Events.Handlers.Player.Verified -= KECustomRole.ShowCustomRole; + } + + + + + private void LoadImage() + { + if (!Directory.Exists(ImageLocation)) + { + Log.Warn("Directory not found. creating..."); + Directory.CreateDirectory(ImageLocation); + } + + string[] rawfile = Directory.GetFiles(ImageLocation, "*.png"); + + foreach (string file in rawfile) + { + string noExFile = Path.GetFileNameWithoutExtension(file); + Log.Info($"loading {file} as {noExFile}"); + icons.Add(noExFile,new TextImage(Image.FromFile(file),5)); + } + } + + + + public static void ShowEffectHint(Player player, string text) + { + float delay = SettingHandler.GetTime(player); ; + DisplayHandler.Instance.AddHint(MainPlugin.CREffect, player, text, delay); + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/Settings/DebugSettings/DebugSetting.cs b/KruacentExiled/KE.CustomRoles/Settings/DebugSettings/DebugSetting.cs new file mode 100644 index 00000000..8a210797 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Settings/DebugSettings/DebugSetting.cs @@ -0,0 +1,56 @@ +using Exiled.API.Features; +using Exiled.API.Features.Core.UserSettings; +using KE.CustomRoles.API.Features; +using KE.Utils.API.Settings.SettingsCategories; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UserSettings.ServerSpecific; + +namespace KE.CustomRoles.Settings.DebugSettings +{ + public abstract class DebugSetting + { + + internal static List settings = new List(); + + public HeaderSetting Header; + + private SettingsCategory category = null; + + public DebugSetting() + { + settings.Add(this); + } + + public IReadOnlyCollection Settings { get; private set; } + + + public void Create() + { + Settings = CreateSettings(); + Header = Settings.First(s => s is HeaderSetting) as HeaderSetting; + } + + protected abstract List CreateSettings(); + + public virtual void OnSettingValueReceived(Player player, ServerSpecificSettingBase settingBase) + { + + } + + + public SettingsCategory GetCategory() + { + if(category == null) + { + category = new SettingsCategory(Header, 0, Settings.Where(s => !(s is HeaderSetting)).ToList()); + } + return category; + } + + + } +} diff --git a/KruacentExiled/KE.CustomRoles/Settings/DebugSettings/FollowingTextToyDebugSetting.cs b/KruacentExiled/KE.CustomRoles/Settings/DebugSettings/FollowingTextToyDebugSetting.cs new file mode 100644 index 00000000..0f6aebd3 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Settings/DebugSettings/FollowingTextToyDebugSetting.cs @@ -0,0 +1,110 @@ +using Exiled.API.Features; +using Exiled.API.Features.Core.UserSettings; +using HintServiceMeow.Core.Enum; +using HintServiceMeow.Core.Utilities; +using KE.Utils.API.KETextToy; +using System.Collections.Generic; +using UnityEngine; +using UserSettings.ServerSpecific; +using YamlDotNet.Core.Tokens; + +namespace KE.CustomRoles.Settings.DebugSettings +{ + internal class FollowingTextToyDebugSetting : DebugSetting + { + + private int _idTestHintspawn = 190; + private int _idTestHinttext = 191; + private int _idTestHintslidersize = 192; + private int _idTestHintsliderx = 193; + private int _idTestHintslidery = 194; + private int _idTestHintsliderz = 195; + private int _idHeaderTestHint = 196; + private int _idTestHintdestroy = 197; + protected override List CreateSettings() + { + created = true; + return new List() + { + new HeaderSetting(_idHeaderTestHint, "Follow Text Creator", padding: true), + new SliderSetting(_idTestHintsliderx, "x", 0, 360, 0), + new SliderSetting(_idTestHintslidery, "y", 0, 360, 0), + new SliderSetting(_idTestHintsliderz, "z", 0, 360, 0), + new SliderSetting(_idTestHintslidersize, "size", 0, 100, 5), + SettingBase.Create(new SSPlaintextSetting(_idTestHinttext, "text")), + new ButtonSetting(_idTestHintspawn, "spawn", "spawn"), + new ButtonSetting(_idTestHintdestroy, "destroyall", "destroyall"), + }; + } + private bool created = false; + string text = "TEST"; + float size = 10; + + private float x = 0; + private float y = 0; + private float z = 0; + + public override void OnSettingValueReceived(Player player, ServerSpecificSettingBase settingBase) + { + + if (created) + { + CreateTextToy(player, settingBase); + } + } + + private List texttoy = new List(); + private void CreateTextToy(Player player, ServerSpecificSettingBase setting) + { + if (SettingBase.TryGetSetting(player, _idTestHinttext, out var textsetting)) + { + text = textsetting.Text; + } + + if (SettingBase.TryGetSetting(player, _idTestHintslidersize, out var slidersize)) + { + size = slidersize.SliderValue; + } + + + if (SettingBase.TryGetSetting(player, _idTestHintsliderx, out var sliderx)) + { + x = sliderx.SliderValue; + } + if (SettingBase.TryGetSetting(player, _idTestHintslidery, out var slidery)) + { + y = slidery.SliderValue; + } + if (SettingBase.TryGetSetting(player, _idTestHintsliderz, out var sliderz)) + { + z = sliderz.SliderValue; + } + + if (SettingBase.TryGetSetting(player, _idTestHintspawn, out var button)) + { + if (setting == button.Base) + { + string fulltext = "" + text + ""; + + Quaternion rotation = Quaternion.Euler(x, y, z); + + FollowingTextToy followtext = new FollowingTextToy(Player.List, player.Position, rotation, Vector3.one * size); + followtext.Toy.TextFormat = fulltext; + texttoy.Add(followtext); + } + } + + if (SettingBase.TryGetSetting(player, _idTestHintdestroy, out var buttondestroy)) + { + if (setting == buttondestroy.Base) + { + foreach(var following in texttoy) + { + following.Destroy(); + } + } + } + + } + } +} diff --git a/KruacentExiled/KE.CustomRoles/Settings/DebugSettings/HintCreatorDebugSetting.cs b/KruacentExiled/KE.CustomRoles/Settings/DebugSettings/HintCreatorDebugSetting.cs new file mode 100644 index 00000000..2161fb41 --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Settings/DebugSettings/HintCreatorDebugSetting.cs @@ -0,0 +1,137 @@ +using Exiled.API.Features; +using Exiled.API.Features.Core.UserSettings; +using HintServiceMeow.Core.Enum; +using HintServiceMeow.Core.Extension; +using HintServiceMeow.Core.Utilities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UserSettings.ServerSpecific; + +using Hint = HintServiceMeow.Core.Models.Hints.Hint; + +namespace KE.CustomRoles.Settings.DebugSettings +{ + internal class HintCreatorDebugSetting : DebugSetting + { + private int _idTestHintslidery = 154; + private int _idTestHintsliderx = 155; + private int _idTestHintslidertime = 156; + private int _idTestHintalign = 157; + private int _idTestHintspawn = 158; + private int _idTestHinttext = 159; + private int _idTestHintslidersize = 180; + private int _idHeaderTestHint = 160; + protected override List CreateSettings() + { + string[] options = new string[] { "left", "center", "right" }; + created = true; + return new List() + { + new HeaderSetting(_idHeaderTestHint,"Hint creator",padding:true), + new SliderSetting(_idTestHintsliderx,"x",-2000,2000,0), + new SliderSetting(_idTestHintslidery,"y",0,2000,0), + new SliderSetting(_idTestHintslidertime,"time",0,10,5), + new SliderSetting(_idTestHintslidersize,"size",0,100,5), + new DropdownSetting(_idTestHintalign,"alignment",options), + new ButtonSetting(_idTestHintspawn,"spawn","spawn"), + SettingBase.Create(new SSPlaintextSetting(_idTestHinttext,"text")), + }; + + } + + public override void OnSettingValueReceived(Player player, ServerSpecificSettingBase settingBase) + { + if (created) + { + CreateHint(player, settingBase); + } + } + + + private bool created = false; + float xvalue = 0; + float yvalue = 0; + float timevalue = 5; + HintAlignment HintAlignment = HintAlignment.Center; + string text = "TEST"; + float size = 10; + + public void CreateHint(Player player, ServerSpecificSettingBase setting) + { + if (SettingBase.TryGetSetting(player, _idTestHintslidery, out var slidery)) + { + yvalue = slidery.SliderValue; + } + + if (SettingBase.TryGetSetting(player, _idTestHintsliderx, out var sliderx)) + { + xvalue = sliderx.SliderValue; + } + + if (SettingBase.TryGetSetting(player, _idTestHintslidertime, out var slidertime)) + { + timevalue = slidertime.SliderValue; + } + + if (SettingBase.TryGetSetting(player, _idTestHinttext, out var textsetting)) + { + text = textsetting.Text; + } + if (SettingBase.TryGetSetting(player, _idTestHintslidersize, out var slidersize)) + { + size = slidersize.SliderValue; + } + + if (SettingBase.TryGetSetting(player, _idTestHintalign, out var dropdown)) + { + + int selected = dropdown.SelectedIndex; + if (selected == 0) + { + HintAlignment = HintAlignment.Left; + } + if (selected == 1) + { + HintAlignment = HintAlignment.Center; + } + if (selected == 2) + { + HintAlignment = HintAlignment.Right; + } + } + + if (SettingBase.TryGetSetting(player, _idTestHintspawn, out var button)) + { + if (setting == button.Base) + { + PlayerDisplay display = PlayerDisplay.Get(player); + Log.Debug($"creating hint at {xvalue},{yvalue} for {timevalue}"); + string fulltext = "" + text + ""; + + var hint = new Hint() + { + XCoordinate = xvalue, + YCoordinate = yvalue, + Alignment = HintAlignment, + Text = fulltext + }; + display.ClearHint(); + display.AddHint(hint); + hint.HideAfter(timevalue); + + } + + + + } + + + } + + + + } +} diff --git a/KruacentExiled/KE.CustomRoles/Settings/SettingHandler.cs b/KruacentExiled/KE.CustomRoles/Settings/SettingHandler.cs new file mode 100644 index 00000000..5a3b4d1a --- /dev/null +++ b/KruacentExiled/KE.CustomRoles/Settings/SettingHandler.cs @@ -0,0 +1,272 @@ +using Exiled.API.Features; +using Exiled.API.Features.Core.UserSettings; +using KE.CustomRoles.API.Features; +using KE.CustomRoles.Settings.DebugSettings; +using KE.Utils.API; +using KE.Utils.API.Interfaces; +using KE.Utils.API.Settings; +using KE.Utils.API.Settings.SettingsCategories; +using LabApi.Events.Arguments.PlayerEvents; +using System; +using System.Collections.Generic; +using System.Linq; +using TMPro; +using UserSettings.ServerSpecific; + +namespace KE.CustomRoles.Settings +{ + internal class SettingHandler : IUsingEvents + { + private readonly int _idHeader = 148; + + private readonly int _idTimeCustomRole = 144; + private readonly int _idDesc = 143; + private readonly int _idMode = 145; + private readonly int _idDown = 149; + private readonly int _idUp = 150; + private readonly int _idSelect = 151; + private readonly int _idArrow = 152; + private readonly int _idTimeAbilityDesc = 153; + private readonly int _idReshowCustomRole = 154; + private readonly int _idLeft = 7000; + private readonly int _idRight = 7001; + + + + + public static event Action DownPressed = delegate { }; + public static event Action UpPressed = delegate { }; + public static event Action LeftPressed = delegate { }; + public static event Action RightPressed = delegate { }; + + public static SettingHandler Instance { get; private set; } + private List settings; + //public readonly SettingsPage page; + //public readonly SettingsPage hintpage = null; + public const string baseArrow = "<--"; + public SettingHandler() + { + Instance = this; + HeaderSetting header = new HeaderSetting(_idHeader, "Custom Roles", padding: true); + SettingBase arrow = SettingBase.Create(new SSPlaintextSetting(_idArrow, "Personalize the arrow next to the selected ability", baseArrow, 16, TMP_InputField.ContentType.Standard, string.Empty, 0)); + arrow.Header = header; + + + + settings = new List() + { + new TwoButtonsSetting(_idDesc,"Descriptions","Disabled","Enabled",true,"hide/show the description the Custom Role ",header:header), + new SliderSetting(_idTimeCustomRole,"Time shown",0,30,20,header:header), + new SliderSetting(_idTimeAbilityDesc,"Ability Description time shown",0,30,20,header:header), + new KeybindSetting(_idUp, "Select up", UnityEngine.KeyCode.None,header:header), + new KeybindSetting(_idDown, "Select down", UnityEngine.KeyCode.None,header:header), + new KeybindSetting(_idSelect, "Use selected ability", UnityEngine.KeyCode.None,header:header), + arrow, + new ButtonSetting(_idReshowCustomRole, "Reshow Custom role description","click",header:header), + new KeybindSetting(_idLeft, "Left", UnityEngine.KeyCode.LeftArrow,header:header), + new KeybindSetting(_idRight, "Right", UnityEngine.KeyCode.RightArrow,header:header), + }; + + + + SettingsCategory cat = new SettingsCategory(header,1000,settings); + + + if (MainPlugin.Instance.Config.Debug) + { + + ReflectionHelper.GetObjects().ToList(); + foreach (DebugSetting debug in DebugSetting.settings) + { + debug.Create(); + debug.GetCategory(); + } + + } + + } + + private void OnWaitingForPlayers() + { + } + + public void SubscribeEvents() + { + + ServerSpecificSettingsSync.ServerOnSettingValueReceived += SafeOnSettingValueReceived; + Exiled.Events.Handlers.Server.WaitingForPlayers += OnWaitingForPlayers; + LabApi.Events.Handlers.PlayerEvents.Joined += AddPlayer; + DownPressed += Down; + UpPressed += Up; + + } + + public void UnsubscribeEvents() + { + ServerSpecificSettingsSync.ServerOnSettingValueReceived -= SafeOnSettingValueReceived; + LabApi.Events.Handlers.PlayerEvents.Joined -= AddPlayer; + Exiled.Events.Handlers.Server.WaitingForPlayers -= OnWaitingForPlayers; + DownPressed -= Down; + UpPressed -= Up; + } + + + private void AddPlayer(PlayerJoinedEventArgs ev) + { + ServerSpecificSettingsSync.SendToPlayer(ev.Player.ReferenceHub); + } + + private void SafeOnSettingValueReceived(ReferenceHub hub, ServerSpecificSettingBase settingBase) + { + //not catching the exception will desync & kick the player + try + { + OnSettingValueReceived(Player.Get(hub), settingBase); + } + catch (Exception e) + { + Log.Error(e); + } + } + + private void OnSettingValueReceived(Player player, ServerSpecificSettingBase settingBase) + { + if (CheckPressed(settingBase, _idDown)) + { + DownPressed?.Invoke(player); + } + + if (CheckPressed(settingBase, _idUp)) + { + UpPressed?.Invoke(player); + } + + if (CheckPressed(settingBase, _idSelect)) + { + Log.Debug("select"); + KEAbilities.UseSelected(player); + } + + if (CheckPressed(settingBase, _idLeft)) + { + LeftPressed?.Invoke(player); + } + + if (CheckPressed(settingBase, _idRight)) + { + RightPressed?.Invoke(player); + } + + if (settingBase is SSButton buttonSetting && buttonSetting.SettingId == _idReshowCustomRole) + { + KECustomRole cr = KECustomRole.Get(player).FirstOrDefault(); + + if(cr != null) + { + cr.Show(player); + } + } + + + foreach (DebugSetting debug in DebugSetting.settings) + { + debug.OnSettingValueReceived(player, settingBase); + + } + + + } + + private Dictionary playerPos = new Dictionary(); + + private void Up(Player player) + { + Move(player, +1); + } + + private int mod(int x, int m) + { + return (x % m + m) % m; + } + + private void Down(Player player) + { + Move(player, -1); + } + + private void Move(Player player, int movement) + { + if (KEAbilities.PlayersAbility.TryGetValue(player, out var list)) + { + if (list.Count == 0) + { + return; + } + + if (!playerPos.ContainsKey(player)) + { + playerPos.Add(player, 0); + } + + + int index = mod((playerPos[player] + movement), list.Count); + Log.Debug("down " + index); + KEAbilities ability = list[index]; + playerPos[player] += movement; + ability?.SelectAbility(player); + KEAbilities.UpdateGUI(player); + } + } + + public static bool CheckPressed(ServerSpecificSettingBase settingBase, int id) + { + return settingBase.SettingId == id && + (settingBase is SSKeybindSetting keybindSetting && keybindSetting.SyncIsPressed); + } + + + + /// + /// + /// + /// + /// true if keybinds mode is activated ; false otherwise + internal bool GetMode(Player p) + { + if (!SettingBase.TryGetSetting(p, _idMode, out var setting)) return setting.IsSecondDefault; + + return setting.IsFirst; + } + + + internal string GetArrow(Player p) + { + if (!SettingBase.TryGetSetting(p, _idArrow, out var setting)) return baseArrow; + + return setting.Text; + + } + internal float GetTime(Player p) + { + if (!SettingBase.TryGetSetting(p, _idTimeCustomRole, out var setting)) return 20; + return setting.SliderValue; + } + internal float GetAbilityTime(Player p) + { + if (!SettingBase.TryGetSetting(p, _idTimeAbilityDesc, out var setting)) return 20; + return setting.SliderValue; + } + + /// + /// + /// + /// + /// true if the player wants description ; false otherwise + internal bool GetDescriptionsSettings(Player p) + { + if (!SettingBase.TryGetSetting(p, _idDesc, out var setting)) return true; + return setting.IsSecond; + } + + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/CassieGoCrazy/AntagonisticCassie.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/CassieGoCrazy/AntagonisticCassie.cs new file mode 100644 index 00000000..7b3c2735 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/CassieGoCrazy/AntagonisticCassie.cs @@ -0,0 +1,91 @@ +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.Events.EventArgs.Player; +using KE.GlobalEventFramework.GEFE.API.Features.Hints; +using KE.GlobalEventFramework.GEFE.API.Interfaces; +using KE.Utils.API.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static UnityEngine.GraphicsBuffer; + +namespace KE.GlobalEventFramework.Examples.API.Feature.CassieGoCrazy +{ + // Turns off the lights in the Heavy Containment zone. + public class AntagonisticCassie : ICGCEffect, IUsingEvents + { + private Player target; + /// + /// Percentage for rare event to occur + /// + public int RareEvent { get; set; } = 2; + private bool flag = false; + + public IReadOnlyCollection Reward { get; } = new HashSet() + { + ItemType.ParticleDisruptor, + ItemType.Coin, + ItemType.KeycardO5, + ItemType.SCP268 + }; + + + + public void Effect() + { + List nonScpPlayers = Player.List.Where(player => !player.IsScp).ToList(); + + /*if (nonScpPlayers.Count > 0) + { + target = nonScpPlayers[UnityEngine.Random.Range(0, nonScpPlayers.Count)]; + + Cassie.Message("New target : " + target.Nickname, true, true, true); + + flag = true; + + + }*/ + } + + private void OnPlayerDeath(DyingEventArgs ev) + { + if (!flag) return; + + + if (ev.Player == target && ev.Attacker != null && ev.Attacker != target) + { + if (!ev.Attacker.IsScp) + { + GiveRandomRewardPlayer(ev.Attacker); + } + flag = false; + } + } + + private void GiveRandomRewardPlayer(Player player) + { + + ItemType randomItem = Reward.GetRandomValue(); + + player.AddItem(randomItem); + + if (UnityEngine.Random.Range(0, 100) < RareEvent) + { + player.MaxHealth += 25; + DisplayHints.AddHintEffect(player, "Another gift for you!", 5); + } + } + + public void SubscribeEvents() + { + Exiled.Events.Handlers.Player.Dying += OnPlayerDeath; + } + + public void UnsubscribeEvents() + { + Exiled.Events.Handlers.Player.Dying -= OnPlayerDeath; + } + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/CassieGoCrazy/ChangeColor.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/CassieGoCrazy/ChangeColor.cs new file mode 100644 index 00000000..4288c161 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/CassieGoCrazy/ChangeColor.cs @@ -0,0 +1,18 @@ +using Exiled.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.Examples.API.Feature.CassieGoCrazy +{ + // Turns off the lights in the Heavy Containment zone. + public class ChangeColor : ICGCEffect + { + public void Effect() + { + Exiled.API.Features.Map.ChangeLightsColor(UnityEngine.Color.cyan); + } + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/CassieGoCrazy/ICGCEffect.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/CassieGoCrazy/ICGCEffect.cs new file mode 100644 index 00000000..83331aab --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/CassieGoCrazy/ICGCEffect.cs @@ -0,0 +1,15 @@ +using Exiled.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.Examples.API.Feature.CassieGoCrazy +{ + public interface ICGCEffect + { + + public abstract void Effect(); + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/CassieGoCrazy/NoLight.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/CassieGoCrazy/NoLight.cs new file mode 100644 index 00000000..02f0a72e --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/CassieGoCrazy/NoLight.cs @@ -0,0 +1,18 @@ +using Exiled.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.Examples.API.Feature.CassieGoCrazy +{ + // Turns off the lights in the Heavy Containment zone. + public class NoLight : ICGCEffect + { + public void Effect() + { + Exiled.API.Features.Map.TurnOffAllLights(20, Exiled.API.Enums.ZoneType.HeavyContainment); + } + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/CassieGoCrazy/SadCassie.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/CassieGoCrazy/SadCassie.cs new file mode 100644 index 00000000..7e4d4686 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/CassieGoCrazy/SadCassie.cs @@ -0,0 +1,38 @@ +using Exiled.API.Features; +using Exiled.API.Features.Items; +using MEC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.Examples.API.Feature.CassieGoCrazy +{ + // Sad Cassie scenario. + public class SadCassie : ICGCEffect + { + public void Effect() + { + + //Cassie.Message("I wanna just be useful", true, true, true); + Exiled.API.Features.Map.TurnOffAllLights(2, Exiled.API.Enums.ZoneType.HeavyContainment); + Exiled.API.Features.Map.TurnOffAllLights(2, Exiled.API.Enums.ZoneType.Entrance); + + + if (!Warhead.IsDetonated) + Warhead.Start(); + + for (int i = 0; i < 10; i++) + { + ((ExplosiveGrenade)Item.Create(ItemType.GrenadeHE)).SpawnActive(Room.Random().Position); + } + + Timing.CallDelayed(10, delegate + { + Warhead.Stop(); + }); + + } + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/CassieGoCrazy/WarheadEffect.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/CassieGoCrazy/WarheadEffect.cs new file mode 100644 index 00000000..4b51aea5 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/CassieGoCrazy/WarheadEffect.cs @@ -0,0 +1,21 @@ +using Exiled.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.Examples.API.Feature.CassieGoCrazy +{ + // Starting the Warhead + public class WarheadEffect : ICGCEffect + { + public void Effect() + { + if (!Warhead.IsDetonated) + { + Warhead.Start(); + } + } + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/MF/MalfunctionDisplay.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/MF/MalfunctionDisplay.cs new file mode 100644 index 00000000..b5934cb1 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/MF/MalfunctionDisplay.cs @@ -0,0 +1,100 @@ +using Exiled.API.Features; +using KE.GlobalEventFramework.Examples.API.Interfaces; +using KE.GlobalEventFramework.GEFE.API.Features.Hints; +using MEC; +using PlayerRoles; +using System.Collections.Generic; +using System.Linq; + +namespace KE.GlobalEventFramework.Examples.API.Feature.mf +{ + public class MalfunctionDisplay + { + private Malfunctions _malfunction; + public float RefreshRate { get; set; } = 1; + private CoroutineHandle _coroutineHandle; + public MalfunctionDisplay(Malfunctions malfunction) + { + _malfunction = malfunction; + _coroutineHandle = Show(); + } + + ~MalfunctionDisplay() + { + Timing.KillCoroutines(_coroutineHandle); + } + + public CoroutineHandle Show() + { + return Timing.RunCoroutine(Tick()); + } + + private IEnumerator Tick() + { + while (Round.InProgress) + { + yield return Timing.WaitForSeconds(RefreshRate); + ShowAllSpect(GetHint()); + } + } + + + + private void ShowAllSpect(string hint) + { + + foreach (Player p in Player.List.Where(p => p.Role == RoleTypeId.Spectator)) + { + DisplayHints.AddHintEffect(p, hint, RefreshRate); + } + } + + private string GetHint() + { + return $"{GetCurrentMalfunction()}\n{GetAllEffect()}"; + } + + + private string GetCurrentMalfunction() + { + sbyte malfunction = _malfunction.Malfunction; + sbyte previous = _malfunction.PreviousMalfunction; + if (malfunction > previous) + return $" {malfunction}\u2191 (+{malfunction - previous})"; + if (malfunction < previous) + return $" {malfunction}\u2193 ({malfunction - previous})"; + else + return $" {malfunction}\u2192 ({malfunction - previous})"; + + } + + private string GetAllEffect() + { + string result = string.Empty; + foreach (MalfunctionEffect me in Malfunctions.MalfunctionEffects) + { + if (Malfunctions.EffectAlreadyActivated(me)) + { + if (IsREActivated(me)) result += ""; + result += $"{me.MalfunctionActivation} - {me.Name}"; + if (IsREActivated(me)) result += ""; + } + + } + return result; + } + private bool IsREActivated(MalfunctionEffect me) + { + if (me is IReversibleEffect re) + { + if (_malfunction.Malfunction >= re.MalfunctionDeactivation) + { + return true; + } + } + + return false; + } + + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/MF/MalfunctionEffect.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/MF/MalfunctionEffect.cs new file mode 100644 index 00000000..6a793ef9 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/MF/MalfunctionEffect.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.Examples.API.Feature.mf +{ + public abstract class MalfunctionEffect + { + public abstract string Name { get; } + public abstract string VoiceLine { get; } + public abstract string VoiceLineTranslated { get; } + public abstract sbyte MalfunctionActivation { get; } + + public abstract void ActivateEffect(); + + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/MF/Malfunctions.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/MF/Malfunctions.cs new file mode 100644 index 00000000..1eae759a --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/API/Feature/MF/Malfunctions.cs @@ -0,0 +1,200 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Doors; +using Exiled.API.Interfaces; +using Exiled.Events.EventArgs.Player; +using Exiled.Events.EventArgs.Scp049; +using KE.GlobalEventFramework.Examples.API.Interfaces; +using KE.GlobalEventFramework.GEFE.API.Features; +using KE.GlobalEventFramework.GEFE.API.Interfaces; +using MEC; +using PlayerRoles; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Utils.NonAllocLINQ; + +namespace KE.GlobalEventFramework.Examples.API.Feature.mf +{ + public class Malfunctions + { + + private sbyte _malfunction = 15; + public const sbyte Lower = -50; + public const sbyte Higher = 125; + public sbyte Malfunction + { + get { return _malfunction; } + set + { + if (value > Higher) _malfunction = Higher; + else if (value < Lower) _malfunction = Lower; + else _malfunction = value; + } + } + + public MalfunctionLevel MalfunctionLevels + { + get { return (MalfunctionLevel)Malfunction; } + } + + private static HashSet _malfunctionEffects = new HashSet(); + public static HashSet MalfunctionEffects => _malfunctionEffects.ToList().ToHashSet(); + private static Dictionary _voiced; + private static Dictionary _voicedDeactivate; + + public static bool EffectAlreadyActivated(MalfunctionEffect effect) + { + return _voiced[effect]; + } + + public sbyte PreviousMalfunction { get; private set; } + public sbyte MalfunctionAdd { get; set; } = 1; + public MalfunctionDisplay MalfunctionDisplay { get; private set; } + + + internal Malfunctions(bool display = true) + { + try + { + if (display) + MalfunctionDisplay = new MalfunctionDisplay(this); + else + MalfunctionDisplay = null; + } + catch (Exception ex) + { + Log.Error(ex + "\nRueI is probably missing : put it in dependency or disable the display"); + MalfunctionDisplay = null; + } + + LoadMalfunctionsEffect(); + } + + public bool IsDisplayEnabled() + { + return MalfunctionDisplay != null; + } + + private void LoadMalfunctionsEffect() + { + foreach (IPlugin plugin in Exiled.Loader.Loader.Plugins.Where(pl => pl.Name != MainPlugin.Instance.Name)) + { + foreach (Type type in plugin.Assembly.GetTypes()) + { + try + { + if (type.IsSubclassOf(typeof(MalfunctionEffect))) + { + MalfunctionEffect me = Activator.CreateInstance(type) as MalfunctionEffect; + _malfunctionEffects.Add(me); + } + } + catch (Exception e) + { + Log.Error($"Error registering in plugin {plugin.Name} : {e.Message}"); + } + } + } + _voiced = _malfunctionEffects.ToDictionary(m => m, m => false); + _voicedDeactivate = _malfunctionEffects.Where(m => m is IReversibleEffect).ToDictionary(m => m, m => false); + } + + + internal IEnumerator Tick() + { + while (Round.InProgress) + { + PreviousMalfunction = Malfunction; + Malfunction += MalfunctionAdd; + Malfunction += AdditionnalMalfunction(); + CheckMalfunctionEffect(Malfunction); + Log.Debug($"Malfunction={Malfunction}"); + yield return Timing.WaitForSeconds(60); + } + } + + + + private void CheckMalfunctionEffect(sbyte malfunction) + { + foreach (MalfunctionEffect me in _malfunctionEffects) + { + if (malfunction >= me.MalfunctionActivation) + { + if (!_voiced[me]) + { + _voiced[me] = true; + //Cassie.MessageTranslated(me.VoiceLine, me.VoiceLineTranslated, false, false); + } + me.ActivateEffect(); + } + + if (me is IReversibleEffect re && malfunction < re.MalfunctionDeactivation) + { + if (!_voicedDeactivate[me]) + { + _voicedDeactivate[me] = true; + //Cassie.MessageTranslated(re.VoiceLineDeactivate, re.VoiceLineDeactivateTranslated, false, false); + } + re.DeactivateEffect(); + } + } + } + + + + + private sbyte AdditionnalMalfunction() + { + sbyte result = (sbyte)UnityEngine.Random.Range(-2, 3); + //generator reduce the malfunction + result -= (sbyte)(Generator.List.Count(x => x.IsEngaged) * 3); + //number of scp increase 3 (except zombies) + result += (sbyte)(Player.List.Count(p => p.Role.Side == Side.Scp && p.Role != RoleTypeId.Scp0492) * 3); + //number of zombies increase 1 + result += (sbyte)Player.List.Count(p => p.Role == RoleTypeId.Scp0492); + //reduce when walking + + return result; + + } + + + internal void OnDying(DyingEventArgs ev) + { + switch (ev.Player.Role.Side) + { + case Side.Mtf: + Malfunction += 1; + break; + case Side.ChaosInsurgency: + Malfunction -= 1; + break; + case Side.Scp: + if (ev.Player.Role != RoleTypeId.Scp0492) Malfunction -= 10; + else Malfunction -= 2; + break; + } + } + + internal void OnFinishingRevive(FinishingRecallEventArgs ev) + { + Malfunction += 3; + } + + + + public enum MalfunctionLevel + { + VeryLowMalfunction = 0, + LowMalfunction = 25, + MediumMalfunction = 50, + HighMalfunction = 75, + VeryHighMalfunction = 100, + } + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/API/Interfaces/IReversibleEffect.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/API/Interfaces/IReversibleEffect.cs new file mode 100644 index 00000000..22be1c71 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/API/Interfaces/IReversibleEffect.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.Examples.API.Interfaces +{ + internal interface IReversibleEffect + { + string VoiceLineDeactivate { get; } + string VoiceLineDeactivateTranslated { get; } + sbyte MalfunctionDeactivation { get; } + void DeactivateEffect(); + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/API/MalfunctionEffects/AutoNuke.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/API/MalfunctionEffects/AutoNuke.cs new file mode 100644 index 00000000..f116532a --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/API/MalfunctionEffects/AutoNuke.cs @@ -0,0 +1,51 @@ +using Exiled.API.Features; +using KE.GlobalEventFramework.Examples.API.Feature.mf; +using KE.GlobalEventFramework.Examples.GE; +using MEC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.Examples.API.MalfunctionEffects +{ + internal class AutoNuke : MalfunctionEffect + { + public override string Name { get; } = "Automatic Warhead"; + public override string VoiceLine { get; } = "Malfunctions levels above . 90 percent . . starting emergency warhead"; + public override string VoiceLineTranslated { get; } = "Malfunctions levels above 90%, starting emergency warhead"; + public override sbyte MalfunctionActivation { get; } = 90; + + + public sbyte MalfunctionDeactivation { get; } = 85; + private CoroutineHandle _checkNuke; + + public override void ActivateEffect() + { + if (!Warhead.IsInProgress) + { + Warhead.Start(); + Warhead.IsLocked = true; + Timing.KillCoroutines(_checkNuke); + _checkNuke = Timing.RunCoroutine(CheckNuke()); + } + } + + public IEnumerator CheckNuke() + { + while (Warhead.IsInProgress) + { + yield return Timing.WaitForSeconds(5); + var malfunction = SystemMalfunction.Malfunction.Malfunction; + if (malfunction <= 85) + { + Log.Debug($"Malfunction low enough ({malfunction}) disabling the nuke"); + Warhead.IsLocked = false; + Warhead.Stop(); + break; + } + } + } + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/API/MalfunctionEffects/ForceDecon.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/API/MalfunctionEffects/ForceDecon.cs new file mode 100644 index 00000000..1a33fdb4 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/API/MalfunctionEffects/ForceDecon.cs @@ -0,0 +1,50 @@ + + +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Doors; +using KE.GlobalEventFramework.Examples.API.Feature.mf; +using MEC; +using System.Linq; + +namespace KE.GlobalEventFramework.Examples.API.MalfunctionEffects +{ + internal class ForceDecon : MalfunctionEffect + { + public override string Name { get; } = "Forced Decontamination"; + public override string VoiceLine { get; } = "Malfunctions levels above . 25 percent . . decontamination of Light Containment Zone in . 30 seconds"; + public override string VoiceLineTranslated { get; } = "Malfunctions levels above 25%, decontamination of Light Containment Zone in 30 seconds"; + public override sbyte MalfunctionActivation { get; } = 25; + + public override void ActivateEffect() + { + if (Exiled.API.Features.Map.IsLczDecontaminated || !Exiled.API.Features.Map.IsLczDecontaminated) return; + Door.List.ToList().ForEach(d => + { + if (d.Zone == ZoneType.LightContainment) + { + if (!d.IsElevator) + { + d.ChangeLock(DoorLockType.DecontEvacuate); + d.IsOpen = true; + } + + } + }); + Timing.CallDelayed(30, () => + { + Exiled.API.Features.Map.StartDecontamination(); + + foreach (Door d in Door.List) + { + if (d.Type == DoorType.ElevatorLczA || d.Type == DoorType.ElevatorLczB) + { + d.Lock(DoorLockType.DecontEvacuate); + d.IsOpen = false; + } + } + }); + } + + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/API/MalfunctionEffects/Locks.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/API/MalfunctionEffects/Locks.cs new file mode 100644 index 00000000..50b31266 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/API/MalfunctionEffects/Locks.cs @@ -0,0 +1,50 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Doors; +using KE.GlobalEventFramework.Examples.API.Feature.mf; +using KE.GlobalEventFramework.Examples.API.Interfaces; +using MEC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Utils.NonAllocLINQ; + +namespace KE.GlobalEventFramework.Examples.API.MalfunctionEffects +{ + internal class Locks : MalfunctionEffect, IReversibleEffect + { + public override string Name { get; } = "Door locks terminated"; + public override string VoiceLine { get; } = "Malfunctions levels above . 50 percent . . terminating all door locks"; + public override string VoiceLineTranslated { get; } = "Malfunctions levels above 50%, terminating all door locks"; + public override sbyte MalfunctionActivation { get; } = 50; + + public string VoiceLineDeactivate { get; } = "Malfunctions back to more stable levels, reputting all door locks"; + public string VoiceLineDeactivateTranslated { get; } = "Malfunctions back to more stable levels, reputting all door locks"; + public sbyte MalfunctionDeactivation { get; } = 40; + + + private Dictionary doorkeys = new Dictionary(); + public override void ActivateEffect() + { + Door.List.ToList().ForEach(d => + { + if (d.IsKeycardDoor) + { + doorkeys.Add(d, d.KeycardPermissions); + d.KeycardPermissions = KeycardPermissions.None; + } + }); + } + + public void DeactivateEffect() + { + doorkeys.ForEach(dk => + { + dk.Key.KeycardPermissions = dk.Value; + }); + } + + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/GE/Blitz.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/Blitz.cs new file mode 100644 index 00000000..a5a6ad0d --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/Blitz.cs @@ -0,0 +1,57 @@ +using Exiled.API.Features; +using Exiled.API.Features.Items; +using KE.GlobalEventFramework.GEFE.API.Enums; +using KE.GlobalEventFramework.GEFE.API.Features; +using KE.GlobalEventFramework.GEFE.API.Interfaces; +using MEC; +using System.Collections.Generic; + +namespace KE.GlobalEventFramework.Examples.GE +{ + /// + /// Spawn fused grenades in random rooms in the map + /// + public class Blitz : GlobalEvent, IAsyncStart + { + /// + public override uint Id { get; set; } = 1046; + /// + public override string Name { get; set; } = "Blitz"; + /// + public override string Description { get; } = "La Luftwaffe arrive!"; + public override string[] AltDescription => new string[] + { + "Attention aux bombardements!" + }; + /// + public override int WeightedChance => 4; + /// + /// The cooldown between 2 spawn of grenades + /// + public int Cooldown { get; set; } = 120; + /// + /// The number of grenades in each spawn + /// + public int NbGrenadeSpawned { get; set; } = 5; + + public override ImpactLevel ImpactLevel => ImpactLevel.Low; + + + /// + public IEnumerator Start() + { + while (!Round.IsEnded) + { + Log.Debug("waiting"); + yield return Timing.WaitForSeconds(Cooldown); + for (int i = 0; i < NbGrenadeSpawned; i++) + { + ((ExplosiveGrenade)Item.Create(ItemType.GrenadeHE)).SpawnActive(Room.Random().Position); + } + + + } + } + + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/GE/CassieGoCrazy.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/CassieGoCrazy.cs new file mode 100644 index 00000000..486b21a8 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/CassieGoCrazy.cs @@ -0,0 +1,97 @@ +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Items; +using Exiled.Events.EventArgs.Player; +using KE.GlobalEventFramework.Examples.API.Feature.CassieGoCrazy; +using KE.GlobalEventFramework.GEFE.API.Enums; +using KE.GlobalEventFramework.GEFE.API.Features; +using KE.GlobalEventFramework.GEFE.API.Features.Hints; +using KE.GlobalEventFramework.GEFE.API.Interfaces; +using KE.Utils.API.Displays.DisplayMeow; +using KE.Utils.API.Interfaces; +using MEC; +using System.Collections.Generic; +using System.Linq; + +namespace KE.GlobalEventFramework.Examples.GE +{ + /// + /// Spawn fused grenades in random rooms in the map + /// + /*public class CassieGoCrazy : GlobalEvent, IStart + { + /// + public override uint Id { get; set; } = 1049; + /// + public override string Name { get; set; } = "Cassie Go Crazy"; + /// + public override string Description => "Crazy Cassie !"; + /// + public override int WeightedChance { get; set; } = 1; + + /// + /// The cooldown between 2 cassie event + /// + public int Cooldown { get; set; } = 380; + + + + public override ImpactLevel ImpactLevel => ImpactLevel.Low; + + private List Effects; + + protected override void SubscribeEvents() + { + Effects = new() + { + new AntagonisticCassie(), + new ChangeColor(), + new NoLight(), + new SadCassie(), + new WarheadEffect() + }; + + foreach (ICGCEffect effect in Effects) + { + if (effect is IUsingEvents @event) + { + @event.SubscribeEvents(); + } + } + + } + + + protected override void UnsubscribeEvents() + { + foreach (ICGCEffect effect in Effects) + { + if (effect is IUsingEvents @event) + { + @event.UnsubscribeEvents(); + } + } + } + + + + /// + /// Starts a coroutine to perform random actions during the game. + /// + /// A coroutine that runs until the game round ends. + /// + public IEnumerator Start() + { + while (!Round.IsEnded) + { + Log.Debug("waiting"); + yield return Timing.WaitForSeconds(Cooldown); + + Effects.GetRandomValue().Effect(); + + + } + } + + }*/ +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/GE/ChangedItemEffect.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/ChangedItemEffect.cs new file mode 100644 index 00000000..af10c177 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/ChangedItemEffect.cs @@ -0,0 +1,180 @@ +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Items; +using Exiled.CustomItems.API.Features; +using Exiled.Events.EventArgs.Player; +using Exiled.Events.EventArgs.Scp330; +using InventorySystem.Items.Usables.Scp330; +using KE.GlobalEventFramework.GEFE.API.Enums; +using KE.GlobalEventFramework.GEFE.API.Features; +using KE.GlobalEventFramework.GEFE.API.Features.Hints; +using KE.GlobalEventFramework.GEFE.API.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace KE.GlobalEventFramework.Examples.GE +{ + public class ChangedItemEffect : GlobalEvent,IStart ,IEvent + { + + public override uint Id { get; set; } = 1080; + /// + public override string Name { get; set; } = "SwitchItemEffect"; + /// + public override string Description { get; } = "Les effets des items ont changé"; + public override string[] AltDescription => new string[] + { + "Roulette russe" + }; + /// + public override int WeightedChance { get; set; } = 0; + + public Dictionary newEffects; + public Dictionary newCandyEffects; + + + + public override ImpactLevel ImpactLevel => ImpactLevel.High; + + public void Start() + { + + + + ChangeItemsEffect(); + ChangeCandyEffect(); + } + + private void ChangeItemsEffect() + { + + List usableList = new List(); + + foreach (var item in (ItemType[])Enum.GetValues(typeof(ItemType))) + { + if (IsUsable(item) && item != ItemType.SCP330) + { + Log.Debug("adding : " + item); + usableList.Add(item); + } + + + } + newEffects = new Dictionary(); + + int switchNumber = UnityEngine.Random.Range(1, usableList.Count); + + for (int i = 0; i < usableList.Count; i++) + { + newEffects.Add(usableList[i], usableList[(i + switchNumber) % usableList.Count]); + } + + } + + private void ChangeCandyEffect() + { + List candys = new List(); + foreach (var item in (CandyKindID[])Enum.GetValues(typeof(CandyKindID))) + { + if (item != CandyKindID.None) + { + Log.Debug("adding : " + item); + candys.Add(item); + } + } + + newCandyEffects = new Dictionary(); + + int switchNumber = UnityEngine.Random.Range(1, candys.Count); + + for (int i = 0; i < candys.Count; i++) + { + + CandyKindID old = candys[i]; + CandyKindID newCandy = candys[(i + switchNumber) % candys.Count]; + + Log.Debug($"old = {old} ; new = {newCandy}"); + + newCandyEffects.Add(old, newCandy); + } + } + + + + + + public void SubscribeEvent() + { + Exiled.Events.Handlers.Player.UsingItemCompleted += OnUsingItemCompleted; + Exiled.Events.Handlers.Scp330.EatingScp330 += OnEatingScp330; + + } + public void UnsubscribeEvent() + { + Exiled.Events.Handlers.Player.UsingItemCompleted -= OnUsingItemCompleted; + Exiled.Events.Handlers.Scp330.EatingScp330 -= OnEatingScp330; + } + + public static bool IsUsable(ItemType type) + { + try + { + if (type.IsMedical() || type.IsThrowable() || type.IsScp()) return true; + return false; + } + catch (Exception) + { + return false; + } + + } + + private void OnEatingScp330(EatingScp330EventArgs ev) + { + if (!newCandyEffects.TryGetValue(ev.Candy.Kind, out CandyKindID newCandy)) + { + Log.Debug("not found"); + return; + } + + + ev.IsAllowed = false; + + ev.Scp330.RemoveCandy(ev.Candy.Kind); + Log.Debug($"old candy = {ev.Candy.Kind}"); + Log.Debug($"new candy = {newCandy}"); + + + + Scp330.AvailableCandies[newCandy].ServerApplyEffects(ev.Player.ReferenceHub); + } + + + private void OnUsingItemCompleted(UsingItemCompletedEventArgs ev) + { + if (CustomItem.TryGet(ev.Item, out var _)) return; + + if (!newEffects.ContainsKey(ev.Item.Type)) return; + + if(!newEffects.TryGetValue(ev.Usable.Type,out ItemType newUsable)) + { + Log.Debug("not found"); + return; + } + + + Log.Debug($"item used : {ev.Usable.Type}, item effect : {newUsable}"); + ev.IsAllowed = false; + Usable use = Usable.Create(newUsable, ev.Player) as Usable; + if (use is null) + { + Log.Error("Usable null stopping"); + return; + } + + ev.Item.Destroy(); + use.Use(ev.Player); + } + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/GE/Impostor.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/Impostor.cs new file mode 100644 index 00000000..4b8f1369 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/Impostor.cs @@ -0,0 +1,87 @@ +using Player = Exiled.API.Features.Player; +using System.Collections.Generic; +using KE.GlobalEventFramework.GEFE.API.Interfaces; +using MEC; +using System.Linq; +using Exiled.API.Extensions; +using Exiled.API.Features; +using KE.GlobalEventFramework.GEFE.API.Features; +using KE.GlobalEventFramework.GEFE.API.Features.Hints; +using KE.GlobalEventFramework.GEFE.API.Enums; + +namespace KE.GlobalEventFramework.Examples.GE +{ + public class Impostor : GlobalEvent, IAsyncStart + { + public override uint Id { get; set; } = 1044; + public override string Name { get; set; } = "Impostor"; + public override string Description { get; } = "Ne vous fiez pas aux apparences !"; + public override string[] AltDescription => new string[] + { + "sussy" + }; + public override int WeightedChance { get; set; } = 2; + + public override ImpactLevel ImpactLevel => ImpactLevel.High; + + public IEnumerator Start() + { + while (!Round.IsEnded) + { + yield return Timing.WaitForSeconds(UnityEngine.Random.Range(180, 300)); + + ChangingHumanApparence(); + ChangingSCPApparence(); + } + } + + private void ChangingHumanApparence() + { + // Liste des joueurs vivants + List playerInServer = Player.List.Where(p => !p.IsNPC && p.IsAlive && !p.IsScp).ToList(); + + if (playerInServer.Count < 2) + { + Log.Warn("Pas assez de joueurs vivants pour effectuer un échange !"); + return; + } + + // Debug : afficher la liste initiale des joueurs avec leurs rôles + Log.Debug("===== Liste des joueurs avant permutation ====="); + + playerInServer.ForEach(p => Log.Debug($"{p.Nickname} ({p.Role})")); + + // Mélanger la liste des joueurs + playerInServer.ShuffleList(); + + // Copier les données actuelles des joueurs + var originalNicknames = playerInServer.Select(p => p.Nickname).ToList(); + var originalRoles = playerInServer.Select(p => p.Role).ToList(); + + // Permutation circulaire des rôles et pseudonymes + for (int i = 0; i < playerInServer.Count; i++) + { + int nextIndex = (i + 1) % playerInServer.Count; + playerInServer[i].ChangeAppearance(originalRoles[nextIndex]); + playerInServer[i].DisplayNickname = originalNicknames[nextIndex]; + } + + // Debug : afficher les correspondances après permutation + Log.Debug("===== Correspondances après permutation ====="); + for (int i = 0; i < playerInServer.Count; i++) + { + int nextIndex = (i + 1) % playerInServer.Count; + Log.Debug($"{originalNicknames[i]} ({originalRoles[i]}) -> {originalNicknames[nextIndex]} ({originalRoles[nextIndex]})"); + } + } + + private void ChangingSCPApparence() + { + Player randomHumanPlayer = Player.List.Where(p => !p.IsNPC && p.IsAlive && !p.IsScp).ToList().GetRandomValue(); + + Player randomScpPlayer = Player.List.Where(p => !p.IsNPC && p.IsAlive && p.IsScp).ToList().GetRandomValue(); + + randomScpPlayer.ChangeAppearance(randomHumanPlayer.Role); + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/GE/ItemRain.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/ItemRain.cs new file mode 100644 index 00000000..f1831bb7 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/ItemRain.cs @@ -0,0 +1,59 @@ +using Exiled.API.Features; +using Exiled.API.Features.Items; +using KE.GlobalEventFramework.GEFE.API.Features; +using KE.GlobalEventFramework.GEFE.API.Interfaces; +using MEC; +using System; +using System.Collections.Generic; + +namespace KE.GlobalEventFramework.Examples.GE +{ + /// + /// Randomly item will fall from the sky + /// + public class ItemRain : GlobalEvent, IAsyncStart + { + /// + public override uint Id { get; set; } = 1090; + /// + public override string Name { get; set; } = "ItemRain"; + /// + public override string Description { get; } = "Il pleut des items !!"; + /// + public override int WeightedChance => 1; + + public int Cooldown = 120; + public int NbItemSpawned = 5; + + public static readonly HashSet BlacklistedItems = new HashSet() + { + ItemType.None,ItemType.Coal,ItemType.SpecialCoal,ItemType.Snowball,ItemType.SCP1507Tape,ItemType.Scp021J,ItemType.DebugRagdollMover, + ItemType.KeycardCustomManagement,ItemType.KeycardCustomMetalCase,ItemType.KeycardCustomSite02,ItemType.KeycardCustomTaskForce, + }; + public IEnumerator Start() + { + while (!Round.IsEnded) + { + Log.Debug("waiting"); + Array values = Enum.GetValues(typeof(ItemType)); + yield return Timing.WaitForSeconds(Cooldown); + + for (int i = 0; i < NbItemSpawned; i++) + { + + ItemType itemType = (ItemType)values.GetValue(UnityEngine.Random.Range(0, values.Length)); + + if (CheckItemType(itemType)) continue; + + Item.Create(itemType).CreatePickup(Room.Random().Position); + } + } + } + + + private bool CheckItemType(ItemType itemType) + { + return !BlacklistedItems.Contains(itemType); + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/GE/KIWIS.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/KIWIS.cs new file mode 100644 index 00000000..2d49406b --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/KIWIS.cs @@ -0,0 +1,54 @@ +using Exiled.API.Features; +using MEC; +using PlayerRoles; +using Utils.NonAllocLINQ; +using System.Collections.Generic; +using System.Linq; +using KE.GlobalEventFramework.GEFE.API.Interfaces; +using KE.GlobalEventFramework.GEFE.API.Features; +using KE.GlobalEventFramework.GEFE.API.Features.Hints; +using KE.GlobalEventFramework.GEFE.API.Enums; + +namespace KE.GlobalEventFramework.Examples.GE +{ + /// + /// The scp start with 2/3 of it's life and end with 4/3 of it's vanilla life + /// + public class KIWIS : GlobalEvent, IAsyncStart + { + /// + public override uint Id { get; set; } = 1047; + /// + public override string Name { get; set; } = "KIWIS"; + /// + public override string Description { get; } = "Kill It While It's Small"; + public override ImpactLevel ImpactLevel => ImpactLevel.Medium; + /// + public override int WeightedChance { get; set; } = 1; + /// + public IEnumerator Start() + { + var listScp = Player.List.ToList().Where(p => p.IsScp && p.Role.Type != RoleTypeId.Scp0492).ToList().ToDictionary(p => p, p => p.MaxHealth/3); + + + yield return Timing.WaitUntilTrue(() => Round.ElapsedTime.TotalMinutes >= 15); + + listScp = listScp.Where(p => p.Key.IsScp && p.Key.Role.Type != RoleTypeId.Scp0492).ToList().ToDictionary(p => p.Key, p => p.Value); + listScp.ForEach(k => { + k.Key.MaxHealth += k.Value; + k.Key.Heal(k.Value); + }); + + yield return Timing.WaitUntilTrue(() => Round.ElapsedTime.TotalMinutes >= 30 || Warhead.IsDetonated); + listScp = listScp.Where(p => p.Key.IsScp && p.Key.Role.Type != RoleTypeId.Scp0492).ToList().ToDictionary(p => p.Key, p => p.Value); + listScp.ForEach(k => { + k.Key.MaxHealth += k.Value; + k.Key.Heal(k.Value); + }); + + yield return 0; + + } + + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/GE/Kaboom.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/Kaboom.cs new file mode 100644 index 00000000..a688a0b7 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/Kaboom.cs @@ -0,0 +1,106 @@ +using Exiled.API.Enums; +using Exiled.API.Features.Items; +using Exiled.API.Features; +using Exiled.Events.EventArgs.Player; +using KE.GlobalEventFramework.GEFE.API.Interfaces; +using KE.GlobalEventFramework.GEFE.API.Features; +using KE.GlobalEventFramework.GEFE.API.Features.Hints; +using KE.GlobalEventFramework.GEFE.API.Enums; +using Exiled.API.Features.Doors; +namespace KE.GlobalEventFramework.Examples.GE +{ + public class Kaboom : GlobalEvent, IEvent + { + /// + public override uint Id { get; set; } = 1052; + /// + public override string Name { get; set; } = "Kaboom"; + /// + public override string Description { get; } = "Les portes sont piegés attention!"; + + public override string[] AltDescription => new string[] + { + "La guerrilla est présente" + }; + /// + public override int WeightedChance { get; set; } = 2; + + public override ImpactLevel ImpactLevel => ImpactLevel.Medium; + + public const float BaseChanceElevator = .05f; + private float _chanceElevator; + public float ChanceElevator + { + get{ return _chanceElevator;} + set + { + if (value >= 0 && value <= 1) + _chanceElevator = value; + else + _chanceElevator = BaseChanceElevator; + } + } + public const float BaseChanceGate = .25f; + private float _chanceGate; + + public float ChanceGate + { + get { return _chanceGate;} + set + { + if (value >= 0 && value <= 1) + _chanceGate = value; + else + _chanceGate = BaseChanceGate; + } + } + + public const float BaseChanceDoor = .1f; + + private float _chanceDoor; + public float ChanceDoor + { + get { return _chanceDoor; } + set + { + if (value > 0 && value <= 1) + _chanceDoor = value; + else + _chanceDoor= BaseChanceDoor; + } + } + + + + /// + public void SubscribeEvent() + { + Exiled.Events.Handlers.Player.InteractingDoor += OnInteractingDoor; + } + public void UnsubscribeEvent() + { + Exiled.Events.Handlers.Player.InteractingDoor -= OnInteractingDoor; + } + + private void OnInteractingDoor(InteractingDoorEventArgs ev) + { + float random = UnityEngine.Random.value; + Door door = ev.Door; + + if (!door.IsOpen) return; + + + Log.Debug($"i love debugging random value : {random}"); + if ((door.IsElevator && random < .05f) || + (door.IsGate && random < .5f) || + (door.IsDamageable && random < .1f)) + { + ExplosiveGrenade grenade = ((ExplosiveGrenade)Item.Create(ItemType.GrenadeHE)); + grenade.ScpDamageMultiplier = 0.5f; + + + grenade.SpawnActive(ev.Player.Position); + } + } + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/GE/Librarby.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/Librarby.cs new file mode 100644 index 00000000..b9ebe63a --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/Librarby.cs @@ -0,0 +1,70 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Items; +using Exiled.Events.EventArgs.Player; +using KE.GlobalEventFramework.GEFE.API.Features; +using KE.GlobalEventFramework.GEFE.API.Interfaces; +using MEC; +using PlayerRoles; +using System; +using System.Collections.Generic; +using UnityEngine; +using VoiceChat.Codec; + +namespace KE.GlobalEventFramework.Examples.GE +{ + /// + /// You cannot talk to loud + /// + public class Librarby : GlobalEvent, IEvent, IChanceRedactable + { + /// + public override uint Id { get; set; } = 1091; + /// + public override string Name { get; set; } = "Librarby"; + /// + public override string Description { get; } = "Ne parlez pas trop fort sinon vous subirez les conséquences !"; + /// + public override int WeightedChance => 1; + + public float ChanceRedacted => 0; + + private float MaxVolume = 0.7f; + + private OpusDecoder _decoder = new OpusDecoder(); + private float[] _pcmBuffer = new float[1920]; + + public void SubscribeEvent() + { + Exiled.Events.Handlers.Player.VoiceChatting += PlayerYapping; + } + + public void UnsubscribeEvent() + { + Exiled.Events.Handlers.Player.VoiceChatting -= PlayerYapping; + } + + private void PlayerYapping(VoiceChattingEventArgs ev) + { + if (ev.Player.Role.Side == Side.Scp || ev.Player.Role == RoleTypeId.Tutorial) return; + + int decodedLength = _decoder.Decode(ev.VoiceMessage.Data, ev.VoiceMessage.DataLength, _pcmBuffer); + float maxVolume = 0f; + + for (int i = 0; i < decodedLength; i++) + { + float absValue = Mathf.Abs(_pcmBuffer[i]); + + if (absValue > maxVolume) + { + maxVolume = absValue; + } + } + + if (maxVolume > MaxVolume) + { + ev.Player.Explode(); + } + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/GE/OpenBar.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/OpenBar.cs new file mode 100644 index 00000000..47eed3fa --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/OpenBar.cs @@ -0,0 +1,98 @@ +using Exiled.API.Features.Doors; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.Events.EventArgs.Map; +using KE.GlobalEventFramework.GEFE.API.Interfaces; +using KE.GlobalEventFramework.GEFE.API.Features; +using KE.GlobalEventFramework.GEFE.API.Features.Hints; +using KE.Map.Others.BlackoutNDoor.Events.EventArgs; +using Exiled.API.Interfaces; +namespace KE.GlobalEventFramework.Examples.GE +{ + public class OpenBar : GlobalEvent, IStart, IEvent + { + public override uint Id { get; set; } = 1048; + public override string Name { get; set; } = "OpenBar"; + public override string Description { get; } = "j'espère que vous avez pas prévu de kampé"; + public override int WeightedChance { get; set; } = 3; + public override uint[] IncompatibleEvents { get; set; } = { 1 }; + + public int NbAdditionalDoor = 3; + + public static readonly HashSet DoorsToUnlock = new HashSet() + { + DoorType.GateA, DoorType.GateB, + }; + + public static readonly HashSet DoorsToMaybeUnlock = new HashSet() + { + DoorType.HczArmory, DoorType.HIDChamber,DoorType.Intercom,DoorType.Scp049Armory,DoorType.Scp096,DoorType.Scp106Primary,DoorType.Scp106Secondary,DoorType.Scp330,DoorType.Scp914Gate + }; + + private IEnumerable doorsLocked; + + public void Start() + { + List door = DoorsToMaybeUnlock.ToList(); + List result = new List(); + + for (int i = 0; i < NbAdditionalDoor; i++) + { + result.Add(door.PullRandomItem()); + } + result.AddRange(DoorsToUnlock); + + doorsLocked = Door.List.Where(d => result.Contains(d.Type)); + + UnlockAndOpen(doorsLocked); + + } + + private void UnlockAndOpen(IEnumerable doors) + { + + foreach(Door door in doors) + { + if(door is CheckpointDoor && door is IDamageableDoor damage) + { + damage.Break(Interactables.Interobjects.DoorUtils.DoorDamageType.ServerCommand); + } + else + { + UnlockAndOpen(door); + } + } + } + + + private void UnlockAndOpen(Door door) + { + door.IsOpen = true; + door.ChangeLock(DoorLockType.NoPower); + } + + public void SubscribeEvent() + { + Map.Others.BlackoutNDoor.Events.Handlers.DoorStuckHandler.DoorStucking += OnDoorStucking; + } + + public void UnsubscribeEvent() + { + Map.Others.BlackoutNDoor.Events.Handlers.DoorStuckHandler.DoorStucking -= OnDoorStucking; + } + + + private void OnDoorStucking(DoorStuckEventArgs ev) + { + + int result = ev.Doors.RemoveWhere(door => doorsLocked.Contains(door)); + + Log.Info(result); + } + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/GE/R.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/R.cs new file mode 100644 index 00000000..800ffc5f --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/R.cs @@ -0,0 +1,23 @@ +using KE.GlobalEventFramework.GEFE.API.Enums; +using KE.GlobalEventFramework.GEFE.API.Features; +using KE.GlobalEventFramework.GEFE.API.Features.Hints; +namespace KE.GlobalEventFramework.Examples.GE +{ + /// + /// Literally does nothing + /// + public class R : GlobalEvent + { + /// + public override uint Id { get; set; } = 0; + /// + public override string Name { get; set; } = "nothing"; + /// + public override string Description { get; } = "y'a r"; + /// + public override int WeightedChance => 10; + public override ImpactLevel ImpactLevel => ImpactLevel.VeryLow; + + + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/GE/RandomSpawn.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/RandomSpawn.cs new file mode 100644 index 00000000..69265bfc --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/RandomSpawn.cs @@ -0,0 +1,55 @@ +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using KE.GlobalEventFramework.GEFE.API.Enums; +using KE.GlobalEventFramework.GEFE.API.Features; +using KE.GlobalEventFramework.GEFE.API.Features.Hints; +using KE.GlobalEventFramework.GEFE.API.Interfaces; +using KE.Utils.Extensions; +using PlayerRoles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.Examples.GE +{ + /// + /// All spawn are random at the start of the game (NTF & Chaos not included) + /// Note: all role spawn with each other except SCPs + /// + public class RandomSpawn : GlobalEvent, IStart + { + /// + public override uint Id { get; set; } = 1043; + /// + public override string Name { get; set; } = "RandomSpawn"; + /// + public override string Description { get; } = "Les spawns sont random"; + /// + public override int WeightedChance { get; set; } = 5; + public override ImpactLevel ImpactLevel => ImpactLevel.High; + public IEnumerable BlacklistedRooms { get; } = new List() { RoomType.EzGateA, RoomType.EzGateB }; + /// + public void Start() + { + Room room; + foreach (RoleTypeId r in Enum.GetValues(typeof(RoleTypeId))) + { + room = Room.List.GetRandomValue(r => !BlacklistedRooms.Contains(r.Type)); + foreach (Player p in Player.List) + { + + if (p.Role == r) + { + p.Teleport(room.GetValidPosition()); + } + + } + + } + } + + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/GE/Rollback.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/Rollback.cs new file mode 100644 index 00000000..99daf180 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/Rollback.cs @@ -0,0 +1,75 @@ +using System.Collections.Generic; +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Interfaces; +using KE.GlobalEventFramework.GEFE.API.Enums; +using KE.GlobalEventFramework.GEFE.API.Features; +using KE.GlobalEventFramework.GEFE.API.Features.Hints; +using KE.GlobalEventFramework.GEFE.API.Interfaces; +using KE.Utils.Extensions; +using MapGeneration; +using MEC; +using PlayerRoles.FirstPersonControl; +using UnityEngine; + + +namespace KE.GlobalEventFramework.Examples.GE +{ + public class RollBack: GlobalEvent, IAsyncStart + { + /// + public override uint Id { get; set; } = 10800; + /// + public override string Name { get; set; } = "RollBack"; + /// + public override string Description { get; } = "Shit the server is lagging"; + public override string[] AltDescription => new string[] + { + "Pov Omer" + }; + /// + public override int WeightedChance { get; set; } = 1; + + public static readonly float RefreshRate = 30; + public static float Luck = 5; + public override ImpactLevel ImpactLevel => ImpactLevel.High; + + private Dictionary playerpos = new Dictionary(); + + /// + public IEnumerator Start() + { + bool luck; + while (base.IsActive) + { + yield return Timing.WaitForSeconds(RefreshRate); + luck = Luck > Random.Range(0f,100f); + foreach(Player p in Player.List) + { + + if (luck) + { + p.Teleport(playerpos[p].Item1); + p.Rotation = playerpos[p].Item2; + } + + if (p.IsAlive && Lift.Get(p.Position) is null && p.Zone.IsSafe()) + { + playerpos[p] = (p.Position, p.Rotation); + } + + + + + } + + } + + + } + + + + + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/GE/Shuffle.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/Shuffle.cs new file mode 100644 index 00000000..38170f92 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/Shuffle.cs @@ -0,0 +1,110 @@ +using Exiled.API.Features; +using MEC; +using PlayerHandler = Exiled.Events.Handlers.Player; +using Exiled.Events.EventArgs.Player; +using UnityEngine; +using System.Collections.Generic; +using System.Linq; +using KE.GlobalEventFramework.GEFE.API.Interfaces; +using KE.GlobalEventFramework.GEFE.API.Features; +using KE.GlobalEventFramework.GEFE.API.Features.Hints; +using KE.GlobalEventFramework.GEFE.API.Enums; + +namespace KE.GlobalEventFramework.Examples.GE +{ + /// + /// Every some amount of time all player take the position of another + /// + public class Shuffle : GlobalEvent, IAsyncStart,IEvent + { + /// + public override uint Id { get; set; } = 1045; + /// + public override string Name { get; set; } = "Shuffle"; + /// + public override string Description { get; } = "et ça fait roomba café dans le scp"; + /// + public override int WeightedChance { get; set; } = 0; + public override ImpactLevel ImpactLevel => ImpactLevel.VeryHigh; + private List players; + private List pos; + /// + public IEnumerator Start() + { + this.players = Player.List.ToList().Where(p => !p.IsNPC).ToList(); + this.players.ShuffleList(); + pos = new List(players.Count); + for (int i = 0; i < players.Count; i++) + { + pos.Add(Vector3.zero); + } + Log.Debug($"before while"); + while (!Round.IsEnded) + { + + Log.Debug($"waiting for {GetType().Name}"); + yield return Timing.WaitForSeconds(UnityEngine.Random.Range(300, 900)); + for (int i = 0; i < this.players.Count; i++) + { + Log.Debug($"old position of player {this.players[i]} : {this.players[i].Position}"); + var player = this.players[i]; + pos[i] = player.Position; + Log.Debug("------"); + } + + ShiftLeft(this.players); + Log.Debug("shifted players"); + for (int i = 0; i < this.players.Count; i++) + { + Log.Debug("before tp"); + this.players[i].Teleport(pos[i]); + Log.Debug($"new position of player {this.players[i]} : {this.players[i].Position}"); + } + Log.Debug($"tp player"); + for (int i = 0; i < players.Count; i++) + { + pos.Add(Vector3.zero); + } + Log.Debug($"cleared"); + } + } + /// + public void SubscribeEvent() + { + PlayerHandler.Joined += OnJoined; + } + /// + public void UnsubscribeEvent() + { + PlayerHandler.Joined -= OnJoined; + } + + private void OnJoined(JoinedEventArgs ev) + { + if (!ev.Player.IsNPC) + { + this.players.Add(ev.Player); + this.pos.Add(ev.Player.Position); + } + } + /// + /// Shift a List to the left + /// + /// the type of the List + /// the List to shift + private void ShiftLeft(List lst) + { + if (lst.Count > 0) + { + T firstElement = lst[0]; + for (int i = 1; i < lst.Count; i++) + { + lst[i - 1] = lst[i]; + } + lst[lst.Count - 1] = firstElement; + } + } + + + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/GE/Speed.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/Speed.cs new file mode 100644 index 00000000..6854c8db --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/Speed.cs @@ -0,0 +1,152 @@ +using CustomPlayerEffects; +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Items; +using Exiled.Events.EventArgs.Player; +using Exiled.Events.EventArgs.Scp096; +using Exiled.Events.EventArgs.Scp173; +using Exiled.Events.EventArgs.Scp939; +using KE.GlobalEventFramework.GEFE.API.Enums; +using KE.GlobalEventFramework.GEFE.API.Features; +using KE.GlobalEventFramework.GEFE.API.Features.Hints; +using KE.GlobalEventFramework.GEFE.API.Interfaces; +using MEC; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using Utils.NonAllocLINQ; +using PlayerHandler = Exiled.Events.Handlers.Player; + +namespace KE.GlobalEventFramework.Examples.GE +{ + /// + /// Everyone has a movement boost effect (stackable) + /// Maybe inspired by Dr Bright's Mayhem + /// + public class Speed : GlobalEvent, IAsyncStart, IEvent + { + public override ImpactLevel ImpactLevel => ImpactLevel.VeryHigh; + + /// + public override uint Id { get; set; } = 1042; + /// + public override string Name { get; set; } = "Speed"; + /// + public override string Description { get; } = "Gas! gas! gas!"; + + public override string[] AltDescription => new string[] + { + "Super instinct!", + "Mayhem mode activated", + }; + /// + public override int WeightedChance { get; set; } = 5; + /// + /// intensity of the movement boost effect + /// + public static byte MovementBoost { get; set; } = 100; + /// + public IEnumerator Start() + { + yield return Timing.WaitForSeconds(1); + foreach (Player player in Player.Enumerable) + { + GiveEffect(player); + } + + } + /// + public void SubscribeEvent() + { + PlayerHandler.ChangingRole += ReactivateEffectSpawn; + Exiled.Events.Handlers.Scp173.Blinking += SpeedyNut; + Exiled.Events.Handlers.Scp939.Lunging += OnLunging; + Exiled.Events.Handlers.Scp096.Charging += OnCharging; + Exiled.Events.Handlers.Scp939.Clawed += OnClawed; + } + /// + public void UnsubscribeEvent() + { + PlayerHandler.ChangingRole -= ReactivateEffectSpawn; + Exiled.Events.Handlers.Scp173.Blinking -= SpeedyNut; + Exiled.Events.Handlers.Scp939.Lunging -= OnLunging; + Exiled.Events.Handlers.Scp096.Charging -= OnCharging; + Exiled.Events.Handlers.Scp939.Clawed -= OnClawed; + + foreach (Player player in Player.Enumerable) + { + player.DisableEffect(); + } + } + /// + /// Decrease the blink cooldown of SCP-173 + /// + public static void SpeedyNut(BlinkingEventArgs ev) + { + ev.BlinkCooldown = ev.BlinkCooldown/3; + } + + /// + /// Reactivate the effect at each role changement + /// + public static void ReactivateEffectSpawn(ChangingRoleEventArgs ev) + { + + GiveEffect(ev.Player); + } + + public static void GiveEffect(Player player) + { + Timing.CallDelayed(.1f, () => player.EnableEffect(MovementBoost, 999999999, true)); + } + + public static void OnClawed(ClawedEventArgs ev) + { + if(ev.Player.TryGetEffect(out _)) + { + ev.Player.DisableEffect(); + } + } + + + public static void OnLunging(LungingEventArgs ev) + { + Timing.CallDelayed(.70f, delegate + { + ExplosiveGrenade grenade = (ExplosiveGrenade)Item.Create(ItemType.GrenadeHE); + grenade.ScpDamageMultiplier = 0; + grenade.FuseTime = .1f; + grenade.SpawnActive(ev.Player.Position, ev.Player); + }); + + ev.Player.EnableEffect(10); + } + + + public static void OnCharging(ChargingEventArgs ev) + { + if (!ev.IsAllowed) return; + + Timing.RunCoroutine(SpawnGrenade(ev.Player)); + + } + + + private static IEnumerator SpawnGrenade(Player player) + { + int i = 0; + int target = 5; + + while (i < target) + { + ExplosiveGrenade grenade = (ExplosiveGrenade)Item.Create(ItemType.GrenadeHE); + grenade.ScpDamageMultiplier = 0; + grenade.FuseTime = .1f; + grenade.SpawnActive(player.Position, player); + yield return Timing.WaitForSeconds(.25f); + i++; + } + } + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/GE/SwapProtocol.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/SwapProtocol.cs new file mode 100644 index 00000000..eeb476fa --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/SwapProtocol.cs @@ -0,0 +1,213 @@ +using CustomPlayerEffects; +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Items; +using InventorySystem.Items.Usables; +using KE.GlobalEventFramework.GEFE.API.Features; +using KE.GlobalEventFramework.GEFE.API.Interfaces; +using KE.Misc.Events.EventsArgs; +using KE.Utils.Extensions; +using MEC; +using PlayerRoles; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using Player = Exiled.API.Features.Player; + +namespace KE.GlobalEventFramework.Examples.GE +{ + public class SwapProtocol : GlobalEvent, IAsyncStart, IEvent + { + public override uint Id { get; set; } = 1051; + public override string Name { get; set; } = "SwapProtocol"; + public override string Description { get; } = "EEEEEEEET c'est parti la roulette tourne"; + public override int WeightedChance { get; set; } = 0; + + private int numberSwap = 0; + public int NumberOfSwap => numberSwap; + + /// + /// Cooldown between each change in seconds + /// + public int Cooldown = 60; + + public IEnumerator Start() + { + while (!Round.IsEnded) + { + yield return Timing.WaitForSeconds(Cooldown); + numberSwap++; + SwapAllPlayers(); + } + } + + private void SwapAllPlayers() + { + List players = Player.List.ToList(); + Log.Debug($"[swapprotocol] player count : {players.Count}"); + + if (players.Count < 2) + { + Log.Debug("[swapprotocol] not enough player"); + return; + } + + players.ShuffleList(); + + List playersData = players.Select(p => new PlayerData(p)).ToList(); + + for (int i = 0; i < players.Count; i++) + { + int sourceIndex = (i + 1) % players.Count; + + Player target = players[i]; + PlayerData dataToApply = playersData[sourceIndex]; + + Log.Debug($"[swappotocol] swap {target.Nickname} from {sourceIndex}"); + + ExecuteSwap(target, dataToApply); + } + + } + + /// + /// Move a player's data to another player + /// + /// + /// + private void ExecuteSwap(Player target, PlayerData data) + { + data.ApplyTo(target); + } + + public void SubscribeEvent() + { + Misc.Features.SCPBuff.OnBuffingSCP += OnBuffingSCP; + } + + public void UnsubscribeEvent() + { + Misc.Features.SCPBuff.OnBuffingSCP -= OnBuffingSCP; + } + + private void OnBuffingSCP(BuffingSCPEventArgs ev) + { + if(NumberOfSwap > 0) + { + ev.IsAllowed = false; + } + } + + private sealed class PlayerData + { + public string Nickname { get; } + public RoleTypeId Role { get; } + public float MaxHealth { get; } + public float Health { get; } + public Vector3 PlayerScale { get; } + public bool IsCuffed { get; } + public List SavedItems { get; } + public Dictionary Ammo { get; } + public List SavedEffects { get; } + + public PlayerData(Player player) + { + Nickname = player.Nickname; + Role = player.Role.Type; + MaxHealth = player.MaxHealth; + Health = player.Health; + PlayerScale = player.Scale; + IsCuffed = player.IsCuffed; + + SavedItems = new List(); + foreach (Item item in player.Items) + { + SavedItems.Add(new ItemState(item)); + } + + Ammo = player.Ammo.ToDictionary(k => k.Key.GetAmmoType(), v => v.Value); + SavedEffects = player.ActiveEffects.Select(e => new EffectState(e)).ToList(); + } + + public void ApplyTo(Player target) + { + target.ClearInventory(); + + target.Role.Set(Role, RoleSpawnFlags.None); + + + if (target.PreviousRole == RoleTypeId.Spectator) + { + Vector3 randomRoom; + if (!Warhead.IsDetonated) + { + randomRoom = Room.Random(ZoneType.HeavyContainment).GetValidPosition(); + } + else + { + randomRoom = Room.List.First(r => r.Type == RoomType.Surface).GetValidPosition(); + } + target.Position = randomRoom; + } + + target.MaxHealth = MaxHealth; + target.Health = Health; + target.Scale = PlayerScale; + if (IsCuffed && !target.IsCuffed) target.Handcuff(); else target.RemoveHandcuffs(); + + foreach (ItemState state in SavedItems) + { + Item newItem = target.AddItem(state.Type); + state.ApplyState(newItem); + } + + foreach (var ammo in Ammo) target.SetAmmo(ammo.Key, ammo.Value); + + target.DisableAllEffects(); + foreach (EffectState state in SavedEffects) target.EnableEffect(state.Type, state.Intensity, state.Duration); + } + } + + private readonly struct ItemState + { + public ItemType Type { get; } + public int AmmoInMag { get; } + public float MicroEnergy { get; } + public int JailbirdCharges { get; } + + public ItemState(Item item) + { + Type = item.Type; + AmmoInMag = 0; + MicroEnergy = 0; + JailbirdCharges = 0; + + if (item is Firearm gun) AmmoInMag = gun.MagazineAmmo; + else if (item is MicroHid micro) MicroEnergy = micro.Energy; + else if (item is Jailbird jail) JailbirdCharges = jail.TotalCharges; + } + + public void ApplyState(Item item) + { + if (item is Firearm gun) gun.MagazineAmmo = AmmoInMag; + else if (item is MicroHid micro) micro.Energy = MicroEnergy; + else if (item is Jailbird jail) jail.TotalCharges = JailbirdCharges; + } + } + + private readonly struct EffectState + { + public EffectType Type { get; } + public byte Intensity { get; } + public float Duration { get; } + + public EffectState(StatusEffectBase effect) + { + Type = effect.GetEffectType(); + Intensity = effect.Intensity; + Duration = effect.Duration; + } + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/GE/SystemMalfunction.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/SystemMalfunction.cs new file mode 100644 index 00000000..356b50c2 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/GE/SystemMalfunction.cs @@ -0,0 +1,136 @@ +using Exiled.API.Features; +using Exiled.API.Features.Doors; +using MEC; +using System.Collections.Generic; +using System.Linq; +using Interactables.Interobjects.DoorUtils; +using Exiled.API.Enums; +using Exiled.API.Extensions; +using KE.GlobalEventFramework.GEFE.API.Interfaces; +using KE.GlobalEventFramework.Examples.API.Feature.mf; +using KE.GlobalEventFramework.GEFE.API.Features; +using KE.GlobalEventFramework.GEFE.API.Enums; +namespace KE.GlobalEventFramework.Examples.GE +{ + /// + /// The original + /// + /// The nuke can go off random from 15 to 30 min in the round (can be disable like a normal nuke) + /// If BlackoutNDoor is enabled in the server, increase the frequence of blackouts and door lockdowns + /// Can lock Elevator and Gate for an amount of time + /// Checkpoints can open randomly + /// + /// + public class SystemMalfunction : GlobalEvent, IAsyncStart, IEvent + { + + + public override ImpactLevel ImpactLevel => ImpactLevel.VeryLow; + /// + public override uint Id { get; set; } = 1041; + /// + public override string Name { get; set; } = "System Malfunction"; + /// + public override string Description { get; } = "System Malfunction"; + public override string[] AltDescription => new string[] + { + "La facilité marche pas trop là" + }; + /// + public override int WeightedChance { get; set; } = 1; + /// + public override uint[] IncompatibleEvents { get; set; } = { 38 }; + /// + /// Set the cooldown for the BlackoutNDoor + /// + public int NewCooldown { get; set; } = 180; + public static Malfunctions Malfunction { get; private set; } + + + + /// + public IEnumerator Start() + { + Log.Debug("system malfunction start"); + //MoreBlackOutNDoors(); + //Coroutine.LaunchCoroutine(EarlyNuke()); + + Timing.RunCoroutine(Malfunction.Tick()); + CoroutineHandle handle; + + while(Round.InProgress){ + Log.Debug("system malfunction"); + yield return Timing.WaitForSeconds(UnityEngine.Random.Range(200, 300)); + List> l = new []{CheckpointMalfunction(),GateLockdown(),ElevatorLockdown()}.ToList(); + handle = Timing.RunCoroutine(l[UnityEngine.Random.Range(0,3)]); + yield return Timing.WaitUntilDone(handle); + } + yield return 0; + } + + + public void SubscribeEvent() + { + Malfunction = new Malfunctions(); + Exiled.Events.Handlers.Player.Dying += Malfunction.OnDying; + Exiled.Events.Handlers.Scp049.FinishingRecall += Malfunction.OnFinishingRevive; + + + //Searching for the plugin + /*var otherPlugin = Exiled.Loader.Loader.Plugins.FirstOrDefault(plugin => plugin.Name == "KE.BlackoutDoor"); + if (otherPlugin != null) + { + + if (otherPlugin is BlackoutNDoor.MainPlugin blackout) + { + Log.Info("Found BlackOutNDoors"); + BlackoutNDoor = blackout; + return; + } + + }*/ + } + public void UnsubscribeEvent() + { + Exiled.Events.Handlers.Player.Dying -= Malfunction.OnDying; + Exiled.Events.Handlers.Scp049.FinishingRecall -= Malfunction.OnFinishingRevive; + Malfunction = null; + } + + private IEnumerator EarlyNuke() + { + int timeNuke = UnityEngine.Random.Range(15, 30); + Log.Debug($"the nuke will detonate in {timeNuke}min"); + yield return Timing.WaitUntilTrue(() => timeNuke == Round.ElapsedTime.TotalMinutes); + Warhead.Start(); + Log.Debug($"kaboom"); + } + + + private IEnumerator CheckpointMalfunction(){ + Log.Debug("CheckpointMalfunction"); + var checkpoints = Door.List.Where(d => d.IsCheckpoint).ToList().RandomItem().IsOpen =true; + yield return Timing.WaitForSeconds(UnityEngine.Random.Range(20, 60)); + + } + + private IEnumerator GateLockdown(){ + Log.Debug("GateLockdown"); + var gates = Door.List.Where(d => d.Type == DoorType.GateA ||d.Type == DoorType.GateB); + var gate = gates.GetRandomValue(); + gate.IsOpen = UnityEngine.Random.value <= .5f ? false : true ; + var timelock = UnityEngine.Random.Range(10,30); + gate.Lock(timelock,DoorLockType.Isolation); + yield return Timing.WaitForSeconds(timelock); + } + + private IEnumerator ElevatorLockdown(){ + Log.Debug("ElevatorLockdown"); + var lift = Lift.Random; + lift.ChangeLock(DoorLockReason.Isolation); + yield return Timing.WaitForSeconds(UnityEngine.Random.Range(10,20)); + lift.ChangeLock(DoorLockReason.None); + } + + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/MainPlugin.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/MainPlugin.cs new file mode 100644 index 00000000..bfe91663 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/MainPlugin.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Interfaces; +using HarmonyLib; +using KruacentExiled; +using MEC; +using UnityEngine; + + +namespace KE.GlobalEventFramework.Examples +{ + internal class MainPlugin : KEPlugin + { + + public override string Name => "KE.GEF.Examples"; + public override string Prefix => "KE.GEFE"; + public static MainPlugin Instance { get; private set; } + + private static Harmony Harmony; + + + + public override IConfig Config => config; + private Config config; + public static Config Configs => Instance?.config; + + public override void OnEnabled() + { + config = KruacentExiled.MainPlugin.Instance.Config.GEFEConfig; + Instance = this; + Utils.API.Sounds.SoundPlayer.Load(); + Exiled.Events.Handlers.Server.WaitingForPlayers += OnWaitingPlayer; + + + + + //Kannassie.LoadVoiceLine(); + Log.Debug("patching"); + Harmony = new Harmony(Name); + //Harmony.PatchAll(); + } + + private void OnWaitingPlayer() + { + + } + + + private IEnumerator Showpos() + { + while (true) + { + Log.Debug(Player.List.First()?.Position); + yield return Timing.WaitForSeconds(2); + } + + } + + public override void OnDisabled() + { + Exiled.Events.Handlers.Server.WaitingForPlayers -= OnWaitingPlayer; + + Instance = null; + Harmony?.UnpatchAll(); + Harmony = null; + } + } + + + public class Config : IConfig + { + public bool IsEnabled { get; set; } = true; + public bool Debug { get; set; } = false; + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/MiddleEvents/Disco.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/MiddleEvents/Disco.cs new file mode 100644 index 00000000..784fa09f --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/MiddleEvents/Disco.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.Examples.MiddleEvents +{ + internal class Disco + { + + //lumière random + + + + private void Start() + { + + } + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/MiddleEvents/HealSCP.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/MiddleEvents/HealSCP.cs new file mode 100644 index 00000000..a41d76d0 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/MiddleEvents/HealSCP.cs @@ -0,0 +1,55 @@ +using CustomPlayerEffects; +using Exiled.API.Features; +using Exiled.Events.EventArgs.Player; +using Exiled.Events.EventArgs.Scp173; +using InventorySystem.Items.Usables; +using KE.GlobalEventFramework.GEFE.API.Features; +using KE.GlobalEventFramework.GEFE.API.Interfaces; +using MEC; +using PlayerRoles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.Examples.MiddleEvents +{ + public class HealSCP : MiddleEvent, IStart, IConditional + { + /// + public override uint Id { get; set; } = 10043; + /// + public override string Name { get; set; } = "HealSCPM"; + /// + public override string Description { get; set; } = "Bon aller on rallonge un peu la game"; + /// + public override int WeightedChance { get; set; } = 1; + + public bool Condition() + { + List list = Player.List.Where(p => p.IsScp && p.Role != RoleTypeId.Scp0492).ToList(); + if (list.Count ==0) return false; + + foreach(Player p in list) + { + if(p.Health/p.MaxHealth > .3f) + { + return false; + } + } + return true; + } + + public void Start() + { + foreach (Player p in Player.List.Where(p => p.IsScp && p.Role != RoleTypeId.Scp0492)) + { + p.Health = p.MaxHealth / 2; + } + + } + + + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/MiddleEvents/ShuffleInv.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/MiddleEvents/ShuffleInv.cs new file mode 100644 index 00000000..1c96ccf1 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/MiddleEvents/ShuffleInv.cs @@ -0,0 +1,36 @@ +using Exiled.API.Features; +using KE.GlobalEventFramework.GEFE.API.Features; +using MEC; +using PlayerRoles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.Examples.MiddleEvents +{ + /*public class ShuffleInv : MiddleEvent + { + //shuffle d'inventaire une fois quand il est activé + /// + public override uint Id { get; set; } = 10045; + /// + public override string Name { get; set; } = "MShuffleI"; + /// + public override string Description { get; set; } = "Aller on change de stuff"; + /// + public override int WeightedChance { get; set; } = 0; + + public IEnumerator Start() + { + Player.List.ToList(); + + + + yield return Timing.WaitForOneFrame; + } + + + }*/ +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/MiddleEvents/ShufflePosition.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/MiddleEvents/ShufflePosition.cs new file mode 100644 index 00000000..28ff8c61 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/MiddleEvents/ShufflePosition.cs @@ -0,0 +1,56 @@ +using Exiled.API.Features; +using KE.GlobalEventFramework.GEFE.API.Features; +using KE.GlobalEventFramework.GEFE.API.Interfaces; +using MEC; +using NorthwoodLib.Pools; +using PlayerRoles; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.GlobalEventFramework.Examples.MiddleEvents +{ + public class ShufflePosition : MiddleEvent, IStart + { + //shuffle de position une fois quand il est activé + /// + public override uint Id { get; set; } = 10045; + /// + public override string Name { get; set; } = "MShuffleP"; + /// + public override string Description { get; set; } = "Aller on change de place"; + /// + public override int WeightedChance { get; set; } = 1; + + public void Start() + { + + + + + List players = Player.Enumerable.ToList(); + List positions = ListPool.Shared.Rent(players.Count); + if(players.Count > 1) + { + Vector3 tmp = positions[0]; + for (int i = 0; i < positions.Count - 1; i++) + { + positions[i] = players[i + 1].Position; + } + + positions[positions.Count - 1] = tmp; + + for (int i = 0; i < players.Count - 1; i++) + { + players[i].Teleport(positions[i]); + } + } + } + + + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/MiddleEvents/ShuffleRole.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/MiddleEvents/ShuffleRole.cs new file mode 100644 index 00000000..72e2710f --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/MiddleEvents/ShuffleRole.cs @@ -0,0 +1,36 @@ +using Exiled.API.Features; +using KE.GlobalEventFramework.GEFE.API.Features; +using MEC; +using PlayerRoles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.Examples.MiddleEvents +{ + /*public class ShuffleRole : MiddleEvent + { + //shuffle de role une fois quand il est activé + /// + public override uint Id { get; set; } = 10045; + /// + public override string Name { get; set; } = "MShuffle"; + /// + public override string Description { get; set; } = "Aller on change de role"; + /// + public override int WeightedChance { get; set; } = 0; + + public IEnumerator Start() + { + Player.List.ToList(); + + + + yield return Timing.WaitForOneFrame; + } + + + }*/ +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/MiddleEvents/Speed.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/MiddleEvents/Speed.cs new file mode 100644 index 00000000..cdf7e574 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/MiddleEvents/Speed.cs @@ -0,0 +1,81 @@ +using CustomPlayerEffects; +using Exiled.API.Features; +using Exiled.Events.EventArgs.Player; +using Exiled.Events.EventArgs.Scp173; +using InventorySystem.Items.Usables; +using KE.GlobalEventFramework.GEFE.API.Features; +using KE.GlobalEventFramework.GEFE.API.Interfaces; +using MEC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.Examples.MiddleEvents +{ + public class SpeedM : MiddleEvent, IAsyncStart, IEvent + { + /// + public override uint Id { get; set; } = 10042; + /// + public override string Name { get; set; } = "SpeedM"; + /// + public override string Description { get; set; } = "Gas! gas! gas!"; + /// + public override int WeightedChance { get; set; } = 1; + public override uint[] IncompatibleEvents => new uint[] { 1042 }; + /// + /// intensity of the movement boost effect + /// + public byte MovementBoost { get; set; } = 100; + /// + public IEnumerator Start() + { + yield return Timing.WaitForSeconds(1); + Player.List.ToList().ForEach(p => p.EnableEffect(MovementBoost, 999999999, true)); + + } + /// + + public void SubscribeEvent() + { + Exiled.Events.Handlers.Player.ChangingRole += ReactivateEffectSpawn; + Exiled.Events.Handlers.Scp173.Blinking += SpeedyNut; + } + /// + public void UnsubscribeEvent() + { + Exiled.Events.Handlers.Player.ChangingRole -= ReactivateEffectSpawn; + Exiled.Events.Handlers.Scp173.Blinking -= SpeedyNut; + } + + protected override void Disable(KEEvents ev) + { + OnDisable(); + base.Disable(ev); + } + + public void OnDisable() + { + Player.List.ToList().ForEach(p => p.DisableEffect()); + } + + /// + /// Decrease the blink cooldown of SCP-173 + /// + private void SpeedyNut(BlinkingEventArgs ev) + { + ev.BlinkCooldown = ev.BlinkCooldown / 4; + } + + /// + /// Reactivate the effect at each role changement + /// + private void ReactivateEffectSpawn(ChangingRoleEventArgs ev) + { + Timing.CallDelayed(.1f, () => ev.Player.EnableEffect(MovementBoost, 999999999, true)); + + } + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework.Examples/MiddleEvents/WallHack.cs b/KruacentExiled/KE.GlobalEventFramework.Examples/MiddleEvents/WallHack.cs new file mode 100644 index 00000000..867c8339 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework.Examples/MiddleEvents/WallHack.cs @@ -0,0 +1,67 @@ +using CustomPlayerEffects; +using Exiled.API.Features; +using Exiled.Events.EventArgs.Player; +using Exiled.Events.EventArgs.Scp173; +using KE.GlobalEventFramework.GEFE.API.Features; +using KE.GlobalEventFramework.GEFE.API.Interfaces; +using MEC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.Examples.MiddleEvents +{ + public class WallHackM : MiddleEvent, IAsyncStart, IEvent + { + /// + public override uint Id { get; set; } = 10052; + /// + public override string Name { get; set; } = "WallHack"; + /// + public override string Description { get; set; } = "Tout le monde wallhack yippee!!!!!"; + /// + public override int WeightedChance { get; set; } = 1; + + /// + public IEnumerator Start() + { + yield return Timing.WaitForSeconds(.1f); + Player.List.ToList().ForEach(p => p.EnableEffect(999999999, true)); + + } + /// + public void SubscribeEvent() + { + Log.Debug("subscribe events wh"); + Exiled.Events.Handlers.Player.ChangingRole += ReactivateEffectSpawn; + } + /// + public void UnsubscribeEvent() + { + Exiled.Events.Handlers.Player.ChangingRole -= ReactivateEffectSpawn; + + } + + protected override void Disable(KEEvents ev) + { + OnDisable(); + } + + public void OnDisable() + { + Player.List.ToList().ForEach(p => p.DisableEffect()); + } + + + /// + /// Reactivate the effect at each role changement + /// + private void ReactivateEffectSpawn(ChangingRoleEventArgs ev) + { + Timing.CallDelayed(.1f, () => ev.Player.EnableEffect( 999999999, true)); + + } + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework/Config.cs b/KruacentExiled/KE.GlobalEventFramework/Config.cs new file mode 100644 index 00000000..bc7f73bd --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework/Config.cs @@ -0,0 +1,25 @@ +using Exiled.API.Interfaces; +using Exiled.Events.Commands.PluginManager; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework +{ + public class Config : IConfig + { + + public bool IsEnabled { get; set; } = true; + public bool Debug { get; set; } = false; + [Description("Show the log when the plugin is registering global event (require Debug to be true)")] + public bool ShowRegisteringLog { get; set; } = false; + [Description("The chance a global event is not shown and show [REDACTED] instead (0~100)")] + public int ChanceRedacted { get; set; } = 10; + [Description("Activate or not the multiple descriptions")] + public bool ActivateAltDescription { get; set; } = true; + + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Enums/ImpactLevel.cs b/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Enums/ImpactLevel.cs new file mode 100644 index 00000000..f78ac934 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Enums/ImpactLevel.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.GlobalEventFramework.GEFE.API.Enums +{ + public enum ImpactLevel + { + VeryLow, + Low, + Medium, + High, + VeryHigh, + Insane + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Extensions/ImpactLevelExtension.cs b/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Extensions/ImpactLevelExtension.cs new file mode 100644 index 00000000..c2ce6c8f --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Extensions/ImpactLevelExtension.cs @@ -0,0 +1,28 @@ +using KE.GlobalEventFramework.GEFE.API.Enums; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.GEFE.API.Extensions +{ + public static class ImpactLevelExtension + { + + public static string Shorten(this ImpactLevel impact) + { + return impact switch + { + ImpactLevel.VeryLow => "[VL]", + ImpactLevel.Low => "[L]", + ImpactLevel.Medium => "[M]", + ImpactLevel.High => "[H]", + ImpactLevel.VeryHigh => "[VH]", + ImpactLevel.Insane => "[I]", + _ => "" + }; + } + + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Features/GlobalEvent.cs b/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Features/GlobalEvent.cs new file mode 100644 index 00000000..d3453e81 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Features/GlobalEvent.cs @@ -0,0 +1,275 @@ +using CommandSystem.Commands.RemoteAdmin.Broadcasts; +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Pools; +using Exiled.Events.EventArgs.Map; +using Exiled.Events.EventArgs.Server; +using HintServiceMeow.Core.Models.Hints; +using KE.GlobalEventFramework.GEFE.API.Enums; +using KE.GlobalEventFramework.GEFE.API.Extensions; +using KE.GlobalEventFramework.GEFE.API.Interfaces; +using KE.Utils.API.Displays.DisplayMeow; +using KE.Utils.API.Interfaces; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; + +namespace KE.GlobalEventFramework.GEFE.API.Features +{ + public abstract class GlobalEvent : KEEvents + { + + private class GlobalEventHandler : IUsingEvents + { + private bool _eventsub = false; + + public void SubscribeEvents() + { + if (_eventsub) return; + + Exiled.Events.Handlers.Server.RoundStarted += OnRoundStarted; + Exiled.Events.Handlers.Server.RoundEnded += OnEndingRound; + + _eventsub = true; + } + + public void UnsubscribeEvents() + { + if (!_eventsub) return; + Exiled.Events.Handlers.Server.RoundStarted -= OnRoundStarted; + Exiled.Events.Handlers.Server.RoundEnded -= OnEndingRound; + + _eventsub = false; + } + + private void OnEndingRound(RoundEndedEventArgs _) + { + Log.Warn("ending round"); + DisableEvents(_activeGE); + } + private void OnRoundStarted() + { + if(ForcedGE.Count == 0) + { + Activate(); + } + else + { + _activeGE = ForcedGE.ToHashSet(); + ForcedGE.Clear(); + EnableEvents(_activeGE); + + Show(); + } + } + + + } + + + private static Config Config => MainPlugin.Configs; + private static GlobalEventHandler _handler = new GlobalEventHandler(); + + private static HashSet _activeGE = new HashSet(); + + public static IReadOnlyDictionary ImpactToColor = new Dictionary() + { + { ImpactLevel.VeryLow, "#d8d8ff" }, + { ImpactLevel.Low, "#d8e8f0" }, + { ImpactLevel.Medium, "#d8fcde" }, + { ImpactLevel.High, "#fbfbd8" }, + { ImpactLevel.VeryHigh, "#f0e8d8" }, + { ImpactLevel.Insane, "#ffd8d8" }, + }; + + + public static HashSet ForcedGE { get; } = new HashSet(); + + + /// + /// A list of all registered GlobalEvents + /// + public static IEnumerable GlobalEventsList => List.Where(ev => ev is GlobalEvent).Cast(); + /// + public abstract string Description { get; } + public virtual string[] AltDescription { get; } = null; + + public virtual ImpactLevel ImpactLevel { get; } = ImpactLevel.Medium; + public bool IsActive + { + get + { + return _activeGE.Contains(this); + } + } + + + public static int NumberOfGE { get; set; } = -1; + + + + protected sealed override void SubscribeEvents() + { + _handler.SubscribeEvents(); + base.SubscribeEvents(); + } + + protected sealed override void UnsubscribeEvents() + { + _handler.UnsubscribeEvents(); + + base.UnsubscribeEvents(); + } + + + protected override void Disable(KEEvents ev) + { + + _activeGE.Remove(ev as GlobalEvent); + base.Disable(ev); + } + + private static void Activate() + { + if(NumberOfGE == -1) + { + NumberOfGE = Random.value < .1f ? 2 : 1; + } + + + _activeGE = GetRandomEvent(NumberOfGE).ToHashSet(); + + EnableEvents(_activeGE); + Show(); + } + + private static void Show() + { + + + ShowConsole(); + + + string text = ShowText(); + + foreach (Player player in Player.List) + { + AbstractHint hint = DisplayHandler.Instance.AddHint(MainPlugin.GEAnnouncement, player, text, 10); + + hint.FontSize = 30; + + } + } + + private static void ShowConsole() + { + Log.Info($"Global Event(s) ({_activeGE.Count()}): "); + foreach (GlobalEvent ge in _activeGE) + { + Log.Info(ge.Name); + } + + } + + private List AllDesc + { + get + { + List allDesc = new List() + { + Description, + }; + allDesc.AddRange(AltDescription); + return allDesc; + } + } + + + /// + /// + /// + /// 0 mean the default desc + /// + /// + private static string ShowText() + { + StringBuilder builder = StringBuilderPool.Pool.Get(); + + builder.Append("Global Events: "); + List ge = ListPool.Pool.Get(_activeGE); + + + + + + + + for (int i = 0; i < ge.Count(); i++) + { + GlobalEvent globalEvent = ge[i]; + + + + + builder.Append(""); + + builder.Append(globalEvent.ImpactLevel.Shorten()); + + + + if (globalEvent.IsRedacted()) + { + builder.Append("[REDACTED]"); + } + else + { + if (!Config.ActivateAltDescription || globalEvent.AltDescription == null) + { + builder.Append(globalEvent.Description); + } + else + { + builder.Append(globalEvent.AllDesc.GetRandomValue()); + } + } + + builder.AppendLine(""); + if (ge.Count() > 1 && i < ge.Count() - 1) + { + builder.Append(", "); + } + + + } + ListPool.Pool.Return(ge); + + + return StringBuilderPool.Pool.ToStringReturn(builder); + } + + + private bool IsRedacted() + { + float chanceRedacted; + + if(this is IChanceRedactable force) + { + chanceRedacted = force.ChanceRedacted; + } + else + { + chanceRedacted = Config.ChanceRedacted; + } + + chanceRedacted = Mathf.Clamp(chanceRedacted, 0, 100); + + return UnityEngine.Random.Range(0f, 100f) <= chanceRedacted; + + + } + + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Features/Hints/DisplayHints.cs b/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Features/Hints/DisplayHints.cs new file mode 100644 index 00000000..dbc2a4eb --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Features/Hints/DisplayHints.cs @@ -0,0 +1,19 @@ +using Exiled.API.Features; +using KE.Utils.API.Displays.DisplayMeow; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.GEFE.API.Features.Hints +{ + public static class DisplayHints + { + + public static void AddHintEffect(Player player,string text,float delay) + { + DisplayHandler.Instance.AddHint(MainPlugin.GEEffect, player, text, delay); + } + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Features/KEEvents.cs b/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Features/KEEvents.cs new file mode 100644 index 00000000..b05e249c --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Features/KEEvents.cs @@ -0,0 +1,299 @@ +using Exiled.API.Features; +using Exiled.API.Interfaces; +using Exiled.CustomItems.API.Features; +using Exiled.Events.EventArgs.Server; +using Exiled.Events.Features; +using KE.GlobalEventFramework.GEFE.API.Interfaces; +using KE.GlobalEventFramework.GEFE.Events.EventArgs; +using KE.GlobalEventFramework.GEFE.Events.Handlers; +using KE.GlobalEventFramework.GEFE.Exceptions; +using KE.Utils.API; +using KE.Utils.API.Interfaces; +using MEC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.GEFE.API.Features +{ + + public abstract class KEEvents + { + #region Abstract Properties + public abstract uint Id { get; set; } + public abstract string Name { get; set; } + public virtual int WeightedChance { get; set; } = 1; + public virtual uint[] IncompatibleEvents { get; set; } = new uint[0]; + protected HashSet CoroutineHandles { get; } = new HashSet(); + protected static readonly HashSet s_activeEvents = new HashSet(); + + #endregion + + #region Static Variables + private static Dictionary _idLookup = new Dictionary(); + private static Dictionary _nameLookup = new Dictionary(); + + public static HashSet List => new HashSet(_idLookup.Values); + #endregion + #region Events + + //public static Event Enabling = new(); + //public static Event Enabled = new(); + //public static Event Disabled = new(); + #endregion + + #region Register + + public static IEnumerable RegisterAll() + { + List assemblies = new List(); + foreach(var plugin in Exiled.Loader.Loader.Plugins) + { + if (!assemblies.Contains(plugin.Assembly) && plugin.Config.IsEnabled) + { + assemblies.Add(plugin.Assembly); + } + + } + + + IEnumerable events = ReflectionHelper.GetObjects(assemblies); + foreach(KEEvents ev in events) + { + try + { + ev.Register(); + } + catch (FailedRegisterException e) + { + Log.Error($"Failed to register KEevent {ev.Name} \nError : {e}"); + } + + } + return events; + + + } + public virtual void Register() + { + if (_idLookup.ContainsKey(Id)) + { + throw new FailedRegisterException($"id already used by {Get(Id).Name} "); + } + LogRegister(); + Init(); + } + + public virtual void Init() + { + _idLookup.Add(Id, this); + _nameLookup.Add(Name, this); + SubscribeEvents(); + } + + + public virtual void Destroy() + { + _idLookup.Remove(Id); + _nameLookup.Remove(Name); + foreach(CoroutineHandle handle in CoroutineHandles) + { + Timing.KillCoroutines(handle); + } + UnsubscribeEvents(); + } + + + public static void DestroyAll() + { + + foreach (KEEvents ev in List) + { + ev.Destroy(); + } + } + #endregion + + + + public static void OnEnabled() + { + RegisterAll(); + + } + + public static void OnDisabled() + { + DestroyAll(); + } + + + + + protected virtual void SubscribeEvents() + { + } + + + protected virtual void UnsubscribeEvents() + { + + } + + + + protected static void EnableEvents(IEnumerable events) + { + foreach (KEEvents ev in events) + { + Log.Info("enabling " + ev.Name); + EnablingEventArgs args = new EnablingEventArgs(ev, true); + KEEventsHandler.OnEnabling(args); + + if (!args.IsAllowed) continue; + + if (ev is IEvent @event) + { + @event.SubscribeEvent(); + } + + + if (ev is IAsyncStart asyncstart) + { + ev.CoroutineHandles.Add(Timing.RunCoroutine(asyncstart.Start())); + } + + if(ev is IStart start) + { + start.Start(); + } + + + s_activeEvents.Add(ev); + + KEEventsHandler.OnEnabled(new EnabledEventArgs(ev)); + } + } + + + protected static void DisableEvents(IEnumerable events) + { + foreach (KEEvents ev in events.ToList()) + { + Log.Info("disabling " + ev.Name); + if (ev is IEvent @event) + { + @event.UnsubscribeEvent(); + } + foreach (CoroutineHandle handle in ev.CoroutineHandles) + { + Timing.KillCoroutines(handle); + } + ev.Disable(ev); + KEEventsHandler.OnDisabled(new DisabledEventArgs(ev)); + } + } + + protected virtual void Disable(KEEvents ev) + { + + } + + + protected static IEnumerable GetRandomEvent(int numberEvent = 1) where T : KEEvents + { + List result = new List(); + List weightedPool = new List(); + foreach (T ge in List.Where(ev => ev is T)) + { + if (!(ge is IConditional) || (ge is IConditional c && c.Condition())) + { + if (!ge.IsCompatible()) continue; + for (int i = 0; i < ge.WeightedChance; i++) + { + + weightedPool.Add(ge); + Log.Debug($"gettochoose : {ge.Name} "); + } + } + } + + numberEvent = Math.Min(numberEvent, weightedPool.Count); + + for (int i = 0; i < numberEvent; i++) + { + int randomIndex = UnityEngine.Random.Range(0, weightedPool.Count); + T selectedGE = weightedPool[randomIndex]; + + result.Add(selectedGE); + + weightedPool.Remove(selectedGE); + weightedPool.RemoveAll(e => selectedGE.IncompatibleEvents.Contains(e.Id)); + if (weightedPool.Count == 0) break; + } + + return result; + } + + public virtual void LogRegister() => + Log.Send($"REGISTERED {Name}", Discord.LogLevel.Info, ConsoleColor.Blue); + + #region Getter + public static bool TryGet(uint id, out KEEvents globalEvent) + { + globalEvent = Get(id); + return globalEvent != null; + } + + public static bool TryGet(string name, out KEEvents globalEvent) + { + if (string.IsNullOrEmpty(name)) + { + throw new ArgumentException("name can't be null or empty"); + } + globalEvent = uint.TryParse(name, out uint id) ? Get(id) : Get(name); + + return globalEvent != null; + } + + public static KEEvents Get(string name) + { + return _nameLookup[name]; + } + + public static KEEvents Get(uint id) + { + return _idLookup.TryGetValue(id, out KEEvents globalEvent) ? globalEvent : null; + } + + + public static T Get(uint id) where T : KEEvents + { + return _idLookup.TryGetValue(id, out KEEvents globalEvent) && globalEvent is T ? globalEvent as T : null; + } + + public static bool TryGet(uint id, out T events) where T : KEEvents + { + events = Get(id); + return events != null; + } + + public bool IsCompatible() + { + foreach(KEEvents ev in s_activeEvents) + { + foreach(int i in ev.IncompatibleEvents) + { + if (i == Id) + return false; + } + } + return true; + } +#endregion + + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Features/MiddleEvent.cs b/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Features/MiddleEvent.cs new file mode 100644 index 00000000..f16fb686 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Features/MiddleEvent.cs @@ -0,0 +1,194 @@ +using Exiled.API.Features; +using Exiled.API.Interfaces; +using Exiled.CustomItems.API.Features; +using Exiled.Events.EventArgs.Server; +using KE.GlobalEventFramework.GEFE.API.Interfaces; +using KE.Utils.API; +using KE.Utils.API.Displays.DisplayMeow; +using MEC; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.GEFE.API.Features +{ + + public abstract class MiddleEvent : KEEvents + { + private class MiddleEventHandler + { + private CoroutineHandle _handle; + private bool _eventsub = false; + public void SubscribeEvents() + { + if (_eventsub) return; + Exiled.Events.Handlers.Server.RoundStarted += OnRoundStarted; + Exiled.Events.Handlers.Server.RoundEnded += OnEndingRound; + Exiled.Events.Handlers.Server.RestartingRound += OnRestartingRound; + + + _eventsub = true; + } + + public void UnsubscribeEvents() + { + if (!_eventsub) return; + + Exiled.Events.Handlers.Server.RoundStarted -= OnRoundStarted; + Exiled.Events.Handlers.Server.RoundEnded -= OnEndingRound; + Exiled.Events.Handlers.Server.RestartingRound -= OnRestartingRound; + + _eventsub = false; + } + + private void OnEndingRound(RoundEndedEventArgs _) + { + Log.Debug("ending round"); + DisableEvents(_activeEv); + } + private void OnRestartingRound() + { + Log.Debug("restarting"); + DisableEvents(_activeEv); + } + private void OnRoundStarted() + { + TimeToActivate = new TimeSpan(0,UnityEngine.Random.Range(MinTimeToActivate.Minutes,MaxTimeToActivate.Minutes),0); + _handle = Timing.RunCoroutine(Timer()); + } + + private IEnumerator Timer() + { + if (UnityEngine.Random.Range(0f, 100f) < Chance) + { + while (Round.InProgress) + { + yield return Timing.WaitForSeconds(60); + if(Round.ElapsedTime > TimeToActivate) + { + Activate(); + } + } + } + } + } + + public abstract string Description { get; set; } + + public static float Chance = 37; + public static TimeSpan MinTimeToActivate = new TimeSpan(0, 10, 0); + public static TimeSpan MaxTimeToActivate = new TimeSpan(0, 16, 0); + private static TimeSpan TimeToActivate; + + private static HashSet _activeEv = new HashSet(); + private static MiddleEventHandler _handler = new MiddleEventHandler(); + + + public static IReadOnlyCollection ActiveMiddleEvent => _activeEv; + + public bool IsActive + { + get + { + return _activeEv.Contains(this); + } + } + + + /// + /// A list of all registered + /// + public static IEnumerable MiddleEventsList => List.Where(ev => ev is MiddleEvent).Cast(); + + protected sealed override void SubscribeEvents() + { + _handler.SubscribeEvents(); + base.SubscribeEvents(); + } + protected sealed override void UnsubscribeEvents() + { + _handler.UnsubscribeEvents(); + DisableEvents(_activeEv); + base.UnsubscribeEvents(); + } + + + + + /// + /// Get a random and activate it + /// + public static bool Activate() + { + if (_activeEv.Count > 0) return false; + + _activeEv = GetRandomEvent().ToHashSet(); + EnableEvents(_activeEv); + Show(); + + return true; + } + + protected override void Disable(KEEvents ev) + { + _activeEv.Remove(ev as MiddleEvent); + base.Disable(ev); + } + + #region Show + private static void Show() + { + var random = UnityEngine.Random.Range(0f, 100f); + + ShowConsole(); + if (_activeEv.Count == 0) return; + foreach (Player player in Player.List) + { + DisplayHandler.Instance.AddHint(MainPlugin.GEAnnouncement, player, ShowText(), 10).FontSize = 30; + } + } + + private static void ShowConsole() + { + Log.Info($"Middle Event(s) ({_activeEv.Count()}): "); + + foreach (MiddleEvent ge in _activeEv) + { + Log.Info(ge.Name); + } + + } + + private static string ShowText() + { + string result = "Middle Event: "; + List ge = _activeEv.ToList(); + + + for (int i = 0; i < ge.Count(); i++) + { + result += ge[i].Description; + + if (ge.Count() > 1 && i < ge.Count() - 1) + { + result += ", "; + } + } + + + return result; + } + + #endregion + + + + + + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Interfaces/IAsyncStart.cs b/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Interfaces/IAsyncStart.cs new file mode 100644 index 00000000..15ea06a0 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Interfaces/IAsyncStart.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.GEFE.API.Interfaces +{ + public interface IAsyncStart + { + /// + /// Is launched at the start of a round + /// + IEnumerator Start(); + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Interfaces/IChanceRedactable.cs b/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Interfaces/IChanceRedactable.cs new file mode 100644 index 00000000..d93ae408 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Interfaces/IChanceRedactable.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.GEFE.API.Interfaces +{ + /// + /// Override the redactable chance + /// + public interface IChanceRedactable + { + + /// + /// 0-100 + /// + public abstract float ChanceRedacted { get; } + + + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Interfaces/IConditional.cs b/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Interfaces/IConditional.cs new file mode 100644 index 00000000..e0bbc247 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Interfaces/IConditional.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.GEFE.API.Interfaces +{ + public interface IConditional + { + public bool Condition(); + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Interfaces/IEvent.cs b/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Interfaces/IEvent.cs new file mode 100644 index 00000000..55b20279 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Interfaces/IEvent.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.GEFE.API.Interfaces +{ + public interface IEvent + { + /// + /// The method used to subcribe to event like with normal plugins + /// + void SubscribeEvent(); + /// + /// The method used to unsubcribe to event like with normal plugins + /// + void UnsubscribeEvent(); + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Interfaces/IReversible.cs b/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Interfaces/IReversible.cs new file mode 100644 index 00000000..06cbec24 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Interfaces/IReversible.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.GEFE.API.Interfaces +{ + public interface IReversible + { + + public void OnDisable(); + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Interfaces/IStart.cs b/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Interfaces/IStart.cs new file mode 100644 index 00000000..bdd5faf5 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework/GEFE/API/Interfaces/IStart.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.GEFE.API.Interfaces +{ + public interface IStart + { + /// + /// Is launched at the start of a round + /// + void Start(); + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework/GEFE/Commands/ForceGE.cs b/KruacentExiled/KE.GlobalEventFramework/GEFE/Commands/ForceGE.cs new file mode 100644 index 00000000..87db8254 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework/GEFE/Commands/ForceGE.cs @@ -0,0 +1,98 @@ +namespace KE.GlobalEventFramework.GEFE.Commands +{ + using Exiled.API.Features; + using Exiled.API.Features.Pickups; + using System; + using CommandSystem; + using System.Runtime.InteropServices.WindowsRuntime; + using GEFE.API.Interfaces; + using GEFE.API.Features; + using System.Collections.Generic; + using System.Linq; + + public class ForceGE : ICommand + { + public string Command { get; } = "force"; + public string[] Aliases { get; } = new string[] { "f" }; + public string Description { get; } = "force a or multiple global event"; + internal static List ForcedGE { get; } = new List(); + + public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) + { + + + + if (!Round.IsLobby) + { + response = "You can only force a global event in the lobby"; + return false; + } + + if (!uint.TryParse(arguments.At(0),out uint arg1)) + { + response = "argument 1 invalid"; + return false; + } + + + if (!KEEvents.TryGet(arg1, out GlobalEvent ge1) || ge1 == null) + { + response = $"Global event ({arguments.At(0)}) not found "; + return false; + } + + if (arguments.Count == 1) + { + response = $"Forcing {ge1.Name}"; + return GlobalEvent.ForcedGE.Add(ge1); + } + + response = "WIP"; + return false; + + + /*if (!Round.IsLobby) + { + response = "You can only force a global event in the lobby"; + ForcedGE = new List(); + return false; + } + + if (!GlobalEvent.TryGet(arguments.At(0), out GlobalEvent ge1) || ge1 == null) + { + response = $"Global event ({arguments.At(0)}) not found "; + ForcedGE = new GlobalEvent[] { ge1 }.ToList(); + return false; + } + + + + if (arguments.Count == 1) + { + response = $"Forcing {ge1.Name}"; + ForcedGE = new GlobalEvent[] { ge1 }.ToList(); + return true; + } + + if (!GlobalEvent.TryGet(arguments.At(1), out GlobalEvent ge2) || ge2 == null) + { + response = $"Global event ({arguments.At(1)}) not found "; + ForcedGE = new List(); + return false; + } + + if (arguments.Count == 2) + { + response = $"Forcing {ge1.Name} & {ge2.Name}"; + ForcedGE = new GlobalEvent[] { ge1, ge2 }.ToList(); + return true; + } + + ForcedGE = new List(); + response = ""; + return false; + */ + + } + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework/GEFE/Commands/ForceMiddleEvent.cs b/KruacentExiled/KE.GlobalEventFramework/GEFE/Commands/ForceMiddleEvent.cs new file mode 100644 index 00000000..80783e8e --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework/GEFE/Commands/ForceMiddleEvent.cs @@ -0,0 +1,25 @@ +namespace KE.GlobalEventFramework.GEFE.Commands +{ + using Exiled.API.Features; + using Exiled.API.Features.Pickups; + using System; + using CommandSystem; + using System.Runtime.InteropServices.WindowsRuntime; + using GEFE.API.Interfaces; + using GEFE.API.Features; + + public class ForceMiddleEvent : ICommand + { + public string Command { get; } = "forcemiddle"; + public string[] Aliases { get; } = new string[] { "fm" }; + public string Description { get; } = "force a random middle event"; + + public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) + { + bool res = MiddleEvent.Activate(); + + response = res ? "activated" : "not activated"; + return res; + } + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework/GEFE/Commands/ForceNbGE.cs b/KruacentExiled/KE.GlobalEventFramework/GEFE/Commands/ForceNbGE.cs new file mode 100644 index 00000000..e9e027f5 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework/GEFE/Commands/ForceNbGE.cs @@ -0,0 +1,57 @@ +namespace KE.GlobalEventFramework.GEFE.Commands +{ + using Exiled.API.Features; + using Exiled.API.Features.Pickups; + using System; + using CommandSystem; + using System.Runtime.InteropServices.WindowsRuntime; + using GEFE.API.Interfaces; + using GEFE.API.Features; + using System.Collections.Generic; + using System.Linq; + using KE.GlobalEventFramework.GEFE.API.Features.Hints; + + public class ForceNbGE : ICommand + { + public string Command { get; } = "forceNb"; + public string[] Aliases { get; } = new string[] { "nb","n" }; + public string Description { get; } = "force a number global event"; + + public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) + { + if (!Round.IsLobby) + { + response = "You can only force a global event in the lobby"; + return false; + } + + + + if (arguments.Count == 1) + { + if (int.TryParse(arguments.At(0), out int nbge) && nbge > -1) + { + if(nbge <= 0) + { + response = "You can't force 0 global event"; + return false; + } + if(nbge > GlobalEvent.GlobalEventsList.Count()) + { + response = $"You can't force {nbge} global event, there are only {GlobalEvent.GlobalEventsList.Count()} global events"; + return false; + } + + + response = $"Forcing {nbge} global event"; + GlobalEvent.NumberOfGE = nbge; + return true; + } + } + + response = "Too many arguments"; + return false; + + } + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework/GEFE/Commands/List.cs b/KruacentExiled/KE.GlobalEventFramework/GEFE/Commands/List.cs new file mode 100644 index 00000000..1db15b09 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework/GEFE/Commands/List.cs @@ -0,0 +1,95 @@ +namespace KE.GlobalEventFramework.GEFE.Commands +{ + using Exiled.API.Features; + using Exiled.API.Features.Pickups; + using System; + using CommandSystem; + using System.Runtime.InteropServices.WindowsRuntime; + using GEFE.API.Interfaces; + using GEFE.API.Features; + using KE.GlobalEventFramework.GEFE.API.Features.Hints; + using UnityEngine; + using System.Text; + using Exiled.API.Features.Pools; + using System.Linq; + using System.Collections.Generic; + using Exiled.Events; + + public class List : ICommand + { + public string Command { get; } = "list"; + public string[] Aliases { get; } = new string[] { "l", "ls" }; + public string Description { get; } = "get the list of all Global Events"; + + + public static string Activated => "[o]"; + public static string NotActivated => "[ ]"; + + public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) + { + + StringBuilder builder = StringBuilderPool.Pool.Get(); + builder.AppendLine(); + + + + + List glist = GlobalEvent.GlobalEventsList.ToList(); + + builder.Append("Global Events ("); + builder.Append(glist.Count); + builder.Append(")"); + + + foreach(GlobalEvent ge in glist) + { + builder.AppendLine(); + if (ge.IsActive) + { + builder.Append(Activated); + } + else + { + builder.Append(NotActivated); + } + Show(builder, ge); + } + builder.AppendLine(); + List mlist = MiddleEvent.MiddleEventsList.ToList(); + builder.Append("Middle Event ("); + builder.Append(mlist.Count); + builder.Append(")"); + + foreach (MiddleEvent me in mlist) + { + builder.AppendLine(); + if (me.IsActive) + { + builder.Append(Activated); + } + else + { + builder.Append(NotActivated); + } + Show(builder, me); + } + + response = builder.ToString(); + StringBuilderPool.Pool.Return(builder); + return true; + } + + + + private void Show(StringBuilder builder, KEEvents events) + { + + builder.Append(" ("); + builder.Append(events.Id); + builder.Append(") "); + builder.Append(events.Name); + builder.Append(" - "); + builder.Append(events.WeightedChance); + } + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework/GEFE/Commands/ParentCommandGEFE.cs b/KruacentExiled/KE.GlobalEventFramework/GEFE/Commands/ParentCommandGEFE.cs new file mode 100644 index 00000000..03046509 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework/GEFE/Commands/ParentCommandGEFE.cs @@ -0,0 +1,69 @@ + +namespace KE.GlobalEventFramework.GEFE.Commands +{ + using CommandSystem; + using Exiled.API.Features.Pools; + using System; + using System.Text; + + [CommandHandler(typeof(RemoteAdminCommandHandler))] + public class ParentCommandGEFE : ParentCommand + { + public ParentCommandGEFE() + { + LoadGeneratedCommands(); + } + public override string Command { get; } = "globalevent"; + public override string Description { get; } = "the parent command to check the Global Events"; + public override string[] Aliases { get; } = { "ge" }; + + public override void LoadGeneratedCommands() + { + RegisterCommand(new List()); + RegisterCommand(new ForceGE()); + RegisterCommand(new ForceNbGE()); + RegisterCommand(new ForceMiddleEvent()); + } + + protected override bool ExecuteParent(ArraySegment arguments, ICommandSender sender, out string response) + { + + + if (arguments.Count > 0) + { + response = ""; + return true; + } + + StringBuilder builder = StringBuilderPool.Pool.Get(); + builder.AppendLine(); + foreach (ICommand command in AllCommands) + { + builder.Append(command.Command); + + builder.Append("("); + string[] alias = command.Aliases; + for (int i = 0; i < alias.Length; i++) + { + builder.Append(alias[i]); + if (alias.Length > 1 && i < alias.Length - 1) + { + builder.Append(", "); + } + } + builder.Append(") - "); + + builder.Append(command.Description); + builder.AppendLine(); + } + + + response = builder.ToString(); + StringBuilderPool.Pool.Return(builder); + return false; + } + + + } + +} \ No newline at end of file diff --git a/KruacentExiled/KE.GlobalEventFramework/GEFE/Events/EventArgs/DisabledEventArgs.cs b/KruacentExiled/KE.GlobalEventFramework/GEFE/Events/EventArgs/DisabledEventArgs.cs new file mode 100644 index 00000000..b1acacc7 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework/GEFE/Events/EventArgs/DisabledEventArgs.cs @@ -0,0 +1,21 @@ +using Exiled.Events.EventArgs.Interfaces; +using KE.GlobalEventFramework.GEFE.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.GEFE.Events.EventArgs +{ + public class DisabledEventArgs : IExiledEvent + { + + public KEEvents Event { get; } + + public DisabledEventArgs(KEEvents ev) + { + Event = ev; + } + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework/GEFE/Events/EventArgs/EnabledEventArgs.cs b/KruacentExiled/KE.GlobalEventFramework/GEFE/Events/EventArgs/EnabledEventArgs.cs new file mode 100644 index 00000000..b831f2ba --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework/GEFE/Events/EventArgs/EnabledEventArgs.cs @@ -0,0 +1,21 @@ +using Exiled.Events.EventArgs.Interfaces; +using KE.GlobalEventFramework.GEFE.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.GEFE.Events.EventArgs +{ + public class EnabledEventArgs : IExiledEvent + { + + public KEEvents Event { get; } + + public EnabledEventArgs(KEEvents ev) + { + Event = ev; + } + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework/GEFE/Events/EventArgs/EnablingEventArgs.cs b/KruacentExiled/KE.GlobalEventFramework/GEFE/Events/EventArgs/EnablingEventArgs.cs new file mode 100644 index 00000000..17cdbec3 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework/GEFE/Events/EventArgs/EnablingEventArgs.cs @@ -0,0 +1,23 @@ +using Exiled.Events.EventArgs.Interfaces; +using KE.GlobalEventFramework.GEFE.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.GEFE.Events.EventArgs +{ + public class EnablingEventArgs : IExiledEvent, IDeniableEvent + { + public bool IsAllowed { get; set; } + + public KEEvents Event { get; } + + public EnablingEventArgs(KEEvents ev,bool isallowed) + { + Event = ev; + IsAllowed = isallowed; + } + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework/GEFE/Events/Handlers/KEEventsHandler.cs b/KruacentExiled/KE.GlobalEventFramework/GEFE/Events/Handlers/KEEventsHandler.cs new file mode 100644 index 00000000..7da48ae1 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework/GEFE/Events/Handlers/KEEventsHandler.cs @@ -0,0 +1,36 @@ +using KE.GlobalEventFramework.GEFE.Events.EventArgs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.GEFE.Events.Handlers +{ + public static class KEEventsHandler + { + + + + public static event Action Disabled = delegate { }; + public static event Action Enabled = delegate { }; + public static event Action Enabling = delegate { }; + + + public static void OnDisabled(DisabledEventArgs ev) + { + Disabled?.Invoke(ev); + } + public static void OnEnabled(EnabledEventArgs ev) + { + Enabled?.Invoke(ev); + } + public static void OnEnabling(EnablingEventArgs ev) + { + Enabling?.Invoke(ev); + } + + + + } +} diff --git a/KruacentExiled/KE.GlobalEventFramework/GEFE/Exceptions/FailedRegisterException.cs b/KruacentExiled/KE.GlobalEventFramework/GEFE/Exceptions/FailedRegisterException.cs new file mode 100644 index 00000000..cbfe635a --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework/GEFE/Exceptions/FailedRegisterException.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.GlobalEventFramework.GEFE.Exceptions +{ + public class FailedRegisterException : Exception + { + + public FailedRegisterException(string message) : base(message) + { + + } + } +} diff --git a/KruacentE.GlobalEventFramework/GEFE/Exception/GlobalEventNullException.cs b/KruacentExiled/KE.GlobalEventFramework/GEFE/Exceptions/GlobalEventNullException.cs similarity index 84% rename from KruacentE.GlobalEventFramework/GEFE/Exception/GlobalEventNullException.cs rename to KruacentExiled/KE.GlobalEventFramework/GEFE/Exceptions/GlobalEventNullException.cs index b0ed864a..f36db7ca 100644 --- a/KruacentE.GlobalEventFramework/GEFE/Exception/GlobalEventNullException.cs +++ b/KruacentExiled/KE.GlobalEventFramework/GEFE/Exceptions/GlobalEventNullException.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace GEFExiled.Exception +namespace KE.GlobalEventFramework.GEFE.Exceptions { public class GlobalEventNullException : ArgumentException { diff --git a/KruacentExiled/KE.GlobalEventFramework/MainPlugin.cs b/KruacentExiled/KE.GlobalEventFramework/MainPlugin.cs new file mode 100644 index 00000000..98c861f6 --- /dev/null +++ b/KruacentExiled/KE.GlobalEventFramework/MainPlugin.cs @@ -0,0 +1,47 @@ +using Exiled.API.Features; +using System; +using Exiled.API.Enums; +using KE.GlobalEventFramework.GEFE.API.Features; +using KE.Utils.API.Displays.DisplayMeow; +using KruacentExiled; +using Exiled.API.Interfaces; +namespace KE.GlobalEventFramework +{ + internal class MainPlugin : KEPlugin + { + public override string Name => "KE.GEFramework"; + public override string Prefix => "KE.GEF"; + + + + public static readonly HintPlacement GEAnnouncement = new HintPlacement(0, 50, HintServiceMeow.Core.Enum.HintAlignment.Center); + public static readonly HintPlacement GEEffect = new HintPlacement(360, 300, HintServiceMeow.Core.Enum.HintAlignment.Right); + + + internal static MainPlugin Instance { get; private set;} + + public override IConfig Config => config; + private Config config; + public static Config Configs => Instance?.config; + + public override void OnEnabled() + { + config = KruacentExiled.MainPlugin.Instance.Config.GEFConfig; + Instance = this; + + + KEEvents.OnEnabled(); + + } + + public override void OnDisabled() + { + KEEvents.OnDisabled(); + Instance = null; + } + + + + + } +} diff --git a/KruacentExiled/KE.Items/API/Core/Lights/LightsHandler.cs b/KruacentExiled/KE.Items/API/Core/Lights/LightsHandler.cs new file mode 100644 index 00000000..7b9d260c --- /dev/null +++ b/KruacentExiled/KE.Items/API/Core/Lights/LightsHandler.cs @@ -0,0 +1,38 @@ +using Exiled.API.Features; +using Exiled.CustomItems.API.Features; +using InventorySystem.Items.Pickups; +using KE.Items.API.Interface; +using KE.Utils.API.Interfaces; +using LabApi.Features.Wrappers; + +namespace KE.Items.API.Core.Lights +{ + internal class LightsHandler : IUsingEvents + { + public float Intensity { get; set; } = .5f; + public void SubscribeEvents() + { + ItemPickupBase.OnPickupAdded += AddPickup; + } + + public void UnsubscribeEvents() + { + ItemPickupBase.OnPickupAdded -= AddPickup; + } + + + private void AddPickup(ItemPickupBase pickup) + { + if (CustomItem.TryGet(Exiled.API.Features.Pickups.Pickup.Get(pickup), out CustomItem item) && item is ILumosItem li) + { + + var l = LightSourceToy.Create(pickup.transform, false); + l.Color = li.Color; + l.Intensity = Intensity; + l.MovementSmoothing = 0; + l.Spawn(); + } + + } + } +} diff --git a/KruacentExiled/KE.Items/API/Core/Models/PickupModel.cs b/KruacentExiled/KE.Items/API/Core/Models/PickupModel.cs new file mode 100644 index 00000000..48a2353d --- /dev/null +++ b/KruacentExiled/KE.Items/API/Core/Models/PickupModel.cs @@ -0,0 +1,111 @@ +using AdminToys; +using Exiled.API.Features; +using Exiled.API.Features.Pickups; +using Exiled.API.Features.Toys; +using Exiled.CustomItems.API.Features; +using InventorySystem.Items.Pickups; +using KE.Items.API.Features; +using KE.Utils.API.Features.Models; +using KE.Utils.API.Interfaces; +using MEC; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.Windows.Speech; + +namespace KE.Items.API.Core.Models +{ + public abstract class PickupModel : ModelBase, IUsingEvents + { + + public CustomItem KECI { get; } + public abstract float Scale { get; } + + + public PickupModel(CustomItem customItem) + { + KECI = customItem; + } + + public void SubscribeEvents() + { + PickupToParent = new Dictionary(); + + ItemPickupBase.OnPickupAdded += OnPickupAdded; + ItemPickupBase.OnPickupDestroyed += OnPickupDestroyed; + } + + public void UnsubscribeEvents() + { + ItemPickupBase.OnPickupAdded -= OnPickupAdded; + ItemPickupBase.OnPickupDestroyed -= OnPickupDestroyed; + PickupToParent = null; + } + + private Dictionary PickupToParent; + + protected override PrimitiveObjectToy CreatePrimitive(Transform parent, PrimitiveType type, Vector3 localPosition, Quaternion localRotation, Vector3 localScale, Color32 color, byte movementSmoothing = 0, bool collidable = false) + { + return base.CreatePrimitive(parent, type, localPosition, localRotation, localScale, color, 0, false); + } + + private Primitive CreateParent(Pickup pickup) + { + + + Transform parent = pickup.Transform; + Primitive prim = Primitive.Create(null, null, null, false); + prim.Flags = AdminToys.PrimitiveFlags.None; + + prim.Transform.parent = parent; + + Vector3 parentScale = parent.localScale; + prim.Transform.localPosition = Vector3.zero; + prim.Transform.localRotation = Quaternion.identity; + prim.Transform.localScale = new Vector3(1f/ parentScale.x,1f/ parentScale.y,1f/parentScale.z); + prim.Transform.localScale *= Scale; + prim.MovementSmoothing = 0; + + prim.Spawn(); + PickupToParent.Add(pickup, prim); + + return prim; + } + + public void OnPickupAdded(ItemPickupBase pickupBase) + { + Pickup pickup = Pickup.Get(pickupBase); + if (!Check(pickup)) return; + + Transform parent = CreateParent(pickup).Transform; + CreateModel(parent); + + } + + public void OnPickupDestroyed(ItemPickupBase pickupBase) + { + Pickup pickup = Pickup.Get(pickupBase); + if (!Check(pickup)) return; + + if(PickupToParent.TryGetValue(pickup,out Primitive prim)) + { + + Log.Info("destroying parent of " + pickup); + Destroy(prim.Transform); + PickupToParent.Remove(pickup); + } + + } + + public bool Check(Pickup pickup) + { + if (pickup is null) return false; + if (!CustomItem.TryGet(pickup, out CustomItem ci)) return false; + if (ci != KECI) return false; + + return true; + + } + + } +} diff --git a/KruacentExiled/KE.Items/API/Core/Settings/SettingsHandler.cs b/KruacentExiled/KE.Items/API/Core/Settings/SettingsHandler.cs new file mode 100644 index 00000000..a922cec8 --- /dev/null +++ b/KruacentExiled/KE.Items/API/Core/Settings/SettingsHandler.cs @@ -0,0 +1,90 @@ +using Exiled.API.Features; +using Exiled.API.Features.Core.UserSettings; +using KE.Utils.API.Settings.SettingsCategories; +using System; +using System.Collections.Generic; + + +namespace KE.Items.API.Core.Settings +{ + internal class SettingsHandler + { + + private const int _idHeader = 55; + private const int _idDesc = 0; + private const int _idPrefix = 1; + private const int _idTimeCustomItem = 2; + private const int _idTimeCustomItemEffect = 3; + + //public readonly SettingsPage page; + + public const string SettingDescription = "Description"; + public const string SettingDescription1 = "Disabled"; + public const string SettingDescription2 = "Enabled"; + public const string SettingDescriptionDescription = "hide/show the description of the item and its upgrade chances"; + + + public SettingsHandler() + { + HeaderSetting header = new HeaderSetting(_idHeader, "Custom Items", padding: true); + + List settings = new List() + { + + new TwoButtonsSetting(_idDesc,SettingDescription,SettingDescription1,SettingDescription2,true,SettingDescriptionDescription), + new TwoButtonsSetting(_idPrefix,"Pickup/Select prefixes","Disabled","Enabled",false,"show/hide the whole sentence for the picking or selecting of the item, if hidden it will show a (P) if pickup or a (I) if in inventory before the name of the item "), + new SliderSetting(_idTimeCustomItem,"Time shown",0,30,10), + new SliderSetting(_idTimeCustomItemEffect,"Time effect shown",0,30,10), + }; + + SettingsCategory category = new SettingsCategory(header,999, settings); + + //page = new("Custom Items", settings); + + } + + + + internal float GetTime(Player p) + { + if (!SettingBase.TryGetSetting(p, _idTimeCustomItem, out var setting)) return 10; + return setting.SliderValue; + } + + internal float GetTimeEffect(Player p) + { + if (!SettingBase.TryGetSetting(p, _idTimeCustomItemEffect, out var setting)) return 10; + return setting.SliderValue; + } + + /// + /// + /// + /// + /// true if the player wants description ; false otherwise + internal bool GetDescriptionsSettings(Player p) + { + try + { + if (!SettingBase.TryGetSetting(p, _idDesc, out var setting)) return true; + return setting.IsSecond; + } + catch(InvalidCastException e) + { + Log.Error(e); + } + return true; + } + + /// + /// + /// + /// + /// true if the player wants prefixes ; false otherwise + internal bool GetPrefixes(Player p) + { + if (!SettingBase.TryGetSetting(p, _idPrefix, out var setting)) return false; + return setting.IsSecond; + } + } +} diff --git a/KruacentExiled/KE.Items/API/Core/Upgrade/UpgradeHandler.cs b/KruacentExiled/KE.Items/API/Core/Upgrade/UpgradeHandler.cs new file mode 100644 index 00000000..b6d77171 --- /dev/null +++ b/KruacentExiled/KE.Items/API/Core/Upgrade/UpgradeHandler.cs @@ -0,0 +1,93 @@ +using Exiled.API.Features; +using Exiled.API.Features.Items; +using Exiled.CustomItems.API.Features; +using Exiled.Events.EventArgs.Scp914; +using KE.Items.API.Features; +using KE.Items.API.Interface; +using KE.Utils.API.Interfaces; +using Scp914; + +namespace KE.Items.API.Core.Upgrade +{ + internal class UpgradeHandler : IUsingEvents + { + + public void SubscribeEvents() + { + Exiled.Events.Handlers.Scp914.UpgradingInventoryItem += UpgradeItem; + Exiled.Events.Handlers.Scp914.UpgradingPickup += UpgradePickUp; + } + + public void UnsubscribeEvents() + { + Exiled.Events.Handlers.Scp914.UpgradingInventoryItem -= UpgradeItem; + Exiled.Events.Handlers.Scp914.UpgradingPickup -= UpgradePickUp; + } + + + private void UpgradeItem(UpgradingInventoryItemEventArgs ev) + { + if (!CustomItem.TryGet(ev.Item, out CustomItem ci)) return; + if (!(ci is IUpgradableCustomItem upgradable)) return; + Log.Debug("upgrading item"); + if (UpgradeCheck(upgradable, ev.KnobSetting)) + { + Log.Debug("success"); + var newItemid = upgradable.Upgrade[ev.KnobSetting].UpgradedItem; + + CustomItem newItem = CustomItem.Get(newItemid); + + ev.Player.RemoveItem(ev.Item); + newItem?.Give(ev.Player); + if (newItem == null) Log.Warn("warning id of custom item not found"); + + } + ev.IsAllowed = false; + } + + private void UpgradePickUp(UpgradingPickupEventArgs ev) + { + + if (!CustomItem.TryGet(ev.Pickup, out CustomItem ci)) return; + if (!(ci is IUpgradableCustomItem upgradable)) return; + Log.Debug("upgrading pickup"); + + if(upgradable.Upgrade is null || upgradable.Upgrade.Count == 0) + { + throw new System.ArgumentException("upgradable null or empty"); + } + + + if (UpgradeCheck(upgradable, ev.KnobSetting)) + { + Log.Debug("success"); + string newItemName = upgradable.Upgrade[ev.KnobSetting].UpgradedItem; + + KECustomItem newItem = KECustomItem.Get(newItemName); + ev.Pickup.Destroy(); + if (newItem == null) + { + Log.Warn("warning id of custom item not found"); + return; + } + + newItem.Spawn(ev.OutputPosition); + } + + ev.IsAllowed = false; + } + + private bool UpgradeCheck(IUpgradableCustomItem upgradable, Scp914KnobSetting knob) + { + if (!upgradable.Upgrade.TryGetValue(knob, out UpgradeProperties item)) return false; + if (MainPlugin.Instance.Config.Debug) + { + Log.Warn("debug activated!"); + return true; + } + float random = UnityEngine.Random.Range(0f, 100f); + return random < item.Chance; + } + + } +} diff --git a/KruacentExiled/KE.Items/API/Core/Upgrade/UpgradeProperties.cs b/KruacentExiled/KE.Items/API/Core/Upgrade/UpgradeProperties.cs new file mode 100644 index 00000000..5c23af8b --- /dev/null +++ b/KruacentExiled/KE.Items/API/Core/Upgrade/UpgradeProperties.cs @@ -0,0 +1,26 @@ +using Exiled.CustomItems.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.ExceptionServices; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using YamlDotNet.Core.Tokens; + +namespace KE.Items.API.Core.Upgrade +{ + public class UpgradeProperties + { + public float Chance { get; } + + public string UpgradedItem { get; } + + public UpgradeProperties(float chance, string newItem) + { + UpgradedItem = newItem; + Chance = Mathf.Clamp(chance, 0,100); + } + + } +} diff --git a/KruacentExiled/KE.Items/API/Events/ExplodeEvent.cs b/KruacentExiled/KE.Items/API/Events/ExplodeEvent.cs new file mode 100644 index 00000000..75628c61 --- /dev/null +++ b/KruacentExiled/KE.Items/API/Events/ExplodeEvent.cs @@ -0,0 +1,25 @@ +using InventorySystem.Items.ThrowableProjectiles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Items.API.Events +{ + public static class ExplodeEvent + { + + public static event Action ExplodeDestructible = delegate { }; + + + public static void OnExplodeDestructible(OnExplodeDestructibleEventsArgs ev) + { + ExplodeDestructible?.Invoke(ev); + } + + + + + } +} diff --git a/KruacentExiled/KE.Items/API/Events/OnExplodeDestructibleEventsArgs.cs b/KruacentExiled/KE.Items/API/Events/OnExplodeDestructibleEventsArgs.cs new file mode 100644 index 00000000..d87c3b6e --- /dev/null +++ b/KruacentExiled/KE.Items/API/Events/OnExplodeDestructibleEventsArgs.cs @@ -0,0 +1,25 @@ +using InventorySystem.Items.ThrowableProjectiles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Items.API.Events +{ + public class OnExplodeDestructibleEventsArgs + { + + public IDestructible Destructible { get; set; } + public float Damage { get; set; } + public ExplosionGrenade ExplosionGrenade { get;} + public OnExplodeDestructibleEventsArgs(IDestructible destructible,float damage, ExplosionGrenade explosionGrenade) + { + Destructible = destructible; + Damage = damage; + ExplosionGrenade = explosionGrenade; + } + + + } +} diff --git a/KruacentExiled/KE.Items/API/Extensions/PlayerExtensions.cs b/KruacentExiled/KE.Items/API/Extensions/PlayerExtensions.cs new file mode 100644 index 00000000..696cf969 --- /dev/null +++ b/KruacentExiled/KE.Items/API/Extensions/PlayerExtensions.cs @@ -0,0 +1,19 @@ +using Exiled.API.Features; +using KE.Items.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Items.API.Extensions +{ + public static class PlayerExtensions + { + + public static void ItemEffectHint(this Player player, string text) + { + KECustomItem.ItemEffectHint(player, text); + } + } +} diff --git a/KruacentExiled/KE.Items/API/Features/Complexes/ComplexBase.cs b/KruacentExiled/KE.Items/API/Features/Complexes/ComplexBase.cs new file mode 100644 index 00000000..4c08cf30 --- /dev/null +++ b/KruacentExiled/KE.Items/API/Features/Complexes/ComplexBase.cs @@ -0,0 +1,147 @@ +using Exiled.API.Features; +using Exiled.API.Features.Toys; +using Exiled.API.Interfaces; +using Mirror; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using InteractableToy = LabApi.Features.Wrappers.InteractableToy; + +namespace KE.Items.API.Features.Complexes +{ + public abstract class ComplexBase : IWorldSpace + { + private GameObject gameObject; + public Vector3 Position + { + get + { + return gameObject.transform.localPosition; + } + } + + public Quaternion Rotation + { + get + { + return gameObject.transform.localRotation; + } + } + + private InteractableToy interactableToy; + private Primitive debugprim; + + private Player player; + + + public ComplexBase() + { + gameObject = new GameObject(); + CreateInteractable(gameObject); + + } + + private void CreateInteractable(GameObject gameObject) + { + interactableToy = InteractableToy.Create(networkSpawn: false); + interactableToy.Transform.parent = gameObject.transform; + interactableToy.Transform.localPosition = Vector3.zero; + interactableToy.Scale = Vector3.one; + interactableToy.Shape = AdminToys.InvisibleInteractableToy.ColliderShape.Box; + interactableToy.InteractionDuration = 1f; + interactableToy.OnSearched += OnSearched; + + CreateDebugPrim(interactableToy.GameObject); + } + private void CreateDebugPrim(GameObject gameObject) + { + debugprim = Primitive.Create(Vector3.zero, null, interactableToy.Scale, false); + debugprim.GameObject.transform.parent = interactableToy.Transform; + debugprim.Scale = gameObject.transform.localScale; + debugprim.Transform.localPosition = Vector3.zero; + debugprim.Collidable = false; + debugprim.Visible = true; + } + + private void OnSearched(LabApi.Features.Wrappers.Player ev) + { + Player player = Player.Get(ev); + + if (!AddPlayer(player)) + { + RemovePlayer(); + } + + } + + public bool AddPlayer(Player player) + { + if (!(this.player is null)) return false; + + this.player = player; + return true; + } + public bool RemovePlayer() + { + if (player is null) return false; + player = null; + return true; + } + + public void Spawn(Vector3 position,Quaternion rotation) + { + gameObject.transform.localPosition = position; + gameObject.transform.localRotation = rotation; + + + SpawnInteractable(); + LabApi.Events.Handlers.PlayerEvents.ShootingWeapon += OnShootingWeapon; + } + + private void OnShootingWeapon(LabApi.Events.Arguments.PlayerEvents.PlayerShootingWeaponEventArgs ev) + { + Player player = Player.Get(ev.Player); + if (player != this.player) return; + OnShoot(player); + + + } + + public void Unspawn() + { + LabApi.Events.Handlers.PlayerEvents.ShootingWeapon -= OnShootingWeapon; + UnspawnInteractable(); + } + + public void Destroy() + { + UnityEngine.Object.Destroy(gameObject); + interactableToy.OnSearched -= OnSearched; + interactableToy.Destroy(); + debugprim.Destroy(); + } + + private void SpawnInteractable() + { + Log.Debug("spawn"+ interactableToy.Transform.position); + Log.Debug("spawn"+ debugprim.Position); + interactableToy.Spawn(); + debugprim.Spawn(); + } + private void UnspawnInteractable() + { + NetworkServer.UnSpawn(interactableToy.GameObject); + debugprim.UnSpawn(); + } + + + + + protected abstract void OnShoot(Player player); + + + } +} diff --git a/KruacentExiled/KE.Items/API/Features/Complexes/ComplexGatling.cs b/KruacentExiled/KE.Items/API/Features/Complexes/ComplexGatling.cs new file mode 100644 index 00000000..62660f7a --- /dev/null +++ b/KruacentExiled/KE.Items/API/Features/Complexes/ComplexGatling.cs @@ -0,0 +1,21 @@ +using Exiled.API.Features; +using Exiled.API.Interfaces; +using InteractableToy = LabApi.Features.Wrappers.InteractableToy; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using Mirror; + +namespace KE.Items.API.Features.Complexes +{ + public class ComplexGatling : ComplexBase + { + protected override void OnShoot(Player player) + { + player.ShowHitMarker(Hitmarker.MaxSize); + } + } +} diff --git a/KruacentExiled/KE.Items/API/Features/KECustomGrenade.cs b/KruacentExiled/KE.Items/API/Features/KECustomGrenade.cs new file mode 100644 index 00000000..415917b2 --- /dev/null +++ b/KruacentExiled/KE.Items/API/Features/KECustomGrenade.cs @@ -0,0 +1,157 @@ +using Exiled.API.Features; +using Exiled.API.Features.Components; +using Exiled.API.Features.Pickups; +using Exiled.API.Features.Pickups.Projectiles; +using Exiled.API.Features.Spawn; +using Exiled.CustomItems.API.Features; +using Exiled.Events.EventArgs.Map; +using Exiled.Events.EventArgs.Player; +using Exiled.Events.Features; +using InventorySystem.Items.ThrowableProjectiles; +using KE.Items.API.Events; +using KE.Items.API.Features.SpawnPoints; +using KE.Utils.API.Features; +using System.Collections.Generic; +using System.Linq; +using static KE.Items.API.Features.SpawnPoints.PoseRoomSpawnPointHandler; + +namespace KE.Items.API.Features +{ + public abstract class KECustomGrenade : KECustomItem + { + public virtual float DamageModifier { get; } = 1f; + public abstract bool ExplodeOnCollision { get; } + public abstract float FuseTime { get; } + protected override void SubscribeEvents() + { + Exiled.Events.Handlers.Player.ThrowingRequest += OnInternalThrowingRequest; + Exiled.Events.Handlers.Player.ThrownProjectile += OnInternalThrownProjectile; + Exiled.Events.Handlers.Map.ExplodingGrenade += OnInternalExplodingGrenade; + Exiled.Events.Handlers.Map.ChangedIntoGrenade += OnInternalChangedIntoGrenade; + ExplodeEvent.ExplodeDestructible += OnInternalExplodeDestructible; + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Player.ThrowingRequest -= OnInternalThrowingRequest; + Exiled.Events.Handlers.Player.ThrownProjectile -= OnInternalThrownProjectile; + Exiled.Events.Handlers.Map.ExplodingGrenade -= OnInternalExplodingGrenade; + Exiled.Events.Handlers.Map.ChangedIntoGrenade -= OnInternalChangedIntoGrenade; + ExplodeEvent.ExplodeDestructible -= OnInternalExplodeDestructible; + base.UnsubscribeEvents(); + } + + + public virtual bool Check(Projectile grenade) + { + if (grenade != null) + { + return base.TrackedSerials.Contains(grenade.Serial); + } + + return false; + } + + public virtual bool Check(ExplosionGrenade explosionGrenade) + { + return Check(Pickup.Get(explosionGrenade)); + } + private void OnInternalExplodeDestructible(OnExplodeDestructibleEventsArgs ev) + { + + if (Check(ev.ExplosionGrenade)) + { + if(ev.Damage > 0f) + { + ev.Damage *= DamageModifier; + } + + OnExplodeDestructible(ev); + } + } + + + private void OnInternalThrowingRequest(ThrowingRequestEventArgs ev) + { + if (Check(ev.Item)) + { + OnThrowingRequest(ev); + } + } + private void OnInternalThrownProjectile(ThrownProjectileEventArgs ev) + { + if (Check(ev.Throwable)) + { + OnThrownProjectile(ev); + if (ev.Projectile is TimeGrenadeProjectile timeGrenadeProjectile) + { + timeGrenadeProjectile.FuseTime = FuseTime; + } + + if (ExplodeOnCollision) + { + ev.Projectile.GameObject.AddComponent().Init((ev.Player ?? Server.Host).GameObject, ev.Projectile.Base); + } + } + } + private void OnInternalExplodingGrenade(ExplodingGrenadeEventArgs ev) + { + if (Check(ev.Projectile)) + { + OnExplodingGrenade(ev); + } + } + private void OnInternalChangedIntoGrenade(ChangedIntoGrenadeEventArgs ev) + { + + + if (Check(ev.Pickup)) + { + if (ev.Projectile is TimeGrenadeProjectile timeGrenadeProjectile) + { + timeGrenadeProjectile.FuseTime = FuseTime; + } + + OnChangedIntoGrenade(ev); + if (ExplodeOnCollision) + { + ev.Projectile.GameObject.AddComponent().Init((ev.Pickup.PreviousOwner ?? Server.Host).GameObject, ev.Projectile.Base); + } + } + + } + + + protected virtual void OnExplodeDestructible(OnExplodeDestructibleEventsArgs ev) + { + + } + protected virtual void OnThrowingRequest(ThrowingRequestEventArgs ev) + { + + } + protected virtual void OnThrownProjectile(ThrownProjectileEventArgs ev) + { + + } + protected virtual void OnExplodingGrenade(ExplodingGrenadeEventArgs ev) + { + + } + protected virtual void OnChangedIntoGrenade(ChangedIntoGrenadeEventArgs ev) + { + + } + + protected override void ShowPickedUpMessage(Player player) + { + KECustomItem.Message(this, player, true); + } + + protected override void ShowSelectedMessage(Player player) + { + KECustomItem.Message(this, player); + } + } +} diff --git a/KruacentExiled/KE.Items/API/Features/KECustomItem.cs b/KruacentExiled/KE.Items/API/Features/KECustomItem.cs new file mode 100644 index 00000000..b58b9ac0 --- /dev/null +++ b/KruacentExiled/KE.Items/API/Features/KECustomItem.cs @@ -0,0 +1,386 @@ +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Pools; +using Exiled.API.Features.Spawn; +using Exiled.CustomItems.API; +using Exiled.CustomItems.API.Features; +using Exiled.Events.EventArgs.Player; +using KE.Items.API.Features.SpawnPoints; +using KE.Items.API.Interface; +using KE.Utils.API.Displays.DisplayMeow; +using KE.Utils.API.Displays.Feeds; +using KE.Utils.API.Translations; +using PlayerRoles.SpawnData; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using UnityEngine; +using static KE.Items.API.Features.SpawnPoints.PoseRoomSpawnPointHandler; +using Pickup = Exiled.API.Features.Pickups.Pickup; + +namespace KE.Items.API.Features +{ + public abstract class KECustomItem : CustomItem + { + + + private static Dictionary _typeLookup = new Dictionary(); + + private static Dictionary _nameLookup = new Dictionary(); + + + [Obsolete("Uses only the name",true)] + public sealed override uint Id { get; set; } = 0; + + public sealed override string Description { get; set; } = string.Empty; + + public abstract ItemType ItemType { get; } + + public override float Weight { get; set; } = 1; + + public sealed override ItemType Type + { + get + { + return ItemType; + } + set + { + + } + } + public const string CustomItemNameKey = "Name"; + public const string CustomItemDescriptionKey = "Desc"; + public string TranslationKeyName => Name + "_" + CustomItemNameKey; + public string TranslationKeyDesc => Name + "_" + CustomItemDescriptionKey; + + public const string CustomItemTranslationId = "CustomItem"; + protected abstract Dictionary> SetTranslation(); + + + public const string PickupKey = "Pickup"; + public const string InventoryKey = "Inventory"; + + private Dictionary> GetBasicTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [PickupKey] = "You've picked up ", + [InventoryKey] = "You've selected ", + }, + ["fr"] = new Dictionary() + { + [PickupKey] = "Tu as pris ", + [InventoryKey] = "Tu as selectionné ", + }, + }; + } + + private void OneTimeInit() + { + if (init) return; + + var trans = GetBasicTranslation(); + TranslationHub.Add(CustomItemTranslationId, trans); + init = true; + } + private bool init = false; + + public static string GetTranslation(Player player, string key) + { + return TranslationHub.Get(player, CustomItemTranslationId, key); + } + public static string GetTranslation(string lang, string key) + { + return TranslationHub.Get(lang, CustomItemTranslationId, key); + } + + public static void TranslationHint(Player player,string key) + { + ItemEffectHint(player, GetTranslation(player, key)); + } + + public static void TranslationFeed(Player player,string key) + { + HintFeed.AddFeed(player, GetTranslation(player, key)); + } + + public override void Init() + { + _typeLookup.Add(GetType(), this); + Name = Name.RemoveSpaces(); + + _nameLookup.Add(Name, this); + SubscribeEvents(); + + OneTimeInit(); + + var translate = SetTranslation(); + TranslationHub.Add(CustomItemTranslationId, translate); + } + + + public override void Destroy() + { + UnsubscribeEvents(); + _nameLookup.Remove(Name); + _typeLookup.Remove(GetType()); + } + + + + + + public static T Get() where T : KECustomItem + { + return (T)_typeLookup[typeof(T)]; + } + public new static KECustomItem Get(string name) + { + return _nameLookup[name]; + } + public static bool TryGet(string name, out KECustomItem item) + { + + + return _nameLookup.TryGetValue(name,out item); + } + + + protected override void ShowPickedUpMessage(Player player) + { + Message(this, player, true); + } + + protected override void ShowSelectedMessage(Player player) + { + Message(this, player); + } + + + public override uint Spawn(IEnumerable spawnPoints, uint limit) + { + + + + Log.Debug($"spawning {this.Name}"); + HashSet spawns = spawnPoints.ToHashSet(); + uint num = 0; + foreach (SpawnPoint spawnpoint in spawnPoints.Where(sp => sp is RoomSpawnPoint)) + { + Pickup pickup; + if (Exiled.Loader.Loader.Random.NextDouble() * 100.0 >= (double)spawnpoint.Chance || (limit != 0 && num >= limit)) + { + continue; + } + spawns.Remove(spawnpoint); + RoomSpawnPoint room = spawnpoint as RoomSpawnPoint; + ItemSpawn spawn = PoseRoomSpawnPointHandler.UseRandomPose(room.Room); + Log.Debug(room.Room+ " : "+PoseRoomSpawnPointHandler.UsablePoses.Count(p => p.roomType == room.Room)); + Log.Debug($"spawning {this.Name} in {room.Room}" ); + + if (spawn != null) + { + Log.Debug($"spawning custom pos"); + pickup = Spawn(spawn.Position); + } + else + { + Log.Error($"can't spawn ({Name}) in custom ({room.Room})"); + pickup = Spawn(spawnpoint.Position); + } + + + + if (pickup != null) + { + num++; + } + + + } + + return base.Spawn(spawns, limit-num); + } + + + + public void ReplacePickup(Pickup pickup) + { + Vector3 position = pickup.Position; + pickup.Destroy(); + Spawn(position); + } + + + internal static void Message(CustomItem c, Player player, bool pickedUp = false) + { + KECustomItem kECustomItem = c as KECustomItem; + StringBuilder builder = StringBuilderPool.Pool.Get(); + + string lang = TranslationHub.GetLang(player); + + if (MainPlugin.Instance.SettingsHandler.GetPrefixes(player)) + { + if (pickedUp) + { + builder.Append("(P)"); + } + else + { + builder.Append("(I)"); + } + } + else + { + if (pickedUp) + { + builder.Append(TranslationHub.Get(lang,CustomItemTranslationId,PickupKey)); + } + else + { + builder.Append(TranslationHub.Get(lang, CustomItemTranslationId, InventoryKey)); + } + } + + builder.Append(""); + builder.Append(TranslationHub.Get(lang, CustomItemTranslationId, kECustomItem.TranslationKeyName)); + builder.AppendLine(""); + + bool desc = MainPlugin.Instance.SettingsHandler.GetDescriptionsSettings(player); + + if (desc) + { + builder.Append(TranslationHub.Get(lang, CustomItemTranslationId, kECustomItem.TranslationKeyDesc)); + if (c is IUpgradableCustomItem ci) + { + builder.Append(""); + foreach (var a in ci.Upgrade) + { + builder.Append(a.Key); + builder.Append(" ("); + builder.Append(a.Value.Chance); + builder.Append("%) -> ???"); + } + builder.AppendLine(""); + } + + } + + + + float delay = MainPlugin.Instance.SettingsHandler.GetTime(player); + + DisplayHandler.Instance.AddHint(MainPlugin.HintPlacement, player, StringBuilderPool.Pool.ToStringReturn(builder), delay); + + + } + + + + + public static void ItemEffectHint(Player player, string text) + { + float delay = MainPlugin.Instance.SettingsHandler.GetTimeEffect(player); + + + DisplayHandler.Instance.AddHint(MainPlugin.ItemEffectPlacement, player, text, delay); + } + + + public virtual bool TryRegister() + { + + + if (Registered.Contains(this)) + { + Log.Error($"{Name} is already registered"); + return false; + } + + + if (TryGet(Name,out _)) + { + Log.Error($"A Custom item already have the name {Name}"); + return false; + } + + + if(ItemType == ItemType.None) + { + Log.Error($"No ItemType for {Name}"); + return false; + } + + + Registered.Add(this); + Init(); + return true; + + + } + + public static bool IsConsideredViolent(KECustomItem item) + { + if(item is IViolentItem violent) + { + return violent.IsViolent; + } + + if (item.ItemType.IsWeapon(true)) + { + return true; + } + ItemCategory itemCategory = item.ItemType.GetCategory(); + + if (itemCategory == ItemCategory.Firearm) + { + return true; + } + ProjectileType projectileType = item.ItemType.GetProjectileType(); + if (projectileType == ProjectileType.FragGrenade || projectileType == ProjectileType.Scp018) + { + return true; + } + + + return false; + } + + + +//obselete warning +#pragma warning disable CS0618 +#pragma warning disable CS0672 + protected sealed override void OnDropping(DroppingItemEventArgs ev) + { + base.OnDropping(ev); + } + +#pragma warning restore CS0618 +#pragma warning restore CS0672 + public static void RegisterItems(Assembly assembly = null) + { + IEnumerable items = KE.Utils.API.ReflectionHelper.GetObjects(assembly); + + foreach(KECustomItem customItem in items) + { + customItem.TryRegister(); + } + + } + + public new static void UnregisterItems() + { + foreach(CustomItem item in Registered) + { + item.Unregister(); + } + } + } +} diff --git a/KruacentExiled/KE.Items/API/Features/KECustomKeycard.cs b/KruacentExiled/KE.Items/API/Features/KECustomKeycard.cs new file mode 100644 index 00000000..8a8b1a57 --- /dev/null +++ b/KruacentExiled/KE.Items/API/Features/KECustomKeycard.cs @@ -0,0 +1,79 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Doors; +using Exiled.API.Features.Pickups; +using Exiled.API.Features.Spawn; +using Exiled.CustomItems.API.Features; +using KE.Items.API.Features.SpawnPoints; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static KE.Items.API.Features.SpawnPoints.PoseRoomSpawnPointHandler; + +namespace KE.Items.API.Features +{ + public abstract class KECustomKeycard : CustomKeycard + { + + public override KeycardPermissions Permissions { get; set; } = KeycardPermissions.None; + + + public override uint Spawn(IEnumerable spawnPoints, uint limit) + { + Log.Debug($"spawning {this.Name}"); + HashSet spawns = spawnPoints.ToHashSet(); + uint num = 0; + foreach (SpawnPoint spawnpoint in spawnPoints.Where(sp => sp is RoomSpawnPoint)) + { + Pickup pickup; + if (Exiled.Loader.Loader.Random.NextDouble() * 100.0 >= (double)spawnpoint.Chance || (limit != 0 && num >= limit)) + { + continue; + } + spawns.Remove(spawnpoint); + RoomSpawnPoint room = spawnpoint as RoomSpawnPoint; + ItemSpawn spawn = PoseRoomSpawnPointHandler.UseRandomPose(room.Room); + + Log.Debug($"spawning {this.Name} in {room.Room}"); + Log.Debug(room.Room + " : " + PoseRoomSpawnPointHandler.UsablePoses.Count(p => p.roomType == room.Room)); + + if (spawn != null) + { + Log.Debug($"spawning custom pos"); + pickup = Spawn(spawn.Position); + } + else + { + Log.Error($"can't spawn ({Name}) in custom ({room.Room})"); + pickup = Spawn(spawnpoint.Position); + } + + + + if (pickup != null) + { + num++; + } + + + } + + return base.Spawn(spawns, limit - num); + } + + + + protected override void ShowPickedUpMessage(Player player) + { + KECustomItem.Message(this, player, true); + } + + protected override void ShowSelectedMessage(Player player) + { + KECustomItem.Message(this, player); + } + + } +} diff --git a/KruacentExiled/KE.Items/API/Features/KECustomWeapon.cs b/KruacentExiled/KE.Items/API/Features/KECustomWeapon.cs new file mode 100644 index 00000000..3e90cfbf --- /dev/null +++ b/KruacentExiled/KE.Items/API/Features/KECustomWeapon.cs @@ -0,0 +1,221 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Items; +using Exiled.API.Features.Pickups; +using Exiled.API.Features.Spawn; +using Exiled.CustomItems.API.Features; +using Exiled.Events.EventArgs.Player; +using Exiled.Events.Features; +using InventorySystem.Items.Firearms.Modules; +using KE.Items.API.Features.SpawnPoints; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Items.API.Features +{ + public abstract class KECustomWeapon : KECustomItem + { + + + public virtual float Damage { get; set; } = -1; + public virtual byte ClipSize { get; } + protected override void SubscribeEvents() + { + Exiled.Events.Handlers.Player.Shooting += InternalOnShooting; + Exiled.Events.Handlers.Player.Hurting += InternalOnHurting; + Exiled.Events.Handlers.Player.ReloadingWeapon += OnInternalReloading; + Exiled.Events.Handlers.Player.ReloadedWeapon += OnInternalReloaded; + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Player.Shooting -= InternalOnShooting; + Exiled.Events.Handlers.Player.Hurting -= InternalOnHurting; + Exiled.Events.Handlers.Player.ReloadingWeapon -= OnInternalReloading; + Exiled.Events.Handlers.Player.ReloadedWeapon -= OnInternalReloaded; + base.UnsubscribeEvents(); + } + + + private void InternalOnHurting(HurtingEventArgs ev) + { + + if (ev.Attacker is null) return; + + if (Check(ev.Attacker.CurrentItem)) + { + OnHurting(ev); + } + + } + private void InternalOnShooting(ShootingEventArgs ev) + { + if (Check(ev.Item)) + { + OnShooting(ev); + } + } + + private void OnInternalReloading(ReloadingWeaponEventArgs ev) + { + if (Check(ev.Item)) + { + if (ClipSize > 0 && ev.Firearm.TotalAmmo >= ClipSize) + { + ev.IsAllowed = false; + } + else + { + OnReloading(ev); + } + } + } + private void OnInternalReloaded(ReloadedWeaponEventArgs ev) + { + + if (!Check(ev.Item)) + return; + + if (ClipSize > 0) + { + int ammoChambered = ((AutomaticActionModule)ev.Firearm.Base.Modules.FirstOrDefault(x => x is AutomaticActionModule))?.AmmoStored ?? 0; + int ammoToGive = ClipSize - ammoChambered; + + AmmoType ammoType = ev.Firearm.AmmoType; + int firearmAmmo = ev.Firearm.MagazineAmmo; + int ammoDrop = -(ClipSize - firearmAmmo - ammoChambered); + + int ammoInInventory = ev.Player.GetAmmo(ammoType) + firearmAmmo; + if (ammoToGive < ammoInInventory) + { + ev.Firearm.MagazineAmmo = ammoToGive; + int newAmmo = ev.Player.GetAmmo(ammoType) + ammoDrop; + ev.Player.SetAmmo(ammoType, (ushort)newAmmo); + } + else + { + ev.Firearm.MagazineAmmo = ammoInInventory; + ev.Player.SetAmmo(ammoType, 0); + } + } + + OnReloaded(ev); + } + + protected virtual void OnShooting(ShootingEventArgs ev) + { + + } + + protected virtual void OnHurting(HurtingEventArgs ev) + { + if (ev.IsAllowed && Damage >= 0) + { + ev.Amount = Damage; + + } + } + protected virtual void OnReloading(ReloadingWeaponEventArgs ev) + { + + } + + protected virtual void OnReloaded(ReloadedWeaponEventArgs ev) + { + + } + + + public override Pickup Spawn(Vector3 position, Player previousOwner = null) + { + if (!(Item.Create(Type) is Firearm firearm)) + { + Log.Debug("Spawn: Item is not Firearm."); + return null; + } + + + Pickup pickup = firearm.CreatePickup(position); + if (pickup == null) + { + Log.Debug("Spawn: Pickup is null."); + return null; + } + + if (ClipSize > 0) + { + firearm.MagazineAmmo = ClipSize; + } + + pickup.Weight = Weight; + pickup.Scale = Scale; + if ((object)previousOwner != null) + { + pickup.PreviousOwner = previousOwner; + } + + base.TrackedSerials.Add(pickup.Serial); + return pickup; + } + + public override Pickup Spawn(Vector3 position, Item item, Player previousOwner = null) + { + if (item is Firearm firearm) + { + + if (ClipSize > 0) + { + firearm.MagazineAmmo = ClipSize; + } + + int magazineAmmo = firearm.MagazineAmmo; + Log.Debug(string.Format("{0}.{1}: Spawning weapon with {2} ammo.", "Name", "Spawn", magazineAmmo)); + Pickup pickup = firearm.CreatePickup(position); + pickup.Scale = Scale; + if ((object)previousOwner != null) + { + pickup.PreviousOwner = previousOwner; + } + + base.TrackedSerials.Add(pickup.Serial); + return pickup; + } + + return base.Spawn(position, item, previousOwner); + } + + public override void Give(Exiled.API.Features.Player player, bool displayMessage = true) + { + Exiled.API.Features.Items.Item item = player.AddItem(Type); + if (item is Firearm firearm) + { + + if (ClipSize > 0) + { + firearm.MagazineAmmo = ClipSize; + } + } + + Log.Debug(string.Format("{0}: Adding {1} to tracker.", "Give", item.Serial)); + base.TrackedSerials.Add(item.Serial); + OnAcquired(player, item, displayMessage); + } + + + protected override void ShowPickedUpMessage(Player player) + { + Message(this, player, true); + } + + protected override void ShowSelectedMessage(Player player) + { + Message(this, player); + } + + } +} diff --git a/KruacentExiled/KE.Items/API/Features/SpawnPoints/PoseRoomSpawnPoint.cs b/KruacentExiled/KE.Items/API/Features/SpawnPoints/PoseRoomSpawnPoint.cs new file mode 100644 index 00000000..520fc9c2 --- /dev/null +++ b/KruacentExiled/KE.Items/API/Features/SpawnPoints/PoseRoomSpawnPoint.cs @@ -0,0 +1,117 @@ +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Toys; +using MEC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Items.API.Features.SpawnPoints +{ + public static class PoseRoomSpawnPointHandler + { + public class ItemSpawn : IEquatable + { + public readonly RoomType roomType; + + //item position in the room at 0° + public readonly Vector3 localposition; + //item rotation + public readonly Quaternion localrotation; + + + /// + /// don't use the corridor + /// + /// + /// + /// + public ItemSpawn(RoomType roomType,Vector3 position,Quaternion rotation) + { + this.roomType = roomType; + this.localposition = position; + this.localrotation = rotation; + } + + private Room Room + { + get + { + return Room.List.FirstOrDefault(r => r.Type == roomType); + } + } + + public Vector3 Position + { + get + { + return Room.Position + Room.Rotation * localposition; + } + } + + public Quaternion Rotation + { + get + { + return Room.Rotation * localrotation; + } + } + + + public bool Equals(ItemSpawn other) + { + return other.roomType == roomType && other.localposition == localposition && other.localrotation == localrotation; + } + } + + public static readonly HashSet AllPoses = new HashSet(); + private static HashSet usablePoses = new HashSet(); + public static IReadOnlyCollection UsablePoses => usablePoses; + + public static ItemSpawn UseRandomPose(RoomType roomType) + { + + if (UsablePoses.Count(r => r.roomType == roomType) <= 0) + { + return null; + } + Log.Debug("count before =" + UsablePoses.Count(r => r.roomType == roomType)); + ItemSpawn result = UsablePoses.GetRandomValue(r => r.roomType == roomType); + usablePoses.Remove(result); + Log.Debug("count after =" + UsablePoses.Count(r => r.roomType == roomType)); + return result; + + } + + /// + /// + /// + /// + /// add the poses should be from a zero rotation room + public static void AddRoomPoses(HashSet poses) + { + + foreach(ItemSpawn item in poses) + { + AllPoses.Add(item); + usablePoses.Add(item); + } + + } + + public static void Reset() + { + usablePoses = AllPoses.ToHashSet(); + } + + + + } + + + +} diff --git a/KruacentExiled/KE.Items/API/Interface/CustomItemEffect.cs b/KruacentExiled/KE.Items/API/Interface/CustomItemEffect.cs new file mode 100644 index 00000000..496e5f10 --- /dev/null +++ b/KruacentExiled/KE.Items/API/Interface/CustomItemEffect.cs @@ -0,0 +1,23 @@ +using Exiled.API.Features; +using Exiled.CustomItems.API.Features; +using Exiled.Events.EventArgs.Map; +using Exiled.Events.EventArgs.Player; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Items.API.Interface +{ + public abstract class CustomItemEffect + { + public abstract void Effect(UsedItemEventArgs ev); + + public abstract void Effect(ExplodingGrenadeEventArgs ev); + + public abstract void Effect(DroppingItemEventArgs ev); + + } +} diff --git a/KruacentExiled/KE.Items/API/Interface/ICustomPickupModel.cs b/KruacentExiled/KE.Items/API/Interface/ICustomPickupModel.cs new file mode 100644 index 00000000..c1bb22df --- /dev/null +++ b/KruacentExiled/KE.Items/API/Interface/ICustomPickupModel.cs @@ -0,0 +1,9 @@ +using KE.Items.API.Core.Models; + +namespace KE.Items.API.Interface +{ + public interface ICustomPickupModel + { + public PickupModel PickupModel { get; } + } +} diff --git a/KruacentExiled/KE.Items/API/Interface/ILumosItem.cs b/KruacentExiled/KE.Items/API/Interface/ILumosItem.cs new file mode 100644 index 00000000..3b540fc8 --- /dev/null +++ b/KruacentExiled/KE.Items/API/Interface/ILumosItem.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Items.API.Interface +{ + internal interface ILumosItem + { + + UnityEngine.Color Color { get; set; } + } +} diff --git a/KruacentExiled/KE.Items/API/Interface/ISwitchableEffect.cs b/KruacentExiled/KE.Items/API/Interface/ISwitchableEffect.cs new file mode 100644 index 00000000..c3889dba --- /dev/null +++ b/KruacentExiled/KE.Items/API/Interface/ISwitchableEffect.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + + + +namespace KE.Items.API.Interface +{ + public interface ISwitchableEffect + { + CustomItemEffect Effect { get; set; } + } +} diff --git a/KruacentExiled/KE.Items/API/Interface/IUpgradableCustomItem.cs b/KruacentExiled/KE.Items/API/Interface/IUpgradableCustomItem.cs new file mode 100644 index 00000000..4aa040b0 --- /dev/null +++ b/KruacentExiled/KE.Items/API/Interface/IUpgradableCustomItem.cs @@ -0,0 +1,15 @@ +using KE.Items.API.Core.Upgrade; +using Scp914; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Items.API.Interface +{ + public interface IUpgradableCustomItem + { + IReadOnlyDictionary Upgrade { get; } + } +} diff --git a/KruacentExiled/KE.Items/API/Interface/IUse.cs b/KruacentExiled/KE.Items/API/Interface/IUse.cs new file mode 100644 index 00000000..ff83f033 --- /dev/null +++ b/KruacentExiled/KE.Items/API/Interface/IUse.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + + + +namespace KE.Items.API.Interface +{ + public interface IReversible + { + + public void OnDisable(); + } +} diff --git a/KruacentExiled/KE.Items/API/Interface/IViolentItem.cs b/KruacentExiled/KE.Items/API/Interface/IViolentItem.cs new file mode 100644 index 00000000..28b82a1e --- /dev/null +++ b/KruacentExiled/KE.Items/API/Interface/IViolentItem.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + + +namespace KE.Items.API.Interface +{ + public interface IViolentItem + { + bool IsViolent { get; } + + } +} diff --git a/KruacentExiled/KE.Items/CommandPos.cs b/KruacentExiled/KE.Items/CommandPos.cs new file mode 100644 index 00000000..394bdac1 --- /dev/null +++ b/KruacentExiled/KE.Items/CommandPos.cs @@ -0,0 +1,56 @@ +using CommandSystem; +using Exiled.API.Features; +using Exiled.API.Features.Toys; +using MEC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Items +{ + + [CommandHandler(typeof(RemoteAdminCommandHandler))] + internal class CommandPos : ICommand + { + public string Command => "curpos"; + + public string[] Aliases => new string[0]; + + public string Description => "position"; + + public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) + { + var p = Player.Get(sender); + response = string.Empty; + + if(p != null) + { + Primitive prim = Primitive.Create(p.Position, null, Vector3.one * .1f, true); + + Timing.CallDelayed(3f,prim.Destroy); + + if(p.CurrentRoom == null) + { + response = p.Position.ToString(); + } + else + { + response = p.CurrentRoom.Type + + "\n" + p.CurrentRoom.LocalPosition(p.Position) + + "\n" + p.CurrentRoom.Rotation.eulerAngles + + "\n" + p.Position.ToString() + + "\n" + p.CurrentRoom.LocalPosition(p.Position); + } + + + + return true; + } + response = "no"; + return false; + } + } +} diff --git a/KruacentExiled/KE.Items/CommandPosRoom.cs b/KruacentExiled/KE.Items/CommandPosRoom.cs new file mode 100644 index 00000000..7d7899fb --- /dev/null +++ b/KruacentExiled/KE.Items/CommandPosRoom.cs @@ -0,0 +1,86 @@ +using CommandSystem; +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Toys; +using KE.Items.API.Features.SpawnPoints; +using MEC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using static KE.Items.API.Features.SpawnPoints.PoseRoomSpawnPointHandler; + +namespace KE.Items +{ + + [CommandHandler(typeof(RemoteAdminCommandHandler))] + internal class CommandPosRoom : ICommand + { + public string Command => "roompos"; + + public string[] Aliases => new string[0]; + + public string Description => "position"; + + public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) + { + var p = Player.Get(sender); + response = string.Empty; + + if(p != null) + { + Room room = p.CurrentRoom; + + if(room is null) + { + response = "no room"; + return false; + } + + ShowPoses(room.Type); + response= "ok"; + return true; + } + response = "no"; + return false; + } + + public static void ShowPoses(RoomType roomType) + { + List primitives = new List(); + Room room = Room.Get(roomType); + foreach (ItemSpawn pose in AllPoses.Where(p => p.roomType == roomType)) + { + + + Log.Info(pose.localposition); + Log.Info(pose.Position); + Color color = Color.red; + + if (UsablePoses.Contains(pose)) + { + color = Color.green; + } + + + Primitive prim = Primitive.Create(pose.Position, null, Vector3.one * .1f, false, color); + prim.Collidable = false; + prim.Spawn(); + primitives.Add(prim); + + + } + + + Timing.CallDelayed(5, delegate + { + foreach (Primitive primive in primitives) + { + primive.Destroy(); + } + }); + } + } +} diff --git a/KruacentExiled/KE.Items/Commands/ForceAddFeed.cs b/KruacentExiled/KE.Items/Commands/ForceAddFeed.cs new file mode 100644 index 00000000..585b3342 --- /dev/null +++ b/KruacentExiled/KE.Items/Commands/ForceAddFeed.cs @@ -0,0 +1,46 @@ +using CommandSystem; +using Exiled.API.Features; +using KE.Utils.API.Displays.Feeds; +using MEC; +using System; + +namespace KE.Items.Commands +{ + [CommandHandler(typeof(RemoteAdminCommandHandler))] + internal class ForceAddFeed : Utils.API.Commands.KECommand + { + public override string Command => "forceaddfeed"; + + public override string[] Aliases => new string[0]; + + public override string Description => "add a feed"; + + public override string[] Usage => new string[0]; + + public override bool ExecuteCommand(ArraySegment arguments, ICommandSender sender, out string response) + { + Player player = Player.Get(sender); + if(player == null) + { + + response = "no"; + return false; + } + + + + HintFeed hintfeed = HintFeed.GetOrCreate(player); + + + HintFeed.AddFeed(player, "test1"); + + Timing.CallDelayed(1, () => HintFeed.AddFeed(player, "test2")); + Timing.CallDelayed(2, () => HintFeed.AddFeed(player, "test3")); + response = "ok"; + return true; + } + + + } + +} \ No newline at end of file diff --git a/KruacentExiled/KE.Items/Commands/Give.cs b/KruacentExiled/KE.Items/Commands/Give.cs new file mode 100644 index 00000000..929b1ca6 --- /dev/null +++ b/KruacentExiled/KE.Items/Commands/Give.cs @@ -0,0 +1,104 @@ +using CommandSystem; +using Exiled.API.Features; +using Exiled.API.Features.Pools; +using Exiled.CustomItems.API.Features; +using Exiled.Permissions.Extensions; +using KE.Items.API.Features; +using RemoteAdmin; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Items.Commands +{ + internal class Give : ICommand + { + public string Command => "give"; + + public string[] Aliases => new string[] { "g" }; + + public string Description => "give"; + + public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) + { + if (!sender.CheckPermission("customitems.give")) + { + response = "Permission Denied, required: customitems.give"; + return false; + } + + if (arguments.Count == 0) + { + response = "give [Nickname/PlayerID/UserID/all/*]"; + return false; + } + + if (!KECustomItem.TryGet(arguments.At(0), out CustomItem item)) + { + response = $"Custom item {arguments.At(0)} not found!"; + return false; + } + + if (arguments.Count == 1) + { + if (sender is PlayerCommandSender playerCommandSender) + { + Player player = Player.Get(playerCommandSender.SenderId); + + if (!CheckEligible(player)) + { + response = "You cannot receive custom items!"; + return false; + } + + item.Give(player); + response = $"{item.Name} given to {player.Nickname} ({player.UserId})"; + return true; + } + + response = "Failed to provide a valid player, please follow the syntax."; + return false; + } + + string identifier = string.Join(" ", arguments.Skip(1)); + + switch (identifier) + { + case "*": + case "all": + List eligiblePlayers = Player.List.Where(CheckEligible).ToList(); + foreach (Player ply in eligiblePlayers) + item.Give(ply); + + response = $"Custom item {item.Name} given to all players who can receive them ({eligiblePlayers.Count} players)"; + return true; + default: + break; + } + + IEnumerable list = Player.GetProcessedData(arguments, 1); + int num = 0; + + if (list.IsEmpty()) + { + response = "Cannot find player! Try using the player ID!"; + return false; + } + + foreach (Player player in list) + { + if (CheckEligible(player)) + { + item.Give(player); + num++; + } + } + + response = $"{item.Name} given to {num} players!"; + return true; + } + private bool CheckEligible(Player player) => player.IsAlive && !player.IsCuffed && (player.Items.Count < 8); + } +} diff --git a/KruacentExiled/KE.Items/Commands/KECustomItems.cs b/KruacentExiled/KE.Items/Commands/KECustomItems.cs new file mode 100644 index 00000000..52aab765 --- /dev/null +++ b/KruacentExiled/KE.Items/Commands/KECustomItems.cs @@ -0,0 +1,23 @@ +using CommandSystem; +using KE.CustomRoles.Commands.KECR; +using System; + +namespace KE.Items.Commands +{ + [CommandHandler(typeof(RemoteAdminCommandHandler))] + internal class KECustomItems : KE.Utils.API.Commands.KEParentCommand + { + public override string Command => "keci"; + + public override string[] Aliases => new string[0]; + + public override string Description => "kecustom item parent command"; + + public override void LoadGeneratedCommands() + { + RegisterCommand(new List()); + RegisterCommand(new Give()); + } + + } +} diff --git a/KruacentExiled/KE.Items/Commands/List.cs b/KruacentExiled/KE.Items/Commands/List.cs new file mode 100644 index 00000000..db6a4d01 --- /dev/null +++ b/KruacentExiled/KE.Items/Commands/List.cs @@ -0,0 +1,44 @@ +using CommandSystem; +using Exiled.API.Features.Pools; +using Exiled.CustomItems.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Items.Commands +{ + internal class List : ICommand + { + public string Command => "list"; + + public string[] Aliases => new string[] { "l" }; + + public string Description => "list"; + + public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) + { + + StringBuilder sb = StringBuilderPool.Pool.Get().AppendLine(); + + + + sb.Append("[Registered custom items (").Append(CustomItem.Registered.Count).AppendLine(")]"); + + foreach (CustomItem customItem in CustomItem.Registered.OrderBy(item => item.Name)) + { + sb.Append('['); + sb.Append(customItem.Name); + sb.Append(" ("); + sb.Append(customItem.Type); + sb.Append(')'); + sb.AppendLine("]"); + } + + + response = StringBuilderPool.Pool.ToStringReturn(sb); + return true; + } + } +} diff --git a/KruacentExiled/KE.Items/Config.cs b/KruacentExiled/KE.Items/Config.cs new file mode 100644 index 00000000..5e8354e8 --- /dev/null +++ b/KruacentExiled/KE.Items/Config.cs @@ -0,0 +1,19 @@ +using Exiled.API.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Items +{ + public class Config : IConfig + { + public bool IsEnabled { get; set; } = true; + public bool Debug { get; set; } = false; + public float LightRefreshRate { get; set; } = .01f; + public float ModelRefreshRate { get; set; } = .1f; + public string SoundLocation { get; set; } = "C:\\Users\\Patrique\\AppData\\Roaming\\EXILED\\Plugins\\audio"; + public int Position { get; set; } = 300; + } +} diff --git a/KruacentExiled/KE.Items/Items/Defibrilator.cs b/KruacentExiled/KE.Items/Items/Defibrilator.cs new file mode 100644 index 00000000..4013d824 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/Defibrilator.cs @@ -0,0 +1,225 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using Exiled.API.Features.Spawn; +using Exiled.CustomItems.API.Features; +using Exiled.Events.EventArgs.Player; +using KE.Items.API.Features; +using KE.Items.API.Interface; +using KE.Utils.API.Displays.Feeds; +using MEC; +using PlayerRoles; +using PlayerRoles.Ragdolls; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using YamlDotNet.Core.Tokens; + +public class Defibrillator : KECustomItem, ILumosItem +{ + + public const string TranslationFailed = "DefibrillatorFailed"; + public const string TranslationExpired = "DefibrillatorExpired"; + public const string TranslationTooFar = "DefibrillatorTooFar"; + public const string TranslationRevived = "DefibrillatorRevived"; + public const string TranslationSucceed = "DefibrillatorSucceed"; + public const string TranslationInProgress = "DefibrillatorInProgress"; + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Defibrillator", + [TranslationKeyDesc] = "Aim for a dead body to try to resuscitate him", + [TranslationFailed] = "Failed!", + [TranslationExpired] = "Body expired.", + [TranslationTooFar] = "Aim at a body.", + [TranslationRevived] = "Revived by %MedicName%\nyou got brian damage", + [TranslationSucceed] = "Successfully ressucitate %PatientName%!", + [TranslationInProgress] = "In progress", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Défibrillateur", + [TranslationKeyDesc] = "Visez un cadavre de près pour tenter une réanimation.", + [TranslationFailed] = "Réanimation échouée !", + [TranslationExpired] = "Mort trop ancienne.", + [TranslationTooFar] = "Rapprochez-vous ou visez un cadavre.", + [TranslationRevived] = "Réanimé par %MedicName%\nvous avez des traumatisme crânien", + [TranslationSucceed] = "Réanimation réussie sur %PatientName% !", + [TranslationInProgress] = "Réanimation en cours...", + }, + }; + } + public override ItemType ItemType => ItemType.SCP1853; + public override string Name { get; set; } = "Defibrillator"; + public override float Weight { get; set; } = 1.0f; + public UnityEngine.Color Color { get; set; } = UnityEngine.Color.magenta; + + public override SpawnProperties SpawnProperties { get; set; } = new SpawnProperties() + { + Limit = 4, + DynamicSpawnPoints = new List + { + new DynamicSpawnPoint() { Chance = 50, Location = SpawnLocationType.Inside079Secondary }, + new DynamicSpawnPoint() { Chance = 10, Location = SpawnLocationType.Inside096 }, + new DynamicSpawnPoint() { Chance = 10, Location = SpawnLocationType.Inside330 }, + new DynamicSpawnPoint() { Chance = 10, Location = SpawnLocationType.Inside939Cryo }, + }, + LockerSpawnPoints = new List + { + new LockerSpawnPoint(){ Chance= 50, Type = LockerType.Medkit, }, + new LockerSpawnPoint(){ Chance= 20, Type = LockerType.Misc, }, + } + }; + + private struct DeathData + { + public float Time; + public RoleTypeId Role; + } + + private Dictionary _deathRecords; + private const float MaxReviveTime = 60f; + private const float RaycastDistance = 2.5f; + + protected override void SubscribeEvents() + { + _deathRecords = new Dictionary(); + Exiled.Events.Handlers.Player.UsingItem += OnUsingItem; + Exiled.Events.Handlers.Player.Dying += OnDying; + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Player.UsingItem -= OnUsingItem; + Exiled.Events.Handlers.Player.Dying -= OnDying; + _deathRecords = null; + base.UnsubscribeEvents(); + } + + private void OnDying(DyingEventArgs ev) + { + if (ev.Player == null) return; + + _deathRecords[ev.Player] = new DeathData + { + Time = Time.time, + Role = ev.Player.Role.Type + }; + } + + private void OnUsingItem(UsingItemEventArgs ev) + { + if (!Check(ev.Item)) return; + ev.IsAllowed = false; + Player player = ev.Player; + + Log.Debug($"[Defib] Tentative par {player.Nickname}"); + + if (Physics.Raycast(player.CameraTransform.position, player.CameraTransform.forward, out RaycastHit hit, RaycastDistance)) + { + Log.Debug($"[Defib] Raycast a touché : {hit.collider.name}"); + + Collider[] colliders = Physics.OverlapSphere(hit.point, 2.5f); + BasicRagdoll foundRagdoll = null; + + foreach (Collider col in colliders) + { + foundRagdoll = col.GetComponentInParent(); + if (foundRagdoll != null) break; + } + + if (foundRagdoll != null) + { + Player target = Player.Get(foundRagdoll.NetworkInfo.OwnerHub); + Log.Debug($"[Defib] Cadavre trouvé ! Owner: {(target != null ? target.Nickname : "Inconnu")}"); + + if (target != null && target.Role.Type == RoleTypeId.Spectator && _deathRecords.TryGetValue(target, out DeathData data)) + { + if (Time.time - data.Time <= MaxReviveTime) + { + Log.Debug("[Defib] RÉANIMATION LANCÉE."); + player.RemoveItem(ev.Item); + Timing.RunCoroutine(ReviveSequence(player, target, foundRagdoll, data.Role)); + return; + } + TranslationFeed(player, TranslationExpired); + return; + } + } + } + TranslationFeed(player, TranslationTooFar); + } + + private IEnumerator ReviveSequence(Player medic, Player patient, BasicRagdoll ragdoll, RoleTypeId previousRole) + { + Vector3 lightPos = ragdoll.CenterPoint.position + Vector3.up * 0.75f; + var shockLight = LabApi.Features.Wrappers.LightSourceToy.Create(lightPos); + shockLight.Color = UnityEngine.Color.cyan; + shockLight.Intensity = 50f; + shockLight.Range = 7f; + shockLight.Spawn(); + + TranslationFeed(medic, TranslationInProgress); + + float elapsed = 0f; + while (elapsed < 1.0f) + { + if (Vector3.Distance(medic.Position, ragdoll.transform.position) > RaycastDistance + 1f) + { + TranslationFeed(medic, TranslationFailed); + shockLight.Destroy(); + CustomItem.TryGive(medic, "Defibrillator",false); + yield break; + } + + shockLight.Intensity = (elapsed % 0.2f > 0.1f) ? 100f : 20f; + + elapsed += 0.1f; + yield return Timing.WaitForSeconds(0.1f); + } + + shockLight.Intensity = 200f; + + yield return Timing.WaitForSeconds(0.2f); + shockLight.Destroy(); + + patient.Role.Set(previousRole, SpawnReason.Revived, RoleSpawnFlags.All); + + yield return Timing.WaitForSeconds(0.4f); + + if (patient == null) yield break; + + if (patient.IsAlive) + { + patient.Position = ragdoll.transform.position + Vector3.up * 0.5f; + + patient.Health = patient.MaxHealth *.2f; + patient.EnableEffect(EffectType.Flashed, 2f); + patient.EnableEffect(EffectType.Concussed, 20f); + patient.EnableEffect(EffectType.Deafened, 20f); + + + string msgMedic = GetTranslation(medic, TranslationSucceed); + string msgPatient = GetTranslation(medic, TranslationRevived); + + + HintFeed.AddFeed(medic, msgMedic.Replace("%PatientName%",patient.Nickname)); + KECustomItem.ItemEffectHint(patient, msgPatient.Replace("%MedicName%", medic.Nickname)); + + _deathRecords.Remove(patient); + + if (ragdoll != null) + { + Object.Destroy(ragdoll.gameObject); + } + } + else + { + Log.Error($"[Defib] ÉCHEC : Le joueur {patient.Nickname} n'a pas spawn."); + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Items/Items/DeployableWall.cs b/KruacentExiled/KE.Items/Items/DeployableWall.cs new file mode 100644 index 00000000..41bd7c4b --- /dev/null +++ b/KruacentExiled/KE.Items/Items/DeployableWall.cs @@ -0,0 +1,88 @@ +using Exiled.API.Enums; +using Exiled.API.Features.Attributes; +using Exiled.API.Features.Spawn; +using Exiled.CustomItems.API.Features; +using System.Collections.Generic; +using UnityEngine; +using Exiled.Events.EventArgs.Player; +using KE.Items.API.Interface; +using KE.Items.Items.ItemEffects; +using KE.Items.API.Features; + +namespace KE.Items.Items +{ + public class DeployableWall : KECustomItem, ILumosItem, ISwitchableEffect + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Deployable Wall", + [TranslationKeyDesc] = "Drop to deploy a wall", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Mur", + [TranslationKeyDesc] = "Lâcher pour faire un mur", + }, + }; + } + public override ItemType ItemType => ItemType.KeycardJanitor; + public override string Name { get; set; } = "Deployable Wall"; + public override float Weight { get; set; } = 0.65f; + public Color Color { get; set; } = Color.green; + + public CustomItemEffect Effect { get; set; } + public override SpawnProperties SpawnProperties { get; set; } = new SpawnProperties() + { + Limit = 2, + DynamicSpawnPoints = new List + { + new DynamicSpawnPoint() + { + Chance=25, + Location = SpawnLocationType.Inside049Armory, + }, + new DynamicSpawnPoint() + { + Chance=25, + Location = SpawnLocationType.InsideLczArmory, + } + }, + LockerSpawnPoints = new List + { + new LockerSpawnPoint() + { + Chance=50, + Type = LockerType.RifleRack, + }, + } + + }; + + public DeployableWall() + { + Effect = new DeployableWallEffect(); + } + + protected override void OnDroppingItem(DroppingItemEventArgs ev) + { + if (ev.IsThrown) + { + ev.IsAllowed = true; + return; + } + + ev.IsAllowed = false; + ev.Player.RemoveItem(ev.Item); + Effect.Effect(ev); + + } + + + + } + +} diff --git a/KruacentExiled/KE.Items/Items/DivinePills.cs b/KruacentExiled/KE.Items/Items/DivinePills.cs new file mode 100644 index 00000000..1c8a8066 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/DivinePills.cs @@ -0,0 +1,102 @@ +using System.Collections.Generic; +using Exiled.API.Enums; +using Exiled.API.Features.Attributes; +using Exiled.API.Features.Spawn; +using Exiled.CustomItems.API.Features; +using Exiled.Events.EventArgs.Player; +using PlayerHandle = Exiled.Events.Handlers.Player; +using Scp914; +using KE.Items.API.Core.Upgrade; +using KE.Items.API.Interface; +using KE.Items.Items.ItemEffects; +using KE.Items.API.Features; + +public class DivinePills : KECustomItem, ILumosItem, ISwitchableEffect, IUpgradableCustomItem +{ + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Divine Pills", + [TranslationKeyDesc] = "25% chance you die\n 75% you respawn someone\n", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Divine Pills", + [TranslationKeyDesc] = "25% de chance de mourrir\n 75% de ramener quelqu'un à la vie", + }, + }; + } + public override ItemType ItemType => ItemType.Painkillers; + + /// + public override string Name { get; set; } = "Divine Pills"; + + /// + public override float Weight { get; set; } = 0.65f; + public UnityEngine.Color Color { get; set; } = UnityEngine.Color.yellow; + public IReadOnlyDictionary Upgrade { get; private set; } = new Dictionary() + { + { Scp914KnobSetting.VeryFine,new UpgradeProperties(10, "TrueDivinePills")} + }; + + /// + public override SpawnProperties SpawnProperties { get; set; } = new SpawnProperties() + { + LockerSpawnPoints = new List + { + new LockerSpawnPoint() + { + Chance = 75, + UseChamber = true, + Type = LockerType.Misc, + Zone = ZoneType.Entrance, + }, + new LockerSpawnPoint() + { + Chance = 25, + UseChamber = true, + Type = LockerType.Medkit, + Zone = ZoneType.LightContainment, + }, + }, + RoomSpawnPoints = new List + { + new RoomSpawnPoint() + { + Chance = 80, + Room = RoomType.LczGlassBox, + }, + }, + + }; + + public CustomItemEffect Effect { get;set; } + public DivinePills() + { + Effect = new DivinePillsEffect(); + } + + /// + protected override void SubscribeEvents() + { + PlayerHandle.UsedItem += OnUsedItem; + base.SubscribeEvents(); + } + + /// + protected override void UnsubscribeEvents() + { + PlayerHandle.UsedItem -= OnUsedItem; + base.UnsubscribeEvents(); + } + + private void OnUsedItem(UsedItemEventArgs ev) + { + if (!Check(ev.Item)) return; + Effect.Effect(ev); + } + +} \ No newline at end of file diff --git a/KruacentExiled/KE.Items/Items/Drone.cs b/KruacentExiled/KE.Items/Items/Drone.cs new file mode 100644 index 00000000..19c09610 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/Drone.cs @@ -0,0 +1,414 @@ +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Items; +using Exiled.API.Features.Roles; +using Exiled.API.Features.Spawn; +using Exiled.API.Features.Toys; +using Exiled.Events.EventArgs.Player; +using KE.CustomRoles.API.Features; +using KE.Items.API.Features; +using MEC; +using PlayerRoles.FirstPersonControl; +using System.Collections.Generic; +using UnityEngine; + +namespace KE.Items.Items +{ + public class Drone : KECustomItem + { + public override string Name { get; set; } = "Drone"; + public override float Weight { get; set; } = 3f; + public Color Color { get; set; } = Color.blue; + public override ItemType ItemType => ItemType.KeycardJanitor;// make a custom keycard + + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Drone", + [TranslationKeyDesc] = "Military drone (drop to use)", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Drone", + [TranslationKeyDesc] = "Drone de reconnaissance militaire (lâcher pour l'utiliser)", + }, + }; + } + public override SpawnProperties SpawnProperties { get; set; } = new SpawnProperties() + { + Limit = 1, + RoomSpawnPoints = new List + { + new RoomSpawnPoint() + { + Chance = 25, Room = RoomType.HczCornerDeep, + }, + new RoomSpawnPoint() + { + Chance = 25, Room = RoomType.HczIncineratorWayside, + }, + new RoomSpawnPoint() + { + Chance = 25, Room = RoomType.LczAirlock, + }, + }, + }; + + private Dictionary DroneBatteries = new Dictionary(); + private Dictionary ActiveDrones = new Dictionary(); + private const float MaxBattery = 100f; + // Battery drain rate per second + public const float BatteryDrainRate = 2.5f; + + protected override void SubscribeEvents() + { + Exiled.Events.Handlers.Player.Died += OnDied; + Exiled.Events.Handlers.Player.PickingUpItem += OnPicking; + Exiled.Events.Handlers.Player.Shooting += OnShooting; + Exiled.Events.Handlers.Player.InteractingDoor += OnInteracting; + Exiled.Events.Handlers.Player.UsingItem += OnUsingItem; + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Player.Died -= OnDied; + Exiled.Events.Handlers.Player.PickingUpItem -= OnPicking; + Exiled.Events.Handlers.Player.Shooting -= OnShooting; + Exiled.Events.Handlers.Player.InteractingDoor -= OnInteracting; + Exiled.Events.Handlers.Player.UsingItem -= OnUsingItem; + base.UnsubscribeEvents(); + } + + private void OnDied(DiedEventArgs ev) + { + if(ev.Player.GameObject.TryGetComponent(out DroneController dc)) + { + UnityEngine.Object.Destroy(dc); + ActiveDrones.Remove(ev.Player); + } + } + + protected override void OnDroppingItem(DroppingItemEventArgs ev) + { + if (ev.IsThrown) return; + ev.IsAllowed = false; + + if (KEAbilities.PlayersAbility.TryGetValue(ev.Player, out List abilities)) + { + foreach (KEAbilities ability in abilities) + { + if (ability.IsAbilityActive(ev.Player)) + { + KECustomItem.ItemEffectHint(ev.Player, "Tu ne peux pas utiliser cette item avec une abilité active"); + return; + } + } + } + + if (ActiveDrones.ContainsKey(ev.Player)) + { + StopDrone(ev.Player); + } + else + { + StartDrone(ev.Player, ev.Item.Serial); + } + } + + private void OnInteracting(InteractingDoorEventArgs ev) + { + if (!ev.IsAllowed) return; + ev.IsAllowed = DroneDontHaveArms(ev.Player); + } + + private void OnPicking(PickingUpItemEventArgs ev) + { + if (!ev.IsAllowed) return; + ev.IsAllowed = DroneDontHaveArms(ev.Player); + } + + private void OnShooting(ShootingEventArgs ev) + { + if (!ev.IsAllowed) return; + ev.IsAllowed = DroneDontHaveArms(ev.Player); + } + private void OnUsingItem(UsingItemEventArgs ev) + { + if (!ev.IsAllowed) return; + ev.IsAllowed = DroneDontHaveArms(ev.Player); + } + + private bool DroneDontHaveArms(Player player) + { + if (ActiveDrones.ContainsKey(player)) + { + KECustomItem.ItemEffectHint(player, "Le drone n'a pas de bras pour faire ça"); + return false; + } + return true; + } + + private void StartDrone(Player p, ushort serial) + { + if (!DroneBatteries.ContainsKey(serial)) DroneBatteries[serial] = MaxBattery; + + if(DroneBatteries[serial] <= 0) + { + KECustomItem.ItemEffectHint(p, "Plus de batterie"); + return; + } + + DroneController dController = p.GameObject.AddComponent(); + dController.DroneItem = this; + dController.Serial = serial; + dController.Battery = DroneBatteries[serial]; + + ActiveDrones.Add(p, dController); + + KEAbilities.TemporaryRemoveAbilities(p); + } + + public void StopDrone(Player p) + { + if (ActiveDrones.TryGetValue(p, out DroneController dc)) + { + DroneBatteries[dc.Serial] = dc.Battery; + UnityEngine.Object.Destroy(dc); + ActiveDrones.Remove(p); + KEAbilities.ReaffectRemovedAbilities(p); + p.RemoveItem(dc.Serial, true); + } + } + } + + internal class DroneController : MonoBehaviour + { + public Drone DroneItem; + public ushort Serial; + public float Battery; + + private Player player; + private Primitive drone; + private Npc npc; + private bool npcIsDead = false; + + private Dictionary playerSavedAmmo = new Dictionary(); + private List playerSavedItems = new List(); + private Vector3 originalSize; + + private CoroutineHandle update; + private bool isDestroying = false; + + private void Awake() + { + this.player = Player.Get(base.gameObject); + this.originalSize = player.Scale; + + SpawnNpc(); + PutPlayerOnDrone(); + + this.drone = Primitive.Create(PrimitiveType.Cube, player.Position, player.Rotation.eulerAngles, Vector3.one, false); + this.drone.Collidable = false; + this.drone.Spawn(); + this.drone.Transform.parent = player.Transform; + this.drone.Transform.localPosition = Vector3.zero; + this.drone.Transform.localRotation = Quaternion.identity; + } + + private void Start() + { + Exiled.Events.Handlers.Player.Hurting += OnHurting; + this.update = Timing.RunCoroutine(DroneUpdate()); + } + + private void OnDestroy() + { + if (isDestroying) return; + isDestroying = true; + + Exiled.Events.Handlers.Player.Hurting -= OnHurting; + Timing.KillCoroutines(update); + + if (this.player != null && this.player.IsAlive) + { + this.player.Scale = this.originalSize; + + if (this.player.Role is FpcRole fpc) + { + fpc.Gravity = FpcGravityController.DefaultGravity; + } + + if (this.npc != null) + { + this.player.Position = this.npc.Position; + } + RestorePlayerInventory(); + } + + if (this.player != null && this.player.Role.Type.IsDead()) + { + if (this.npc != null) + { + this.npc.ClearInventory(false); + this.npc.Destroy(); + } + } + else + { + if (this.npc != null) + { + this.npc.Destroy(); + } + } + + if (this.drone != null) + { + this.drone.Destroy(); + } + } + + private IEnumerator DroneUpdate() + { + while (true) + { + this.Battery -= Drone.BatteryDrainRate * Time.deltaTime; + if (this.Battery <= 0) + { + KECustomItem.ItemEffectHint(player, "Batterie épuisée"); + DroneItem.StopDrone(player); + yield break; + } + + KECustomItem.ItemEffectHint(player, $"Batterie: {Battery:F1}%"); + + if (player.Role is FpcRole fpc) + { + float pitch = player.CameraTransform.forward.y; + bool isMoving = fpc.MovementDetected; + bool isCrouching = fpc.IsCrouching; + + float targetVelY = 0f; + + if (isCrouching) + { + targetVelY = -3f; + } + else if (isMoving && Mathf.Abs(pitch) > 0.1f) + { + targetVelY = pitch * 3f; + } + + float currentVelY = fpc.Velocity.y; + float diff = targetVelY - currentVelY; + + Vector3 newGravity = new Vector3(0, diff * 15f, 0); + + if (Vector3.Distance(fpc.Gravity, newGravity) > 0.2f) + { + fpc.Gravity = newGravity; + } + + if (targetVelY > 0 && Physics.Raycast(player.Position, Vector3.down, 0.3f, 1 << 0)) + { + player.Position += Vector3.up * 0.2f; + } + } + + yield return Timing.WaitForOneFrame; + } + } + + private void OnHurting(HurtingEventArgs ev) + { + if (isDestroying) return; + + if (this.npc != null && ev.Player == this.npc) + { + if(this.npc.Health - ev.Amount <= 0 || ev.IsInstantKill) + { + this.npcIsDead = true; + } + + this.player.Hurt(ev.Amount, ev.DamageHandler.Type); + } + + if (ev.Player == this.player) + { + if ((ev.Player.Health - ev.Amount) <= 0 || ev.IsInstantKill) + { + if (this.npcIsDead) + { + this.DroneItem.StopDrone(ev.Player); + } + else + { + ev.IsAllowed = false; + this.player.Position = this.npc.Position; + this.player.Health = this.npc.Health; + RestorePlayerInventory(); + this.DroneItem.StopDrone(ev.Player); + } + } + } + } + + private void SpawnNpc() + { + this.npc = Npc.Spawn(this.player.Nickname, this.player.Role, this.player.Position); + this.npc.Health = this.player.Health; + this.npc.Scale = this.player.Scale; + + foreach(Item item in this.player.Items) + { + this.npc.AddItem(item.Clone()); + } + + foreach (var ammo in this.player.Ammo) + { + this.npc.SetAmmo((AmmoType)ammo.Key, ammo.Value); + } + } + + private void PutPlayerOnDrone() + { + playerSavedItems.Clear(); + foreach (Item item in this.player.Items) + { + playerSavedItems.Add(item.Type); + } + + playerSavedAmmo.Clear(); + foreach (var ammo in this.player.Ammo) + { + playerSavedAmmo.Add(ammo.Key, ammo.Value); + } + + this.player.ClearInventory(true); + this.player.Scale = Vector3.one * 0.1f; + + if (this.player.Role is FpcRole fpc) + { + fpc.Gravity = Vector3.zero; + } + } + + private void RestorePlayerInventory() + { + this.player.ClearInventory(true); + foreach (ItemType type in playerSavedItems) + { + this.player.AddItem(type); + } + + foreach (var ammo in playerSavedAmmo) + { + this.player.SetAmmo((AmmoType)ammo.Key, ammo.Value); + } + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Items/Items/FriendMaker.cs b/KruacentExiled/KE.Items/Items/FriendMaker.cs new file mode 100644 index 00000000..1475b384 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/FriendMaker.cs @@ -0,0 +1,167 @@ +using Exiled.API.Features; +using Exiled.API.Features.Spawn; +using Exiled.Events.EventArgs.Player; +using KE.Items.API.Features; +using KE.Items.API.Interface; +using KE.Utils.API.Features; +using PlayerRoles; +using System; +using System.Collections.Generic; + +namespace KE.Items.Items +{ + + public class FriendMaker : KECustomWeapon, IViolentItem + { + + public const string TranslationCooldown = "FriendMakerCooldown"; + public const string TranslationNobody = "FriendMakerNobody"; + public const string TranslationSameTeam = "FriendMakerSameTeam"; + public const string TranslationNonZombie = "FriendMakerNonZombie"; + public const string TranslationSuccess = "FriendMakerSuccess"; + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Friend Maker™", + [TranslationKeyDesc] = "The number one (1) method to make friends", + [TranslationCooldown] = "You must wait %second% seconds before using it again", + [TranslationNobody] = "But nobody came", + [TranslationSameTeam] = "I know you don't like them but they're in your team", + [TranslationNonZombie] = "That ain't a zombie", + [TranslationSuccess] = "New friend acquired!", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Friend Maker™", + [TranslationKeyDesc] = "LA méthode pour se faire des amis ! Produit non remboursable", + [TranslationCooldown] = "Tu dois attendre %second% secondes avant de pouvoir l'utiliser à nouveau", + [TranslationNobody] = "Mais personne n'est venu", + [TranslationSameTeam] = "Je sais que tu l'aime pas mais il est bien avec toi", + [TranslationNonZombie] = "C'est pas un zombie ça", + [TranslationSuccess] = "Nouvel ami obtenu!", + }, + }; + } + + public override ItemType ItemType => ItemType.GunCOM15; + public override string Name { get; set; } = "FriendMaker"; + public override float Weight { get; set; } = 1f; + public override SpawnProperties SpawnProperties { get; set; } = null; + + + public override byte ClipSize { get; } = 1; + + public bool IsViolent => false; + + private Dictionary cooldowns; + + private TimeSpan Cooldown = new TimeSpan(0,1,0); + + protected override void SubscribeEvents() + { + cooldowns = new Dictionary(); + base.SubscribeEvents(); + } + protected override void UnsubscribeEvents() + { + cooldowns = null; + base.UnsubscribeEvents(); + } + + + protected override void OnShooting(ShootingEventArgs ev) + { + + KELog.Debug("firne dmaker"); + Player player = ev.Player; + if (!Check(player)) return; + if (!ev.IsAllowed) return; + if (!CheckCooldown(player)) + { + DateTime dateTime = cooldowns[player] + Cooldown; + + + string msg = GetTranslation(player, TranslationCooldown).Replace("%second%", Math.Round((dateTime - DateTime.Now).TotalSeconds).ToString()); + KECustomItem.ItemEffectHint(player, msg); + ev.IsAllowed = false; + } + + + } + + + protected override void OnHurting(HurtingEventArgs ev) + { + Player attacker = ev.Attacker; + Player player = ev.Player; + if (!Check(attacker)) return; + ev.IsAllowed = false; + + + + + if(Convert(ev.Player, ev.Attacker)) + { + cooldowns[ev.Attacker] = DateTime.Now; + } + + + } + + + private bool CheckCooldown(Player player) + { + if (cooldowns.TryGetValue(player, out DateTime time)) + { + if (DateTime.Now >= time + Cooldown) + { + return true; + } + return false; + } + cooldowns[player] = DateTime.Now; + return true; + + } + + + private bool Convert(Player player,Player attacker) + { + if (player == null) + { + TranslationHint(attacker, TranslationNobody); + return false; + } + + if (attacker.Role.Side == player.Role.Side) + { + TranslationHint(attacker, TranslationSameTeam); + return false; + } + + + if (player.IsScp && player.Role != RoleTypeId.Scp0492) + { + TranslationHint(attacker, TranslationNonZombie); + return false; + } + + if (player.IsScp) + { + player.Role.Set(attacker.Role, RoleSpawnFlags.AssignInventory); + } + else + { + player.Role.Set(attacker.Role, RoleSpawnFlags.None); + } + TranslationHint(attacker, TranslationSuccess); + return true; + } + + + + } +} diff --git a/KruacentExiled/KE.Items/Items/HealZone.cs b/KruacentExiled/KE.Items/Items/HealZone.cs new file mode 100644 index 00000000..ef324def --- /dev/null +++ b/KruacentExiled/KE.Items/Items/HealZone.cs @@ -0,0 +1,102 @@ +using System.Collections.Generic; +using Exiled.API.Enums; +using Exiled.API.Features.Attributes; +using Exiled.API.Features.Spawn; +using Exiled.CustomItems.API.Features; +using Exiled.Events.EventArgs.Map; +using KE.Items.API.Interface; +using KE.Items.Items.ItemEffects; +using KE.Items.API.Features; +using Scp914; +using KE.Items.API.Core.Upgrade; + +namespace KE.Items.Items +{ + public class HealZone : KECustomGrenade, ILumosItem, ISwitchableEffect, IUpgradableCustomItem, IViolentItem + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Heal Zone", + [TranslationKeyDesc] = "Allow to heal you and your ally", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Heal Zone", + [TranslationKeyDesc] = "Créer une zone pour soigner", + }, + }; + } + public override ItemType ItemType => ItemType.GrenadeFlash; + public override string Name { get; set; } = "HealZone"; + public override float Weight => 0.65f; + public override float FuseTime => 5f; + public override bool ExplodeOnCollision => true; + public UnityEngine.Color Color { get; set; } = UnityEngine.Color.green; + public CustomItemEffect Effect { get; set; } + public override SpawnProperties SpawnProperties { get; set; } = new SpawnProperties() + { + Limit = 3, + LockerSpawnPoints = new List + { + new LockerSpawnPoint() + { + Chance = 75, + UseChamber = true, + Type = LockerType.Medkit, + Zone = ZoneType.Entrance, + }, + new LockerSpawnPoint() + { + Chance = 50, + UseChamber = true, + Type = LockerType.Medkit, + Zone = ZoneType.LightContainment, + }, + new LockerSpawnPoint() + { + Chance = 100, + UseChamber = true, + Type = LockerType.Medkit, + Zone = ZoneType.HeavyContainment, + }, + }, + + RoomSpawnPoints = new List + { + new RoomSpawnPoint() + { + Chance = 75, + Room = RoomType.HczHid, + }, + new RoomSpawnPoint() + { + Chance = 50, + Room = RoomType.HczNuke, + }, + }, + }; + + public IReadOnlyDictionary Upgrade => new Dictionary() + { + [Scp914KnobSetting.OneToOne] = new UpgradeProperties(100, "CocktailMolotov") + }; + + public bool IsViolent => false; + + public HealZone() + { + Effect = new HealZoneEffect(); + } + + protected override void OnExplodingGrenade(ExplodingGrenadeEventArgs ev) + { + + Effect.Effect(ev); + ev.TargetsToAffect.Clear(); + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Items/Items/ImpactFlash.cs b/KruacentExiled/KE.Items/Items/ImpactFlash.cs new file mode 100644 index 00000000..25a662e7 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/ImpactFlash.cs @@ -0,0 +1,64 @@ +using System.Collections.Generic; +using Exiled.API.Enums; +using Exiled.API.Features.Attributes; +using Exiled.API.Features.Spawn; +using Exiled.CustomItems.API.Features; +using KE.Items.API.Features; +using KE.Items.API.Interface; + +namespace KE.Items.Items +{ + public class ImpactFlash : KECustomGrenade, IViolentItem + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Impact Flash", + [TranslationKeyDesc] = "The name is self-explanatory", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Impact Flash", + [TranslationKeyDesc] = "Une flashbang qui explose à l'impacte", + }, + }; + } + public override ItemType ItemType => ItemType.GrenadeFlash; + public override string Name { get; set; } = "ImpactFlash"; + public override float Weight { get; set; } = 0.65f; + public override float FuseTime => 3f; + public override bool ExplodeOnCollision => true; + public bool IsViolent => false; + public override SpawnProperties SpawnProperties { get; set; } = new SpawnProperties() + { + Limit = 5, + DynamicSpawnPoints = new List + { + new DynamicSpawnPoint() + { + Chance = 50, + Location = SpawnLocationType.InsideHczArmory, + }, + new DynamicSpawnPoint() + { + Chance = 2, + Location = SpawnLocationType.Inside914, + }, + new DynamicSpawnPoint() + { + Chance= 50, + Location = SpawnLocationType.Inside049Armory, + }, + new DynamicSpawnPoint() + { + Chance= 50, + Location = SpawnLocationType.InsideLczArmory, + } + }, + + }; + } +} diff --git a/KruacentExiled/KE.Items/Items/ItemEffects/DeployableWallEffect.cs b/KruacentExiled/KE.Items/Items/ItemEffects/DeployableWallEffect.cs new file mode 100644 index 00000000..b45825e5 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/ItemEffects/DeployableWallEffect.cs @@ -0,0 +1,66 @@ +using Exiled.API.Features.Toys; +using Exiled.Events.EventArgs.Map; +using Exiled.Events.EventArgs.Player; +using KE.Items.API.Interface; +using KE.Utils.API.Sounds; +using MEC; +using UnityEngine; + + +namespace KE.Items.Items.ItemEffects +{ + public class DeployableWallEffect : CustomItemEffect + { + public override void Effect(UsedItemEventArgs ev) + { + SpawnWall(ev.Player.Position, ev.Player.Rotation); + } + public override void Effect(DroppingItemEventArgs ev) + { + SpawnWall(ev.Player.Position, ev.Player.Rotation); + } + + public override void Effect(ExplodingGrenadeEventArgs ev) + { + SpawnWall(ev.Position, ev.Projectile.Rotation); + } + + private void SpawnWall(Vector3 pos, Quaternion rotation) + { + float distance = 2; + Vector3 forward = rotation * Vector3.forward; + Vector3 spawnPos = pos + forward * distance; + Vector3 rotat = new Vector3(0, rotation.eulerAngles.y, 0); + + + + + Primitive wall = Primitive.Create(PrimitiveType.Cube, spawnPos, rotat, new Vector3(4, 4, 0.2f), true); + KE.Utils.API.Sounds.SoundPlayer.Instance.Play("lego", wall.GameObject, 10f, 40); + wall.Collidable = true; + wall.Visible = true; + Timing.CallDelayed(10, () => + { + wall?.Destroy(); + wall = null; + }); + Timing.CallDelayed(5, () => + { + if(wall != null) + { + wall.Color = Color.yellow; + } + + }); + Timing.CallDelayed(8, () => + { + if (wall != null) + { + wall.Color = Color.red; + } + }); + + + } + } +} diff --git a/KruacentExiled/KE.Items/Items/ItemEffects/DivinePillsEffect.cs b/KruacentExiled/KE.Items/Items/ItemEffects/DivinePillsEffect.cs new file mode 100644 index 00000000..6a75e5cb --- /dev/null +++ b/KruacentExiled/KE.Items/Items/ItemEffects/DivinePillsEffect.cs @@ -0,0 +1,72 @@ +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.CustomItems.API.Features; +using Exiled.Events.EventArgs.Interfaces; +using Exiled.Events.EventArgs.Map; +using Exiled.Events.EventArgs.Player; +using KE.Items.API.Extensions; +using KE.Items.API.Interface; +using MEC; +using PlayerRoles; +using System.Linq; +using Random = UnityEngine.Random; + +namespace KE.Items.Items.ItemEffects +{ + public class DivinePillsEffect : CustomItemEffect + { + public override void Effect(UsedItemEventArgs ev) + { + EffectItem(ev.Player); + } + public override void Effect(DroppingItemEventArgs ev) + { + EffectItem(ev.Player, ev); + } + + public override void Effect(ExplodingGrenadeEventArgs ev) + { + foreach (Player p in ev.TargetsToAffect) + { + EffectItem(p); + } + } + + + + private void EffectItem(Player player, IDeniableEvent ev = null) + { + if (Player.List.Count(x => x.Role == RoleTypeId.Spectator) == 0) + { + player.ItemEffectHint("No spectators to respawn"); + return; + } + var random = Random.Range(0, 100); + + + if (random < 25) + { + player.Kill("unlucky bro"); + return; + } + Player respawning = Player.List.GetRandomValue(x => x.Role == RoleTypeId.Spectator); + switch (player.Role.Side) + { + case Side.ChaosInsurgency: + respawning.Role.Set(RoleTypeId.ChaosRifleman); + break; + case Side.Mtf: + respawning.Role.Set(RoleTypeId.NtfPrivate); + break; + } + + if (random >= 75) + { + Log.Debug("tp"); + Timing.CallDelayed(1, () =>respawning.Teleport(player)); + + } + } + } +} diff --git a/KruacentExiled/KE.Items/Items/ItemEffects/HealZoneEffect.cs b/KruacentExiled/KE.Items/Items/ItemEffects/HealZoneEffect.cs new file mode 100644 index 00000000..72f31128 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/ItemEffects/HealZoneEffect.cs @@ -0,0 +1,98 @@ +using Exiled.API.Features; +using Exiled.API.Features.Toys; +using Exiled.Events.EventArgs.Map; +using Exiled.Events.EventArgs.Player; +using KE.Items.API.Interface; +using MEC; +using PlayerRoles; +using System.Collections.Generic; +using UnityEngine; + +namespace KE.Items.Items.ItemEffects +{ + public class HealZoneEffect : CustomItemEffect + { + + public override void Effect(UsedItemEventArgs ev) + { + SetZone(ev.Player, ev.Player.Position); + } + public override void Effect(DroppingItemEventArgs ev) + { + SetZone(ev.Player, ev.Player.Position); + } + + public override void Effect(ExplodingGrenadeEventArgs ev) + { + SetZone(ev.Player, ev.Position); + } + + private void SetZone(Player player, Vector3 position) + { + float cylinderSize = 5; + + Player playerThrowingGrenade = player; + Vector3 healZonePosition = position; + Primitive wall = Primitive.Create(PrimitiveType.Cylinder, healZonePosition, null, new Vector3(cylinderSize, 0.01f, cylinderSize), true); + wall.Collidable = false; + wall.Visible = true; + + wall.Color = Color.green; + + var coroutineHandler = Timing.RunCoroutine(HealZoneHeal(wall.Position, cylinderSize, playerThrowingGrenade)); + + Timing.CallDelayed(20, () => + { + wall.UnSpawn(); + Timing.KillCoroutines(coroutineHandler); + wall.Destroy(); + }); + } + + private IEnumerator HealZoneHeal(Vector3 wallPosition, float cylinderSize, Player playerThrowingGrenade) + { + Dictionary playerHealedAmounts = new Dictionary(); + + + Team team = playerThrowingGrenade.Role.Team; + + + while (true) + { + foreach (Player player in Player.List) + { + + // Check if a player is in the zone. + if (IsPlayerInZone(player, wallPosition, cylinderSize)) + { + if (team == player.Role.Team) + { + if (!playerHealedAmounts.ContainsKey(player)) + { + playerHealedAmounts[player] = 0; + } + + + + if (playerHealedAmounts[player] <= 100) + { + player.Heal(1); + playerHealedAmounts[player] += 1; + } + } + } + } + + // Waiting 0.5s before re-check. + yield return Timing.WaitForSeconds(0.1f); + } + } + + private bool IsPlayerInZone(Player player, Vector3 zonePosition, float radius) + { + float distance = Vector3.Distance(new Vector3(player.Position.x, 0, player.Position.z), + new Vector3(zonePosition.x, 0, zonePosition.z)); + return distance <= radius / 2; + } + } +} diff --git a/KruacentExiled/KE.Items/Items/ItemEffects/LowGravityGrenadeEffect.cs b/KruacentExiled/KE.Items/Items/ItemEffects/LowGravityGrenadeEffect.cs new file mode 100644 index 00000000..05320e2b --- /dev/null +++ b/KruacentExiled/KE.Items/Items/ItemEffects/LowGravityGrenadeEffect.cs @@ -0,0 +1,57 @@ +using Exiled.API.Features; +using Exiled.API.Features.Roles; +using Exiled.Events.EventArgs.Map; +using Exiled.Events.EventArgs.Player; +using KE.Items.API.Interface; +using MEC; +using PlayerRoles.FirstPersonControl; +using System.Collections.Generic; +using UnityEngine; + +namespace KE.Items.Items.ItemEffects +{ + public class LowGravityGrenadeEffect : CustomItemEffect + { + public float Duration { get; set; } = 15f; + public float Range { get; set; } = 10f; + + public override void Effect(UsedItemEventArgs ev) + { + OnExploding(ev.Player); + } + public override void Effect(DroppingItemEventArgs ev) + { + OnExploding(ev.Player); + } + + public override void Effect(ExplodingGrenadeEventArgs ev) + { + ev.IsAllowed = false; + + foreach(Player player in ev.TargetsToAffect) + { + OnExploding(player); + } + + + } + + public void OnExploding(Player player) + { + if (player is null) return; + + if (player.Role is FpcRole fpcRole) + { + fpcRole.Gravity = FpcGravityController.DefaultGravity * 0.15f; + } + + Timing.CallDelayed(Duration, () => + { + if (player.Role is FpcRole fpcRole) + { + fpcRole.Gravity = FpcGravityController.DefaultGravity; + } + }); + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Items/Items/ItemEffects/MineEffect.cs b/KruacentExiled/KE.Items/Items/ItemEffects/MineEffect.cs new file mode 100644 index 00000000..dbe41690 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/ItemEffects/MineEffect.cs @@ -0,0 +1,185 @@ +/*using Exiled.API.Features; +using Exiled.API.Features.Items; +using Exiled.API.Features.Pickups; +using Exiled.API.Features.Pickups.Projectiles; +using Exiled.API.Interfaces; +using Exiled.Events.EventArgs.Map; +using Exiled.Events.EventArgs.Player; +using InventorySystem.Items.ThrowableProjectiles; +using KE.Items.API.Events; +using KE.Items.API.Extensions; +using KE.Items.API.Interface; +using KE.Items.Items.Models; +using KE.Utils.API.Interfaces; +using MEC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Items.Items.ItemEffects +{ + /*public class MineEffect : CustomItemEffect, IUsingEvents + { + private const float RefreshRate = .05f; + private const int MineActivationTime = 10; + private const float MineRadius = 0.7f; + public override void Effect(UsedItemEventArgs ev) + { + PlaceMine(ev.Player); + } + public override void Effect(DroppingItemEventArgs ev) + { + PlaceMine(ev.Player); + } + + public override void Effect(ExplodingGrenadeEventArgs ev) + { + PlaceMine(ev.Player, ev.Position); + } + + public void SubscribeEvents() + { + ExplodeEvent.ExplodeDestructible += OnExplodeDestructible; + grenades = new(); + } + + private void OnExplodeDestructible(OnExplodeDestructibleEventsArgs obj) + { + if (grenades.Contains(obj.ExplosionGrenade)) + { + obj.Damage = 100; + + } + } + + public void UnsubscribeEvents() + { + ExplodeEvent.ExplodeDestructible -= OnExplodeDestructible; + } + + /// + /// Place the mine at the feet of the player + /// + /// + private void PlaceMine(Player p) + { + SpawnMine(p, p.Position - new Vector3(0, p.Scale.y)); + } + /// + /// Place the mine at the specified position + /// + /// + /// + private void PlaceMine(Player p, Vector3 pos) + { + + SpawnMine(p, pos); + } + + private void SpawnMine(Player p, Vector3 pos) + { + + MineModel m = new MineModel(); + + //put the mine on the floor + m.Create(pos, new Quaternion()); + + Timing.RunCoroutine(WaitAndActivateMine(p, m)); + } + + + + private HashSet grenades; + + private void SpawnActive(Vector3 position) + { + ExplosiveGrenade grenade = (ExplosiveGrenade)Item.Create(ItemType.GrenadeHE); + grenade.MaxRadius = 1; + grenade.ScpDamageMultiplier = .25f; + grenade.FuseTime = 0f; + + grenades.Add(grenade.SpawnActive(position).Base); + } + private IEnumerator WaitAndActivateMine(Player player, MineModel mine) + { + int countdown = MineActivationTime; + while (countdown > 0) + { + player.ItemEffectHint($"The mine will be active in {countdown} seconds !"); + yield return Timing.WaitForSeconds(1f); + countdown--; + } + + // Message final lorsque la mine s'active + player.ItemEffectHint("Mine activated !"); + Timing.RunCoroutine(ActiveMine(mine, MineRadius)); + } + + private IEnumerator ActiveMine(MineModel mine, float cylinderSize) + { + Timing.RunCoroutine(mine.Activate()); + bool isActivated = true; + while (isActivated) + { + //Log.Debug("activated"); + foreach (Pickup p in Pickup.List) + { + if (isActivated && IsPositionInZone(p.Position, mine.Position, cylinderSize, 3)) + { + SpawnActive(mine.Position+Vector3.up); + DestroyMine(mine); + isActivated = false; + break; + } + } + + foreach (Player player in Player.List) + { + + if (isActivated && IsPlayerInZone(player, mine.Position, cylinderSize, 3)) + { + SpawnActive(mine.Position + Vector3.up); + DestroyMine(mine); + isActivated = false; + break; + } + } + + yield return Timing.WaitForSeconds(RefreshRate); + } + DestroyMine(mine); + } + + private void DestroyMine(MineModel mine) + { + mine.Destroy(); + UnsubscribeEvents(); + } + + + private bool IsPlayerInZone(Player player, Vector3 zonePosition, float radius, float height) + { + return IsPositionInZone(player.Position, zonePosition, radius, height); + + } + + private bool IsPositionInZone(Vector3 position, Vector3 zonePosition, float radius, float height) + { + // Calculate the horizontal distance (x, z) + float horizontalDistance = Vector3.Distance( + new Vector3(position.x, 0, position.z), + new Vector3(zonePosition.x, 0, zonePosition.z) + ); + + // Calculate the vertical difference (y) + float verticalDifference = Mathf.Abs(position.y - zonePosition.y); + + // Check if the player is in the 3d zone. + return horizontalDistance <= radius / 2 && verticalDifference <= height / 2; + } + + } +}*/ diff --git a/KruacentExiled/KE.Items/Items/ItemEffects/MolotovEffect.cs b/KruacentExiled/KE.Items/Items/ItemEffects/MolotovEffect.cs new file mode 100644 index 00000000..c0c12408 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/ItemEffects/MolotovEffect.cs @@ -0,0 +1,160 @@ +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Items; +using Exiled.API.Features.Pickups; +using Exiled.API.Features.Toys; +using Exiled.Events.EventArgs.Map; +using Exiled.Events.EventArgs.Player; +using KE.Items.API.Features; +using KE.Items.API.Interface; +using MEC; +using PlayerRoles; +using System.Collections.Generic; +using UnityEngine; +using Light = Exiled.API.Features.Toys.Light; + +namespace KE.Items.Items.ItemEffects +{ + public class MolotovEffect : CustomItemEffect + { + public float Duration { get; set; } = 20f; + public float Radius { get; set; } = 5f; + public float TickRate { get; set; } = 0.5f; + + private static HashSet ActiveMolotovSerialNumbers = new HashSet(); + private static List ActiveFireZones = new List(); + + public override void Effect(UsedItemEventArgs ev) => SpawnMolotov(ev.Player, ev.Player.Position); + public override void Effect(DroppingItemEventArgs ev) => SpawnMolotov(ev.Player, ev.Player.Position); + public override void Effect(ExplodingGrenadeEventArgs ev) => SpawnMolotov(ev.Player, ev.Position); + + public void OnReceivingEffect(ReceivingEffectEventArgs ev) + { + if (ev.Effect.GetEffectType() == EffectType.Hypothermia) + { + foreach (Vector3 zoneCenter in ActiveFireZones) + { + if (Vector3.Distance(ev.Player.Position, zoneCenter) <= Radius + 1f) + { + ev.IsAllowed = false; + return; + } + } + } + } + + public void OnPickingUp(PickingUpItemEventArgs ev) + { + if (ev.Pickup != null && ActiveMolotovSerialNumbers.Contains(ev.Pickup.Serial)) + { + ev.IsAllowed = false; + KECustomItem.ItemEffectHint(ev.Player, "C'est brûlant ! Touche pas à ça !"); + ev.Player.Hurt(10f, DamageType.Tesla); + } + } + + private void SpawnMolotov(Player owner, Vector3 centerPos) + { + Scp244 jarItem = (Scp244)Item.Create(ItemType.SCP244a); + jarItem.Scale = new Vector3(0.2f, 0.2f, 0.2f); + jarItem.Primed = true; + jarItem.MaxDiameter = 0.2f; + + Pickup jarPickup = jarItem.CreatePickup(centerPos); + if (jarPickup.GameObject.TryGetComponent(out Rigidbody rb)) rb.isKinematic = true; + + ActiveMolotovSerialNumbers.Add(jarPickup.Serial); + ActiveFireZones.Add(centerPos); + + Vector3 groundPos = centerPos - Vector3.up * 0.15f; + Primitive dangerZone = Primitive.Create(PrimitiveType.Cylinder, groundPos, Vector3.zero, new Vector3(Radius, 0.1f, Radius), true); + dangerZone.Collidable = false; + dangerZone.Color = new Color(1f, 0f, 0f, 0.5f); + + List fireLights = new List(); + Color fireColor = new Color(255, 128, 0); + + fireLights.Add(CreateLight(centerPos + Vector3.up * 0.5f, fireColor, Radius /2f, 0.2f)); + Timing.RunCoroutine(Fire(jarPickup, dangerZone, fireLights, owner, centerPos)); + } + + private Light CreateLight(Vector3 pos, Color col, float range, float intensity) + { + Light l = Light.Create(pos,spawn:false); + l.LightType = LightType.Point; + l.Color = col; + l.Range = range; + l.Intensity = intensity; + l.Spawn(); + return l; + } + + private IEnumerator Fire(Pickup jar, Primitive zone, List lights, Player owner, Vector3 center) + { + float elapsed = 0f; + + while (elapsed < Duration) + { + foreach (var l in lights) + { + if (l != null) l.Intensity = Random.Range(0.1f, 0.3f); + } + + if (zone != null) + { + float pulse = 0.1f + Mathf.PingPong(Time.time, 0.05f); + zone.Scale = new Vector3(Radius, pulse, Radius); + } + + foreach (Player target in Player.List) + { + if (!target.IsAlive) continue; + + if (Vector3.Distance(new Vector3(center.x, 0, center.z), new Vector3(target.Position.x, 0, target.Position.z)) <= Radius / 2 + && Mathf.Abs(center.y - target.Position.y) < 2.5f) + { + if (owner != null && !Server.FriendlyFire && target.Role.Team == owner.Role.Team && target != owner) + continue; + + target.EnableEffect(EffectType.Burned, 1f); + + if (target.IsHuman) + { + if (target.ArtificialHealth > 0) target.ArtificialHealth -= 10f; + else target.Hurt(6f, DamageType.Firearm); + } + else if (target.IsScp) + { + float dmg = 20f; + + if (target.Role == RoleTypeId.Scp0492) + { + dmg *= 2.5f; + } + + target.Hurt(dmg, DamageType.Firearm); + } + } + } + + yield return Timing.WaitForSeconds(TickRate); + elapsed += TickRate; + } + + if (zone != null) zone.Destroy(); + + foreach (var l in lights) + { + if (l != null) l.Destroy(); + } + + if (jar != null) + { + ActiveMolotovSerialNumbers.Remove(jar.Serial); + ActiveFireZones.Remove(center); + jar.Destroy(); + } + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Items/Items/ItemEffects/ProximityGrenadeEffect.cs b/KruacentExiled/KE.Items/Items/ItemEffects/ProximityGrenadeEffect.cs new file mode 100644 index 00000000..3b600cb0 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/ItemEffects/ProximityGrenadeEffect.cs @@ -0,0 +1,84 @@ +using AdminToys; +using Exiled.API.Enums; +using Exiled.API.Features.Toys; +using Exiled.Events.EventArgs.Map; +using Exiled.Events.EventArgs.Player; +using MEC; +using UnityEngine; +using Exiled.API.Features; +using KE.Items.API.Interface; +using System.Collections.Generic; +using System.Linq; + +namespace KE.Items.Items.ItemEffects +{ + public class ProximityGrenadeEffect : CustomItemEffect + { + public float Duration { get; set; } = 20; + public float RoomRadius { get; set; } = 3; + + public override void Effect(UsedItemEventArgs ev) + { + OnExploding(ev.Player.CurrentRoom, ev.Player.Position); + } + public override void Effect(DroppingItemEventArgs ev) + { + OnExploding(ev.Player.CurrentRoom, ev.Player.Position); + } + + public override void Effect(ExplodingGrenadeEventArgs ev) + { + ev.IsAllowed = false; + OnExploding(ev.Projectile.Room, ev.Position); + } + + public void OnExploding(Room originRoom, Vector3 position) + { + HashSet roomsInRange = new HashSet(); + + roomsInRange.Add(originRoom); + + List currentLayer = new List(); + currentLayer.Add(originRoom); + + for (int i = 0; i < RoomRadius; i++) + { + List nextLayer = new List(); + + foreach (Room r in currentLayer) + { + foreach (Room neighbor in r.NearestRooms) + { + if (roomsInRange.Add(neighbor)) + { + nextLayer.Add(neighbor); + } + } + } + currentLayer = nextLayer; + } + + foreach (Player player in Player.Enumerable.Where(p => p.IsAlive)) + { + if (roomsInRange.Contains(player.CurrentRoom)) + { + var lineColor = player.Role.Color; + + var direction = player.Position - position; + var distance = direction.magnitude; + + var scale = new Vector3(0.1f, distance * 0.5f, 0.1f); + var laserPos = position + direction * 0.5f; + + var rotation = Quaternion.LookRotation(direction) * Quaternion.Euler(90, 0, 0); + + var laser = Primitive.Create(PrimitiveType.Cylinder, PrimitiveFlags.Visible, laserPos, rotation.eulerAngles, scale, true, lineColor); + + Timing.CallDelayed(Duration, laser.Destroy); + } + } + } + + + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Items/Items/ItemEffects/RedbullEnergyEffect.cs b/KruacentExiled/KE.Items/Items/ItemEffects/RedbullEnergyEffect.cs new file mode 100644 index 00000000..212a0fd8 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/ItemEffects/RedbullEnergyEffect.cs @@ -0,0 +1,122 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Doors; +using Exiled.API.Features.Items; +using Exiled.Events.EventArgs.Map; +using Exiled.Events.EventArgs.Player; +using KE.Items.API.Features; +using KE.Items.API.Interface; +using MEC; +using System.Collections.Generic; +using UnityEngine; + +namespace KE.Items.Items.ItemEffects +{ + public class RedBullEnergyEffect : CustomItemEffect + { + private const string ActiveKey = "KE.Items.Redbull.Active"; + private const string DoorExplodeKey = "KE.Items.Redbull.Immune"; + private const float ScanRadiusSqr = 6.40f; + private const float BoostDuration = 20f; + private const float CrashDuration = 30f; + + private readonly Dictionary _activeRoutines = new Dictionary(); + + public override void Effect(UsedItemEventArgs ev) => TriggerEffect(ev.Player); + + public override void Effect(DroppingItemEventArgs ev) => TriggerEffect(ev.Player); + + public override void Effect(ExplodingGrenadeEventArgs ev) => TriggerEffect(ev.Player); + + private void TriggerEffect(Player player) + { + if (player.SessionVariables.ContainsKey(ActiveKey)) + { + KECustomItem.ItemEffectHint(player, "Ton cœur ne supporterait pas une autre canette !"); + return; + } + + if (_activeRoutines.TryGetValue(player.Id, out CoroutineHandle oldHandle)) + Timing.KillCoroutines(oldHandle); + + _activeRoutines[player.Id] = Timing.RunCoroutine(RunRedBullSequence(player)); + } + + private IEnumerator RunRedBullSequence(Player p) + { + p.SessionVariables[ActiveKey] = true; + + p.EnableEffect(EffectType.Scp207, 5, BoostDuration); + p.EnableEffect(EffectType.DamageReduction, 40, BoostDuration); + + float elapsed = 0f; + while (elapsed < BoostDuration) + { + if (p == null || !p.IsAlive) yield break; + + p.Stamina = 1f; + if (p.Health < p.MaxHealth) p.Health += 0.5f; + + Vector3 pPos = p.Position; + foreach (Door door in Door.List) + { + if (!door.IsGate && door is BreakableDoor breakable && !breakable.IsDestroyed) + { + if ((breakable.Position - pPos).sqrMagnitude < ScanRadiusSqr) + { + p.SessionVariables[DoorExplodeKey] = true; + + ExplosiveGrenade grenade = (ExplosiveGrenade)Item.Create(ItemType.GrenadeHE); + grenade.FuseTime = 0.1f; + breakable.IsOpen = true; + grenade.SpawnActive(breakable.Position, p); + + Timing.CallDelayed(0.2f, () => { + if (p != null) p.SessionVariables.Remove(DoorExplodeKey); + }); + } + } + } + + elapsed += 0.2f; + yield return Timing.WaitForSeconds(0.2f); + } + + if (p == null || !p.IsAlive) yield break; + + KECustomItem.ItemEffectHint(p, "Ton coeur va exploser !"); + + p.DisableEffect(EffectType.Scp207); + p.Stamina = 0.25f; + + p.EnableEffect(EffectType.Deafened, CrashDuration); + p.EnableEffect(EffectType.Slowness, CrashDuration); + p.EnableEffect(EffectType.Concussed, CrashDuration); + p.EnableEffect(EffectType.Exhausted, CrashDuration); + + yield return Timing.WaitForSeconds(CrashDuration); + + Cleanup(p); + } + + public void OnHurting(HurtingEventArgs ev) + { + if (ev.DamageHandler.Type == DamageType.Explosion && ev.Player.SessionVariables.ContainsKey(DoorExplodeKey)) + ev.Amount = 0; + } + + public void Cleanup(Player p) + { + if (p == null) return; + + if (_activeRoutines.TryGetValue(p.Id, out CoroutineHandle handle)) + { + Timing.KillCoroutines(handle); + _activeRoutines.Remove(p.Id); + } + + if (p.SessionVariables.ContainsKey(ActiveKey)) p.SessionVariables.Remove(ActiveKey); + if (p.SessionVariables.ContainsKey(DoorExplodeKey)) p.SessionVariables.Remove(DoorExplodeKey); + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Items/Items/ItemEffects/Scp1650Effect.cs b/KruacentExiled/KE.Items/Items/ItemEffects/Scp1650Effect.cs new file mode 100644 index 00000000..590f83f6 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/ItemEffects/Scp1650Effect.cs @@ -0,0 +1,103 @@ +using Exiled.API.Features; +using Exiled.Events.EventArgs.Map; +using Exiled.Events.EventArgs.Player; +using KE.Items.API.Interface; +using MEC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using static KE.Items.Items.Scp1650; + +namespace KE.Items.Items.ItemEffects +{ + public class Scp1650Effect : CustomItemEffect + { + public enum CardinalPoints + { + South, + West, + North, + East, + } + + public override void Effect(UsedItemEventArgs ev) + { + OnUsedItem(ev.Player); + } + public override void Effect(DroppingItemEventArgs ev) + { + OnUsedItem(ev.Player); + } + + public override void Effect(ExplodingGrenadeEventArgs ev) + { + foreach (Player player in ev.TargetsToAffect) + { + OnUsedItem(player); + } + } + + + private void OnUsedItem(Player player) + { + + CardinalPoints rotation = Rotation(player.Rotation); + + Log.Debug(rotation); + switch (rotation) + { + + case CardinalPoints.South: + Timing.RunCoroutine(Regeneration(player, 10)); + break; + case CardinalPoints.West: + player.EnableEffect(Exiled.API.Enums.EffectType.BodyshotReduction, 25, 60); + break; + case CardinalPoints.North: + player.EnableEffect(Exiled.API.Enums.EffectType.Invigorated, 1, 30); + break; + case CardinalPoints.East: + player.EnableEffect(Exiled.API.Enums.EffectType.CardiacArrest, 1, 10); + break; + + } + + + } + + private IEnumerator Regeneration(Player p, float duration) + { + float timeWaited = duration; + + while (timeWaited > 0) + { + timeWaited -= .1f; + p.Heal(1); + yield return Timing.WaitForSeconds(.1f); + } + + + } + + private CardinalPoints Rotation(Quaternion rotation) + { + + Vector3 forward1 = rotation * Vector3.forward; + float x = forward1.x; + float z = forward1.z; + + if (z > .75f) + return CardinalPoints.South; + if (x > .75f) + return CardinalPoints.West; + if (z <= -.75f) + return CardinalPoints.North; + if (x <= -.75f) + return CardinalPoints.East; + return CardinalPoints.East; + } + } +} diff --git a/KruacentExiled/KE.Items/Items/ItemEffects/SmokeGrenadeEffect.cs b/KruacentExiled/KE.Items/Items/ItemEffects/SmokeGrenadeEffect.cs new file mode 100644 index 00000000..a1e93e56 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/ItemEffects/SmokeGrenadeEffect.cs @@ -0,0 +1,55 @@ +using Exiled.API.Features.Items; +using Exiled.API.Features.Pickups; +using Exiled.Events.EventArgs.Map; +using Exiled.Events.EventArgs.Player; +using KE.Items.API.Interface; +using MEC; +using System.ComponentModel; +using UnityEngine; + +namespace KE.Items.Items.ItemEffects +{ + public class SmokeGrenadeEffect : CustomItemEffect + { + public bool RemoveSmoke { get; set; } = true; + [Description("If RemoveSmoke is true, how long does it take before the smoke will be removed")] + public float FogTime { get; set; } = 30; + + public override void Effect(UsedItemEventArgs ev) + { + OnExploding(ev.Player.Position); + } + public override void Effect(DroppingItemEventArgs ev) + { + OnExploding(ev.Player.Position); + } + + public override void Effect(ExplodingGrenadeEventArgs ev) + { + OnExploding(ev.Position); + } + + public void OnExploding(Vector3 position) + { + Vector3 savedGrenadePosition = position; + Scp244 scp244 = (Scp244)Item.Create(ItemType.SCP244a); + Pickup pickup = null; + scp244.Scale = new Vector3(0.01f, 0.01f, 0.01f); + scp244.Primed = true; + scp244.MaxDiameter = 0.0f; + pickup = scp244.CreatePickup(savedGrenadePosition); + if (RemoveSmoke) + { + Timing.CallDelayed(FogTime, () => + { + pickup.Position += Vector3.down * 10; + + Timing.CallDelayed(10, () => + { + pickup.Destroy(); + }); + }); + } + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Items/Items/ItemEffects/TPGrenadaEffect.cs b/KruacentExiled/KE.Items/Items/ItemEffects/TPGrenadaEffect.cs new file mode 100644 index 00000000..7f574cb1 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/ItemEffects/TPGrenadaEffect.cs @@ -0,0 +1,112 @@ +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Pickups.Projectiles; +using Exiled.API.Features.Pools; +using Exiled.Events.EventArgs.Map; +using Exiled.Events.EventArgs.Player; +using KE.Items.API.Interface; +using KE.Utils.Extensions; +using PlayerRoles; +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace KE.Items.Items.ItemEffects +{ + public class TPGrenadaEffect : CustomItemEffect + { + private List effectedPlayers = new List(); + public HashSet BlacklistedRoles { get; set; } = new HashSet() { RoleTypeId.Scp173, RoleTypeId.Scp106, RoleTypeId.Scp049, RoleTypeId.Scp096, RoleTypeId.Scp3114, RoleTypeId.Scp0492, RoleTypeId.Scp939 }; + + public HashSet BlacklistedRooms { get; } = new HashSet() + { + RoomType.HczTestRoom, + RoomType.HczTesla, + RoomType.Lcz173, + }; + + public override void Effect(UsedItemEventArgs ev) + { + OnExploding(new HashSet() { ev.Player }); + } + public override void Effect(DroppingItemEventArgs ev) + { + OnExploding(new HashSet() { ev.Player }); + } + + public override void Effect(ExplodingGrenadeEventArgs ev) + { + OnExploding(ev.TargetsToAffect, ev.Projectile); + } + + + + private void OnExploding(HashSet targets, EffectGrenadeProjectile projectile = null) + { + + effectedPlayers = ListPool.Pool.Get(); + foreach (Player player in targets) + { + if (BlacklistedRoles.Contains(player.Role)) + continue; + try + { + bool line; + if (projectile == null) + { + line = true; + } + else + { + line = Physics.Linecast(projectile.Transform.position, player.Position); + } + + + if (line) + { + effectedPlayers.Add(player); + player.Teleport(RandomRoom().GetValidPosition()); + } + } + catch (Exception exception) + { + Log.Error($"{nameof(OnExploding)} error: {exception}"); + } + } + } + + + + private Room RandomRoom() + { + Room room = Room.List.GetRandomValue((r) => !BlacklistedRooms.Contains(r.Type) && r.IsSafe()); + if (Warhead.IsDetonated) + { + return ZoneType.Surface.RandomSafeRoom(); + } + + if (Exiled.API.Features.Map.IsLczDecontaminated) + { + float random = UnityEngine.Random.value; + Log.Debug($"random={random}"); + if (random <= 0.33f) + { + room = ZoneType.HeavyContainment.RandomSafeRoom(); + } + else if (random > 0.33f && random <= 0.66f) + { + room = ZoneType.Entrance.RandomSafeRoom(); + } + else + { + room = ZoneType.Surface.RandomSafeRoom(); + } + + } + + Log.Debug($"roomZone={room.Zone}"); + return room; + } + } +} diff --git a/KruacentExiled/KE.Items/Items/LeSoleil/LeSoleil.cs b/KruacentExiled/KE.Items/Items/LeSoleil/LeSoleil.cs new file mode 100644 index 00000000..16a24cce --- /dev/null +++ b/KruacentExiled/KE.Items/Items/LeSoleil/LeSoleil.cs @@ -0,0 +1,78 @@ +using AdminToys; +using Exiled.API.Enums; +using Exiled.API.Features.Attributes; +using Exiled.API.Features.Spawn; +using Exiled.API.Features.Toys; +using Exiled.Events.EventArgs.Map; +using Exiled.Events.EventArgs.Player; +using KE.Items.API.Core.Models; +using KE.Items.API.Core.Upgrade; +using KE.Items.API.Features; +using KE.Items.API.Interface; +using KE.Items.Items.ItemEffects; +using KE.Items.Items.PickupModels; +using Scp914; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Items.Items.LeSoleil +{ + public class LeSoleil : KECustomGrenade, IUpgradableCustomItem, IViolentItem + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "The Sun", + [TranslationKeyDesc] = "Probably not the best idea to use it", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Le Soleil", + [TranslationKeyDesc] = "pas ouf", + }, + }; + } + + public bool IsViolent { get; } + public override ItemType ItemType => ItemType.GrenadeFlash; + public override string Name { get; set; } = "Le Soleil"; + public override float Weight { get; set; } = 0.65f; + public override float FuseTime =>5f; + public override bool ExplodeOnCollision =>true; + + public IReadOnlyDictionary Upgrade => new Dictionary() + { + //[Scp914KnobSetting.OneToOne] = new UpgradeProperties(100, 1051) + }; + public override SpawnProperties SpawnProperties { get; set; } = new SpawnProperties() + { + + }; + + + protected override void OnExplodingGrenade(ExplodingGrenadeEventArgs ev) + { + CastTheSun(); + } + + private void CastTheSun() + { + Vector3 position = new Vector3(58.72f, 300, 20f); + Primitive prim = Primitive.Create(position, null, null, false); + prim.Flags = AdminToys.PrimitiveFlags.None; + + SoleilComp comp =prim.GameObject.AddComponent(); + + comp.Init(prim); + } + + + } +} diff --git a/KruacentExiled/KE.Items/Items/LeSoleil/SoleilComp.cs b/KruacentExiled/KE.Items/Items/LeSoleil/SoleilComp.cs new file mode 100644 index 00000000..6497c9dd --- /dev/null +++ b/KruacentExiled/KE.Items/Items/LeSoleil/SoleilComp.cs @@ -0,0 +1,102 @@ +using Exiled.API.Features; +using Exiled.API.Features.Toys; +using KE.Utils.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using Utf8Json.Formatters; +using Light = Exiled.API.Features.Toys.Light; + +namespace KE.Items.Items.LeSoleil +{ + public class SoleilComp : MonoBehaviour + { + + public float TimeActive { get; set; } + + + private Primitive @base = null; + + private Primitive Sun; + private Light[] Lights; + public void Init(Primitive prim) + { + @base = prim; + @base.Spawn(); + TimeActive = 300; + Lights = new Light[3]; + time = 0; + + + Sun = Primitive.Create(@base.Position, null, Vector3.one * 3, false); + Sun.Color = Color.yellow; + Sun.Collidable = false; + + Light l = Light.Create(@base.Position, null, null, false); + l.LightType = LightType.Directional; // not a good idea + l.Color = Color.yellow; + l.Intensity = 10; + l.Rotation = Quaternion.Euler(135, 0, 0); + l.ShadowType = LightShadows.None; + Lights[0] = l; + l.Spawn(); + + KELog.Debug("soucisse"); + + Sun.Spawn(); + } + + private float time; + + + + private void Update() + { + if (@base == null) return; + time += Time.deltaTime; + + float intensity = time / 2; + + Lights[0].Intensity = Mathf.Min(50, intensity); + + Sun.Scale = Mathf.Min(50, intensity)* Vector3.one; + + if (time >= TimeActive) + { + Destroy(); + } + + } + + + private void OnDestroy() + { + try + { + Lights[0].Destroy(); + Lights = null; + Sun.Destroy(); + Sun = null; + @base.Destroy(); + @base = null; + } + catch(Exception e) + { + Log.Error(e); + } + + Log.Debug("on desroy"); + } + + public void Destroy() + { + Log.Debug("desroy"); + Destroy(this); + + } + + } +} diff --git a/KruacentExiled/KE.Items/Items/LowGravityGrenade.cs b/KruacentExiled/KE.Items/Items/LowGravityGrenade.cs new file mode 100644 index 00000000..42e12a2f --- /dev/null +++ b/KruacentExiled/KE.Items/Items/LowGravityGrenade.cs @@ -0,0 +1,87 @@ +using System.Collections.Generic; +using Exiled.API.Enums; +using Exiled.API.Features.Spawn; +using Exiled.Events.EventArgs.Map; +using KE.Items.API.Features; +using KE.Items.API.Interface; +using KE.Items.Items.ItemEffects; + +namespace KE.Items.Items +{ + public class LowGravityGrenade : KECustomGrenade, ISwitchableEffect, IViolentItem + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Low Gravity Grenade", + [TranslationKeyDesc] = "You always wanna be on the moon, if the answer is yes this grenade will grant your wishes!", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Grenade basse gravité", + [TranslationKeyDesc] = "Pour aller attraper les étoiles", + }, + }; + } + public override ItemType ItemType => ItemType.GrenadeHE; + public override string Name { get; set; } = "Low Gravity Grenade"; + public override float Weight { get; set; } = 0.65f; + public bool IsViolent => false; + public override float FuseTime => 3f; + public override bool ExplodeOnCollision => false; + public UnityEngine.Color Color { get; set; } = UnityEngine.Color.gray; + public CustomItemEffect Effect { get; set; } + public override SpawnProperties SpawnProperties { get; set; } = new SpawnProperties() + { + Limit = 2, + RoomSpawnPoints = new List + { + new RoomSpawnPoint() + { + Room = RoomType.Hcz127, + Chance = 25 + }, + + new RoomSpawnPoint() + { + Room = RoomType.Hcz939, + Chance = 25 + }, + }, + + DynamicSpawnPoints = new List + { + new DynamicSpawnPoint() + { + Chance = 25, + Location = SpawnLocationType.InsideHidLab, + }, + }, + + }; + + public LowGravityGrenade() + { + Effect = new LowGravityGrenadeEffect(); + } + + protected override void OnExplodingGrenade(ExplodingGrenadeEventArgs ev) + { + Effect.Effect(ev); + ev.TargetsToAffect.Clear(); + } + + protected override void SubscribeEvents() + { + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + base.UnsubscribeEvents(); + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Items/Items/MScan.cs b/KruacentExiled/KE.Items/Items/MScan.cs new file mode 100644 index 00000000..9a607aa4 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/MScan.cs @@ -0,0 +1,317 @@ +using AdminToys; +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Pickups; +using Exiled.API.Features.Pools; +using Exiled.API.Features.Roles; +using Exiled.API.Features.Spawn; +using Exiled.API.Features.Toys; +using Exiled.Events.EventArgs.Player; +using Exiled.Events.EventArgs.Scp096; +using Exiled.Events.EventArgs.Scp106; +using Exiled.Events.EventArgs.Scp939; +using KE.Items.API.Features; +using KE.Utils.API.Displays.Feeds; +using MEC; +using System.Collections.Generic; +using UnityEngine; + +namespace KE.Items.Items +{ + public class MScan : KECustomItem + { + + + public const string Deploy = "MScanDeploy"; + public const string PickUp = "MScanPickUp"; + public const string TranslationDestroy = "MScanDestroy"; + public const string NoBattery = "MScanNoBattery"; + public const string Detect = "MScanDetect"; + + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "M-Scan", + [TranslationKeyDesc] = "Detect movement", + [Deploy] = "MSCAN DEPLOYED Battery: {TimeUp} seconds", + [PickUp] = "M-Scan picked up", + [TranslationDestroy] = "M-SCAN DESTROYED", + [NoBattery] = "M-Scan : No battery left", + [Detect] = "M-SCAN: %Name%", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "M-Scan", + [TranslationKeyDesc] = "Détecte les mouvements des personnes passant devant", + [Deploy] = "SCANNER DÉPLOYÉ Batterie: {TimeUp} secondes", + [PickUp] = "Scanner récupéré.", + [TranslationDestroy] = "SCANNER DÉTRUIT", + [NoBattery] = "Scanner: Batterie épuisée.", + [Detect] = "M-SCAN: %Name%", + }, + }; + } + public override ItemType ItemType => ItemType.Flashlight; + public override string Name { get; set; } = "MScan"; + public override float Weight { get; set; } = 1.5f; + public Color Color { get; set; } = Color.cyan; + public override SpawnProperties SpawnProperties { get; set; } = new SpawnProperties() + { + Limit = 2, + RoomSpawnPoints = new List + { + new RoomSpawnPoint() + { + Chance = 25, + Room = RoomType.LczGlassBox, + }, + new RoomSpawnPoint() + { + Chance = 25, + Room = RoomType.HczIncineratorWayside, + }, + new RoomSpawnPoint() + { + Chance = 25, + Room = RoomType.LczCafe, + }, + }, + }; + + private Dictionary ActiveSensors; + private Dictionary Cooldowns = new Dictionary(); + + private Dictionary BatteryLife = new Dictionary(); + private Dictionary Models = new Dictionary(); + + private CoroutineHandle SensorRoutine; + + protected override void SubscribeEvents() + { + + + ActiveSensors = DictionaryPool.Pool.Get(); + Exiled.Events.Handlers.Player.Shot += OnShot; + Exiled.Events.Handlers.Player.DroppedItem += OnDroppedItem; + + Exiled.Events.Handlers.Scp049.Attacking += OnSCP049Attacking; + Exiled.Events.Handlers.Scp106.Teleporting += OnSCP106Teleporting; + Exiled.Events.Handlers.Scp939.Clawed += OnSCP939Attacked; + Exiled.Events.Handlers.Scp096.Enraging += OnSCP096Enraging; + + SensorRoutine = Timing.RunCoroutine(MotionDetector()); + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Player.Shot -= OnShot; + Exiled.Events.Handlers.Player.DroppedItem -= OnDroppedItem; + + Exiled.Events.Handlers.Scp049.Attacking -= OnSCP049Attacking; + Exiled.Events.Handlers.Scp106.Teleporting -= OnSCP106Teleporting; + Exiled.Events.Handlers.Scp939.Clawed -= OnSCP939Attacked; + Exiled.Events.Handlers.Scp096.Enraging -= OnSCP096Enraging; + + Timing.KillCoroutines(SensorRoutine); + DictionaryPool.Pool.Return(ActiveSensors); + base.UnsubscribeEvents(); + } + + + + private void OnSCP049Attacking(Exiled.Events.EventArgs.Scp049.AttackingEventArgs ev) + { + CheckDestruction(ev.Player.Position, 2f); + } + private void OnSCP106Teleporting(TeleportingEventArgs ev) + { + CheckDestruction(ev.Player.Position, 2f); + } + private void OnSCP939Attacked(ClawedEventArgs ev) + { + CheckDestruction(ev.Player.Position, 2f); + } + + private void OnSCP096Enraging(EnragingEventArgs ev) + { + CheckDestruction(ev.Player.Position, 2f); + } + + public const float TimeUp = 120; + + private void OnDroppedItem(DroppedItemEventArgs ev) + { + if (!Check(ev.Pickup)) return; + + + Player player = ev.Player; + Pickup pickup = ev.Pickup; + + if (!ActiveSensors.ContainsKey(pickup)) + { + ActiveSensors.Add(pickup, player); + BatteryLife[pickup] = Time.time + TimeUp; + //Models[pickup] = CreateBaseModel(pickup); + + TranslationFeed(player, Deploy); + } + } + + private Primitive CreateBaseModel(Pickup pickup) + { + Primitive primitive = Primitive.Create(null, null, null, false); + primitive.Transform.localPosition = pickup.Position; + primitive.Transform.localRotation = Quaternion.identity; + primitive.Transform.localScale = Vector3.one; + primitive.MovementSmoothing = 0; + primitive.Flags = PrimitiveFlags.None; + primitive.Spawn(); + return primitive; + } + + protected override void OnPickingUp(PickingUpItemEventArgs ev) + { + Player player = ev.Player; + Pickup pickup = ev.Pickup; + + if (ActiveSensors.ContainsKey(pickup)) + { + + Remove(pickup); + TranslationFeed(player, PickUp); + } + } + + private void OnShot(ShotEventArgs ev) + { + CheckDestruction(ev.Position, 0.5f); + } + + private void CheckDestruction(Vector3 hitPos, float radius) + { + List toDestroy = ListPool.Pool.Get(); + + foreach (var kvp in ActiveSensors) + { + Pickup sensor = kvp.Key; + Player owner = kvp.Value; + if (!sensor.IsSpawned) continue; + + if (Vector3.Distance(hitPos, sensor.Position) <= radius) + { + toDestroy.Add(sensor); + if (owner != null) + { + TranslationFeed(owner, TranslationDestroy); + } + } + } + + foreach (Pickup pickup in toDestroy) + { + Remove(pickup); + pickup.Destroy(); + } + + ListPool.Pool.Return(toDestroy); + } + + private void Remove(Pickup pickup) + { + ActiveSensors.Remove(pickup); + BatteryLife.Remove(pickup); + Cooldowns.Remove(pickup); + //Models[pickup].Destroy(); + } + + + private void CheckBattery() + { + List invalid = ListPool.Pool.Get(); + foreach (var key in ActiveSensors.Keys) + { + if (BatteryLife.ContainsKey(key) && Time.time > BatteryLife[key]) + { + + Player player = ActiveSensors[key]; + if (player != null) + { + TranslationFeed(player, NoBattery); + } + invalid.Add(key); + } + } + + foreach (var i in invalid) + { + Remove(i); + i.Destroy(); + } + ListPool.Pool.Return(invalid); + } + + private IEnumerator MotionDetector() + { + while (true) + { + + float currentTime = Time.time; + + CheckBattery(); + + + foreach (var kvp in ActiveSensors) + { + Pickup sensor = kvp.Key; + Player owner = kvp.Value; + + if (sensor.Base == null) continue; + + if (Cooldowns.ContainsKey(sensor) && currentTime < Cooldowns[sensor]) continue; + + bool detected = false; + + foreach (Player target in Player.List) + { + if (target == owner) continue; + if (!target.IsAlive || target.IsNoclipPermitted) continue; + if (owner != null && target.Role.Side == owner.Role.Side) continue; + + if(target.Role is Scp106Role scp106 && !scp106.CanActivateTesla) + { + continue; + } + + + if (Vector3.Distance(sensor.Position, target.Position) < 3.5f) + { + detected = true; + + if (owner != null) + { + string color = target.Role.Color.ToHex(); + + string msg = GetTranslation(owner, Detect).Replace("%Color%", color).Replace("%Name%", target.Role.Name); + + HintFeed.AddFeed(owner, msg); + } + + break; + } + } + + if (detected) + { + Cooldowns[sensor] = currentTime + 3.0f; + } + } + + yield return Timing.WaitForSeconds(0.5f); + } + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Items/Items/Mine.cs b/KruacentExiled/KE.Items/Items/Mine.cs new file mode 100644 index 00000000..1b92ce9f --- /dev/null +++ b/KruacentExiled/KE.Items/Items/Mine.cs @@ -0,0 +1,123 @@ +/*using UnityEngine; +using Exiled.Events.EventArgs.Player; +using Exiled.API.Features.Spawn; +using KE.Items.API.Interface; +using KE.Items.Items.ItemEffects; +using KE.Items.API.Features; +using System.Collections.Generic; +using Exiled.API.Enums; +using KE.Items.API.Core.Models; +using KE.Items.Items.PickupModels; + +namespace KE.Items.Items +{ + /*public class Mine : KECustomItem, ISwitchableEffect, ICustomPickupModel,IViolentItem + { + protected override Dictionary> SetTranslation() + { + return new() + { + ["en"] = new() + { + [TranslationKeyName] = "Mine", + [TranslationKeyDesc] = "Drop to deploy the mine, little advice : don't step on it", + }, + ["fr"] = new() + { + [TranslationKeyName] = "Mine", + [TranslationKeyDesc] = "Une Mine ! Lâcher pour la déployer", + }, + }; + } + public override ItemType ItemType => ItemType.KeycardJanitor; + public bool IsViolent => true; + public override string Name { get; set; } = "Mine"; + public override float Weight { get; set; } = 0.65f; + public Color Color { get; set; } = Color.yellow; + public CustomItemEffect Effect { get; set; } + + + public override SpawnProperties SpawnProperties { get; set; } = null; + /*new SpawnProperties() + { + Limit = 1, + DynamicSpawnPoints = new List + { + new DynamicSpawnPoint() + { + Chance= 25, + Location = SpawnLocationType.Inside049Armory, + }, + new DynamicSpawnPoint() + { + Chance= 25, + Location = SpawnLocationType.InsideEscapeSecondary, + }, + new DynamicSpawnPoint() + { + Chance= 25, + Location = SpawnLocationType.InsideGateA, + }, + new DynamicSpawnPoint() + { + Chance= 25, + Location = SpawnLocationType.InsideGateB, + } + }, + LockerSpawnPoints = new List + { + new LockerSpawnPoint() + { + Chance= 20, + Type = LockerType.Misc, + }, + new LockerSpawnPoint() + { + Chance= 20, + Type = LockerType.RifleRack, + }, + } + + }; + + public PickupModel PickupModel { get; } + + public Mine() + { + //Effect = new MineEffect(); + PickupModel = new MinePModel(this); + } + + protected override void SubscribeEvents() + { + PickupModel.SubscribeEvents(); + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + PickupModel.UnsubscribeEvents(); + base.UnsubscribeEvents(); + } + + protected override void OnDroppingItem(DroppingItemEventArgs ev) + { + + + if (ev.IsThrown) + { + ev.IsAllowed = true; + return; + } + + ev.IsAllowed = false; + ev.Player.RemoveItem(ev.Item); + Effect.Effect(ev); + + } + + + + } +} +*/ \ No newline at end of file diff --git a/KruacentExiled/KE.Items/Items/Models/DeployWallModel.cs b/KruacentExiled/KE.Items/Items/Models/DeployWallModel.cs new file mode 100644 index 00000000..c7dd65a2 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/Models/DeployWallModel.cs @@ -0,0 +1,65 @@ + + +/*using Exiled.API.Features; +using Exiled.API.Features.Toys; +using Exiled.Events.Commands.Reload; +using MEC; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using Light = Exiled.API.Features.Toys.Light; + +namespace KE.Items.Items.Models +{ + internal class DeployWallModel : Model + { + const float distance = 2; + internal override void Create(Vector3 spawnPos, Quaternion rotation) + { + + Vector3 forward = rotation * Vector3.forward; + Vector3 spawnPosi = spawnPos + forward * distance; + Vector3 rotat = new Vector3(0, rotation.eulerAngles.y, 0); + + + + } + + internal void Change() + { + Timing.CallDelayed(10, () => + { + UnSpawn(); + }); + Timing.CallDelayed(5, () => + { + Toys.ForEach(t => + { + if (t is Primitive p) p.Color = Color.yellow; + }); + }); + Timing.CallDelayed(8, () => + { + Toys.ForEach(t => + { + if (t is Primitive p) p.Color = Color.red; + }); + }); + } + + private void SpawnWall(Vector3 pos, Quaternion rotation) + { + Vector3 forward = rotation * Vector3.forward; + Vector3 spawnPos = pos + forward * distance; + Vector3 rotat = new Vector3(0, rotation.eulerAngles.y, 0); + + Primitive wall = Primitive.Create(PrimitiveType.Cube, spawnPos, rotat, new Vector3(4, 4, 0.2f), true); + wall.Collidable = true; + wall.Visible = true; + + + + } + } +} +*/ \ No newline at end of file diff --git a/KruacentExiled/KE.Items/Items/Molotov.cs b/KruacentExiled/KE.Items/Items/Molotov.cs new file mode 100644 index 00000000..09ac8633 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/Molotov.cs @@ -0,0 +1,112 @@ +using Exiled.API.Enums; +using Exiled.API.Features.Attributes; +using Exiled.API.Features.Spawn; +using Exiled.CustomItems.API.Features; +using Exiled.Events.EventArgs.Map; +using Exiled.Events.EventArgs.Player; +using KE.Items.API.Core.Models; +using KE.Items.API.Core.Upgrade; +using KE.Items.API.Features; +using KE.Items.API.Interface; +using KE.Items.Items.ItemEffects; +using KE.Items.Items.PickupModels; +using Scp914; +using System.Collections.Generic; +using UnityEngine; + +namespace KE.Items.Items +{ + public class Molotov : KECustomGrenade, ISwitchableEffect, ICustomPickupModel, IUpgradableCustomItem + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Molotov Cocktail", + [TranslationKeyDesc] = "ARSON", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Cocktail Molotov", + [TranslationKeyDesc] = "La meilleure arme contre un blindé", + }, + }; + } + public override ItemType ItemType => ItemType.GrenadeFlash; + public override string Name { get; set; } = "CocktailMolotov"; + public override float Weight { get; set; } = 0.65f; + public override float FuseTime => 5f; + public override bool ExplodeOnCollision => true; + public Color Color { get; set; } = Color.yellow; + public CustomItemEffect Effect { get; set; } + public PickupModel PickupModel { get; } + + public IReadOnlyDictionary Upgrade => new Dictionary() + { + [Scp914KnobSetting.OneToOne] = new UpgradeProperties(100, "HealZone") + }; + public override SpawnProperties SpawnProperties { get; set; } = new SpawnProperties() + { + Limit = 2, + LockerSpawnPoints = new List + { + new LockerSpawnPoint() { Chance = 75, UseChamber = true, Type = LockerType.Misc, Zone = ZoneType.Entrance, }, + new LockerSpawnPoint() { Chance = 50, UseChamber = true, Type = LockerType.Misc, Zone = ZoneType.LightContainment, }, + new LockerSpawnPoint() { Chance = 50, UseChamber = true, Type = LockerType.Misc, Zone = ZoneType.HeavyContainment, }, + }, + + RoomSpawnPoints = new List + { + new RoomSpawnPoint() { Chance = 75, Room = RoomType.LczGlassBox, }, + new RoomSpawnPoint() { Chance = 80, Room = RoomType.HczArmory, }, + new RoomSpawnPoint() { Chance = 80, Room = RoomType.Hcz049, }, + }, + }; + + public Molotov() + { + Effect = new MolotovEffect(); + PickupModel = new MolotovPModel(this); + } + + protected override void SubscribeEvents() + { + PickupModel.SubscribeEvents(); + Exiled.Events.Handlers.Player.ReceivingEffect += ReceivedEffect; + Exiled.Events.Handlers.Player.PickingUpItem += PickingItem; + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + PickupModel.UnsubscribeEvents(); + Exiled.Events.Handlers.Player.ReceivingEffect -= ReceivedEffect; + Exiled.Events.Handlers.Player.PickingUpItem -= PickingItem; + base.UnsubscribeEvents(); + } + + private void ReceivedEffect(ReceivingEffectEventArgs ev) + { + if (Effect is MolotovEffect molotovEffect) + { + molotovEffect.OnReceivingEffect(ev); + } + } + + private void PickingItem(PickingUpItemEventArgs ev) + { + if (Effect is MolotovEffect molotovEffect) + { + molotovEffect.OnPickingUp(ev); + } + } + + protected override void OnExplodingGrenade(ExplodingGrenadeEventArgs ev) + { + Effect.Effect(ev); + ev.TargetsToAffect.Clear(); + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Items/Items/PickupModels/HolyGrenadePModel.cs b/KruacentExiled/KE.Items/Items/PickupModels/HolyGrenadePModel.cs new file mode 100644 index 00000000..2041ad6c --- /dev/null +++ b/KruacentExiled/KE.Items/Items/PickupModels/HolyGrenadePModel.cs @@ -0,0 +1,45 @@ +using Exiled.API.Features.Toys; +using Exiled.CustomItems.API.Features; +using KE.Items.API.Core.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Items.Items.PickupModels +{ + public class HolyGrenadePModel : PickupModel + { + public HolyGrenadePModel(CustomItem customItem) : base(customItem) + { + } + + private static Color32 red = new Color32(255, 0, 0, 255); + private static Color32 gold = new Color32(255, 215, 0, 255); + private static Color32 white = new Color32(255, 255, 255, 255); + + + public override float Scale => 0.2f; + + protected override void CreateModel(Transform parent) + { + + + var sphere = CreatePrimitive(parent, PrimitiveType.Sphere, Vector3.zero, Quaternion.identity, Vector3.one, white); + + var gem = CreatePrimitive(parent, PrimitiveType.Sphere, new Vector3(0,0.8f,0), Quaternion.identity, new Vector3(.15f,.1f,.1f), red); + + var crossV = CreatePrimitive(parent, PrimitiveType.Cube, new Vector3(0, 0.74f, 0), Quaternion.identity, new Vector3(.1f, .5f, .1f), white); + var crossH = CreatePrimitive(parent, PrimitiveType.Cube, new Vector3(0, 0.8f, 0), Quaternion.identity, new Vector3(.1f, .1f, .37f), white); + + var ring1 = CreatePrimitive(parent, PrimitiveType.Cylinder, Vector3.zero, Quaternion.Euler(90,0,0), new Vector3(1.05f, .05f, 1.05f), gold); + var ring2 = CreatePrimitive(parent, PrimitiveType.Cylinder, Vector3.zero, Quaternion.identity, new Vector3(1.05f, .05f, 1.05f), gold); + var ring3 = CreatePrimitive(parent, PrimitiveType.Cylinder, Vector3.zero, Quaternion.Euler(90, 90, 0), new Vector3(1.05f, .05f, 1.05f), gold); + + var littleBase = CreatePrimitive(parent, PrimitiveType.Cylinder, new Vector3(0, 0.5f, 0), Quaternion.identity, new Vector3(.3f, .05f, .3f), gold); + + } + } +} diff --git a/KruacentExiled/KE.Items/Items/PickupModels/MinePModel.cs b/KruacentExiled/KE.Items/Items/PickupModels/MinePModel.cs new file mode 100644 index 00000000..09575dd7 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/PickupModels/MinePModel.cs @@ -0,0 +1,84 @@ +using Exiled.API.Features; +using Exiled.API.Features.Toys; +using KE.Items.API.Core.Models; +using KE.Items.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using Light = Exiled.API.Features.Toys.Light; + +namespace KE.Items.Items.PickupModels +{ + public class MinePModel : PickupModel + { + + public MinePModel(KECustomItem customItem) : base(customItem) { } + + public override float Scale => 0.25f; + + + public static Color32 lightColor = new Color32(255, 0, 0, 0); + protected override void CreateModel(Transform parent) + { + // This code was auto-generated + + var Mine = CreateEmptyPrimitive( + parent, + new Vector3(0, 0, 0), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(1f, 1f, 1f) + ); + + var botomCylinder = CreatePrimitive( + Mine.transform, + PrimitiveType.Cylinder, + new Vector3(0f, 0f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(1f, 0.07f, 1f), + new Color32(0, 101, 6, 255) + ); + + var middleCylinder = CreatePrimitive( + Mine.transform, + PrimitiveType.Cylinder, + new Vector3(0f, 0.084f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(0.7f, 0.03f, 0.7f), + new Color32(0, 101, 6, 255) + ); + + var topCylinder = CreatePrimitive( + Mine.transform, + PrimitiveType.Cylinder, + new Vector3(0f, 0.121f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(0.6f, 0.03f, 0.6f), + new Color32(0, 101, 6, 255) + ); + + var light = CreatePrimitive( + Mine.transform, + PrimitiveType.Sphere, + new Vector3(0f, 0.135f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(0.08f, 0.08f, 0.08f), + new Color32(255, 0, 0, 112) + ); + + var Point_Light = CreateLight( + light.transform, + new Vector3(0f, 0.9f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(12.5f, 12.5f, 12.5f), + new Color32(255, 0, 0, 255), + LightType.Point, + 3.48f, + 0.4f + ); + } + + } +} diff --git a/KruacentExiled/KE.Items/Items/PickupModels/MolotovPModel.cs b/KruacentExiled/KE.Items/Items/PickupModels/MolotovPModel.cs new file mode 100644 index 00000000..1bb9ac0c --- /dev/null +++ b/KruacentExiled/KE.Items/Items/PickupModels/MolotovPModel.cs @@ -0,0 +1,27 @@ +using Exiled.API.Features.Toys; +using Exiled.CustomItems.API.Features; +using KE.Items.API.Core.Models; +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace KE.Items.Items.PickupModels +{ + public class MolotovPModel : PickupModel + { + + public MolotovPModel(CustomItem customItem) : base(customItem) { } + private static Color32 bottleColor = new Color32(148, 37, 1,233); + + public override float Scale => .15f; + + protected override void CreateModel(Transform parent) + { + var base1 = CreatePrimitive(parent, PrimitiveType.Cylinder, Vector3.up*.7f, Quaternion.identity, new Vector3(0.6f, 0.09f, 0.6f), bottleColor); + var base2 = CreatePrimitive(parent, PrimitiveType.Cylinder, Vector3.down *.08f, Quaternion.identity, new Vector3(0.8f,0.7f,0.8f), bottleColor); + var base3 = CreatePrimitive(parent, PrimitiveType.Cylinder, Vector3.up *.9f, Quaternion.identity, new Vector3(0.4f,0.1f,0.4f), bottleColor); + var base4 = CreatePrimitive(parent, PrimitiveType.Cylinder, Vector3.up *1.4f, Quaternion.identity, new Vector3(0.3f,0.4f,0.3f), bottleColor); + var base5 = CreatePrimitive(parent, PrimitiveType.Cylinder, Vector3.down *.8f, Quaternion.identity, new Vector3(0.6f,0.06f,0.6f), bottleColor); + } + } +} diff --git a/KruacentExiled/KE.Items/Items/PickupModels/PressePureePModel.cs b/KruacentExiled/KE.Items/Items/PickupModels/PressePureePModel.cs new file mode 100644 index 00000000..95311133 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/PickupModels/PressePureePModel.cs @@ -0,0 +1,29 @@ +using Exiled.API.Features.Toys; +using Exiled.CustomItems.API.Features; +using KE.Items.API.Core.Models; +using UnityEngine; + +namespace KE.Items.Items.PickupModels +{ + public class PressePureePModel : PickupModel + { + + public PressePureePModel(CustomItem customItem) : base(customItem) { } + + + private static Vector3 mancheScale = new Vector3(.5f, .885f, .5f); + + public static readonly Color32 colorManche = new Color32(84, 43, 11, 255); + public static readonly Color32 colorExplosif = new Color32(31, 31, 31, 255); + + public override float Scale => 0.15f; + + protected override void CreateModel(Transform parent) + { + var manche = CreatePrimitive(parent,PrimitiveType.Cylinder,new Vector3(0,.006f,0),Quaternion.identity, mancheScale, colorManche); + var manche2 = CreatePrimitive(parent,PrimitiveType.Cylinder,new Vector3(0,.9463f,0),Quaternion.identity, new Vector3(0.45f,0.05f,0.45f), colorExplosif); + var explosif1 = CreatePrimitive(parent,PrimitiveType.Cylinder,new Vector3(0,1.2113f,0),Quaternion.identity, new Vector3(0.7f,0.2274f,0.7f), colorExplosif); + var explosif2 = CreatePrimitive(parent,PrimitiveType.Cylinder,new Vector3(0,1,0),Quaternion.identity, new Vector3(0.8f,0.02f,0.8f), colorExplosif); + } + } +} diff --git a/KruacentExiled/KE.Items/Items/PickupModels/Scp3136PModel.cs b/KruacentExiled/KE.Items/Items/PickupModels/Scp3136PModel.cs new file mode 100644 index 00000000..836aa583 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/PickupModels/Scp3136PModel.cs @@ -0,0 +1,38 @@ +using Exiled.API.Features.Toys; +using Exiled.CustomItems.API.Features; +using KE.Items.API.Core.Models; +using UnityEngine; + +namespace KE.Items.Items.PickupModels +{ + public class Scp3136PModel : PickupModel + { + + public Scp3136PModel(CustomItem customItem) : base(customItem) { } + + + public override float Scale => .5f; + + protected override void CreateModel(Transform parent) + { + + + + + + Primitive land = Primitive.Get(CreatePrimitive(parent, PrimitiveType.Cube, Vector3.up * -0.035f,Quaternion.identity,Vector3.one,new Color32())); + land.Flags = AdminToys.PrimitiveFlags.None; + + + + #region land + + + + + #endregion + + + } + } +} diff --git a/KruacentExiled/KE.Items/Items/PickupModels/TPGrenadaPModel.cs b/KruacentExiled/KE.Items/Items/PickupModels/TPGrenadaPModel.cs new file mode 100644 index 00000000..328b5d0a --- /dev/null +++ b/KruacentExiled/KE.Items/Items/PickupModels/TPGrenadaPModel.cs @@ -0,0 +1,178 @@ +using Exiled.API.Features.Toys; +using Exiled.CustomItems.API.Features; +using KE.Items.API.Core.Models; +using KE.Items.API.Features; +using UnityEngine; +using Light = Exiled.API.Features.Toys.Light; + +namespace KE.Items.Items.PickupModels +{ + public class TPGrenadaPModel : PickupModel + { + public TPGrenadaPModel(CustomItem customItem) : base(customItem) + { + } + + public override float Scale => .15f; + + protected override void CreateModel(Transform parent) + { + // This code was auto-generated + + var Tpgrenada = CreateEmptyPrimitive( + parent, + new Vector3(0f, 0f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(1.21f, 1.21f, 1.21f) + ); + + var bottomSupport = CreatePrimitive( + Tpgrenada.transform, + PrimitiveType.Cube, + new Vector3(0f, -1f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(1f, 0.25f, 1f), + new Color32(217, 217, 217, 255) + ); + + var topSupport = CreatePrimitive( + Tpgrenada.transform, + PrimitiveType.Cube, + new Vector3(0f, 1f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(1f, 0.25f, 1f), + new Color32(217, 217, 217, 255) + ); + + var pillars = CreateEmptyPrimitive( + Tpgrenada.transform, + new Vector3(0f, 0f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(1f, 1f, 1f) + ); + + var pillar1 = CreatePrimitive( + pillars.transform, + PrimitiveType.Cylinder, + new Vector3(-0.4f, 0f, 0.4f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(0.1f, 1f, 0.1f), + new Color32(217, 217, 217, 255) + ); + + var pillar2 = CreatePrimitive( + pillars.transform, + PrimitiveType.Cylinder, + new Vector3(0.4f, 0f, 0.4f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(0.1f, 1f, 0.1f), + new Color32(217, 217, 217, 255) + ); + + var pillar3 = CreatePrimitive( + pillars.transform, + PrimitiveType.Cylinder, + new Vector3(0.4f, 0f, -0.4f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(0.1f, 1f, 0.1f), + new Color32(217, 217, 217, 255) + ); + + var pillar4 = CreatePrimitive( + pillars.transform, + PrimitiveType.Cylinder, + new Vector3(-0.4f, 0f, -0.4f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(0.1f, 1f, 0.1f), + new Color32(217, 217, 217, 255) + ); + + var centralPillar = CreatePrimitive( + pillars.transform, + PrimitiveType.Cylinder, + new Vector3(0f, 0f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(0.3f, 0.8f, 0.3f), + new Color32(0, 255, 255, 255) + ); + + var glass = CreatePrimitive( + Tpgrenada.transform, + PrimitiveType.Cube, + new Vector3(0f, 0f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(1f, 1.75f, 1f), + new Color32(54, 219, 243, 69) + ); + + var innerglass = CreatePrimitive( + Tpgrenada.transform, + PrimitiveType.Cylinder, + new Vector3(0f, 0f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(0.7f, 1f, 0.7f), + new Color32(54, 219, 243, 69) + ); + + var lights = CreateEmptyPrimitive( + Tpgrenada.transform, + new Vector3(0f, 0f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(1f, 1f, 1f) + ); + + // var top = CreateLight( + // lights.transform, + // new Vector3(0f, 0.5f, 0f), + // new Quaternion(0f, 0f, 0f, 1f), + // new Vector3(1f, 1f, 1f), + // new Color32(0, 255, 255, 255), + // LightType.Point, + // 1f, + // 1.25f + //); + + + var center = CreateLight( + lights.transform, + new Vector3(0f, 0f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(1f, 1f, 1f), + new Color32(0, 255, 255, 255), + LightType.Point, + .1f, + 1.25f + ); + + // var bottom = CreateLight( + // lights.transform, + // new Vector3(0f, -0.5f, 0f), + // new Quaternion(0f, 0f, 0f, 1f), + // new Vector3(1f, 1f, 1f), + // new Color32(0, 255, 255, 255), + // LightType.Point, + // 1f, + // 1.25f + //); + + var topbase = CreatePrimitive( + Tpgrenada.transform, + PrimitiveType.Cylinder, + new Vector3(0f, 0.9f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(0.9f, 0.1f, 0.9f), + new Color32(255, 255, 255, 255) + ); + + var botbase = CreatePrimitive( + Tpgrenada.transform, + PrimitiveType.Cylinder, + new Vector3(0f, -0.9f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(0.9f, 0.1f, 0.9f), + new Color32(255, 255, 255, 255) + ); + } + + } +} diff --git a/KruacentExiled/KE.Items/Items/PressePuree.cs b/KruacentExiled/KE.Items/Items/PressePuree.cs new file mode 100644 index 00000000..6facda1f --- /dev/null +++ b/KruacentExiled/KE.Items/Items/PressePuree.cs @@ -0,0 +1,120 @@ + +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using Exiled.API.Features.Pickups.Projectiles; +using Exiled.API.Features.Spawn; +using Exiled.CustomItems.API.Features; +using Exiled.Events.EventArgs.Scp914; +using KE.Items.API.Core.Models; +using KE.Items.API.Core.Upgrade; +using KE.Items.API.Events; +using KE.Items.API.Features; +using KE.Items.API.Interface; +using KE.Items.Items.PickupModels; +using KE.Utils.API.Features; +using Scp914; +using System; +using System.Collections.Generic; + +namespace KE.Items.Items +{ + public class PressePuree : KECustomGrenade, IUpgradableCustomItem, ICustomPickupModel + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Presse Purée", + [TranslationKeyDesc] = "explode at impact but does less damage", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Presse Purée", + [TranslationKeyDesc] = "Explosion à l'impact mais moins efficace", + }, + }; + } + public override string Name { get; set; } = "PressePuree"; + + public override ItemType ItemType => ItemType.GrenadeHE; + public override float Weight { get; set; } = 0.65f; + public override float FuseTime => 5f; + public override bool ExplodeOnCollision => true; + public override float DamageModifier => .3f; + public PickupModel PickupModel { get; } + public override SpawnProperties SpawnProperties { get; set; } = new SpawnProperties() + { + Limit = 5, + DynamicSpawnPoints = new List + { + new DynamicSpawnPoint() + { + Chance = 50, + Location = SpawnLocationType.InsideHczArmory, + }, + new DynamicSpawnPoint() + { + Chance = 5, + Location = SpawnLocationType.Inside914, + }, + new DynamicSpawnPoint() + { + Chance= 50, + Location = SpawnLocationType.Inside049Armory, + }, + new DynamicSpawnPoint() + { + Chance= 50, + Location = SpawnLocationType.InsideLczArmory, + } + }, + }; + + public PressePuree() + { + PickupModel = new PressePureePModel(this); + } + + + protected override void SubscribeEvents() + { + PickupModel.SubscribeEvents(); + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + PickupModel.UnsubscribeEvents(); + base.UnsubscribeEvents(); + } + + + + + protected override void OnExplodeDestructible(OnExplodeDestructibleEventsArgs ev) + { + KELog.Debug("old dmagea="+ev.Damage); + Player player = Player.Get(ev.Destructible.NetworkId); + if (!Check(ev.ExplosionGrenade)) return; + + + if (ev.Damage <= 0f) return; + + if (player != null && player.IsScp) + { + ev.Damage /= 3f; + } + + KELog.Debug("new daamager="+ev.Damage); + + } + + public IReadOnlyDictionary Upgrade { get; private set; } = new Dictionary() + { + { Scp914KnobSetting.VeryFine,new UpgradeProperties(5, "SainteGrenada")} + }; + } +} diff --git a/KruacentExiled/KE.Items/Items/ProximityGrenade.cs b/KruacentExiled/KE.Items/Items/ProximityGrenade.cs new file mode 100644 index 00000000..84d0ec0f --- /dev/null +++ b/KruacentExiled/KE.Items/Items/ProximityGrenade.cs @@ -0,0 +1,75 @@ +using System.Collections.Generic; +using Exiled.API.Enums; +using Exiled.API.Features.Spawn; +using Exiled.Events.EventArgs.Map; +using KE.Items.API.Features; +using KE.Items.API.Interface; +using KE.Items.Items.ItemEffects; + +namespace KE.Items.Items +{ + public class ProximityGrenade : KECustomGrenade, ISwitchableEffect, IViolentItem + { + + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Proximity Grenade", + [TranslationKeyDesc] = "Show lines to all players around 3 rooms", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Grenade de proximité", + [TranslationKeyDesc] = "Montre tous les joueurs dans un rayon de 3 salles", + }, + }; + } + + public bool IsViolent => false; + public override ItemType ItemType => ItemType.GrenadeFlash; + public override string Name { get; set; } = "ProximityGrenade"; + public override float Weight { get; set; } = 0.65f; + public override float FuseTime => 3f; + public override bool ExplodeOnCollision => false; + public UnityEngine.Color Color { get; set; } = UnityEngine.Color.red; + public CustomItemEffect Effect { get; set; } + public override SpawnProperties SpawnProperties { get; set; } = new SpawnProperties() + { + Limit = 1, + RoomSpawnPoints = new List + { + new RoomSpawnPoint() + { + Room = RoomType.HczElevatorA, + Offset = new UnityEngine.Vector3(1f, 0f, 1f), + Chance = 50 + } + }, + + }; + + public ProximityGrenade() + { + Effect = new ProximityGrenadeEffect(); + } + + protected override void OnExplodingGrenade(ExplodingGrenadeEventArgs ev) + { + Effect.Effect(ev); + ev.TargetsToAffect.Clear(); + } + + protected override void SubscribeEvents() + { + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + base.UnsubscribeEvents(); + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Items/Items/RedbullEnergy.cs b/KruacentExiled/KE.Items/Items/RedbullEnergy.cs new file mode 100644 index 00000000..91ba02ac --- /dev/null +++ b/KruacentExiled/KE.Items/Items/RedbullEnergy.cs @@ -0,0 +1,85 @@ +using System.Collections.Generic; +using Exiled.API.Enums; +using Exiled.API.Features.Attributes; +using Exiled.API.Features.Spawn; +using Exiled.CustomItems.API.Features; +using Exiled.Events.EventArgs.Player; +using KE.Items.API.Features; +using KE.Items.API.Interface; +using KE.Items.Items.ItemEffects; + +namespace KE.Items.Items +{ + public class RedbullEnergy : KECustomItem, ISwitchableEffect + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "RedBull™ Energy", + [TranslationKeyDesc] = "RedBull™ gives you wings!", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "RedBull™ Energy", + [TranslationKeyDesc] = "RedBull™ donne des ailes ! Attention à la chute !!!", + }, + }; + } + public override ItemType ItemType => ItemType.SCP207; + public override string Name { get; set; } = "RedBullEnergy"; + public override float Weight { get; set; } = 0.65f; + public UnityEngine.Color Color { get; set; } = UnityEngine.Color.blue; + public CustomItemEffect Effect { get; set; } + + public override SpawnProperties SpawnProperties { get; set; } = new SpawnProperties() + { + Limit = 2, + RoomSpawnPoints = new List + { + new RoomSpawnPoint() { Room = RoomType.EzStraight, Chance = 25 }, + new RoomSpawnPoint() { Room = RoomType.LczCrossing, Chance = 25 }, + }, + LockerSpawnPoints = new List + { + new LockerSpawnPoint { Chance = 20, UseChamber = true, Type = LockerType.Medkit, Zone = ZoneType.Entrance }, + new LockerSpawnPoint { Chance = 30, UseChamber = true, Type = LockerType.Misc, Zone = ZoneType.LightContainment} + } + }; + + public RedbullEnergy() + { + Effect = new RedBullEnergyEffect(); + } + + protected override void SubscribeEvents() + { + Exiled.Events.Handlers.Player.UsedItem += OnUsedItem; + Exiled.Events.Handlers.Player.Hurting += OnHurting; + Exiled.Events.Handlers.Player.Dying += OnDying; + Exiled.Events.Handlers.Player.Left += OnLeft; + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Player.UsedItem -= OnUsedItem; + Exiled.Events.Handlers.Player.Hurting -= OnHurting; + Exiled.Events.Handlers.Player.Dying -= OnDying; + Exiled.Events.Handlers.Player.Left -= OnLeft; + base.UnsubscribeEvents(); + } + + private void OnUsedItem(UsedItemEventArgs ev) + { + if (!Check(ev.Item)) return; + Effect.Effect(ev); + } + + private void OnHurting(HurtingEventArgs ev) => (Effect as RedBullEnergyEffect)?.OnHurting(ev); + private void OnDying(DyingEventArgs ev) => (Effect as RedBullEnergyEffect)?.Cleanup(ev.Player); + private void OnLeft(LeftEventArgs ev) => (Effect as RedBullEnergyEffect)?.Cleanup(ev.Player); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Items/Items/SainteGrenada.cs b/KruacentExiled/KE.Items/Items/SainteGrenada.cs new file mode 100644 index 00000000..88524a6c --- /dev/null +++ b/KruacentExiled/KE.Items/Items/SainteGrenada.cs @@ -0,0 +1,102 @@ + +using System; +using System.Collections.Generic; +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using Exiled.API.Features.Items; +using Exiled.API.Features.Spawn; +using Exiled.API.Features.Toys; +using Exiled.CustomItems.API.Features; +using Exiled.Events.EventArgs.Map; +using Exiled.Events.EventArgs.Player; +using KE.Items.API.Core.Models; +using KE.Items.API.Features; +using KE.Items.API.Interface; +using KE.Items.Items.PickupModels; +using MEC; +using UnityEngine; + +namespace KE.Items.Items +{ + public class SainteGrenada : KECustomGrenade, ICustomPickupModel, IViolentItem + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Holy Grenade", + [TranslationKeyDesc] = "HOLY SHIT WORMS????", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Sainte Grenade", + [TranslationKeyDesc] = "Worms reference !?", + }, + }; + } + public bool IsViolent => false; + public override ItemType ItemType => ItemType.GrenadeHE; + public override string Name { get; set; } = "SainteGrenada"; + public override float Weight { get; set; } = 1.5f; + public override float FuseTime => 6f; + public override bool ExplodeOnCollision => false; + public override float DamageModifier => 3f; + public Color Color { get; set; } = Color.red; + + + public int NbGrenadeSpawned { get; set; } = 3; + public float SpawnRadius { get; set; } = 5f; + public float GrenadeSize { get; set; } = 4f; + + + public override SpawnProperties SpawnProperties { get; set; } = new SpawnProperties() + { + + }; + + public PickupModel PickupModel { get; } + + + public SainteGrenada() + { + PickupModel = new HolyGrenadePModel(this); + } + + + protected override void SubscribeEvents() + { + PickupModel.SubscribeEvents(); + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + PickupModel.UnsubscribeEvents(); + base.UnsubscribeEvents(); + } + + + protected override void OnThrownProjectile(ThrownProjectileEventArgs ev) + { + KE.Utils.API.Sounds.SoundPlayer.Instance.Play("worms", ev.Projectile.Position, 50,20f); + } + protected override void OnExplodingGrenade(ExplodingGrenadeEventArgs ev) + { + ev.Projectile.Scale = new Vector3(GrenadeSize, GrenadeSize, GrenadeSize); + + for (int i = 0; i < NbGrenadeSpawned; i++) + { + + Vector3 spawnPosition = ev.Position; + + ExplosiveGrenade grenade = (ExplosiveGrenade)Item.Create(ItemType.GrenadeHE); + grenade.SpawnActive(spawnPosition).FuseTime = 0f; + } + + + } + } +} diff --git a/KruacentExiled/KE.Items/Items/Scp1650.cs b/KruacentExiled/KE.Items/Items/Scp1650.cs new file mode 100644 index 00000000..cf7f1ca9 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/Scp1650.cs @@ -0,0 +1,71 @@ +using Exiled.API.Features.Attributes; +using Exiled.API.Features.Spawn; +using Exiled.CustomItems.API.Features; +using Exiled.Events.EventArgs.Player; +using KE.Items.API.Features; +using KE.Items.API.Interface; +using KE.Items.Items.ItemEffects; +using System.Collections.Generic; +using PlayerHandle = Exiled.Events.Handlers.Player; + + +namespace KE.Items.Items +{ + public class Scp1650 : KECustomItem, ISwitchableEffect + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "SCP-1650", + [TranslationKeyDesc] = "ויאמר ה' ל— סח השמן על בשרך ולך בינות ה— כששם אלוהים על שפתיך, וזעם ה' וחרון אפו ו— דם המכבים יטהר בית מקדשו אחריך, לנצח נצחים", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "SCP-1650", + [TranslationKeyDesc] = "ויאמר ה' ל— סח השמן על בשרך ולך בינות ה— כששם אלוהים על שפתיך, וזעם ה' וחרון אפו ו— דם המכבים יטהר בית מקדשו אחריך, לנצח נצחים", + }, + }; + } + public override ItemType ItemType => ItemType.Painkillers; + + public override string Name { get; set; } = "SCP-1650"; + public override float Weight { get; set; } = 0.65f; + public CustomItemEffect Effect { get; set; } + + public override SpawnProperties SpawnProperties { get; set; } = new SpawnProperties() + { + Limit = 1, + LockerSpawnPoints = new List() + { + new LockerSpawnPoint() + { + Type = Exiled.API.Enums.LockerType.Scp207Pedestal, + UseChamber = true, + } + } + }; + + public Scp1650() + { + Effect = new Scp1650Effect(); + } + + protected override void SubscribeEvents() + { + PlayerHandle.UsedItem += OnUsedItem; + } + protected override void UnsubscribeEvents() + { + PlayerHandle.UsedItem -= OnUsedItem; + } + + private void OnUsedItem(UsedItemEventArgs ev) + { + if (!Check(ev.Item)) return; + Effect.Effect(ev); + } + } +} diff --git a/KruacentExiled/KE.Items/Items/Scp3136.cs b/KruacentExiled/KE.Items/Items/Scp3136.cs new file mode 100644 index 00000000..a156f731 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/Scp3136.cs @@ -0,0 +1,116 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using Exiled.API.Features.Items; +using Exiled.API.Features.Spawn; +using Exiled.CustomItems.API.Features; +using Exiled.Events.EventArgs.Player; +using Exiled.Events.EventArgs.Server; +using KE.Items.API.Features; +using PlayerRoles; +using System.Collections.Generic; +using UnityEngine; + +namespace KE.Items.Items +{ + public class Scp3136 : KECustomItem/*, ICustomPickupModel*/ + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "SCP-3136", + [TranslationKeyDesc] = "A map of the facility, you could draw your friends next to you", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "SCP-3136", + [TranslationKeyDesc] = "Une carte de la facilité, tu pourrais dessiner tes amis proche de toi", + }, + }; + } + public override ItemType ItemType => ItemType.SCP1576; + public override string Name { get; set; } = "SCP-3136"; + public override float Weight { get; set; } = 0.65f; + + private Dictionary _respawnPositions = new Dictionary + { + { Faction.FoundationStaff , Vector3.zero}, + { Faction.FoundationEnemy , Vector3.zero} + }; + + public override SpawnProperties SpawnProperties { get; set; } = new SpawnProperties() + { + Limit = 1, + LockerSpawnPoints = new List() + { + new LockerSpawnPoint() + { + Type = Exiled.API.Enums.LockerType.Scp1576Pedestal, + UseChamber = true, + Chance = .5f + } + } + }; + + //public PickupModel PickupModel { get; } + + public Scp3136() + { + //PickupModel = new Scp3136PModel(this); + } + + protected override void SubscribeEvents() + { + Exiled.Events.Handlers.Player.UsedItem += OnDrawing; + Exiled.Events.Handlers.Server.RespawnedTeam += OnRespawnedTeam; + //PickupModel?.SubscribeEvents(); + base.SubscribeEvents(); + } + protected override void UnsubscribeEvents() + { + //PickupModel?.UnsubscribeEvents(); + Exiled.Events.Handlers.Player.UsedItem -= OnDrawing; + Exiled.Events.Handlers.Server.RespawnedTeam -= OnRespawnedTeam; + base.UnsubscribeEvents(); + } + + private void OnDrawing(UsedItemEventArgs ev) + { + if (!Check(ev.Item)) return; + Scp1576 item = ((Scp1576)ev.Item); + switch (ev.Player.Role.Side) + { + case Side.Mtf: + _respawnPositions[Faction.FoundationStaff] = ev.Player.Position; + break; + case Side.ChaosInsurgency: + case Side.Tutorial: + _respawnPositions[Faction.FoundationEnemy] = ev.Player.Position; + break; + } + item.StopTransmitting(); + item.RemainingCooldown = 5*60; + + + + } + + private void OnRespawnedTeam(RespawnedTeamEventArgs ev) + { + Vector3 spawnPos = _respawnPositions[ev.Wave.TargetFaction]; + if (spawnPos == Vector3.zero) return; + + foreach (Player player in ev.Players) + { + player.Teleport(spawnPos); + _respawnPositions[ev.Wave.TargetFaction] = Vector3.zero; + } + } + + + + } +} diff --git a/KruacentExiled/KE.Items/Items/Scp514.cs b/KruacentExiled/KE.Items/Items/Scp514.cs new file mode 100644 index 00000000..89c61aad --- /dev/null +++ b/KruacentExiled/KE.Items/Items/Scp514.cs @@ -0,0 +1,178 @@ +/*using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using Exiled.API.Features.Spawn; +using Exiled.Events.EventArgs.Interfaces; +using Exiled.Events.EventArgs.Item; +using Exiled.Events.EventArgs.Player; +using Exiled.Events.EventArgs.Scp173; +using Exiled.Events.EventArgs.Scp939; +using Exiled.Events.Handlers; +using KE.Items.API.Features; +using KE.Items.Items.Models; +using MEC; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using UnityEngine; +using Player = Exiled.API.Features.Player; + +/*namespace KE.Items.Items +{ + public class Scp514 : KECustomItem + { + protected override Dictionary> SetTranslation() + { + return new() + { + ["en"] = new() + { + [TranslationKeyName] = "SCP-514", + [TranslationKeyDesc] = "birb", + }, + ["fr"] = new() + { + [TranslationKeyName] = "SCP-514", + [TranslationKeyDesc] = "birb", + }, + }; + } + public override ItemType ItemType => ItemType.Flashlight; + public override string Name { get; set; } = "SCP-514"; + public override float Weight { get; set; } = 0.65f; + public float TimeActive { get; set; } = 5; + public static float Radius { get; set; } = 5; + public override SpawnProperties SpawnProperties { get; set; } = null; + private HashSet _affectedPlayers = new HashSet(); + + + protected override void SubscribeEvents() + { + Exiled.Events.Handlers.Player.Shooting += OnShooting; + Exiled.Events.Handlers.Player.UsingMicroHIDEnergy += OnUsingMicroHIDEnergy; + Exiled.Events.Handlers.Player.Dying += OnDying; + Exiled.Events.Handlers.Scp049.Attacking += OnAttacking049; + Exiled.Events.Handlers.Scp106.Attacking += OnAttacking106; + Exiled.Events.Handlers.Item.ChargingJailbird += OnChargingJailbird; + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Player.Shooting -= OnShooting; + Exiled.Events.Handlers.Player.Dying -= OnDying; + Exiled.Events.Handlers.Player.UsingMicroHIDEnergy -= OnUsingMicroHIDEnergy; + Exiled.Events.Handlers.Scp049.Attacking -= OnAttacking049; + Exiled.Events.Handlers.Scp106.Attacking -= OnAttacking106; + Exiled.Events.Handlers.Item.ChargingJailbird -= OnChargingJailbird; + base.UnsubscribeEvents(); + } + + private void Disallow(IDeniableEvent ev) + { + if(ev is IPlayerEvent p) + { + if (_affectedPlayers.Contains(p.Player)) + { + ev.IsAllowed = false; + } + } + + } + + private void OnUsingMicroHIDEnergy(UsingMicroHIDEnergyEventArgs ev) + { + Disallow(ev); + } + private void OnChargingJailbird(ChargingJailbirdEventArgs ev) + { + if (_affectedPlayers.Contains(ev.Player)) + ev.IsAllowed = false; + } + private void OnAttacking106(Exiled.Events.EventArgs.Scp106.AttackingEventArgs ev) + { + Disallow(ev); + } + + private void OnAttacking049(Exiled.Events.EventArgs.Scp049.AttackingEventArgs ev) + { + Disallow(ev); + } + private void OnDying(DyingEventArgs ev) + { + Disallow(ev); + + } + private void OnShooting(ShootingEventArgs ev) + { + Disallow(ev); + } + protected override void OnDroppingItem(DroppingItemEventArgs ev) + { + if (ev.IsThrown) + { + return; + } + ev.Item.Destroy(); + Vector3 pos = ev.Player.Position; + Scp514Model cage = new(); + + cage.Create(pos,new()); + + Timing.RunCoroutine(DoEffectIfInside(Radius,pos,cage)); + + + + } + + + /*private IEnumerator DoEffectIfInside(float radius,Vector3 spawnPos, Scp514Model model) + { + var time= Stopwatch.StartNew(); + Log.Debug("starting effect"); + while(time.Elapsed.TotalSeconds <= TimeActive) + { + foreach(Player p in Player.List) + { + if (IsPlayerInZone(p, spawnPos, radius)) + { + if (_affectedPlayers.Add(p)) + { + p.IsGodModeEnabled = true; + Log.Debug("adding " + p.Id); + } + + + } + else + { + if (_affectedPlayers.Remove(p)) + { + p.IsGodModeEnabled = false; + Log.Debug("removing" + p.Id); + } + } + } + yield return Timing.WaitForOneFrame; + } + Log.Debug("ending effects"); + time.Stop(); + + foreach(var p in _affectedPlayers) + { + p.IsGodModeEnabled = false; + } + _affectedPlayers.Clear(); + model.Destroy(); + } + + + + private bool IsPlayerInZone(Player player, Vector3 zonePosition, float radius) + { + float distance = Vector3.Distance(new Vector3(player.Position.x, 0, player.Position.z), + new Vector3(zonePosition.x, 0, zonePosition.z)); + return distance <= (radius / 2); + } + } +} +*/ \ No newline at end of file diff --git a/KruacentExiled/KE.Items/Items/Scp7045.cs b/KruacentExiled/KE.Items/Items/Scp7045.cs new file mode 100644 index 00000000..06fe6809 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/Scp7045.cs @@ -0,0 +1,204 @@ +namespace KE.Items.Items +{ + //[CustomItem(ItemType.SCP1576)] + /*[Obsolete("Scrapped - Can't make a good sound quality without external file")] + public class Scp7045 : KECustomItem + { + /* + private static readonly OpusDecoder _decoder = new(); + private static readonly OpusEncoder _encoder = new(OpusApplicationType.Voip); + private static readonly Dictionary _speakers = new(); + public override uint Id { get; set; } = 1800; + public override string Name { get; set; } = "SCP-7045"; + public override string Description { get; set; } = "A weird looking radio"; + public override float Weight { get; set; } = 0.65f; + + private AudioMessage _message; + public override SpawnProperties SpawnProperties { get; set; } = new SpawnProperties() + { + Limit = 1, + LockerSpawnPoints = new List() + { + new LockerSpawnPoint() + { + Type = LockerType.Scp1576Pedestal, + UseChamber = true, + Chance = .5f + } + } + }; + + protected override void SubscribeEvents() + { + Exiled.Events.Handlers.Player.UsedItem += OnUsedItem; + Exiled.Events.Handlers.Player.VoiceChatting += OnVoiceChatting; + base.SubscribeEvents(); + + + } + protected override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Player.UsedItem -= OnUsedItem; + Exiled.Events.Handlers.Player.VoiceChatting -= OnVoiceChatting; + base.UnsubscribeEvents(); + } + + private bool _isItemActive = false; + + private void OnUsedItem(UsedItemEventArgs ev) + { + if (!Check(ev.Item)) return; + + Scp1576 item = (Scp1576)ev.Item; + item.StopTransmitting(); + + if (!_isItemActive) + { + _isItemActive = true; + _recordingBuffer.Clear(); + _isRecording = true; + _lastVoiceTime = Time.time; + Log.Info("Recording"); + + Timing.RunCoroutine(CheckIfRecordingFinished(item, ev.Player)); + } + else + { + _isItemActive = false; + _isRecording = false; + Log.Info("Playing"); + + Log.Info("buffer : " + _recordingBuffer.ToArray().Count()); + if (_recordingBuffer.Count > 0) + { + Speaker speaker; + byte speakerid = (byte)ev.Player.Id; + + + if (!_speakers.TryGetValue(ev.Player, out Speaker _)) + _speakers[ev.Player] = Speaker.Create(speakerid, ev.Player.Position); + + _speakers[ev.Player].Position = ev.Player.Position; + + Timing.RunCoroutine(PlayVoice([.. _recordingBuffer], (byte)ev.Player.Id, ev.Player)); + } + + item.StopTransmitting(); + + } + } + + + + public const int sampleSize = 480; + + private List _recordingBuffer = new(); + private bool _isRecording = false; + private float _lastVoiceTime = 0f; // Timestamp of last voice packet + private void OnVoiceChatting(VoiceChattingEventArgs ev) + { + if (!_isItemActive || !Check(ev.Player.CurrentItem)) return; + + Speaker speaker; + byte speakerid = (byte)ev.Player.Id; + if (!_speakers.TryGetValue(ev.Player, out speaker)) + { + _speakers[ev.Player] = Speaker.Create(speakerid, ev.Player.Position); + } + speaker = _speakers[ev.Player]; + + VoiceMessage message = ev.VoiceMessage; + + // Si on est en mode enregistrement et que l'item est activé, on enregistre la voix + if (_isRecording) + { + _recordingBuffer.AddRange(message.Data); + + // Mettre à jour le timestamp de la dernière voix reçue + _lastVoiceTime = Time.time; + } + } + + private IEnumerator CheckIfRecordingFinished(Scp1576 item, Player p) + { + while (_isRecording) + { + // Wait a short time before checking again + yield return Timing.WaitForSeconds(0.2f); + + // If no voice data has been received for 0.5s, stop recording + if (Time.time - _lastVoiceTime >= 0.5f) + { + _isRecording = false; + Log.Info($"Final recorded voice message length: {_recordingBuffer.Count} samples."); + + float[] finalRecording = _recordingBuffer.ToArray(); + Log.Info($"Final recorded voice message length: {finalRecording.Length} samples."); + + //Timing.RunCoroutine(PlayVoice(finalRecording, speaker.Base.NetworkControllerId)); + } + } + + TeleportItem(item, p); + } + + private IEnumerator PlayVoice(byte[] data, byte speakerid, Player player) + { + Log.Info("message for " +player.Nickname); + foreach (var chunk in SplitData(data, 512)) + { + var message = new AudioMessage(speakerid, chunk, chunk.Length); + player.ReferenceHub.connectionToClient.Send(message); + yield return Timing.WaitForSeconds(0.1f); + } + + Log.Info("end message"); + + } + private IEnumerable SplitData(byte[] data, int chunkSize) + { + for (int i = 0; i < data.Length; i += chunkSize) + { + int size = Math.Min(chunkSize, data.Length - i); + byte[] chunk = new byte[size]; + Array.Copy(data, i, chunk, 0, size); + yield return chunk; + } + //78336 + } + + + private void TeleportItem(Scp1576 item, Player p) + { + Player nextPlayer = Player.List.Where(x => x.IsHuman && x != p).GetRandomValue(); + + Log.Info("NextPlayer : " + nextPlayer); + + Room closeRoom = FindNearbyRoom(nextPlayer, 2); + + Log.Info("Room : " + closeRoom); + + + + Spawn(closeRoom.Position, p); + p.RemoveItem(item, false); + } + + private Room FindNearbyRoom(Player p, int depth) + { + if (p == null) return Room.Random(); + + HashSet rooms = new HashSet(p.CurrentRoom.NearestRooms); + for (int i = 0; i < depth; i++) + { + // Copier les nouvelles rooms avant de les ajouter + HashSet newRooms = new HashSet(rooms.SelectMany(r => r.NearestRooms)); + rooms.UnionWith(newRooms); + } + + // Si rooms est vide, on retourne une salle aléatoire + return rooms.Count > 0 ? rooms.GetRandomValue() : Room.Random(); + } + } + */ +} \ No newline at end of file diff --git a/KruacentExiled/KE.Items/Items/ShieldBelt/ShieldBelt.cs b/KruacentExiled/KE.Items/Items/ShieldBelt/ShieldBelt.cs new file mode 100644 index 00000000..c34d6719 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/ShieldBelt/ShieldBelt.cs @@ -0,0 +1,178 @@ +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using Exiled.API.Features.DamageHandlers; +using Exiled.API.Features.Items; +using Exiled.API.Features.Pickups.Projectiles; +using Exiled.API.Features.Pools; +using Exiled.API.Features.Spawn; +using Exiled.API.Features.Toys; +using Exiled.Events.EventArgs.Map; +using Exiled.Events.EventArgs.Player; +using HintServiceMeow.Core.Models.HintContent; +using HintServiceMeow.Core.Utilities; +using KE.Items.API.Features; +using KE.Items.API.Interface; +using KE.Utils.API.Displays.DisplayMeow; +using KE.Utils.API.Displays.DisplayMeow.Placements; +using MapGeneration; +using MEC; +using PlayerRoles.FirstPersonControl; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; + +namespace KE.Items.Items.ShieldBelt +{ + [CustomItem(ItemType.KeycardCustomSite02)] + public class ShieldBelt : KECustomKeycard + { + + public override uint Id { get; set; } = 5982; + public override string Name { get; set; } = "Shield belt"; + public override string Description { get; set; } = "A projectile-repulsion device.\nIt will attempt to stop incoming projectiles or shrapnel, but does nothing against melee attacks or heat.\nIt prevents the wearer from firing out.\n(works in the inventory) "; + public override float Weight { get; set; } = 0.65f; + public override SpawnProperties SpawnProperties { get; set; } = null; + + protected override void SubscribeEvents() + { + Exiled.Events.Handlers.Player.DroppedItem += OnDroppedItem; + Exiled.Events.Handlers.Player.Hurting += OnHurting; + Exiled.Events.Handlers.Player.Dying += OnDying; + Exiled.Events.Handlers.Player.Shooting += OnShooting; + InventorySystem.Items.ThrowableProjectiles.Scp2176Projectile.OnServerShattered += OnServerShattered; + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Player.DroppedItem -= OnDroppedItem; + Exiled.Events.Handlers.Player.Hurting -= OnHurting; + Exiled.Events.Handlers.Player.Dying -= OnDying; + Exiled.Events.Handlers.Player.Shooting -= OnShooting; + InventorySystem.Items.ThrowableProjectiles.Scp2176Projectile.OnServerShattered -= OnServerShattered; + base.UnsubscribeEvents(); + } + + public override bool Check(Player player) + { + + if (player is null) return false; + + return player.Items.Any(Check); + } + private static HintPosition HintPosition = new ShieldBeltPosition(); + protected override void OnAcquired(Player player, Item item, bool displayMessage) + { + if (!Check(item)) return; + var comp = player.GameObject.AddComponent(); + Log.Debug("player got shield"); + + + + Log.Debug("no hints"); + if(!DisplayHandler.Instance.HasHint(player, HintPosition.HintPlacement)) + { + DisplayHandler.Instance.CreateAuto(player, (arg) => GetContent(player), HintPosition.HintPlacement); + } + base.OnAcquired(player, item, displayMessage); + } + + + private string GetContent(Player player) + { + if (player.GameObject.TryGetComponent(out var stat)) + { + StringBuilder sb = StringBuilderPool.Pool.Get(); + + if (stat.IsActive) + { + sb.Append(""); + } + else + { + sb.Append(""); + } + + + sb.Append("ShieldBelt Status : "); + sb.Append(stat.CurrentCharge); + sb.Append("/"); + sb.Append(ShieldBeltStat.MaxCharge); + sb.Append("HP"); + sb.Append(""); + return StringBuilderPool.Pool.ToStringReturn(sb); + } + + + return " "; + } + + private void OnDroppedItem(DroppedItemEventArgs ev) + { + if (!Check(ev.Pickup)) return; + + if(ev.Player.GameObject.TryGetComponent(out var comp)) + { + comp.Destroy(); + } + Log.Info("player lost shilde"); + + } + private void OnShooting(ShootingEventArgs ev) + { + if (!Check(ev.Player)) return; + + + ev.IsAllowed = false; + } + private void OnServerShattered(InventorySystem.Items.ThrowableProjectiles.Scp2176Projectile projectile,RoomIdentifier roomid) + { + Room room = Room.Get(roomid); + + foreach(Player player in room.Players.Where(Check)) + { + if (player.GameObject.TryGetComponent(out var comp)) + { + comp.Break(); + } + } + + + } + + private void OnDying(DyingEventArgs ev) + { + if (ev.ItemsToDrop.Any(Check)) + { + if (ev.Player.GameObject.TryGetComponent(out var comp)) + { + comp.Destroy(); + } + } + } + + private void OnHurting(HurtingEventArgs ev) + { + if (!Check(ev.Player)) + return; + if (ev.IsInstantKill) + return; + if (!(ev.DamageHandler.CustomBase is FirearmDamageHandler)) + return; + if (!ev.Player.GameObject.TryGetComponent(out var stat)) + { + return; + } + if (!stat.IsActive) return; + ev.Amount = stat.Damage(ev.Amount); + + + + } + + + + + } +} diff --git a/KruacentExiled/KE.Items/Items/ShieldBelt/ShieldBeltPosition.cs b/KruacentExiled/KE.Items/Items/ShieldBelt/ShieldBeltPosition.cs new file mode 100644 index 00000000..fdf67450 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/ShieldBelt/ShieldBeltPosition.cs @@ -0,0 +1,21 @@ +using HintServiceMeow.Core.Enum; +using KE.Utils.API.Displays.DisplayMeow.Placements; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Items.Items.ShieldBelt +{ + public class ShieldBeltPosition : HintPosition + { + public override float Xposition => 0; + + public override float Yposition => 1050; + + public override HintAlignment HintAlignment => HintAlignment.Center; + + public override string Name => "ShieldBelt"; + } +} diff --git a/KruacentExiled/KE.Items/Items/ShieldBelt/ShieldBeltStat.cs b/KruacentExiled/KE.Items/Items/ShieldBelt/ShieldBeltStat.cs new file mode 100644 index 00000000..8ec6c4f2 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/ShieldBelt/ShieldBeltStat.cs @@ -0,0 +1,177 @@ +using Exiled.API.Features; +using Exiled.API.Features.Toys; +using KE.Utils.API.Features.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Items.Items.ShieldBelt +{ + public class ShieldBeltStat : MonoBehaviour + { + public const float MaxCharge = 220; + public const float RechargeRatePerS = 13; + public const float TimeBroken = 50; + public const float Base = 20; + public static readonly float MaxSize = 2; + public static readonly float MinSize = 1.5f; + + public float CurrentCharge => currentCharge; + + private float currentCharge; + private float timeRemaining; + private bool recharging = false; + + private Player player; + private Primitive primitive; + public void RechargeTick() + { + + if (timeRemaining <= 0 && recharging) + { + Log.Debug("recharged"); + currentCharge = 20; + recharging = false; + } + + if (currentCharge <= 0) + { + Break(); + } + + if (!(primitive is null)) + { + float percent = Mathf.Clamp01(currentCharge / MaxCharge); + primitive.Scale = Mathf.Lerp(MinSize, MaxSize, percent)*Vector3.one; + + } + + + if (!recharging) + { + if (!primitive.Visible) + { + primitive.Visible = true; + } + + if (currentCharge != MaxCharge) + { + + float tempcharge = currentCharge + RechargeRatePerS * Time.deltaTime; + currentCharge = Mathf.Clamp(tempcharge, 0, MaxCharge); + } + + } + else + { + timeRemaining -= Time.deltaTime; + if (timeRemaining < 0) + { + timeRemaining = 0; + } + if (primitive.Visible) + { + primitive.Visible = false; + } + } + + + } + + + public float Damage(float damage) + { + + + currentCharge = Mathf.Clamp(currentCharge - damage, 0, MaxCharge); + Log.Debug("cur=" + currentCharge); + Log.Debug("time=" + timeRemaining); + + if (IsActive) + { + return 0; + } + else + { + return damage; + } + + } + + + public void Break() + { + + if (!recharging) + { + Log.Debug("breakign"); + timeRemaining = TimeBroken; + currentCharge = 0; + recharging = true; + player.PlayShieldBreakSound(); + } + + } + + + public bool IsActive + { + get + { + return currentCharge > 0; + } + } + public bool IsRecharging + { + get + { + return recharging; + } + } + private Primitive CreatePrimitive(Player player) + { + Primitive prim = Primitive.Create(null, null, null, false); + prim.Collidable = false; + prim.Visible = true; + prim.Transform.parent = player.ReferenceHub.transform; + prim.Transform.localPosition = Vector3.zero; + prim.Scale = MaxSize*Vector3.one; + prim.Color = new Color32(50, 50, 50, 50); + prim.MovementSmoothing = 0; + prim.Spawn(); + + + + + return prim; + } + public void Awake() + { + player = Player.Get(transform.root.gameObject); + primitive = CreatePrimitive(player); + currentCharge = Base; + timeRemaining = 0; + } + + + public void Destroy() + { + Log.Debug($"destroying {this}"); + Destroy(this); + } + + private void OnDestroy() + { + primitive.Destroy(); + primitive = null; + } + + public void Update() + { + RechargeTick(); + } + } +} diff --git a/KruacentExiled/KE.Items/Items/SmokeGrenade.cs b/KruacentExiled/KE.Items/Items/SmokeGrenade.cs new file mode 100644 index 00000000..b5ea4d00 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/SmokeGrenade.cs @@ -0,0 +1,77 @@ +using System.Collections.Generic; +using Exiled.API.Enums; +using Exiled.API.Features.Spawn; +using Exiled.Events.EventArgs.Map; +using KE.Items.API.Features; +using KE.Items.API.Interface; +using KE.Items.Items.ItemEffects; + +namespace KE.Items.Items +{ + public class SmokeGrenade : KECustomGrenade, ISwitchableEffect,IViolentItem + { + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Smoke Grenade", + [TranslationKeyDesc] = "We finally put your grandma inside this thing ! Don't throw it or she will get out !", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Fumigène", + [TranslationKeyDesc] = "Fait beaucoup de fumée", + }, + }; + } + public bool IsViolent => false; + public override ItemType ItemType => ItemType.GrenadeFlash; + public override string Name { get; set; } = "Smoke Grenade"; + public override float Weight { get; set; } = 0.65f; + public override float FuseTime => 3f; + public override bool ExplodeOnCollision => false; + public UnityEngine.Color Color { get; set; } = UnityEngine.Color.black; + public CustomItemEffect Effect { get; set; } + public override SpawnProperties SpawnProperties { get; set; } = new SpawnProperties() + { + Limit = 2, + RoomSpawnPoints = new List + { + new RoomSpawnPoint() + { + Room = RoomType.HczStraightPipeRoom, + Chance = 35 + }, + + new RoomSpawnPoint() + { + Room = RoomType.Surface, + Chance = 35 + } + }, + }; + + public SmokeGrenade() + { + Effect = new SmokeGrenadeEffect(); + } + + protected override void OnExplodingGrenade(ExplodingGrenadeEventArgs ev) + { + Effect.Effect(ev); + ev.TargetsToAffect.Clear(); + } + + protected override void SubscribeEvents() + { + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + base.UnsubscribeEvents(); + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Items/Items/TPGrenada.cs b/KruacentExiled/KE.Items/Items/TPGrenada.cs new file mode 100644 index 00000000..43de77ef --- /dev/null +++ b/KruacentExiled/KE.Items/Items/TPGrenada.cs @@ -0,0 +1,99 @@ +using System.Collections.Generic; +using Exiled.API.Enums; +using Exiled.API.Features.Attributes; +using Exiled.API.Features.Spawn; +using Exiled.CustomItems.API.Features; +using Exiled.Events.EventArgs.Map; +using KE.Items.API.Core.Models; +using KE.Items.API.Features; +using KE.Items.API.Interface; +using KE.Items.Items.ItemEffects; +using KE.Items.Items.PickupModels; + +namespace KE.Items.Items +{ + public class TPGrenada : KECustomGrenade, ISwitchableEffect, ICustomPickupModel,IViolentItem + { + + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "Teleportation Grenade", + [TranslationKeyDesc] = "This grenade does 0 damage but teleport nearby players in a random place (does work in other dimension ;3 )", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "Grenade de Téléportation", + [TranslationKeyDesc] = "Ne fait pas de dégât mais téléporte dans une pièce aléatoire", + }, + }; + } + public bool IsViolent => false; + public override ItemType ItemType => ItemType.GrenadeHE; + public override string Name { get; set; } = "Teleportation Grenade"; + public override float Weight { get; set; } = 0.65f; + public override float FuseTime => 3f; + public override bool ExplodeOnCollision => false; + public UnityEngine.Color Color { get; set; } = UnityEngine.Color.cyan; + public CustomItemEffect Effect { get; set; } + public override SpawnProperties SpawnProperties { get; set; } = new SpawnProperties() + { + Limit = 3, + DynamicSpawnPoints = new List + { + new DynamicSpawnPoint() + { + Chance = 50, + Location = SpawnLocationType.InsideHczArmory, + }, + new DynamicSpawnPoint() + { + Chance =2, + Location = SpawnLocationType.Inside914, + }, + new DynamicSpawnPoint() + { + Chance=50, + Location = SpawnLocationType.Inside049Armory, + }, + new DynamicSpawnPoint() + { + Chance=50, + Location = SpawnLocationType.InsideLczArmory, + } + }, + + }; + + public PickupModel PickupModel { get; } + + public TPGrenada() + { + Effect = new TPGrenadaEffect(); + PickupModel = new TPGrenadaPModel(this); + } + + protected override void OnExplodingGrenade(ExplodingGrenadeEventArgs ev) + { + Effect.Effect(ev); + ev.TargetsToAffect.Clear(); + } + + protected override void SubscribeEvents() + { + PickupModel.SubscribeEvents(); + base.SubscribeEvents(); + } + + protected override void UnsubscribeEvents() + { + PickupModel.UnsubscribeEvents(); + base.UnsubscribeEvents(); + } + + } + +} diff --git a/KruacentExiled/KE.Items/Items/TrueDivinePills.cs b/KruacentExiled/KE.Items/Items/TrueDivinePills.cs new file mode 100644 index 00000000..4d0dfd34 --- /dev/null +++ b/KruacentExiled/KE.Items/Items/TrueDivinePills.cs @@ -0,0 +1,125 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Attributes; +using Exiled.API.Features.Spawn; +using Exiled.CustomItems.API.Features; +using Exiled.Events.EventArgs.Player; +using KE.Items.API.Features; +using KE.Items.API.Interface; +using PlayerRoles; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using PlayerHandle = Exiled.Events.Handlers.Player; + +/// +public class TrueDivinePills : KECustomItem, ILumosItem +{ + protected override Dictionary> SetTranslation() + { + return new Dictionary>() + { + ["en"] = new Dictionary() + { + [TranslationKeyName] = "True Divine Pills", + [TranslationKeyDesc] = "Guaranteed to respawn everybody, drop to change the mode", + }, + ["fr"] = new Dictionary() + { + [TranslationKeyName] = "True Divine Pills", + [TranslationKeyDesc] = "Fait réappaître tout le monde, lâcher pour changer le mode", + }, + }; + } + + public override ItemType ItemType => ItemType.SCP500; + + /// + public override string Name { get; set; } = "TrueDivinePills"; + + /// + + /// + public override float Weight { get; set; } = 0.65f; + public Color Color { get; set; } = Color.yellow; + private bool tp = false; + + /// + public override SpawnProperties SpawnProperties { get; set; } = new SpawnProperties() + { + + }; + + /// + protected override void SubscribeEvents() + { + PlayerHandle.UsingItem += OnUsingItem; + base.SubscribeEvents(); + } + + /// + protected override void UnsubscribeEvents() + { + PlayerHandle.UsingItem -= OnUsingItem; + base.UnsubscribeEvents(); + } + + protected override void OnDroppingItem(DroppingItemEventArgs ev) + { + if (!Check(ev.Item)) + return; + + if (ev.IsThrown) + { + ev.IsAllowed = true; + return; + } + Player player = ev.Player; + + tp = !tp; + if (tp) + KECustomItem.ItemEffectHint(player, "Players will spawn to you"); + else + KECustomItem.ItemEffectHint(player, "Players won't spawn to you"); + ev.IsAllowed = false; + + + + } + + private void OnUsingItem(UsingItemEventArgs ev) + { + if (!Check(ev.Item)) + return; + Player player = ev.Player; + Log.Debug(Player.List.Count); + Log.Debug(Player.List.Where(x => x.Role == RoleTypeId.Spectator).Count()); + + if (Player.List.Where(x => x.Role == RoleTypeId.Spectator).Count() == 0) + { + KECustomItem.ItemEffectHint(player, "No one to respawn"); + ev.IsAllowed = false; + return; + } + + + Player.List.Where(x => x.Role == RoleTypeId.Spectator).ToList().ForEach(x => + { + switch (player.Role.Side) + { + case Side.ChaosInsurgency: + x.Role.Set(RoleTypeId.ChaosRifleman); + break; + case Side.Mtf: + x.Role.Set(RoleTypeId.NtfPrivate); + break; + } + if (tp) + { + x.Teleport(player); + } + }); + + } + +} \ No newline at end of file diff --git a/KruacentExiled/KE.Items/MainPlugin.cs b/KruacentExiled/KE.Items/MainPlugin.cs new file mode 100644 index 00000000..36349086 --- /dev/null +++ b/KruacentExiled/KE.Items/MainPlugin.cs @@ -0,0 +1,106 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Interfaces; +using HarmonyLib; +using KE.Items.API.Core.Lights; +using KE.Items.API.Core.Settings; +using KE.Items.API.Core.Upgrade; +using KE.Items.API.Features; +using KE.Items.API.Features.SpawnPoints; +using KE.Utils.API.Displays.DisplayMeow; +using KruacentExiled; +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace KE.Items +{ + public class MainPlugin : KEPlugin + { + public override string Name => "KE.Items"; + public override string Prefix => "KE.I"; + internal UpgradeHandler UpgradeHandler { get; private set; } + internal LightsHandler LightsHandler { get; private set; } + internal static MainPlugin Instance { get; private set; } + internal SettingsHandler SettingsHandler { get; private set; } + + public override IConfig Config => config; + private IConfig config; + + internal static readonly HintPlacement ItemEffectPlacement = new HintPlacement(0, 200, HintServiceMeow.Core.Enum.HintAlignment.Center); + internal static readonly HintPlacement HintPlacement = new HintPlacement(0, 400, HintServiceMeow.Core.Enum.HintAlignment.Center); + + internal Harmony harmony; + + public override void OnEnabled() + { + Instance = this; + config = KruacentExiled.MainPlugin.Instance.Config.CustomItemConfig; + harmony = new Harmony(Name); + harmony.PatchAll(KruacentExiled.MainPlugin.Instance.Assembly); + UpgradeHandler = new UpgradeHandler(); + LightsHandler = new LightsHandler(); + SettingsHandler = new SettingsHandler(); + + + KE.Utils.API.Sounds.SoundPlayer.Load(); + PoseRoomSpawnPointHandler.AddRoomPoses(new HashSet() + { + new PoseRoomSpawnPointHandler.ItemSpawn(RoomType.Lcz914,new Vector3(0,0.70f,-7.14f),Quaternion.identity), + new PoseRoomSpawnPointHandler.ItemSpawn(RoomType.LczGlassBox,new Vector3(7.39f,0.75f,-5.89f),Quaternion.identity), + new PoseRoomSpawnPointHandler.ItemSpawn(RoomType.LczGlassBox,new Vector3(8.71f,1.2f,-5.89f),Quaternion.identity), + new PoseRoomSpawnPointHandler.ItemSpawn(RoomType.Lcz173,new Vector3(7.39f,0.75f,-5.89f),Quaternion.identity), + new PoseRoomSpawnPointHandler.ItemSpawn(RoomType.EzUpstairsPcs,new Vector3(-2.09f,0.75f,-7f),Quaternion.identity), + new PoseRoomSpawnPointHandler.ItemSpawn(RoomType.EzUpstairsPcs,new Vector3(-4,1.1f,-0.36f),Quaternion.identity), + new PoseRoomSpawnPointHandler.ItemSpawn(RoomType.EzGateB,new Vector3(-0.68f,1.33f,4f),Quaternion.identity), + new PoseRoomSpawnPointHandler.ItemSpawn(RoomType.EzChef,new Vector3(2.36f,.2f,-0.16f),Quaternion.identity), + new PoseRoomSpawnPointHandler.ItemSpawn(RoomType.HczStraightPipeRoom,new Vector3(6.1f,1.05f,-4.8f),Quaternion.identity), + new PoseRoomSpawnPointHandler.ItemSpawn(RoomType.HczStraightPipeRoom,new Vector3(-4f,0.23f,-4.52f),Quaternion.identity), + new PoseRoomSpawnPointHandler.ItemSpawn(RoomType.HczServerRoom,new Vector3(1.79f,0.78f,-0.6f),Quaternion.identity), + new PoseRoomSpawnPointHandler.ItemSpawn(RoomType.HczNuke,new Vector3(12.11f,-75.11f,2.6f),Quaternion.identity), + new PoseRoomSpawnPointHandler.ItemSpawn(RoomType.HczNuke,new Vector3(11.06f,-74.85f,-2.5f),Quaternion.identity), + new PoseRoomSpawnPointHandler.ItemSpawn(RoomType.Surface,new Vector3(27.45f,-8.07f,24.96f),Quaternion.identity), + new PoseRoomSpawnPointHandler.ItemSpawn(RoomType.Surface,new Vector3(27.45f,-8.07f,-23.62f),Quaternion.identity), + new PoseRoomSpawnPointHandler.ItemSpawn(RoomType.Surface,new Vector3(27.45f,-8.07f,-23.62f),Quaternion.identity), + new PoseRoomSpawnPointHandler.ItemSpawn(RoomType.HczHid,new Vector3(-3.41f,5.68f,-2.3f),Quaternion.identity), + new PoseRoomSpawnPointHandler.ItemSpawn(RoomType.HczHid,new Vector3(-6.44f,5.7f,-2.5f),Quaternion.identity), + new PoseRoomSpawnPointHandler.ItemSpawn(RoomType.Hcz127,new Vector3(4.77f, 1.12f, 1.83f),Quaternion.identity), + new PoseRoomSpawnPointHandler.ItemSpawn(RoomType.Hcz939,new Vector3(.6f, 1.3f, -2.8f),Quaternion.identity), + new PoseRoomSpawnPointHandler.ItemSpawn(RoomType.LczCafe,new Vector3(-2.27f, 0.92f, 3.28f),Quaternion.identity), + new PoseRoomSpawnPointHandler.ItemSpawn(RoomType.LczCafe,new Vector3(-2.38f, 0.87f, -1.49f),Quaternion.identity), + //new(RoomType.HczArmory,new Vector3(1.66f, 1.06f, -2.25f),Quaternion.identity), + //new(RoomType.HczArmory,new Vector3(1.33f, 1.06f, -1.66f),Quaternion.identity), + new PoseRoomSpawnPointHandler.ItemSpawn(RoomType.Hcz049,new Vector3(-6.31f, 89.22f, -11.38f),Quaternion.identity), + new PoseRoomSpawnPointHandler.ItemSpawn(RoomType.Hcz049,new Vector3(0.64f, 89.31f, 7.22f),Quaternion.identity), + new PoseRoomSpawnPointHandler.ItemSpawn(RoomType.Hcz049,new Vector3(18.46f, 93.73f, 13.13f),Quaternion.identity), + }); + + + + KECustomItem.RegisterItems(); + UpgradeHandler.SubscribeEvents(); + LightsHandler.SubscribeEvents(); + Exiled.Events.Handlers.Map.Generated += OnGenerated; + } + + public override void OnDisabled() + { + KECustomItem.UnregisterItems(); + UpgradeHandler?.UnsubscribeEvents(); + LightsHandler?.UnsubscribeEvents(); + Exiled.Events.Handlers.Map.Generated -= OnGenerated; + + harmony.UnpatchAll(harmony.Id); + SettingsHandler = null; + LightsHandler = null; + UpgradeHandler = null; + Instance = null; + } + + private void OnGenerated() + { + PoseRoomSpawnPointHandler.Reset(); + } + + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Items/Patches/ExplosionGrenadePatches.cs b/KruacentExiled/KE.Items/Patches/ExplosionGrenadePatches.cs new file mode 100644 index 00000000..de7c7507 --- /dev/null +++ b/KruacentExiled/KE.Items/Patches/ExplosionGrenadePatches.cs @@ -0,0 +1,66 @@ +using Exiled.API.Features; +using Exiled.API.Features.Pools; +using Exiled.Events.EventArgs.Map; +using Footprinting; +using HarmonyLib; +using InventorySystem.Items.ThrowableProjectiles; +using KE.Items.API.Events; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using static HarmonyLib.AccessTools; + +namespace KE.Items.Patches +{ + public static class ExplosionGrenadePatches + { + + [HarmonyPatch(typeof(ExplosionGrenade),nameof(ExplosionGrenade.ExplodeDestructible))] + public static class ExplodeDestructiblePatch + { + public static IEnumerable Transpiler(IEnumerable instructions) + { + var codes = new List(instructions); + + var argsCtor = AccessTools.Constructor(typeof(OnExplodeDestructibleEventsArgs), + new[] { typeof(IDestructible), typeof(float), typeof(ExplosionGrenade) }); + var eventInvoker = AccessTools.Method(typeof(ExplodeEvent), nameof(ExplodeEvent.OnExplodeDestructible)); + var damageGetter = AccessTools.PropertyGetter(typeof(OnExplodeDestructibleEventsArgs), nameof(OnExplodeDestructibleEventsArgs.Damage)); + + for (int i = 0; i < codes.Count - 2; i++) + { + // Look for Vector3::op_Addition followed by any stloc.* instruction + if (codes[i].Calls(AccessTools.Method(typeof(Vector3), "op_Addition")) && + codes[i + 1].opcode.Name.StartsWith("stloc")) + { + var injectionIndex = i + 2; + + codes.InsertRange(injectionIndex, new[] + { + // Create and fire event + new CodeInstruction(OpCodes.Ldarg_0), // dest + new CodeInstruction(OpCodes.Ldloc_2), // num + new CodeInstruction(OpCodes.Ldarg_3), // setts + new CodeInstruction(OpCodes.Newobj, argsCtor), + new CodeInstruction(OpCodes.Dup), + new CodeInstruction(OpCodes.Call, eventInvoker), + new CodeInstruction(OpCodes.Callvirt, damageGetter), + new CodeInstruction(OpCodes.Stloc_2), + }); + + UnityEngine.Debug.Log("[ExplosionPatch] Injected OnPreExplodeDestructible successfully"); + break; + } + } + + return codes; + } + } + } +} diff --git a/KruacentExiled/KE.Items/SpawnModel.cs b/KruacentExiled/KE.Items/SpawnModel.cs new file mode 100644 index 00000000..f7467390 --- /dev/null +++ b/KruacentExiled/KE.Items/SpawnModel.cs @@ -0,0 +1,53 @@ +using CommandSystem; +using Exiled.API.Features; +using Exiled.API.Features.Toys; +using KE.Items.Items.PickupModels; +using KE.Utils.API.Features.Models; +using KE.Utils.Extensions; +using MEC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Items +{ + + [CommandHandler(typeof(RemoteAdminCommandHandler))] + public class SpawnModel : ICommand + { + public string Command => "spawnmodel"; + + public string[] Aliases => new string[0]; + + public string Description => "spawnmodel"; + + public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) + { + var p = Player.Get(sender); + response = string.Empty; + + if(p != null) + { + Primitive prim = Primitive.Create(p.Position, null, Vector3.one, false); + prim.Collidable = false; + prim.Visible = false; + prim.Spawn(); + TPGrenadaPModel m = new TPGrenadaPModel(null); + + + Log.Info("position model=" + prim.Position); + + m.Create(prim.Transform); + + Timing.CallDelayed(float.Parse(arguments.At(0)),prim.Destroy); + + return true; + } + response = "no"; + return false; + } + } +} diff --git a/KruacentExiled/KE.Map/Commands/TestGetValidPosition.cs b/KruacentExiled/KE.Map/Commands/TestGetValidPosition.cs new file mode 100644 index 00000000..be77590d --- /dev/null +++ b/KruacentExiled/KE.Map/Commands/TestGetValidPosition.cs @@ -0,0 +1,73 @@ +using CommandSystem; +using Exiled.API.Enums; +using Exiled.API.Features; +using KE.Utils.API.Commands; +using KE.Utils.Extensions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map.Commands +{ + + [CommandHandler(typeof(RemoteAdminCommandHandler))] + [CommandHandler(typeof(ClientCommandHandler))] + public class TestGetValidPosition : KECommand + { + public override string Command => "test"; + + public override string[] Aliases => new string[0]; + + public override string Description => ""; + + public override string[] Usage => new string[0]; + + public override bool ExecuteCommand(ArraySegment arguments, ICommandSender sender, out string response) + { + + if (!MainPlugin.Instance.Config.Debug) + { + response = "you can't do that"; + return false; + } + + Player player = Player.Get(sender); + + if(player == null) + { + response = "invalid player"; + return false; + } + + if(arguments.Count == 0) + { + response = "give a roomtype"; + return false; + } + + + if(!Enum.TryParse(arguments.At(0),out RoomType roomtype)) + { + response = "room type not found"; + return false; + } + Room room = Room.Get(roomtype); + + + if(room == null) + { + response = "room not found"; + return false; + } + + + + player.Teleport(room.GetValidPosition()); + response = "ok"; + + return false; + } + } +} diff --git a/KruacentExiled/KE.Map/Entrance/EzArmory.cs b/KruacentExiled/KE.Map/Entrance/EzArmory.cs new file mode 100644 index 00000000..26a3b388 --- /dev/null +++ b/KruacentExiled/KE.Map/Entrance/EzArmory.cs @@ -0,0 +1,32 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Interactables.Interobjects.DoorUtils; +using KE.Utils.API.Map; +using LabApi.Events.Arguments.ServerEvents; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Map.Entrance +{ + public class EzArmory : MoreRoom + { + public override RoomType RoomType => RoomType.EzVent; + + public override int Limit => 1; + + + public override void Create(Vector3 position, Quaternion rotation) + { + StructureSpawner.SpawnDoor(ProjectMER.Features.Enums.DoorType.Ez, position,rotation,new Vector3(2f,1,1f)); + } + + public override void Destroy() + { + + } + } +} diff --git a/KruacentExiled/KE.Map/Entrance/Locked.cs b/KruacentExiled/KE.Map/Entrance/Locked.cs new file mode 100644 index 00000000..38afc41d --- /dev/null +++ b/KruacentExiled/KE.Map/Entrance/Locked.cs @@ -0,0 +1,44 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Interactables.Interobjects.DoorUtils; +using KE.Utils.API.Map; +using LabApi.Events.Arguments.ServerEvents; +using LabApi.Features.Wrappers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Map.Entrance +{ + public class Locked : MoreRoom + { + public override RoomType RoomType => RoomType.EzVent; + + public override int Limit => 1; + + private PrimitiveObjectToy primitive; + + public override void Create(Vector3 position, Quaternion rotation) + { + Log.Debug("create"); + DoorVariant door1 = StructureSpawner.SpawnDoor(ProjectMER.Features.Enums.DoorType.Gate, position, rotation, new Vector3(.6f, 1.1f, .6f)); + Door door = Door.Get(door1); + + door.Lock(DoorLockReason.AdminCommand, true); + primitive = PrimitiveObjectToy.Create(door.Transform,false); + primitive.Transform.localPosition = Vector3.up * 2; + primitive.Type = PrimitiveType.Cube; + primitive.Flags = AdminToys.PrimitiveFlags.Collidable; + primitive.Scale = new Vector3(6,5,1); + primitive.Spawn(); + } + + public override void Destroy() + { + primitive.Destroy(); + } + } +} diff --git a/KruacentExiled/KE.Map/Entrance/MoreRoom.cs b/KruacentExiled/KE.Map/Entrance/MoreRoom.cs new file mode 100644 index 00000000..f1f16b80 --- /dev/null +++ b/KruacentExiled/KE.Map/Entrance/MoreRoom.cs @@ -0,0 +1,79 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using LabApi.Events.Arguments.ServerEvents; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Map.Entrance +{ + public abstract class MoreRoom + { + private static HashSet all = new HashSet(); + public MoreRoom() + { + all.Add(this); + } + public abstract RoomType RoomType { get; } + + public abstract void Create(Vector3 position, Quaternion rotation); + public abstract void Destroy(); + + public abstract int Limit { get; } + private int curr = 0; + private static HashSet usedRooms = new HashSet(); + + public static void CreateAll() + { + //new EzArmory(); + new Locked(); + } + public static void SubscribeEvents() + { + Exiled.Events.Handlers.Map.Generated += OnMapGenerated; + //LabApi.Events.Handlers.ServerEvents.MapGenerated += OnMapGenerated; + } + + public static void UnsubscribeEvents() + { + Exiled.Events.Handlers.Map.Generated -= OnMapGenerated; + //LabApi.Events.Handlers.ServerEvents.MapGenerated -= OnMapGenerated; + DestroyAll(); + } + public static void DestroyAll() + { + foreach(MoreRoom room in all) + { + room.Destroy(); + } + } + //MapGeneratedEventArgs _ + public static void OnMapGenerated() + { + foreach (MoreRoom room in all) + { + foreach (Room r in Room.List.Where(r => r.Type == room.RoomType && !usedRooms.Contains(r))) + { + if(room.curr < room.Limit) + { + Log.Debug("spawn"); + room.Create(r.Position, r.Rotation); + room.curr++; + usedRooms.Add(r); + } + } + } + } + + + + + + + + + } +} diff --git a/KruacentExiled/KE.Map/Heavy/BulkDoor049.cs b/KruacentExiled/KE.Map/Heavy/BulkDoor049.cs new file mode 100644 index 00000000..e093386b --- /dev/null +++ b/KruacentExiled/KE.Map/Heavy/BulkDoor049.cs @@ -0,0 +1,84 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Interactables.Interobjects.DoorUtils; +using KE.Utils.API.Map; +using MEC; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace KE.Map.Heavy +{ + public static class BulkDoor049 + { + private static DoorVariant door; + public static void Create() + { + + + Room room = Room.List.Where(r => r.Type == RoomType.Hcz049).First(); + + + Vector3 pos = new Vector3(-19.7f ,89f, 9f); + Quaternion rot = Quaternion.identity; + + Vector3 worldpos = room.Position + room.Rotation*pos; + + + + door = StructureSpawner.SpawnDoor(ProjectMER.Features.Enums.DoorType.HeavyBulkDoor, worldpos, room.Rotation * rot , Vector3.one,ZoneType.HeavyContainment); + + + door.OnStateChanged += Door_OnStateChanged; + + DoorVariant.OnInstanceRemoved += DoorVariant_OnInstanceRemoved; + + + + Log.Debug("spawn 049 bulk door at "+ worldpos); + } + + private static void DoorVariant_OnInstanceRemoved(DoorVariant obj) + { + door.OnStateChanged -= Door_OnStateChanged; + DoorVariant.OnInstanceRemoved -= DoorVariant_OnInstanceRemoved; + } + + public const float IdleDuration = 20f; + private static float duration = IdleDuration; + public const float RefreshRate = 1f; + private static void Door_OnStateChanged() + { + if (door.NetworkTargetState) + { + Timing.RunCoroutine(AutoClose()); + } + + } + + private static IEnumerator AutoClose() + { + duration = IdleDuration; + + while (duration > 0) + { + if (!door.NetworkTargetState) + { + yield break; + } + duration -= RefreshRate; + Log.Info(duration); + if (duration <= 0) + { + Log.Info("auto close"); + door.NetworkTargetState = false; + duration = IdleDuration; + } + yield return Timing.WaitForSeconds(RefreshRate); + + } + } + + } +} diff --git a/KruacentExiled/KE.Map/Heavy/GamblingZone/AbstractGambling.cs b/KruacentExiled/KE.Map/Heavy/GamblingZone/AbstractGambling.cs new file mode 100644 index 00000000..845d4b43 --- /dev/null +++ b/KruacentExiled/KE.Map/Heavy/GamblingZone/AbstractGambling.cs @@ -0,0 +1,14 @@ +using UnityEngine; + +namespace KE.Map.Heavy.GamblingZone +{ + public abstract class AbstractGambling + { + + + + public abstract void Destroy(); + + + } +} diff --git a/KruacentExiled/KE.Map/Heavy/GamblingZone/DroppableItem.cs b/KruacentExiled/KE.Map/Heavy/GamblingZone/DroppableItem.cs new file mode 100644 index 00000000..cf20b1d5 --- /dev/null +++ b/KruacentExiled/KE.Map/Heavy/GamblingZone/DroppableItem.cs @@ -0,0 +1,69 @@ +using System; +using UnityEngine; +using ExiledItem = Exiled.API.Features.Items.Item; +using Exiled.API.Features.Items; + +namespace KE.Map.Heavy.GamblingZone +{ + public class DroppableItem : IEquatable + { + private readonly ItemType _item; + internal ItemType Item { get { return _item; } } + private int _chance; + internal int Chance + { + get { return _chance; } + set + { + _chance = Mathf.Clamp(value, 0, 100); + } + } + + private int _itemCap = -1; + internal int ItemCap + { + get { return _itemCap; } + set { _itemCap = value; } + } + + private int _currentCap = 0; + internal int CurrentCap + { + get { return _currentCap; } + set { _currentCap = value; } + } + + + + + internal DroppableItem(ItemType item, int chance, int itemCap = -1) + { + + _item = item; + Chance = chance; + ItemCap = itemCap; + } + public static implicit operator DroppableItem(ItemType d) => new DroppableItem(d, 1, -1); + public bool Equals(DroppableItem other) + { + return other.Item == Item && other.Chance == Chance && other.ItemCap == ItemCap && CurrentCap == other.CurrentCap; + } + + public ExiledItem GetItem() + { + if (HasReachCap()) throw new Exception("Cap reached"); + CurrentCap++; + return ExiledItem.Create(Item); + } + + public bool HasReachCap() + { + return CurrentCap >= ItemCap && ItemCap != -1; + } + + public override string ToString() + { + return $"{Item.ToString()} : ({Chance}%) {CurrentCap}/{ItemCap}"; + } + } +} diff --git a/KruacentExiled/KE.Map/Heavy/GamblingZone/Events/EventArgs/GamblingEventArgs.cs b/KruacentExiled/KE.Map/Heavy/GamblingZone/Events/EventArgs/GamblingEventArgs.cs new file mode 100644 index 00000000..e690b94c --- /dev/null +++ b/KruacentExiled/KE.Map/Heavy/GamblingZone/Events/EventArgs/GamblingEventArgs.cs @@ -0,0 +1,27 @@ +using Exiled.API.Features; +using Exiled.API.Features.Items; +using Exiled.Events.EventArgs.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map.Heavy.GamblingZone.Events.EventArgs +{ + public class GamblingEventArgs : IPlayerEvent, IDeniableEvent, IItemEvent + { + public Player Player { get; } + + public bool IsAllowed { get; set; } + + public Item Item { get; } + + public GamblingEventArgs(Player player, Item item,bool isAllowed = true) + { + Player = player; + Item = item; + IsAllowed = isAllowed; + } + } +} diff --git a/KruacentExiled/KE.Map/Heavy/GamblingZone/Events/Handlers/GamblingRoom.cs b/KruacentExiled/KE.Map/Heavy/GamblingZone/Events/Handlers/GamblingRoom.cs new file mode 100644 index 00000000..eacdab49 --- /dev/null +++ b/KruacentExiled/KE.Map/Heavy/GamblingZone/Events/Handlers/GamblingRoom.cs @@ -0,0 +1,22 @@ +using KE.Map.Heavy.GamblingZone.Events.EventArgs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map.Heavy.GamblingZone.Events.Handlers +{ + public static class GamblingRoom + { + + + public static event Action Gambling = delegate { }; + + + public static void OnGambling(GamblingEventArgs ev) + { + Gambling?.Invoke(ev); + } + } +} diff --git a/KruacentExiled/KE.Map/Heavy/GamblingZone/GamblingRoom.cs b/KruacentExiled/KE.Map/Heavy/GamblingZone/GamblingRoom.cs new file mode 100644 index 00000000..5367207d --- /dev/null +++ b/KruacentExiled/KE.Map/Heavy/GamblingZone/GamblingRoom.cs @@ -0,0 +1,197 @@ +using Exiled.API.Features; +using Exiled.API.Features.Doors; +using Exiled.API.Features.Items; +using Exiled.API.Features.Toys; +using Exiled.Events.EventArgs.Server; +using KE.Map.Heavy.GamblingZone.Events.EventArgs; +using KE.Utils.API.Features.SCPs; +using LabApi.Events.Arguments.PlayerEvents; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using InteractableToy = LabApi.Features.Wrappers.InteractableToy; + +namespace KE.Map.Heavy.GamblingZone +{ + public class GamblingRoom : AbstractGambling + { + private static readonly HashSet _list = new HashSet(); + public static IReadOnlyCollection List => _list; + + + + private HashSet _model; + public const float BasePickupTime = 5; + public float PickupTime + { + get + { + return _interact?.InteractionDuration ?? BasePickupTime; + } + set + { + if(value < 1) + { + _interact.InteractionDuration = 1; + } + else + { + _interact.InteractionDuration = value; + } + + } + } + private InteractableToy _interact; + private Vector3 _position; + private LootTable _lootTable; + + + + internal GamblingRoom(Vector3 position, LootTable lootTable, Vector3? offset = null) + { + Init(position, lootTable, offset); + } + + + + protected void Init(Vector3 position, LootTable lootTable, Vector3? offset = null) + { + + + + _position = position + (offset ?? new Vector3()); + _list.Add(this); + + + _interact = InteractableToy.Create(_position, networkSpawn: false); + _interact.InteractionDuration = BasePickupTime; + + LabApi.Events.Handlers.PlayerEvents.SearchedToy += OnPickup; + + CreateModel(_position); + _interact.Spawn(); + _lootTable = lootTable; + + + if (MainPlugin.Configs.Debug) + { + + _interact.OnInteracted += p => Log.Info($"{p.Nickname} interatcted"); // Runs if interactionduration is set to 0 + _interact.OnSearching += p => Log.Info($"{p.Nickname} OnSearching"); // runs when the player presses E & interactionduration != 0 + _interact.OnSearched += p => Log.Info($"{p.Nickname} OnSearched"); // Runs after searching is completed. + _interact.OnSearchAborted += p => Log.Info($"{p.Nickname} OnSearchAborted"); // Runs after + } + + + } + + + private void CreateModel(Vector3 positionWithOffset) + { + + + Vector3 width = new Vector3(.1f, 1, .1f); + + + _model = new HashSet() + { + Primitive.Create(PrimitiveType.Cube,positionWithOffset , null,new Vector3(1,.8f,1),true,Color.black), + Primitive.Create(PrimitiveType.Cube,positionWithOffset + Vector3.forward/2,null, Vector3.right+width,true,Color.white), + Primitive.Create(PrimitiveType.Cube,positionWithOffset + Vector3.back/2, null, Vector3.right+width,true,Color.white), + Primitive.Create(PrimitiveType.Cube,positionWithOffset + Vector3.right/2, null, Vector3.forward+width,true,Color.white), + Primitive.Create(PrimitiveType.Cube,positionWithOffset + Vector3.left/2, null, Vector3.forward+width,true,Color.white), + + }; + foreach (Primitive p in _model) + { + p.Collidable = false; + } + } + + + + + public override void Destroy() + { + foreach (Primitive p in _model) + { + p.Destroy(); + } + LabApi.Events.Handlers.PlayerEvents.SearchedToy -= OnPickup; + _interact.Destroy(); + _list.Remove(this); + } + + + + public void OnPickup(PlayerSearchedToyEventArgs ev) + { + Player player2 = ev.Player; + if (ev.Interactable != _interact) return; + if (player2 == null) return; + if (SCPTeam.IsSCP(player2.ReferenceHub)) return; + + if (player2.CurrentItem == null) return; + Item item = _lootTable.GetRandomItem(); + + GamblingEventArgs ev1 = new GamblingEventArgs(player2, item, true); + + Events.Handlers.GamblingRoom.OnGambling(ev1); + + if (!ev1.IsAllowed) + { + return; + } + + + player2.CurrentItem.Destroy(); + + player2.AddItem(item); + if (!CanPickupOther(player2, item.Category)) + { + player2.DropItem(item, false); + } + + } + + private static bool CanPickupOther(Player player,ItemCategory category) + { + sbyte nbitem = 0; + foreach(Item item in player.Items) + { + if(item.Category == category) + { + nbitem++; + } + } + + return nbitem <= player.GetCategoryLimit(category); + + } + + public static void DestroyAll() + { + foreach (GamblingRoom gamblingRoom in List.ToList()) + { + gamblingRoom.Destroy(); + } + } + + public static void SubscribeEvents() + { + Exiled.Events.Handlers.Server.RoundEnded += OnRoundEnded; + } + + public static void UnsubscribeEvents() + { + Exiled.Events.Handlers.Server.RoundEnded -= OnRoundEnded; + DestroyAll(); + } + + + private static void OnRoundEnded(RoundEndedEventArgs ev) + { + DestroyAll(); + } + } +} diff --git a/KruacentExiled/KE.Map/Heavy/GamblingZone/LootTable.cs b/KruacentExiled/KE.Map/Heavy/GamblingZone/LootTable.cs new file mode 100644 index 00000000..73ae17ee --- /dev/null +++ b/KruacentExiled/KE.Map/Heavy/GamblingZone/LootTable.cs @@ -0,0 +1,86 @@ +using Exiled.API.Features; +using Exiled.API.Features.Items; +using Exiled.API.Features.Pickups; +using Exiled.API.Features.Pools; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Map.Heavy.GamblingZone +{ + public class LootTable + { + public IReadOnlyCollection Items { get; } + + /// + /// Create a with some testing items + /// + public LootTable() + { + Items = new HashSet() + { + new DroppableItem(ItemType.Jailbird,5,1), + new DroppableItem(ItemType.ParticleDisruptor,5,1), + new DroppableItem(ItemType.Radio,15), + }; + + } + + /// + /// Create a with customizable s + /// + public LootTable(IEnumerable items) + { + Items = items.ToHashSet(); + } + + private DroppableItem ChooseRandomItem() + { + int totalWeight = 0; + foreach (DroppableItem drop in Items) + { + if (!drop.HasReachCap()) + totalWeight += drop.Chance; + } + + if (totalWeight == 0) + throw new ArgumentException("Total probability must be greater than zero."); + + int randValue = UnityEngine.Random.Range(0, totalWeight); + int cumulativeSum = 0; + + foreach (DroppableItem drop in Items) + { + if (!drop.HasReachCap()) + cumulativeSum += drop.Chance; + if (randValue < cumulativeSum) + return drop; + } + return null; + + } + + public Item GetRandomItem() + { + DroppableItem item = ChooseRandomItem(); + Log.Debug("random item =" + item); + return item.GetItem(); + } + + /// + public override string ToString() + { + StringBuilder builder = StringBuilderPool.Pool.Get(); + + + foreach (DroppableItem item in Items) + { + builder.AppendLine(item.ToString()); + } + return StringBuilderPool.Pool.ToStringReturn(builder); + } + } +} diff --git a/KruacentExiled/KE.Map/Heavy/GamblingZone/OldGamblingRoom.cs b/KruacentExiled/KE.Map/Heavy/GamblingZone/OldGamblingRoom.cs new file mode 100644 index 00000000..ebd106df --- /dev/null +++ b/KruacentExiled/KE.Map/Heavy/GamblingZone/OldGamblingRoom.cs @@ -0,0 +1,96 @@ +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Doors; +using Exiled.API.Features.Items; +using Exiled.API.Features.Pickups; +using Exiled.API.Features.Spawn; +using Exiled.API.Features.Toys; +using Exiled.Events.EventArgs.Player; +using KE.Utils.API.Features.SCPs; +using PlayerRoles; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace KE.Map.Heavy.GamblingZone +{ + public class OldGamblingRoom : AbstractGambling + { + private static readonly HashSet _list = new HashSet(); + public static HashSet List => new HashSet(_list); + + + private Vector3 _position; + private Vector3 _scale; + private LootTable _lootTable; + + internal OldGamblingRoom(Room room, Vector3 scale, LootTable lootTable, Vector3? offset = null) + { + Init(room.Position, scale, lootTable, offset); + } + + + internal OldGamblingRoom(Door door, Vector3 scale, LootTable lootTable, Vector3? offset = null) + { + Init(door.Position, scale, lootTable, offset); + } + + internal OldGamblingRoom(Vector3 position, Vector3 scale, LootTable lootTable, Vector3? offset = null) + { + Init(position, scale, lootTable, offset); + } + + + + private void Init(Vector3 position, Vector3 scale, LootTable lootTable, Vector3? offset = null) + { + Log.Debug("position w/out offset " + position); + _position = position + (offset ?? new Vector3()); + Log.Debug("position w/ offset " + _position); + _scale = scale; + _list.Add(this); + _lootTable = lootTable; + Exiled.Events.Handlers.Player.DroppedItem += OnDropped; + if (MainPlugin.Instance.Config.Debug) + { + var p = Primitive.Create(PrimitiveType.Cube, _position, null, _scale); + p.Collidable = false; + } + } + + public bool IsInGamblingRoom(Player p) + { + Vector3 playerPosition = p.Position; + Vector3 halfSize = _scale / 2; + return playerPosition.x >= _position.x - halfSize.x && + playerPosition.x <= _position.x + halfSize.x && + playerPosition.y >= _position.y - halfSize.y && + playerPosition.y <= _position.y + halfSize.y && + playerPosition.z >= _position.z - halfSize.z && + playerPosition.z <= _position.z + halfSize.z; + } + + public override void Destroy() + { + Exiled.Events.Handlers.Player.DroppedItem -= OnDropped; + } + + public void OnDropped(DroppedItemEventArgs ev) + { + Player player = ev.Player; + if (player == null) return; + if (ev.Pickup == null) return; + if (SCPTeam.IsSCP(player.ReferenceHub)) return; + + if (!IsInGamblingRoom(player)) + { + Log.Debug($"player ({player.CustomName}) not in room ({player.Position})"); + return; + } + ev.Pickup.Destroy(); + Item item = _lootTable.GetRandomItem(); + item.CreatePickup(player.Position); + } + + } +} diff --git a/KruacentExiled/KE.Map/MainPlugin.cs b/KruacentExiled/KE.Map/MainPlugin.cs new file mode 100644 index 00000000..7ec76ba0 --- /dev/null +++ b/KruacentExiled/KE.Map/MainPlugin.cs @@ -0,0 +1,305 @@ + +using AdminToys; +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Toys; +using Exiled.API.Interfaces; +using HarmonyLib; +using KE.Map.Heavy; +using KE.Map.Heavy.GamblingZone; +using KE.Map.Others.BlackoutNDoor.Handlers; +using KE.Map.Others.CustomZones; +using KE.Map.Surface.ElevatorGateA; +using KE.Map.Surface.Rooms; +using KE.Utils.API.Features; +using KE.Utils.API.KETextToy; +using KE.Utils.Extensions; +using KruacentExiled; +using MEC; +using PlayerRoles; +using PlayerRoles.PlayableScps.Scp106; +using ProjectMER.Features; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEngine; +using static PlayerList; +using Door = Exiled.API.Features.Doors.Door; +using Player = Exiled.API.Features.Player; + +namespace KE.Map +{ + public class MainPlugin : KEPlugin + { + public override string Name => "KE.Map"; + public override string Prefix => "KE.M"; + public static MainPlugin Instance { get; private set; } + private Handler handler; + public static Translations Translations { get; } = new Translations(); + public static Config Configs => (Config) Instance?.Config; + + public override IConfig Config => config; + private Config config; + private Harmony harmony; + + private SurfaceRooms SurfaceRooms; + private CREventHandler cREventHandler; + + + private Translations translations; + + public override void OnEnabled() + { + Instance = this; + handler = new Handler(); + harmony = new Harmony(Prefix); + + config = KruacentExiled.MainPlugin.Instance.Config.MapConfig; + + + //cREventHandler = new CREventHandler(); + + //cREventHandler.SubscribeEvents(); + handler.SubscribeEvents(); + KE.Utils.API.Sounds.SoundPlayer.Instance.TryLoad(); + Exiled.Events.Handlers.Map.Generated += OnGenerated; + Exiled.Events.Handlers.Server.RoundStarted += OnRoundStarted; + Exiled.Events.Handlers.Server.WaitingForPlayers += OnWaitingForPlayers; + + + //SurfaceRooms.SubscribeEvents(); + + GamblingRoom.SubscribeEvents(); + //MoreRoom.CreateAll(); + //MoreRoom.SubscribeEvents(); + harmony.PatchAll(KruacentExiled.MainPlugin.Instance.Assembly); + + + + + + + } + + private void OnWaitingForPlayers() + { + PrefabManager.RegisterPrefabs(); + BulkDoor049.Create(); + CustomElevatorGateA.Create(); + } + + private void OnRoundStarted() + { + if (Config.Debug) + { + Player player = Player.List.First(); + Vector3 pos; + //StructureSpawner.SpawnPedestal(ItemType.KeycardJanitor,player.Position,Quaternion.identity,Vector3.one); + + //player.Teleport(Room.List.Where(r => r.Type == Exiled.API.Enums.RoomType.EzVent).First()); + + + //player.Teleport(teleport); + + Timing.CallDelayed(2, () => + { + //player.Teleport(teleport); + }); + + //pos = RoleTypeId.Scp049.GetRandomSpawnLocation().Position; + pos = player.Position; + + + + + //glass or door w/glass => fake to 106s & 173s & 049-2s & 049s & 096s & ghostly + //door w/out glass => fake to 106s & ghostly + + //Primitive prim = Primitive.Create(PrimitiveType.Cube, pos, spawn: false); + + //prim.Base.gameObject.layer = 14; + //prim.Spawn(); + //Collider col = prim.Base.gameObject.GetComponent(); + + //MirrorExtensions.SendFakeSyncVar(player, prim.Base.netIdentity,typeof(PrimitiveObjectToy), "NetworkPrimitiveFlags", PrimitiveFlags.Visible); + + + //Log.Info(prim.Base.gameObject.layer); + //Log.Info(LayerMask.LayerToName(14)); + + //Log.Info(Scp106MovementModule.GetSlowdownFromCollider(col, out bool passable) + " passable?" + passable); + + //FollowingTextToy f1 = new([player], player.Position, Quaternion.identity, Vector3.one); + //f1.Toy.TextFormat = "F1 sur un bateau"; + + //FollowingTextToy f2 = new([], player.Position, Quaternion.identity, Vector3.one); + //f2.Toy.TextFormat = "F2 tombe à l'eau"; + + + // [█ ] + //[█      ] + //[█      ] + + //[█                                                            ]\n[██████████] + /*try + { + Primitive parent = Primitive.Create(PrimitiveType.Cube, pos, spawn: false); + parent.Flags = PrimitiveFlags.None; + + parent.Spawn(); + + Primitive prim = Primitive.Create(PrimitiveType.Cube, null, spawn: false); + prim.Transform.parent = parent.Transform; + prim.Transform.localPosition = Vector3.zero; + prim.Flags = PrimitiveFlags.Visible; + prim.MovementSmoothing = 60; + prim.Spawn(); + + Log.Info(prim.Position); + + AnimationClip clip = new AnimationClip(); + clip.legacy = true; + clip.wrapMode = WrapMode.PingPong; + + Transform t = prim.GameObject.transform; + + float startX = t.localPosition.x; + + AnimationCurve curve = AnimationCurve.Linear( + 0f, startX, + 3f, startX + 1f + ); + + clip.SetCurve("", typeof(Transform), "localPosition.x", curve); + + Animation animation = prim.GameObject.AddComponent(); + animation.AddClip(clip, "move"); + animation.Play("move"); + + + Log.Debug(animation.IsPlaying("move")); + Timing.CallDelayed(3, () => + { + Log.Info(prim.Position); + player.Position = prim.Position; + }); + } + catch(Exception e) + { + Log.Error(e); + } + + */ + + + } + + + } + + + + public override void OnDisabled() + { + handler?.UnsubscribeEvents(); + Exiled.Events.Handlers.Map.Generated -= OnGenerated; + Exiled.Events.Handlers.Server.RoundStarted -= OnRoundStarted; + Exiled.Events.Handlers.Server.WaitingForPlayers -= OnWaitingForPlayers; + + cREventHandler?.UnsubscribeEvents(); + cREventHandler = null; + + + harmony.UnpatchAll(harmony.Id); + GamblingRoom.UnsubscribeEvents(); + //MoreRoom.UnsubscribeEvents(); + handler = null; + Instance = null; + } + + + + private void OnGenerated() + { + + Door lcz173 = Door.Get(Exiled.API.Enums.DoorType.Scp173Gate); + HashSet normal = new HashSet() + { + new DroppableItem(ItemType.KeycardO5,1,2), + new DroppableItem(ItemType.Jailbird,1,2), + new DroppableItem(ItemType.SCP268,1,1), + ItemType.SCP500, + ItemType.KeycardMTFCaptain, + ItemType.GunCOM15, + ItemType.SCP207, + ItemType.Adrenaline, + ItemType.GunCOM18, + ItemType.KeycardFacilityManager, + ItemType.Medkit, + ItemType.KeycardMTFOperative, + ItemType.KeycardGuard, + ItemType.Radio, + ItemType.Ammo9x19, + ItemType.Ammo44cal, + ItemType.Ammo12gauge, + ItemType.Ammo556x45, + ItemType.Ammo762x39, + ItemType.GrenadeFlash, + ItemType.KeycardScientist, + ItemType.KeycardJanitor, + ItemType.Coin, + ItemType.Flashlight, + ItemType.AntiSCP207, + ItemType.GunCom45, + ItemType.GunShotgun, + ItemType.GunRevolver, + ItemType.GunA7, + }; + + + var g = new GamblingRoom(RoleTypeId.Scp173.GetRandomSpawnLocation().Position + Vector3.down, new LootTable(normal)); + + //var g = new OldGamblingRoom(RoleTypeId.Scp173.GetRandomSpawnLocation().Position + Vector3.down, Vector3.one*10, new LootTable(normal)); + + + + + /* + + if (Config.Debug) + { + var door = KEDoor.Create(null, RoleTypeId.Scp049.GetRandomSpawnLocation().Position, new()); + var d2 = KEDoor.Create(null, RoleTypeId.Scp049.GetRandomSpawnLocation().Position + Vector3.forward * 2, new()); + door.LinkOtherDoor(d2); + } + + + //Blinking Blocks + HashSet list = new() + { + new BlinkingBlock(new(19, 300, -44), new(), new(2, .5f, 2), BlockColor.Red), + new BlinkingBlock(new(19, 300, -38), new(), new(2, .5f, 2), BlockColor.Blue) + }; + + BlinkingBlocksGroup group = new(list); + Timing.RunCoroutine(ShowPos()); + */ + + } + + } + + + public class Config : IConfig + { + public bool IsEnabled { get; set; } = true; + public bool Debug { get; set; } = false; + public bool SupplyDropEnabled { get; set; } = false; + public bool TurretEnabled { get; set; } = false; + public bool EasterEggEnabled { get; set; } = true; + public bool BlackoutNDoorEnabled { get; set; } = true; + + + } +} diff --git a/KruacentExiled/KE.Map/Others/BlackoutNDoor/Blackout.cs b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Blackout.cs new file mode 100644 index 00000000..f2e7f360 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Blackout.cs @@ -0,0 +1,54 @@ +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Doors; +using Exiled.Events.EventArgs.Player; +using KE.Map.Others.BlackoutNDoor.Handlers; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace KE.Map.Others.BlackoutNDoor +{ + public class Blackout : MapEvent + { + public override string Cassie => MainPlugin.Translations.Blackout; + + public override string CassieTranslated => MainPlugin.Translations.BlackoutTranslation; + public override float Duration => 20; + + private ZoneType currentZone = ZoneType.Unspecified; + public override void Start(ZoneType zone) + { + currentZone = zone; + foreach (Room room in Room.List.Where(r => r.Zone == zone)) + { + room.TurnOffLights(); + } + + Exiled.Events.Handlers.Player.TriggeringTesla += OnTriggeringTesla; + } + + private void OnTriggeringTesla(TriggeringTeslaEventArgs ev) + { + if (ev.Tesla.Room.Zone.HasFlagFast(currentZone)) + { + ev.IsAllowed = false; + } + + } + + public override void Stop(ZoneType zone) + { + + Exiled.Events.Handlers.Player.TriggeringTesla -= OnTriggeringTesla; + foreach (Room room in Room.List.Where(r => r.Zone == zone)) + { + room.AreLightsOff = false; + } + currentZone = ZoneType.Unspecified; + } + + + } +} diff --git a/KruacentExiled/KE.Map/Others/BlackoutNDoor/Both.cs b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Both.cs new file mode 100644 index 00000000..50c52e11 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Both.cs @@ -0,0 +1,31 @@ +using Exiled.API.Enums; +using KE.Map.Others.BlackoutNDoor.Handlers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map.Others.BlackoutNDoor +{ + public class Both : MapEvent + { + private DoorStuck doorstuck = new DoorStuck(); + private Blackout blackout = new Blackout(); + public override string Cassie => MainPlugin.Translations.Both; + public override float Duration => 20; + + public override string CassieTranslated => MainPlugin.Translations.BothTranslation; + public override void Start(ZoneType zone) + { + doorstuck.Start(zone); + blackout.Start(zone); + } + + public override void Stop(ZoneType zone) + { + doorstuck.Stop(zone); + blackout.Stop(zone); + } + } +} diff --git a/KruacentExiled/KE.Map/Others/BlackoutNDoor/Commands/BlackoutCommand.cs b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Commands/BlackoutCommand.cs new file mode 100644 index 00000000..bb18d9c4 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Commands/BlackoutCommand.cs @@ -0,0 +1,49 @@ +using CommandSystem; +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Pools; +using KE.Utils.API.Commands; +using MapGeneration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map.Others.BlackoutNDoor.Commands +{ + + internal class BlackoutCommand : KECommand + { + public override string Command => "blackout"; + + public override string[] Aliases => new string[] { "b" }; + + public override string Description => "force a blackout"; + + public override string[] Usage => new string[] { "FacilityZone" }; + + public override bool ExecuteCommand(ArraySegment arguments, ICommandSender sender, out string response) + { + response = string.Empty; + if (arguments.Count == 0) + { + return false; + } + string arg1 = arguments.At(0); + if(!Enum.TryParse(arg1, true, out FacilityZone zone)) + { + response = "Could not parse the zone"; + return false; + } + + + Blackout blackout = new Blackout(); + blackout.StartEvent(zone.GetZone(),-1); + response = "blackout forced at " + zone.ToString(); + + return true; + } + } +} diff --git a/KruacentExiled/KE.Map/Others/BlackoutNDoor/Commands/BothCommand.cs b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Commands/BothCommand.cs new file mode 100644 index 00000000..39dea340 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Commands/BothCommand.cs @@ -0,0 +1,42 @@ +using CommandSystem; +using Exiled.API.Extensions; +using KE.Utils.API.Commands; +using MapGeneration; +using System; + +namespace KE.Map.Others.BlackoutNDoor.Commands +{ + + internal class BothCommand : KECommand + { + public override string Command => "both"; + + public override string[] Aliases => new string[0]; + + public override string Description => "force a doorstuck and a blackout"; + + public override string[] Usage => new string[] { "FacilityZone" }; + + public override bool ExecuteCommand(ArraySegment arguments, ICommandSender sender, out string response) + { + response = string.Empty; + if (arguments.Count == 0) + { + return false; + } + string arg1 = arguments.At(0); + if(!Enum.TryParse(arg1, true, out FacilityZone zone)) + { + response = "Could not parse the zone"; + return false; + } + + + Both both = new Both(); + both.StartEvent(zone.GetZone(),-1); + response = "both forced at " + zone.ToString(); + + return true; + } + } +} diff --git a/KruacentExiled/KE.Map/Others/BlackoutNDoor/Commands/DoorstuckCommand.cs b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Commands/DoorstuckCommand.cs new file mode 100644 index 00000000..ce6aa7cd --- /dev/null +++ b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Commands/DoorstuckCommand.cs @@ -0,0 +1,49 @@ +using CommandSystem; +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Pools; +using KE.Utils.API.Commands; +using MapGeneration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map.Others.BlackoutNDoor.Commands +{ + + internal class DoorstuckCommand : KECommand + { + public override string Command => "doorstuck"; + + public override string[] Aliases => new string[] { "d" }; + + public override string Description => "force a doorstuck"; + + public override string[] Usage => new string[] { "FacilityZone" }; + + public override bool ExecuteCommand(ArraySegment arguments, ICommandSender sender, out string response) + { + response = string.Empty; + if (arguments.Count == 0) + { + return false; + } + string arg1 = arguments.At(0); + if(!Enum.TryParse(arg1, true, out FacilityZone zone)) + { + response = "Could not parse the zone"; + return false; + } + + + DoorStuck doorstuck = new DoorStuck(); + doorstuck.StartEvent(zone.GetZone(),-1); + response = "doorstuck forced at " + zone.ToString(); + + return true; + } + } +} diff --git a/KruacentExiled/KE.Map/Others/BlackoutNDoor/Commands/MainCommand.cs b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Commands/MainCommand.cs new file mode 100644 index 00000000..1831b25f --- /dev/null +++ b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Commands/MainCommand.cs @@ -0,0 +1,30 @@ +using CommandSystem; +using Exiled.API.Features.Pools; +using KE.Utils.API.Commands; +using RelativePositioning; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map.Others.BlackoutNDoor.Commands +{ + [CommandHandler(typeof(RemoteAdminCommandHandler))] + public class MainCommand : KEParentCommand + { + public override string Command => "mapevent"; + + public override string[] Aliases => new string[] { "me" }; + + public override string Description => "mapevents"; + + public override void LoadGeneratedCommands() + { + RegisterCommand(new BlackoutCommand()); + RegisterCommand(new DoorstuckCommand()); + RegisterCommand(new BothCommand()); + } + + } +} diff --git a/KruacentExiled/KE.Map/Others/BlackoutNDoor/DoorStuck.cs b/KruacentExiled/KE.Map/Others/BlackoutNDoor/DoorStuck.cs new file mode 100644 index 00000000..8efa6e46 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/BlackoutNDoor/DoorStuck.cs @@ -0,0 +1,89 @@ +using Exiled.API.Enums; +using Exiled.API.Features.Doors; +using Interactables.Interobjects.DoorUtils; +using KE.Map.Others.BlackoutNDoor.Events.EventArgs; +using KE.Map.Others.BlackoutNDoor.Events.Handlers; +using KE.Map.Others.BlackoutNDoor.Handlers; +using KE.Utils.API.Map; +using NorthwoodLib.Pools; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace KE.Map.Others.BlackoutNDoor +{ + + public class DoorStuck : MapEvent + { + + private static HashSet doors; + public override string Cassie => MainPlugin.Translations.Doorstuck; + + public override string CassieTranslated => MainPlugin.Translations.DoorstuckTranslation; + public override float Duration => 15; + + public override void Start(ZoneType zone) + { + bool open = UnityEngine.Random.value > .5f; + doors = HashSetPool.Shared.Rent(); + foreach (Door door in Door.List.Where(d => d != null&& d.Zone == zone && !d.IsElevator && d.Type != DoorType.Scp079First && d.Type != DoorType.Scp079Second)) + { + if (door.DoorLockType == DoorLockType.None) + { + doors.Add(door); + + } + } + + if(StructureSpawner.AdditionalDoors.TryGetValue(zone,out var addDoors)) + { + foreach(DoorVariant doorVariant in addDoors) + { + Door door2 = Door.Get(doorVariant); + if (door2 != null && door2.DoorLockType == DoorLockType.None) + { + doors.Add(door2); + } + } + } + + DoorStuckEventArgs ev = new DoorStuckEventArgs(doors,zone,true); + DoorStuckHandler.OnDoorStucking(ev); + + if (ev.IsAllowed && ev.Doors != null) + { + doors = ev.Doors; + foreach (Door door in doors) + { + LockDoor(door, open); + } + } + else + { + doors.Clear(); + } + + + } + + private void LockDoor(Door door, bool open) + { + door.ChangeLock(DoorLockType.NoPower); + door.IsOpen = open; + } + + public override void Stop(ZoneType zone) + { + bool open = UnityEngine.Random.value > .5f; + foreach (Door door in doors) + { + door.IsOpen = open; + door.ChangeLock(DoorLockType.None); + } + + + HashSetPool.Shared.Return(doors); + + } + } +} diff --git a/KruacentExiled/KE.Map/Others/BlackoutNDoor/Events/EventArgs/ChoseMapEventEventArgs.cs b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Events/EventArgs/ChoseMapEventEventArgs.cs new file mode 100644 index 00000000..446b6073 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Events/EventArgs/ChoseMapEventEventArgs.cs @@ -0,0 +1,22 @@ +using Exiled.Events.EventArgs.Interfaces; +using KE.Map.Others.BlackoutNDoor.Handlers; +using KE.Map.Others.BlackoutNDoor.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map.Others.BlackoutNDoor.Events.EventArgs +{ + public class ChoseMapEventEventArgs : IExiledEvent, IMapEvent + { + public MapEvent MapEvent { get; set; } + + public ChoseMapEventEventArgs(MapEvent mapEvent) + { + MapEvent = mapEvent; + } + + } +} diff --git a/KruacentExiled/KE.Map/Others/BlackoutNDoor/Events/EventArgs/ChoseZoneEventArgs.cs b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Events/EventArgs/ChoseZoneEventArgs.cs new file mode 100644 index 00000000..288eb31d --- /dev/null +++ b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Events/EventArgs/ChoseZoneEventArgs.cs @@ -0,0 +1,23 @@ +using Exiled.API.Enums; +using Exiled.Events.EventArgs.Interfaces; +using KE.Map.Others.BlackoutNDoor.Handlers; +using KE.Map.Others.BlackoutNDoor.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map.Others.BlackoutNDoor.Events.EventArgs +{ + public class ChoseZoneEventArgs : IExiledEvent + { + public ZoneType Zone { get; set; } + + public ChoseZoneEventArgs(ZoneType zone) + { + Zone = zone; + } + + } +} diff --git a/KruacentExiled/KE.Map/Others/BlackoutNDoor/Events/EventArgs/DoorStuckEventArgs.cs b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Events/EventArgs/DoorStuckEventArgs.cs new file mode 100644 index 00000000..91e05581 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Events/EventArgs/DoorStuckEventArgs.cs @@ -0,0 +1,26 @@ +using Exiled.API.Enums; +using Exiled.API.Features.Doors; +using Exiled.Events.EventArgs.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map.Others.BlackoutNDoor.Events.EventArgs +{ + public class DoorStuckEventArgs : IExiledEvent, IDeniableEvent, IZoneType + { + + public HashSet Doors { get; set; } + public bool IsAllowed { get; set; } + public ZoneType Zone { get; set; } + + public DoorStuckEventArgs(HashSet doors,ZoneType zone, bool isAllowed = true) + { + Doors = doors; + Zone = zone; + IsAllowed = isAllowed; + } + } +} diff --git a/KruacentExiled/KE.Map/Others/BlackoutNDoor/Events/EventArgs/PostEventEventArgs.cs b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Events/EventArgs/PostEventEventArgs.cs new file mode 100644 index 00000000..6e13cacd --- /dev/null +++ b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Events/EventArgs/PostEventEventArgs.cs @@ -0,0 +1,22 @@ +using Exiled.Events.EventArgs.Interfaces; +using KE.Map.Others.BlackoutNDoor.Handlers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map.Others.BlackoutNDoor.Events.EventArgs +{ + public class PostEventEventArgs : IExiledEvent + { + public MapEvent MapEvent { get; set; } + + public PostEventEventArgs(MapEvent mapEvent) + { + MapEvent = mapEvent; + } + + + } +} diff --git a/KruacentExiled/KE.Map/Others/BlackoutNDoor/Events/EventArgs/PreEventEventArgs.cs b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Events/EventArgs/PreEventEventArgs.cs new file mode 100644 index 00000000..92034f47 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Events/EventArgs/PreEventEventArgs.cs @@ -0,0 +1,23 @@ +using Exiled.Events.EventArgs.Interfaces; +using KE.Map.Others.BlackoutNDoor.Handlers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map.Others.BlackoutNDoor.Events.EventArgs +{ + public class PreEventEventArgs: IExiledEvent,IDeniableEvent + { + public bool IsAllowed { get; set; } + public MapEvent MapEvent { get; set; } + + public PreEventEventArgs(MapEvent mapEvent, bool isAllowed) + { + MapEvent = mapEvent; + IsAllowed = isAllowed; + } + + } +} diff --git a/KruacentExiled/KE.Map/Others/BlackoutNDoor/Events/Handlers/BlackoutNDoor.cs b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Events/Handlers/BlackoutNDoor.cs new file mode 100644 index 00000000..f9078faa --- /dev/null +++ b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Events/Handlers/BlackoutNDoor.cs @@ -0,0 +1,40 @@ +using Exiled.Events.Features; +using KE.Map.Others.BlackoutNDoor.Events.EventArgs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map.Others.BlackoutNDoor.Events.Handlers +{ + public static class BlackoutNDoor + { + + + public static Event PreEvent { get; set; } = new Event(); + public static Event PostEvent { get; set; } = new Event(); + + public static Event ChoseMapEvent { get; set; } = new Event(); + public static Event ChoseZoneEvent { get; set; } = new Event(); + public static void OnPreEvent(PreEventEventArgs ev) + { + PreEvent.InvokeSafely(ev); + } + public static void OnPostEvent(PostEventEventArgs ev) + { + PostEvent.InvokeSafely(ev); + } + + public static void OnChoseMapEvent(ChoseMapEventEventArgs ev) + { + ChoseMapEvent.InvokeSafely(ev); + } + + public static void OnChoseZoneEvent(ChoseZoneEventArgs ev) + { + ChoseZoneEvent.InvokeSafely(ev); + } + + } +} diff --git a/KruacentExiled/KE.Map/Others/BlackoutNDoor/Events/Handlers/DoorStuckHandler.cs b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Events/Handlers/DoorStuckHandler.cs new file mode 100644 index 00000000..486e5d2e --- /dev/null +++ b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Events/Handlers/DoorStuckHandler.cs @@ -0,0 +1,26 @@ +using Exiled.Events; +using Exiled.Events.Features; +using KE.Map.Others.BlackoutNDoor.Events.EventArgs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map.Others.BlackoutNDoor.Events.Handlers +{ + public static class DoorStuckHandler + { + + + + public static Event DoorStucking { get; set; } = new Event(); + + + + public static void OnDoorStucking(DoorStuckEventArgs ev) + { + DoorStucking?.InvokeSafely(ev); + } + } +} diff --git a/KruacentExiled/KE.Map/Others/BlackoutNDoor/Events/IZoneType.cs b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Events/IZoneType.cs new file mode 100644 index 00000000..5f169b9f --- /dev/null +++ b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Events/IZoneType.cs @@ -0,0 +1,15 @@ +using Exiled.API.Enums; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map.Others.BlackoutNDoor.Events +{ + public interface IZoneType + { + + public ZoneType Zone { get; set; } + } +} diff --git a/KruacentExiled/KE.Map/Others/BlackoutNDoor/Handlers/Handler.cs b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Handlers/Handler.cs new file mode 100644 index 00000000..ea368a66 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Handlers/Handler.cs @@ -0,0 +1,242 @@ +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using KE.Map.Others.BlackoutNDoor.Events.EventArgs; +using KE.Utils.API.Features.SCPs; +using KE.Utils.API.Interfaces; +using KE.Utils.Extensions; +using MapGeneration; +using MEC; +using PlayerRoles; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using EventHandle = KE.Map.Others.BlackoutNDoor.Events.Handlers.BlackoutNDoor; + +namespace KE.Map.Others.BlackoutNDoor.Handlers +{ + public class Handler : IUsingEvents + { + public static float MinInterval = 60 * 3; + public static float MaxInterval = 60 * 6; + + public Pattern ChosenPattern = null; + + private float time = 0; + public float TimeBeforeNextEvent => time; + + public static readonly HashSet Zones = new HashSet() + { + FacilityZone.LightContainment,FacilityZone.HeavyContainment,FacilityZone.Entrance,FacilityZone.Surface + }; + + private FacilityZone currentScpZone = FacilityZone.None; + private int weightPerSeconds = 5; + private int weight = defaultWeight; + private const int defaultWeight = 330; + + public void SubscribeEvents() + { + Exiled.Events.Handlers.Server.RoundStarted += OnRoundStarted; + } + + public void UnsubscribeEvents() + { + Exiled.Events.Handlers.Server.RoundStarted -= OnRoundStarted; + } + + private void OnRoundStarted() + { + + if (!MainPlugin.Configs.BlackoutNDoorEnabled) return; + + ChosenPattern = Pattern.AllPatterns.GetRandomValue(); + time = GetRandomTime(); + + if (MainPlugin.Instance.Config.Debug) + { + time = 30; + ChosenPattern = new Pattern + ( + new List() + { + new Blackout(),new DoorStuck() + } + ); + } + + + Timing.RunCoroutine(Timer()); + } + + private IEnumerator Timer() + { + int timeRefresh = 10; + while (Round.InProgress) + { + yield return Timing.WaitForSeconds(timeRefresh); + if (Warhead.IsInProgress) continue; + + ReferenceHub scp = SCPTeam.SCPs.Where(p => p.GetRoleId() != RoleTypeId.Scp0492).FirstOrDefault(); + + + if(scp == null) + { + scp = SCPTeam.SCPs.FirstOrDefault(); + } + + + if (scp != null) + { + FacilityZone zone = scp.GetCurrentZone(); + + if (zone == currentScpZone) + { + weight += timeRefresh* weightPerSeconds; + } + else + { + weight = defaultWeight; + } + currentScpZone = zone; + } + else + { + currentScpZone = FacilityZone.None; + } + + Log.Debug("weight=" + weight + " at " + currentScpZone); + + + time-= timeRefresh; + Log.Debug(time +"s"); + if (time <= 0) + { + LaunchEvent(); + time = GetRandomTime(); + } + + + } + } + private float GetRandomTime() + { + return UnityEngine.Random.Range(MinInterval, MaxInterval); + } + + + private void LaunchEvent() + { + + + MapEvent mapEvent = ChosenPattern.GetNext(); + + ChoseMapEventEventArgs choseMapEv = new ChoseMapEventEventArgs(mapEvent); + EventHandle.OnChoseMapEvent(choseMapEv); + mapEvent = choseMapEv.MapEvent; + + + ZoneType zone = GetZone(); + ChoseZoneEventArgs choseZoneEv = new ChoseZoneEventArgs(zone); + EventHandle.OnChoseZoneEvent(choseZoneEv); + + if (Zones.Contains(choseZoneEv.Zone.GetZone())) + { + zone = choseZoneEv.Zone; + } + else + { + throw new ArgumentException($"zone ({choseZoneEv.Zone}) not authorized"); + } + + + + Log.Info(mapEvent.GetType().Name + " at " + zone); + + + + PreEventEventArgs preEv = new PreEventEventArgs(mapEvent, true); + EventHandle.OnPreEvent(preEv); + + + if (preEv.IsAllowed && Zones.Contains(zone.GetZone())) + { + + string message = mapEvent.Cassie + " " + ZoneTypeToCassie(zone) + " " + MainPlugin.Translations.End; + string translate = mapEvent.CassieTranslated + " " + ZoneTypeToCassieTranslated(zone) + " " + MainPlugin.Translations.End; + float yap = Exiled.API.Features.Cassie.CalculateDuration(message); + Exiled.API.Features.Cassie.MessageTranslated(message, translate,false,false,true); + + + Timing.CallDelayed(5 + yap, delegate + { + + mapEvent.StartEvent(zone,TimeBeforeNextEvent); + + }); + } + } + + + private string ZoneTypeToCassie(ZoneType zone) + { + return zone switch + { + ZoneType.LightContainment => MainPlugin.Translations.LightContainment, + ZoneType.HeavyContainment => MainPlugin.Translations.HeavyContainment, + ZoneType.Entrance => MainPlugin.Translations.EntranceZone, + ZoneType.Surface => MainPlugin.Translations.SurfaceZone, + _ => string.Empty + }; + } + + private string ZoneTypeToCassieTranslated(ZoneType zone) + { + return zone switch + { + ZoneType.LightContainment => MainPlugin.Translations.LightContainmentTranslation, + ZoneType.HeavyContainment => MainPlugin.Translations.HeavyContainmentTranslation, + ZoneType.Entrance => MainPlugin.Translations.EntranceZoneTranslation, + ZoneType.Surface => MainPlugin.Translations.SurfaceZoneTranslation, + _ => string.Empty + }; + } + + + private ZoneType GetZone() + { + return RandomZoneByWeight(); + } + + private ZoneType RandomZoneByWeight() + { + List weightedPool = new List(); + + + + + foreach (FacilityZone zone in Zones.Where(z => ZoneExtensions.IsSafe(z.GetZone()))) + { + if(zone != currentScpZone) + { + for (int i = 0; i < defaultWeight; i++) + { + weightedPool.Add(zone.GetZone()); + } + } + else + { + for (int i = 0; i < weight; i++) + { + weightedPool.Add(zone.GetZone()); + } + } + + + + } + return weightedPool.GetRandomValue(); + } + } +} diff --git a/KruacentExiled/KE.Map/Others/BlackoutNDoor/Handlers/MapEvent.cs b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Handlers/MapEvent.cs new file mode 100644 index 00000000..c7a98f77 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Handlers/MapEvent.cs @@ -0,0 +1,63 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using KE.Map.Others.BlackoutNDoor.Events.EventArgs; +using KE.Map.Others.BlackoutNDoor.Interfaces; +using MEC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Map.Others.BlackoutNDoor.Handlers +{ + public abstract class MapEvent + { + + public abstract string Cassie { get; } + public abstract string CassieTranslated { get; } + public virtual float Duration { get; } = 10f; + public void StartEvent(ZoneType zone,float maxTime) + { + Log.Debug("start to " + zone); + float time = Duration; + if (maxTime > 1) + { + time = Mathf.Clamp(Duration, 1, maxTime); + } + + try + { + Start(zone); + } + finally + { + Timing.CallDelayed(time, delegate + { + Log.Debug("stopping"); + Stop(zone); + PostEventEventArgs postEv = new PostEventEventArgs(this); + Events.Handlers.BlackoutNDoor.OnPostEvent(postEv); + }); + } + + + + + } + + /// + /// Start a without stopping it + /// + /// + public abstract void Start(ZoneType zone); + + /// + /// Stop a + /// + /// + public abstract void Stop(ZoneType zone); + + } +} diff --git a/KruacentExiled/KE.Map/Others/BlackoutNDoor/Handlers/Pattern.cs b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Handlers/Pattern.cs new file mode 100644 index 00000000..eaf4c47f --- /dev/null +++ b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Handlers/Pattern.cs @@ -0,0 +1,89 @@ +using Exiled.API.Features; +using Mirror; +using PlayerRoles.PlayableScps.Scp3114; +using System; +using System.Collections.Generic; +using System.IO.IsolatedStorage; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map.Others.BlackoutNDoor.Handlers +{ + public class Pattern + { + public static readonly HashSet AllPatterns = new HashSet() + { + new Pattern + (new List() + { + new Blackout(),new DoorStuck() + }) + , + new Pattern + (new List() + { + new Blackout(),new Blackout(),new DoorStuck(),new DoorStuck() + }) + , + new Pattern + (new List() + { + new DoorStuck(),new Blackout(),new Both(),new Blackout(),new DoorStuck(),new Both() + }) + , + new Pattern + (new List() + { + new DoorStuck(),new Blackout(),new Both() + }) + , + new Pattern + (new List() + { + new DoorStuck(),new Both() + }) + , + new Pattern + (new List() + { + new Blackout(),new Both() + }) + , + new Pattern + (new List() + { + new Both() + }) + }; + + + private int current = -1; + private readonly List _pattern; + + public Pattern(List pattern) + { + _pattern = new List(); + for (int i = 0; i < pattern.Count; i++) + { + _pattern.Add(pattern[i]); + } + } + + + public MapEvent GetNext() + { + current = (current + 1) % _pattern.Count; + return _pattern[current]; + } + + public MapEvent SeeNext() + { + return _pattern[(current + 1) % _pattern.Count]; + } + + + + + } +} diff --git a/KruacentExiled/KE.Map/Others/BlackoutNDoor/Interfaces/IMapEvent.cs b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Interfaces/IMapEvent.cs new file mode 100644 index 00000000..d7d953f3 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/BlackoutNDoor/Interfaces/IMapEvent.cs @@ -0,0 +1,15 @@ +using KE.Map.Others.BlackoutNDoor.Handlers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map.Others.BlackoutNDoor.Interfaces +{ + public interface IMapEvent + { + + MapEvent MapEvent { get; } + } +} diff --git a/KruacentExiled/KE.Map/Others/CustomDoors/CustomDoor.cs b/KruacentExiled/KE.Map/Others/CustomDoors/CustomDoor.cs new file mode 100644 index 00000000..7d355067 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/CustomDoors/CustomDoor.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map.Others.CustomDoors +{ + internal class CustomDoor + { + } +} diff --git a/KruacentExiled/KE.Map/Others/CustomDoors/KEDoor.cs b/KruacentExiled/KE.Map/Others/CustomDoors/KEDoor.cs new file mode 100644 index 00000000..c6a88d96 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/CustomDoors/KEDoor.cs @@ -0,0 +1,24 @@ +using KE.Map.Others.CustomElevators; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map.Others.CustomDoors +{ + public abstract class KEDoor + { + + + public static HashSet doors = new HashSet(); + + + + public abstract string NameTag { get; } + public KEDoor() + { + doors.Add(this); + } + } +} diff --git a/KruacentExiled/KE.Map/Others/CustomDoors/VanillaDoor.cs b/KruacentExiled/KE.Map/Others/CustomDoors/VanillaDoor.cs new file mode 100644 index 00000000..e29ae586 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/CustomDoors/VanillaDoor.cs @@ -0,0 +1,23 @@ +using Exiled.API.Interfaces; +using LabApi.Features.Wrappers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map.Others.CustomDoors +{ + public class VanillaDoor : KEDoor, IWrapper + { + public Door Base { get; } + + public override string NameTag => Base.NameTag; + public VanillaDoor(Door door) : base() + { + Base = door; + + + } + } +} diff --git a/KruacentExiled/KE.Map/Others/CustomElevators/CustomElevator.cs b/KruacentExiled/KE.Map/Others/CustomElevators/CustomElevator.cs new file mode 100644 index 00000000..a608541d --- /dev/null +++ b/KruacentExiled/KE.Map/Others/CustomElevators/CustomElevator.cs @@ -0,0 +1,30 @@ +using Exiled.API.Interfaces; +using KE.Map.Surface.ElevatorGateA; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Map.Others.CustomElevators +{ + public class CustomElevator : KEElevator + { + + public static ElevatorModel model { get; } = new ElevatorModel(); + + public override bool IsReady => false; + + public override void Send() + { + + } + + + public CustomElevator() + { + + } + } +} diff --git a/KruacentExiled/KE.Map/Others/CustomElevators/KECustomElevators/KECustomElevator.cs b/KruacentExiled/KE.Map/Others/CustomElevators/KECustomElevators/KECustomElevator.cs new file mode 100644 index 00000000..f85cefc8 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/CustomElevators/KECustomElevators/KECustomElevator.cs @@ -0,0 +1,191 @@ +using Exiled.API.Features; +using Exiled.API.Features.Objectives; +using Exiled.API.Features.Toys; +using KE.Map.Surface.ElevatorGateA; +using KE.Utils.API.Features; +using KE.Utils.Quality.Models; +using MEC; +using ProjectMER.Commands.Modifying.Scale; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Unity.Profiling; +using UnityEngine; + +namespace KE.Map.Others.CustomElevators.KECustomElevators +{ + public class KECustomElevator : MonoBehaviour + { + + + public float BottomHeight; + public float TopHeight = 301f; + + private float objective; + + + private ElevatorModel model; + private Panel toppanel; + private Primitive primtop; + private Panel bottompanel; + private Primitive primbottom; + + + private static Primitive step; + private static Primitive help; + + + private void Awake() + { + + SpawnModel(); + CreatePrimitives(); + CreateModel(); + + BottomHeight = base.transform.localPosition.y; + + objective = TopHeight; + model.SendingElevator += TrySend; + bottompanel.SendingElevator += TrySend; + toppanel.SendingElevator += TrySend; + } + + + public const float Scale = 0.8f; + + private void SpawnModel() + { + model = new ElevatorModel(); + toppanel = new Panel(); + bottompanel = new Panel(); + + } + private void CreateModel() + { + model.Create(base.transform); + toppanel.Create(base.transform); + bottompanel.Create(base.transform); + + + model.button.NetworkMaterialColor = Color.blue; + bottompanel.button.NetworkMaterialColor = Color.blue; + toppanel.button.NetworkMaterialColor = Color.blue; + + } + + private void CreatePrimitives() + { + Vector3 helppos = new Vector3(18.24f, 255.65f, -46.07f); + Vector3 postop = new Vector3(19.92f, 299.97f, -48.95f); + Vector3 posbot = new Vector3(15.62f, 290.65f, -45.19f); + + primtop = Primitive.Create(postop, null, Vector3.one * Scale); + primtop.Flags = AdminToys.PrimitiveFlags.None; + primbottom.Position = postop; + + primbottom = Primitive.Create(posbot, null, Vector3.one * Scale); + primbottom.Position = posbot; + primbottom.Flags = AdminToys.PrimitiveFlags.None; + + + help = Primitive.Create(PrimitiveType.Cube, helppos, null, new Vector3(50, 50, 50), false); + help.Flags = AdminToys.PrimitiveFlags.Visible; + help.Spawn(); + help.GameObject.AddComponent(); + } + + + + public void Destroy() + { + Destroy(this); + } + + private void OnDestroy() + { + model.SendingElevator -= TrySend; + bottompanel.SendingElevator -= TrySend; + toppanel.SendingElevator -= TrySend; + model.Destroy(base.transform); + toppanel.Destroy(primtop.Transform); + bottompanel.Destroy(primbottom.Transform); + step.Destroy(); + help.Destroy(); + primbottom = null; + primtop = null; + step = null; + help = null; + } + + public void TrySend() + { + if (isMoving) return; + + startY = transform.position.y; + elapsed = 0f; + + objective = Mathf.Approximately(startY, BottomHeight) + ? TopHeight + : BottomHeight; + + isMoving = true; + } + + + private bool isMoving = false; + + private float duration = 5f; + + private bool sending = false; + private float elapsed = 0f; + private float startY; + private void SendingElevator() + { + if (isMoving) + { + model.button.NetworkMaterialColor = Color.yellow; + bottompanel.button.NetworkMaterialColor = Color.yellow; + toppanel.button.NetworkMaterialColor = Color.yellow; + + elapsed += Time.deltaTime; + + float t = Mathf.Clamp01(elapsed / duration); + t = t * t * (3f - 2f * t); // smoothstep + + float newY = Mathf.Lerp(startY, objective, t); + + transform.position = new Vector3( + transform.position.x, + newY, + transform.position.z + ); + + if (elapsed >= duration) + { + transform.position = new Vector3( + transform.position.x, + objective, + transform.position.z + ); + + isMoving = false; + } + } + else + { + model.button.NetworkMaterialColor = Color.blue; + bottompanel.button.NetworkMaterialColor = Color.blue; + toppanel.button.NetworkMaterialColor = Color.blue; + } + } + private void Update() + { + SendingElevator(); + } + + + + } +} diff --git a/KruacentExiled/KE.Map/Others/CustomElevators/KECustomElevators/KEElevatorPanel.cs b/KruacentExiled/KE.Map/Others/CustomElevators/KECustomElevators/KEElevatorPanel.cs new file mode 100644 index 00000000..07b224bc --- /dev/null +++ b/KruacentExiled/KE.Map/Others/CustomElevators/KECustomElevators/KEElevatorPanel.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map.Others.CustomElevators.KECustomElevators +{ + public class KEElevatorPanel + { + } +} diff --git a/KruacentExiled/KE.Map/Others/CustomElevators/KEElevator.cs b/KruacentExiled/KE.Map/Others/CustomElevators/KEElevator.cs new file mode 100644 index 00000000..3966cc41 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/CustomElevators/KEElevator.cs @@ -0,0 +1,30 @@ +using CustomPlayerEffects; +using LabApi.Features.Wrappers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map.Others.CustomElevators +{ + public abstract class KEElevator + { + + public static HashSet elevators = new HashSet(); + + + + public abstract bool IsReady { get; } + public abstract void Send(); + + + + public KEElevator() + { + elevators.Add(this); + } + + + } +} diff --git a/KruacentExiled/KE.Map/Others/CustomElevators/VanillaElevator.cs b/KruacentExiled/KE.Map/Others/CustomElevators/VanillaElevator.cs new file mode 100644 index 00000000..9f77dc9f --- /dev/null +++ b/KruacentExiled/KE.Map/Others/CustomElevators/VanillaElevator.cs @@ -0,0 +1,29 @@ +using Exiled.API.Interfaces; +using LabApi.Features.Wrappers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map.Others.CustomElevators +{ + public class VanillaElevator : KEElevator, IWrapper + { + + public Elevator Base { get; } + + + public override bool IsReady => Base.IsReady; + public override void Send() + { + Base.SendToNextFloor(); + } + + + public VanillaElevator(Elevator elevator) : base() + { + Base = elevator; + } + } +} diff --git a/KruacentExiled/KE.Map/Others/CustomZones/AltasReader.cs b/KruacentExiled/KE.Map/Others/CustomZones/AltasReader.cs new file mode 100644 index 00000000..89abb09f --- /dev/null +++ b/KruacentExiled/KE.Map/Others/CustomZones/AltasReader.cs @@ -0,0 +1,249 @@ +using Exiled.API.Features; +using Exiled.API.Features.Pools; +using MapGeneration; +using System; +using System.Collections.Generic; +using System.Data.SqlTypes; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using Color = System.Drawing.Color; + +namespace KE.Map.Others.CustomZones +{ + public static class AltasReader + { + + + public static readonly Dictionary ColortoRoom = new Dictionary() + { + { new Color32(0,255,0,255), RoomShape.Endroom }, + { new Color32(255,0,0,255), RoomShape.TShape }, + { new Color32(0,0,255,255), RoomShape.Curve }, + { new Color32(255,255,0,255), RoomShape.Straight }, + { new Color32(255,0,255,255), RoomShape.XShape } + }; + + public static string Path => Paths.Configs + "/Atlases/"; + + public class RoomShapeRotation + { + + public RoomShape RoomShape { get; } + public Vector3 Rotation { get; } + + + public RoomShapeRotation(RoomShape roomShape, Vector3 rotation) + { + RoomShape = roomShape; + Rotation = rotation; + } + + + } + + + public static void Read() + { + if (!Directory.Exists(Path)) + { + Log.Warn("no atlases directory. creating"); + Directory.CreateDirectory(Path); + return ; + } + + HashSet layouts = new HashSet(); + + IEnumerable files = Directory.GetFiles(Path, "*.png"); + + Dictionary coordtoroom = new Dictionary(); + + foreach (string file in files) + { + + string noExFile = System.IO.Path.GetFileNameWithoutExtension(file); + Log.Debug($"loading {file} as {noExFile}"); + using Bitmap bmp = new Bitmap(file); + int width = bmp.Width; + int height = bmp.Height; + + if(width % 3 != 0 || height % 3 != 0) + { + throw new FileLoadException("wrong size : must be a multiple of 3"); + } + + for (int y = 1; y < height; y += 3) + { + for (int x = 1; x < width; x+= 3) + { + + Color pixel = bmp.GetPixel(x, y); + if (pixel.A == 0) continue; + Color pixelup = bmp.GetPixel(x, y+1); + Color pixeldown = bmp.GetPixel(x, y-1); + Color pixelleft = bmp.GetPixel(x-1, y); + Color pixelright = bmp.GetPixel(x+1, y); + + Dictionary colors = new Dictionary() + { + { new Vector2Int(0,1),pixelup }, + { new Vector2Int(0,-1),pixeldown }, + { new Vector2Int(-1,0),pixelleft }, + { new Vector2Int(1,0),pixelright } + }; + + + + List empty = ListPool.Pool.Get(); + foreach(Vector2Int pos in colors.Keys) + { + if(colors[pos].A == 0) + { + empty.Add(pos); + } + } + int numEmpty = empty.Count; + + RoomShape shape = RoomShape.Undefined; + Vector3 rotation = Vector3.zero; + + if (numEmpty == 0) + { + shape = RoomShape.XShape; + rotation = Vector3.zero; + } + + if(numEmpty == 1) + { + shape = RoomShape.TShape; + Vector2Int coordempty = empty[0]; + rotation = new Vector3(0, coordempty.x * 90, 0); + + if (coordempty.x == 0 && coordempty.y == -1) + { + rotation = Vector3.zero; + } + if (coordempty.x == 0 && coordempty.y == 1) + { + rotation = new Vector3(0, 180, 0); + } + } + + + if(numEmpty == 2) + { + if (Mathf.Abs(empty[0].x) == Mathf.Abs(empty[1].x) + || Mathf.Abs(empty[0].y) == Mathf.Abs(empty[1].y)) + { + shape = RoomShape.Straight; + if(empty[0].x == 0) + { + rotation = new Vector3(0, 90, 0); + } + else + { + rotation = Vector3.zero; + } + } + else + { + shape = RoomShape.Curve; + + Dictionary coordToRotation = new Dictionary() + { + {new Vector2Int(1,1),Vector3.zero }, + {new Vector2Int(-1,1), new Vector3(0,90,0) }, + {new Vector2Int(-1,-1), new Vector3(0,180,0) }, + {new Vector2Int(1,-1), new Vector3(0,-90,0) } + }; + + + foreach(var kvp in coordToRotation) + { + if (!empty.Contains(new Vector2Int(0, kvp.Key.y)) && + !empty.Contains(new Vector2Int(kvp.Key.x, 0))) + { + rotation = kvp.Value; + } + } + + } + } + + + if(numEmpty == 3) + { + Vector2Int coordnotempty = Vector2Int.zero; + shape = RoomShape.Endroom; + foreach (Vector2Int pos in colors.Keys) + { + if (!empty.Contains(pos)) + { + coordnotempty = pos; + } + } + + + if (coordnotempty.x == 0 && coordnotempty.y == 1) + { + rotation = Vector3.zero; + } + if (coordnotempty.x == 0 && coordnotempty.y == -1) + { + rotation = new Vector3(0, 180, 0); + } + if (coordnotempty.x == 1 && coordnotempty.y == 0) + { + rotation = new Vector3(0, -90, 0); + } + if (coordnotempty.x == -1 && coordnotempty.y == 0) + { + rotation = new Vector3(0, 90, 0); + } + + + + } + + + + + /*Color32 color = new Color32(pixel.R, pixel.G, pixel.B, pixel.A); + + + if (!ColortoRoom.ContainsKey(color)) + { + throw new ArgumentException($"[{noExFile}] Unrecognized color at {coord}"); + } + shape = ColortoRoom[color]; + */ + + + Vector2Int coord = new Vector2Int(x, y); + + if(shape == RoomShape.Undefined) + { + Log.Warn($"found undefined at {coord} room skipping"); + continue; + } + Log.Debug($"adding {shape} at {coord} ({rotation.y})"); + coordtoroom.Add(coord, new RoomShapeRotation(shape,rotation)); + + } + } + + Layout layout = new Layout(coordtoroom, noExFile); + coordtoroom.Clear(); + } + } + + + + + + + } +} diff --git a/KruacentExiled/KE.Map/Others/CustomZones/CREventHandler.cs b/KruacentExiled/KE.Map/Others/CustomZones/CREventHandler.cs new file mode 100644 index 00000000..5e2bc43f --- /dev/null +++ b/KruacentExiled/KE.Map/Others/CustomZones/CREventHandler.cs @@ -0,0 +1,68 @@ +using Exiled.API.Features; +using KE.Map.Others.CustomZones.CustomRooms; +using KE.Map.Others.CustomZones.CustomRooms.MCZ; +using KE.Utils.API.Interfaces; +using LabApi.Events.Arguments.ServerEvents; +using MapGeneration; +using System; +using System.Linq; +using UnityEngine; + +namespace KE.Map.Others.CustomZones +{ + internal class CREventHandler : IUsingEvents + { + public void SubscribeEvents() + { + Exiled.Events.Handlers.Map.Generated += OnGenerated; + LabApi.Events.Handlers.ServerEvents.MapGenerating += OnGenerating; + } + + public void UnsubscribeEvents() + { + Exiled.Events.Handlers.Map.Generated -= OnGenerated; + LabApi.Events.Handlers.ServerEvents.MapGenerating -= OnGenerating; + } + + private int seed; + private Vector3 teleport; + private void OnGenerating(MapGeneratingEventArgs ev) + { + seed = ev.Seed; + + } + private void OnGenerated() + { + Log.Debug("read"); + try + { + + AltasReader.Read(); + } + catch (Exception e) + { + Log.Error(e); + } + + + CustomZone zone = new MediumContainmentZone(); + //teleport = zone.Spawnzone; + new SCorridor(); + new EndRoom(); + new TCorridor(); + new Curve(); + new XCorridor(); + new MCZDoorSeparator(); + + System.Random random = new System.Random(seed); + + zone.Generate(random,Layout.Layouts.First(l => l.Name == "Circle")); + + + teleport = CustomRoom.RegisteredRoom.First().SpawnedRoom.First(s => s.Shape == RoomShape.Straight).Position + Vector3.up * 5; + Log.Debug("teleport " + teleport); + } + + + } +} diff --git a/KruacentExiled/KE.Map/Others/CustomZones/CustomFacilityZone.cs b/KruacentExiled/KE.Map/Others/CustomZones/CustomFacilityZone.cs new file mode 100644 index 00000000..b0f4b250 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/CustomZones/CustomFacilityZone.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map.Others.CustomZones +{ + public enum CustomFacilityZone + { + None, + MediumContainment + } +} diff --git a/KruacentExiled/KE.Map/Others/CustomZones/CustomRoom.cs b/KruacentExiled/KE.Map/Others/CustomZones/CustomRoom.cs new file mode 100644 index 00000000..2d46d6f5 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/CustomZones/CustomRoom.cs @@ -0,0 +1,63 @@ +using Exiled.API.Features; +using Exiled.API.Features.Toys; +using MapGeneration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Map.Others.CustomZones +{ + public abstract class CustomRoom + { + private static readonly HashSet registered = new HashSet(); + public static IReadOnlyCollection RegisteredRoom => registered; + + public abstract RoomShape Shape { get; } + + public abstract CustomFacilityZone FacilityZone { get; } + + public Vector3 Size => RoomIdentifier.GridScale; + public HashSet SpawnedRoom { get; } = new HashSet(); + + public CustomRoom() + { + registered.Add(this); + } + + protected abstract IEnumerable SpawnRoom(Vector3 position,Vector3 rotation); + + + public SpawnedCustomRoom Spawn(Vector2Int coord,Vector3 rotation,Vector3 spawnzone) + { + Vector3 position = new Vector3(-coord.x * Size.x+ spawnzone.x, spawnzone.y, coord.y * Size.z + spawnzone.z); + + Log.Debug("spawn room at " + position); + IEnumerable prims = SpawnRoom(position,rotation); + + foreach(AdminToy admin in prims) + { + Log.Debug("spawning prim at "+ admin.Position); + } + + + SpawnedCustomRoom room = new SpawnedCustomRoom(this, Shape, position,rotation, coord, prims.ToHashSet()); + room.Spawn(); + SpawnedRoom.Add(room); + return room; + + } + + + public static Primitive CreatePrimitive(PrimitiveType type = PrimitiveType.Cube,Vector3? position = null, Vector3? rotation = null, Vector3? scale = null,Color? color = null) + { + return Primitive.Create(type, position, rotation, scale, false, color); + } + + + + } +} diff --git a/KruacentExiled/KE.Map/Others/CustomZones/CustomRooms/DoorSeparator.cs b/KruacentExiled/KE.Map/Others/CustomZones/CustomRooms/DoorSeparator.cs new file mode 100644 index 00000000..d64a09fb --- /dev/null +++ b/KruacentExiled/KE.Map/Others/CustomZones/CustomRooms/DoorSeparator.cs @@ -0,0 +1,56 @@ +using Exiled.API.Features; +using KE.Map.Others.CustomZones; +using Mirror; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Map.Others.CustomZones.CustomRooms +{ + public abstract class DoorSeparator + { + private static readonly HashSet registered = new HashSet(); + public static IReadOnlyCollection RegisteredDoorSeparator => registered; + public DoorSeparator() + { + registered.Add(this); + } + + + + public HashSet SpawnDoorSeparator { get; } = new HashSet(); + public abstract CustomFacilityZone FacilityZone { get; } + + protected abstract IEnumerable Create(Vector3 position, Vector3 rotation); + + + public SpawnedDoorSeparator Spawn(SpawnedCustomRoom room1, SpawnedCustomRoom room2) + { + Vector3 posdif = room1.Position + room2.Position; + Vector3 position = posdif / 2f; + Vector3 rotation = Vector3.zero; + if (Mathf.Approximately(room1.Position.z, room2.Position.z)) + { + rotation = new Vector3(0, 90, 0); + } + + + //Log.Debug("spawning door at " + position + $"({rotation})"); + IEnumerable objs = Create(position, rotation); + + + + SpawnedDoorSeparator spawned = new SpawnedDoorSeparator(this, position, rotation, new List() { room1, room2 }, objs.ToHashSet()); + spawned.Spawn(); + + SpawnDoorSeparator.Add(spawned); + room1.Door.Add(spawned); + room2.Door.Add(spawned); + return spawned; + } + + } +} diff --git a/KruacentExiled/KE.Map/Others/CustomZones/CustomRooms/MCZ/Curve.cs b/KruacentExiled/KE.Map/Others/CustomZones/CustomRooms/MCZ/Curve.cs new file mode 100644 index 00000000..5dc16169 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/CustomZones/CustomRooms/MCZ/Curve.cs @@ -0,0 +1,41 @@ +using Exiled.API.Features.Toys; +using MapGeneration; +using System.Collections.Generic; +using UnityEngine; +using Light = Exiled.API.Features.Toys.Light; + +namespace KE.Map.Others.CustomZones.CustomRooms.MCZ +{ + public class Curve : CustomRoom + { + public override RoomShape Shape => RoomShape.Curve; + + public override CustomFacilityZone FacilityZone => CustomFacilityZone.MediumContainment; + private float width = 6f; + protected override IEnumerable SpawnRoom(Vector3 position, Vector3 rotation) + { + Quaternion rot = Quaternion.Euler(rotation); + + + Light light = Light.Create(position + Vector3.up * 2, null, null, false, Color.white); + light.LightType = LightType.Spot; + light.Rotation = Quaternion.LookRotation(Vector3.down); + light.Range = 50f; + light.SpotAngle = 90; + light.Intensity = 8; + + + float lengthbranch = Size.x / 2f - width / 2f; + float lengthmain = Size.x / 2f + width / 2f; + + + return new HashSet() + { + + Primitive.Create(PrimitiveType.Cube,position + rot*Vector3.left*(lengthmain/2f-width/2f), rotation, new Vector3(lengthmain,1,6f), false),//main + CreatePrimitive(PrimitiveType.Cube,position + rot*Vector3.forward*(width/2f+lengthbranch/2f),rot.eulerAngles, new Vector3(width,1,lengthbranch)), //branch + light, + }; + } + } +} diff --git a/KruacentExiled/KE.Map/Others/CustomZones/CustomRooms/MCZ/EndRoom.cs b/KruacentExiled/KE.Map/Others/CustomZones/CustomRooms/MCZ/EndRoom.cs new file mode 100644 index 00000000..10f8bfd2 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/CustomZones/CustomRooms/MCZ/EndRoom.cs @@ -0,0 +1,37 @@ +using Exiled.API.Features.Toys; +using MapGeneration; +using System.Collections.Generic; +using UnityEngine; +using Light = Exiled.API.Features.Toys.Light; + +namespace KE.Map.Others.CustomZones.CustomRooms.MCZ +{ + public class EndRoom : CustomRoom + { + public override RoomShape Shape => RoomShape.Endroom; + + public override CustomFacilityZone FacilityZone => CustomFacilityZone.MediumContainment; + private float width = 6f; + protected override IEnumerable SpawnRoom(Vector3 position,Vector3 rotation) + { + Quaternion rot = Quaternion.Euler(rotation); + + + Light light = Light.Create(position + Vector3.up * 2, null, null, false, Color.white); + light.LightType = LightType.Spot; + light.Rotation = Quaternion.LookRotation(Vector3.down); + light.Range = 50f; + light.SpotAngle = 120; + light.Intensity = 8; + + + float lengthmain = (Size.x / 2f) + (width / 2f); + + return new HashSet() + { + Primitive.Create(PrimitiveType.Cube,position + rot*Vector3.forward*((lengthmain/2f)-(width/2f)), rotation, new Vector3(6f,1,lengthmain), false,Color.red),//main + light, + }; + } + } +} diff --git a/KruacentExiled/KE.Map/Others/CustomZones/CustomRooms/MCZ/MCZDoorSeparator.cs b/KruacentExiled/KE.Map/Others/CustomZones/CustomRooms/MCZ/MCZDoorSeparator.cs new file mode 100644 index 00000000..e6de0d05 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/CustomZones/CustomRooms/MCZ/MCZDoorSeparator.cs @@ -0,0 +1,34 @@ +using Interactables.Interobjects.DoorUtils; +using KE.Map.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Map.Others.CustomZones.CustomRooms.MCZ +{ + public class MCZDoorSeparator : DoorSeparator + { + public override CustomFacilityZone FacilityZone => CustomFacilityZone.MediumContainment; + private Vector3 doorpos; + protected override IEnumerable Create(Vector3 position, Vector3 rotation) + { + Quaternion rot = Quaternion.Euler(rotation); + DoorVariant door = StructureSpawner.CreateRawDoor(ProjectMER.Features.Enums.DoorType.Ez, position + Vector3.up / 2f, Quaternion.Euler(rotation), Vector3.one); + + doorpos = door.transform.position+Vector3.up*1.5f; + Vector3 scale = new Vector3(2.5f, 3f, .2f); + + return new HashSet() + { + door.gameObject, + CustomRoom.CreatePrimitive(PrimitiveType.Cube,doorpos+rot*Vector3.right*2,rotation,scale).GameObject, + CustomRoom.CreatePrimitive(PrimitiveType.Cube,doorpos+rot*Vector3.left*2,rotation,scale).GameObject, + }; + + + } + } +} diff --git a/KruacentExiled/KE.Map/Others/CustomZones/CustomRooms/MCZ/SCorridor.cs b/KruacentExiled/KE.Map/Others/CustomZones/CustomRooms/MCZ/SCorridor.cs new file mode 100644 index 00000000..2105d6ff --- /dev/null +++ b/KruacentExiled/KE.Map/Others/CustomZones/CustomRooms/MCZ/SCorridor.cs @@ -0,0 +1,39 @@ +using Exiled.API.Features.Toys; +using MapGeneration; +using System.Collections.Generic; +using UnityEngine; +using Light = Exiled.API.Features.Toys.Light; + +namespace KE.Map.Others.CustomZones.CustomRooms.MCZ +{ + public class SCorridor : CustomRoom + { + public override RoomShape Shape => RoomShape.Straight; + + public override CustomFacilityZone FacilityZone => CustomFacilityZone.MediumContainment; + + private float height = 4; + protected override IEnumerable SpawnRoom(Vector3 position, Vector3 rotation) + { + Quaternion rot = Quaternion.Euler(rotation); + + + Light light = Light.Create(position + Vector3.up * height, null, null, false, Color.white); + light.LightType = LightType.Point; + light.Rotation = Quaternion.LookRotation(Vector3.down); + light.Range = 10; + light.Intensity = 50; + light.ShadowType = LightShadows.None; + + + return new HashSet() + { + Primitive.Create(PrimitiveType.Cube,position, rotation, new Vector3(6f,1,15f), false), + Primitive.Create(PrimitiveType.Cube,position+Vector3.up*height, rotation, new Vector3(6f,.5f,15f), false), + Primitive.Create(PrimitiveType.Cube,position+Vector3.up*(height/2)+rot*Vector3.left*2, rotation, new Vector3(1f,height,15f), false), + Primitive.Create(PrimitiveType.Cube,position+Vector3.up*(height/2)+rot*Vector3.right*2, rotation, new Vector3(1f,height,15f), false), + light, + }; + } + } +} diff --git a/KruacentExiled/KE.Map/Others/CustomZones/CustomRooms/MCZ/TCorridor.cs b/KruacentExiled/KE.Map/Others/CustomZones/CustomRooms/MCZ/TCorridor.cs new file mode 100644 index 00000000..093e6fcc --- /dev/null +++ b/KruacentExiled/KE.Map/Others/CustomZones/CustomRooms/MCZ/TCorridor.cs @@ -0,0 +1,48 @@ +using DrawableLine; +using Exiled.API.Features.Toys; +using MapGeneration; +using MEC; +using System.Collections.Generic; +using UnityEngine; +using Light = Exiled.API.Features.Toys.Light; + +namespace KE.Map.Others.CustomZones.CustomRooms.MCZ +{ + public class TCorridor : CustomRoom + { + public override RoomShape Shape => RoomShape.TShape; + + public override CustomFacilityZone FacilityZone => CustomFacilityZone.MediumContainment; + + private float width = 6f; + private float height = 4; + protected override IEnumerable SpawnRoom(Vector3 position, Vector3 rotation) + { + Quaternion rot = Quaternion.Euler(rotation); + + + Light light = Light.Create(position + Vector3.up * height, null, null, false, Color.white); + light.LightType = LightType.Point; + light.Rotation = Quaternion.LookRotation(Vector3.down); + light.Range = 10; + light.Intensity = 50; + light.ShadowType = LightShadows.None; + + + + float lengthbranch = (Size.x / 2f) - (width / 2f); + + return new HashSet() + { + + CreatePrimitive(PrimitiveType.Cube,position,rotation, new Vector3(Size.x,1,width)), + CreatePrimitive(PrimitiveType.Cube,position + rot*Vector3.forward*((width/2)+(lengthbranch/2)),rotation, new Vector3(width,1,lengthbranch)), + light, + + CreatePrimitive(PrimitiveType.Cube,position +Vector3.up * height,rotation, new Vector3(Size.x,1,width)), + CreatePrimitive(PrimitiveType.Cube,position +Vector3.up * height+ rot*Vector3.forward*((width/2)+(lengthbranch/2)),rotation, new Vector3(width,1,lengthbranch)), + }; + + } + } +} diff --git a/KruacentExiled/KE.Map/Others/CustomZones/CustomRooms/MCZ/XCorridor.cs b/KruacentExiled/KE.Map/Others/CustomZones/CustomRooms/MCZ/XCorridor.cs new file mode 100644 index 00000000..aa330636 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/CustomZones/CustomRooms/MCZ/XCorridor.cs @@ -0,0 +1,39 @@ +using Exiled.API.Features.Toys; +using MapGeneration; +using System.Collections.Generic; +using UnityEngine; +using Light = Exiled.API.Features.Toys.Light; + +namespace KE.Map.Others.CustomZones.CustomRooms.MCZ +{ + public class XCorridor : CustomRoom + { + public override RoomShape Shape => RoomShape.XShape; + + public override CustomFacilityZone FacilityZone => CustomFacilityZone.MediumContainment; + private float width = 6f; + protected override IEnumerable SpawnRoom(Vector3 position, Vector3 rotation) + { + Quaternion rot = Quaternion.Euler(rotation); + + + Light light = Light.Create(position + Vector3.up * 2, null, null, false, Color.white); + light.LightType = LightType.Spot; + light.Rotation = Quaternion.LookRotation(Vector3.down); + light.Range = 50f; + light.SpotAngle = 90; + light.Intensity = 8; + + float lengthbranch = Size.x / 2f - width / 2f; + + + return new HashSet() + { + CreatePrimitive(PrimitiveType.Cube,position,rot.eulerAngles, new Vector3(Size.x,1,width)), + CreatePrimitive(PrimitiveType.Cube,position + rot*Vector3.forward*(width/2+lengthbranch/2),rot.eulerAngles, new Vector3(width,1,lengthbranch)), + CreatePrimitive(PrimitiveType.Cube,position + rot*Vector3.back*(width/2+lengthbranch/2),rot.eulerAngles, new Vector3(width,1,lengthbranch)), + light, + }; + } + } +} diff --git a/KruacentExiled/KE.Map/Others/CustomZones/CustomRooms/SpawnedDoorSeparator.cs b/KruacentExiled/KE.Map/Others/CustomZones/CustomRooms/SpawnedDoorSeparator.cs new file mode 100644 index 00000000..e46788e9 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/CustomZones/CustomRooms/SpawnedDoorSeparator.cs @@ -0,0 +1,58 @@ +using Exiled.API.Features.Toys; +using MapGeneration; +using Mirror; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Map.Others.CustomZones.CustomRooms +{ + public class SpawnedDoorSeparator + { + public SpawnedDoorSeparator(DoorSeparator basedoor, Vector3 position, Vector3 rotation, IEnumerable rooms, HashSet gameObjects) + { + BaseDoor = basedoor; + Position = position; + Rooms = rooms; + GameObjects = gameObjects.ToHashSet(); + } + + + public DoorSeparator BaseDoor { get; } + public Vector3 Position { get; } + public Vector3 Rotation { get; } + + public IEnumerable Rooms { get; } + + public HashSet GameObjects { get; } + + + public void Spawn() + { + foreach(GameObject obj in GameObjects) + { + NetworkServer.Spawn(obj); + } + } + + public void Unspawn() + { + foreach (GameObject obj in GameObjects) + { + NetworkServer.UnSpawn(obj); + } + } + + public void Destroy() + { + foreach (GameObject obj in GameObjects.ToList()) + { + NetworkServer.Destroy(obj); + } + } + + } +} diff --git a/KruacentExiled/KE.Map/Others/CustomZones/CustomZone.cs b/KruacentExiled/KE.Map/Others/CustomZones/CustomZone.cs new file mode 100644 index 00000000..d70c71dc --- /dev/null +++ b/KruacentExiled/KE.Map/Others/CustomZones/CustomZone.cs @@ -0,0 +1,37 @@ +using Exiled.API.Extensions; +using LabApi.Features.Wrappers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Map.Others.CustomZones +{ + public abstract class CustomZone + { + public abstract CustomFacilityZone FacilityZone { get; } + public Layout Layout { get; private set; } + public abstract Vector3 Spawnzone { get; } + public void Generate(System.Random rng) + { + Generate(rng,SetRandomLayout()); + } + + public void GenerateWithLayout(System.Random rng,Layout layout) + { + Generate(rng, layout); + } + public abstract void Generate(System.Random rng, Layout layout); + + private Layout SetRandomLayout() + { + Layout layout =Layout.Layouts.GetRandomValue(); + Layout = layout; + return layout; + + } + + } +} diff --git a/KruacentExiled/KE.Map/Others/CustomZones/Layout.cs b/KruacentExiled/KE.Map/Others/CustomZones/Layout.cs new file mode 100644 index 00000000..9d828235 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/CustomZones/Layout.cs @@ -0,0 +1,27 @@ +using MapGeneration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using static KE.Map.Others.CustomZones.AltasReader; + +namespace KE.Map.Others.CustomZones +{ + public struct Layout + { + public static HashSet Layouts = new HashSet(); + public string Name { get; } + public readonly Dictionary coordtoroom; + public Layout(Dictionary coordtoroom, string name) + { + Name = name; + this.coordtoroom = new Dictionary(coordtoroom); + Layouts.Add(this); + } + + + } + +} diff --git a/KruacentExiled/KE.Map/Others/CustomZones/MediumContainmentZone.cs b/KruacentExiled/KE.Map/Others/CustomZones/MediumContainmentZone.cs new file mode 100644 index 00000000..ece53962 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/CustomZones/MediumContainmentZone.cs @@ -0,0 +1,99 @@ +using Exiled.API.Extensions; +using Exiled.API.Features; +using Interactables.Interobjects.DoorButtons; +using KE.Map.Others.CustomZones.CustomRooms; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using UnityEngine.Rendering; +using Random = System.Random; + +namespace KE.Map.Others.CustomZones +{ + public class MediumContainmentZone : CustomZone + { + public override CustomFacilityZone FacilityZone => CustomFacilityZone.MediumContainment; + + public override Vector3 Spawnzone { get; } + + private GameObject gameObject; + public MediumContainmentZone() + { + Spawnzone = AlphaWarheadNukesitePanel.Singleton.lever.position+ new Vector3(0, -50, 0); + //Spawnzone = Room.List.Where(r => r.Type == Exiled.API.Enums.RoomType.HczEzCheckpointB).First().Position + new Vector3(0,0,-200); + gameObject = new GameObject("MCZ"); + + } + + + + public override void Generate(Random rng,Layout layout) + { + + + if(CustomRoom.RegisteredRoom.Count == 0) + { + throw new ArgumentNullException("no round found"); + } + + IEnumerable rooms = CustomRoom.RegisteredRoom.Where(r => r.FacilityZone == FacilityZone); + + foreach(var kvp in layout.coordtoroom) + { + CustomRoom room = rooms.GetRandomValue(c => c.Shape == kvp.Value.RoomShape); + + if(room is null) + { + throw new Exception($"couldn't find a room for {FacilityZone} with shape {kvp.Value.RoomShape}"); + } + + Vector3 rotation = kvp.Value.Rotation; + + Vector2Int coord = kvp.Key/3; + + + room.Spawn(coord, rotation, Spawnzone); + } + + IEnumerable doors = DoorSeparator.RegisteredDoorSeparator.Where(d => d.FacilityZone == FacilityZone); + + + foreach(SpawnedCustomRoom scr in SpawnedCustomRoom.SpawnedRoom) + { + //Log.Debug($"scr1 {scr.Shape} ({scr.Coord})"); + foreach(SpawnedCustomRoom scr2 in scr.Neighbors) + { + //Log.Debug($"scr2 {scr2.Shape} ({scr2.Coord})"); + + var scrDoors = scr.Door.Intersect(scr2.Door); + + + if (scrDoors.Count() > 0) + { + //Log.Debug("already a door"); + continue; + } + + if (scr.DoorAvailable(scr2.Coord)) + { + Log.Debug("not available " + scr2.Coord); + continue; + } + + DoorSeparator door = doors.GetRandomValue(); + door.Spawn(scr, scr2); + + } + } + + + + } + + + + } +} diff --git a/KruacentExiled/KE.Map/Others/CustomZones/SpawnedCustomRoom.cs b/KruacentExiled/KE.Map/Others/CustomZones/SpawnedCustomRoom.cs new file mode 100644 index 00000000..84ad4261 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/CustomZones/SpawnedCustomRoom.cs @@ -0,0 +1,127 @@ +using Exiled.API.Features; +using Exiled.API.Features.Toys; +using KE.Map.Others.CustomZones.CustomRooms; +using MapGeneration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Map.Others.CustomZones +{ + public class SpawnedCustomRoom + { + private static readonly HashSet spawned = new HashSet(); + public static IReadOnlyCollection SpawnedRoom => spawned; + public SpawnedCustomRoom(CustomRoom baseRoom, RoomShape shape, Vector3 position, Vector3 rotation, Vector2Int coord, HashSet primitives) + { + BaseRoom = baseRoom; + Shape = shape; + Position = position; + Coord = coord; + Primitives = primitives.ToHashSet(); + spawned.Add(this); + } + + + public CustomRoom BaseRoom { get; } + public RoomShape Shape { get; } + + public Vector3 Position { get; } + public Vector3 Rotation { get; } + + public Vector2Int Coord { get; } + + private HashSet cachedNeighbors = null; + public IEnumerable Neighbors + { + get + { + if (cachedNeighbors is null) + { + cachedNeighbors = new HashSet(); + foreach (SpawnedCustomRoom scr in SpawnedRoom) + { + + int dx = scr.Coord.x - Coord.x; + int dy = scr.Coord.y - Coord.y; + + if ((Math.Abs(dx) == 1 && dy == 0) || (Math.Abs(dy) == 1 && dx == 0)) + { + if (!cachedNeighbors.Contains(scr)) + cachedNeighbors.Add(scr); + } + } + } + + return cachedNeighbors; + } + } + + public HashSet Door { get; } = new HashSet(); + + public HashSet Primitives { get; } + + public void Spawn() + { + foreach(AdminToy adminToy in Primitives) + { + adminToy.Spawn(); + } + } + + public void Unspawn() + { + foreach (AdminToy adminToy in Primitives) + { + adminToy.UnSpawn(); + } + } + + public void Destroy() + { + foreach (AdminToy adminToy in Primitives.ToList()) + { + adminToy.Destroy(); + } + } + + + + public bool DoorAvailable(Vector2Int roomToLink) + { + if(Shape == RoomShape.Curve) + { + var curveLinks = new Dictionary + { + { 0, new [] { ( 0, 1), ( 1, 0) } }, // Up, Right + { 90, new [] { ( -1, 0), ( 0, 1) } }, // Right, Down + { 180, new [] { ( 0, -1), (-1, 0) } }, // Down, Left + { 270, new [] { (1, 0), ( 0, -1) } }, // Left, Up + }; + + int rot = Mathf.RoundToInt(Rotation.y) % 360; + + if (curveLinks.TryGetValue(rot, out var links)) + { + foreach (var (dx, dy) in links) + { + if (roomToLink.x == Coord.x + dx && + roomToLink.y == Coord.y + dy) + { + return true; + } + } + } + + return false; + } + + + return true; + } + + } +} diff --git a/KruacentExiled/KE.Map/Others/EasterEggs/Capybaras.cs b/KruacentExiled/KE.Map/Others/EasterEggs/Capybaras.cs new file mode 100644 index 00000000..96bbfe06 --- /dev/null +++ b/KruacentExiled/KE.Map/Others/EasterEggs/Capybaras.cs @@ -0,0 +1,42 @@ +using AdminToys; +using Exiled.API.Extensions; +using Exiled.API.Features.Toys; +using Exiled.API.Interfaces; +using KE.Utils.API.Interfaces; +using LabApi.Features.Wrappers; +using PlayerRoles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using Light = Exiled.API.Features.Toys.Light; + +namespace KE.Map.Others.EasterEggs +{ + internal class Capybaras : IUsingEvents + { + private HashSet _spinnyBaras = new HashSet(); + public readonly Vector3 _capy1 = new Vector3(129, 293, 18); + + public void SubscribeEvents() + { + Exiled.Events.Handlers.Map.Generated += OnGenerated; + + } + + public void UnsubscribeEvents() + { + Exiled.Events.Handlers.Map.Generated -= OnGenerated; + } + + + private void OnGenerated() + { + //e + _spinnyBaras.Add(new SpinnyBaras(_capy1)); + + } + } +} diff --git a/KruacentExiled/KE.Map/Others/EasterEggs/SpinnyBaras.cs b/KruacentExiled/KE.Map/Others/EasterEggs/SpinnyBaras.cs new file mode 100644 index 00000000..a10f6a2c --- /dev/null +++ b/KruacentExiled/KE.Map/Others/EasterEggs/SpinnyBaras.cs @@ -0,0 +1,48 @@ +using Exiled.API.Features; +using Exiled.API.Features.Toys; +using LabApi.Features.Wrappers; +using MEC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using Player = Exiled.API.Features.Player; + +namespace KE.Map.Others.EasterEggs +{ + internal class SpinnyBaras + { + private CapybaraToy _capybara; + private CoroutineHandle _handle; + + private float speed = 100; + public SpinnyBaras(Vector3 position) + { + _capybara = CapybaraToy.Create(position); + _handle = Timing.RunCoroutine(Spin()); + } + + ~SpinnyBaras() + { + Timing.KillCoroutines(_handle); + } + private IEnumerator Spin() + { + while (true) + { + _capybara.Transform.Rotate(Vector3.up, 1); + yield return Timing.WaitForSeconds(1 / speed); + } + + } + + + public void Kill() + { + Timing.KillCoroutines(_handle); + } + + } +} diff --git a/KruacentExiled/KE.Map/Patches/MapGenerationPatches.cs b/KruacentExiled/KE.Map/Patches/MapGenerationPatches.cs new file mode 100644 index 00000000..088e098a --- /dev/null +++ b/KruacentExiled/KE.Map/Patches/MapGenerationPatches.cs @@ -0,0 +1,131 @@ +using Exiled.API.Features; +using HarmonyLib; +using MapGeneration; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; +using UnityEngine; +using UnityEngine.Experimental.Rendering; +using Color = System.Drawing.Color; +namespace KE.Map.Patches +{ + /*public static class MapGenerationPatches + { + + [HarmonyPatch(typeof(MapAtlasInterpreter),nameof(MapAtlasInterpreter.Interpret))] + public static class InterpretPatch + { + static int counter = 0; + public static void Prefix(UnityEngine.Texture2D atlas, System.Random rng, MapAtlasInterpreter __instance) + { + //PrintAtlas(atlas); + } + + + public static void PrintAtlas(UnityEngine.Texture2D atlas) + { + + var _bytes = EncodeToPNG(atlas); + + var dirPath = Paths.Configs + "/Images/"; + + if (!Directory.Exists(dirPath)) + { + + Directory.CreateDirectory(dirPath); + + } + var name = "test" + counter; + counter++; + + File.WriteAllBytes(dirPath + name + ".png", _bytes); + } + + private static byte[] EncodeToPNG(Texture2D texture) + { + // Get raw pixel colors from the texture + Color32[] pixels = texture.GetPixels32(); + + using (Bitmap bmp = new Bitmap(texture.width, texture.height, PixelFormat.Format32bppArgb)) + { + for (int y = 0; y < texture.height; y++) + { + for (int x = 0; x < texture.width; x++) + { + Color32 c = pixels[y * texture.width + x]; + bmp.SetPixel(x, texture.height - y - 1, Color.FromArgb(c.a, c.r, c.g, c.b)); + } + } + + using (MemoryStream ms = new MemoryStream()) + { + bmp.Save(ms, ImageFormat.Png); + return ms.ToArray(); + } + } + } + + } + + [HarmonyPatch(typeof(AtlasZoneGenerator), nameof(AtlasZoneGenerator.Generate))] + public static class GeneratePatch + { + static bool flag = false; + static int counter = 0; + public static void Prefix(System.Random rng, AtlasZoneGenerator __instance) + { + + if (!flag && __instance is LightContainmentZoneGenerator lcz) + { + List a = __instance.Atlases.ToList(); + Texture2D tex = DecodePNGFromFile(Paths.Configs + "/atlais.png"); + a.Clear(); + a.Add(tex); + __instance.Atlases = a.ToArray(); + + flag = true; + } + Log.Debug(counter); + counter++; + + foreach (Texture2D atlas in __instance.Atlases) + { + InterpretPatch.PrintAtlas(atlas); + } + } + + + public static Texture2D DecodePNGFromFile(string path) + { + + using (var bmp = new Bitmap(path)) + { + Texture2D tex = new Texture2D(bmp.Width, bmp.Height, TextureFormat.RGBA32, false); + + for (int y = 0; y < bmp.Height; y++) + { + for (int x = 0; x < bmp.Width; x++) + { + var c = bmp.GetPixel(x, bmp.Height - 1 - y); + tex.SetPixel(x, y, new Color32(c.R, c.G, c.B, c.A)); + } + } + + tex.Apply(); + return tex; + } + + } + + + } + + + }*/ +} diff --git a/KruacentExiled/KE.Map/ShowPos.cs b/KruacentExiled/KE.Map/ShowPos.cs new file mode 100644 index 00000000..a34a8d79 --- /dev/null +++ b/KruacentExiled/KE.Map/ShowPos.cs @@ -0,0 +1,33 @@ +using CommandSystem; +using Exiled.API.Features; +using KE.Map.Surface.Rooms; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map +{ + [CommandHandler(typeof(RemoteAdminCommandHandler))] + public class ShowPos : ICommand + { + public string Command => "showposition"; + + public string[] Aliases => new string[] { "showpos" }; + + public string Description => "show pos"; + + public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) + { + Player player = Player.Get(sender); + + + response = player.Position.ToString() + "\n" + + player.CurrentRoom?.Position + "\n" + + (player.CurrentRoom?.Position - player.Position); + + return true; + } + } +} diff --git a/KruacentExiled/KE.Map/SpawnImage.cs b/KruacentExiled/KE.Map/SpawnImage.cs new file mode 100644 index 00000000..e758b383 --- /dev/null +++ b/KruacentExiled/KE.Map/SpawnImage.cs @@ -0,0 +1,63 @@ +using CommandSystem; +using Exiled.API.Features; +using HintServiceMeow.Core.Enum; +using KE.Utils.API.Commands; +using KE.Utils.API.Displays.DisplayMeow; +using KE.Utils.API.Displays.DisplayMeow.Placements; +using KE.Utils.API.GifAnimator; +using LabApi.Features.Wrappers; +using System; +using System.Collections.Generic; +using System.Drawing; +using UnityEngine; +using Player = Exiled.API.Features.Player; + +namespace KE.Map +{ + [CommandHandler(typeof(RemoteAdminCommandHandler))] + public class SpawnImage : KECommand + { + public override string Command => "spawnimage"; + + public override string[] Aliases => new string[0]; + + public override string Description => "image"; + + public override string[] Usage => new string[0]; + + public override bool ExecuteCommand(ArraySegment arguments, ICommandSender sender, out string response) + { + if(!Player.TryGet(sender, out Player player)) + { + response = "player not foudn"; + return false; + } + + + + Image img = Image.FromFile(Paths.Configs + "/ome.png"); + TextImage textimg = new TextImage(img,20); + Log.Debug(textimg.RawString.Length); + + + + DisplayHandler.Instance.AddHint(position.HintPlacement, player, textimg.RawString, 30); + + + response = "ok"; + return true; + } + + private TestHintPosition position = new TestHintPosition(); + } + + + public class TestHintPosition : HintPosition + { + public override float Xposition => 0; + + public override float Yposition => 500; + + public override HintAlignment HintAlignment => HintAlignment.Center; + } +} diff --git a/KruacentExiled/KE.Map/Surface/BlinkingBlocks/BlinkingBlock.cs b/KruacentExiled/KE.Map/Surface/BlinkingBlocks/BlinkingBlock.cs new file mode 100644 index 00000000..aa790d5f --- /dev/null +++ b/KruacentExiled/KE.Map/Surface/BlinkingBlocks/BlinkingBlock.cs @@ -0,0 +1,86 @@ +using Exiled.API.Features.Doors; +using Exiled.API.Features.Toys; +using Exiled.API.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using VoiceChat.Networking; + +namespace KE.Map.Surface.BlinkingBlocks +{ + public class BlinkingBlock : IWorldSpace + { + + + public Vector3 Position { get; } + + public Quaternion Rotation { get; } + + public BlockColor BlockColor { get; } + + + + + private HashSet _list = new HashSet(); + private HashSet outline = new HashSet(); + public BlinkingBlock(Vector3 pos, Quaternion rotation, Vector3 scale, BlockColor color) + { + Position = pos; + Rotation = rotation; + BlockColor = color; + _list.Add(Primitive.Create(PrimitiveType.Cube, pos, rotation.eulerAngles, scale, false, BlockColorToColor(color))); + } + + + + public void Switch(BlockColor newColor) + { + foreach(Primitive p in _list) + { + if (newColor == BlockColor) + { + p.Spawn(); + } + else + { + p.UnSpawn(); + } + + } + + foreach(Primitive p in outline) + { + if (newColor == BlockColor) + { + p.UnSpawn(); + } + else + { + p.Spawn(); + } + } + } + + public static Color BlockColorToColor(BlockColor c) + { + return c switch + { + BlockColor.Blue => Color.blue, + BlockColor.Red => Color.red, + _ => Color.red + }; + } + + + + } + public enum BlockColor + { + Red, + Blue + } + +} diff --git a/KruacentExiled/KE.Map/Surface/BlinkingBlocks/BlinkingBlocksGroup.cs b/KruacentExiled/KE.Map/Surface/BlinkingBlocks/BlinkingBlocksGroup.cs new file mode 100644 index 00000000..ce241f34 --- /dev/null +++ b/KruacentExiled/KE.Map/Surface/BlinkingBlocks/BlinkingBlocksGroup.cs @@ -0,0 +1,64 @@ +using Exiled.API.Features; +using Exiled.API.Interfaces; +using Exiled.Events.EventArgs.Player; +using KE.Utils.API.Sounds; +using MEC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Map.Surface.BlinkingBlocks +{ + public class BlinkingBlocksGroup : IPosition + { + private AudioPlayer player; + public Vector3 Position { get; } + public BlockColor Color { get; private set; } + + private HashSet _list; + public BlinkingBlocksGroup(IEnumerable blocks) + { + _list = new HashSet(blocks); + + Vector3 center = new Vector3(_list.Average(x => x.Position.x), _list.Average(x => x.Position.y),_list.Average(x => x.Position.z)); + + + + var a = new GameObject(); + a.transform.position = center; + Timing.RunCoroutine(Switch()); + KE.Utils.API.Sounds.SoundPlayer.Instance.Play("beep", a, 50, 50); + } + + private BlockColor GetNewColor() + { + if(Color == BlockColor.Red) + { + return BlockColor.Blue; + } + else + { + return BlockColor.Red; + } + } + + private IEnumerator Switch() + { + while (true) + { + + yield return Timing.WaitForSeconds(5); + foreach (BlinkingBlock b in _list) + { + b.Switch(Color); + } + //Log.Debug("switch to "+Color.ToString()); + Color = GetNewColor(); + } + } + + } +} diff --git a/KruacentExiled/KE.Map/Surface/ElevatorGateA/CustomElevatorComp.cs b/KruacentExiled/KE.Map/Surface/ElevatorGateA/CustomElevatorComp.cs new file mode 100644 index 00000000..fe403036 --- /dev/null +++ b/KruacentExiled/KE.Map/Surface/ElevatorGateA/CustomElevatorComp.cs @@ -0,0 +1,104 @@ +using Exiled.API.Features.Objectives; +using Exiled.API.Features.Toys; +using KE.Utils.Quality.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Map.Surface.ElevatorGateA +{ + internal class CustomElevatorComp : MonoBehaviour + { + private Primitive Primitive; + + private HashSet Models; + + public void Init(Primitive primitive,IEnumerable models) + { + Primitive = primitive; + Models = models.ToHashSet(); + + baseheight = Primitive.Position.y; + objective = increase; + + startPos = Primitive.Position; + endPos = new Vector3(startPos.x, objective, startPos.z); + } + + private float baseheight; + private float increase = 301f; + private float objective; + private float duration = 5f; + private bool isMoving = false; + + + private float startY; + private float elapsed; + public void Send() + { + if (isMoving) return; + if (Primitive == null) return; + + isMoving = true; + + ChangeAllPanel(Color.yellow); + + startY = Primitive.Position.y; + elapsed = 0f; + startPos = Primitive.Position; + endPos = new Vector3(startPos.x, objective, startPos.z); + } + + private void ChangeAllPanel(Color color) + { + foreach(IContainPanel model in Models) + { + model.ChangeColor(color); + } + } + private Vector3 startPos; + private Vector3 endPos; + private float sendInterval = 1f / 240f; + private float sendTimer = 0f; + public void Update() + { + if (!isMoving) return; + + elapsed += Time.deltaTime; + sendTimer += Time.deltaTime; + float t = elapsed / duration; + t = t * t * (3f - 2f * t); // smoothstep + + + if(sendTimer >= sendInterval) + { + sendTimer = 0f; + Primitive.Position = Vector3.Lerp(startPos, endPos, t); + } + + + + if (elapsed >= duration) + { + Primitive.Position = new Vector3( + Primitive.Position.x, + objective, + Primitive.Position.z + ); + + objective = Mathf.Approximately(objective, baseheight) + ? increase + : baseheight; + + ChangeAllPanel(Color.blue); + + isMoving = false; + } + } + + + } +} diff --git a/KruacentExiled/KE.Map/Surface/ElevatorGateA/CustomElevatorGateA.cs b/KruacentExiled/KE.Map/Surface/ElevatorGateA/CustomElevatorGateA.cs new file mode 100644 index 00000000..2baac220 --- /dev/null +++ b/KruacentExiled/KE.Map/Surface/ElevatorGateA/CustomElevatorGateA.cs @@ -0,0 +1,175 @@ +using Exiled.API.Features; +using Exiled.API.Features.Objectives; +using Exiled.API.Features.Toys; +using Exiled.Events.EventArgs.Player; +using InventorySystem.Items.Firearms.Modules.Scp127; +using KE.Map.Others.CustomElevators.KECustomElevators; +using KE.Utils.API.Features; +using MEC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Map.Surface.ElevatorGateA +{ + public static class CustomElevatorGateA + { + + private static ElevatorModel model; + private static Primitive prim; + + + private static Primitive step; + private static Primitive help; + + private static Panel toppanel; + private static Primitive primtop; + private static Panel bottompanel; + private static Primitive primbottom; + private static Primitive helpPlatform; + + private const float Scale = 0.8f; + public static void Create() + { + //Vector3 pos = new(18.24f, 290.65f, -46.07f); + //prim = Primitive.Create(pos, null, Vector3.one * Scale); + //prim.Flags = AdminToys.PrimitiveFlags.None; + + + //prim.GameObject.AddComponent(); + + try + { + Destroy(); + } + catch(Exception e) + { + Log.Error(e); + } + + model = new ElevatorModel(); + + toppanel = new Panel(); + bottompanel = new Panel(); + + CreatePrimitives(); + + CreateModels(); + step = Primitive.Create(PrimitiveType.Cube, new Vector3(18.4f, 300.35f, -49.27f),null,new Vector3(2,.5f,1f)); + + + model.SendingElevator += SendingElevator; + bottompanel.SendingElevator += SendingElevator; + toppanel.SendingElevator += SendingElevator; + } + + private static void CreateModels() + { + KELog.Debug("creating elevator"); + model.Create(prim.Transform); + KELog.Debug("creating top panel"); + toppanel.Create(primtop.Transform); + KELog.Debug("creating bototm panel"); + bottompanel.Create(primbottom.Transform); + + + model.button.NetworkMaterialColor = Color.blue; + bottompanel.button.NetworkMaterialColor = Color.blue; + toppanel.button.NetworkMaterialColor = Color.blue; + } + + private static void CreatePrimitives() + { + Vector3 pos = new Vector3(18.24f, 290.65f, -46.07f); + Vector3 helppos = new Vector3(18.24f, 255.65f, -46.07f); + Vector3 postop = new Vector3(19.92f, 299.97f, -48.95f); + Vector3 posbot = new Vector3(15.62f, 290.65f, -45.19f); + prim = Primitive.Create(pos, null, Vector3.one * Scale); + prim.Flags = AdminToys.PrimitiveFlags.None; + prim.MovementSmoothing = 0; + primtop = Primitive.Create(postop, null, Vector3.one * Scale); + primtop.Flags = AdminToys.PrimitiveFlags.None; + + primbottom = Primitive.Create(posbot, null, Vector3.one * Scale); + primbottom.Flags = AdminToys.PrimitiveFlags.None; + + + help = Primitive.Create(PrimitiveType.Cube,helppos,null,new Vector3(50,50,50),false); + help.Flags = AdminToys.PrimitiveFlags.Visible; + help.Spawn(); + help.GameObject.AddComponent(); + + helpPlatform = Primitive.Create(PrimitiveType.Cube, helppos, null, new Vector3(50, 1, 50), false); + helpPlatform.Flags = AdminToys.PrimitiveFlags.Visible | AdminToys.PrimitiveFlags.Collidable; + helpPlatform.Spawn(); + } + + private static void SendingElevator() + { + Send(); + } + + public static void Destroy() + { + if(CheckPrimitive(prim)) + { + model.SendingElevator -= SendingElevator; + model.Destroy(prim.Transform); + prim = null; + } + + if(CheckPrimitive(step)) + { + step.Destroy(); + step = null; + } + if (CheckPrimitive(help)) + { + help.Destroy(); + help = null; + } + if (CheckPrimitive(helpPlatform)) + { + helpPlatform.Destroy(); + helpPlatform = null; + } + + + + if (CheckPrimitive(primbottom)) + { + + bottompanel.SendingElevator -= SendingElevator; + bottompanel.Destroy(primbottom.Transform); + primbottom = null; + } + + if(CheckPrimitive(primtop)) + { + toppanel.SendingElevator -= SendingElevator; + toppanel.Destroy(primtop.Transform); + + primtop = null; + } + } + + private static bool CheckPrimitive(Primitive primitive) + { + return primitive != null && primitive.Base != null && primitive.GameObject != null; + } + + public static void Send() + { + if (prim == null) return; + if (!prim.GameObject.TryGetComponent(out var comp)) + { + comp = prim.GameObject.AddComponent(); + comp.Init(prim, new List() { model, bottompanel, toppanel }); + } + comp.Send(); + } + } +} diff --git a/KruacentExiled/KE.Map/Surface/ElevatorGateA/CustomKillerCollision.cs b/KruacentExiled/KE.Map/Surface/ElevatorGateA/CustomKillerCollision.cs new file mode 100644 index 00000000..b224e5e8 --- /dev/null +++ b/KruacentExiled/KE.Map/Surface/ElevatorGateA/CustomKillerCollision.cs @@ -0,0 +1,46 @@ +using CustomPlayerEffects; +using Exiled.API.Enums; +using Exiled.API.Features; +using ProjectMER.Commands.Modifying.Scale; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using UnityEngine.PlayerLoop; + +namespace KE.Map.Surface.ElevatorGateA +{ + public class CustomKillerCollision : MonoBehaviour + { + // use OnTriggerEnter instead + private Collider[] NonAlloc = new Collider[8]; + + + private void Update() + { + if(Physics.OverlapBoxNonAlloc(base.transform.localPosition, base.transform.localScale / 2f, NonAlloc, transform.localRotation, (int)LayerMasks.Hitbox) > 0) + { + for (int i = 0; i < NonAlloc.Length; i++) + { + if(Player.TryGet(NonAlloc[i],out Player player)) + { + if (PitDeath.ValidatePlayer(player.ReferenceHub)) + { + PitDeath pit = player.GetEffect(); + //DO NOT USE PitDeath.KillPlayer() + if (!pit.IsEnabled) + { + pit.IsEnabled = true; + } + } + } + } + } + + + } + + } +} diff --git a/KruacentExiled/KE.Map/Surface/ElevatorGateA/ElevatorModel.cs b/KruacentExiled/KE.Map/Surface/ElevatorGateA/ElevatorModel.cs new file mode 100644 index 00000000..b78a5cda --- /dev/null +++ b/KruacentExiled/KE.Map/Surface/ElevatorGateA/ElevatorModel.cs @@ -0,0 +1,385 @@ +using AdminToys; +using Exiled.API.Features.Toys; +using KE.Utils.API.Features.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using UserSettings.ServerSpecific; + +namespace KE.Map.Surface.ElevatorGateA +{ + public class ElevatorModel : ModelBase, IContainPanel + { + public InteractableToy I_button { get; private set; } + public PrimitiveObjectToy button { get; private set; } + protected override void CreateModel(Transform parent) + { + // This code was auto-generated + + var ElevatorCabin = CreatePrimitive( + parent, + PrimitiveType.Sphere, + new Vector3(0f, 0f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(1f, 1f, 1f), + new Color32(0, 0, 0, 0), + 0 + ); + ElevatorCabin.PrimitiveFlags = AdminToys.PrimitiveFlags.None; + + var panel = CreatePrimitive( + ElevatorCabin.transform, + PrimitiveType.Sphere, + new Vector3(0f, 0f, 1.663f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(1f, 1f, 1f), + new Color32(0, 0, 0, 0), + 0 + ); + panel.PrimitiveFlags = AdminToys.PrimitiveFlags.None; + + var poteau = CreatePrimitive( + panel.transform, + PrimitiveType.Cube, + new Vector3(0f, 0.757f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(0.15f, 1.3f, 0.15f), + new Color32(255, 255, 255, 255), + 0 + + ); + + var panneau = CreatePrimitive( + panel.transform, + PrimitiveType.Cube, + new Vector3(0f, 1.397f, 0f), + new Quaternion(0.3775984f, 0f, 0f, 0.9259695f), + new Vector3(1f, 1f, 0.15f), + new Color32(255, 255, 255, 255), + 0 + + ); + + button = CreatePrimitive( + panel.transform, + PrimitiveType.Cube, + new Vector3(0f, 1.419f, 0f), + new Quaternion(0.3775984f, 0f, 0f, 0.9259695f), + new Vector3(0.8f, 0.8f, 0.15f), + new Color32(255, 255, 255, 255), + 0 + + ); + + I_button = CreateInteractable( + panel.transform, + new Vector3(0f, 1.46f, -0.042f), + new Quaternion(0.3775984f, 0f, 0f, 0.9259695f), + new Vector3(0.8f, 0.8f, 0.15f), + InvisibleInteractableToy.ColliderShape.Box + ); + + var top = CreatePrimitive( + ElevatorCabin.transform, + PrimitiveType.Sphere, + new Vector3(0f, 5f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(1f, 1f, 1f), + new Color32(0, 0, 0, 0) + ); + top.PrimitiveFlags = AdminToys.PrimitiveFlags.None; + + var right = CreatePrimitive( + top.transform, + PrimitiveType.Cube, + new Vector3(1.666667f, 0f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(1.666667f, 0.2f, 5f), + new Color32(255, 255, 255, 255), + 0 + + ); + + var left = CreatePrimitive( + top.transform, + PrimitiveType.Cube, + new Vector3(-1.666667f, 0f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(1.666667f, 0.2f, 5f), + new Color32(255, 255, 255, 255), + 0 + + ); + + var up = CreatePrimitive( + top.transform, + PrimitiveType.Cube, + new Vector3(0f, 0f, 1.666667f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(1.666667f, 0.2f, 1.666667f), + new Color32(255, 255, 255, 255), + 0 + + ); + + var down = CreatePrimitive( + top.transform, + PrimitiveType.Cube, + new Vector3(0f, 0f, -1.666667f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(1.666667f, 0.2f, 1.666667f), + new Color32(255, 255, 255, 255), + 0 + + ); + + var Spot_Light = CreatePrimitive( + top.transform, + PrimitiveType.Sphere, + new Vector3(0f, -0.18f, 1.57f), + new Quaternion(0.7071068f, 0f, 0f, 0.7071068f), + new Vector3(1f, 1f, 1f), + new Color32(0, 0, 0, 0) + ); + Spot_Light.PrimitiveFlags = AdminToys.PrimitiveFlags.None; + + var bottom = CreatePrimitive( + ElevatorCabin.transform, + PrimitiveType.Sphere, + new Vector3(0f, 0f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(1f, 1f, 1f), + new Color32(0, 0, 0, 0) + ); + bottom.PrimitiveFlags = AdminToys.PrimitiveFlags.None; + + var floor = CreatePrimitive( + bottom.transform, + PrimitiveType.Cube, + new Vector3(0f, 0f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(5f, 0.2f, 5f), + new Color32(255, 255, 255, 255), + 0 + + ); + + var fence = CreatePrimitive( + bottom.transform, + PrimitiveType.Sphere, + new Vector3(0f, 0f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(1f, 1f, 1f), + new Color32(0, 0, 0, 0) + ); + fence.PrimitiveFlags = AdminToys.PrimitiveFlags.None; + + var pillar1 = CreatePrimitive( + fence.transform, + PrimitiveType.Cube, + new Vector3(-2.3f, 2.5f, -2.3f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(0.1f, 4.9f, 0.1f), + new Color32(255, 255, 255, 255), + 0 + + ); + + var pillar2 = CreatePrimitive( + fence.transform, + PrimitiveType.Cube, + new Vector3(-2.3f, 0.79f, -0.9f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(0.1f, 1.4798f, 0.1f), + new Color32(255, 255, 255, 255), + 0 + + ); + + var pillar3 = CreatePrimitive( + fence.transform, + PrimitiveType.Cube, + new Vector3(-2.3f, 0.79f, 0.9f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(0.1f, 1.4798f, 0.1f), + new Color32(255, 255, 255, 255), + 0 + + ); + + var pillar4 = CreatePrimitive( + fence.transform, + PrimitiveType.Cube, + new Vector3(-2.3f, 2.5f, 2.3f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(0.1f, 4.9f, 0.1f), + new Color32(255, 255, 255, 255), + 0 + + ); + + var pillar5 = CreatePrimitive( + fence.transform, + PrimitiveType.Cube, + new Vector3(2.3f, 2.5f, -2.3f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(0.1f, 4.9f, 0.1f), + new Color32(255, 255, 255, 255), + 0 + + ); + + var pillar6 = CreatePrimitive( + fence.transform, + PrimitiveType.Cube, + new Vector3(2.3f, 0.79f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(0.1f, 1.4798f, 0.1f), + new Color32(255, 255, 255, 255), + 0 + + ); + + var pillar8 = CreatePrimitive( + fence.transform, + PrimitiveType.Cube, + new Vector3(2.3f, 2.5f, 2.3f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(0.1f, 4.9f, 0.1f), + new Color32(255, 255, 255, 255), + 0 + + ); + + var pillar9 = CreatePrimitive( + fence.transform, + PrimitiveType.Cube, + new Vector3(0f, 0.79f, 2.3f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(0.1f, 1.4798f, 0.1f), + new Color32(255, 255, 255, 255), + 0 + + ); + + var pillar10 = CreatePrimitive( + fence.transform, + PrimitiveType.Cube, + new Vector3(-0.9f, 0.79f, -2.3f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(0.1f, 1.4798f, 0.1f), + new Color32(255, 255, 255, 255), + 0 + + ); + + var pillar11 = CreatePrimitive( + fence.transform, + PrimitiveType.Cube, + new Vector3(0.9f, 0.79f, -2.3f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(0.1f, 1.4798f, 0.1f), + new Color32(255, 255, 255, 255), + 0 + + ); + + var fenceback = CreatePrimitive( + fence.transform, + PrimitiveType.Cube, + new Vector3(0f, 1.439f, 2.3f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(5f, 0.2f, 0.2f), + new Color32(255, 255, 255, 255), + 0 + + ); + + var fenceright = CreatePrimitive( + fence.transform, + PrimitiveType.Cube, + new Vector3(2.3f, 1.439f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(0.2f, 0.2f, 5f), + new Color32(255, 255, 255, 255), + 0 + + ); + + var fenceleft1 = CreatePrimitive( + fence.transform, + PrimitiveType.Cube, + new Vector3(-2.3f, 1.439f, 1.6f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(0.2f, 0.2f, 1.7f), + new Color32(255, 255, 255, 255), + 0 + + ); + + var fenceleft2 = CreatePrimitive( + fence.transform, + PrimitiveType.Cube, + new Vector3(-2.3f, 1.439f, -1.6f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(0.2f, 0.2f, 1.7f), + new Color32(255, 255, 255, 255), + 0 + + ); + + var fencefront1 = CreatePrimitive( + fence.transform, + PrimitiveType.Cube, + new Vector3(1.6f, 1.439f, -2.35f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(1.7f, 0.2f, 0.2f), + new Color32(255, 255, 255, 255), + 0 + + ); + + var fencefront2 = CreatePrimitive( + fence.transform, + PrimitiveType.Cube, + new Vector3(-1.6f, 1.439f, -2.3f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(1.7f, 0.2f, 0.2f), + new Color32(255, 255, 255, 255), + 0 + + ); + + var waypoint = CreateWaypoint( + ElevatorCabin.transform, + new Vector3(0f, 3.96f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(4.9f, 7.870179f, 4.9f) + ); + waypoint.VisualizeBounds = false; + I_button.Base.OnInteracted += Base_OnInteracted; + } + + public override void Destroy(Transform parent) + { + I_button.Base.OnInteracted -= Base_OnInteracted; + base.Destroy(parent); + } + + + public event Action SendingElevator = delegate { }; + + private void Base_OnInteracted(ReferenceHub obj) + { + SendingElevator?.Invoke(); + } + + public void ChangeColor(Color newColor) + { + button.NetworkMaterialColor = newColor; + } + } +} diff --git a/KruacentExiled/KE.Map/Surface/ElevatorGateA/IContainPanel.cs b/KruacentExiled/KE.Map/Surface/ElevatorGateA/IContainPanel.cs new file mode 100644 index 00000000..480be48b --- /dev/null +++ b/KruacentExiled/KE.Map/Surface/ElevatorGateA/IContainPanel.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Map.Surface.ElevatorGateA +{ + internal interface IContainPanel + { + + + void ChangeColor(Color newColor); + } +} diff --git a/KruacentExiled/KE.Map/Surface/ElevatorGateA/Panel.cs b/KruacentExiled/KE.Map/Surface/ElevatorGateA/Panel.cs new file mode 100644 index 00000000..0178518b --- /dev/null +++ b/KruacentExiled/KE.Map/Surface/ElevatorGateA/Panel.cs @@ -0,0 +1,88 @@ +using AdminToys; +using Exiled.API.Features.Toys; +using KE.Utils.API.Features.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using UserSettings.ServerSpecific; + +namespace KE.Map.Surface.ElevatorGateA +{ + public class Panel : ModelBase, IContainPanel + { + public InteractableToy I_button { get; private set; } + public PrimitiveObjectToy button { get; private set; } + protected override void CreateModel(Transform parent) + { + // This code was auto-generated + var panel = CreatePrimitive( + parent, + PrimitiveType.Sphere, + new Vector3(0f, 0f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(1f, 1f, 1f), + new Color32(0, 0, 0, 0) + ); + panel.PrimitiveFlags = AdminToys.PrimitiveFlags.None; + + var poteau = CreatePrimitive( + panel.transform, + PrimitiveType.Cube, + new Vector3(0f, 0.757f, 0f), + new Quaternion(0f, 0f, 0f, 1f), + new Vector3(0.15f, 1.3f, 0.15f), + new Color32(255, 255, 255, 255) + ); + + var panneau = CreatePrimitive( + panel.transform, + PrimitiveType.Cube, + new Vector3(0f, 1.397f, 0f), + new Quaternion(0.3775984f, 0f, 0f, 0.9259695f), + new Vector3(1f, 1f, 0.15f), + new Color32(255, 255, 255, 255) + ); + + button = CreatePrimitive( + panel.transform, + PrimitiveType.Cube, + new Vector3(0f, 1.419f, 0f), + new Quaternion(0.3775984f, 0f, 0f, 0.9259695f), + new Vector3(0.8f, 0.8f, 0.15f), + new Color32(255, 255, 255, 255) + ); + + I_button = CreateInteractable( + panel.transform, + new Vector3(0f, 1.46f, -0.042f), + new Quaternion(0.3775984f, 0f, 0f, 0.9259695f), + new Vector3(0.8f, 0.8f, 0.15f), + InvisibleInteractableToy.ColliderShape.Box + ); + I_button.Base.OnInteracted += Base_OnInteracted; + } + + public override void Destroy(Transform parent) + { + I_button.Base.OnInteracted -= Base_OnInteracted; + base.Destroy(parent); + } + + + + public event Action SendingElevator = delegate { }; + + private void Base_OnInteracted(ReferenceHub obj) + { + SendingElevator?.Invoke(); + } + + public void ChangeColor(Color newColor) + { + button.NetworkMaterialColor = newColor; + } + } +} diff --git a/KruacentExiled/KE.Map/Surface/Rooms/SurfaceArmory.cs b/KruacentExiled/KE.Map/Surface/Rooms/SurfaceArmory.cs new file mode 100644 index 00000000..4b1d56f5 --- /dev/null +++ b/KruacentExiled/KE.Map/Surface/Rooms/SurfaceArmory.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace KE.Map.Surface.Rooms +{ + public class SurfaceArmory : SurfaceRoom + { + protected override string RoomName => "Armory"; + + + + + } +} diff --git a/KruacentExiled/KE.Map/Surface/Rooms/SurfaceRoom.cs b/KruacentExiled/KE.Map/Surface/Rooms/SurfaceRoom.cs new file mode 100644 index 00000000..e2dcb27f --- /dev/null +++ b/KruacentExiled/KE.Map/Surface/Rooms/SurfaceRoom.cs @@ -0,0 +1,17 @@ +using HintServiceMeow.UI.Utilities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map.Surface.Rooms +{ + public abstract class SurfaceRoom + { + public const string ZoneName = "Surface"; + protected abstract string RoomName { get; } + public string Name => ZoneName + RoomName; + + } +} diff --git a/KruacentExiled/KE.Map/Surface/Rooms/SurfaceRooms.cs b/KruacentExiled/KE.Map/Surface/Rooms/SurfaceRooms.cs new file mode 100644 index 00000000..0cb59618 --- /dev/null +++ b/KruacentExiled/KE.Map/Surface/Rooms/SurfaceRooms.cs @@ -0,0 +1,52 @@ +using Exiled.API.Extensions; +using Exiled.API.Features; +using KE.Utils.API.Interfaces; +using Mirror; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Map.Surface.Rooms +{ + public class SurfaceRooms : IUsingEvents + { + + public static GameObject gameObject; + public static readonly HashSet Rooms = new HashSet() + { + new SurfaceArmory() + }; + public void SubscribeEvents() + { + //Exiled.Events.Handlers.Map.Generated += OnGenerated; + } + + public void UnsubscribeEvents() + { + //Exiled.Events.Handlers.Map.Generated -= OnGenerated; + + } + + private void OnGenerated() + { + + + Vector3 roomPos = Room.List.FirstOrDefault(x => x.Type == Exiled.API.Enums.RoomType.Surface).Position- new Vector3(-137, 4, 60); + SpawnRandomRoom(roomPos); + + + } + + + private void SpawnRandomRoom(Vector3 position) + { + SurfaceRoom bp = Rooms.GetRandomValue(); + + NetworkServer.Spawn(gameObject); + } + + } +} diff --git a/KruacentExiled/KE.Map/Surface/SupplyDrops/SupplyDrop.cs b/KruacentExiled/KE.Map/Surface/SupplyDrops/SupplyDrop.cs new file mode 100644 index 00000000..7ac559d5 --- /dev/null +++ b/KruacentExiled/KE.Map/Surface/SupplyDrops/SupplyDrop.cs @@ -0,0 +1,269 @@ +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Items; +using Exiled.API.Features.Toys; +using Exiled.API.Interfaces; +using KE.Utils.API; +using MEC; +using PlayerRoles; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using UnityEngine; + +namespace KE.Map.Surface.SupplyDrops +{ + public class SupplyDrop : IPosition + { + + public static bool IsActivated => MainPlugin.Configs.SupplyDropEnabled; + + private static byte _scpSteal = 0; + + public const byte ScpStealLimit = 3; + public const byte MaxSupplyDrop = 10; + public static readonly TimeSpan TimeStaying = new TimeSpan(0, 1, 0); + public static readonly TimeSpan TimeSpawn = new TimeSpan(0, 5, 0); + public const float Radius = 7f; + public const float RefreshRate = .1f; + + public bool Show + { + get + { + DayOfWeek day = DateTime.Now.DayOfWeek; + + if (day == DayOfWeek.Saturday) + return true; + //starting at monday + int idDay = (int)day +1; + + if ( idDay % 2 == 0 && NumberDrop % 2 != 0) + { + return true; + } + if (idDay % 2 != 0 && NumberDrop % 2 == 0) + { + return true; + } + + return false; + } + } + public Vector3 Position { get; } + public int NumberDrop => list.FindIndex(s => s == this); + + private static Stopwatch _spawnTime; + private static TimeSpan _nextSpawn; + private static List list = new List(); + public static IReadOnlyCollection SpawnPositions = new List() + { + new Vector3(-15,292,-39), //spawn chaos + new Vector3(40,301,-52), // above the gate + new Vector3(138,295,-64), //behind mtf spawn at the unopenable gate + new Vector3(124,289,22) //escape + }; + private HashSet primitives = new HashSet(); + private bool _detectingSomeone = false; + + public RoleTypeId SideClaimed { get; private set; } = RoleTypeId.None; + public Player PlayerClaimed { get; private set; } + + public static readonly string CassieMessageDrop = "Drop in surface"; + public static readonly string CassieTooMuchStealing = "Too Much Supplys Taken By SCPs"; + public static readonly string CassieTooMuchStealingSub = "Too Much Supplies Taken By SCPs"; + + private static SupplyDrop CurrentDrop = null; + private static CoroutineHandle _handle; + public SupplyDrop(Vector3 position) + { + list.Add(this); + Position = position; + + //Model + primitives.Add(Primitive.Create(PrimitiveType.Cube,position,null,Vector3.one,false)); + //radius of pickup + var pr = Primitive.Create(PrimitiveType.Sphere, Position, null, new Vector3(Radius, Radius, Radius), false, new Color(0, 1, 0, .30f)); + pr.Collidable = false; + primitives.Add(pr); + + + if (Show) + { + //Cassie.Message(CassieMessageDrop); + } + + foreach (var p in primitives) + { + p.Spawn(); + } + CurrentDrop = this; + Timing.RunCoroutine(Detecting()); + } + + + + + public static void SubscribeEvents() + { + if (!IsActivated) return; + + Exiled.Events.Handlers.Server.RoundStarted += OnRoundStarted; + } + + public static void UnsubscribeEvents() + { + if (!IsActivated) return; + Exiled.Events.Handlers.Server.RoundStarted -= OnRoundStarted; + } + + public static void OnRoundStarted() + { + _spawnTime = Stopwatch.StartNew(); + _nextSpawn = TimeSpawn; + + _handle = Timing.RunCoroutine(Loop()); + } + + private static IEnumerator Loop() + { + + bool notmax = true; + while (notmax) + { + if (_spawnTime.Elapsed > _nextSpawn) + { + _nextSpawn += TimeSpawn; + if(CurrentDrop == null) + { + SpawnRandom(); + } + + Log.Info("next spawn " + _nextSpawn); + } + notmax = list.Count <= MaxSupplyDrop; + yield return Timing.WaitForSeconds(RefreshRate); + } + } + + private static void SpawnRandom() + { + //Todo random lol + Vector3 spawnloc = SpawnPositions.GetRandomValue(); + Log.Info($"spawning drop at {spawnloc}"); + SupplyDrop soup = new SupplyDrop(spawnloc); + + + } + + public void Destroy() + { + float timeExplode = 10; + var grenade = (ExplosiveGrenade)Item.Create(ItemType.GrenadeHE); + grenade.ScpDamageMultiplier = 1; + grenade.BurnDuration = timeExplode; + grenade.SpawnActive(Position + Vector3.up); + + foreach (var p in primitives) + { + p.Destroy(); + } + CurrentDrop = null; + + + } + + private IEnumerator Detecting() + { + var s = Stopwatch.StartNew(); + var playerAlreadyIn = Player.List.Where(p => InRadius(p)).ToHashSet(); + + _detectingSomeone = true; + while (_detectingSomeone && s.Elapsed < TimeStaying) + { + yield return Timing.WaitForSeconds(RefreshRate); + foreach (Player p in Player.List.Where(p=> p.IsAlive && p.Role != RoleTypeId.Scp106)) + { + if (!playerAlreadyIn.Contains(p) && InRadius(p)) + { + Effect(p); + _detectingSomeone = false; + + } + if (playerAlreadyIn.Contains(p)) + { + if (!InRadius(p)) + { + playerAlreadyIn.Remove(p); + } + } + } + } + _detectingSomeone = false; + Destroy(); + } + + private bool InRadius(Player p) => OtherUtils.IsInCircle(p.Position, Position, Radius / 2); + + + private void Effect(Player p) + { + Log.Debug($"Player {p.Id} got the supply drop"); + //todo add trapped drop (explode) + SideClaimed = p.Role; + PlayerClaimed = p; + + if (p.IsScp) + { + BuffScps(); + } + else + { + SpawnLoot(p); + } + + + } + + private void SpawnLoot(Player p) + { + Faction playerFaction = p.Role.Team.GetFaction(); + Respawn.GrantInfluence(playerFaction, 20); + Respawn.AdvanceTimer(playerFaction, 10); + + Log.Debug("human got it!"); + if (!p.HasItem(ItemType.GunCrossvec)) + { + p.AddItem(ItemType.GunCrossvec); + } + + p.AddAmmo(AmmoType.Nato9, 50); + } + + private void BuffScps() + { + Log.Debug("scps got it!"); + foreach(Player p in Player.List.Where(p => p.IsScp && p.Role != RoleTypeId.Scp106)) + { + float healthAdded = p.MaxHealth * 1.2f; + p.MaxHealth += healthAdded; + p.Health += healthAdded; + + } + _scpSteal++; + + if (_scpSteal >= ScpStealLimit) + { + //Cassie.MessageTranslated(CassieTooMuchStealing,CassieTooMuchStealingSub); + Warhead.Start(true, true); + _spawnTime.Stop(); + Timing.KillCoroutines(_handle); + } + } + + + + } +} diff --git a/KruacentExiled/KE.Map/Surface/Turrets/Turret.cs b/KruacentExiled/KE.Map/Surface/Turrets/Turret.cs new file mode 100644 index 00000000..316cb223 --- /dev/null +++ b/KruacentExiled/KE.Map/Surface/Turrets/Turret.cs @@ -0,0 +1,196 @@ +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Roles; +using Exiled.API.Features.Toys; +using Exiled.API.Interfaces; +using Exiled.Events.EventArgs.Server; +using Exiled.Events.Patches.Events.Player; +using KE.Utils.API; +using KE.Utils.Extensions; +using MapGeneration; +using MEC; +using PlayerRoles; +using PlayerRoles.FirstPersonControl; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Serialization; +using UnityEngine; +using YamlDotNet.Core.Tokens; + + +namespace KE.Map.Surface.Turrets +{ + public class Turret : IWorldSpace + { + public static bool IsEnabled => MainPlugin.Configs.TurretEnabled; + + private readonly Npc npc; + private readonly CoroutineHandle handle; + public float Range { get; private set; } = 10; + public int Id { get; } + public Player Player { get; private set; } + + public bool IsNeutral + { + get + { + return Player == null; + } + } + public Vector3 Position + { + get + { + return npc.Position; + } + set + { + Move(value); + } + } + public Quaternion Rotation + { + get + { + return npc.Rotation; + } + set + { + npc.Rotation = value; + } + } + + + + private static List list = new List(); + + private Turret(Player p,Vector3 position) + { + Log.Debug($"player {p.Nickname} spawned turret"); + Id = list.Count; + list.Add(this); + Player = p; + RoleTypeId role = p.Role; + + if (!role.IsFpcRole()) + { + role = RoleTypeId.ClassD; + } + + if (role.IsScp()) + { + role = RoleTypeId.Scp0492; + } + + + npc = Npc.Spawn("Turret" + Id, role, true, position); + Timing.CallDelayed(Npc.SpawnSetRoleDelay +Timing.WaitForOneFrame, () => npc.IsGodModeEnabled = true); + if (npc.Role is FpcRole fpc) + { + fpc.Gravity = Vector3.zero; + } + + npc.Hide(); + + + handle = Timing.RunCoroutine(Detect()); + } + + + + public static Turret Create(Player owner, Vector3 position) + { + if (!IsEnabled) return null; + + return new Turret(owner, position); + + + } + + + private IEnumerator Detect() + { + + while (Player.IsAlive) + { + //&& p.Role.Side != Player.Role.Side + List inRange = Player.List.Where(p => OtherUtils.IsInCircle(p.Position, Position, Range) && !p.IsNPC ).OrderBy(p => Vector3.Distance(p.Position,Position)).ToList(); + Player closest; + if(inRange.TryGet(0,out closest)) + { + Log.Debug("player closest " + closest); + + Rotation = Quaternion.LookRotation(closest.Position - Position); + + + RaycastHit hit; + if (UnityEngine.Physics.Linecast(Position, closest.Position, out hit)) + { + Player playerhit = Player.Get(hit.collider); + Log.Debug("hit = " + playerhit?.Nickname); + if (playerhit != null) + { + + playerhit.Hurt(10); + } + } + + + + + yield return Timing.WaitForSeconds(1); + } + else + { + Log.Debug("no player nearby"); + yield return Timing.WaitForSeconds(5); + } + } + } + + public void Move(Vector3 newPosition) + { + npc.Teleport(newPosition); + + } + + + + public void Destroy() + { + npc.Destroy(); + } + + private static void OnEndingRound(EndingRoundEventArgs ev) + { + if (ev.IsAllowed) + { + DestroyAll(); + } + } + + public static void DestroyAll() + { + foreach (Turret turret in list) + { + turret.Destroy(); + } + } + + public static void SubscribeEvents() + { + if (!IsEnabled) return; + Exiled.Events.Handlers.Server.EndingRound += OnEndingRound; + } + + public static void UnsubscribeEvents() + { + if (!IsEnabled) return; + DestroyAll(); + Exiled.Events.Handlers.Server.EndingRound -= OnEndingRound; + + } + + } +} diff --git a/KruacentExiled/KE.Map/Translations.cs b/KruacentExiled/KE.Map/Translations.cs new file mode 100644 index 00000000..fd3fad11 --- /dev/null +++ b/KruacentExiled/KE.Map/Translations.cs @@ -0,0 +1,35 @@ +using Exiled.API.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map +{ + public class Translations : ITranslation + { + + public string Blackout { get; set; } = "Warning Failure Of All Lights In"; + public string BlackoutTranslation { get; set; } = "Warning Failure Of All Lights In"; + public string Doorstuck { get; set; } = "Warning Failure Of All Doors In"; + public string DoorstuckTranslation { get; set; } = "Warning Failure Of All Doors In"; + public string Both { get; set; } = "Warning Failure Of All Systems In"; + public string BothTranslation { get; set; } = "Warning Failure Of All Systems In"; + + + public string LightContainment { get; set; } = "Light Containment Zone"; + public string LightContainmentTranslation { get; set; } = "Light Containment Zone"; + public string HeavyContainment { get; set; } = "Heavy Containment Zone"; + public string HeavyContainmentTranslation { get; set; } = "Heavy Containment Zone"; + public string EntranceZone { get; set; } = "Entrance Zone"; + public string EntranceZoneTranslation { get; set; } = "Entrance Zone"; + public string SurfaceZone { get; set; } = "Surface"; + public string SurfaceZoneTranslation { get; set; } = "Surface"; + + public string End { get; set; } = "In 5 seconds"; + + + + } +} diff --git a/KruacentExiled/KE.Map/Utils/Animations.cs b/KruacentExiled/KE.Map/Utils/Animations.cs new file mode 100644 index 00000000..34ad9f94 --- /dev/null +++ b/KruacentExiled/KE.Map/Utils/Animations.cs @@ -0,0 +1,25 @@ +using KE.Utils.API.Features.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Map.Utils +{ + public abstract class AnimatedModel : ModelBase + { + + protected List animationNames = new List(); + + + + + + + + + + + } +} diff --git a/KruacentExiled/KE.Map/Utils/StructureSpawner.cs b/KruacentExiled/KE.Map/Utils/StructureSpawner.cs new file mode 100644 index 00000000..3214b55d --- /dev/null +++ b/KruacentExiled/KE.Map/Utils/StructureSpawner.cs @@ -0,0 +1,177 @@ +using Interactables.Interobjects.DoorUtils; +using InventorySystem.Items.Firearms.Attachments; +using MapGeneration; +using MapGeneration.Distributors; +using Mirror; +using ProjectMER.Features; +using ProjectMER.Features.Enums; +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace KE.Map.Utils +{ + public static class StructureSpawner + { + public static Dictionary> AdditionalDoors { get; } = new Dictionary>(); + + + + public static DoorVariant GetDoorPrefab(DoorType doortype) + { + return doortype switch + { + DoorType.Lcz => PrefabManager.DoorLcz, + DoorType.Hcz => PrefabManager.DoorHcz, + DoorType.Ez => PrefabManager.DoorEz, + DoorType.Bulkdoor => PrefabManager.DoorHeavyBulk, + DoorType.Gate => PrefabManager.DoorGate, + _ => throw new InvalidOperationException(), + }; + } + + public static MapGeneration.Distributors.Locker LockerPrefab(LockerType lockertype) + { + return lockertype switch + { + LockerType.PedestalScp500 => PrefabManager.PedestalScp500, + LockerType.LargeGun => PrefabManager.LockerLargeGun, + LockerType.RifleRack => PrefabManager.LockerRifleRack, + LockerType.Misc => PrefabManager.LockerMisc, + LockerType.Medkit => PrefabManager.LockerRegularMedkit, + LockerType.Adrenaline => PrefabManager.LockerAdrenalineMedkit, + LockerType.PedestalScp018 => PrefabManager.PedestalScp018, + LockerType.PedestalScp207 => PrefabManager.PedstalScp207, + LockerType.PedestalScp244 => PrefabManager.PedestalScp244, + LockerType.PedestalScp268 => PrefabManager.PedestalScp268, + LockerType.PedestalScp1853 => PrefabManager.PedstalScp1853, + LockerType.PedestalScp2176 => PrefabManager.PedestalScp2176, + LockerType.PedestalScpScp1576 => PrefabManager.PedestalScp1576, + LockerType.PedestalAntiScp207 => PrefabManager.PedestalAntiScp207, + LockerType.PedestalScp1344 => PrefabManager.PedestalScp1344, + LockerType.ExperimentalWeapon => PrefabManager.LockerExperimentalWeapon, + _ => throw new InvalidOperationException(), + }; + } + + + public static DoorVariant SpawnDoor(DoorType doortype,Vector3 position,Quaternion rotation, Vector3 scale) + { + DoorVariant doorVariant = CreateRawDoor(doortype, position, rotation, scale, true); + FacilityZone zone = doorVariant.transform.position.GetZone(); + + if (!AdditionalDoors.ContainsKey(zone)) + { + AdditionalDoors.Add(zone, new HashSet()); + } + AdditionalDoors[zone].Add(doorVariant); + + + + return doorVariant; + } + + public static DoorVariant CreateRawDoor(DoorType doortype, Vector3 position, Quaternion rotation, Vector3 scale, bool spawn = false) + { + Vector3 absolutePosition = position; + Quaternion absoluteRotation = rotation; + DoorVariant doorVariant = UnityEngine.Object.Instantiate(GetDoorPrefab(doortype)); + if (doorVariant.TryGetComponent(out var component)) + { + UnityEngine.Object.Destroy(component); + } + + doorVariant.transform.SetPositionAndRotation(absolutePosition, absoluteRotation); + doorVariant.transform.localScale = scale; + doorVariant.NetworkTargetState = false; + doorVariant.ServerChangeLock(DoorLockReason.SpecialDoorFeature, false); + doorVariant.RequiredPermissions = new DoorPermissionsPolicy(DoorPermissionFlags.None, false); + + NetworkServer.UnSpawn(doorVariant.gameObject); + if (spawn) + { + NetworkServer.Spawn(doorVariant.gameObject); + } + return doorVariant; + } + + public static MapGeneration.Distributors.Locker SpawnPedestal(ItemType item, Vector3 position, Quaternion rotation,Vector3? scale) + { + MapGeneration.Distributors.Locker locker = UnityEngine.Object.Instantiate(LockerPrefab(LockerType.PedestalScp500)); + Vector3 absolutePosition = position; + Quaternion absoluteRotation = rotation; + locker.transform.localScale = scale ?? Vector3.one; + locker.transform.SetPositionAndRotation(absolutePosition, absoluteRotation); + if (locker.TryGetComponent(out var component)) + { + component.Network_position = locker.transform.position; + component.Network_rotationY = (sbyte)Mathf.RoundToInt(locker.transform.rotation.eulerAngles.y / 5.625f); + } + + LabApi.Features.Wrappers.Locker labApiLocker = LabApi.Features.Wrappers.Locker.Get(locker); + + labApiLocker.ClearLockerLoot(); + + + labApiLocker.ClearAllChambers(); + NetworkServer.UnSpawn(locker.gameObject); + NetworkServer.Spawn(locker.gameObject); + + + + foreach (MapGeneration.Distributors.LockerChamber chamber in locker.Chambers) + { + chamber.SpawnItem(item, 1); + } + + return locker; + } + + public static WorkstationController SpawnWorkshop(Vector3 position, Quaternion rotation,Vector3 scale) + { + WorkstationController workstationController = UnityEngine.Object.Instantiate(PrefabManager.Workstation); + Vector3 absolutePosition = position; + Quaternion absoluteRotation = rotation; + workstationController.transform.SetPositionAndRotation(absolutePosition, absoluteRotation); + workstationController.transform.localScale = scale; + workstationController.NetworkStatus = (byte)0u; + if (workstationController.TryGetComponent(out var component)) + { + component.Network_position = workstationController.transform.position; + component.Network_rotationY = (sbyte)Mathf.RoundToInt(workstationController.transform.rotation.eulerAngles.y / 5.625f); + } + + NetworkServer.UnSpawn(workstationController.gameObject); + NetworkServer.Spawn(workstationController.gameObject); + return workstationController; + } + + + + + + + + [Obsolete("one day it'll work",true)] + public static BreakableWindow CreateWindow(Vector3 position, Quaternion rotation) + { + BreakableWindow br = null; + foreach (GameObject gameObject in NetworkClient.prefabs.Values) + { + if (gameObject.TryGetComponent(out var window)) + { + br = UnityEngine.Object.Instantiate(window); + } + } + + br.transform.SetPositionAndRotation(position, rotation); + br.transform.localScale = Vector3.one; + br.NetworkIsBroken = false; + + NetworkServer.UnSpawn(br.gameObject); + NetworkServer.Spawn(br.gameObject); + return br; + } + + } +} diff --git a/KruacentExiled/KE.Misc/Config.cs b/KruacentExiled/KE.Misc/Config.cs new file mode 100644 index 00000000..98e85b92 --- /dev/null +++ b/KruacentExiled/KE.Misc/Config.cs @@ -0,0 +1,34 @@ +using Exiled.API.Interfaces; +using System.ComponentModel; + +namespace KE.Misc +{ + public class Config : IConfig + { + public bool IsEnabled { get; set; } = true; + public bool Debug { get; set; } = true; + [Description("Chance to d-boy doors goes boom")] + public int ChanceClassDDoorGoesBoom { get; set; } = 2; + [Description("Enable or disable the auto elevator")] + public bool AutoElevator { get; set; } = true; + [Description("Chance to get a pink candy (0-100)")] + public int ChancePinkCandy { get; set; } = 10; + public bool SurfaceLight { get; set; } = true; + + public bool ScpPreferences { get; set; } = true; + public bool Scp035Enabled { get; set; } = true; + public bool GamblingCoin { get; set; } = true; + public int GamblingCoinMinUse { get; set; } = 1; + public int GamblingCoinMaxUse { get; set; } = 2; + public int GamblingCoinCooldown { get; set; } = 3; + + [Description("health mutiplicator scps)")] + public float MultSCP049 { get; set; } = 0.8f; + public float MultSCP939 { get; set; } = 1.2f; + public float MultSCP106 { get; set; } = 1.1f; + public int MinPlayerVote { get; set; } = 6; + + + + } +} diff --git a/KruacentExiled/KE.Misc/Events/EventsArgs/BuffedSCPEventArgs .cs b/KruacentExiled/KE.Misc/Events/EventsArgs/BuffedSCPEventArgs .cs new file mode 100644 index 00000000..7de27722 --- /dev/null +++ b/KruacentExiled/KE.Misc/Events/EventsArgs/BuffedSCPEventArgs .cs @@ -0,0 +1,24 @@ +using Exiled.API.Features; +using Exiled.Events.EventArgs.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Events.EventsArgs +{ + public class BuffedSCPEventArgs : IExiledEvent, IPlayerEvent + { + + public Player Player { get; } + + public float BuffAmount { get; set; } + + public BuffedSCPEventArgs(Player player,float buff) + { + Player = player; + BuffAmount = buff; + } + } +} diff --git a/KruacentExiled/KE.Misc/Events/EventsArgs/BuffingSCPEventArgs.cs b/KruacentExiled/KE.Misc/Events/EventsArgs/BuffingSCPEventArgs.cs new file mode 100644 index 00000000..1a836c31 --- /dev/null +++ b/KruacentExiled/KE.Misc/Events/EventsArgs/BuffingSCPEventArgs.cs @@ -0,0 +1,26 @@ +using Exiled.API.Features; +using Exiled.Events.EventArgs.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Events.EventsArgs +{ + public class BuffingSCPEventArgs : IExiledEvent, IDeniableEvent, IPlayerEvent + { + public bool IsAllowed { get; set; } + + public Player Player { get; } + + public float BuffAmount { get; set; } + + public BuffingSCPEventArgs(Player player,bool isAllowed,float buff) + { + Player = player; + IsAllowed = isAllowed; + BuffAmount = buff; + } + } +} diff --git a/KruacentExiled/KE.Misc/Events/EventsArgs/GamblingCoinsEventArgs/GambledEventArgs.cs b/KruacentExiled/KE.Misc/Events/EventsArgs/GamblingCoinsEventArgs/GambledEventArgs.cs new file mode 100644 index 00000000..56715639 --- /dev/null +++ b/KruacentExiled/KE.Misc/Events/EventsArgs/GamblingCoinsEventArgs/GambledEventArgs.cs @@ -0,0 +1,39 @@ +using Exiled.API.Features; +using Exiled.API.Features.Items; +using Exiled.Events.EventArgs.Interfaces; +using KE.Misc.Features.GamblingCoin.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Events.EventsArgs.GamblingCoinsEventArgs +{ + public class GambledEventArgs : IExiledEvent, IPlayerEvent, IItemEvent + { + /// + /// can be null + /// + public Item Item { get; } + public Player Player { get; } + + public bool CoinBroke { get; } + + public ICoinEffect Effect { get; } + + /// + /// no effect if the coins is broken + /// + public int RemainingUses { get; set; } + + public GambledEventArgs(Player player, Item item,ICoinEffect effect,int remaining,bool coinBroke) + { + Player = player; + CoinBroke = coinBroke; + Item = item; + Effect = effect; + RemainingUses = remaining; + } + } +} diff --git a/KruacentExiled/KE.Misc/Events/EventsArgs/GamblingCoinsEventArgs/GamblingEventArgs.cs b/KruacentExiled/KE.Misc/Events/EventsArgs/GamblingCoinsEventArgs/GamblingEventArgs.cs new file mode 100644 index 00000000..9e1f7624 --- /dev/null +++ b/KruacentExiled/KE.Misc/Events/EventsArgs/GamblingCoinsEventArgs/GamblingEventArgs.cs @@ -0,0 +1,26 @@ +using Exiled.API.Features; +using Exiled.API.Features.Items; +using Exiled.Events.EventArgs.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Events.EventsArgs.GamblingCoinsEventArgs +{ + public class GamblingEventArgs : IExiledEvent, IPlayerEvent, IItemEvent, IDeniableEvent + { + + public Item Item { get; } + public Player Player { get; } + public bool IsAllowed { get; set; } + + public GamblingEventArgs(Player player, Item item,bool isAllowed = true) + { + Item = item; + Player = player; + IsAllowed = isAllowed; + } + } +} diff --git a/KruacentExiled/KE.Misc/Events/Handlers/GamblingCoins.cs b/KruacentExiled/KE.Misc/Events/Handlers/GamblingCoins.cs new file mode 100644 index 00000000..d3e830c0 --- /dev/null +++ b/KruacentExiled/KE.Misc/Events/Handlers/GamblingCoins.cs @@ -0,0 +1,34 @@ +using Exiled.Events.Features; +using KE.Misc.Events.EventsArgs.GamblingCoinsEventArgs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Events.Handlers +{ + public static class GamblingCoins + { + + + + public static Event Gambling = new Event(); + public static Event Gambled = new Event(); + + + + + + public static void OnGambling(GamblingEventArgs ev) + { + Gambling?.InvokeSafely(ev); + } + + public static void OnGambled(GambledEventArgs ev) + { + Gambled?.InvokeSafely(ev); + } + + } +} diff --git a/KruacentExiled/KE.Misc/Features/914.cs b/KruacentExiled/KE.Misc/Features/914.cs new file mode 100644 index 00000000..36c49557 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/914.cs @@ -0,0 +1,45 @@ +using Exiled.Events.EventArgs.Scp914; +using KE.Misc.Features._914Upgrades; + +namespace KE.Misc.Features +{ + internal class _914 : LoadingMiscFeature + { + + public override void SubscribeEvents() + { + Exiled.Events.Handlers.Scp914.UpgradingPlayer += InternalUpgradingPlayer; + base.SubscribeEvents(); + } + + public override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Scp914.UpgradingPlayer -= InternalUpgradingPlayer; + base.UnsubscribeEvents(); + } + + + + internal void InternalUpgradingPlayer(UpgradingPlayerEventArgs ev) + { + if (!ev.IsAllowed) + { + return; + } + + + foreach(Base914Upgrade feature in _allLoadedFeatures) + { + bool flag = feature.InternalUpgradingPlayer(ev); + if (flag) + { + break; + } + } + + + } + + + } +} diff --git a/KruacentExiled/KE.Misc/Features/914Upgrades/Base914PlayerUpgrade.cs b/KruacentExiled/KE.Misc/Features/914Upgrades/Base914PlayerUpgrade.cs new file mode 100644 index 00000000..488489c5 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/914Upgrades/Base914PlayerUpgrade.cs @@ -0,0 +1,21 @@ +using Exiled.Events.EventArgs.Scp914; +using PlayerRoles; +using Scp914; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features._914Upgrades +{ + public abstract class Base914PlayerUpgrade : Base914Upgrade + { + + protected override abstract bool OnUpgradingPlayer(UpgradingPlayerEventArgs ev); + + + + + } +} diff --git a/KruacentExiled/KE.Misc/Features/914Upgrades/Base914Upgrade.cs b/KruacentExiled/KE.Misc/Features/914Upgrades/Base914Upgrade.cs new file mode 100644 index 00000000..e229cccd --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/914Upgrades/Base914Upgrade.cs @@ -0,0 +1,50 @@ +using Exiled.API.Features; +using Exiled.Events.EventArgs.Scp914; +using KE.Utils.API.Features; +using KE.Utils.API.Interfaces; +using UnityEngine; + + +namespace KE.Misc.Features._914Upgrades +{ + public abstract class Base914Upgrade + { + + protected abstract float Chance { get; } + + + + + internal bool InternalUpgradingPlayer(UpgradingPlayerEventArgs ev) + { + if (!LuckCheck()) return false; + return OnUpgradingPlayer(ev); + } + + + /// + /// Auto check the probability with the and if it's allowed + /// + protected abstract bool OnUpgradingPlayer(UpgradingPlayerEventArgs ev); + + public bool LuckCheck() + { + return LuckCheck(Chance); + } + + /// + /// Check luck with a different value than + /// + /// true if it passed the luck check ; false otherwise + protected bool LuckCheck(float chance) + { + float wanted = Mathf.Clamp(chance, 0f, 100f); + float random = UnityEngine.Random.Range(0f, 100f); + + KELog.Debug($"{random} < {wanted} : {random < wanted} "); + + return random < wanted ; + } + + } +} diff --git a/KruacentExiled/KE.Misc/Features/914Upgrades/GiveOmni.cs b/KruacentExiled/KE.Misc/Features/914Upgrades/GiveOmni.cs new file mode 100644 index 00000000..447d2ede --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/914Upgrades/GiveOmni.cs @@ -0,0 +1,47 @@ +using CommandSystem; +using Exiled.API.Features; +using Exiled.API.Features.Roles; +using KE.Utils.API.Commands; +using System; +using LabPlayer = LabApi.Features.Wrappers.Player; +namespace KE.Misc.Features._914Upgrades +{ + [CommandHandler(typeof(RemoteAdminCommandHandler))] + public class GiveOmni : KECommand + { + public override string Command => "giveomni"; + + public override string[] Aliases => new string[0]; + + public override string Description => "gives an omni card"; + + public override string[] Usage => new string[0]; + + public override bool ExecuteCommand(ArraySegment arguments, ICommandSender sender, out string response) + { + Player player = Player.Get(sender); + + if(player is null) + { + response = "player null"; + return false; + } + + if(!(player.Role is FpcRole)) + { + response = "wrong role"; + return false; + } + + + LabPlayer labPlayer = LabPlayer.Get(sender); + labPlayer.CurrentItem = OmniCardUpgrade.CreateOmniCard(player); + + response = "done"; + return true; + + + + } + } +} diff --git a/KruacentExiled/KE.Misc/Features/914Upgrades/OmniCardUpgrade.cs b/KruacentExiled/KE.Misc/Features/914Upgrades/OmniCardUpgrade.cs new file mode 100644 index 00000000..cdb0734b --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/914Upgrades/OmniCardUpgrade.cs @@ -0,0 +1,35 @@ +using Exiled.Events.EventArgs.Scp914; +using UnityEngine; +using LabPlayer = LabApi.Features.Wrappers.Player; +using Interactables.Interobjects.DoorUtils; +using KeycardItem = LabApi.Features.Wrappers.KeycardItem; + + +namespace KE.Misc.Features._914Upgrades +{ + public class OmniCardUpgrade : Base914PlayerUpgrade + { + protected override float Chance => 1; + + public static readonly Color32 CardColor = new Color32(45, 44, 249,255); + protected override bool OnUpgradingPlayer(UpgradingPlayerEventArgs ev) + { + LabPlayer player = ev.Player; + if (player.CurrentItem is null) return false; + if (!(player.CurrentItem is KeycardItem keycard)) return false; + + player.RemoveItem(keycard); + + player.CurrentItem = CreateOmniCard(player); + return true; + } + + public static KeycardItem CreateOmniCard(LabPlayer player) + { + return KeycardItem.CreateCustomKeycardSite02(player, "Omni-Card", string.Empty, "Omni-Card", + new KeycardLevels(3, 3, 3), + CardColor, CardColor, Color.white, 1); + } + + } +} diff --git a/KruacentExiled/KE.Misc/Features/914Upgrades/PlayerTeleport914.cs b/KruacentExiled/KE.Misc/Features/914Upgrades/PlayerTeleport914.cs new file mode 100644 index 00000000..737383c0 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/914Upgrades/PlayerTeleport914.cs @@ -0,0 +1,74 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.Events.EventArgs.Scp914; +using InventorySystem.Items.Usables.Scp330; +using KE.Utils.API.Features; +using KE.Utils.Extensions; +using MEC; +using PlayerRoles.FirstPersonControl; +using Scp914; +using System; + +namespace KE.Misc.Features._914Upgrades +{ + public class PlayerTeleport914 : Base914PlayerUpgrade + { + public float ChanceTpEntrance = 1; + protected override float Chance => 100; + protected override bool OnUpgradingPlayer(UpgradingPlayerEventArgs ev) + { + KELog.Debug("Upgrade teleport"); + Player player = ev.Player; + Room room = null; + + if(!(player.Role is IFpcRole fpc)) + { + return false; + } + + //TeleportOutcome.GetBestExitPosition(fpc); + if (ev.KnobSetting == Scp914KnobSetting.Fine && LuckCheck(ChanceTpEntrance)) + { + try + { + room = ZoneType.Entrance.RandomSafeRoom(); + } + catch (Exception) + { + room = Room.Random(ZoneType.Entrance); + } + + + } + if(ev.KnobSetting == Scp914KnobSetting.Coarse && LuckCheck(25)) + { + try + { + room = ZoneType.LightContainment.RandomSafeRoom(); + } + catch (Exception) + { + room = Room.Random(ZoneType.LightContainment); + } + } + + if (room != null) + { + //idk why but need a delay + Timing.CallDelayed(.1f, delegate + { + KELog.Debug($"teleporting {player.Nickname} to {room.Name}"); + player.Teleport(room); + }); + + } + + + return true; + } + + + + + } +} diff --git a/KruacentExiled/KE.Misc/Features/914Upgrades/RoleChanging/Base914PlayerRoleChange.cs b/KruacentExiled/KE.Misc/Features/914Upgrades/RoleChanging/Base914PlayerRoleChange.cs new file mode 100644 index 00000000..b2fad26f --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/914Upgrades/RoleChanging/Base914PlayerRoleChange.cs @@ -0,0 +1,58 @@ +using Exiled.API.Features; +using Exiled.Events.EventArgs.Scp914; +using KE.Utils.API.Features; +using KE.Utils.API.Interfaces; +using KE.Utils.Extensions; +using MEC; +using PlayerRoles; +using Scp914; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features._914Upgrades +{ + public abstract class Base914PlayerRoleChange : Base914PlayerUpgrade + { + protected static HashSet _upgradingPlayer = new HashSet(); + + public abstract RoleTypeId InputRole { get; } + + public abstract IReadOnlyDictionary OutputRoles { get; } + protected sealed override float Chance => 100; + + protected sealed override bool OnUpgradingPlayer(UpgradingPlayerEventArgs ev) + { + Player player = ev.Player; + if (player.Role != InputRole) return false; + if (!OutputRoles.TryGetValue(ev.KnobSetting, out var newRole)) return false; + if (_upgradingPlayer.Contains(player)) return false; + if (!LuckCheck(newRole.chance)) return false; + + KELog.Debug($"upgrading {player.Role.Type}->{newRole.role}"); + + + Set(player, newRole.role); + + _upgradingPlayer.Add(player); + return true; + } + + private void Set(Player player, RoleTypeId newRole) + { + + SetRole(player, newRole); + Timing.CallDelayed(.5f, () => + { + _upgradingPlayer.Remove(player); + }); + } + + protected virtual void SetRole(Player player,RoleTypeId newRole) + { + player.ChangeRole(newRole, Exiled.API.Enums.SpawnReason.ForceClass, RoleSpawnFlags.None); + } + } +} diff --git a/KruacentExiled/KE.Misc/Features/914Upgrades/RoleChanging/Human914RC.cs b/KruacentExiled/KE.Misc/Features/914Upgrades/RoleChanging/Human914RC.cs new file mode 100644 index 00000000..85f730fd --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/914Upgrades/RoleChanging/Human914RC.cs @@ -0,0 +1,70 @@ +using Exiled.API.Features; +using KE.Utils.Extensions; +using MEC; +using PlayerRoles; +using Scp914; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features._914Upgrades.RoleChanging +{ + public class ClassD914RC : Base914PlayerRoleChange + { + public override RoleTypeId InputRole => RoleTypeId.ClassD; + + public override IReadOnlyDictionary OutputRoles { get; } = new Dictionary() + { + { Scp914KnobSetting.OneToOne,new RoleOutput(RoleTypeId.Scientist,50f)} + }; + + } + public class Scientist914RC : Base914PlayerRoleChange + { + public override RoleTypeId InputRole => RoleTypeId.Scientist; + + public override IReadOnlyDictionary OutputRoles { get; } = new Dictionary() + { + { Scp914KnobSetting.OneToOne,new RoleOutput(RoleTypeId.ClassD,50f)} + }; + + } + public class Guard914RC : Base914PlayerRoleChange + { + public override RoleTypeId InputRole => RoleTypeId.FacilityGuard; + + public override IReadOnlyDictionary OutputRoles { get; } = new Dictionary() + { + { Scp914KnobSetting.Rough,new RoleOutput(RoleTypeId.Scientist,100f)} + }; + + protected override void SetRole(Player player, RoleTypeId newRole) + { + player.ChangeRole(newRole, Exiled.API.Enums.SpawnReason.ForceClass, RoleSpawnFlags.AssignInventory); + } + + } + public class MTF914RC : Multiple914PlayerRoleChangeBase + { + public override HashSet InputRole => new HashSet(){ RoleTypeId.NtfCaptain, RoleTypeId.NtfPrivate, RoleTypeId.NtfSergeant, RoleTypeId.NtfSpecialist}; + + public override IReadOnlyDictionary OutputRoles { get; } = new Dictionary() + { + { Scp914KnobSetting.OneToOne,new RoleOutput(RoleTypeId.ChaosRifleman,50f)} + }; + + } + + public class Chaos914RC : Multiple914PlayerRoleChangeBase + { + public override HashSet InputRole => new HashSet() { RoleTypeId.ChaosRifleman, RoleTypeId.ChaosRepressor, RoleTypeId.ChaosMarauder, RoleTypeId.ChaosConscript }; + + public override IReadOnlyDictionary OutputRoles { get; } = new Dictionary() + { + { Scp914KnobSetting.OneToOne,new RoleOutput(RoleTypeId.NtfPrivate,50f)} + }; + + } +} diff --git a/KruacentExiled/KE.Misc/Features/914Upgrades/RoleChanging/Multiple914PlayerRoleChangeBase.cs b/KruacentExiled/KE.Misc/Features/914Upgrades/RoleChanging/Multiple914PlayerRoleChangeBase.cs new file mode 100644 index 00000000..d8c132d7 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/914Upgrades/RoleChanging/Multiple914PlayerRoleChangeBase.cs @@ -0,0 +1,60 @@ +using Exiled.API.Features; +using Exiled.Events.EventArgs.Scp914; +using KE.Utils.API.Features; +using KE.Utils.API.Interfaces; +using KE.Utils.Extensions; +using MEC; +using PlayerRoles; +using Scp914; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features._914Upgrades +{ + public abstract class Multiple914PlayerRoleChangeBase : Base914PlayerUpgrade + { + protected static HashSet _upgradingPlayer = new HashSet(); + + public abstract HashSet InputRole { get; } + + public abstract IReadOnlyDictionary OutputRoles { get; } + protected sealed override float Chance => 100; + + protected sealed override bool OnUpgradingPlayer(UpgradingPlayerEventArgs ev) + { + Player player = ev.Player; + if (!InputRole.Contains(ev.Player.Role)) return false; + if (!OutputRoles.TryGetValue(ev.KnobSetting, out var newRole)) return false; + if (_upgradingPlayer.Contains(player)) return false; + if (!LuckCheck(newRole.chance)) return false; + + KELog.Debug($"upgrading {player.Role.Type}->{newRole.role}"); + + + SetRole(player, newRole.role); + + _upgradingPlayer.Add(player); + return true; + } + + protected virtual void SetRole(Player player,RoleTypeId newRole) + { + player.ChangeRole(newRole, Exiled.API.Enums.SpawnReason.ForceClass, RoleSpawnFlags.None); + Timing.CallDelayed(.5f, () => + { + _upgradingPlayer.Remove(player); + }); + } + + + + + + + + + } +} diff --git a/KruacentExiled/KE.Misc/Features/914Upgrades/RoleChanging/SCP914RC.cs b/KruacentExiled/KE.Misc/Features/914Upgrades/RoleChanging/SCP914RC.cs new file mode 100644 index 00000000..89978b86 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/914Upgrades/RoleChanging/SCP914RC.cs @@ -0,0 +1,81 @@ +using PlayerRoles; +using Scp914; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features._914Upgrades.RoleChanging +{ + public class Scp096RC : Base914PlayerRoleChange + { + public override RoleTypeId InputRole => RoleTypeId.Scp096; + + public override IReadOnlyDictionary OutputRoles { get; } = new Dictionary() + { + { Scp914KnobSetting.OneToOne,new RoleOutput(RoleTypeId.Scp3114,50f)}, + { Scp914KnobSetting.VeryFine,new RoleOutput(RoleTypeId.Scp939,50f)} + }; + + } + public class Scp3114RC : Base914PlayerRoleChange + { + public override RoleTypeId InputRole => RoleTypeId.Scp3114; + + public override IReadOnlyDictionary OutputRoles { get; } = new Dictionary() + { + { Scp914KnobSetting.OneToOne,new RoleOutput(RoleTypeId.Scp096,50f)}, + { Scp914KnobSetting.VeryFine,new RoleOutput(RoleTypeId.Scp173,50f)} + }; + + } + public class Scp939RC : Base914PlayerRoleChange + { + public override RoleTypeId InputRole => RoleTypeId.Scp939; + + public override IReadOnlyDictionary OutputRoles { get; } = new Dictionary() + { + { Scp914KnobSetting.Rough,new RoleOutput(RoleTypeId.Scp096,50f)}, + { Scp914KnobSetting.OneToOne,new RoleOutput(RoleTypeId.Scp173,50f)}, + { Scp914KnobSetting.VeryFine,new RoleOutput(RoleTypeId.Scp049,50f)}, + }; + + } + + public class Scp173RC : Base914PlayerRoleChange + { + public override RoleTypeId InputRole => RoleTypeId.Scp173; + + public override IReadOnlyDictionary OutputRoles { get; } = new Dictionary() + { + { Scp914KnobSetting.Rough,new RoleOutput(RoleTypeId.Scp096,50f)}, + { Scp914KnobSetting.OneToOne,new RoleOutput(RoleTypeId.Scp939,50f)}, + { Scp914KnobSetting.VeryFine,new RoleOutput(RoleTypeId.Scp106,50f)}, + }; + + } + + public class Scp106RC : Base914PlayerRoleChange + { + public override RoleTypeId InputRole => RoleTypeId.Scp106; + + public override IReadOnlyDictionary OutputRoles { get; } = new Dictionary() + { + { Scp914KnobSetting.Rough,new RoleOutput(RoleTypeId.Scp173,50f)}, + { Scp914KnobSetting.OneToOne,new RoleOutput(RoleTypeId.Scp049,50f)}, + }; + + } + public class Scp049RC : Base914PlayerRoleChange + { + public override RoleTypeId InputRole => RoleTypeId.Scp049; + + public override IReadOnlyDictionary OutputRoles { get; } = new Dictionary() + { + { Scp914KnobSetting.Rough,new RoleOutput(RoleTypeId.Scp939,50f)}, + { Scp914KnobSetting.OneToOne,new RoleOutput(RoleTypeId.Scp106,50f)}, + }; + + } +} diff --git a/KruacentExiled/KE.Misc/Features/914Upgrades/RoleOutput.cs b/KruacentExiled/KE.Misc/Features/914Upgrades/RoleOutput.cs new file mode 100644 index 00000000..ba5e0295 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/914Upgrades/RoleOutput.cs @@ -0,0 +1,26 @@ +using PlayerRoles; +using Scp914; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Misc.Features._914Upgrades +{ + public struct RoleOutput + { + + public RoleTypeId role; + public float chance; + + + public RoleOutput(RoleTypeId role, float chance) + { + this.chance = Mathf.Clamp(chance, 0f, 100f); + this.role = role; + } + + } +} diff --git a/KruacentExiled/KE.Misc/Features/AutoElevator.cs b/KruacentExiled/KE.Misc/Features/AutoElevator.cs new file mode 100644 index 00000000..10199271 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/AutoElevator.cs @@ -0,0 +1,50 @@ +using Exiled.API.Features; +using MEC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features +{ + /// + /// The elevator will random activate in the round + /// + internal class AutoElevator + { + private CoroutineHandle handle; + ~AutoElevator() + { + Timing.KillCoroutines(handle); + } + + + public void StartLoop() + { + handle = Timing.RunCoroutine(StartElevator()); + } + /// + /// Start the auto elevator loop + /// + private IEnumerator StartElevator() + { + //KELog.Debug("elevator"); + while (!Round.IsEnded) + { + foreach (Lift l in Lift.List.ToList()) + { + yield return Timing.WaitForSeconds(UnityEngine.Random.Range(30, 45)); + SendElevator(l); + } + } + } + + + private void SendElevator(Lift e) + { + //KELog.Debug($"{e.Name}"); + e.TryStart(0, true); + } + } +} diff --git a/KruacentExiled/KE.Misc/Features/AutoNukeAnnoucement.cs b/KruacentExiled/KE.Misc/Features/AutoNukeAnnoucement.cs new file mode 100644 index 00000000..20d729cb --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/AutoNukeAnnoucement.cs @@ -0,0 +1,34 @@ +using Exiled.API.Features; +using GameCore; +using KE.Utils.API.Interfaces; +using MEC; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features +{ + public class NukeKill : IUsingEvents + { + public void SubscribeEvents() + { + } + + public void UnsubscribeEvents() + { + + } + + private void OnDetonated() + { + + } + + + + + } +} diff --git a/KruacentExiled/KE.Misc/Features/AutoTesla.cs b/KruacentExiled/KE.Misc/Features/AutoTesla.cs new file mode 100644 index 00000000..7e5e717e --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/AutoTesla.cs @@ -0,0 +1,49 @@ +using Exiled.API.Extensions; +using Exiled.API.Features; +using LabApi.Features.Wrappers; +using MEC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Round = Exiled.API.Features.Round; + +namespace KE.Misc.Features +{ + /// + /// The elevator will random activate in the round + /// + internal class AutoTesla + { + private CoroutineHandle handle; + + public void StopLoop() + { + Timing.KillCoroutines(handle); + } + public void StartLoop() + { + handle = Timing.RunCoroutine(StartElevator()); + } + /// + /// Start the auto elevator loop + /// + private IEnumerator StartElevator() + { + while (!Round.IsEnded) + { + yield return Timing.WaitForSeconds(UnityEngine.Random.Range(120f, 200f)); + Tesla tesla = Tesla.List.GetRandomValue(); + + tesla.Trigger(); + if (UnityEngine.Random.Range(0f, 100f) <= 70f) + { + yield return Timing.WaitForSeconds(tesla.Base.windupTime + tesla.Base.cooldownTime + .1f); + tesla.Trigger(); + } + } + } + + } +} diff --git a/KruacentExiled/KE.Misc/Features/Candy.cs b/KruacentExiled/KE.Misc/Features/Candy.cs new file mode 100644 index 00000000..d58d400e --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/Candy.cs @@ -0,0 +1,32 @@ +using Exiled.Events.EventArgs.Scp330; +using Exiled.Events.Patches.Events.Scp330; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using InventorySystem.Items.Usables.Scp330; + +namespace KE.Misc.Features +{ + internal class Candy : MiscFeature + { + + public override void SubscribeEvents() + { + Exiled.Events.Handlers.Scp330.InteractingScp330 += InteractingScp330; + } + + public override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Scp330.InteractingScp330 -= InteractingScp330; + } + public void InteractingScp330(InteractingScp330EventArgs ev) + { + if (UnityEngine.Random.Range(0, 100) < MainPlugin.Configs.ChancePinkCandy) + { + ev.Candy = CandyKindID.Pink; + } + } + } +} diff --git a/KruacentExiled/KE.Misc/Features/ClassDDoor.cs b/KruacentExiled/KE.Misc/Features/ClassDDoor.cs new file mode 100644 index 00000000..c7cd01db --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/ClassDDoor.cs @@ -0,0 +1,60 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Doors; +using Exiled.API.Interfaces; +using KE.Utils.API.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features +{ + /// + /// Everything about classD door + /// + public class ClassDDoor : IUsingEvents + { + public void SubscribeEvents() + { + Exiled.Events.Handlers.Server.RoundStarted += OnRoundStarted; + } + + public void UnsubscribeEvents() + { + Exiled.Events.Handlers.Server.RoundStarted -= OnRoundStarted; + + } + + + private void OnRoundStarted() + { + if(UnityEngine.Random.Range(0, 101) < MainPlugin.Configs.ChanceClassDDoorGoesBoom) + { + HumanDoorsGoesBoom(); + } + + } + + + private void HumanDoorsGoesBoom() + { + foreach(Player player in Player.List.Where(p => p.IsHuman)) + { + if(player.CurrentRoom != null) + { + foreach(Door door in player.CurrentRoom.Doors.Where(d => d is IDamageableDoor && !d.IsCheckpoint)) + { + IDamageableDoor damageable = door as IDamageableDoor; + damageable.Break(); + } + } + } + } + + + + + } +} diff --git a/KruacentExiled/KE.Misc/Features/FriendlyFire.cs b/KruacentExiled/KE.Misc/Features/FriendlyFire.cs new file mode 100644 index 00000000..131963c8 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/FriendlyFire.cs @@ -0,0 +1,16 @@ +using Exiled.API.Features; +using Exiled.Events.EventArgs.Player; +using Exiled.Events.EventArgs.Warhead; +using KE.Misc.Features.FriendlyFireConditions; +using KE.Utils.API; +using KE.Utils.API.Interfaces; +using System; +using System.Collections.Generic; + +namespace KE.Misc.Features +{ + internal class FriendlyFire : LoadingMiscFeature + { + + } +} diff --git a/KruacentExiled/KE.Misc/Features/FriendlyFireConditions/FFChangingCondition.cs b/KruacentExiled/KE.Misc/Features/FriendlyFireConditions/FFChangingCondition.cs new file mode 100644 index 00000000..e0e8dd03 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/FriendlyFireConditions/FFChangingCondition.cs @@ -0,0 +1,57 @@ +using Exiled.API.Features; +using KE.Utils.API.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Misc.Features.FriendlyFireConditions +{ + internal abstract class FFChangingCondition : IUsingEvents + { + + /// + /// The chance of triggering a changement from 0f to 100f + /// + protected abstract float Chance { get; } + + + /// + /// No need to calculate the probability + /// + protected void ChangeFriendlyFire() + { + float correctedChance = Mathf.Clamp(Chance, 0f, 100f); + if (UnityEngine.Random.Range(0f, 100f) < correctedChance) + { + Server.FriendlyFire = !Server.FriendlyFire; + } + } + + /// + /// No need to calculate the probability + /// + protected void ForceFriendlyFireState(bool isFFActive) + { + byte correctedChance = (byte)Mathf.Clamp(Chance, 0, 100); + if (UnityEngine.Random.Range(0f, 100f) < correctedChance) + { + Server.FriendlyFire = isFFActive; + } + } + + public virtual void SubscribeEvents() + { + + } + + public virtual void UnsubscribeEvents() + { + + } + + + } +} diff --git a/KruacentExiled/KE.Misc/Features/FriendlyFireConditions/OpenDoorFFCC.cs b/KruacentExiled/KE.Misc/Features/FriendlyFireConditions/OpenDoorFFCC.cs new file mode 100644 index 00000000..c8646a25 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/FriendlyFireConditions/OpenDoorFFCC.cs @@ -0,0 +1,33 @@ +using Exiled.Events.EventArgs.Player; +using KE.Utils.API.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features.FriendlyFireConditions +{ + internal class OpenDoorFFCC : FFChangingCondition + { + protected override float Chance => 0.1f; + + public override void SubscribeEvents() + { + Exiled.Events.Handlers.Player.InteractingDoor += OnInteractingDoor; + base.SubscribeEvents(); + } + + public override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Player.InteractingDoor -= OnInteractingDoor; + base.UnsubscribeEvents(); + } + + private void OnInteractingDoor(InteractingDoorEventArgs ev) + { + if (ev.Door.IsOpen) return; + ChangeFriendlyFire(); + } + } +} diff --git a/KruacentExiled/KE.Misc/Features/FriendlyFireConditions/ScpDeathFFCC.cs b/KruacentExiled/KE.Misc/Features/FriendlyFireConditions/ScpDeathFFCC.cs new file mode 100644 index 00000000..419d2c84 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/FriendlyFireConditions/ScpDeathFFCC.cs @@ -0,0 +1,37 @@ +using Exiled.API.Features; +using Exiled.Events.EventArgs.Player; +using KE.Utils.API.Features.SCPs; +using KE.Utils.API.Interfaces; +using PlayerRoles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features.FriendlyFireConditions +{ + internal class ScpDeathFFCC : FFChangingCondition + { + protected override float Chance => 20; + + public override void SubscribeEvents() + { + Exiled.Events.Handlers.Player.Dying += OnDying; + base.SubscribeEvents(); + } + + public override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Player.Dying -= OnDying; + base.UnsubscribeEvents(); + } + + private void OnDying(DyingEventArgs ev) + { + Player player = ev.Player; + if (!SCPTeam.IsSCP(player.ReferenceHub) && player.Role != RoleTypeId.Scp0492) return; + ChangeFriendlyFire(); + } + } +} diff --git a/KruacentExiled/KE.Misc/Features/FriendlyFireConditions/StartRoundFFCC.cs b/KruacentExiled/KE.Misc/Features/FriendlyFireConditions/StartRoundFFCC.cs new file mode 100644 index 00000000..a5c5ee77 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/FriendlyFireConditions/StartRoundFFCC.cs @@ -0,0 +1,32 @@ +using Exiled.Events.EventArgs.Player; +using KE.Utils.API.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features.FriendlyFireConditions +{ + internal class StartRoundFFCC : FFChangingCondition + { + protected override float Chance => 50; + + public override void SubscribeEvents() + { + Exiled.Events.Handlers.Server.RoundStarted += OnRoundStarted; + base.SubscribeEvents(); + } + + public override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Server.RoundStarted -= OnRoundStarted; + base.UnsubscribeEvents(); + } + + private void OnRoundStarted() + { + ForceFriendlyFireState(true); + } + } +} diff --git a/KruacentExiled/KE.Misc/Features/FriendlyFireConditions/WarheadStartingFFCC.cs b/KruacentExiled/KE.Misc/Features/FriendlyFireConditions/WarheadStartingFFCC.cs new file mode 100644 index 00000000..1ce18971 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/FriendlyFireConditions/WarheadStartingFFCC.cs @@ -0,0 +1,33 @@ +using Exiled.Events.EventArgs.Player; +using Exiled.Events.EventArgs.Warhead; +using KE.Utils.API.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features.FriendlyFireConditions +{ + internal class WarheadStartingFFCC : FFChangingCondition + { + protected override float Chance => 50; + + public override void SubscribeEvents() + { + Exiled.Events.Handlers.Warhead.Starting += OnStarting; + base.SubscribeEvents(); + } + + public override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Warhead.Starting -= OnStarting; + base.UnsubscribeEvents(); + } + + private void OnStarting(StartingEventArgs ev) + { + ForceFriendlyFireState(true); + } + } +} diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/CoinHintPosition.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/CoinHintPosition.cs new file mode 100644 index 00000000..b795c070 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/CoinHintPosition.cs @@ -0,0 +1,20 @@ +using HintServiceMeow.Core.Enum; +using KE.Utils.API.Displays.DisplayMeow.Placements; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features.GamblingCoin +{ + public class CoinHintPosition : HintPosition + { + public override float Xposition => 0; + + public override float Yposition => 800; + + public override HintAlignment HintAlignment => HintAlignment.Center; + public override string Name => "Coins"; + } +} diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/AutoDoor.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/AutoDoor.cs new file mode 100644 index 00000000..98f2a26d --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/AutoDoor.cs @@ -0,0 +1,31 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Doors; +using KE.Misc.Features.GamblingCoin; +using KE.Misc.Features.GamblingCoin.Interfaces; +using MapGeneration; +using System; +using System.Linq; +using EffectType = KE.Misc.Features.GamblingCoin.Types.EffectType; + +internal class AutoDoor : ICoinEffect +{ + public string Name { get; set; } = "AutoDoor"; + public string Message { get; set; } = "You are very talented in messing up the game !"; + public int Weight { get; set; } = 5; + public EffectType Type { get; set; } = EffectType.Negative; + + public void Execute(Player player) + { + Array values = Enum.GetValues(typeof(FacilityZone)); + FacilityZone randomZone = (FacilityZone)values.GetValue(UnityEngine.Random.Range(0, values.Length)); + + + + foreach(Door door in Door.List.Where(d => d.Position.GetZone() == randomZone && d.DoorLockType == DoorLockType.None)) + { + door.IsOpen = true; + } + + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/DetonatedFlash.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/DetonatedFlash.cs new file mode 100644 index 00000000..9bf6ef10 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/DetonatedFlash.cs @@ -0,0 +1,19 @@ +using Exiled.API.Features; +using Exiled.API.Features.Items; +using KE.Misc.Features.GamblingCoin.Interfaces; +using EffectType = KE.Misc.Features.GamblingCoin.Types.EffectType; + +internal class DetonatedFlash : ICoinEffect +{ + public string Name { get; set; } = "DetonatedFlash"; + public string Message { get; set; } = "a gift for your eyes"; + public int Weight { get; set; } = 50; + public EffectType Type { get; set; } = EffectType.Negative; + + public void Execute(Player player) + { + FlashGrenade flash = (FlashGrenade)Item.Create(ItemType.GrenadeFlash, player); + flash.FuseTime = 1f; + flash.SpawnActive(player.Position); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/EatingStar.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/EatingStar.cs new file mode 100644 index 00000000..6cf6d5a9 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/EatingStar.cs @@ -0,0 +1,72 @@ +using Exiled.API.Features; +using Exiled.API.Features.Toys; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; +using MEC; +using System.Collections.Generic; +using UnityEngine; +using Light = Exiled.API.Features.Toys.Light; + +public class EatingStar : IDurationEffect +{ + public string Name { get; set; } = "EatingStar"; + public string Message { get; set; } = "Eating the disco ball wasn't good idea"; + public int Weight { get; set; } = 2; + public EffectType Type { get; set; } = EffectType.Negative; + + + private Dictionary _lights = new Dictionary(); + private Dictionary _clips = new Dictionary(); + + public float Duration { get; set; } = 20; + private static CoroutineHandle _coroutines; + + public void Execute(Player player) + { + Light light = Light.Create(player.Position, null, null, true, UnityEngine.Color.blue); + light.Transform.parent = player.GameObject.transform; + light.MovementSmoothing = 0; + _lights[player] = light; + + + var c = KE.Utils.API.Sounds.SoundPlayer.Instance.Play("starman", player.GameObject,volume:.5f); + + _clips[player] = c; + _coroutines = Timing.RunCoroutine(ColorTransformer(player)); + } + + public IEnumerator ColorTransformer(Player player) + { + while (true) + { + if(_lights.TryGetValue(player,out var l)) + { + l.Color = ColorPicker(); + } + else + { + yield break; + } + yield return Timing.WaitForSeconds(0.5f); + } + } + + public static Color32 ColorPicker() + { + byte r = (byte)UnityEngine.Random.Range(0, 256); + byte g = (byte)UnityEngine.Random.Range(0, 256); + byte b = (byte)UnityEngine.Random.Range(0, 256); + + return new Color32(r, g, b, 0); + } + + public void ExecuteAfterDuration(Player player) + { + Timing.KillCoroutines(_coroutines); + Light light = _lights[player]; + light.Destroy(); + _clips[player].IsPaused = true; + _lights.Remove(player); + + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/EveryoneChange.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/EveryoneChange.cs new file mode 100644 index 00000000..8880882a --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/EveryoneChange.cs @@ -0,0 +1,36 @@ +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; +using PlayerRoles; +using System.Collections.Generic; +using System.Linq; + +internal class EveryoneChange : ICoinEffect +{ + public string Name { get; set; } = "EveryoneChange"; + public string Message { get; set; } = "You converted everyone in the opposite team !"; + public int Weight { get; set; } = 10; + public EffectType Type { get; set; } = EffectType.Negative; + + private readonly Dictionary RoleInversions = new Dictionary + { + { RoleTypeId.ClassD, RoleTypeId.Scientist }, + { RoleTypeId.Scientist, RoleTypeId.ClassD }, + { RoleTypeId.ChaosConscript, RoleTypeId.NtfPrivate }, + { RoleTypeId.ChaosRifleman, RoleTypeId.NtfSergeant }, + { RoleTypeId.ChaosRepressor, RoleTypeId.NtfCaptain }, + { RoleTypeId.ChaosMarauder, RoleTypeId.NtfCaptain }, + { RoleTypeId.NtfPrivate, RoleTypeId.ChaosRifleman }, + { RoleTypeId.NtfSergeant, RoleTypeId.ChaosRifleman }, + { RoleTypeId.NtfCaptain, RoleTypeId.ChaosRepressor }, + { RoleTypeId.NtfSpecialist, RoleTypeId.ChaosMarauder } + }; + + public void Execute(Player caster) + { + foreach (Player target in Player.List.Where(p => p.IsAlive && RoleInversions.ContainsKey(p.Role.Type))) + { + target.Role.Set(RoleInversions[target.Role.Type], RoleSpawnFlags.None); + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/FakeNuke.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/FakeNuke.cs new file mode 100644 index 00000000..7357ebe4 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/FakeNuke.cs @@ -0,0 +1,25 @@ +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; + +internal class FakeNuke : IDurationEffect +{ + public string Name { get; set; } = "FakeNuke"; + public string Message { get; set; } = string.Empty; + public int Weight { get; set; } = 5; + public EffectType Type { get; set; } = EffectType.Negative; + public float Duration { get; set; } = 15; + + public void Execute(Player player) + { + if(!Warhead.IsDetonated) Warhead.Start(); + } + + public void ExecuteAfterDuration(Player player) + { + if (Warhead.IsInProgress && Warhead.Controller.Info.ScenarioType != WarheadScenarioType.DeadmanSwitch) + { + Warhead.Stop(); + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/FakeSCPDeath.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/FakeSCPDeath.cs new file mode 100644 index 00000000..0c4fd45d --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/FakeSCPDeath.cs @@ -0,0 +1,52 @@ +using Exiled.API.Extensions; +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; +using PlayerRoles; +using Respawning.NamingRules; +using System.Collections.Generic; +using System.Linq; + +/*internal class FakeSCPDeath : ICoinEffect +{ + public string Name { get; set; } = "FakeSCPDeath"; + public string Message { get; set; } = "DID YOU JUST KILLED AN SCP !?!?"; + public int Weight { get; set; } = 40; + public EffectType Type { get; set; } = EffectType.Negative; + + private static readonly Dictionary _scpNames = new() + { + { "1 7 3", "SCP-173"}, + { "9 3 9", "SCP-939"}, + { "0 9 6", "SCP-096"}, + { "0 7 9", "SCP-079"}, + { "0 4 9", "SCP-049"}, + { "1 0 6", "SCP-106"} + }; + + private static readonly Dictionary scpTermination = new() + { + { 0, "SUCCESSFULLY TERMINATED BY AUTOMATIC SECURITY SYSTEM" }, + { 1, "SUCCESSFULLY TERMINATED BY ALPHA WARHEAD" }, + { 2, "LOST IN DECONTAMINATION SEQUENCE" }, + { 3, "CONTAINEDSUCCESSFULLY" }, + { 4, "SUCCESSFULLY TERMINATED . TERMINATION CAUSE UNSPECIFIED" } + }; + + public void Execute(Player player) + { + var scpName = _scpNames.ToList().RandomItem(); + var scpDeath = scpTermination.GetRandomValue(); + string deathMessage = scpDeath.Value; + Team deathTeam = Player.List.Where(p => !p.IsScp).GetRandomValue().Role.Team; + string unitTeam = NineTailedFoxNamingRule.PossibleCodes.GetRandomValue() + "-" + UnityEngine.Random.Range(NineTailedFoxNamingRule.MinUnitNumber, NineTailedFoxNamingRule.MaxUnitNumber); ; + + if (scpDeath.Key == 3) + { + deathMessage += " " + Cassie.ConvertTeam(deathTeam, unitTeam); + } + + Cassie.MessageTranslated($"scp {scpName.Key} successfully terminated by automatic security system", + $"{scpName.Value} ${deathMessage}."); + } +}*/ \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/FlipPlayerRole.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/FlipPlayerRole.cs new file mode 100644 index 00000000..ba68dbf0 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/FlipPlayerRole.cs @@ -0,0 +1,34 @@ +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; +using PlayerRoles; +using System.Collections.Generic; +using static UnityEngine.GraphicsBuffer; +internal class FlipPlayerRole : ICoinEffect +{ + public string Name { get; set; } = "FlipPlayerRole"; + public string Message { get; set; } = "That's what I call an UNO reverse card!"; + public int Weight { get; set; } = 10; + public EffectType Type { get; set; } = EffectType.Negative; + + private static readonly Dictionary RoleInversions = new Dictionary + { + { RoleTypeId.Scientist, RoleTypeId.ClassD }, + { RoleTypeId.ClassD, RoleTypeId.Scientist }, + { RoleTypeId.ChaosConscript, RoleTypeId.NtfSergeant }, + { RoleTypeId.ChaosRifleman, RoleTypeId.NtfSergeant }, + { RoleTypeId.ChaosMarauder, RoleTypeId.NtfCaptain }, + { RoleTypeId.ChaosRepressor, RoleTypeId.NtfCaptain }, + { RoleTypeId.FacilityGuard, RoleTypeId.ChaosRifleman }, + { RoleTypeId.NtfPrivate, RoleTypeId.ChaosRifleman }, + { RoleTypeId.NtfSergeant, RoleTypeId.ChaosRifleman }, + { RoleTypeId.NtfSpecialist, RoleTypeId.ChaosRifleman }, + { RoleTypeId.NtfCaptain, RoleTypeId.ChaosRepressor }, + }; + + public void Execute(Player player) + { + player.DropItems(); + player.Role.Set(RoleInversions[player.Role.Type], RoleSpawnFlags.None); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/Handcuff.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/Handcuff.cs new file mode 100644 index 00000000..b3da7a64 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/Handcuff.cs @@ -0,0 +1,17 @@ +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin.Interfaces; +using EffectType = KE.Misc.Features.GamblingCoin.Types.EffectType; + +public class Handcuff : ICoinEffect +{ + public string Name { get; set; } = "Handcuff"; + public string Message { get; set; } = "You were arrested for uhh commiting war crimes... or something"; + public int Weight { get; set; } = 10; + public EffectType Type { get; set; } = EffectType.Negative; + + public void Execute(Player player) + { + player.Handcuff(); + player.DropItems(); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/InventoryReset.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/InventoryReset.cs new file mode 100644 index 00000000..b5e8dafd --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/InventoryReset.cs @@ -0,0 +1,17 @@ +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; + +internal class InventoryReset : ICoinEffect +{ + public string Name { get; set; } = "InventoryReset"; + public string Message { get; set; } = "have you ever got item in your inventory ?"; + public int Weight { get; set; } = 20; + public EffectType Type { get; set; } = EffectType.Negative; + + public void Execute(Player player) + { + player.DropHeldItem(); + player.ClearInventory(); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/Kick.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/Kick.cs new file mode 100644 index 00000000..f410dcdc --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/Kick.cs @@ -0,0 +1,16 @@ +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; + +internal class Kick : ICoinEffect +{ + public string Name { get; set; } = "Kick"; + public string Message { get; set; } = "Bye !"; + public int Weight { get; set; } = 5; + public EffectType Type { get; set; } = EffectType.Negative; + + public void Execute(Player player) + { + player.Kick("You gamble too much !"); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/LiveHEGrenade.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/LiveHEGrenade.cs new file mode 100644 index 00000000..0bd0d0ab --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/LiveHEGrenade.cs @@ -0,0 +1,21 @@ +using Exiled.API.Features; +using Exiled.API.Features.Items; +using KE.Misc.Features.GamblingCoin.Interfaces; +using UnityEngine; +using EffectType = KE.Misc.Features.GamblingCoin.Types.EffectType; + +internal class LiveHEGrenade : ICoinEffect +{ + public string Name { get; set; } = "LiveHEGrenade"; + public string Message { get; set; } = "Watch your head !"; + public int Weight { get; set; } = 30; + public EffectType Type { get; set; } = EffectType.Negative; + public static float FuseTime = 3.25f; + + public void Execute(Player player) + { + ExplosiveGrenade grenade = (ExplosiveGrenade)Item.Create(ItemType.GrenadeHE); + grenade.FuseTime = FuseTime; + grenade.SpawnActive(player.Position + Vector3.up, player); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/MakeOneHp.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/MakeOneHp.cs new file mode 100644 index 00000000..53917903 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/MakeOneHp.cs @@ -0,0 +1,22 @@ +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; + +internal class MakeOneHP : ICoinEffect +{ + public string Name { get; set; } = "MakeOneHP"; + public string Message { get; set; } = ""; + public int Weight { get; set; } = 15; + public EffectType Type { get; set; } = EffectType.Negative; + + public void Execute(Player player) + { + if (player.Health > 1) + { + player.Health = 1; + } else + { + player.Hurt(99999, "No luck"); + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/PrimedVase.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/PrimedVase.cs new file mode 100644 index 00000000..77c18358 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/PrimedVase.cs @@ -0,0 +1,19 @@ +using Exiled.API.Features; +using Exiled.API.Features.Items; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; + +internal class PrimedVase : ICoinEffect +{ + public string Name { get; set; } = "PrimedVase"; + public string Message { get; set; } = "You're grandma come to visit you"; + public int Weight { get; set; } = 20; + public EffectType Type { get; set; } = EffectType.Negative; + + public void Execute(Player player) + { + Scp244 vase = (Scp244)Item.Create(ItemType.SCP244a); + vase.Primed = true; + vase.CreatePickup(player.Position); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/RandomBadEffect.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/RandomBadEffect.cs new file mode 100644 index 00000000..72185238 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/RandomBadEffect.cs @@ -0,0 +1,34 @@ +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; + +internal class RandomBadEffect : ICoinEffect +{ + public string Name { get; set; } = "RandomBadEffect"; + public string Message { get; set; } = "You got a random effect !"; + public int Weight { get; set; } = 20; + public KE.Misc.Features.GamblingCoin.Types.EffectType Type { get; set; } = KE.Misc.Features.GamblingCoin.Types.EffectType.Positive; + + public void Execute(Player player) + { + var negativeEffects = Enum.GetValues(typeof(EffectType)) + .Cast() + .Where(e => e.GetCategories().HasFlag(EffectCategory.Negative)) + .ToList(); + + if (negativeEffects.Count == 0) + { + Log.Warn("no negative effect found"); + return; + } + + + var randomEffect = negativeEffects[UnityEngine.Random.Range(0, negativeEffects.Count)]; + + player.EnableEffect(randomEffect, 5, true); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/RandomTeleport.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/RandomTeleport.cs new file mode 100644 index 00000000..679c7c66 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/RandomTeleport.cs @@ -0,0 +1,18 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin.Interfaces; +using System; +using EffectType = KE.Misc.Features.GamblingCoin.Types.EffectType; + +public class RandomTeleport : ICoinEffect +{ + public string Name { get; set; } = "RandomTeleport"; + public string Message { get; set; } = "You were randomly teleported"; + public int Weight { get; set; } = 15; + public EffectType Type { get; set; } = EffectType.Negative; + + public void Execute(Player player) + { + player.Teleport(Room.Random()); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/RandomizeInventory.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/RandomizeInventory.cs new file mode 100644 index 00000000..447e6999 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/RandomizeInventory.cs @@ -0,0 +1,31 @@ +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; +using System; + +internal class RandomizeInventory : ICoinEffect +{ + public string Name { get; set; } = "RandomizeInventory"; + public string Message { get; set; } = "You shouldn't gamble !"; + public int Weight { get; set; } = 5; + public EffectType Type { get; set; } = EffectType.Negative; + + public void Execute(Player player) + { + player.ClearInventory(); + int numberOfItem = UnityEngine.Random.Range(0, 9); + + for (int i = 0; i < numberOfItem; i++) + { + player.AddItem(GiveRandomItem()); + } + } + + public static ItemType GiveRandomItem() + { + Array values = Enum.GetValues(typeof(ItemType)); + ItemType randomItem = (ItemType)values.GetValue(UnityEngine.Random.Range(0, values.Length)); + + return randomItem; + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/ReduceHP.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/ReduceHP.cs new file mode 100644 index 00000000..13d5b140 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/ReduceHP.cs @@ -0,0 +1,21 @@ +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; + +internal class ReduceHP : ICoinEffect +{ + public string Name { get; set; } = "ReduceHP"; + public string Message { get; set; } = "The coin fall on your head !"; + public int Weight { get; set; } = 20; + public EffectType Type { get; set; } = EffectType.Negative; + + /// + /// Damage dealt to the player + /// + public static float Damage = 30; + + public void Execute(Player player) + { + player.Hurt(Damage, "Coin fall too hard"); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/Reversed.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/Reversed.cs new file mode 100644 index 00000000..b7bc73e8 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/Reversed.cs @@ -0,0 +1,38 @@ +using CustomPlayerEffects; +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin.Interfaces; +using EffectType = KE.Misc.Features.GamblingCoin.Types.EffectType; + +internal class Reversed : IDurationEffect +{ + public string Name { get; set; } = "Reversed"; + public string Message { get; set; } = "Oops ! I connected your keyboard in the wrong way, sorryyyy"; + public float Duration { get; set; } = 30; + public int Weight { get; set; } = 5; + public EffectType Type { get; set; } = EffectType.Negative; + + public void Execute(Player player) + { + StatusEffectBase effect = player.GetEffect(Exiled.API.Enums.EffectType.Slowness); + if (effect.Intensity >= 200) + { + effect.Intensity = 0; + } else + { + player.EnableEffect(Exiled.API.Enums.EffectType.Slowness, 200); + } + } + + public void ExecuteAfterDuration(Player player) + { + StatusEffectBase effect = player.GetEffect(Exiled.API.Enums.EffectType.Slowness); + if (effect.Intensity >= 200) + { + effect.Intensity = 0; + } + else + { + player.EnableEffect(Exiled.API.Enums.EffectType.Slowness, 200); + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/Shit.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/Shit.cs new file mode 100644 index 00000000..fa82e1f5 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/Shit.cs @@ -0,0 +1,17 @@ +using Exiled.API.Features; +using Exiled.API.Features.Items; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; + +internal class Shit : ICoinEffect +{ + public string Name { get; set; } = "Shit"; + public string Message { get; set; } = "The tacos of the otherday was not passed very good"; + public int Weight { get; set; } = 40; + public EffectType Type { get; set; } = EffectType.Negative; + + public void Execute(Player player) + { + player.PlaceTantrum(); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/SwapInventory.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/SwapInventory.cs new file mode 100644 index 00000000..5c7944a7 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/SwapInventory.cs @@ -0,0 +1,59 @@ +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin; +using KE.Misc.Features.GamblingCoin.Interfaces; +using System.Collections.Generic; +using System.Linq; +using EffectType = KE.Misc.Features.GamblingCoin.Types.EffectType; + +public class SwapInventory : ICoinEffect +{ + public string Name { get; set; } = "SwapInventory"; + public string Message { get; set; } = "I think you don't deserve this stuff, i take it away"; + public int Weight { get; set; } = 20; + public EffectType Type { get; set; } = EffectType.Negative; + + public void Execute(Player player) + { + List playerList = Player.List.Where(x => x != player && !x.IsScp).ToList(); + + if (playerList.Count(x => x.IsAlive) <= 1) + { + player.Hurt(50); + return; + } + + var target = playerList.Where(x => x != player).ToList().RandomItem(); + + List items1 = player.Items.Select(item => item.Type).ToList(); + List items2 = target.Items.Select(item => item.Type).ToList(); + + Dictionary ammo1 = new Dictionary(); + Dictionary ammo2 = new Dictionary(); + for (int i = 0; i < player.Ammo.Count; i++) + { + ammo1.Add(player.Ammo.ElementAt(i).Key.GetAmmoType(), player.Ammo.ElementAt(i).Value); + player.SetAmmo(ammo1.ElementAt(i).Key, 0); + } + for (int i = 0; i < target.Ammo.Count; i++) + { + ammo2.Add(target.Ammo.ElementAt(i).Key.GetAmmoType(), target.Ammo.ElementAt(i).Value); + target.SetAmmo(ammo2.ElementAt(i).Key, 0); + } + + target.ResetInventory(items1); + player.ResetInventory(items2); + + foreach (var ammo in ammo2) + { + player.SetAmmo(ammo.Key, ammo.Value); + } + foreach (var ammo in ammo1) + { + target.SetAmmo(ammo.Key, ammo.Value); + } + + PlayerUtils.SendBroadcast(target, this.Message); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/SwapPosition.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/SwapPosition.cs new file mode 100644 index 00000000..2ac47923 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/SwapPosition.cs @@ -0,0 +1,31 @@ +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; +using System.Linq; + +internal class SwapPosition : ICoinEffect +{ + public string Name { get; set; } = "SwapPosition"; + public string Message { get; set; } = "Congratulations ! You just unlocked the premium teleport Uber service... but with no refunds."; + public int Weight { get; set; } = 20; + public EffectType Type { get; set; } = EffectType.Negative; + + public void Execute(Player player) + { + var playerList = Player.List.Where(x => x.IsAlive).ToList(); + playerList.Remove(player); + + if (playerList.IsEmpty()) + { + return; + } + + var targetPlayer = playerList.RandomItem(); + var pos = targetPlayer.Position; + + targetPlayer.Teleport(player.Position); + PlayerUtils.SendBroadcast(targetPlayer, this.Message); + player.Teleport(pos); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/SwapWithSpectator.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/SwapWithSpectator.cs new file mode 100644 index 00000000..a4aae25c --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/SwapWithSpectator.cs @@ -0,0 +1,50 @@ +using Exiled.API.Extensions; +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; +using PlayerRoles; +using System.Collections.Generic; +using System.Linq; + +public class SwapWithSpectator : ICoinEffect +{ + public string Name { get; set; } = "SwapWithSpectator"; + public string Message { get; set; } = "You just made someone's round better !"; + public int Weight { get; set; } = 10; + public EffectType Type { get; set; } = EffectType.Negative; + + public void Execute(Player player) + { + var spectList = Player.List.Where(x => x.Role.Type == RoleTypeId.Spectator).ToList(); + + if (spectList.IsEmpty()) + { + PlayerUtils.SendBroadcast(player, "you got lucky !"); + return; + } + + var spect = spectList.RandomItem(); + + spect.Role.Set(player.Role.Type, RoleSpawnFlags.None); + spect.Teleport(player); + spect.Health = player.Health; + + List playerItems = player.Items.Select(item => item.Type).ToList(); + + foreach (var item in playerItems) + { + spect.AddItem(item); + } + + + for (int i = 0; i < player.Ammo.Count; i++) + { + spect.AddAmmo(player.Ammo.ElementAt(i).Key.GetAmmoType(), player.Ammo.ElementAt(i).Value); + player.SetAmmo(player.Ammo.ElementAt(i).Key.GetAmmoType(), 0); + } + + player.ClearInventory(); + player.Role.Set(RoleTypeId.Spectator); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/TeleportToDBoyCell.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/TeleportToDBoyCell.cs new file mode 100644 index 00000000..f26a2533 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/TeleportToDBoyCell.cs @@ -0,0 +1,24 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Doors; +using KE.Misc.Features.GamblingCoin.Interfaces; +using EffectType = KE.Misc.Features.GamblingCoin.Types.EffectType; + +internal class TeleportToDBoyCell : ICoinEffect +{ + public string Name { get; set; } = "TeleportToDBoyCell"; + public string Message { get; set; } = "You got teleported to Class D cells !"; + public int Weight { get; set; } = 5; + public EffectType Type { get; set; } = EffectType.Negative; + + public void Execute(Player player) + { + player.DropHeldItem(); + player.Teleport(Door.Get(DoorType.PrisonDoor)); + + if (Warhead.IsDetonated) + { + player.Kill(DamageType.Decontamination, "You were teleported into a radioactive zone."); + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/TeleportToEnemy.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/TeleportToEnemy.cs new file mode 100644 index 00000000..a4750628 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/TeleportToEnemy.cs @@ -0,0 +1,45 @@ +using Exiled.API.Extensions; +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Utils.API.Features.SCPs; +using PlayerRoles; +using System.Collections.Generic; +using System.Linq; +using EffectType = KE.Misc.Features.GamblingCoin.Types.EffectType; + +internal class TeleportToEnemy : ICoinEffect +{ + public string Name { get; set; } = "TeleportToEnemy"; + public string Message { get; set; } = "You were teleported to an enemy !!"; + public int Weight { get; set; } = 30; + public EffectType Type { get; set; } = EffectType.Negative; + + public void Execute(Player player) + { + + + List scp = SCPTeam.SCPs.ToList(); + + + Player target = null; + + if (scp.Count > 0) + { + target = Player.Get(scp.GetRandomValue()); + } + else + { + var enemies = Player.List + .Where(p => p != player && p.Role.Team != player.Role.Team) + .ToList(); + + if (enemies.Count > 0) + target = enemies[UnityEngine.Random.Range(0, enemies.Count)]; + } + + if (target != null) + { + player.Teleport(target.Position); + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/TransformZombie.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/TransformZombie.cs new file mode 100644 index 00000000..05c26b53 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/TransformZombie.cs @@ -0,0 +1,16 @@ +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; + +internal class TransformZombie : ICoinEffect +{ + public string Name { get; set; } = "TransformZombie"; + public string Message { get; set; } = string.Empty; + public int Weight { get; set; } = 30; + public EffectType Type { get; set; } = EffectType.Negative; + + public void Execute(Player player) + { + player.Role.Set(PlayerRoles.RoleTypeId.Scp0492); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/TurnOffLight.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/TurnOffLight.cs new file mode 100644 index 00000000..6093e483 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/TurnOffLight.cs @@ -0,0 +1,18 @@ +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin.Interfaces; +using EffectType = KE.Misc.Features.GamblingCoin.Types.EffectType; + +internal class TurnOffLight : ICoinEffect +{ + public string Name { get; set; } = "TurnOffLight"; + public string Message { get; set; } = string.Empty; + public int Weight { get; set; } = 10; + public EffectType Type { get; set; } = EffectType.Negative; + + public static int BlackoutTime = 10; + + public void Execute(Player player) + { + Map.TurnOffAllLights(BlackoutTime); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/Vaporize.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/Vaporize.cs new file mode 100644 index 00000000..6cfc148a --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/Vaporize.cs @@ -0,0 +1,23 @@ +using Exiled.API.Features; +using Exiled.API.Features.Items; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; +using MEC; + +internal class Vaporize : ICoinEffect +{ + public string Name { get; set; } = "Vaporize"; + public string Message { get; set; } = "I think eating mosses in your cells is not good for you"; + public int Weight { get; set; } = 1; + public EffectType Type { get; set; } = EffectType.Negative; + + public static int MaxSeconds = 600; + + public void Execute(Player player) + { + Timing.CallDelayed(UnityEngine.Random.Range(30, MaxSeconds), () => + { + player.Vaporize(); + }); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/WarheadCoin.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/WarheadCoin.cs new file mode 100644 index 00000000..e742dd2b --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/WarheadCoin.cs @@ -0,0 +1,17 @@ +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin.Interfaces; +using EffectType = KE.Misc.Features.GamblingCoin.Types.EffectType; + +internal class WarheadCoin : ICoinEffect +{ + public string Name { get; set; } = "Warhead"; + public string Message { get; set; } = string.Empty; + public int Weight { get; set; } = 10; + public EffectType Type { get; set; } = EffectType.Negative; + + public void Execute(Player player) + { + if (!Warhead.IsDetonated || !Warhead.IsInProgress) + Warhead.Start(); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/YouLoveTesla.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/YouLoveTesla.cs new file mode 100644 index 00000000..20aa9e98 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/NegativeEffect/YouLoveTesla.cs @@ -0,0 +1,24 @@ +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; +using System.Linq; + +internal class YouLoveTesla : ICoinEffect +{ + public string Name { get; set; } = "YouLoveTesla"; + public string Message { get; set; } = "You love Tesla much more than Elon Musk"; + public int Weight { get; set; } = 15; + public EffectType Type { get; set; } = EffectType.Negative; + + public void Execute(Player player) + { + player.DropHeldItem(); + + player.Teleport(Exiled.API.Features.TeslaGate.List.ToList().RandomItem()); + + if (Warhead.IsDetonated) + { + player.Kill(Exiled.API.Enums.DamageType.Decontamination); + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/EmptyMicro.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/EmptyMicro.cs new file mode 100644 index 00000000..08fcbfb4 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/EmptyMicro.cs @@ -0,0 +1,20 @@ +using Exiled.API.Features; +using Exiled.API.Features.Pickups; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; + +internal class EmptyMicro : ICoinEffect +{ + public string Name { get; set; } = "EmptyMicro"; + public string Message { get; set; } = "DID YOU JUST GET A MICRO HID !?"; + public int Weight { get; set; } = 5; + public EffectType Type { get; set; } = EffectType.Positive; + + public void Execute(Player player) + { + MicroHIDPickup item = (MicroHIDPickup)Pickup.Create(ItemType.MicroHID); + item.Position = player.Position; + item.Energy = 2; + item.Spawn(); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/ForceRespawn.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/ForceRespawn.cs new file mode 100644 index 00000000..75e74084 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/ForceRespawn.cs @@ -0,0 +1,17 @@ +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; +using Respawning; + +internal class ForceRespawn : ICoinEffect +{ + public string Name { get; set; } = "ForceRespawn"; + public string Message { get; set; } = "Someone respawned... probably."; + public int Weight { get; set; } = 15; + public EffectType Type { get; set; } = EffectType.Positive; + + public void Execute(Player player) + { + Respawn.ForceWave(WaveManager.Waves.RandomItem()); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/GiveKeycard.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/GiveKeycard.cs new file mode 100644 index 00000000..72eaee8b --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/GiveKeycard.cs @@ -0,0 +1,31 @@ +using Exiled.API.Features; +using Exiled.API.Features.Pickups; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; +using UnityEngine; + +internal class GiveKeycard : ICoinEffect +{ + public string Name { get; set; } = "GiveKeycard"; + public string Message { get; set; } = "You got a keycard !"; + public int Weight { get; set; } = 20; + public EffectType Type { get; set; } = EffectType.Positive; + + /// + /// Chance in % to get a FacilityManager Card instead of containement engineer one. + /// + public static float FacilityManagerCard = 15; + + public void Execute(Player player) + { + float random = UnityEngine.Random.Range(1f, 100f); + ItemType keycard = ItemType.KeycardContainmentEngineer; + + if(random <= FacilityManagerCard) + { + keycard = ItemType.KeycardFacilityManager; + } + + Pickup.CreateAndSpawn(keycard, player.Position, new Quaternion()); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/Gnomed.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/Gnomed.cs new file mode 100644 index 00000000..3ae11980 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/Gnomed.cs @@ -0,0 +1,17 @@ +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; +using UnityEngine; + +internal class Gnomed : ICoinEffect +{ + public string Name { get; set; } = "Gnomed"; + public string Message { get; set; } = "You got gnomed."; + public int Weight { get; set; } = 20; + public EffectType Type { get; set; } = EffectType.Positive; + + public void Execute(Player player) + { + player.Scale = new Vector3(1.13f, 0.5f, 1.13f); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/HealPlayer.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/HealPlayer.cs new file mode 100644 index 00000000..86526dfc --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/HealPlayer.cs @@ -0,0 +1,20 @@ +using Exiled.API.Features; +using Exiled.API.Features.Pickups; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; +using UnityEngine; + +internal class HealPlayer : ICoinEffect +{ + public string Name { get; set; } = "HealPlayer"; + public string Message { get; set; } = "You got magically healed !"; + public int Weight { get; set; } = 10; + public EffectType Type { get; set; } = EffectType.Positive; + + public static int Heal = 25; + + public void Execute(Player player) + { + player.Heal(Heal, true); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/IncreasePlayerHealth.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/IncreasePlayerHealth.cs new file mode 100644 index 00000000..a2306668 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/IncreasePlayerHealth.cs @@ -0,0 +1,24 @@ +using Exiled.API.Features; +using Exiled.API.Features.Pickups; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; +using UnityEngine; + +internal class IncreasePlayerHealth : ICoinEffect +{ + public string Name { get; set; } = "IncreasePlayerHeal"; + //human i remember you're life expectancy + public string Message { get; set; } = "You're life expectancy is extended !"; + public int Weight { get; set; } = 10; + public EffectType Type { get; set; } = EffectType.Positive; + + /// + /// % of heal applied to the player. + /// + public int Heal = 30; + + public void Execute(Player player) + { + player.Heal(((player.Health*Heal)/100), true); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/KaboomCandy.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/KaboomCandy.cs new file mode 100644 index 00000000..0ba17e31 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/KaboomCandy.cs @@ -0,0 +1,19 @@ +using Exiled.API.Features; +using Exiled.API.Features.Items; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; + +internal class KaboomCandy : ICoinEffect +{ + public string Name { get; set; } = "KaboomCandy"; + public string Message { get; set; } = "Pink candy kaboooom !!"; + public int Weight { get; set; } = 10; + public EffectType Type { get; set; } = EffectType.Positive; + + public void Execute(Player player) + { + Scp330 candy = (Scp330)Item.Create(ItemType.SCP330); + candy.AddCandy(InventorySystem.Items.Usables.Scp330.CandyKindID.Pink); + candy.CreatePickup(player.Position); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/Lightbulb.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/Lightbulb.cs new file mode 100644 index 00000000..92126a18 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/Lightbulb.cs @@ -0,0 +1,18 @@ +using Exiled.API.Features; +using Exiled.API.Features.Pickups; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; +using UnityEngine; + +internal class Lightbulb : ICoinEffect +{ + public string Name { get; set; } = "Lightbulb"; + public string Message { get; set; } = "Follow the light !!"; + public int Weight { get; set; } = 15; + public EffectType Type { get; set; } = EffectType.Positive; + + public void Execute(Player player) + { + Pickup.CreateAndSpawn(ItemType.SCP2176, player.Position, new Quaternion()); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/NiceHat.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/NiceHat.cs new file mode 100644 index 00000000..35ae58b7 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/NiceHat.cs @@ -0,0 +1,18 @@ +using Exiled.API.Features; +using Exiled.API.Features.Pickups; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; +using UnityEngine; + +internal class NiceHat : ICoinEffect +{ + public string Name { get; set; } = "NiceHat"; + public string Message { get; set; } = "You've got a nice hat !"; + public int Weight { get; set; } = 10; + public EffectType Type { get; set; } = EffectType.Positive; + + public void Execute(Player player) + { + Pickup.CreateAndSpawn(ItemType.SCP268, player.Position, Quaternion.identity); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/NightVision.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/NightVision.cs new file mode 100644 index 00000000..4606927c --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/NightVision.cs @@ -0,0 +1,19 @@ +using Exiled.API.Features; +using KE.Utils.API.Features.SCPs; +using KE.Misc.Features.GamblingCoin.Interfaces; +using EffectType = KE.Misc.Features.GamblingCoin.Types.EffectType; + +internal class NightVision : ICoinEffect +{ + public string Name { get; set; } = "NightVision"; + public string Message { get; set; } = "Yippee, you can see in the dark"; + public int Weight { get; set; } = 5; + public EffectType Type { get; set; } = EffectType.Positive; + public float Duration { get; set; } = 30; + + public void Execute(Player player) + { + if (SCPTeam.IsSCP(player.ReferenceHub)) return; + player.EnableEffect(100, Duration, true); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/OneAmmoLogicer.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/OneAmmoLogicer.cs new file mode 100644 index 00000000..daaa91ba --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/OneAmmoLogicer.cs @@ -0,0 +1,19 @@ +using Exiled.API.Features; +using Exiled.API.Features.Items; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; + +internal class OneAmmoLogicer : ICoinEffect +{ + public string Name { get; set; } = "OneAmmoLogicer"; + public string Message { get; set; } = "Were you hiding that in your... "; + public int Weight { get; set; } = 1; + public EffectType Type { get; set; } = EffectType.Positive; + + public void Execute(Player player) + { + Firearm gun = (Firearm)Item.Create(ItemType.GunLogicer); + gun.BarrelAmmo = 1; + gun.CreatePickup(player.Position); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/RandomGoodEffect.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/RandomGoodEffect.cs new file mode 100644 index 00000000..d51565cf --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/RandomGoodEffect.cs @@ -0,0 +1,33 @@ +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; + +internal class RandomGoodEffect : ICoinEffect +{ + public string Name { get; set; } = "RandomGoodEffect"; + public string Message { get; set; } = "You got a random effect !"; + public int Weight { get; set; } = 10; + public KE.Misc.Features.GamblingCoin.Types.EffectType Type { get; set; } = KE.Misc.Features.GamblingCoin.Types.EffectType.Positive; + + public void Execute(Player player) + { + var positiveEffects = Enum.GetValues(typeof(EffectType)) + .Cast() + .Where(e => e.GetCategories().HasFlag(EffectCategory.Positive)) + .ToList(); + + if (positiveEffects.Count == 0) + { + Log.Warn("no negative effect found"); + return; + } + + var randomEffect = positiveEffects[UnityEngine.Random.Range(0, positiveEffects.Count)]; + + player.EnableEffect(randomEffect, 5,true); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/RandomItem.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/RandomItem.cs new file mode 100644 index 00000000..9b41a383 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/RandomItem.cs @@ -0,0 +1,21 @@ +using Exiled.API.Features; +using Exiled.API.Features.Items; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; +using System; + +internal class RandomItem : ICoinEffect +{ + public string Name { get; set; } = "RandomItem"; + public string Message { get; set; } = "You got a random item !"; + public int Weight { get; set; } = 35; + public EffectType Type { get; set; } = EffectType.Positive; + + public void Execute(Player player) + { + Array values = Enum.GetValues(typeof(ItemType)); + ItemType randomItem = (ItemType)values.GetValue(UnityEngine.Random.Range(0, values.Length)); + + Item.Create(randomItem).CreatePickup(player.Position); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/RandomKeycard.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/RandomKeycard.cs new file mode 100644 index 00000000..969509ef --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/RandomKeycard.cs @@ -0,0 +1,34 @@ +using Exiled.API.Features; +using Exiled.API.Features.Items; +using Exiled.API.Features.Pickups; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; +using UnityEngine; + +internal class GiveRandomKeycard : ICoinEffect +{ + public string Name { get; set; } = "GiveRandomKeycard"; + public string Message { get; set; } = "You got a keycard !"; + public int Weight { get; set; } = 20; + public EffectType Type { get; set; } = EffectType.Positive; + + public void Execute(Player player) + { + LabApi.Features.Wrappers.KeycardItem keycard = LabApi.Features.Wrappers.KeycardItem.CreateCustomKeycardManagement(player, "Keycard", "Keycard", + new Interactables.Interobjects.DoorUtils.KeycardLevels(Random.Range(0, 4), Random.Range(0, 4), Random.Range(0, 4)), RandomColor(), RandomColor(), RandomColor()); + + + player.DropItem(Item.Get(keycard.Base)); + } + + private Color32 RandomColor() + { + return new Color32 + ( + (byte)Random.Range(0, 256), + (byte)Random.Range(0, 256), + (byte)Random.Range(0, 256), + (byte)Random.Range(0, 256) + ); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/RedCandy.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/RedCandy.cs new file mode 100644 index 00000000..7c7eda3e --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/RedCandy.cs @@ -0,0 +1,19 @@ +using Exiled.API.Features; +using Exiled.API.Features.Items; +using KE.Misc.Features.GamblingCoin.Interfaces; +using EffectType = KE.Misc.Features.GamblingCoin.Types.EffectType; + +public class RedCandy : ICoinEffect +{ + public string Name { get; set; } = "RedCandy"; + public string Message { get; set; } = "You got a pink candy ! Wait is that pink no ?"; + public int Weight { get; set; } = 20; + public EffectType Type { get; set; } = EffectType.Positive; + + public void Execute(Player player) + { + Scp330 candy = (Scp330)Item.Create(ItemType.SCP330); + candy.AddCandy(InventorySystem.Items.Usables.Scp330.CandyKindID.Red); + candy.CreatePickup(player.Position); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/Revolver.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/Revolver.cs new file mode 100644 index 00000000..f413b0b3 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/Revolver.cs @@ -0,0 +1,21 @@ +using Exiled.API.Features; +using Exiled.API.Features.Items; +using InventorySystem.Items.Firearms.Attachments; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; + +internal class Revolver : ICoinEffect +{ + public string Name { get; set; } = "Revolver"; + public string Message { get; set; } = "It's a gift from the sky !!"; + public int Weight { get; set; } = 5; + public EffectType Type { get; set; } = EffectType.Positive; + + public void Execute(Player player) + { + Firearm revo = (Firearm)Item.Create(ItemType.GunRevolver); + revo.AddAttachment(new[] + {AttachmentName.CylinderMag7, AttachmentName.ShortBarrel, AttachmentName.ScopeSight}); + revo.CreatePickup(player.Position); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/SpawnHeal.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/SpawnHeal.cs new file mode 100644 index 00000000..4b2107e0 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/SpawnHeal.cs @@ -0,0 +1,19 @@ +using Exiled.API.Features; +using Exiled.API.Features.Pickups; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; +using UnityEngine; + +internal class SpawnHeal : ICoinEffect +{ + public string Name { get; set; } = "SpawnHeal"; + public string Message { get; set; } = string.Empty; + public int Weight { get; set; } = 35; + public EffectType Type { get; set; } = EffectType.Positive; + + public void Execute(Player player) + { + Pickup.CreateAndSpawn(ItemType.Medkit, player.Position, new Quaternion()); + Pickup.CreateAndSpawn(ItemType.Painkillers, player.Position, new Quaternion()); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/TeleportToEscape.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/TeleportToEscape.cs new file mode 100644 index 00000000..b306adb3 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Effect/PositiveEffect/TeleportToEscape.cs @@ -0,0 +1,18 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Doors; +using KE.Misc.Features.GamblingCoin.Interfaces; +using EffectType = KE.Misc.Features.GamblingCoin.Types.EffectType; + +internal class TeleportToEscape : ICoinEffect +{ + public string Name { get; set; } = "TeleportToEscape"; + public string Message { get; set; } = string.Empty; + public int Weight { get; set; } = 5; + public EffectType Type { get; set; } = EffectType.Positive; + + public void Execute(Player player) + { + player.Teleport(Door.Get(DoorType.EscapeSecondary)); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/EventHandlers.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/EventHandlers.cs new file mode 100644 index 00000000..582d3ef7 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/EventHandlers.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using Exiled.API.Features; +using Exiled.API.Features.Items; +using Exiled.CustomItems.API.Features; +using Exiled.Events.EventArgs.Player; +using KE.Misc.Events.EventsArgs.GamblingCoinsEventArgs; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; +using KE.Utils.API.Features; +using MEC; + +namespace KE.Misc.Features.GamblingCoin +{ + public class EventHandlers + { + private static Config Config => MainPlugin.Configs; + + private readonly Dictionary _cooldowns = new Dictionary(); + public static Dictionary CoinUses = new Dictionary(); + + public void OnCoinFlip(FlippingCoinEventArgs ev) + { + Player player = ev.Player; + Item item = ev.Item; + + if (CustomItem.TryGet(item, out _)) return; + + + GamblingEventArgs ev1 = new GamblingEventArgs(player, item, true); + + Events.Handlers.GamblingCoins.OnGambling(ev1); + + if (!ev1.IsAllowed) return; + + if (_cooldowns.TryGetValue(player.UserId, out var lastFlip) && + (DateTime.UtcNow - lastFlip).TotalSeconds < Config.GamblingCoinCooldown) + { + ev.IsAllowed = false; + PlayerUtils.SendBroadcast(player, "You must wait before flipping again"); + return; + } + + _cooldowns[player.UserId] = DateTime.UtcNow; + ushort itemSerial = player.CurrentItem.Serial; + + + if (!CoinUses.ContainsKey(itemSerial)) + { + CoinUses[itemSerial] = UnityEngine.Random.Range(Config.GamblingCoinMinUse, Config.GamblingCoinMaxUse); + + KELog.Debug($"Registered new coin: {CoinUses[itemSerial]} uses left."); + } + + CoinUses[itemSerial]--; + + + + EffectType type = ev.IsTails ? EffectType.Negative : EffectType.Positive; + + ICoinEffect effect = GamblingCoinManager.GetRandomEffect(type); + + if (effect == null) + { + Log.Warn($"No {type} effect found in GamblingCoinManager!"); + player.Broadcast(5, "This coin is empty."); + return; + } + + + effect.ExecuteEffect(player); + + if (!string.IsNullOrEmpty(effect.Message)) + { + PlayerUtils.SendBroadcast(player, effect.Message); + } + + int remainingUses = CoinUses[itemSerial]; + bool shouldBreak = remainingUses <= 0; + if (shouldBreak) + { + CoinUses.Remove(itemSerial); + player.RemoveHeldItem(); + player.Broadcast(5, "no more coin"); + item = null; + } + GambledEventArgs ev2 = new GambledEventArgs(player, item, effect, remainingUses, shouldBreak); + + Events.Handlers.GamblingCoins.OnGambled(ev2); + + CoinUses[itemSerial] = ev2.RemainingUses; + } + + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/ForceEffect.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/ForceEffect.cs new file mode 100644 index 00000000..7a327484 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/ForceEffect.cs @@ -0,0 +1,73 @@ +using CommandSystem; +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features.GamblingCoin +{ + [CommandHandler(typeof(RemoteAdminCommandHandler))] + public class ForceCoinEffect : ICommand + { + public string Command => "forcecoineffect"; + + public string[] Aliases => new string[] { "fce" }; + + public string Description => "force a effect for a player"; + + public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) + { + Player player = Player.Get(sender); + + if (player is null) + { + response = "player not found"; + return false; + } + + if (arguments.Count < 1) + { + response = "need effect"; + return false; + } + + ICoinEffect chose= null; + foreach (ICoinEffect effect in GamblingCoinManager.EffectList) + { + if (effect.Name == arguments.At(0)) + { + chose = effect; + break; + } + } + + + + + + + if (chose is null) + { + response = $"effect {arguments.At(0)} not found"; + return false; + } + + + try + { + chose.ExecuteEffect(player); + } + catch(Exception e) + { + Log.Error(e); + } + response = $"forced effect {chose.Name} on {player.Nickname}"; + return true; + + + } + } +} diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/GamblingCoinManager.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/GamblingCoinManager.cs new file mode 100644 index 00000000..f0149dbd --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/GamblingCoinManager.cs @@ -0,0 +1,115 @@ +using Exiled.API.Extensions; +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin.Interfaces; +using KE.Misc.Features.GamblingCoin.Types; +using KE.Utils.API; +using KE.Utils.API.Features; +using MEC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace KE.Misc.Features.GamblingCoin +{ + public static class GamblingCoinManager + { + private static readonly Dictionary _nameLookup = new Dictionary(); + private static readonly HashSet _activeEffects = new HashSet(); + + public static List EffectList { get; private set; } = new List(); + + public static IEnumerable RegisterAll() + { + List assemblies = new List(); + foreach (var plugin in Exiled.Loader.Loader.Plugins) + assemblies.Add(plugin.Assembly); + + IEnumerable effects = ReflectionHelper.GetObjects(assemblies) + .Where(e => e != null); + + foreach (var effect in effects) + { + try + { + Register(effect); + } + catch (Exception ex) + { + Log.Error($"[GamblingCoin] Failed to register {effect.Name} : {ex}"); + } + } + + BuildWeightedList(); + + return effects; + } + + public static void Register(ICoinEffect effect) + { + if (_nameLookup.ContainsKey(effect.Name)) + throw new Exception($"Effect with Name {effect.Name} is already registered."); + + _nameLookup.Add(effect.Name, effect); + + if (effect.Weight > 0) + _activeEffects.Add(effect); + + KELog.Register($"[GamblingCoin] {effect.Name}"); + + } + + private static void BuildWeightedList() + { + EffectList.Clear(); + foreach (var effect in _activeEffects) + { + for (int i = 0; i < effect.Weight; i++) + EffectList.Add(effect); + } + } + + public static void DestroyAll() + { + _nameLookup.Clear(); + _activeEffects.Clear(); + EffectList.Clear(); + } + + public static ICoinEffect GetRandomEffect() + { + if (EffectList.Count == 0) return null; + + return (EffectList.GetRandomValue()); + } + + public static ICoinEffect GetRandomEffect(EffectType type) + { + if(EffectList.Count == 0) return null; + + return(EffectList.Where(e => e.Type == type).GetRandomValue()); + } + + + public static void ExecuteEffect(this ICoinEffect effect,Player player) + { + if (effect == null) + { + throw new ArgumentNullException(); + } + + effect.Execute(player); + + if (effect is IDurationEffect durationEffect && durationEffect.Duration > 0) + { + float duration = durationEffect.Duration; + KELog.Debug("effect " + duration); + Timing.CallDelayed(duration, () => + { + KELog.Debug("effect " + duration); + durationEffect.ExecuteAfterDuration(player); + }); + } + } + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Interfaces/ICoinEffect.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Interfaces/ICoinEffect.cs new file mode 100644 index 00000000..4fa0ba10 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Interfaces/ICoinEffect.cs @@ -0,0 +1,37 @@ +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin.Types; + +namespace KE.Misc.Features.GamblingCoin.Interfaces +{ + /// + /// Represents an effect that can be applied via gambling coin. + /// + public interface ICoinEffect + { + /// + /// Name of the Coin Effect (must be unique). + /// + string Name { get; set; } + + /// + /// Message displayed to the player that used the coin. + /// + string Message { get; set; } + + /// + /// Weight for random selection. Higher weight = higher chance to be chosen. + /// + int Weight { get; set; } + + /// + /// Type of the effect (positive, negative, neutral). + /// + EffectType Type { get; set; } + + /// + /// Executes the effect on the given player. + /// + /// Player to apply the effect to. + void Execute(Player player); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Interfaces/IDurationEffect.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Interfaces/IDurationEffect.cs new file mode 100644 index 00000000..f1caf467 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Interfaces/IDurationEffect.cs @@ -0,0 +1,22 @@ +using Exiled.API.Features; +using KE.Misc.Features.GamblingCoin.Types; + +namespace KE.Misc.Features.GamblingCoin.Interfaces +{ + /// + /// Represents an effect that can be applied via gambling coin. + /// + public interface IDurationEffect : ICoinEffect + { + /// + /// Duration of the effect in seconds (-1 for infinite, 0 if not used). + /// + float Duration { get; set; } + + /// + /// Executes the effets on the given player after the duration time. + /// + /// + void ExecuteAfterDuration(Player player); + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/PlayerUtils.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/PlayerUtils.cs new file mode 100644 index 00000000..e198a0ce --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/PlayerUtils.cs @@ -0,0 +1,19 @@ +using System; +using System.Linq; +using Exiled.API.Features; +using KE.Utils.API.Displays.DisplayMeow; +using KE.Utils.API.Displays.DisplayMeow.Placements; + +namespace KE.Misc.Features.GamblingCoin +{ + internal static class PlayerUtils + { + + private static HintPosition Position = new CoinHintPosition(); + public static void SendBroadcast(Player player, string message) + { + DisplayHandler.Instance.AddHint(Position.HintPlacement, player, message,5); + + } + } +} diff --git a/KruacentExiled/KE.Misc/Features/GamblingCoin/Types/EffectType.cs b/KruacentExiled/KE.Misc/Features/GamblingCoin/Types/EffectType.cs new file mode 100644 index 00000000..832aafe0 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/GamblingCoin/Types/EffectType.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features.GamblingCoin.Types +{ + public enum EffectType + { + Positive, + Negative, + } +} diff --git a/KruacentExiled/KE.Misc/Features/LastHuman/LastHumanHandler.cs b/KruacentExiled/KE.Misc/Features/LastHuman/LastHumanHandler.cs new file mode 100644 index 00000000..e18683af --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/LastHuman/LastHumanHandler.cs @@ -0,0 +1,176 @@ +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.Events.EventArgs.Player; +using Exiled.Events.EventArgs.Scp106; +using Exiled.Events.Patches.Generic; +using HintServiceMeow.Core.Models.Hints; +using HintServiceMeow.Core.Utilities; +using KE.Utils.API.Displays.DisplayMeow; +using KE.Utils.API.Displays.DisplayMeow.Placements; +using KE.Utils.API.Features; +using KE.Utils.API.Features.SCPs; +using KE.Utils.API.Interfaces; +using KE.Utils.API.Translations; +using KE.Utils.Extensions; +using LabApi.Events.Arguments.PlayerEvents; +using PlayerRoles; +using PlayerRoles.FirstPersonControl.Thirdperson; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace KE.Misc.Features.LastHuman +{ + public class LastHumanHandler : IUsingEvents + { + + + + + + public static readonly IReadOnlyCollection TextLast = new HashSet() + { + "texthuman1", + "texthuman2", + }; + + public static readonly string TextSCP = "textscp"; + + + + public static HintPosition position = new LastHumanPosition(); + private DateTime _nextPossibleHint; + + public static readonly TimeSpan Cooldown = TimeSpan.FromSeconds(5); + public void SubscribeEvents() + { + LabApi.Events.Handlers.PlayerEvents.ChangedRole += OnChangedRole; + Exiled.Events.Handlers.Scp106.Attacking += OnAttacking; + } + + public void UnsubscribeEvents() + { + LabApi.Events.Handlers.PlayerEvents.ChangedRole -= OnChangedRole; + Exiled.Events.Handlers.Scp106.Attacking -= OnAttacking; + } + + + + private void OnAttacking(AttackingEventArgs ev) + { + if (ev.IsAllowed && TryGetLastTarget(out _)) + { + ev.Target.Kill(Exiled.API.Enums.DamageType.Scp106); + } + } + private void OnChangedRole(PlayerChangedRoleEventArgs ev) + { + + if (Round.ElapsedTime.TotalSeconds < 10) + return; + + if (ev.OldRole.IsScp()) return; + + if (SCPTeam.IsSCP(ev.Player.ReferenceHub)) return; + + + if (TryGetLastTarget(out Player lastTarget)) + { + if(lastTarget == LastTarget) + { + KELog.Debug("target didn't change"); + + return; + } + + LastTarget = lastTarget; + + foreach (Player player in Player.Enumerable) + { + AbstractHint hint = DisplayHandler.Instance.GetHint(player, position.HintPlacement); + + if (hint is null || hint.Hide) + { + + string translatedZone = lastTarget.Zone.GetTranslatedName(TranslationHub.GetLang(player)); + KELog.Debug("tra,slated zone ="+translatedZone); + string msg = string.Empty; + if (player == lastTarget) + { + msg = MainPlugin.GetTranslation(player, TextLast.GetRandomValue()); + } + else if (!player.IsDead) + { + + msg = MainPlugin.GetTranslation(player, TextSCP).Replace("%Zone%", translatedZone); + } + + + + + if (!player.IsDead && DateTime.Now >= _nextPossibleHint) + { + DisplayHandler.Instance.AddHint(position.HintPlacement, player, msg, 10); + KELog.Debug("show message to " + lastTarget.Nickname); + _nextPossibleHint = DateTime.Now.Add(Cooldown); + } + + + + } + else + { + KELog.Debug("hint still there"); + } + } + } + else + { + KELog.Debug("set target null"); + LastTarget = null; + } + } + private Player last; + private Player LastTarget + { + get + { + return last; + } + set + { + last = value; + } + } + + private bool TryGetLastTarget(out Player lastTarget) + { + lastTarget = null; + int numberHuman = 0; + int numberSCP = 0; + foreach (ReferenceHub allHub in ReferenceHub.AllHubs) + { + if (allHub.IsHuman() && !SCPTeam.IsSCP(allHub)) + { + numberHuman++; + lastTarget = Player.Get(allHub); + + } + else if (SCPTeam.IsSCP(allHub)) + { + numberSCP++; + } + } + + + + if (numberHuman == 1) + { + return numberSCP > 0; + } + return false; + } + + + } +} diff --git a/KruacentExiled/KE.Misc/Features/LastHuman/LastHumanPosition.cs b/KruacentExiled/KE.Misc/Features/LastHuman/LastHumanPosition.cs new file mode 100644 index 00000000..c7a0f462 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/LastHuman/LastHumanPosition.cs @@ -0,0 +1,20 @@ +using HintServiceMeow.Core.Enum; +using KE.Utils.API.Displays.DisplayMeow.Placements; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features.LastHuman +{ + public class LastHumanPosition : HintPosition + { + public override float Xposition => 0; + + public override float Yposition => 120; + public override string Name => "LastHuman"; + + public override HintAlignment HintAlignment => HintAlignment.Center; + } +} diff --git a/KruacentExiled/KE.Misc/Features/LastHuman/LastHumanTranslations.cs b/KruacentExiled/KE.Misc/Features/LastHuman/LastHumanTranslations.cs new file mode 100644 index 00000000..0863f3e7 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/LastHuman/LastHumanTranslations.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features.LastHuman +{ + public static class LastHumanTranslations + { + + + public static Dictionary> LangToKeyToTranslation = new Dictionary>() + { + ["en"] = new Dictionary() + { + ["texthuman1"] = "You feel like everyone is counting on you", + ["texthuman2"] = "You feel suddenly very lonely", + ["textscp"] = "The last human is at %Zone%", + }, + ["fr"] = new Dictionary() + { + ["texthuman1"] = "Tu a la sensation que tout le monde compte sur toi", + ["texthuman2"] = "Tu te sens soudainement très seul", + ["textscp"] = "Le dernier humain est à %Zone%", + }, + }; + + + } +} diff --git a/KruacentExiled/KE.Misc/Features/LoadingMiscFeature.cs b/KruacentExiled/KE.Misc/Features/LoadingMiscFeature.cs new file mode 100644 index 00000000..9a0227aa --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/LoadingMiscFeature.cs @@ -0,0 +1,43 @@ +using Exiled.API.Features; +using Exiled.Events.EventArgs.Player; +using Exiled.Events.EventArgs.Warhead; +using KE.Misc.Features.FriendlyFireConditions; +using KE.Utils.API; +using KE.Utils.API.Features; +using KE.Utils.API.Interfaces; +using System; +using System.Collections.Generic; + +namespace KE.Misc.Features +{ + internal class LoadingMiscFeature : MiscFeature + { + protected readonly HashSet _allLoadedFeatures; + + internal LoadingMiscFeature() : base() + { + _allLoadedFeatures = new HashSet(ReflectionHelper.GetObjects()); + } + + + public override void SubscribeEvents() + { + foreach(T loaded in _allLoadedFeatures) + { + KELog.Debug("subscribing "+ loaded.GetType().Name); + if(loaded is IUsingEvents iue) + iue.SubscribeEvents(); + } + } + + public override void UnsubscribeEvents() + { + foreach (T loaded in _allLoadedFeatures) + { + KELog.Debug("Unsubscribing " + loaded.GetType().Name); + if (loaded is IUsingEvents iue) + iue.UnsubscribeEvents(); + } + } + } +} diff --git a/KruacentExiled/KE.Misc/Features/LobbyHints/AspectRatioTypeExtension.cs b/KruacentExiled/KE.Misc/Features/LobbyHints/AspectRatioTypeExtension.cs new file mode 100644 index 00000000..27651042 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/LobbyHints/AspectRatioTypeExtension.cs @@ -0,0 +1,22 @@ +using Exiled.API.Enums; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features.LobbyHints +{ + public static class AspectRatioTypeExtension + { + + public static string GetTranslation(this AspectRatioType ratio) + { + if (ratio == AspectRatioType.Unknown) return "Unknown"; + string result = ratio.ToString(); + return result.Replace('_', ':').Remove(0,5); + } + + + } +} diff --git a/KruacentExiled/KE.Misc/Features/LobbyHints/LobbyHint.cs b/KruacentExiled/KE.Misc/Features/LobbyHints/LobbyHint.cs new file mode 100644 index 00000000..4d27eacd --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/LobbyHints/LobbyHint.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features.LobbyHints +{ + internal class LobbyHint : LoadingMiscFeature + { + } +} \ No newline at end of file diff --git a/KruacentExiled/KE.Misc/Features/LobbyHints/LobbyHintBase.cs b/KruacentExiled/KE.Misc/Features/LobbyHints/LobbyHintBase.cs new file mode 100644 index 00000000..a1dd024c --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/LobbyHints/LobbyHintBase.cs @@ -0,0 +1,53 @@ +using Exiled.API.Features; +using Exiled.Events.EventArgs.Player; +using KE.Utils.API.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features.LobbyHints +{ + internal abstract class LobbyHintBase : IUsingEvents + { + + + public void SubscribeEvents() + { + Exiled.Events.Handlers.Player.Verified += OnVerified; + Exiled.Events.Handlers.Server.RoundStarted += OnRoundStarted; + } + + public void UnsubscribeEvents() + { + Exiled.Events.Handlers.Player.Verified -= OnVerified; + Exiled.Events.Handlers.Server.RoundStarted -= OnRoundStarted; + } + + + + private void OnVerified(VerifiedEventArgs ev) + { + if (IsNotLobby()) return; + Init(ev.Player); + + } + + + private void OnRoundStarted() + { + Destroy(); + } + + + public abstract void Init(Player player); + public abstract void Destroy(); + + protected bool IsNotLobby() + { + return !Round.IsLobby; + } + + } +} diff --git a/KruacentExiled/KE.Misc/Features/LobbyHints/WrongAspectRatioHintPosition.cs b/KruacentExiled/KE.Misc/Features/LobbyHints/WrongAspectRatioHintPosition.cs new file mode 100644 index 00000000..e6b1e820 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/LobbyHints/WrongAspectRatioHintPosition.cs @@ -0,0 +1,20 @@ +using HintServiceMeow.Core.Enum; +using KE.Utils.API.Displays.DisplayMeow.Placements; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features.LobbyHints +{ + public class WrongAspectRatioHintPosition : HintPosition + { + public override float Xposition => 0; + + public override float Yposition => 900; + public override string Name => "WrongAspectRatio"; + + public override HintAlignment HintAlignment => HintAlignment.Center; + } +} diff --git a/KruacentExiled/KE.Misc/Features/LobbyHints/WrongAspectRatioWarningLobbyHint.cs b/KruacentExiled/KE.Misc/Features/LobbyHints/WrongAspectRatioWarningLobbyHint.cs new file mode 100644 index 00000000..6f25cac9 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/LobbyHints/WrongAspectRatioWarningLobbyHint.cs @@ -0,0 +1,68 @@ +using Exiled.API.Features; +using KE.Utils.API.Displays.DisplayMeow; +using KE.Utils.API.Displays.DisplayMeow.Placements; +using KE.Utils.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features.LobbyHints +{ + internal class WrongAspectRatioWarningLobbyHint : LobbyHintBase + { + + public const string WrongAspectRatioTranslation = "WrongAspectRatioTranslation"; + public static Dictionary> LangToKeyToTranslation = new Dictionary>() + { + ["en"] = new Dictionary() + { + [WrongAspectRatioTranslation] = "Warning wrong aspect ratio : %CurrAspect% instead of 16:9", + }, + ["fr"] = new Dictionary() + { + [WrongAspectRatioTranslation] = "Attention mauvaise résolution d'écran : %CurrAspect% au lieu de 16:9", + }, + }; + + private readonly HintPosition HintPosition = new WrongAspectRatioHintPosition(); + + public override void Init(Player player) + { + DisplayHandler.Instance.CreateAuto(player, (args) => GetWarning(player), HintPosition.HintPlacement,HintServiceMeow.Core.Enum.HintSyncSpeed.Slow); + } + + + public override void Destroy() + { + foreach(Player player in Player.List) + { + DisplayHandler.Instance.RemoveHint(player, HintPosition.HintPlacement); + } + } + + + + private string GetWarning(Player player) + { + if (IsNotLobby()) return " "; + if (CheckAspectRatio(player)) return " "; + + string translation = MainPlugin.GetTranslation(player, WrongAspectRatioTranslation); + + + return translation.Replace("%CurrAspect%",player.AspectRatio.GetTranslation()); + + } + + + public bool CheckAspectRatio(Player player) + { + return player.AspectRatio == Exiled.API.Enums.AspectRatioType.Ratio16_9; + } + } + + + +} diff --git a/KruacentExiled/KE.Misc/Features/MiscFeature.cs b/KruacentExiled/KE.Misc/Features/MiscFeature.cs new file mode 100644 index 00000000..3bbd96bc --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/MiscFeature.cs @@ -0,0 +1,49 @@ +using Exiled.API.Features; +using KE.Utils.API.Features; +using KE.Utils.API.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features +{ + public abstract class MiscFeature : IUsingEvents + { + private static HashSet _list = new HashSet(); + + + internal MiscFeature() + { + _list.Add(this); + } + + internal static void SubscribeAllEvents() + { + foreach (MiscFeature f in _list) + { + KELog.Debug($"subscribing events in {f.GetType()}"); + f.SubscribeEvents(); + } + + } + + internal static void UnsubscribeAllEvents() + { + foreach (MiscFeature f in _list) + f.UnsubscribeEvents(); + _list.Clear(); + } + + + public virtual void SubscribeEvents() + { + + } + public virtual void UnsubscribeEvents() + { + + } + } +} diff --git a/KruacentExiled/KE.Misc/Features/PostNuke/PostNukeHandler.cs b/KruacentExiled/KE.Misc/Features/PostNuke/PostNukeHandler.cs new file mode 100644 index 00000000..976cad6a --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/PostNuke/PostNukeHandler.cs @@ -0,0 +1,92 @@ +using Exiled.API.Features; +using Exiled.API.Features.Core.Generic; +using Exiled.Events.EventArgs.Warhead; +using KE.Utils.API.Features; +using KE.Utils.API.Features.SCPs; +using LabApi.Events.Arguments.WarheadEvents; +using MEC; +using PlayerRoles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features.PostNuke +{ + internal class PostNukeHandler : MiscFeature + { + + private Faction trigger = Faction.Unclassified; + + public override void SubscribeEvents() + { + LabApi.Events.Handlers.WarheadEvents.Detonated += OnDetonated; + LabApi.Events.Handlers.WarheadEvents.Starting += OnStarting; + + base.SubscribeEvents(); + } + + public override void UnsubscribeEvents() + { + LabApi.Events.Handlers.WarheadEvents.Detonated -= OnDetonated; + LabApi.Events.Handlers.WarheadEvents.Starting -= OnStarting; + base.UnsubscribeEvents(); + } + + private void OnStarting(WarheadStartingEventArgs ev) + { + if (SCPTeam.IsSCP(ev.Player.ReferenceHub)) + { + trigger = Faction.SCP; + } + else + { + trigger = ev.Player.Role.GetFaction(); + } + + + } + + private void OnDetonated(WarheadDetonatedEventArgs ev) + { + Player player = ev.Player; + + AlphaWarheadSyncInfo info = AlphaWarheadController.Singleton.Info; + + if (info.ScenarioType == WarheadScenarioType.DeadmanSwitch) return; + + if (player is null || player.IsHost) + { + if(UnityEngine.Random.Range(0,2) == 0) + { + trigger = Faction.FoundationStaff; + } + else + { + trigger = Faction.FoundationEnemy; + } + } + + KELog.Debug(trigger); + + if (trigger == Faction.Unclassified + || trigger == Faction.FoundationStaff + || trigger == Faction.Flamingos) return; + + + Timing.CallDelayed(1, () => + { + Respawn.GrantTokens(trigger, 1); + Respawn.AdvanceTimer(trigger, 150); + }); + + + } + + + + + + } +} diff --git a/KruacentExiled/KE.Misc/Features/SCPBuff.cs b/KruacentExiled/KE.Misc/Features/SCPBuff.cs new file mode 100644 index 00000000..f937118c --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/SCPBuff.cs @@ -0,0 +1,160 @@ +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.Events; +using Exiled.Events.EventArgs.Player; +using Exiled.Events.EventArgs.Scp106; +using Exiled.Permissions.Commands.Permissions; +using KE.Misc.Events.EventsArgs; +using KE.Utils.API.Interfaces; +using KE.Utils.Extensions; +using MEC; +using PlayerRoles; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace KE.Misc.Features +{ + public class SCPBuff : IUsingEvents + { + public const float RefreshRate = 1f; + public float IncreaseSCPHealth { get; } = 1.25f; + private static Config Config => MainPlugin.Configs; + + public Dictionary RoleBuff = new Dictionary() + { + {RoleTypeId.Scp049, Config.MultSCP049 }, + {RoleTypeId.Scp939, Config.MultSCP939 }, + {RoleTypeId.Scp106, Config.MultSCP106 }, + + }; + + + internal SCPBuff() { } + + public static event Action OnBuffingSCP = delegate { }; + public static event Action OnBuffedSCP = delegate { }; + + public void SubscribeEvents() + { + Exiled.Events.Handlers.Player.ChangingRole += BecomingSCP; + } + + public void UnsubscribeEvents() + { + Exiled.Events.Handlers.Player.ChangingRole -= BecomingSCP; + + } + + + + + internal void StartBuff() + { + Timing.RunCoroutine(PeanutShield()); + + } + + + private void BecomingSCP(ChangingRoleEventArgs ev) + { + Player player = ev.Player; + if (!ev.NewRole.IsScp() || ev.NewRole == RoleTypeId.Scp0492) return; + if(player.Role == RoleTypeId.None) return; + float healthincrease = IncreaseSCPHealth; + if(RoleBuff.TryGetValue(ev.NewRole,out float val)) + { + healthincrease *= val; + } + + BuffingSCPEventArgs ev1 = new BuffingSCPEventArgs(player, true, healthincrease); + + OnBuffingSCP?.Invoke(ev1); + if (ev1.IsAllowed) + { + Timing.CallDelayed(2, () => + { + player.MaxHealth *= healthincrease; + player.Health = player.MaxHealth; + }); + BuffedSCPEventArgs ev2 = new BuffedSCPEventArgs(player, healthincrease); + OnBuffedSCP?.Invoke(ev2); + } + + + } + + + private IEnumerator PeanutShield() + { + while (Round.InProgress) + { + List peanuts = Player.List.Where(p => p.Role == RoleTypeId.Scp173).ToList(); + peanuts.ForEach(p => + { + AddHumeShield(p, CheckPlayerAround(p, 6)); + }); + yield return Timing.WaitForSeconds(RefreshRate); + } + } + + + /// + /// Count the number of player around + /// + /// The Player (the peanut) to check around them + /// + /// + /// + private int CheckPlayerAround(Player p, float radius, bool countFriendly = false) + { + int result = 0; + foreach (Player player in Player.List) + { + if (player == p) continue; + if (player.Role.Side == p.Role.Side && !countFriendly) continue; + if (IsPlayerInZone(player, p.Position, radius, radius)) + result += 5; + } + return result; + } + + + private bool IsPlayerInZone(Player player, Vector3 zonePosition, float radius, float height) + { + + float horizontalDistance = Vector3.Distance( + new Vector3(player.Position.x, 0, player.Position.z), + new Vector3(zonePosition.x, 0, zonePosition.z) + ); + + // Calculate the vertical difference (y) + float verticalDifference = Mathf.Abs(player.Position.y - zonePosition.y); + + // Check if the player is in the 3d zone. + return horizontalDistance <= radius / 2 && verticalDifference <= height / 2; + } + + + /// + /// Add HumeShield to a player + /// + /// + /// + /// true if it's not over the max HumeShield of the player; false otherwise + private bool AddHumeShield(Player p, float hum) + { + float max = p.MaxHumeShield; + if (max < hum + p.HumeShield) + { + p.HumeShield = max; + return false; + } + p.HumeShield += hum; + return true; + } + + + } +} diff --git a/KruacentExiled/KE.Misc/Features/Spawn/Spawn.cs b/KruacentExiled/KE.Misc/Features/Spawn/Spawn.cs new file mode 100644 index 00000000..f24152a1 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/Spawn/Spawn.cs @@ -0,0 +1,181 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.Events.EventArgs.Server; +using KE.CustomRoles.API.Features; +using KE.Utils.API.Features; +using MEC; +using PlayerRoles; +using System; +using System.CodeDom; +using System.Collections.Generic; +using System.Linq; + +namespace KE.Misc.Features.Spawn +{ + public class Spawn : MiscFeature + { + + private Dictionary baseRole = new Dictionary() + { + { "173", RoleTypeId.Scp173 }, + { "106", RoleTypeId.Scp106 }, + { "049", RoleTypeId.Scp049 }, + { "939", RoleTypeId.Scp939 }, + }; + + + public const int baseValue = 1; + + private Dictionary SelectableCustomSCPs => CustomSCP.All.ToDictionary(cs =>cs.Name, cs => cs); + + + public static event Action OnAssigned = delegate { }; + + private SpawnedEventArgs eventarg; + + private void OnRoundStarted() + { + if (!MainPlugin.Configs.ScpPreferences) return; + + eventarg = new SpawnedEventArgs(); + + foreach (Player player in Player.List.Where(p => p.IsScp && !p.IsNPC)) + { + SetScpPreferences(player); + } + + OnAssigned?.Invoke(eventarg); + } + + private bool SetScpPreferences(Player player) + { + Config config = MainPlugin.Configs; + if (config == null) + { + Log.Warn("no config, no custom preferences this round"); + return false; + } + Dictionary chancescp = GetPreferences(player); + + if(chancescp == null) + { + Log.Error("no setting found"); + return false; + } + + + string roleScp = ChooseRandomRole(chancescp); + KELog.Debug($"Scp ({player.Nickname}) is {roleScp} previous : {player.Role.Type}"); + SetRoleWithId(player, roleScp); + return true; + + } + + + private Dictionary GetPreferences(Player player) + { + if (player.ScpPreferences.Preferences == null) return null; + Dictionary idChance = new Dictionary(); + + + foreach(var kvp in baseRole) + { + idChance.Add(kvp.Key, player.ScpPreferences.Preferences[kvp.Value] + 5 + baseValue); + } + + foreach (CustomSCP customSCP in SelectableCustomSCPs.Values) + { + if(customSCP.SpawnChance > 0) + { + idChance.Add(customSCP.Name, customSCP.GetPreferences(player) + 5 + baseValue); + } + } + + int nbNotZero = 0; + string scpNotZero = string.Empty; + + foreach(var kvp in idChance) + { + if(kvp.Value != 0 + baseValue) + { + nbNotZero++; + scpNotZero = kvp.Key; + } + } + + if(nbNotZero == 1) + { + idChance[scpNotZero] = 0; + } + + + return idChance; + } + + + + + private string ChooseRandomRole(IDictionary chancescp) + { + if (chancescp == null) throw new ArgumentException("Dictionary null"); + List weightedPool = new List(); + foreach (string ge in chancescp.Keys) + { + for (int i = 0; i < chancescp[ge]; i++) + { + KELog.Debug(ge); + weightedPool.Add(ge); + } + } + + int randomIndex = UnityEngine.Random.Range(0, weightedPool.Count); + + return weightedPool[randomIndex]; + } + + private void SetRoleWithId(Player player, string name) + { + + if (baseRole.ContainsKey(name)) + { + RoleTypeId chosenRole = baseRole[name]; + if (player.Role.Type != chosenRole) + { + player.Role.Set(baseRole[name]); + } + eventarg.VanillaRoles.Add(player); + + Log.Info("vanilla scp"); + return; + } + + if (SelectableCustomSCPs.ContainsKey(name)) + { + SelectableCustomSCPs[name].AddRole(player); + eventarg.CustomRoles.Add(player); + Log.Info("custom scp"); + return; + } + + throw new Exception($"SCP ({name}) not found"); + + + + } + + + + public override void SubscribeEvents() + { + Exiled.Events.Handlers.Server.AllPlayersSpawned += OnRoundStarted; + } + + public override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Server.AllPlayersSpawned -= OnRoundStarted; + } + } + + + +} diff --git a/KruacentExiled/KE.Misc/Features/Spawn/SpawnedEventArgs.cs b/KruacentExiled/KE.Misc/Features/Spawn/SpawnedEventArgs.cs new file mode 100644 index 00000000..24aeeace --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/Spawn/SpawnedEventArgs.cs @@ -0,0 +1,32 @@ +using Exiled.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using YamlDotNet.Serialization.ObjectGraphVisitors; + +namespace KE.Misc.Features.Spawn +{ + public class SpawnedEventArgs + { + + + public List VanillaRoles { get; } + public List CustomRoles { get; } + + + + public SpawnedEventArgs(List vanilla,List custom) + { + VanillaRoles = vanilla; + CustomRoles = custom; + } + + public SpawnedEventArgs() + { + VanillaRoles = new List(); + CustomRoles = new List(); + } + } +} diff --git a/KruacentExiled/KE.Misc/Features/SpawnLcz.cs b/KruacentExiled/KE.Misc/Features/SpawnLcz.cs new file mode 100644 index 00000000..d879ed85 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/SpawnLcz.cs @@ -0,0 +1,129 @@ +using Exiled.API.Enums; +using Exiled.API.Extensions; +using Exiled.API.Features; +using Exiled.API.Features.Doors; +using Exiled.API.Features.Items; +using LabDoor = LabApi.Features.Wrappers.Door; +using MEC; +using PlayerRoles; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using System.Security; + +namespace KE.Misc.Features +{ + internal class SpawnLcz : MiscFeature + { + public float Time { get; } = 198; + + public static readonly Dictionary RoomTypes = new Dictionary() + { + { RoleTypeId.Scp939,RoomType.Lcz330 }, + { RoleTypeId.Scp096,RoomType.LczGlassBox }, + { RoleTypeId.Scp173,RoomType.LczGlassBox }, + }; + private float chance = .33f; + public float Chance + { + get + { + return chance; + } + set + { + chance = Mathf.Clamp01(value); + } + } + + private HashSet serials = new HashSet(); + private HashSet locked = new HashSet(); + + public override void SubscribeEvents() + { + //Chance = 1; + LabApi.Events.Handlers.ServerEvents.RoundStarted += OnRoundStarted; + LabApi.Events.Handlers.PlayerEvents.DroppingItem += OnDroppingItem; + base.SubscribeEvents(); + } + + + + public override void UnsubscribeEvents() + { + LabApi.Events.Handlers.ServerEvents.RoundStarted -= OnRoundStarted; + LabApi.Events.Handlers.PlayerEvents.DroppingItem -= OnDroppingItem; + base.UnsubscribeEvents(); + } + private void OnRoundStarted() + { + LockDoors(); + Timing.CallDelayed(.5f, delegate + { + foreach (Player player in Player.List.Where(p => RoomTypes.ContainsKey(p.Role))) + { + if (UnityEngine.Random.value < Chance) + { + player.Teleport(Room.Get(RoomTypes[player.Role])); + GiveEntertainment(player); + } + } + }); + + + + Timing.CallDelayed(Time, delegate + { + foreach(Door door in locked) + { + LabDoor d = LabDoor.Get(door.Base); + d.Lock(Interactables.Interobjects.DoorUtils.DoorLockReason.SpecialDoorFeature, false); + } + foreach(ushort serial in serials) + { + Item item =Item.Get(serial); + item?.Destroy(); + } + }); + } + private void OnDroppingItem(LabApi.Events.Arguments.PlayerEvents.PlayerDroppingItemEventArgs ev) + { + if (!serials.Contains(ev.Item.Serial)) return; + ev.IsAllowed = false; + } + + public void LockDoors() + { + foreach (RoomType room in RoomTypes.Values) + { + Room r = Room.Get(room); + foreach (Door door in r.Doors) + { + LockDoor(door); + locked.Add(door); + } + } + } + + + + public void GiveEntertainment(Player player) + { + Keycard key = Item.Create(ItemType.KeycardChaosInsurgency); + key.Give(player); + serials.Add(key.Serial); + player.CurrentItem = key; + } + + private void LockDoor(Door door) + { + LabDoor d = LabDoor.Get(door.Base); + d.Lock(Interactables.Interobjects.DoorUtils.DoorLockReason.SpecialDoorFeature, true); + } + + + + + + } +} diff --git a/KruacentExiled/KE.Misc/Features/SurfaceLight.cs b/KruacentExiled/KE.Misc/Features/SurfaceLight.cs new file mode 100644 index 00000000..0a52efaa --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/SurfaceLight.cs @@ -0,0 +1,70 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using UnityEngine; +using System.Collections.Generic; +using System.Linq; +using Exiled.API.Extensions; + +namespace KE.Misc.Features +{ + /// + /// Everything about Surface Light + /// + internal class SurfaceLight : MiscFeature + { + + public static readonly HashSet _colors = new HashSet() + { + Color.cyan, + Color.red, + Color.green, + Color.blue + }; + + private float _chance = 5; + + public float Chance + { + get { return _chance; } + set + { + _chance = Mathf.Clamp(value, 0f, 100f); + } + } + + + public override void SubscribeEvents() + { + Exiled.Events.Handlers.Server.RoundStarted += OnRoundStarted; + } + + public override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Server.RoundStarted -= OnRoundStarted; + + } + + + private void OnRoundStarted() + { + if (!MainPlugin.Configs.SurfaceLight) + { + return; + } + + if(Random.Range(0f,100f) < Chance) + ChangeSurfaceLight(); + } + + private void ChangeSurfaceLight() + { + + // Select a random color + Color randomColor = _colors.GetRandomValue(); + foreach (var room in Room.List.Where(r => r.Type == RoomType.Surface)) + { + room.Color = randomColor; + } + } + } +} diff --git a/KruacentExiled/KE.Misc/Features/VoteStart/ForceVoteNumber.cs b/KruacentExiled/KE.Misc/Features/VoteStart/ForceVoteNumber.cs new file mode 100644 index 00000000..0ea4d76c --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/VoteStart/ForceVoteNumber.cs @@ -0,0 +1,51 @@ +using CommandSystem; +using Exiled.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features.VoteStart +{ + [CommandHandler(typeof(RemoteAdminCommandHandler))] + public class ForceVoteNumber : ICommand + { + public string Command => "nbvote"; + + public string[] Aliases => new string[] { "nbv" }; + + public string Description => "force a number of people needed to vote for this round only"; + + public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) + { + if(!Round.IsLobby) + { + response = "works only in lobby"; + return false; + } + + if(arguments.Count < 1) + { + response = "current min number : "+ MainPlugin.Configs.MinPlayerVote; + return true; + } + + if(!int.TryParse(arguments.At(0),out int result)) + { + response = "couldn't parse the number"; + return false; + } + + + + + + MainPlugin.Configs.MinPlayerVote = result; + + + response = "vote set at " + MainPlugin.Configs.MinPlayerVote + " players"; + return true; + } + } +} diff --git a/KruacentExiled/KE.Misc/Features/VoteStart/RetractVote.cs b/KruacentExiled/KE.Misc/Features/VoteStart/RetractVote.cs new file mode 100644 index 00000000..cd3dad28 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/VoteStart/RetractVote.cs @@ -0,0 +1,49 @@ +using CommandSystem; +using Exiled.API.Features; +using KE.Utils.API.Commands; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features.VoteStart +{ + [CommandHandler(typeof(ClientCommandHandler))] + public class RestractVote : KECommand + { + public override string Command => "removevote"; + + public override string[] Aliases => new string[] { "rv", "retractv", "removev" }; + + public override string Description => "remove the set vote"; + + public override string[] Usage => new string[] { "" }; + public override bool ExecuteCommand(ArraySegment arguments, ICommandSender sender, out string response) + { + if(!Round.IsLobby) + { + response = "works only in lobby"; + return false; + } + + + Player player = Player.Get(sender); + + VoteStart vote = MainPlugin.Instance.vote; + + if (!vote.DidVote(player)) + { + response = "you didn't vote"; + return false; + } + + + vote.CancelVote(player); + + + response = "vote set at " + MainPlugin.Configs.MinPlayerVote + " players"; + return true; + } + } +} diff --git a/KruacentExiled/KE.Misc/Features/VoteStart/VotePosition.cs b/KruacentExiled/KE.Misc/Features/VoteStart/VotePosition.cs new file mode 100644 index 00000000..c606cbc3 --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/VoteStart/VotePosition.cs @@ -0,0 +1,20 @@ +using HintServiceMeow.Core.Enum; +using KE.Utils.API.Displays.DisplayMeow.Placements; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Features.VoteStart +{ + public class VotePosition : HintPosition + { + public override float Xposition => 0; + + public override float Yposition => 860; + public override string Name => "VoteStart"; + + public override HintAlignment HintAlignment => HintAlignment.Center; + } +} diff --git a/KruacentExiled/KE.Misc/Features/VoteStart/VoteStart.cs b/KruacentExiled/KE.Misc/Features/VoteStart/VoteStart.cs new file mode 100644 index 00000000..db728e9e --- /dev/null +++ b/KruacentExiled/KE.Misc/Features/VoteStart/VoteStart.cs @@ -0,0 +1,200 @@ +using Exiled.API.Features; +using Exiled.API.Features.Pools; +using Exiled.Events.EventArgs.Player; +using HintServiceMeow.Core.Models.Hints; +using HintServiceMeow.Core.Utilities; +using KE.Utils.API.Displays.DisplayMeow; +using KE.Utils.API.Displays.DisplayMeow.Placements; +using MEC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace KE.Misc.Features.VoteStart +{ + internal class VoteStart : MiscFeature + { + public const string CancelVoteTranslation = "CancelVote"; + //public static Dictionary> LangToKeyToTranslation { get; } = new() + //{ + // ["en"] = + // { + // [CancelVoteTranslation] = ".rv in the console to cancel your vote." + // }, + // ["fr"] = + // { + // [CancelVoteTranslation] = ".rv dans la console client pour annuler le vote." + // } + //}; + + public static HintPosition HintPosition = new VotePosition(); + + private List Voted = new List(); + private bool voteCasted = false; + public override void SubscribeEvents() + { + Exiled.Events.Handlers.Server.WaitingForPlayers += OnWaitingForPlayers; + Exiled.Events.Handlers.Player.VoiceChatting += OnVoiceChatting; + Exiled.Events.Handlers.Server.RoundStarted += OnRoundStarted; + Exiled.Events.Handlers.Player.Left += OnLeft; + Exiled.Events.Handlers.Player.Verified += OnVerified; + base.SubscribeEvents(); + } + + + public override void UnsubscribeEvents() + { + Exiled.Events.Handlers.Server.WaitingForPlayers -= OnWaitingForPlayers; + Exiled.Events.Handlers.Player.VoiceChatting -= OnVoiceChatting; + Exiled.Events.Handlers.Player.Verified -= OnVerified; + Exiled.Events.Handlers.Server.RoundStarted -= OnRoundStarted; + Exiled.Events.Handlers.Player.Left -= OnLeft; + + base.UnsubscribeEvents(); + } + + + private void OnLeft(LeftEventArgs ev) + { + Player player = ev.Player; + if (DidVote(player)) + { + CancelVote(player); + } + } + + + public bool DidVote(Player player) + { + return Voted.Contains(player); + } + + public void CancelVote(Player player) + { + if (!Voted.Contains(player)) + { + throw new ArgumentOutOfRangeException($"Player ({player}) didn't vote"); + } + + + Voted.Remove(player); + Round.IsLobbyLocked = true; + voteCasted = false; + } + + + + private void OnVoiceChatting(VoiceChattingEventArgs ev) + { + if (voteCasted) return; + if (!Round.IsLobby) return; + if (ev.Player is null) + { + return; + } + + if (string.IsNullOrEmpty(ev.Player.AuthenticationToken)) + { + return; + } + + if (DidVote(ev.Player)) + { + return; + } + + Voted.Add(ev.Player); + if (Voted.Count >= MainPlugin.Configs.MinPlayerVote) + { + Log.Info("starting the round"); + Round.IsLobbyLocked = false; + voteCasted = true; + } + } + private void OnWaitingForPlayers() + { + Init(); + Round.IsLobbyLocked = true; + } + + private void Init() + { + Voted.Clear(); + voteCasted = false; + } + + private void OnVerified(VerifiedEventArgs ev) + { + Player player = ev.Player; + + if(ev.Player == null) + { + return; + } + + Timing.CallDelayed(1f, () => + { + if (Round.IsLobby) + { + DisplayHandler.Instance.CreateAuto(player, (args) => GetPlayers(player), HintPosition.HintPlacement); + } + }); + + } + + private string GetPlayers(Player player) + { + if (!Round.IsLobby) return string.Empty; + + StringBuilder sb = StringBuilderPool.Pool.Get(); + + sb.Append("Votes ("); + + sb.Append(Voted.Count); + sb.Append("/"); + sb.Append(MainPlugin.Configs.MinPlayerVote); + + sb.AppendLine(") : "); + foreach (Player other in Voted) + { + bool flag1 = other == player; + + + if (flag1) + { + sb.Append(""); + } + sb.Append(other.Nickname); + if (flag1) + { + sb.Append(""); + } + sb.Append(" "); + } + if (Voted.Contains(player)) + { + sb.AppendLine(); + + sb.Append(".rv dans la console client pour annuler le vote."); + //sb.Append(MainPlugin.GetTranslation(player,CancelVoteTranslation)); + } + + + return StringBuilderPool.Pool.ToStringReturn(sb); + + } + private void OnRoundStarted() + { + foreach(Player player in Player.List) + { + DisplayHandler.Instance.RemoveHint(player, HintPosition.HintPlacement); + } + + } + + + } +} diff --git a/KruacentExiled/KE.Misc/Handlers/ServerHandler.cs b/KruacentExiled/KE.Misc/Handlers/ServerHandler.cs new file mode 100644 index 00000000..de94f8bd --- /dev/null +++ b/KruacentExiled/KE.Misc/Handlers/ServerHandler.cs @@ -0,0 +1,55 @@ +using Exiled.API.Enums; +using Exiled.API.Features; + +namespace KE.Misc.Handlers +{ + internal class ServerHandler + { + public void OnRoundStarted() + { + if (MainPlugin.Configs.AutoElevator) + MainPlugin.Instance.AutoElevator.StartLoop(); + + MainPlugin.Instance.AutoTesla.StartLoop(); + MainPlugin.Instance.SCPBuff.StartBuff(); + Respawn.SetTokens(SpawnableFaction.NtfWave, 1); + Respawn.SetTokens(SpawnableFaction.ChaosWave, 1); + + /* + string test = "test.png"; + string kel = "nokel.png"; + string tenna = "tennasmooving.gif"; + string smol = "smol.gif"; + + Image img = Image.FromFile(Paths.Configs + $"/{kel}"); + Image gif = Image.FromFile(Paths.Configs + $"/{tenna}"); + + + TextImage text = new TextImage(img); + AnimatedTextImage giftext = new(gif); + Vector3 basePos = RoleTypeId.Scp049.GetRandomSpawnLocation().Position; + + Round.IsLocked = true; + var pl = Player.List.FirstOrDefault(); + Timing.CallDelayed(2f, delegate + { + pl.Role.Set(RoleTypeId.NtfSpecialist); + }); + + + Timing.CallDelayed(5f, delegate + { + giftext.Spawn(pl?.Position + Vector3.up ?? basePos, Quaternion.Euler(Vector3.zero)); + //text.Spawn(pl?.Position+Vector3.up ?? basePos, .05f); + }); + + + text.Spawn(basePos + Vector3.back); + foreach(Player p in Player.List.Where(p => !p.IsNPC)) + { + p.Teleport(RoomType.Hcz939); + } + */ + } + } +} diff --git a/KruacentExiled/KE.Misc/MainPlugin.cs b/KruacentExiled/KE.Misc/MainPlugin.cs new file mode 100644 index 00000000..b54cacc3 --- /dev/null +++ b/KruacentExiled/KE.Misc/MainPlugin.cs @@ -0,0 +1,189 @@ +using Exiled.API.Enums; +using Exiled.API.Features; +using ServerHandle = Exiled.Events.Handlers.Server; +using PlayerRoles; +using System; +using KE.Misc.Features; +using KE.Misc.Handlers; +using KE.Misc.Features.GamblingCoin; +using HarmonyLib; +using LabApi.Events.Arguments.ServerEvents; +using KE.Misc.Features.VoteStart; +using KE.Misc.Features.Spawn; +using KE.Misc.Features.LastHuman; +using KE.Utils.API.Settings.GlobalSettings; +using KE.Utils.API.Translations; +using KE.Misc.Features.PostNuke; +using Exiled.Events.EventArgs.Server; +using KE.Misc.Features.LobbyHints; +using KruacentExiled; +using Exiled.API.Interfaces; + +namespace KE.Misc +{ + + public class MainPlugin : KEPlugin, ILocalizable + { + public override string Name => "KE.Misc"; + public override string Prefix => "KE.Misc"; + + + internal static MainPlugin Instance { get; private set; } + private ServerHandler ServerHandler; + internal _914 _914 { get; private set; } + internal AutoElevator AutoElevator { get; private set; } + internal ClassDDoor ClassDDoor { get; private set; } + internal Candy Candy { get; private set; } + internal SurfaceLight SurfaceLight { get; private set; } + internal SCPBuff SCPBuff { get; private set; } + internal Spawn Spawn { get; private set; } + internal FriendlyFire FriendlyFire { get; private set; } + internal NukeKill AutoNukeAnnoucement { get; private set; } + internal AutoTesla AutoTesla { get; private set; } + internal EventHandlers _gamblingCoinHandler { get; private set; } + internal SpawnLcz SpawnLcz { get; private set; } + internal LastHumanHandler LastHuman { get; private set; } + private Harmony harmony; + + internal VoteStart vote { get; private set; } + internal PostNukeHandler postnuke { get; private set; } + internal LobbyHint LobbyHint { get; private set; } + + public string LocalizationId => Prefix; + + public override IConfig Config => config; + public static Config Configs => Instance?.config; + private Config config; + + public override void OnEnabled() + { + Instance = this; + harmony = new Harmony(Prefix); + + config = KruacentExiled.MainPlugin.Instance.Config.MiscConfig; + + _914 = new _914(); + AutoElevator = new AutoElevator(); + ClassDDoor = new ClassDDoor(); + //SurfaceLight = new SurfaceLight(); messes with the nuke light + ServerHandler = new ServerHandler(); + Spawn = new Spawn(); + SCPBuff = new SCPBuff(); + FriendlyFire = new FriendlyFire(); + AutoNukeAnnoucement = new NukeKill(); + AutoTesla = new AutoTesla(); + LastHuman = new LastHumanHandler(); + Candy = new Candy(); + vote = new VoteStart(); + postnuke = new PostNukeHandler(); + LobbyHint = new LobbyHint(); + + + //SpawnLcz = new(); + + GlobalSettingsHandler.Instance.TryLoad(); + GlobalSettingsHandler.Instance.SubscribeEvents(); + RegisterTranslations(); + + harmony.PatchAll(KruacentExiled.MainPlugin.Instance.Assembly); + ClassDDoor.SubscribeEvents(); + ServerHandle.RoundEnded += OnRoundEnded; + MiscFeature.SubscribeAllEvents(); + AutoNukeAnnoucement.SubscribeEvents(); + if (Configs.GamblingCoin) + { + GamblingCoinManager.RegisterAll(); + _gamblingCoinHandler = new EventHandlers(); + Exiled.Events.Handlers.Player.FlippingCoin += _gamblingCoinHandler.OnCoinFlip; + } + LastHuman.SubscribeEvents(); + SCPBuff.SubscribeEvents(); + + ServerHandle.RoundStarted += ServerHandler.OnRoundStarted; + LabApi.Events.Handlers.ServerEvents.CassieQueuingScpTermination += NoeDeath; + + } + + + public override void OnDisabled() + { + ServerHandle.RoundStarted -= ServerHandler.OnRoundStarted; + ServerHandle.RoundEnded -= OnRoundEnded; + LabApi.Events.Handlers.ServerEvents.CassieQueuingScpTermination -= NoeDeath; + AutoNukeAnnoucement.UnsubscribeEvents(); + LastHuman.UnsubscribeEvents(); + SCPBuff.UnsubscribeEvents(); + if (Configs.GamblingCoin) + { + Exiled.Events.Handlers.Player.FlippingCoin -= _gamblingCoinHandler.OnCoinFlip; + } + AutoTesla.StopLoop(); + MiscFeature.UnsubscribeAllEvents(); + ClassDDoor.UnsubscribeEvents(); + harmony.UnpatchAll(harmony.Id); + GlobalSettingsHandler.Instance.UnsubscribeEvents(); + + _914 = null; + Candy = null; + //SpawnLcz = null; + ClassDDoor = null; + ServerHandler = null; + SCPBuff = null; + AutoTesla = null; + Spawn = null; + AutoElevator = null; + vote = null; + AutoNukeAnnoucement = null; + FriendlyFire = null; + //SurfaceLight = null; + GamblingCoinManager.DestroyAll(); + _gamblingCoinHandler = null; + postnuke = null; + LobbyHint = null; + LastHuman = null; + harmony = null; + Instance = null; + } + + private void OnRoundEnded(Exiled.Events.EventArgs.Server.RoundEndedEventArgs ev) + { + Server.FriendlyFire = true; + } + + private void NoeDeath(CassieQueuingScpTerminationEventArgs ev) + { + Player player = Player.Get(ev.Player); + if (!player.UserId.Equals("76561199066936074@steam")) + { + return; + } + + + if (!player.IsScp) + { + return; + } + + if(player.Role == RoleTypeId.Scp0492) + { + return; + } + + ev.IsAllowed = false; + Exiled.API.Features.Cassie.MessageTranslated("SCP 69 420 has been contained successfully", "SCP-69420-NOE has been contained suscessfully"); + } + + public void RegisterTranslations() + { + TranslationHub.Add(LocalizationId, LastHumanTranslations.LangToKeyToTranslation); + TranslationHub.Add(LocalizationId, WrongAspectRatioWarningLobbyHint.LangToKeyToTranslation); + //TranslationHub.Add(LocalizationId, VoteStart.LangToKeyToTranslation); + } + + + public static string GetTranslation(Player player,string key) + { + return TranslationHub.Get(player, Instance.LocalizationId, key); + } + } +} diff --git a/KruacentExiled/KE.Misc/Patches/LastHumanTrackerPatches.cs b/KruacentExiled/KE.Misc/Patches/LastHumanTrackerPatches.cs new file mode 100644 index 00000000..fdd1a163 --- /dev/null +++ b/KruacentExiled/KE.Misc/Patches/LastHumanTrackerPatches.cs @@ -0,0 +1,24 @@ +using HarmonyLib; +using PlayerRoles.PlayableScps.HumanTracker; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Patches +{ + public static class LastHumanTrackerPatches + { + [HarmonyPatch(typeof(LastHumanTracker), nameof(LastHumanTracker.LateUpdate))] + public static class LateUpdatePatch + { + + public static bool Prefix() + { + return false; + } + + } + } +} diff --git a/KruacentExiled/KE.Misc/Patches/ServerPatches.cs b/KruacentExiled/KE.Misc/Patches/ServerPatches.cs new file mode 100644 index 00000000..66974687 --- /dev/null +++ b/KruacentExiled/KE.Misc/Patches/ServerPatches.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Patches +{ + public static class ServerPatches + { + + public static class FriendlyFirePatch + { + //todo patch to check when the friendlyfire changes (for scp hud) + } + } +} diff --git a/KruacentExiled/KE.Misc/Utils/AnimatedTextImage.cs b/KruacentExiled/KE.Misc/Utils/AnimatedTextImage.cs new file mode 100644 index 00000000..4e5adcdb --- /dev/null +++ b/KruacentExiled/KE.Misc/Utils/AnimatedTextImage.cs @@ -0,0 +1,126 @@ +using Exiled.API.Features; +using Exiled.API.Interfaces; +using MEC; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using UnityEngine; + +namespace KE.Misc.Utils +{ + public class AnimatedTextImage : IEquatable, IWorldSpace + { + + private TextImage[] spawnedImage; + + //delay in ms + private int[] delay; + + private bool looping = true; + private TextImage currentImage = null; + + public Vector3 Position { get; set; } = Vector3.zero; + + public Quaternion Rotation { get; set; } = Quaternion.Euler(Vector3.one); + + public float PixelSize { get; set; } = .05f; + private CoroutineHandle handle; + + + public AnimatedTextImage(Image img) + { + CreateImages(img); + } + + private void CreateImages(Image img) + { + FrameDimension dimension = new FrameDimension(img.FrameDimensionsList[0]); + int frameCount = img.GetFrameCount(dimension); + + + PropertyItem delayProperty = img.GetPropertyItem(0x5100); + byte[] delayBytes = delayProperty.Value; + delay = new int[frameCount]; + spawnedImage = new TextImage[frameCount]; + + for (int frame = 0; frame < frameCount; frame++) + { + + delay[frame] = BitConverter.ToInt32(delayBytes, frame * 4) * 10; + + img.SelectActiveFrame(dimension, frame); + spawnedImage[frame] = new TextImage(img); + } + } + + + + public void Spawn(Vector3 position, Quaternion rotation) + { + Position = position; + Rotation = rotation; + handle = Timing.RunCoroutine(Loop()); + } + + public void Destroy() + { + Timing.KillCoroutines(handle); + } + + public void Stop() + { + looping = false; + } + + public void Resume() + { + looping = true; + } + + + private IEnumerator Loop() + { + if(spawnedImage.Length != delay.Length) + { + throw new Exception("not same lenght"); + } + + int i = 0; + while (looping) + { + int index = i % spawnedImage.Length; + float waitTime = 1; + waitTime = delay[index] / 1000f; + + + ShowImage(index); + + yield return Timing.WaitForSeconds(waitTime); + i = (i+1)% spawnedImage.Length; + } + } + + private void ShowImage(int index) + { + try + { + currentImage?.Destroy(); + currentImage = spawnedImage[index]; + currentImage.Spawn(Position, PixelSize, Rotation); + } + catch(Exception e) + { + Log.Error(e); + } + + } + + + + public bool Equals(AnimatedTextImage other) + { + return spawnedImage == other.spawnedImage; + } + } +} diff --git a/KruacentExiled/KE.Misc/Utils/ImageUtils.cs b/KruacentExiled/KE.Misc/Utils/ImageUtils.cs new file mode 100644 index 00000000..318d3b4d --- /dev/null +++ b/KruacentExiled/KE.Misc/Utils/ImageUtils.cs @@ -0,0 +1,29 @@ +using Exiled.API.Features; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KE.Misc.Utils +{ + public static class ImageUtils + { + private static HashSet images = null; + + public static string Path => Paths.Configs + "/"; + + + public static IEnumerable GetAllImages() + { + if(images != null) + { + return images; + } + return null; + + } + + } +} diff --git a/KruacentExiled/KE.Misc/Utils/TextImage.cs b/KruacentExiled/KE.Misc/Utils/TextImage.cs new file mode 100644 index 00000000..0948eb4a --- /dev/null +++ b/KruacentExiled/KE.Misc/Utils/TextImage.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections.Generic; +using System.Drawing.Imaging; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Exiled.API.Features.Pools; +using LabApi.Features.Wrappers; +using UnityEngine; +using Color = System.Drawing.Color; +using Exiled.API.Features.Toys; +using Exiled.API.Features; +using UnityEngine.Rendering; +using Graphics = System.Drawing.Graphics; +using Exiled.API.Interfaces; + +namespace KE.Misc.Utils +{ + public class TextImage : IEquatable + { + private static HashSet list = new HashSet(); + + private string rawString = string.Empty; + private HashSet spawnedTextToys = new HashSet(); + + public static Vector2 DefaultDisplaySize => new Vector2(5000, 5000); + + public static string StartColorTag => " ""; + public static string DefaultCharacter => "█"; + + public TextImage(Image img) + { + + var b = new Bitmap(img,img.Width, img.Height); + StringBuilder sb = StringBuilderPool.Pool.Get(); + //Log.Info($"{b.Height}/{b.Width}"); + + for (int y = 0; y < b.Height; y++) + { + Color? oldColor = null; + int nb = 0; + + for (int x = 0; x < b.Width; x++) + { + Color pixelColor = b.GetPixel(x, y); + + if (oldColor is null) + { + oldColor = pixelColor; + nb = 1; + continue; + } + + if (ColorEquals(oldColor, pixelColor)) + { + nb++; + } + else + { + sb.Append(StartColorTag); + sb.Append(ToHexValue(oldColor.Value) + ">"); + for (int i = 0; i < nb; i++) + sb.Append(DefaultCharacter); + sb.Append(EndColorTag); + + oldColor = pixelColor; + nb = 1; + } + } + + if (nb > 0 && oldColor != null) + { + sb.Append(StartColorTag); + sb.Append(ToHexValue(oldColor.Value) + ">"); + for (int i = 0; i < nb; i++) + sb.Append(DefaultCharacter); + sb.Append(EndColorTag); + } + + sb.AppendLine(); + } + rawString = sb.ToString(); + + StringBuilderPool.Pool.Return(sb); + list.Add(this); + } + + public static bool ColorEquals(Color? color1,Color? color2) + { + if (color1 is null || color2 is null) return false; + + Color tcolor1 = color1 ?? Color.Black; + Color tcolor2 = color2 ?? Color.Black; + + return tcolor1.R == tcolor2.R && + tcolor1.G == tcolor2.G && + tcolor1.B == tcolor2.B && + tcolor1.A == tcolor2.A; + } + + + //Todo change texttoy + public TextToy Spawn(Vector3 position,float pixelSize = 1f, Quaternion? rotation = null, Transform parent = null) + { + Vector3 scale = new Vector3(1,.5f,1)* pixelSize; + + TextToy textToy = TextToy.Create(position, rotation ?? Quaternion.Euler(Vector3.zero), scale, parent, false); + textToy.DisplaySize = DefaultDisplaySize; + textToy.TextFormat = rawString; + + + textToy.Spawn(); + + spawnedTextToys.Add(textToy); + return textToy; + } + + + public void Destroy(TextToy textToy) + { + if (textToy == null) throw new ArgumentNullException(); + + if (spawnedTextToys.Remove(textToy)) + { + textToy.Destroy(); + } + + + } + + public void Destroy() + { + if (spawnedTextToys is null) return; + + + + foreach(TextToy tt in spawnedTextToys) + { + if (!tt.IsDestroyed) + { + tt?.Destroy(); + } + + } + } + + + + + private static string ToHexValue(Color color) + { + return "#" + color.R.ToString("X2") + + color.G.ToString("X2") + + color.B.ToString("X2") + + color.A.ToString("X2"); + } + + public bool Equals(TextImage other) + { + return spawnedTextToys == other.spawnedTextToys; + } + } +} diff --git a/KruacentExiled/KEPlugin.cs b/KruacentExiled/KEPlugin.cs new file mode 100644 index 00000000..61a7a16f --- /dev/null +++ b/KruacentExiled/KEPlugin.cs @@ -0,0 +1,34 @@ +using Exiled.API.Features; +using Exiled.API.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KruacentExiled +{ + public abstract class KEPlugin + { + public abstract string Name { get; } + public abstract string Prefix { get; } + + + public abstract IConfig Config { get; } + + + public void InternalOnEnabled() + { + if (Config.IsEnabled) + { + OnEnabled(); + + Log.Send(Name + " has been enabled!", Discord.LogLevel.Info, ConsoleColor.DarkYellow); + } + } + public abstract void OnEnabled(); + public abstract void OnDisabled(); + + + } +} diff --git a/KruacentExiled/KruacentExiled.csproj b/KruacentExiled/KruacentExiled.csproj new file mode 100644 index 00000000..50852009 --- /dev/null +++ b/KruacentExiled/KruacentExiled.csproj @@ -0,0 +1,37 @@ + + + + 8.0 + net48 + + + + + + + + + + + + + + + + + + + + + + + + + if not "$(EXILED_DEV_REFERENCES)"=="" copy /y "$(OutputPath)$(AssemblyName).dll" "$(EXILED_DEV_REFERENCES)\Plugins\" + + + if [[ ! -z "$EXILED_DEV_REFERENCES" ]]; then cp "$(OutputPath)$(AssemblyName).dll" "$EXILED_DEV_REFERENCES/Plugins/"; fi + + + + diff --git a/KruacentExiled.sln b/KruacentExiled/KruacentExiled.sln similarity index 55% rename from KruacentExiled.sln rename to KruacentExiled/KruacentExiled.sln index 7456edab..116a4e44 100644 --- a/KruacentExiled.sln +++ b/KruacentExiled/KruacentExiled.sln @@ -1,9 +1,9 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.12.35506.116 d17.12 +VisualStudioVersion = 17.12.35506.116 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KruacentExiled", "KruacentExiled.csproj", "{9F49DAA8-F0F8-47C2-8D20-16E34F09BBCC}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KruacentExiled", "KruacentExiled.csproj", "{AD0EAD9B-B7D2-954E-31B2-E78DC28B2D9C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -11,12 +11,15 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {9F49DAA8-F0F8-47C2-8D20-16E34F09BBCC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9F49DAA8-F0F8-47C2-8D20-16E34F09BBCC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9F49DAA8-F0F8-47C2-8D20-16E34F09BBCC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9F49DAA8-F0F8-47C2-8D20-16E34F09BBCC}.Release|Any CPU.Build.0 = Release|Any CPU + {AD0EAD9B-B7D2-954E-31B2-E78DC28B2D9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AD0EAD9B-B7D2-954E-31B2-E78DC28B2D9C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AD0EAD9B-B7D2-954E-31B2-E78DC28B2D9C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AD0EAD9B-B7D2-954E-31B2-E78DC28B2D9C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BA7136A3-3FCA-43C1-825B-9D126AC1D87E} + EndGlobalSection EndGlobal diff --git a/KruacentExiled/MainPlugin.cs b/KruacentExiled/MainPlugin.cs new file mode 100644 index 00000000..937d5a70 --- /dev/null +++ b/KruacentExiled/MainPlugin.cs @@ -0,0 +1,88 @@ +using Exiled.API.Features; +using Exiled.API.Interfaces; +using KE.CustomRoles.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KruacentExiled +{ + internal class MainPlugin : Plugin + { + + private KEPlugin[] plugins; + public override string Author => "Patrique & OmerGS"; + public override Version Version { get; } = new Version(2, 0, 0); + + public static MainPlugin Instance { get; private set; } + + public override void OnEnabled() + { + Instance = this; + + plugins = new KEPlugin[] + { + new KE.CustomRoles.MainPlugin(), + new KE.GlobalEventFramework.MainPlugin(), + new KE.GlobalEventFramework.Examples.MainPlugin(), + new KE.Items.MainPlugin(), + new KE.Misc.MainPlugin(), + new KE.Map.MainPlugin(), + }; + + for (int i = 0; i < plugins.Length; i++) + { + KEPlugin plugin = plugins[i]; + try + { + plugin.OnEnabled(); + } + catch(Exception e) + { + Log.Error(e); + } + + } + + + base.OnEnabled(); + } + + public override void OnDisabled() + { + for (int i = 0; i < plugins.Length; i++) + { + KEPlugin plugin = plugins[i]; + + plugin.OnDisabled(); + Log.Info(plugin.Name + " has been disabled!"); + + } + + Instance = null; + + base.OnDisabled(); + } + + } + + + + + public class Config : IConfig + { + public bool IsEnabled { get; set; } = true; + public bool Debug { get; set; } = false; + + + public KE.CustomRoles.Config CustomRoleConfig { get; set; } = new KE.CustomRoles.Config(); + public KE.Items.Config CustomItemConfig { get; set; } = new KE.Items.Config(); + public KE.Map.Config MapConfig { get; set; } = new KE.Map.Config(); + public KE.Misc.Config MiscConfig { get; set; } = new KE.Misc.Config(); + public KE.GlobalEventFramework.Config GEFConfig { get; set; } = new KE.GlobalEventFramework.Config(); + public KE.GlobalEventFramework.Examples.Config GEFEConfig { get; set; } = new KE.GlobalEventFramework.Examples.Config(); + } + +} diff --git a/KruacentExiled/contribute.md b/KruacentExiled/contribute.md new file mode 100644 index 00000000..ad76e490 --- /dev/null +++ b/KruacentExiled/contribute.md @@ -0,0 +1,3 @@ +# How to contribute + +[Like EXILED](https://github.com/ExMod-Team/EXILED/blob/15ffcc69ced413b72c1f702944fee6f524ad6de2/EXILED/docs/articles/contributing/index.md) \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..7e5ccfac --- /dev/null +++ b/LICENSE @@ -0,0 +1,878 @@ +<<<<<<< HEAD + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +======= + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. +>>>>>>> c57cbf22d00b8f5874995514b0ca12c8d5b83c87 diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index 261eeb9e..00000000 --- a/LICENSE.txt +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/README.md b/README.md index 8b8a26f5..05097e95 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,71 @@ -# KruacentExiled +
+

Kruaçent-Exilede

+

Gameplay overhaul plugins for SCP: Secret Laboratory.

+ Release + Contributors +
+ --- -This repo was made for our small (6 players) private SCP:SL server using the Exiled Framework. +## What is Kruaçent-Exiled? + +**Kruaçent-Exiled** is a plugin built on the [EXILED](https://github.com/ExMod-Team/EXILED) framework for SCP: Secret Laboratory game. + +Created to enhance our private servers, it has evolved into a large plugin. Its purpose is not just to add some item or role, but to **overhaul the gameplay**. + +--- + +## Features + +The framework is divided into five main parts to reshape the Site-19 : +### 1. Global Event Framework +Every round is unique. This sub-plugin randomly selects and triggers a "Global Event" at the start of each round. +* **How it works :** An event can alter the rules of the current game (e.g. dropping bomb everywhere every 5 minutes, giving everyone speed boost, or creating facility malfunction). +* **Usable by developer :** Designed to be extensible, allowing developers to add their own custom global events. -## Global Event Framework +### 2. Custom Items +Introduces new gears into the game. +* Features modified grenades with variety of effect. +* Includes healing items and tactical gears. -the Global Event Framework (GEF) is made to add Global Events which are tweaks in the gameplay which is revealed at the start of each round. +### 3. Custom Roles +Introduces new roles into the game. +* Features custom roles with variety of effect for humans and SCPs. +* Example : + * **Asthmatic** : Human with less stamina. + * **Pacifist** : Human who can't hurt other being. + * **Enderman** : Human who can set a point and teleport to it. + * **SCP-035** -## Blackout 'N Door -This plugin was made to avoid camping on surface and avoid the sole SCP being stuck. +### 4. Miscellaneous +A collection of modifications to the base mechanics of SCP:SL to create new experiences. +* **SCP-914 :** New output for SCP-914 +* **Elevators :** Autonomous elevator calls and modified behaviors. +* **Gambling coin :** Coin with magical power -## Armes (Weapons) -Created new custom weapon will probably renamed it to item when we get item +### 5. Map Modifications +A collection of modifications of SCP:SL map. +* **New elevators in Surface Gate A** +* **A Gambling zone in SCP-173 Heavy Containment** +* **Supply Drops, Turret and more** +--- + +## Installation & Setup + +### Prerequisites +* A working SCP:SL server +* The [EXILED](https://github.com/ExMod-Team/EXILED) framework installed. + +### Installation Steps +1. Download the latest `Kruacent-Exiled.dll` from the [Releases](https://github.com/Kruacent/Kruacent-Exiled/releases) tab. +2. Place the `.dll` file into your server's `/EXILED/Plugins/` directory. +3. Restart your SCP:SL server. + +### Configuration +* After the first launch, the plugin will generate a configuration file in `/EXILED/Configs/`. +Here, you can can adjust configuration for many things. +--- +
+ Developed by the Kruaçent Team +