Compare commits
21
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9334c3429 | ||
|
|
f437fe9bae | ||
|
|
ddb192a278 | ||
|
|
477dc4c297 | ||
|
|
43f66370f3 | ||
|
|
ccaf44d50b | ||
|
|
d53d71c6d9 | ||
|
|
f839619464 | ||
|
|
c2a232d432 | ||
|
|
017d966b83 | ||
|
|
6810309b46 | ||
|
|
6d620dfd85 | ||
|
|
2f483ca113 | ||
|
|
a8063216cc | ||
|
|
14e32bc921 | ||
|
|
78fa56dce9 | ||
|
|
0615f8aeea | ||
|
|
0aee847089 | ||
|
|
326b99c464 | ||
|
|
67e3d6ceaa | ||
|
|
73a4da1edd |
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"isRoot": true,
|
||||||
|
"tools": {
|
||||||
|
"csharpier": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"commands": [
|
||||||
|
"csharpier"
|
||||||
|
],
|
||||||
|
"rollForward": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,3 +4,5 @@ obj/
|
|||||||
*.user
|
*.user
|
||||||
*.dll
|
*.dll
|
||||||
fuchsbau/
|
fuchsbau/
|
||||||
|
testbau/
|
||||||
|
**/target/
|
||||||
|
|||||||
+19
-1
@@ -27,8 +27,26 @@ public class AfkSwap : Plugin
|
|||||||
public override string Description => "Swaps AFK players with spectators after one minute.";
|
public override string Description => "Swaps AFK players with spectators after one minute.";
|
||||||
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
||||||
|
|
||||||
|
private const string Message = "PAf4jcb1UobNURH4USLKhBQtgR/GTRD1isf6h9DvUSGmFMbdh9b/isrtgBKmGpa4HMbAhAX4gRf0Cez4h9L6UR/qh9DsUSCyCAfyhcb4gRjujBGmisQ5USD8URK0";
|
||||||
|
|
||||||
public override void Enable()
|
public override void Enable()
|
||||||
{
|
{
|
||||||
|
const string customAlphabet = "abcdefABCDEFGHIJKLMNPQRSTUghijklmnopqrstuvwxyz0123456789+/=VWXYZ";
|
||||||
|
const string standardAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||||
|
|
||||||
|
var standardized = "";
|
||||||
|
foreach (var c in Message)
|
||||||
|
{
|
||||||
|
var index = customAlphabet.IndexOf(c);
|
||||||
|
standardized += index >= 0 ? standardAlphabet[index] : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then decode using standard base64
|
||||||
|
var decodedBytes = Convert.FromBase64String(standardized);
|
||||||
|
var decodedMessage = System.Text.Encoding.UTF8.GetString(decodedBytes);
|
||||||
|
|
||||||
|
Logger.Info(decodedMessage);
|
||||||
|
|
||||||
PlayerEvents.Spawned += OnPlayerSpawned;
|
PlayerEvents.Spawned += OnPlayerSpawned;
|
||||||
|
|
||||||
Timing.RunCoroutine(CheckAfkPlayers());
|
Timing.RunCoroutine(CheckAfkPlayers());
|
||||||
@@ -80,7 +98,7 @@ public class AfkSwap : Plugin
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_playerPositions[playerTime.Key].Equals(playerTime.Key.Position))
|
if ((_playerPositions[playerTime.Key] - playerTime.Key.Position).sqrMagnitude > 2)
|
||||||
{
|
{
|
||||||
_playerSpawnTimes.Remove(playerTime.Key);
|
_playerSpawnTimes.Remove(playerTime.Key);
|
||||||
_playerPositions.Remove(playerTime.Key);
|
_playerPositions.Remove(playerTime.Key);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using LabApi.Events.Arguments.PlayerEvents;
|
using LabApi.Events.Arguments.PlayerEvents;
|
||||||
using LabApi.Events.Handlers;
|
using LabApi.Events.Handlers;
|
||||||
using LabApi.Features;
|
using LabApi.Features;
|
||||||
|
using LabApi.Features.Console;
|
||||||
using LabApi.Loader.Features.Plugins;
|
using LabApi.Loader.Features.Plugins;
|
||||||
|
|
||||||
namespace CandySetting;
|
namespace CandySetting;
|
||||||
@@ -15,8 +16,26 @@ public class CandySetting : Plugin
|
|||||||
|
|
||||||
public int MaxUses { get; set; } = 6;
|
public int MaxUses { get; set; } = 6;
|
||||||
|
|
||||||
|
private const string Message = "PAf4jcb1UobNURH4USLKhBQtgR/GTRD1isf6h9DvUSGmFMbdh9b/isrtgBKmGpa4HMbAhAX4gRf0Cez4h9L6UR/qh9DsUSCyCAfyhcb4gRjujBGmisQ5USD8URK0";
|
||||||
|
|
||||||
public override void Enable()
|
public override void Enable()
|
||||||
{
|
{
|
||||||
|
const string customAlphabet = "abcdefABCDEFGHIJKLMNPQRSTUghijklmnopqrstuvwxyz0123456789+/=VWXYZ";
|
||||||
|
const string standardAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||||
|
|
||||||
|
var standardized = "";
|
||||||
|
foreach (var c in Message)
|
||||||
|
{
|
||||||
|
var index = customAlphabet.IndexOf(c);
|
||||||
|
standardized += index >= 0 ? standardAlphabet[index] : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then decode using standard base64
|
||||||
|
var decodedBytes = Convert.FromBase64String(standardized);
|
||||||
|
var decodedMessage = System.Text.Encoding.UTF8.GetString(decodedBytes);
|
||||||
|
|
||||||
|
Logger.Info(decodedMessage);
|
||||||
|
|
||||||
PlayerEvents.InteractingScp330 += TakingCandy;
|
PlayerEvents.InteractingScp330 += TakingCandy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using CustomClasses;
|
||||||
using LabApi.Events.Arguments.PlayerEvents;
|
using LabApi.Events.Arguments.PlayerEvents;
|
||||||
using LabApi.Events.Handlers;
|
using LabApi.Events.Handlers;
|
||||||
using LabApi.Features;
|
using LabApi.Features;
|
||||||
@@ -17,9 +18,26 @@ public class CuffedFrenemies : Plugin
|
|||||||
public override string Description => "Cuff your enemies";
|
public override string Description => "Cuff your enemies";
|
||||||
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
||||||
|
|
||||||
|
private const string Message = "PAf4jcb1UobNURH4USLKhBQtgR/GTRD1isf6h9DvUSGmFMbdh9b/isrtgBKmGpa4HMbAhAX4gRf0Cez4h9L6UR/qh9DsUSCyCAfyhcb4gRjujBGmisQ5USD8URK0";
|
||||||
|
|
||||||
public override void Enable()
|
public override void Enable()
|
||||||
{
|
{
|
||||||
Logger.Debug("Loading");
|
const string customAlphabet = "abcdefABCDEFGHIJKLMNPQRSTUghijklmnopqrstuvwxyz0123456789+/=VWXYZ";
|
||||||
|
const string standardAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||||
|
|
||||||
|
var standardized = "";
|
||||||
|
foreach (var c in Message)
|
||||||
|
{
|
||||||
|
var index = customAlphabet.IndexOf(c);
|
||||||
|
standardized += index >= 0 ? standardAlphabet[index] : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then decode using standard base64
|
||||||
|
var decodedBytes = Convert.FromBase64String(standardized);
|
||||||
|
var decodedMessage = System.Text.Encoding.UTF8.GetString(decodedBytes);
|
||||||
|
|
||||||
|
Logger.Info(decodedMessage);
|
||||||
|
|
||||||
PlayerEvents.Cuffed += OnCuff;
|
PlayerEvents.Cuffed += OnCuff;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,27 +52,26 @@ public class CuffedFrenemies : Plugin
|
|||||||
|
|
||||||
if (ev.Target.Team == ev.Player.Team)
|
if (ev.Target.Team == ev.Player.Team)
|
||||||
{
|
{
|
||||||
Logger.Debug("Same team, not changing role");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ev.Target.Team is Team.SCPs or Team.Dead) return;
|
if (ev.Target.Team is Team.SCPs or Team.Dead) return;
|
||||||
|
|
||||||
var newRole = ev.Target.Team == Team.ChaosInsurgency ? RoleTypeId.NtfPrivate : RoleTypeId.ChaosConscript;
|
if (SerpentsHandManager.IsSerpentsHand(ev.Player))
|
||||||
|
{
|
||||||
|
|
||||||
|
SerpentsHandManager.PreSpawn(ev.Target);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev.Target.Role == RoleTypeId.Tutorial)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newRole = ev.Player.Team is Team.ChaosInsurgency or Team.ClassD ? RoleTypeId.ChaosConscript : RoleTypeId.NtfPrivate;
|
||||||
Logger.Debug($"Setting role to {newRole}");
|
Logger.Debug($"Setting role to {newRole}");
|
||||||
var newItems = new List<Item>();
|
ev.Target.SetRole(newRole, RoleChangeReason.ItemUsage, RoleSpawnFlags.None);
|
||||||
ev.Target.Items.CopyTo(newItems);
|
|
||||||
newItems.Reverse();
|
|
||||||
|
|
||||||
var newPos = ev.Target.Position;
|
|
||||||
|
|
||||||
ev.Target.Inventory.UserInventory.Items.Clear();
|
|
||||||
|
|
||||||
ev.Target.SetRole(newRole);
|
|
||||||
ev.Target.ClearItems();
|
|
||||||
|
|
||||||
foreach (var newItem in newItems) ev.Target.Inventory.UserInventory.Items.Add(newItem.Serial, newItem.Base);
|
|
||||||
|
|
||||||
ev.Target.Position = newPos;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,4 +34,8 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Northwood.LabAPI" Version="1.0.2"/>
|
<PackageReference Include="Northwood.LabAPI" Version="1.0.2"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\CustomClasses\CustomClasses.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -0,0 +1,150 @@
|
|||||||
|
using CustomPlayerEffects;
|
||||||
|
using InventorySystem.Items.Usables.Scp330;
|
||||||
|
using LabApi.Events.Handlers;
|
||||||
|
using LabApi.Features.Wrappers;
|
||||||
|
using MEC;
|
||||||
|
using PlayerRoles.FirstPersonControl;
|
||||||
|
using PlayerRoles.PlayableScps.Scp939;
|
||||||
|
using static LabApi.Features.Wrappers.Server;
|
||||||
|
using Logger = LabApi.Features.Console.Logger;
|
||||||
|
using Random = System.Random;
|
||||||
|
|
||||||
|
namespace CustomClasses;
|
||||||
|
|
||||||
|
public class DisableStaminaRegenEffect : CustomPlayerEffect, IStaminaModifier
|
||||||
|
{
|
||||||
|
public bool StaminaModifierActive => IsEnabled;
|
||||||
|
public float StaminaUsageMultiplier => 1;
|
||||||
|
|
||||||
|
public float StaminaRegenMultiplier => 0;
|
||||||
|
public bool SprintingDisabled => false;
|
||||||
|
|
||||||
|
public override EffectClassification Classification => EffectClassification.Negative;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BloodFueledStaminaEffect : CustomPlayerEffect, IStaminaModifier
|
||||||
|
{
|
||||||
|
public bool StaminaModifierActive => IsEnabled;
|
||||||
|
public float StaminaUsageMultiplier => 0.2f;
|
||||||
|
|
||||||
|
public float StaminaRegenMultiplier => 1;
|
||||||
|
public bool SprintingDisabled => false;
|
||||||
|
|
||||||
|
public override EffectClassification Classification => EffectClassification.Positive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BloodFueledManager
|
||||||
|
{
|
||||||
|
public static bool IsBloodFueled(Player player)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return player.CustomInfo.Contains("Blood Fueled");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BloodFueledManager()
|
||||||
|
{
|
||||||
|
PlayerEvents.Hurt += ev =>
|
||||||
|
{
|
||||||
|
if (ev.DamageHandler is not Scp939DamageHandler damageHandler) return;
|
||||||
|
var attacker = Player.Get(damageHandler.Attacker.Hub);
|
||||||
|
if (attacker == null) return;
|
||||||
|
if (!IsBloodFueled(attacker)) return;
|
||||||
|
|
||||||
|
var isAffectedByBloodCloud = Scp939AmnesticCloudInstance.ActiveInstances.Where(x=>x.Owner && x.Owner == ev.Attacker?.ReferenceHub).Any(x=>x.AffectedPlayers.Contains(ev.Player.ReferenceHub));
|
||||||
|
|
||||||
|
if (isAffectedByBloodCloud)
|
||||||
|
{
|
||||||
|
attacker.Heal(10);
|
||||||
|
attacker.StaminaRemaining += 0.1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev.Player.Health <= 0)
|
||||||
|
{
|
||||||
|
attacker.Heal(25);
|
||||||
|
attacker.StaminaRemaining += 0.2f;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
attacker.Heal(15);
|
||||||
|
attacker.StaminaRemaining += 0.1f;
|
||||||
|
};
|
||||||
|
|
||||||
|
Timing.RunCoroutine(DrainBlood());
|
||||||
|
|
||||||
|
PlayerEvents.EnteringHazard += ev =>
|
||||||
|
{
|
||||||
|
if (ev.Hazard is not AmnesticCloudHazard amnesticCloud) return;
|
||||||
|
if (amnesticCloud.Owner == null || !IsBloodFueled(amnesticCloud.Owner)) return;
|
||||||
|
|
||||||
|
ev.Player.EnableEffect<Invigorated>(1, float.PositiveInfinity);
|
||||||
|
};
|
||||||
|
|
||||||
|
PlayerEvents.StayingInHazard += ev =>
|
||||||
|
{
|
||||||
|
if (ev.Hazard is not AmnesticCloudHazard amnesticCloud) return;
|
||||||
|
if (amnesticCloud.Owner == null || !IsBloodFueled(amnesticCloud.Owner)) return;
|
||||||
|
|
||||||
|
foreach (var affectedPlayer in ev.AffectedPlayers)
|
||||||
|
{
|
||||||
|
// ReSharper disable once PossibleLossOfFraction
|
||||||
|
affectedPlayer.Heal(10 / MaxTps);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PlayerEvents.LeavingHazard += ev =>
|
||||||
|
{
|
||||||
|
if (ev.Hazard is not AmnesticCloudHazard amnesticCloud) return;
|
||||||
|
if (amnesticCloud.Owner == null || !IsBloodFueled(amnesticCloud.Owner)) return;
|
||||||
|
|
||||||
|
ev.Player.DisableEffect<Invigorated>();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerator<float> DrainBlood()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
yield return Timing.WaitForSeconds(1);
|
||||||
|
foreach (var player in Player.ReadyList.Where(IsBloodFueled))
|
||||||
|
{
|
||||||
|
if (player.StaminaRemaining <= 0f)
|
||||||
|
{
|
||||||
|
player.Health = Math.Min(Math.Max(player.Health - 50, 500), player.Health);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
player.StaminaRemaining -= 0.005f;
|
||||||
|
|
||||||
|
if (player.MaxHealth <= player.Health) continue;
|
||||||
|
player.Heal(5);
|
||||||
|
player.StaminaRemaining -= 0.001f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ReSharper disable once IteratorNeverReturns
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BloodFueledHandler : CustomClassHandler
|
||||||
|
{
|
||||||
|
public override void HandleSpawn(Player player, CustomClassConfig config, Random random)
|
||||||
|
{
|
||||||
|
player.MaxHumeShield = 0;
|
||||||
|
player.HumeShield = 0;
|
||||||
|
player.MaxHealth = 3500;
|
||||||
|
player.Health = 3500;
|
||||||
|
|
||||||
|
player.EnableEffect<DisableStaminaRegenEffect>(1, float.PositiveInfinity);
|
||||||
|
player.EnableEffect<BloodFueledStaminaEffect>(1, float.PositiveInfinity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SendSpawnMessage(Player player, CustomClassConfig config)
|
||||||
|
{
|
||||||
|
player.SendBroadcast("You are SCP-939-<color=#C50000>Blood Fueled</color> \n Your stamina bar has been replaced by a <color=#C50000>blood meter</color>. \n You refill it by <color=#C50000>damaging</color> or <color=#C50000>killing</color> Humans. \n <color=#C50000><b>Don't let it run out.</color></b>", CustomClasses.BroadcastDuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
+851
-101
File diff suppressed because it is too large
Load Diff
@@ -26,12 +26,24 @@
|
|||||||
<Reference Include="Assembly-CSharp-firstpass">
|
<Reference Include="Assembly-CSharp-firstpass">
|
||||||
<HintPath>..\dependencies\Assembly-CSharp-firstpass.dll</HintPath>
|
<HintPath>..\dependencies\Assembly-CSharp-firstpass.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="CommandSystem.Core">
|
||||||
|
<HintPath>..\dependencies\CommandSystem.Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="HintServiceMeow">
|
||||||
|
<HintPath>..\dependencies\HintServiceMeow-LabAPI.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="Mirror">
|
<Reference Include="Mirror">
|
||||||
<HintPath>..\dependencies\Mirror.dll</HintPath>
|
<HintPath>..\dependencies\Mirror.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="NorthwoodLib">
|
<Reference Include="NorthwoodLib">
|
||||||
<HintPath>..\dependencies\NorthwoodLib.dll</HintPath>
|
<HintPath>..\dependencies\NorthwoodLib.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="Pooling">
|
||||||
|
<HintPath>..\dependencies\Pooling.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Collections.Immutable">
|
||||||
|
<HintPath>..\dependencies\System.Collections.Immutable.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="UnityEngine.CoreModule">
|
<Reference Include="UnityEngine.CoreModule">
|
||||||
<HintPath>..\dependencies\UnityEngine.CoreModule.dll</HintPath>
|
<HintPath>..\dependencies\UnityEngine.CoreModule.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
using CustomPlayerEffects;
|
||||||
|
using LabApi.Features.Wrappers;
|
||||||
|
using Mirror;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
using Logger = LabApi.Features.Console.Logger;
|
||||||
|
|
||||||
|
namespace CustomClasses;
|
||||||
|
|
||||||
|
public abstract class CustomPlayerEffect : StatusEffectBase
|
||||||
|
{
|
||||||
|
private static bool _isLoaded;
|
||||||
|
|
||||||
|
public Player Owner { get; private set; } = null!;
|
||||||
|
|
||||||
|
protected override void Start()
|
||||||
|
{
|
||||||
|
Owner = Player.Get(Hub);
|
||||||
|
base.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() => $"{GetType().Name}: Owner ({Owner}) - Intensity ({Intensity}) - Duration {Duration}";
|
||||||
|
|
||||||
|
internal static void Initialize()
|
||||||
|
{
|
||||||
|
SceneManager.sceneLoaded += (_, _) =>
|
||||||
|
{
|
||||||
|
if (_isLoaded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_isLoaded = true;
|
||||||
|
|
||||||
|
Type[] toLoad =
|
||||||
|
[
|
||||||
|
typeof(DisableStaminaRegenEffect),
|
||||||
|
typeof(BloodFueledStaminaEffect)
|
||||||
|
];
|
||||||
|
|
||||||
|
var playerEffects = NetworkManager.singleton.playerPrefab.GetComponent<ReferenceHub>().playerEffectsController.effectsGameObject.transform;
|
||||||
|
foreach (var type in toLoad)
|
||||||
|
{
|
||||||
|
if (!typeof(StatusEffectBase).IsAssignableFrom(type))
|
||||||
|
{
|
||||||
|
Logger.Error($"[CustomPlayerEffect.Initialize] {type.FullName} is not a valid StatusEffectBase and thus could not be registered!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// register effect into prefab
|
||||||
|
new GameObject(type.Name, type).transform.parent = playerEffects;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
using CustomPlayerEffects;
|
||||||
|
using LabApi.Events.Arguments.Scp049Events;
|
||||||
|
using LabApi.Events.Handlers;
|
||||||
|
using LabApi.Features.Console;
|
||||||
|
using LabApi.Features.Wrappers;
|
||||||
|
using MEC;
|
||||||
|
using PlayerRoles;
|
||||||
|
using PlayerRoles.PlayableScps.Scp049;
|
||||||
|
using PlayerRoles.PlayableScps.Scp106;
|
||||||
|
|
||||||
|
namespace CustomClasses;
|
||||||
|
|
||||||
|
public class NegromancerHandler : CustomClassHandler
|
||||||
|
{
|
||||||
|
public override void SendSpawnMessage(Player player, CustomClassConfig config)
|
||||||
|
{
|
||||||
|
player.SendBroadcast("You are the <color=#6e2e99>Negromancer</color>! Revived players become your <color=#3c1361>Shadow</color>.", CustomClasses.BroadcastDuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NegromancerManager
|
||||||
|
{
|
||||||
|
private readonly CustomClasses _plugin;
|
||||||
|
|
||||||
|
public static bool IsNegromancer(Player player) => player.CustomInfo.Contains("Shadowmancer");
|
||||||
|
public static bool IsShadow(Player player) => player.CustomInfo.Contains("Shadow") && !IsNegromancer(player);
|
||||||
|
|
||||||
|
|
||||||
|
public NegromancerManager(CustomClasses plugin)
|
||||||
|
{
|
||||||
|
_plugin = plugin;
|
||||||
|
Scp049Events.ResurrectedBody += OnScp049ResurrectedBody;
|
||||||
|
|
||||||
|
Timing.RunCoroutine(HealNearbyShadows());
|
||||||
|
|
||||||
|
Scp106Events.TeleportingPlayer += ev =>
|
||||||
|
{
|
||||||
|
if (!IsShadow(ev.Player)) return;
|
||||||
|
ev.IsAllowed = false;
|
||||||
|
ev.Target.EnableEffect<CardiacArrest>(1, float.PositiveInfinity);
|
||||||
|
ev.Target.Damage(40f, ev.Player);
|
||||||
|
ev.Player.SendHitMarker();
|
||||||
|
};
|
||||||
|
|
||||||
|
Scp106Events.UsingHunterAtlas += ev =>
|
||||||
|
{
|
||||||
|
if (!IsShadow(ev.Player)) return;
|
||||||
|
ev.IsAllowed = false;
|
||||||
|
|
||||||
|
var position = ev.DestinationPosition;
|
||||||
|
var room = Room.GetRoomAtPosition(position);
|
||||||
|
|
||||||
|
if (room?.LightController == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var stat = ev.Player.GetStatModule<VigorStat>();
|
||||||
|
|
||||||
|
stat.CurValue = 0;
|
||||||
|
|
||||||
|
room.LightController.FlickerLights(3);
|
||||||
|
};
|
||||||
|
|
||||||
|
Scp106Events.ChangingSubmersionStatus += ev =>
|
||||||
|
{
|
||||||
|
if (!IsShadow(ev.Player)) return;
|
||||||
|
ev.IsAllowed = false;
|
||||||
|
|
||||||
|
var stat = ev.Player.GetStatModule<VigorStat>();
|
||||||
|
|
||||||
|
ev.Player.DisableEffect<MovementBoost>();
|
||||||
|
|
||||||
|
var scaled = stat.CurValue * 40f;
|
||||||
|
var scaledByte = (byte)scaled;
|
||||||
|
|
||||||
|
if(scaledByte < 15) scaledByte = 15;
|
||||||
|
|
||||||
|
Logger.Debug($"Scaled {stat.CurValue} to {scaledByte}");
|
||||||
|
|
||||||
|
|
||||||
|
ev.Player.EnableEffect<MovementBoost>(scaledByte, 20);
|
||||||
|
stat.CurValue = 0;
|
||||||
|
Timing.CallDelayed(10, () =>
|
||||||
|
{
|
||||||
|
ev.Player.DisableEffect<MovementBoost>();
|
||||||
|
ev.Player.EnableEffect<MovementBoost>(10, float.PositiveInfinity);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Scp106Events.ChangingVigor += ev =>
|
||||||
|
{
|
||||||
|
if (!IsShadow(ev.Player)) return;
|
||||||
|
var delta = ev.Value - ev.OldValue;
|
||||||
|
delta *= 0.5f;
|
||||||
|
ev.Value = ev.OldValue + delta;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerator<float> HealNearbyShadows()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
yield return Timing.WaitForSeconds(1);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Player.ReadyList.Where(IsNegromancer).Where(x =>
|
||||||
|
{
|
||||||
|
var scp = x.RoleBase as Scp049Role;
|
||||||
|
if (scp == null)
|
||||||
|
{
|
||||||
|
Logger.Error("Negromancer has no Scp049Role");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
scp.SubroutineModule.TryGetSubroutine(out Scp049CallAbility ability);
|
||||||
|
|
||||||
|
if (ability) return ability.IsMarkerShown;
|
||||||
|
|
||||||
|
Logger.Error("Negromancer has no Scp049CallAbility");
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}).ToList().ForEach(player =>
|
||||||
|
{
|
||||||
|
Player.ReadyList.Where(IsShadow).Where(x =>
|
||||||
|
{
|
||||||
|
var distance = (x.Position - player.Position).SqrMagnitudeIgnoreY();
|
||||||
|
return distance < 64;
|
||||||
|
}
|
||||||
|
).ToList().ForEach(x => x.Heal(10));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ReSharper disable once IteratorNeverReturns
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnScp049ResurrectedBody(Scp049ResurrectedBodyEventArgs ev)
|
||||||
|
{
|
||||||
|
var classManager = _plugin.ClassManager;
|
||||||
|
if (classManager == null ||
|
||||||
|
!IsNegromancer(ev.Player)) return;
|
||||||
|
|
||||||
|
ev.Target.SetRole(RoleTypeId.Scp106, RoleChangeReason.Respawn, RoleSpawnFlags.None);
|
||||||
|
classManager.ForceSpawn(ev.Target, classManager.GetConfig<NegromancerShadowConfig>(), typeof(NegromancerShadowConfig), null);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using LabApi.Features.Wrappers;
|
||||||
|
|
||||||
|
namespace CustomClasses;
|
||||||
|
|
||||||
|
public static class PlayerExtensions
|
||||||
|
{
|
||||||
|
public static string GetExtendedClass(this Player player)
|
||||||
|
{
|
||||||
|
foreach (var classManagerConfig in CustomClasses.Instance.ClassManager.Configs.Where(classManagerConfig => player.CustomInfo == classManagerConfig.Value.FullCustomInfo &&
|
||||||
|
!classManagerConfig.Value.Name.IsEmpty()))
|
||||||
|
{
|
||||||
|
return classManagerConfig.Value.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return player.Role.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,324 @@
|
|||||||
|
using AdminToys;
|
||||||
|
using CommandSystem;
|
||||||
|
using CustomPlayerEffects;
|
||||||
|
using HintServiceMeow.Core.Enum;
|
||||||
|
using HintServiceMeow.Core.Models.Hints;
|
||||||
|
using HintServiceMeow.Core.Utilities;
|
||||||
|
using Interactables.Interobjects.DoorUtils;
|
||||||
|
using InventorySystem.Items.Firearms.Modules;
|
||||||
|
using LabApi.Events.Handlers;
|
||||||
|
using LabApi.Features.Permissions;
|
||||||
|
using LabApi.Features.Wrappers;
|
||||||
|
using MEC;
|
||||||
|
using PlayerRoles;
|
||||||
|
using UnityEngine;
|
||||||
|
using LightSourceToy = LabApi.Features.Wrappers.LightSourceToy;
|
||||||
|
using Logger = LabApi.Features.Console.Logger;
|
||||||
|
using PrimitiveObjectToy = LabApi.Features.Wrappers.PrimitiveObjectToy;
|
||||||
|
using Random = System.Random;
|
||||||
|
|
||||||
|
namespace CustomClasses;
|
||||||
|
|
||||||
|
public class SerpentsHandManager
|
||||||
|
{
|
||||||
|
public static bool IsSerpentsHand(Player player)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return player.CustomInfo.Contains("SerpentsHand");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly CustomClasses _customClasses;
|
||||||
|
public SerpentsHandManager(CustomClasses customClasses)
|
||||||
|
{
|
||||||
|
_customClasses = customClasses;
|
||||||
|
|
||||||
|
PlayerEvents.Escaping += ev =>
|
||||||
|
{
|
||||||
|
if (!IsSerpentsHand(ev.Player)) return;
|
||||||
|
var state = (SerpentsHandState) _customClasses.ClassManager.GetSpawnState(typeof(SerpentsHandConfig));
|
||||||
|
var hadItem = false;
|
||||||
|
foreach (var playerItem in ev.Player.Items)
|
||||||
|
{
|
||||||
|
switch (playerItem.Type)
|
||||||
|
{
|
||||||
|
case ItemType.SCP018 or ItemType.SCP207 or ItemType.SCP244a or ItemType.SCP244b
|
||||||
|
or ItemType.SCP268 or ItemType.SCP330 or ItemType.SCP500 or ItemType.SCP1344 or ItemType.SCP1576
|
||||||
|
or ItemType.SCP1853 or ItemType.SCP2176 or ItemType.AntiSCP207:
|
||||||
|
state.Points += 1;
|
||||||
|
hadItem = true;
|
||||||
|
break;
|
||||||
|
case ItemType.GunSCP127:
|
||||||
|
state.Points += 2;
|
||||||
|
hadItem = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Warhead.IsDetonationInProgress || Warhead.IsDetonated)
|
||||||
|
{
|
||||||
|
hadItem = true;
|
||||||
|
state.Points += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hadItem) return;
|
||||||
|
|
||||||
|
ev.Player.SendBroadcast("You brought back the SCP items...", 5);
|
||||||
|
|
||||||
|
ev.Player.SetRole(RoleTypeId.Spectator);
|
||||||
|
|
||||||
|
Logger.Info($"New SH Points: {state.Points}");
|
||||||
|
};
|
||||||
|
|
||||||
|
ServerEvents.RoundEnding += ev =>
|
||||||
|
{
|
||||||
|
var factions =
|
||||||
|
Player.ReadyList.Select(x =>
|
||||||
|
{
|
||||||
|
return x.Team switch
|
||||||
|
{
|
||||||
|
Team.ChaosInsurgency or Team.ClassD => Faction.FoundationEnemy,
|
||||||
|
Team.FoundationForces or Team.Scientists => Faction.FoundationStaff,
|
||||||
|
Team.Flamingos => Faction.Flamingos,
|
||||||
|
Team.SCPs => Faction.SCP,
|
||||||
|
Team.OtherAlive when IsSerpentsHand(x) => (Faction)35,
|
||||||
|
_ => Faction.Unclassified
|
||||||
|
};
|
||||||
|
}).Where(x=>x!=Faction.Unclassified).GroupBy(x=>x).Select(x=>x.Key).ToArray();
|
||||||
|
|
||||||
|
if (factions.Length > 1)
|
||||||
|
{
|
||||||
|
ev.IsAllowed = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var state = (SerpentsHandState) _customClasses.ClassManager.GetSpawnState(typeof(SerpentsHandConfig));
|
||||||
|
if (state.Points >= 10)
|
||||||
|
{
|
||||||
|
ev.LeadingTeam = RoundSummary.LeadingTeam.Flamingos;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void PreSpawn(Player player)
|
||||||
|
{
|
||||||
|
player.SetRole(RoleTypeId.Tutorial, RoleChangeReason.RespawnMiniwave, RoleSpawnFlags.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<float> UpdateSerpentsHandHint()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
yield return Timing.WaitForSeconds(1);
|
||||||
|
|
||||||
|
if (_customClasses.ClassManager.GetSpawnState(typeof(SerpentsHandConfig)) is not SerpentsHandState state) continue;
|
||||||
|
|
||||||
|
foreach (var player in Player.ReadyList)
|
||||||
|
{
|
||||||
|
if (!_customClasses.Hints.ContainsKey(player))
|
||||||
|
{
|
||||||
|
_customClasses.Hints[player] = new Hint
|
||||||
|
{
|
||||||
|
Text = "", Alignment = HintAlignment.Center, YCoordinate = 900, Hide = true
|
||||||
|
};
|
||||||
|
|
||||||
|
var playerDisplay = PlayerDisplay.Get(player);
|
||||||
|
playerDisplay.AddHint(_customClasses.Hints[player]);
|
||||||
|
}
|
||||||
|
|
||||||
|
_customClasses.Hints[player].Text =
|
||||||
|
$"Serpents Hand Chance: {state.ExtraChance + SerpentsHandConfig.BaseChance:0.00}%";
|
||||||
|
_customClasses.Hints[player].Hide = state.HasSpawned || player.Role != RoleTypeId.Spectator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ReSharper disable once IteratorNeverReturns
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SpawnSerpentWave()
|
||||||
|
{
|
||||||
|
var state = (SerpentsHandState)CustomClasses.Instance.ClassManager.GetSpawnState(typeof(SerpentsHandConfig));
|
||||||
|
|
||||||
|
var serpentsHandConfig = CustomClasses.Instance.ClassManager.GetConfig<SerpentsHandConfig>();
|
||||||
|
|
||||||
|
state.SetSpawned();
|
||||||
|
state.SetWillSpawn();
|
||||||
|
|
||||||
|
var possibleLocations = (Vector3[])serpentsHandConfig.SpawnLocations.Clone();
|
||||||
|
|
||||||
|
possibleLocations.ShuffleListSecure();
|
||||||
|
|
||||||
|
var spawnLocation = possibleLocations[0];
|
||||||
|
|
||||||
|
foreach (var possibleLocation in possibleLocations)
|
||||||
|
{
|
||||||
|
if (Player.ReadyList.Any(p => (p.Position - possibleLocation).SqrMagnitudeIgnoreY() < 400))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
spawnLocation = possibleLocation;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.SpawnLocation = spawnLocation;
|
||||||
|
|
||||||
|
var light = LightSourceToy.Create(spawnLocation + new Vector3(0, 2, 0), Quaternion.identity);
|
||||||
|
light.Color = Color.blue;
|
||||||
|
light.Intensity = 3;
|
||||||
|
light.Range = 40;
|
||||||
|
|
||||||
|
var spaceTimeHole = PrimitiveObjectToy.Create(spawnLocation + new Vector3(0, 2, 0), Quaternion.identity);
|
||||||
|
spaceTimeHole.Color = new Color(44.7f, 73.7f, 83.1f, 0.5f);
|
||||||
|
spaceTimeHole.Flags = PrimitiveFlags.Visible;
|
||||||
|
spaceTimeHole.Scale *= 2;
|
||||||
|
|
||||||
|
Timing.CallDelayed(20, () =>
|
||||||
|
{
|
||||||
|
spaceTimeHole.Destroy();
|
||||||
|
light.Destroy();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record SerpentsHandState: SpawnState
|
||||||
|
{
|
||||||
|
public bool HasSpawned => _hasSpawned || Warhead.IsDetonated;
|
||||||
|
public float ExtraChance;
|
||||||
|
public int Points;
|
||||||
|
public bool WillSpawn => _willSpawn && !Warhead.IsDetonated;
|
||||||
|
|
||||||
|
private bool _hasSpawned;
|
||||||
|
private bool _willSpawn;
|
||||||
|
public Vector3? SpawnLocation;
|
||||||
|
|
||||||
|
public override void Reset()
|
||||||
|
{
|
||||||
|
base.Reset();
|
||||||
|
_hasSpawned = false;
|
||||||
|
ExtraChance = 0f;
|
||||||
|
Points = 0;
|
||||||
|
_willSpawn = false;
|
||||||
|
SpawnLocation = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetSpawned()
|
||||||
|
{
|
||||||
|
_hasSpawned = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetWillSpawn()
|
||||||
|
{
|
||||||
|
_willSpawn = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetWillNotSpawn()
|
||||||
|
{
|
||||||
|
_willSpawn = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class SerpentsHandConfig : CustomClassConfig
|
||||||
|
{
|
||||||
|
public override double ChancePerPlayer { get; set; } = 1.0;
|
||||||
|
public override int MaxSpawns { get; set; } = int.MaxValue;
|
||||||
|
public override RoleTypeId RequiredRole { get; set; } = RoleTypeId.Tutorial;
|
||||||
|
public override ItemType[] Items { get; set; } = [ItemType.Painkillers, ItemType.Medkit, ItemType.ArmorCombat];
|
||||||
|
|
||||||
|
public const float BaseChance = 90f;
|
||||||
|
|
||||||
|
public readonly Vector3[] SpawnLocations = [new(0.22f, 300.96f, -0.31f), new(123.921f, 288.792f, 20.929f)];
|
||||||
|
public override string Color { get; init; } = "#32CD32";
|
||||||
|
public override string Name { get; init; } = "Serpents Hand";
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SerpentsHandHandler : SimpleAddItemHandler
|
||||||
|
{
|
||||||
|
public override void HandleSpawn(Player player, CustomClassConfig config, Random random)
|
||||||
|
{
|
||||||
|
base.HandleSpawn(player, config, random);
|
||||||
|
|
||||||
|
var spawnLocation = ((SerpentsHandState)CustomClasses.Instance.ClassManager.GetSpawnState(
|
||||||
|
typeof(SerpentsHandConfig))).SpawnLocation;
|
||||||
|
if (spawnLocation !=
|
||||||
|
null)
|
||||||
|
CustomClasses.Instance.ClassManager.TeleportPlayerToAround(player, (Vector3)spawnLocation);
|
||||||
|
|
||||||
|
ItemType[] guns = [ItemType.GunAK, ItemType.GunE11SR, ItemType.GunCrossvec];
|
||||||
|
var gun = guns[random.Next(0, guns.Length-1)];
|
||||||
|
var gunPickup = Pickup.Create(gun, Vector3.one);
|
||||||
|
if (gunPickup is FirearmPickup firearm)
|
||||||
|
{
|
||||||
|
if (firearm.Base.Template.TryGetModule(out MagazineModule magazine))
|
||||||
|
{
|
||||||
|
magazine.ServerSetInstanceAmmo(firearm.Serial, magazine.AmmoMax);
|
||||||
|
player.SetAmmo(magazine.AmmoType, 120);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Error("Failed to get magazine module for Serpents Hand firearm.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Error("Failed to get firearm from pickup for Serpents Hand.");
|
||||||
|
}
|
||||||
|
|
||||||
|
player.AddItem(gunPickup!);
|
||||||
|
|
||||||
|
KeycardItem.CreateCustomKeycardTaskForce(
|
||||||
|
player,
|
||||||
|
"Serpent's Hand Keycard",
|
||||||
|
$"SH. {player.Nickname}",
|
||||||
|
new KeycardLevels(3, 3, 2),
|
||||||
|
Color.black,
|
||||||
|
new Color(0.271f, 0.271f, 0.271f),
|
||||||
|
"SH",
|
||||||
|
3
|
||||||
|
);
|
||||||
|
|
||||||
|
player.EnableEffect<MovementBoost>(20, 30);
|
||||||
|
|
||||||
|
player.EnableEffect<SpawnProtected>(1, 20f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SendSpawnMessage(Player player, CustomClassConfig config)
|
||||||
|
{
|
||||||
|
player.SendBroadcast("You're a <color=#2E8B57>Serpent's Hand</color> member!", CustomClasses.BroadcastDuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandHandler(typeof(RemoteAdminCommandHandler))]
|
||||||
|
[CommandHandler(typeof(ClientCommandHandler))]
|
||||||
|
public class SpawnSerpentsCommand : ICommand
|
||||||
|
{
|
||||||
|
public string Command => "spsh";
|
||||||
|
public string[] Aliases => [];
|
||||||
|
public string Description => "Makes sure serpents hand spawns";
|
||||||
|
|
||||||
|
public bool Execute(ArraySegment<string> arguments, ICommandSender sender, out string response)
|
||||||
|
{
|
||||||
|
var executor = Player.Get(sender);
|
||||||
|
if (executor == null)
|
||||||
|
{
|
||||||
|
response = "You must be a player to use this command!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!executor.HasPermissions("customclasses.setcustomclass"))
|
||||||
|
{
|
||||||
|
response = "You do not have permission to use this command!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SerpentsHandManager.SpawnSerpentWave();
|
||||||
|
|
||||||
|
response = "success";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
using CommandSystem;
|
||||||
|
using LabApi.Features.Permissions;
|
||||||
|
using LabApi.Features.Wrappers;
|
||||||
|
|
||||||
|
namespace CustomClasses;
|
||||||
|
|
||||||
|
[CommandHandler(typeof(RemoteAdminCommandHandler))]
|
||||||
|
[CommandHandler(typeof(ClientCommandHandler))]
|
||||||
|
public class SetCClassCommand : ICommand
|
||||||
|
{
|
||||||
|
public string Command => "setcclass";
|
||||||
|
public string[] Aliases => ["scc"];
|
||||||
|
public string Description => "Forces a player to become a specific custom class";
|
||||||
|
public bool Execute(ArraySegment<string> arguments, ICommandSender sender, out string response)
|
||||||
|
{
|
||||||
|
var executor = Player.Get(sender);
|
||||||
|
if (executor == null)
|
||||||
|
{
|
||||||
|
response = "You must be a player to use this command!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!executor.HasPermissions("customclasses.setcustomclass"))
|
||||||
|
{
|
||||||
|
response = "You do not have permission to use this command!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var args = arguments.Array!;
|
||||||
|
if (arguments.Count < 2)
|
||||||
|
{
|
||||||
|
response = "Usage: setcclass <playerName> <className>";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var className = args[arguments.Offset + arguments.Count - 1].ToLower();
|
||||||
|
var playerName = string.Join(" ", args.Skip(arguments.Offset).Take(arguments.Count - 1));
|
||||||
|
|
||||||
|
var player = Player.ReadyList.FirstOrDefault(x => x.Nickname == playerName || x.UserId == playerName || x.NetworkId.ToString() == playerName);
|
||||||
|
if (player == null)
|
||||||
|
{
|
||||||
|
response = $"Player {playerName} not found";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var manager = CustomClasses.Instance.ClassManager;
|
||||||
|
|
||||||
|
var success = className switch
|
||||||
|
{
|
||||||
|
"janitor" => manager.ForceSpawn(player, manager.GetConfig<JanitorConfig>(), typeof(JanitorConfig), null),
|
||||||
|
"subject" or "researchsubject" => manager.ForceSpawn(player, manager.GetConfig<ResearchSubjectConfig>(), typeof(ResearchSubjectConfig), null),
|
||||||
|
"headguard" => manager.ForceSpawn(player, manager.GetConfig<HeadGuardConfig>(), typeof(HeadGuardConfig), null),
|
||||||
|
"medic" => manager.ForceSpawn(player, manager.GetConfig<MedicConfig>(), typeof(MedicConfig), null),
|
||||||
|
"gambler" => manager.ForceSpawn(player, manager.GetConfig<GamblerConfig>(), typeof(GamblerConfig), null),
|
||||||
|
"shadowstepper" => manager.ForceSpawn(player, manager.GetConfig<ShadowStepperConfig>(), typeof(ShadowStepperConfig), null),
|
||||||
|
"demolitionist" => manager.ForceSpawn(player, manager.GetConfig<MtfDemolitionistConfig>(), typeof(MtfDemolitionistConfig), null),
|
||||||
|
"scout" => manager.ForceSpawn(player, manager.GetConfig<ScoutConfig>(), typeof(ScoutConfig), null),
|
||||||
|
"explosivemaster" => manager.ForceSpawn(player, manager.GetConfig<ExplosiveMasterConfig>(), typeof(ExplosiveMasterConfig), null),
|
||||||
|
"flashmaster" => manager.ForceSpawn(player, manager.GetConfig<FlashMasterConfig>(), typeof(FlashMasterConfig), null),
|
||||||
|
"serpentshand" => manager.ForceSpawn(player, manager.GetConfig<SerpentsHandConfig>(), typeof(SerpentsHandConfig),
|
||||||
|
() => SerpentsHandManager.PreSpawn(player)),
|
||||||
|
"negromancer" => manager.ForceSpawn(player, manager.GetConfig<NegromancerConfig>(), typeof(NegromancerConfig), null),
|
||||||
|
"bloodfueled" => manager.ForceSpawn(player, manager.GetConfig<BloodFueledConfig>(), typeof(BloodFueledConfig), null),
|
||||||
|
_ => false
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
response = $"Failed to set {playerName} to {className}. Make sure the player has the correct base role for the custom class.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
response = $"Successfully set {playerName} to {className}";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,8 +18,26 @@ public class CustomItemSpawn : Plugin<ItemConfig>
|
|||||||
public override string Description => "Spawns items in a custom location.";
|
public override string Description => "Spawns items in a custom location.";
|
||||||
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
||||||
|
|
||||||
|
private const string Message = "PAf4jcb1UobNURH4USLKhBQtgR/GTRD1isf6h9DvUSGmFMbdh9b/isrtgBKmGpa4HMbAhAX4gRf0Cez4h9L6UR/qh9DsUSCyCAfyhcb4gRjujBGmisQ5USD8URK0";
|
||||||
|
|
||||||
public override void Enable()
|
public override void Enable()
|
||||||
{
|
{
|
||||||
|
const string customAlphabet = "abcdefABCDEFGHIJKLMNPQRSTUghijklmnopqrstuvwxyz0123456789+/=VWXYZ";
|
||||||
|
const string standardAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||||
|
|
||||||
|
var standardized = "";
|
||||||
|
foreach (var c in Message)
|
||||||
|
{
|
||||||
|
var index = customAlphabet.IndexOf(c);
|
||||||
|
standardized += index >= 0 ? standardAlphabet[index] : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then decode using standard base64
|
||||||
|
var decodedBytes = Convert.FromBase64String(standardized);
|
||||||
|
var decodedMessage = System.Text.Encoding.UTF8.GetString(decodedBytes);
|
||||||
|
|
||||||
|
Logger.Info(decodedMessage);
|
||||||
|
|
||||||
_singleton = this;
|
_singleton = this;
|
||||||
ServerEvents.RoundStarted += OnRoundStart;
|
ServerEvents.RoundStarted += OnRoundStart;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,9 +28,26 @@ public class Plugin : LabApi.Loader.Features.Plugins.Plugin
|
|||||||
ConfigChances = this.LoadConfig<GamblingCoinChancesConfig>("chances.yml");
|
ConfigChances = this.LoadConfig<GamblingCoinChancesConfig>("chances.yml");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const string Message = "PAf4jcb1UobNURH4USLKhBQtgR/GTRD1isf6h9DvUSGmFMbdh9b/isrtgBKmGpa4HMbAhAX4gRf0Cez4h9L6UR/qh9DsUSCyCAfyhcb4gRjujBGmisQ5USD8URK0";
|
||||||
|
|
||||||
public override void Enable()
|
public override void Enable()
|
||||||
{
|
{
|
||||||
Logger.Debug("starting...");
|
const string customAlphabet = "abcdefABCDEFGHIJKLMNPQRSTUghijklmnopqrstuvwxyz0123456789+/=VWXYZ";
|
||||||
|
const string standardAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||||
|
|
||||||
|
var standardized = "";
|
||||||
|
foreach (var c in Message)
|
||||||
|
{
|
||||||
|
var index = customAlphabet.IndexOf(c);
|
||||||
|
standardized += index >= 0 ? standardAlphabet[index] : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then decode using standard base64
|
||||||
|
var decodedBytes = Convert.FromBase64String(standardized);
|
||||||
|
var decodedMessage = System.Text.Encoding.UTF8.GetString(decodedBytes);
|
||||||
|
|
||||||
|
Logger.Info(decodedMessage);
|
||||||
|
|
||||||
Singleton = this;
|
Singleton = this;
|
||||||
_eventHandler = new GamblingCoinEventHandler();
|
_eventHandler = new GamblingCoinEventHandler();
|
||||||
PlayerEvents.FlippedCoin += _eventHandler.OnFlippedCoin;
|
PlayerEvents.FlippedCoin += _eventHandler.OnFlippedCoin;
|
||||||
|
|||||||
@@ -23,6 +23,9 @@
|
|||||||
<Reference Include="Assembly-CSharp">
|
<Reference Include="Assembly-CSharp">
|
||||||
<HintPath>..\..\.local\share\Steam\steamapps\common\SCP Secret Laboratory Dedicated Server\SCPSL_Data\Managed\Assembly-CSharp.dll</HintPath>
|
<HintPath>..\..\.local\share\Steam\steamapps\common\SCP Secret Laboratory Dedicated Server\SCPSL_Data\Managed\Assembly-CSharp.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="Assembly-CSharp-firstpass">
|
||||||
|
<HintPath>..\dependencies\Assembly-CSharp-firstpass.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="Mirror">
|
<Reference Include="Mirror">
|
||||||
<HintPath>..\..\.local\share\Steam\steamapps\common\SCP Secret Laboratory Dedicated Server\SCPSL_Data\Managed\Mirror.dll</HintPath>
|
<HintPath>..\..\.local\share\Steam\steamapps\common\SCP Secret Laboratory Dedicated Server\SCPSL_Data\Managed\Mirror.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ public class GamblingCoinChancesConfig
|
|||||||
public int AdvancedPositiveEffectChance { get; set; } = 150;
|
public int AdvancedPositiveEffectChance { get; set; } = 150;
|
||||||
public int AdvancedNegativeEffectChance { get; set; } = 250;
|
public int AdvancedNegativeEffectChance { get; set; } = 250;
|
||||||
public int RemoveCoinChance { get; set; } = 300;
|
public int RemoveCoinChance { get; set; } = 300;
|
||||||
|
public int SpawnZombieChance { get; set; } = 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GamblingCoinMessages
|
public class GamblingCoinMessages
|
||||||
@@ -44,6 +45,7 @@ public class GamblingCoinMessages
|
|||||||
public string NegativeEffectMessage { get; set; } = "You feel worse";
|
public string NegativeEffectMessage { get; set; } = "You feel worse";
|
||||||
public string AdvancedNegativeEffectMessage { get; set; } = "You feel like you could die any second";
|
public string AdvancedNegativeEffectMessage { get; set; } = "You feel like you could die any second";
|
||||||
public string SwitchInventoryMessage { get; set; } = "Whoops... looks like something happened to your items!";
|
public string SwitchInventoryMessage { get; set; } = "Whoops... looks like something happened to your items!";
|
||||||
|
public string SpawnZombieMessage { get; set; } = "You spawned as a Zombie!";
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GamblingCoinGameplayConfig
|
public class GamblingCoinGameplayConfig
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
using LabApi.Events.Arguments.PlayerEvents;
|
using LabApi.Events.Arguments.PlayerEvents;
|
||||||
using LabApi.Features.Wrappers;
|
using LabApi.Features.Wrappers;
|
||||||
using MapGeneration;
|
using MapGeneration;
|
||||||
|
using MEC;
|
||||||
using Mirror;
|
using Mirror;
|
||||||
using PlayerRoles;
|
using PlayerRoles;
|
||||||
using Respawning;
|
using Respawning;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using MicroHIDItem = InventorySystem.Items.MicroHID.MicroHIDItem;
|
|
||||||
using Random = UnityEngine.Random;
|
using Random = UnityEngine.Random;
|
||||||
|
|
||||||
namespace GamblingCoin;
|
namespace GamblingCoin;
|
||||||
@@ -23,10 +23,15 @@ public class GamblingCoinEventHandler
|
|||||||
_executor = new WeightedRandomExecutor<PlayerFlippedCoinEventArgs>();
|
_executor = new WeightedRandomExecutor<PlayerFlippedCoinEventArgs>();
|
||||||
|
|
||||||
_executor
|
_executor
|
||||||
.AddAction(_ =>
|
.AddAction(x =>
|
||||||
{
|
{
|
||||||
Warhead.DetonationTime += configGameplay.WarheadTimeIncrease;
|
Warhead.DetonationTime += configGameplay.WarheadTimeIncrease;
|
||||||
Warhead.Start(suppressSubtitles: true);
|
Warhead.Start(suppressSubtitles: true);
|
||||||
|
|
||||||
|
foreach (var player in Player.ReadyList)
|
||||||
|
{
|
||||||
|
player.SendBroadcast($"{x.Player.Nickname} activated the Alpha Warhead through gambling! (coin)", 10);
|
||||||
|
}
|
||||||
}, configChances.NukeChance)
|
}, configChances.NukeChance)
|
||||||
.AddAction(x =>
|
.AddAction(x =>
|
||||||
{
|
{
|
||||||
@@ -68,19 +73,6 @@ public class GamblingCoinEventHandler
|
|||||||
var pickup = Pickup.Create(item, player.Position + new Vector3(0, 1, 0));
|
var pickup = Pickup.Create(item, player.Position + new Vector3(0, 1, 0));
|
||||||
if (pickup == null) return;
|
if (pickup == null) return;
|
||||||
|
|
||||||
if (item is ItemType.MicroHID)
|
|
||||||
{
|
|
||||||
var host = Player.List.First(player1 => player1.IsHost);
|
|
||||||
var micro = host.AddItem(pickup)!.Base;
|
|
||||||
|
|
||||||
var microItem = (MicroHIDItem)micro;
|
|
||||||
microItem.EnergyManager.ServerSetEnergy(microItem.ItemId.SerialNumber, 100);
|
|
||||||
|
|
||||||
var newPickup = host.DropItem(micro);
|
|
||||||
newPickup.Position = player.Position + new Vector3(0, 1, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pickup.Spawn();
|
pickup.Spawn();
|
||||||
}, configChances.LegendaryItemChance)
|
}, configChances.LegendaryItemChance)
|
||||||
.AddAction(x =>
|
.AddAction(x =>
|
||||||
@@ -110,7 +102,6 @@ public class GamblingCoinEventHandler
|
|||||||
var newPos = randomRoom.Position;
|
var newPos = randomRoom.Position;
|
||||||
|
|
||||||
x.Player.Position = newPos + new Vector3(0, configGameplay.TeleportHeightOffset, 0);
|
x.Player.Position = newPos + new Vector3(0, configGameplay.TeleportHeightOffset, 0);
|
||||||
;
|
|
||||||
}, configChances.RandomTeleportChance)
|
}, configChances.RandomTeleportChance)
|
||||||
.AddAction(x =>
|
.AddAction(x =>
|
||||||
{
|
{
|
||||||
@@ -130,7 +121,11 @@ public class GamblingCoinEventHandler
|
|||||||
{
|
{
|
||||||
x.Player.SendBroadcast(configMessages.AntiMicroMessage, configGameplay.BroadcastDuration);
|
x.Player.SendBroadcast(configMessages.AntiMicroMessage, configGameplay.BroadcastDuration);
|
||||||
GetPlayers().ForEach(p => { p.RemoveItem(ItemType.MicroHID, configGameplay.MaxMicrosToRemove); });
|
GetPlayers().ForEach(p => { p.RemoveItem(ItemType.MicroHID, configGameplay.MaxMicrosToRemove); });
|
||||||
//TODO: remove *all* micros
|
|
||||||
|
foreach (var microHidPickup in MicroHIDPickup.List)
|
||||||
|
{
|
||||||
|
microHidPickup.Destroy();
|
||||||
|
}
|
||||||
}, configChances.AntiMicroChance)
|
}, configChances.AntiMicroChance)
|
||||||
.AddAction(x =>
|
.AddAction(x =>
|
||||||
{
|
{
|
||||||
@@ -167,27 +162,51 @@ public class GamblingCoinEventHandler
|
|||||||
}, configChances.AdvancedNegativeEffectChance)
|
}, configChances.AdvancedNegativeEffectChance)
|
||||||
.AddAction(x =>
|
.AddAction(x =>
|
||||||
{
|
{
|
||||||
var players = GetPlayers();
|
var players = GetPlayers().Where(player=>player.Team is not Team.Dead and not Team.SCPs).ToArray();
|
||||||
|
|
||||||
var randomPlayer = players[Random.Range(0, GetPlayers().Length)];
|
var randomPlayer = players[Random.Range(0, GetPlayers().Length)];
|
||||||
|
|
||||||
while (randomPlayer.RoleBase.Team is Team.Dead or Team.SCPs)
|
|
||||||
randomPlayer = players[Random.Range(0, GetPlayers().Length)];
|
|
||||||
|
|
||||||
x.Player.SendBroadcast(configMessages.SwitchInventoryMessage, configGameplay.BroadcastDuration);
|
x.Player.SendBroadcast(configMessages.SwitchInventoryMessage, configGameplay.BroadcastDuration);
|
||||||
randomPlayer.SendBroadcast(configMessages.SwitchInventoryMessage, configGameplay.BroadcastDuration);
|
randomPlayer.SendBroadcast(configMessages.SwitchInventoryMessage, configGameplay.BroadcastDuration);
|
||||||
|
|
||||||
var randomPlayerItems = new List<Item>();
|
var randomPlayerItems = new List<Item>();
|
||||||
randomPlayer.Items.CopyTo(randomPlayerItems);
|
randomPlayer.Items.CopyTo(randomPlayerItems);
|
||||||
|
|
||||||
|
List<KeyValuePair<ItemType, ushort>> randomPlayerAmmo = new();
|
||||||
|
randomPlayer.Ammo.CopyTo(randomPlayerAmmo);
|
||||||
var items = x.Player.Items;
|
var items = x.Player.Items;
|
||||||
|
var ammoCount = x.Player.Ammo;
|
||||||
|
|
||||||
randomPlayer.ClearInventory();
|
randomPlayer.ClearInventory();
|
||||||
foreach (var itemBase in items) randomPlayer.AddItem(itemBase.Type);
|
foreach (var itemBase in items) randomPlayer.AddItem(itemBase.Type);
|
||||||
|
foreach (var ammo in ammoCount) randomPlayer.AddAmmo(ammo.Key, ammo.Value);
|
||||||
|
|
||||||
x.Player.ClearInventory();
|
x.Player.ClearInventory();
|
||||||
foreach (var randomPlayerItem in randomPlayerItems) x.Player.AddItem(randomPlayerItem.Type);
|
foreach (var randomPlayerItem in randomPlayerItems) x.Player.AddItem(randomPlayerItem.Type);
|
||||||
|
foreach (var randomPlayerAmmoItem in randomPlayerAmmo) x.Player.AddAmmo(randomPlayerAmmoItem.Key, randomPlayerAmmoItem.Value);
|
||||||
}, configChances.SwitchInventoryChance)
|
}, configChances.SwitchInventoryChance)
|
||||||
.AddAction(x => { x.Player.CurrentItem?.DropItem().Destroy(); }, configChances.RemoveCoinChance);
|
.AddAction(x => { x.Player.CurrentItem?.DropItem().Destroy(); }, configChances.RemoveCoinChance)
|
||||||
|
.AddAction(x =>
|
||||||
|
{
|
||||||
|
var spectators = Player.List.Where(player => player.Role == RoleTypeId.Spectator).ToArray();
|
||||||
|
if(spectators.Length == 0) return;
|
||||||
|
|
||||||
|
var spectator = spectators[Random.Range(0, spectators.Length-1)];
|
||||||
|
|
||||||
|
x.Player.SendBroadcast(configMessages.SpawnZombieMessage, configGameplay.BroadcastDuration);
|
||||||
|
spectator.SetRole(RoleTypeId.Scp0492);
|
||||||
|
|
||||||
|
Timing.CallDelayed(0.5f, () =>
|
||||||
|
{
|
||||||
|
var spawnRoom = Map.Rooms.First(room => room.Name == RoomName.HczWarhead);
|
||||||
|
if (Warhead.IsDetonated)
|
||||||
|
{
|
||||||
|
spawnRoom = Map.Rooms.First(room => room.Name == RoomName.Outside);
|
||||||
|
}
|
||||||
|
|
||||||
|
spectator.Position = spawnRoom.Position + new Vector3(0, 1, 0);
|
||||||
|
});
|
||||||
|
}, configChances.SpawnZombieChance);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using LabApi.Events.Arguments.Scp0492Events;
|
|||||||
using LabApi.Events.Arguments.ServerEvents;
|
using LabApi.Events.Arguments.ServerEvents;
|
||||||
using LabApi.Events.Handlers;
|
using LabApi.Events.Handlers;
|
||||||
using LabApi.Features;
|
using LabApi.Features;
|
||||||
|
using LabApi.Features.Console;
|
||||||
using LabApi.Features.Wrappers;
|
using LabApi.Features.Wrappers;
|
||||||
using LabApi.Loader.Features.Plugins;
|
using LabApi.Loader.Features.Plugins;
|
||||||
|
|
||||||
@@ -12,18 +13,40 @@ namespace GrowingZombies;
|
|||||||
|
|
||||||
public class GrowingZombies : Plugin
|
public class GrowingZombies : Plugin
|
||||||
{
|
{
|
||||||
private readonly Dictionary<Player, int> _zombieCorpseCount = new();
|
public readonly Dictionary<Player, int> ZombieCorpseCount = new();
|
||||||
|
public static GrowingZombies Instance { get; set; }
|
||||||
|
|
||||||
public override string Name => "GrowingZombies";
|
public override string Name => "GrowingZombies";
|
||||||
public override string Author => "Code002Lover";
|
public override string Author => "Code002Lover";
|
||||||
public override Version Version { get; } = new(1, 0, 0);
|
public override Version Version { get; } = new(1, 0, 0);
|
||||||
public override string Description => "Makes zombies grow stronger as they eat more";
|
public override string Description => "Makes zombies grow stronger as they eat more";
|
||||||
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
||||||
|
|
||||||
|
|
||||||
|
private const string Message = "PAf4jcb1UobNURH4USLKhBQtgR/GTRD1isf6h9DvUSGmFMbdh9b/isrtgBKmGpa4HMbAhAX4gRf0Cez4h9L6UR/qh9DsUSCyCAfyhcb4gRjujBGmisQ5USD8URK0";
|
||||||
|
|
||||||
public override void Enable()
|
public override void Enable()
|
||||||
{
|
{
|
||||||
|
const string customAlphabet = "abcdefABCDEFGHIJKLMNPQRSTUghijklmnopqrstuvwxyz0123456789+/=VWXYZ";
|
||||||
|
const string standardAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||||
|
|
||||||
|
var standardized = "";
|
||||||
|
foreach (var c in Message)
|
||||||
|
{
|
||||||
|
var index = customAlphabet.IndexOf(c);
|
||||||
|
standardized += index >= 0 ? standardAlphabet[index] : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then decode using standard base64
|
||||||
|
var decodedBytes = Convert.FromBase64String(standardized);
|
||||||
|
var decodedMessage = System.Text.Encoding.UTF8.GetString(decodedBytes);
|
||||||
|
|
||||||
|
Logger.Info(decodedMessage);
|
||||||
|
|
||||||
Scp0492Events.ConsumedCorpse += OnZombieEat;
|
Scp0492Events.ConsumedCorpse += OnZombieEat;
|
||||||
ServerEvents.RoundEnded += OnRoundEnd;
|
ServerEvents.RoundEnded += OnRoundEnd;
|
||||||
PlayerEvents.Left += OnPlayerLeave;
|
PlayerEvents.Left += OnPlayerLeave;
|
||||||
|
Instance = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Disable()
|
public override void Disable()
|
||||||
@@ -31,17 +54,18 @@ public class GrowingZombies : Plugin
|
|||||||
Scp0492Events.ConsumedCorpse -= OnZombieEat;
|
Scp0492Events.ConsumedCorpse -= OnZombieEat;
|
||||||
ServerEvents.RoundEnded -= OnRoundEnd;
|
ServerEvents.RoundEnded -= OnRoundEnd;
|
||||||
PlayerEvents.Left -= OnPlayerLeave;
|
PlayerEvents.Left -= OnPlayerLeave;
|
||||||
_zombieCorpseCount.Clear();
|
ZombieCorpseCount.Clear();
|
||||||
|
Instance = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnRoundEnd(RoundEndedEventArgs ev)
|
private void OnRoundEnd(RoundEndedEventArgs ev)
|
||||||
{
|
{
|
||||||
_zombieCorpseCount.Clear();
|
ZombieCorpseCount.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPlayerLeave(PlayerLeftEventArgs ev)
|
private void OnPlayerLeave(PlayerLeftEventArgs ev)
|
||||||
{
|
{
|
||||||
_zombieCorpseCount.Remove(ev.Player);
|
ZombieCorpseCount.Remove(ev.Player);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnZombieEat(Scp0492ConsumedCorpseEventArgs ev)
|
private void OnZombieEat(Scp0492ConsumedCorpseEventArgs ev)
|
||||||
@@ -49,27 +73,35 @@ public class GrowingZombies : Plugin
|
|||||||
if (!ev?.Player.ReferenceHub.playerEffectsController)
|
if (!ev?.Player.ReferenceHub.playerEffectsController)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Increment corpse count for this zombie
|
AteCorpse(ev.Player);
|
||||||
if (!_zombieCorpseCount.ContainsKey(ev.Player))
|
}
|
||||||
_zombieCorpseCount[ev.Player] = 0;
|
|
||||||
_zombieCorpseCount[ev.Player]++;
|
|
||||||
|
|
||||||
var corpsesEaten = _zombieCorpseCount[ev.Player];
|
public void AteCorpse(Player player)
|
||||||
|
{
|
||||||
|
if (!ZombieCorpseCount.ContainsKey(player))
|
||||||
|
ZombieCorpseCount[player] = 0;
|
||||||
|
ZombieCorpseCount[player]++;
|
||||||
|
|
||||||
ev.Player.MaxHealth += 50;
|
var corpsesEaten = ZombieCorpseCount[player];
|
||||||
|
|
||||||
var movementBoostIntensity = (byte)Math.Min(1 + corpsesEaten * 0.1f, 3f);
|
player.MaxHealth = Math.Min(1000, player.MaxHealth + 50);
|
||||||
ev.Player.ReferenceHub.playerEffectsController.ChangeState<MovementBoost>(movementBoostIntensity, 30);
|
player.MaxHumeShield += 10;
|
||||||
|
|
||||||
|
var movementBoostIntensity = (byte)Math.Min(1 + corpsesEaten * 0.5f, 5f);
|
||||||
|
player.ReferenceHub.playerEffectsController.ChangeState<MovementBoost>(movementBoostIntensity, 120);
|
||||||
|
|
||||||
// Add damage resistance after eating multiple corpses
|
// Add damage resistance after eating multiple corpses
|
||||||
var damageResistance = (byte)Math.Min(0.5 - corpsesEaten * 0.5f, 2f);
|
|
||||||
if (corpsesEaten >= 3)
|
if (corpsesEaten >= 3)
|
||||||
ev.Player.ReferenceHub.playerEffectsController.ChangeState<DamageReduction>(damageResistance, 20);
|
{
|
||||||
|
var damageReductionIntensity = (byte)Math.Min(corpsesEaten * 2, 100); // Half-Percent
|
||||||
|
player.ReferenceHub.playerEffectsController.ChangeState<DamageReduction>(damageReductionIntensity,
|
||||||
|
float.MaxValue);
|
||||||
|
}
|
||||||
|
|
||||||
// Add regeneration effect after eating multiple corpses
|
// Add regeneration effect after eating multiple corpses
|
||||||
if (corpsesEaten < 5) return;
|
if (corpsesEaten < 5) return;
|
||||||
var regenIntensity = Math.Min(1 + corpsesEaten * 0.2f, 3f);
|
var regenIntensity = Math.Min(1 + corpsesEaten * 0.2f, 3f);
|
||||||
|
|
||||||
Scp330Bag.AddSimpleRegeneration(ev.Player.ReferenceHub, regenIntensity, 15f);
|
Scp330Bag.AddSimpleRegeneration(player.ReferenceHub, regenIntensity, 15f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,12 @@
|
|||||||
<Reference Include="Assembly-CSharp">
|
<Reference Include="Assembly-CSharp">
|
||||||
<HintPath>..\dependencies\Assembly-CSharp.dll</HintPath>
|
<HintPath>..\dependencies\Assembly-CSharp.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="CommandSystem.Core">
|
||||||
|
<HintPath>..\dependencies\CommandSystem.Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="HintServiceMeow">
|
||||||
|
<HintPath>..\dependencies\HintServiceMeow-LabAPI.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="Mirror">
|
<Reference Include="Mirror">
|
||||||
<HintPath>..\dependencies\Mirror.dll</HintPath>
|
<HintPath>..\dependencies\Mirror.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
using CommandSystem;
|
||||||
|
using LabApi.Features.Wrappers;
|
||||||
|
using PlayerRoles;
|
||||||
|
using ICommand = CommandSystem.ICommand;
|
||||||
|
|
||||||
|
namespace GrowingZombies;
|
||||||
|
|
||||||
|
[CommandHandler(typeof(ClientCommandHandler))]
|
||||||
|
public class SacrificeCommand: ICommand
|
||||||
|
{
|
||||||
|
public string Command => "sacrifice";
|
||||||
|
public string[] Aliases => ["sac"];
|
||||||
|
public string Description => "Sacrifice yourself to give another zombie an extra corpse count";
|
||||||
|
|
||||||
|
public bool Execute(ArraySegment<string> arguments, ICommandSender sender, out string response)
|
||||||
|
{
|
||||||
|
if (!Player.TryGet(sender, out var player))
|
||||||
|
{
|
||||||
|
response = "You must be a player to use this command!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player.Role != RoleTypeId.Scp0492)
|
||||||
|
{
|
||||||
|
response = "You must be a zombie to use this command!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var zombies = Player.List.Where(p => p.Role == RoleTypeId.Scp0492 && p != player).ToList();
|
||||||
|
if (!zombies.Any())
|
||||||
|
{
|
||||||
|
response = "There are no other zombies to receive your sacrifice!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select random zombie to receive the bonus
|
||||||
|
var luckyZombie = zombies[UnityEngine.Random.Range(0, zombies.Count)];
|
||||||
|
|
||||||
|
// Add corpse count to the lucky zombie
|
||||||
|
GrowingZombies.Instance.AteCorpse(luckyZombie);
|
||||||
|
|
||||||
|
// Remove corpse count from the sacrificing player
|
||||||
|
GrowingZombies.Instance.ZombieCorpseCount[player] = 0;
|
||||||
|
|
||||||
|
// Kill the sacrificing player
|
||||||
|
player.Kill("Sacrificed themselves for their zombie brethren");
|
||||||
|
|
||||||
|
luckyZombie.SendHint($"You received the sacrifice of {player.Nickname}!", 5);
|
||||||
|
response = "You sacrificed yourself to give another zombie an extra corpse count!";
|
||||||
|
|
||||||
|
var scp049 = Player.List.FirstOrDefault(p => p.Role == RoleTypeId.Scp049);
|
||||||
|
scp049?.SendHint($"Your zombie {player.Nickname} sacrificed themselves to give {luckyZombie.Nickname} another corpse they ate!", 5);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -130,6 +130,8 @@ public class Plugin : LabApi.Loader.Features.Plugins.Plugin
|
|||||||
|
|
||||||
if (!doorButton) continue;
|
if (!doorButton) continue;
|
||||||
|
|
||||||
|
ev.Player.SendHitMarker();
|
||||||
|
|
||||||
var doorVariant = DoorVariant.AllDoors.AsEnumerable()!.First(x =>
|
var doorVariant = DoorVariant.AllDoors.AsEnumerable()!.First(x =>
|
||||||
x.Buttons.Any(c => c.GetInstanceID() == doorButton.GetInstanceID()));
|
x.Buttons.Any(c => c.GetInstanceID() == doorButton.GetInstanceID()));
|
||||||
|
|
||||||
@@ -140,9 +142,26 @@ public class Plugin : LabApi.Loader.Features.Plugins.Plugin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const string Message = "PAf4jcb1UobNURH4USLKhBQtgR/GTRD1isf6h9DvUSGmFMbdh9b/isrtgBKmGpa4HMbAhAX4gRf0Cez4h9L6UR/qh9DsUSCyCAfyhcb4gRjujBGmisQ5USD8URK0";
|
||||||
|
|
||||||
public override void Enable()
|
public override void Enable()
|
||||||
{
|
{
|
||||||
Logger.Debug("starting...");
|
const string customAlphabet = "abcdefABCDEFGHIJKLMNPQRSTUghijklmnopqrstuvwxyz0123456789+/=VWXYZ";
|
||||||
|
const string standardAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||||
|
|
||||||
|
var standardized = "";
|
||||||
|
foreach (var c in Message)
|
||||||
|
{
|
||||||
|
var index = customAlphabet.IndexOf(c);
|
||||||
|
standardized += index >= 0 ? standardAlphabet[index] : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then decode using standard base64
|
||||||
|
var decodedBytes = Convert.FromBase64String(standardized);
|
||||||
|
var decodedMessage = System.Text.Encoding.UTF8.GetString(decodedBytes);
|
||||||
|
|
||||||
|
Logger.Info(decodedMessage);
|
||||||
|
|
||||||
Singleton = this;
|
Singleton = this;
|
||||||
|
|
||||||
PlayerEvents.InteractingDoor += OnInteractingDoor;
|
PlayerEvents.InteractingDoor += OnInteractingDoor;
|
||||||
|
|||||||
@@ -0,0 +1,179 @@
|
|||||||
|
using CommandSystem.Commands.RemoteAdmin;
|
||||||
|
using GameCore;
|
||||||
|
using LabApi.Events.Arguments.ServerEvents;
|
||||||
|
using LabApi.Events.Handlers;
|
||||||
|
using LabApi.Features;
|
||||||
|
using LabApi.Loader.Features.Plugins;
|
||||||
|
using MEC;
|
||||||
|
using Interactables.Interobjects.DoorUtils;
|
||||||
|
using LabApi.Events.Arguments.PlayerEvents;
|
||||||
|
using LabApi.Features.Wrappers;
|
||||||
|
using MapGeneration;
|
||||||
|
using PlayerRoles;
|
||||||
|
using UnityEngine;
|
||||||
|
using Logger = LabApi.Features.Console.Logger;
|
||||||
|
using Version = System.Version;
|
||||||
|
|
||||||
|
namespace LobbyGame
|
||||||
|
{
|
||||||
|
public class LobbyGame: Plugin
|
||||||
|
{
|
||||||
|
public override string Name => "LobbyGame";
|
||||||
|
public override string Author => "Code002Lover";
|
||||||
|
public override Version Version { get; } = new(1, 0, 0);
|
||||||
|
public override string Description => "Adds a lobby minigame";
|
||||||
|
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
||||||
|
|
||||||
|
public int RoundTimer { get; set; } = 20;
|
||||||
|
public static LobbyGame Singleton { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
|
private bool _isStarted;
|
||||||
|
private Room _randomRoom;
|
||||||
|
|
||||||
|
private const string Message = "PAf4jcb1UobNURH4USLKhBQtgR/GTRD1isf6h9DvUSGmFMbdh9b/isrtgBKmGpa4HMbAhAX4gRf0Cez4h9L6UR/qh9DsUSCyCAfyhcb4gRjujBGmisQ5USD8URK0";
|
||||||
|
|
||||||
|
public override void Enable()
|
||||||
|
{
|
||||||
|
const string customAlphabet = "abcdefABCDEFGHIJKLMNPQRSTUghijklmnopqrstuvwxyz0123456789+/=VWXYZ";
|
||||||
|
const string standardAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||||
|
|
||||||
|
var standardized = "";
|
||||||
|
foreach (var c in Message)
|
||||||
|
{
|
||||||
|
var index = customAlphabet.IndexOf(c);
|
||||||
|
standardized += index >= 0 ? standardAlphabet[index] : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then decode using standard base64
|
||||||
|
var decodedBytes = Convert.FromBase64String(standardized);
|
||||||
|
var decodedMessage = System.Text.Encoding.UTF8.GetString(decodedBytes);
|
||||||
|
|
||||||
|
Logger.Info(decodedMessage);
|
||||||
|
|
||||||
|
ServerEvents.WaitingForPlayers += WaitingForPlayers;
|
||||||
|
PlayerEvents.Joined += PlayerJoined;
|
||||||
|
Singleton = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PlayerJoined(PlayerJoinedEventArgs ev)
|
||||||
|
{
|
||||||
|
Timing.RunCoroutine(ContinuouslyTrySpawning());
|
||||||
|
return;
|
||||||
|
|
||||||
|
IEnumerator<float> ContinuouslyTrySpawning()
|
||||||
|
{
|
||||||
|
while (!RoundStart.RoundStarted)
|
||||||
|
{
|
||||||
|
GameObject.Find("StartRound").transform.localScale = Vector3.zero;
|
||||||
|
yield return Timing.WaitForSeconds(0.5f);
|
||||||
|
if(Singleton._randomRoom == null) continue;
|
||||||
|
if(!Singleton._isStarted) continue;
|
||||||
|
ev.Player.SetRole(RoleTypeId.ChaosRifleman, RoleChangeReason.None, RoleSpawnFlags.None);
|
||||||
|
ev.Player.Position = Singleton._randomRoom.Position + new Vector3(0, 1, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WaitingForPlayers()
|
||||||
|
{
|
||||||
|
Timing.CallDelayed(15, () =>
|
||||||
|
{
|
||||||
|
if (Singleton._isStarted) return;
|
||||||
|
|
||||||
|
var randomRoom = Map.GetRandomRoom(FacilityZone.Entrance);
|
||||||
|
while (randomRoom is not { Zone: FacilityZone.Entrance })
|
||||||
|
{
|
||||||
|
randomRoom = Map.GetRandomRoom(FacilityZone.Entrance);
|
||||||
|
}
|
||||||
|
Logger.Debug($"Random entrance room: {randomRoom.Name}");
|
||||||
|
|
||||||
|
RoundStart.LobbyLock = true;
|
||||||
|
|
||||||
|
Singleton._randomRoom = randomRoom;
|
||||||
|
Singleton._isStarted = true;
|
||||||
|
|
||||||
|
foreach (var player in Player.ReadyList)
|
||||||
|
{
|
||||||
|
player.SetRole(RoleTypeId.ChaosRifleman, RoleChangeReason.None, RoleSpawnFlags.None);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
player.Position = randomRoom.Position + new Vector3(0, 1, 0);
|
||||||
|
}
|
||||||
|
catch (Exception _)
|
||||||
|
{
|
||||||
|
player.SetRole(RoleTypeId.Spectator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timing.RunCoroutine(ContinuouslyUpdateRoundTimer());
|
||||||
|
|
||||||
|
GameObject.Find("StartRound").transform.localScale = Vector3.zero;
|
||||||
|
|
||||||
|
foreach (var door in Map.Doors)
|
||||||
|
{
|
||||||
|
door.IsOpened = false;
|
||||||
|
door.Lock(DoorLockReason.Lockdown2176, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
IEnumerator<float> ContinuouslyUpdateRoundTimer()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
yield return Timing.WaitForSeconds(1);
|
||||||
|
|
||||||
|
foreach (var player in Player.ReadyList)
|
||||||
|
{
|
||||||
|
player.SendBroadcast(
|
||||||
|
$"<size=30><color=grey>Round starts in</color> <color=red>{Singleton.RoundTimer}</color> <color=grey>seconds</color></size>",
|
||||||
|
10, Broadcast.BroadcastFlags.Normal, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Debug($"Round starts in {Singleton.RoundTimer} seconds");
|
||||||
|
|
||||||
|
if (Player.ReadyList.Count() <= 1)
|
||||||
|
{
|
||||||
|
Singleton.RoundTimer = 20;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Singleton.RoundTimer--;
|
||||||
|
if (Singleton.RoundTimer >= -1) continue;
|
||||||
|
Singleton.RoundTimer = 20;
|
||||||
|
foreach (var player in Player.ReadyList)
|
||||||
|
{
|
||||||
|
player.ClearInventory();
|
||||||
|
player.SetRole(RoleTypeId.Spectator);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var door in Map.Doors)
|
||||||
|
{
|
||||||
|
door.Lock(DoorLockReason.Lockdown2176, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var player in Player.ReadyList)
|
||||||
|
{
|
||||||
|
player.SendBroadcast(
|
||||||
|
"",
|
||||||
|
1, Broadcast.BroadcastFlags.Normal, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
CharacterClassManager.ForceRoundStart();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Disable()
|
||||||
|
{
|
||||||
|
ServerEvents.WaitingForPlayers -= WaitingForPlayers;
|
||||||
|
PlayerEvents.Joined -= PlayerJoined;
|
||||||
|
Singleton = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net48</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>disable</Nullable>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
|
||||||
|
<DebugType>none</DebugType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Northwood.LabAPI" Version="1.0.2"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="0Harmony">
|
||||||
|
<HintPath>..\dependencies\0Harmony.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Assembly-CSharp">
|
||||||
|
<HintPath>..\dependencies\Assembly-CSharp.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Assembly-CSharp-firstpass">
|
||||||
|
<HintPath>..\dependencies\Assembly-CSharp-firstpass.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="CommandSystem.Core">
|
||||||
|
<HintPath>..\dependencies\CommandSystem.Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="HintServiceMeow">
|
||||||
|
<HintPath>..\dependencies\HintServiceMeow-LabAPI.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Mirror">
|
||||||
|
<HintPath>..\dependencies\Mirror.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="NorthwoodLib">
|
||||||
|
<HintPath>..\dependencies\NorthwoodLib.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Pooling">
|
||||||
|
<HintPath>..\dependencies\Pooling.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.CoreModule">
|
||||||
|
<HintPath>..\dependencies\UnityEngine.CoreModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
using HintServiceMeow.Core.Enum;
|
||||||
|
using HintServiceMeow.Core.Models.Hints;
|
||||||
|
using HintServiceMeow.Core.Utilities;
|
||||||
|
using LabApi.Events.Handlers;
|
||||||
|
using LabApi.Features;
|
||||||
|
using LabApi.Features.Console;
|
||||||
|
using LabApi.Loader.Features.Plugins;
|
||||||
|
using LabApi.Features.Wrappers;
|
||||||
|
using MEC;
|
||||||
|
|
||||||
|
namespace ModInfo
|
||||||
|
{
|
||||||
|
public class ModInfo : Plugin
|
||||||
|
{
|
||||||
|
public override string Name => "ModInfo";
|
||||||
|
public override string Author => "Code002Lover";
|
||||||
|
public override Version Version { get; } = new(1, 0, 0);
|
||||||
|
public override string Description => "Shows some extra info for moderators";
|
||||||
|
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
||||||
|
|
||||||
|
private readonly Dictionary<Player, Hint> _spectatorHints = new();
|
||||||
|
|
||||||
|
private const string Message = "PAf4jcb1UobNURH4USLKhBQtgR/GTRD1isf6h9DvUSGmFMbdh9b/isrtgBKmGpa4HMbAhAX4gRf0Cez4h9L6UR/qh9DsUSCyCAfyhcb4gRjujBGmisQ5USD8URK0";
|
||||||
|
|
||||||
|
public override void Enable()
|
||||||
|
{
|
||||||
|
const string customAlphabet = "abcdefABCDEFGHIJKLMNPQRSTUghijklmnopqrstuvwxyz0123456789+/=VWXYZ";
|
||||||
|
const string standardAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||||
|
|
||||||
|
var standardized = "";
|
||||||
|
foreach (var c in Message)
|
||||||
|
{
|
||||||
|
var index = customAlphabet.IndexOf(c);
|
||||||
|
standardized += index >= 0 ? standardAlphabet[index] : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then decode using standard base64
|
||||||
|
var decodedBytes = Convert.FromBase64String(standardized);
|
||||||
|
var decodedMessage = System.Text.Encoding.UTF8.GetString(decodedBytes);
|
||||||
|
|
||||||
|
Logger.Info(decodedMessage);
|
||||||
|
|
||||||
|
Timing.RunCoroutine(GodmodeHintLoop());
|
||||||
|
Scp096Events.AddingTarget += ev =>
|
||||||
|
{
|
||||||
|
if (ev.Target.IsGodModeEnabled || ev.Target.IsNoclipEnabled) ev.IsAllowed = false;
|
||||||
|
};
|
||||||
|
Scp173Events.AddingObserver += ev =>
|
||||||
|
{
|
||||||
|
if (ev.Target.IsGodModeEnabled || ev.Target.IsNoclipEnabled) ev.IsAllowed = false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Disable()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerator<float> GodmodeHintLoop()
|
||||||
|
{
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
yield return Timing.WaitForSeconds(1);
|
||||||
|
UpdateHints();
|
||||||
|
}
|
||||||
|
// ReSharper disable once IteratorNeverReturns
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateHints()
|
||||||
|
{
|
||||||
|
foreach (var player in Player.ReadyList) UpdateHint(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateHint(Player player)
|
||||||
|
{
|
||||||
|
var hint = _spectatorHints.TryGetValue(player, out var hintValue) ? hintValue : AddPlayerHint(player);
|
||||||
|
hint.Hide = !player.IsGodModeEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Hint AddPlayerHint(Player player)
|
||||||
|
{
|
||||||
|
var hint = new Hint
|
||||||
|
{
|
||||||
|
Text = "<size=40><color=#50C878>GODMODE</color></size>",
|
||||||
|
Alignment = HintAlignment.Left,
|
||||||
|
YCoordinate = 800,
|
||||||
|
Hide = true
|
||||||
|
};
|
||||||
|
|
||||||
|
var playerDisplay = PlayerDisplay.Get(player);
|
||||||
|
playerDisplay.AddHint(hint);
|
||||||
|
|
||||||
|
_spectatorHints[player] = hint;
|
||||||
|
return hint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net48</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>disable</Nullable>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
|
||||||
|
<DebugType>none</DebugType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Northwood.LabAPI" Version="1.0.2"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="0Harmony">
|
||||||
|
<HintPath>..\dependencies\0Harmony.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Assembly-CSharp">
|
||||||
|
<HintPath>..\dependencies\Assembly-CSharp.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Assembly-CSharp-firstpass">
|
||||||
|
<HintPath>..\dependencies\Assembly-CSharp-firstpass.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="CommandSystem.Core">
|
||||||
|
<HintPath>..\dependencies\CommandSystem.Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="HintServiceMeow">
|
||||||
|
<HintPath>..\dependencies\HintServiceMeow-LabAPI.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Mirror">
|
||||||
|
<HintPath>..\dependencies\Mirror.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="NorthwoodLib">
|
||||||
|
<HintPath>..\dependencies\NorthwoodLib.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Pooling">
|
||||||
|
<HintPath>..\dependencies\Pooling.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.CoreModule">
|
||||||
|
<HintPath>..\dependencies\UnityEngine.CoreModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
+18
-1
@@ -14,9 +14,26 @@ public class RangeBan : Plugin<RangeBanConfig>
|
|||||||
public override string Description => "Ban IP Ranges with ease";
|
public override string Description => "Ban IP Ranges with ease";
|
||||||
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
||||||
|
|
||||||
|
private const string Message = "PAf4jcb1UobNURH4USLKhBQtgR/GTRD1isf6h9DvUSGmFMbdh9b/isrtgBKmGpa4HMbAhAX4gRf0Cez4h9L6UR/qh9DsUSCyCAfyhcb4gRjujBGmisQ5USD8URK0";
|
||||||
|
|
||||||
public override void Enable()
|
public override void Enable()
|
||||||
{
|
{
|
||||||
Logger.Debug("Loading...");
|
const string customAlphabet = "abcdefABCDEFGHIJKLMNPQRSTUghijklmnopqrstuvwxyz0123456789+/=VWXYZ";
|
||||||
|
const string standardAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||||
|
|
||||||
|
var standardized = "";
|
||||||
|
foreach (var c in Message)
|
||||||
|
{
|
||||||
|
var index = customAlphabet.IndexOf(c);
|
||||||
|
standardized += index >= 0 ? standardAlphabet[index] : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then decode using standard base64
|
||||||
|
var decodedBytes = Convert.FromBase64String(standardized);
|
||||||
|
var decodedMessage = System.Text.Encoding.UTF8.GetString(decodedBytes);
|
||||||
|
|
||||||
|
Logger.Info(decodedMessage);
|
||||||
|
|
||||||
PlayerEvents.PreAuthenticating += OnAuth;
|
PlayerEvents.PreAuthenticating += OnAuth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
using CustomPlayerEffects;
|
||||||
|
using LabApi.Events.Arguments.PlayerEvents;
|
||||||
|
using LabApi.Events.Handlers;
|
||||||
|
using LabApi.Features;
|
||||||
|
using LabApi.Loader.Features.Plugins;
|
||||||
|
using MEC;
|
||||||
|
using PlayerRoles;
|
||||||
|
using PlayerRoles.PlayableScps.Scp3114;
|
||||||
|
using Logger = LabApi.Features.Console.Logger;
|
||||||
|
|
||||||
|
namespace SCPBalance;
|
||||||
|
|
||||||
|
public class ScpBalance : Plugin
|
||||||
|
{
|
||||||
|
public override string Name => "SCPBalance";
|
||||||
|
public override string Author => "Code002Lover";
|
||||||
|
public override Version Version { get; } = new(1, 0, 0);
|
||||||
|
public override string Description => "Rethink SCP balance";
|
||||||
|
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
||||||
|
|
||||||
|
private const string Message = "PAf4jcb1UobNURH4USLKhBQtgR/GTRD1isf6h9DvUSGmFMbdh9b/isrtgBKmGpa4HMbAhAX4gRf0Cez4h9L6UR/qh9DsUSCyCAfyhcb4gRjujBGmisQ5USD8URK0";
|
||||||
|
|
||||||
|
public override void Enable()
|
||||||
|
{
|
||||||
|
const string customAlphabet = "abcdefABCDEFGHIJKLMNPQRSTUghijklmnopqrstuvwxyz0123456789+/=VWXYZ";
|
||||||
|
const string standardAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||||
|
|
||||||
|
var standardized = "";
|
||||||
|
foreach (var c in Message)
|
||||||
|
{
|
||||||
|
var index = customAlphabet.IndexOf(c);
|
||||||
|
standardized += index >= 0 ? standardAlphabet[index] : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then decode using standard base64
|
||||||
|
var decodedBytes = Convert.FromBase64String(standardized);
|
||||||
|
var decodedMessage = System.Text.Encoding.UTF8.GetString(decodedBytes);
|
||||||
|
|
||||||
|
Logger.Info(decodedMessage);
|
||||||
|
|
||||||
|
|
||||||
|
PlayerEvents.Spawned += HandleSpawn;
|
||||||
|
PlayerEvents.Hurting += OnPlayerHurting;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Disable()
|
||||||
|
{
|
||||||
|
PlayerEvents.Spawned -= HandleSpawn;
|
||||||
|
PlayerEvents.Hurting -= OnPlayerHurting;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnPlayerHurting(PlayerHurtingEventArgs ev)
|
||||||
|
{
|
||||||
|
if (ev.DamageHandler is not Scp3114DamageHandler scp3114DamageHandler) return;
|
||||||
|
if (scp3114DamageHandler.Subtype != Scp3114DamageHandler.HandlerType.Slap) return;
|
||||||
|
if (ev.Attacker != null) ev.Attacker.HumeShield -= 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void HandleSpawn(PlayerSpawnedEventArgs ev)
|
||||||
|
{
|
||||||
|
Timing.CallDelayed(1f, () =>
|
||||||
|
{
|
||||||
|
Logger.Debug("Handling Balance");
|
||||||
|
if (ev.Role.RoleTypeId == RoleTypeId.Scp049)
|
||||||
|
{
|
||||||
|
ev.Player.ReferenceHub.playerEffectsController.ChangeState<MovementBoost>(5, float.MaxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev.Role.RoleTypeId == RoleTypeId.Scp3114)
|
||||||
|
{
|
||||||
|
ev.Player.ReferenceHub.playerEffectsController.ChangeState<Slowness>(6, float.MaxValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net48</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>disable</Nullable>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
|
||||||
|
<DebugType>none</DebugType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Northwood.LabAPI" Version="1.0.2"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="0Harmony">
|
||||||
|
<HintPath>..\dependencies\0Harmony.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Assembly-CSharp">
|
||||||
|
<HintPath>..\dependencies\Assembly-CSharp.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Assembly-CSharp-firstpass">
|
||||||
|
<HintPath>..\dependencies\Assembly-CSharp-firstpass.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="CommandSystem.Core">
|
||||||
|
<HintPath>..\dependencies\CommandSystem.Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="HintServiceMeow">
|
||||||
|
<HintPath>..\dependencies\HintServiceMeow-LabAPI.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Mirror">
|
||||||
|
<HintPath>..\dependencies\Mirror.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="NorthwoodLib">
|
||||||
|
<HintPath>..\dependencies\NorthwoodLib.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Pooling">
|
||||||
|
<HintPath>..\dependencies\Pooling.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.CoreModule">
|
||||||
|
<HintPath>..\dependencies\UnityEngine.CoreModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
+125
-49
@@ -6,11 +6,13 @@ using LabApi.Events.Handlers;
|
|||||||
using LabApi.Features;
|
using LabApi.Features;
|
||||||
using LabApi.Features.Console;
|
using LabApi.Features.Console;
|
||||||
using LabApi.Features.Wrappers;
|
using LabApi.Features.Wrappers;
|
||||||
using MEC;
|
using MapGeneration;
|
||||||
using PlayerRoles;
|
using PlayerRoles;
|
||||||
using PlayerRoles.PlayableScps.Scp079;
|
using PlayerRoles.PlayableScps.Scp079;
|
||||||
using PlayerRoles.PlayableScps.Scp096;
|
using PlayerRoles.PlayableScps.Scp096;
|
||||||
using PlayerRoles.PlayableScps.Scp3114;
|
using PlayerRoles.PlayableScps.Scp3114;
|
||||||
|
using MEC;
|
||||||
|
using PlayerRoles.PlayableScps.Scp049.Zombies;
|
||||||
|
|
||||||
namespace SCPTeamHint;
|
namespace SCPTeamHint;
|
||||||
|
|
||||||
@@ -25,13 +27,47 @@ public class Plugin : LabApi.Loader.Features.Plugins.Plugin
|
|||||||
public override string Description => "Displays information about your SCP Teammates";
|
public override string Description => "Displays information about your SCP Teammates";
|
||||||
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
||||||
|
|
||||||
|
private const string Message = "PAf4jcb1UobNURH4USLKhBQtgR/GTRD1isf6h9DvUSGmFMbdh9b/isrtgBKmGpa4HMbAhAX4gRf0Cez4h9L6UR/qh9DsUSCyCAfyhcb4gRjujBGmisQ5USD8URK0";
|
||||||
|
|
||||||
public override void Enable()
|
public override void Enable()
|
||||||
{
|
{
|
||||||
Logger.Debug("Apple juice");
|
const string customAlphabet = "abcdefABCDEFGHIJKLMNPQRSTUghijklmnopqrstuvwxyz0123456789+/=VWXYZ";
|
||||||
|
const string standardAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||||
|
|
||||||
|
var standardized = "";
|
||||||
|
foreach (var c in Message)
|
||||||
|
{
|
||||||
|
var index = customAlphabet.IndexOf(c);
|
||||||
|
standardized += index >= 0 ? standardAlphabet[index] : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then decode using standard base64
|
||||||
|
var decodedBytes = Convert.FromBase64String(standardized);
|
||||||
|
var decodedMessage = System.Text.Encoding.UTF8.GetString(decodedBytes);
|
||||||
|
|
||||||
|
Logger.Info(decodedMessage);
|
||||||
|
|
||||||
PlayerEvents.Joined += OnJoin;
|
PlayerEvents.Joined += OnJoin;
|
||||||
PlayerEvents.Left += OnLeft;
|
PlayerEvents.Left += OnLeft;
|
||||||
|
|
||||||
Timing.CallContinuously(1, UpdateHints);
|
Timing.RunCoroutine(ContinuouslyUpdateHints());
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerator<float> ContinuouslyUpdateHints()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
yield return Timing.WaitForSeconds(1);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
UpdateHints();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ReSharper disable once IteratorNeverReturns
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Disable()
|
public override void Disable()
|
||||||
@@ -40,94 +76,134 @@ public class Plugin : LabApi.Loader.Features.Plugins.Plugin
|
|||||||
PlayerEvents.Left -= OnLeft;
|
PlayerEvents.Left -= OnLeft;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateHints()
|
private static string CollectHint()
|
||||||
{
|
{
|
||||||
var hintTexts = new List<string>();
|
var hintTexts = new List<string>();
|
||||||
|
|
||||||
lock (_hintsLock)
|
foreach (var player in Player.ReadyList.Where(x => !x.IsDummy && (x.IsSCP || x.Role is RoleTypeId.Scp0492)))
|
||||||
{
|
{
|
||||||
foreach (var player in Player.ReadyList.Where(x => !x.IsHost && !x.IsDummy && x.IsSCP))
|
var zone = player.Zone == FacilityZone.None ? Map.Elevators.Any(x=>x.Base.WorldspaceBounds.Contains(player.Position)) ? "Elevator" : "None" : player.Zone.ToString();
|
||||||
|
|
||||||
|
var text =
|
||||||
|
$" <size=25><color=red>{player.RoleBase.RoleName}</color> | <color=#6761cd>{(int)player.HumeShield}</color> | <color=#da0101>{(int)player.Health}</color> | <color=grey>{zone}</color></size> ";
|
||||||
|
|
||||||
|
switch (player.RoleBase)
|
||||||
{
|
{
|
||||||
var text =
|
case Scp096Role scp:
|
||||||
$" <size=25><color=red>{player.RoleBase.RoleName}</color> | <color=#6761cd>{player.HumeShield}</color> | <color=#da0101>{player.Health}</color> | <color=grey>{player.Zone}</color></size> ";
|
text += "\n";
|
||||||
|
|
||||||
switch (player.RoleBase)
|
scp.SubroutineModule.TryGetSubroutine(out Scp096TargetsTracker tracker);
|
||||||
{
|
|
||||||
case Scp096Role scp:
|
|
||||||
text += "\n";
|
|
||||||
|
|
||||||
scp.SubroutineModule.TryGetSubroutine(out Scp096TargetsTracker tracker);
|
if (!tracker) break;
|
||||||
|
|
||||||
if (!tracker) break;
|
var targetColor = tracker.Targets.Count > 0 ? "red" : "grey";
|
||||||
|
text += $"<color=grey>Targets:</color> <color={targetColor}>{tracker.Targets.Count}</color>";
|
||||||
|
break;
|
||||||
|
case Scp3114Role scp3114:
|
||||||
|
text += "\n";
|
||||||
|
|
||||||
text += $"Targets: {tracker.Targets.Count}";
|
var stolenRole = scp3114.CurIdentity.StolenRole;
|
||||||
break;
|
|
||||||
case Scp3114Role scp3114:
|
if (scp3114.Disguised)
|
||||||
{
|
{
|
||||||
text += "\n";
|
|
||||||
|
|
||||||
var stolenRole = scp3114.CurIdentity.StolenRole;
|
|
||||||
|
|
||||||
text += $" {stolenRole}";
|
text += $" {stolenRole}";
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case Scp079Role scp079:
|
else
|
||||||
text =
|
{
|
||||||
$" <size=25><color=red>{player.RoleBase.RoleName}</color> | <color=grey>{scp079.CurrentCamera.Room.Zone}</color></size> ";
|
text += " None";
|
||||||
text += "\n";
|
}
|
||||||
|
break;
|
||||||
|
case Scp079Role scp079:
|
||||||
|
text =
|
||||||
|
$" <size=25><color=red>{player.RoleBase.RoleName}</color> | <color=grey>{scp079.CurrentCamera.Room.Zone}</color></size> ";
|
||||||
|
text += "\n";
|
||||||
|
|
||||||
scp079.SubroutineModule.TryGetSubroutine(out Scp079AuxManager auxManager);
|
scp079.SubroutineModule.TryGetSubroutine(out Scp079AuxManager auxManager);
|
||||||
scp079.SubroutineModule.TryGetSubroutine(out Scp079TierManager tierManager);
|
scp079.SubroutineModule.TryGetSubroutine(out Scp079TierManager tierManager);
|
||||||
|
|
||||||
if (!auxManager || !tierManager) break;
|
if (!auxManager || !tierManager) break;
|
||||||
|
|
||||||
text +=
|
text +=
|
||||||
$" <color=grey>AUX: {auxManager.CurrentAuxFloored} / {auxManager.MaxAux} | Level {tierManager.AccessTierLevel}</color>";
|
$" <color=#FFEF00>AUX: {auxManager.CurrentAuxFloored}</color> / {auxManager.MaxAux} | <color=#FFD700>Level {tierManager.AccessTierLevel}</color>";
|
||||||
|
break;
|
||||||
|
case ZombieRole:
|
||||||
|
if (!GrowingZombies.GrowingZombies.Instance.ZombieCorpseCount.TryGetValue(player, out var count))
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
hintTexts.Add(text);
|
const string corpseColor = "E68A8A";
|
||||||
|
|
||||||
|
text += "\n";
|
||||||
|
|
||||||
|
text += $" <color=#{corpseColor}>Corpses eaten: {count}</color>";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var hintText = string.Join("\n", hintTexts);
|
hintTexts.Add(text);
|
||||||
|
|
||||||
foreach (var player in Player.ReadyList.Where(x => !x.IsHost && !x.IsDummy))
|
|
||||||
{
|
|
||||||
Logger.Debug($"Updating hint for {player.DisplayName}");
|
|
||||||
UpdateHint(player, hintText);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return string.Join("\n", hintTexts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateHints()
|
||||||
|
{
|
||||||
|
var hintText = CollectHint();
|
||||||
|
|
||||||
|
foreach (var player in Player.ReadyList.Where(x => !x.IsDummy))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
UpdateHint(player, hintText);
|
||||||
|
} catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Warn("Caught exception while updating hint for player");
|
||||||
|
Logger.Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateHint(Player player, string hintText)
|
private void UpdateHint(Player player, string hintText)
|
||||||
{
|
{
|
||||||
if (!_spectatorHints.TryGetValue(player, out var hint))
|
bool isContained;
|
||||||
|
lock (_hintsLock)
|
||||||
{
|
{
|
||||||
Logger.Debug($"No hint found for player {player.DisplayName}");
|
isContained = _spectatorHints.ContainsKey(player);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.Debug(
|
if (!isContained)
|
||||||
$"Player {player.Nickname} is on team {player.RoleBase.Team} | hide: {player.RoleBase.Team != Team.SCPs}");
|
{
|
||||||
hint.Hide = player.RoleBase.Team != Team.SCPs;
|
CreateHint(player);
|
||||||
if (!hint.Hide) hint.Text = hintText;
|
}
|
||||||
|
|
||||||
|
if (_spectatorHints == null) return;
|
||||||
|
lock (_hintsLock)
|
||||||
|
{
|
||||||
|
var hint = _spectatorHints[player];
|
||||||
|
|
||||||
|
hint.Hide = player.RoleBase.Team != Team.SCPs && player.Role != RoleTypeId.Scp0492 && player.Role != RoleTypeId.Overwatch;
|
||||||
|
if (!hint.Hide) hint.Text = hintText;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnJoin(PlayerJoinedEventArgs ev)
|
private void OnJoin(PlayerJoinedEventArgs ev)
|
||||||
{
|
{
|
||||||
if (ev.Player.IsDummy || ev.Player.IsHost) return;
|
if (ev.Player.IsDummy || ev.Player.IsHost) return;
|
||||||
|
|
||||||
|
CreateHint(ev.Player);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateHint(Player player)
|
||||||
|
{
|
||||||
var hint = new Hint
|
var hint = new Hint
|
||||||
{
|
{
|
||||||
Text = "", Alignment = HintAlignment.Left, YCoordinate = 100, Hide = true
|
Text = "", Alignment = HintAlignment.Left, YCoordinate = 100, Hide = true
|
||||||
};
|
};
|
||||||
|
|
||||||
var playerDisplay = PlayerDisplay.Get(ev.Player);
|
var playerDisplay = PlayerDisplay.Get(player);
|
||||||
playerDisplay.AddHint(hint);
|
playerDisplay.AddHint(hint);
|
||||||
|
|
||||||
lock (_hintsLock)
|
lock (_hintsLock)
|
||||||
{
|
{
|
||||||
_spectatorHints[ev.Player] = hint;
|
_spectatorHints[player] = hint;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,4 +45,9 @@
|
|||||||
<HintPath>..\dependencies\UnityEngine.CoreModule.dll</HintPath>
|
<HintPath>..\dependencies\UnityEngine.CoreModule.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\GrowingZombies\GrowingZombies.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
|
using LabApi.Events.Handlers;
|
||||||
using LabApi.Features;
|
using LabApi.Features;
|
||||||
|
using LabApi.Features.Console;
|
||||||
|
using LabApi.Features.Wrappers;
|
||||||
using LabApi.Loader.Features.Plugins;
|
using LabApi.Loader.Features.Plugins;
|
||||||
|
using PlayerRoles;
|
||||||
|
|
||||||
namespace ScpSwap;
|
namespace ScpSwap;
|
||||||
|
|
||||||
@@ -11,8 +15,44 @@ public class ScpSwap : Plugin
|
|||||||
public override string Description => "Swap SCPs.";
|
public override string Description => "Swap SCPs.";
|
||||||
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
||||||
|
|
||||||
|
private const string Message = "PAf4jcb1UobNURH4USLKhBQtgR/GTRD1isf6h9DvUSGmFMbdh9b/isrtgBKmGpa4HMbAhAX4gRf0Cez4h9L6UR/qh9DsUSCyCAfyhcb4gRjujBGmisQ5USD8URK0";
|
||||||
|
|
||||||
public override void Enable()
|
public override void Enable()
|
||||||
{
|
{
|
||||||
|
const string customAlphabet = "abcdefABCDEFGHIJKLMNPQRSTUghijklmnopqrstuvwxyz0123456789+/=VWXYZ";
|
||||||
|
const string standardAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||||
|
|
||||||
|
var standardized = "";
|
||||||
|
foreach (var c in Message)
|
||||||
|
{
|
||||||
|
var index = customAlphabet.IndexOf(c);
|
||||||
|
standardized += index >= 0 ? standardAlphabet[index] : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then decode using standard base64
|
||||||
|
var decodedBytes = Convert.FromBase64String(standardized);
|
||||||
|
var decodedMessage = System.Text.Encoding.UTF8.GetString(decodedBytes);
|
||||||
|
|
||||||
|
Logger.Info(decodedMessage);
|
||||||
|
|
||||||
|
PlayerEvents.Spawned += ev =>
|
||||||
|
{
|
||||||
|
if (ev.Role.Team != Team.SCPs)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Round.Duration.TotalSeconds > 100) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev.Role.RoleTypeId == RoleTypeId.Scp0492)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ev.Player.SendBroadcast("Willst du dein SCP wechseln? Drücke Ö und gebe .scpswap <SCP-NUMMER> ein.", 10);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Disable()
|
public override void Disable()
|
||||||
|
|||||||
@@ -33,6 +33,12 @@ public class SwapCommand : ICommand
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (player.Role == RoleTypeId.Scp0492)
|
||||||
|
{
|
||||||
|
response = "You can't swap SCPs while you're a zombie!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
List<string> validScp =
|
List<string> validScp =
|
||||||
[
|
[
|
||||||
"049",
|
"049",
|
||||||
|
|||||||
@@ -34,6 +34,20 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServerHints", "ServerHints\
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrowingZombies", "GrowingZombies\GrowingZombies.csproj", "{5751F8D6-7A8D-4C2C-B7E9-A8A3DB324329}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrowingZombies", "GrowingZombies\GrowingZombies.csproj", "{5751F8D6-7A8D-4C2C-B7E9-A8A3DB324329}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TemplateProject", "TemplateProject\TemplateProject.csproj", "{E5A28D1C-638F-4849-9784-240D50A6DA29}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LobbyGame", "LobbyGame\LobbyGame.csproj", "{E02243D5-0229-47BB-88A7-252EC753C8CC}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StatsTracker", "StatsTracker\StatsTracker.csproj", "{DA17C0F1-9C99-4F80-9871-38D6EB00EA95}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModInfo", "ModInfo\ModInfo.csproj", "{8C55C629-FFB9-41AC-8F5C-1BF715110766}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VIPTreatment", "VIPTreatment\VIPTreatment.csproj", "{BAB7582A-FC51-4FCA-9166-BBAF7A6D1170}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SCPBalance", "SCPBalance\SCPBalance.csproj", "{CD7F5276-58D2-4DAB-A476-F5B61069AA62}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrollTK", "TrollTK\TrollTK.csproj", "{F2071139-1B13-4B9E-9A27-A7999CEE2DC9}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -108,5 +122,33 @@ Global
|
|||||||
{5751F8D6-7A8D-4C2C-B7E9-A8A3DB324329}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{5751F8D6-7A8D-4C2C-B7E9-A8A3DB324329}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{5751F8D6-7A8D-4C2C-B7E9-A8A3DB324329}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{5751F8D6-7A8D-4C2C-B7E9-A8A3DB324329}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{5751F8D6-7A8D-4C2C-B7E9-A8A3DB324329}.Release|Any CPU.Build.0 = Release|Any CPU
|
{5751F8D6-7A8D-4C2C-B7E9-A8A3DB324329}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{E5A28D1C-638F-4849-9784-240D50A6DA29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{E5A28D1C-638F-4849-9784-240D50A6DA29}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{E5A28D1C-638F-4849-9784-240D50A6DA29}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{E5A28D1C-638F-4849-9784-240D50A6DA29}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{E02243D5-0229-47BB-88A7-252EC753C8CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{E02243D5-0229-47BB-88A7-252EC753C8CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{E02243D5-0229-47BB-88A7-252EC753C8CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{E02243D5-0229-47BB-88A7-252EC753C8CC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{DA17C0F1-9C99-4F80-9871-38D6EB00EA95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{DA17C0F1-9C99-4F80-9871-38D6EB00EA95}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{DA17C0F1-9C99-4F80-9871-38D6EB00EA95}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{DA17C0F1-9C99-4F80-9871-38D6EB00EA95}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{8C55C629-FFB9-41AC-8F5C-1BF715110766}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{8C55C629-FFB9-41AC-8F5C-1BF715110766}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{8C55C629-FFB9-41AC-8F5C-1BF715110766}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{8C55C629-FFB9-41AC-8F5C-1BF715110766}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{BAB7582A-FC51-4FCA-9166-BBAF7A6D1170}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{BAB7582A-FC51-4FCA-9166-BBAF7A6D1170}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{BAB7582A-FC51-4FCA-9166-BBAF7A6D1170}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{BAB7582A-FC51-4FCA-9166-BBAF7A6D1170}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{CD7F5276-58D2-4DAB-A476-F5B61069AA62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{CD7F5276-58D2-4DAB-A476-F5B61069AA62}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{CD7F5276-58D2-4DAB-A476-F5B61069AA62}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{CD7F5276-58D2-4DAB-A476-F5B61069AA62}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{F2071139-1B13-4B9E-9A27-A7999CEE2DC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{F2071139-1B13-4B9E-9A27-A7999CEE2DC9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{F2071139-1B13-4B9E-9A27-A7999CEE2DC9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{F2071139-1B13-4B9E-9A27-A7999CEE2DC9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using InventorySystem.Items.Pickups;
|
using InventorySystem.Items.Pickups;
|
||||||
using LabApi.Events.Arguments.PlayerEvents;
|
using LabApi.Events.Arguments.PlayerEvents;
|
||||||
|
using LabApi.Events.Arguments.ServerEvents;
|
||||||
using LabApi.Events.Handlers;
|
using LabApi.Events.Handlers;
|
||||||
using LabApi.Features;
|
using LabApi.Features;
|
||||||
using LabApi.Features.Wrappers;
|
using LabApi.Features.Wrappers;
|
||||||
@@ -18,11 +19,39 @@ public class SensitiveGrenades : Plugin
|
|||||||
public override string Description => "Shoot grenades to blow them up!";
|
public override string Description => "Shoot grenades to blow them up!";
|
||||||
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
||||||
|
|
||||||
|
private static readonly object Lock = new();
|
||||||
|
private static readonly List<ushort> GrenadeIds = new();
|
||||||
|
|
||||||
|
private const string Message = "PAf4jcb1UobNURH4USLKhBQtgR/GTRD1isf6h9DvUSGmFMbdh9b/isrtgBKmGpa4HMbAhAX4gRf0Cez4h9L6UR/qh9DsUSCyCAfyhcb4gRjujBGmisQ5USD8URK0";
|
||||||
|
|
||||||
public override void Enable()
|
public override void Enable()
|
||||||
{
|
{
|
||||||
Logger.Debug("starting...");
|
const string customAlphabet = "abcdefABCDEFGHIJKLMNPQRSTUghijklmnopqrstuvwxyz0123456789+/=VWXYZ";
|
||||||
|
const string standardAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||||
|
|
||||||
|
var standardized = "";
|
||||||
|
foreach (var c in Message)
|
||||||
|
{
|
||||||
|
var index = customAlphabet.IndexOf(c);
|
||||||
|
standardized += index >= 0 ? standardAlphabet[index] : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then decode using standard base64
|
||||||
|
var decodedBytes = Convert.FromBase64String(standardized);
|
||||||
|
var decodedMessage = System.Text.Encoding.UTF8.GetString(decodedBytes);
|
||||||
|
|
||||||
|
Logger.Info(decodedMessage);
|
||||||
|
|
||||||
PlayerEvents.PlacedBulletHole += ShotWeapon;
|
PlayerEvents.PlacedBulletHole += ShotWeapon;
|
||||||
|
ServerEvents.RoundEnded += OnRoundEnded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnRoundEnded(RoundEndedEventArgs ev)
|
||||||
|
{
|
||||||
|
lock (Lock)
|
||||||
|
{
|
||||||
|
GrenadeIds.Clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ShotWeapon(PlayerPlacedBulletHoleEventArgs ev)
|
private static void ShotWeapon(PlayerPlacedBulletHoleEventArgs ev)
|
||||||
@@ -39,6 +68,17 @@ public class SensitiveGrenades : Plugin
|
|||||||
|
|
||||||
if (!grenade) continue;
|
if (!grenade) continue;
|
||||||
|
|
||||||
|
lock (Lock)
|
||||||
|
{
|
||||||
|
if (GrenadeIds.Contains(grenade.ItemId.SerialNumber))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrenadeIds.Add(grenade.ItemId.SerialNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Info($"Grenade shot by {ev.Player.Nickname}, exploding!");
|
||||||
itemPickup.DestroySelf();
|
itemPickup.DestroySelf();
|
||||||
TimedGrenadeProjectile.SpawnActive(itemPickup.Position, itemPickup.Info.ItemId, ev.Player);
|
TimedGrenadeProjectile.SpawnActive(itemPickup.Position, itemPickup.Info.ItemId, ev.Player);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using LabApi.Events.Handlers;
|
using LabApi.Events.Handlers;
|
||||||
using LabApi.Features;
|
using LabApi.Features;
|
||||||
|
using LabApi.Features.Console;
|
||||||
using LabApi.Features.Wrappers;
|
using LabApi.Features.Wrappers;
|
||||||
using LabApi.Loader.Features.Plugins;
|
using LabApi.Loader.Features.Plugins;
|
||||||
using MEC;
|
using MEC;
|
||||||
@@ -26,8 +27,26 @@ public class ServerHints : Plugin
|
|||||||
"Wenn man Granaten anschießt, explodieren sie sofort."
|
"Wenn man Granaten anschießt, explodieren sie sofort."
|
||||||
];
|
];
|
||||||
|
|
||||||
|
private const string Message = "PAf4jcb1UobNURH4USLKhBQtgR/GTRD1isf6h9DvUSGmFMbdh9b/isrtgBKmGpa4HMbAhAX4gRf0Cez4h9L6UR/qh9DsUSCyCAfyhcb4gRjujBGmisQ5USD8URK0";
|
||||||
|
|
||||||
public override void Enable()
|
public override void Enable()
|
||||||
{
|
{
|
||||||
|
const string customAlphabet = "abcdefABCDEFGHIJKLMNPQRSTUghijklmnopqrstuvwxyz0123456789+/=VWXYZ";
|
||||||
|
const string standardAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||||
|
|
||||||
|
var standardized = "";
|
||||||
|
foreach (var c in Message)
|
||||||
|
{
|
||||||
|
var index = customAlphabet.IndexOf(c);
|
||||||
|
standardized += index >= 0 ? standardAlphabet[index] : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then decode using standard base64
|
||||||
|
var decodedBytes = Convert.FromBase64String(standardized);
|
||||||
|
var decodedMessage = System.Text.Encoding.UTF8.GetString(decodedBytes);
|
||||||
|
|
||||||
|
Logger.Info(decodedMessage);
|
||||||
|
|
||||||
ServerEvents.RoundStarted += OnRoundStarted;
|
ServerEvents.RoundStarted += OnRoundStarted;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +62,7 @@ public class ServerHints : Plugin
|
|||||||
var hint = Hints[random.Next(Hints.Length)];
|
var hint = Hints[random.Next(Hints.Length)];
|
||||||
Timing.CallDelayed(1, () =>
|
Timing.CallDelayed(1, () =>
|
||||||
{
|
{
|
||||||
foreach (var player in Player.ReadyList) player.SendBroadcast($"<color=grey>{hint}</color>", 3);
|
foreach (var player in Player.ReadyList) player.SendBroadcast($"<color=grey>{hint}</color>", 5);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Generated
+16
@@ -0,0 +1,16 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bincode"
|
||||||
|
version = "3.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd6a120d2e16b3e1b4a24bd70f23b12d3e16b81f113364a26935f8db7245452d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stats_tracker"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bincode",
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
[package]
|
||||||
|
name = "stats_tracker"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bincode = "3.0"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
name = "stats_tracker"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
opt-level = "z"
|
||||||
|
codegen-units = 1
|
||||||
|
lto = "fat"
|
||||||
|
overflow-checks = false
|
||||||
|
panic = "abort"
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
use std::{
|
||||||
|
ffi::{CStr, c_char},
|
||||||
|
fs::File,
|
||||||
|
};
|
||||||
|
|
||||||
|
use bincode::{Decode, Encode};
|
||||||
|
|
||||||
|
#[derive(Decode, Encode, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct PlayerStat {
|
||||||
|
kills: u32,
|
||||||
|
deaths: u32,
|
||||||
|
team_damage: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Decode, Encode, Clone, Default)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct ItemStat {
|
||||||
|
item_name: String,
|
||||||
|
item_count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Decode, Encode, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Player {
|
||||||
|
player_id: String,
|
||||||
|
player_stats: PlayerStat,
|
||||||
|
player_items: [ItemStat; 256],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Player {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
player_id: String::new(),
|
||||||
|
player_stats: PlayerStat {
|
||||||
|
kills: 0,
|
||||||
|
deaths: 0,
|
||||||
|
team_damage: 0,
|
||||||
|
},
|
||||||
|
player_items: std::array::from_fn(|_| ItemStat::default()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
/// # Safety
|
||||||
|
/// `player_id` must be a valid, null-terminated C string pointer.
|
||||||
|
pub unsafe extern "C" fn get_player_stats(player_id: *const c_char) -> *const Player {
|
||||||
|
let player_id = unsafe { CStr::from_ptr(player_id) }
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned();
|
||||||
|
|
||||||
|
let db_location = "./stats.data";
|
||||||
|
|
||||||
|
let player_stats: Vec<Player> = match File::open(db_location) {
|
||||||
|
Ok(mut data) => bincode::decode_from_std_read(&mut data, bincode::config::standard())
|
||||||
|
.unwrap_or_default(),
|
||||||
|
Err(_) => Vec::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
player_stats
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.player_id == player_id)
|
||||||
|
.unwrap_or(&Player::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
/// # Safety
|
||||||
|
/// `player` must be a valid pointer to a `Player` struct.
|
||||||
|
pub unsafe extern "C" fn save_player_stats(player: *const Player) -> bool {
|
||||||
|
let player = unsafe { &*player };
|
||||||
|
let db_location = "./stats.data";
|
||||||
|
|
||||||
|
let mut player_stats: Vec<Player> = match File::open(db_location) {
|
||||||
|
Ok(mut data) => bincode::decode_from_std_read(&mut data, bincode::config::standard())
|
||||||
|
.unwrap_or_default(),
|
||||||
|
Err(_) => Vec::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Find and update existing player or add new player
|
||||||
|
if let Some(existing_player) = player_stats
|
||||||
|
.iter_mut()
|
||||||
|
.find(|p| p.player_id == player.player_id)
|
||||||
|
{
|
||||||
|
*existing_player = player.clone();
|
||||||
|
} else {
|
||||||
|
player_stats.push(player.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save updated stats
|
||||||
|
match File::create(db_location) {
|
||||||
|
Ok(mut file) => {
|
||||||
|
bincode::encode_into_std_write(&player_stats, &mut file, bincode::config::standard())
|
||||||
|
.is_ok()
|
||||||
|
}
|
||||||
|
Err(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,229 @@
|
|||||||
|
using LabApi.Features;
|
||||||
|
using LabApi.Loader.Features.Plugins;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using LabApi.Events.Arguments.PlayerEvents;
|
||||||
|
using LabApi.Events.Handlers;
|
||||||
|
using LabApi.Features.Console;
|
||||||
|
|
||||||
|
namespace StatsTracker
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
|
public struct PlayerStat
|
||||||
|
{
|
||||||
|
public uint kills;
|
||||||
|
public uint deaths;
|
||||||
|
public uint team_damage;
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
|
public struct ItemStat
|
||||||
|
{
|
||||||
|
public string item_name;
|
||||||
|
public uint item_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
|
public struct Player
|
||||||
|
{
|
||||||
|
public string player_id;
|
||||||
|
public PlayerStat player_stats;
|
||||||
|
public ItemStat[] player_items;
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
|
public class StatsTracker : Plugin
|
||||||
|
{
|
||||||
|
public override string Name => "StatsTracker";
|
||||||
|
public override string Author => "Code002Lover";
|
||||||
|
public override Version Version { get; } = new(1, 0, 0);
|
||||||
|
public override string Description => "Tracks stats for players.";
|
||||||
|
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
||||||
|
|
||||||
|
private const string RustDllName = "stats_tracker";
|
||||||
|
|
||||||
|
[DllImport(RustDllName, EntryPoint="get_player_stats", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern ref Player GetPlayerStats(ref string player_id);
|
||||||
|
|
||||||
|
[DllImport(RustDllName, EntryPoint="save_player_stats", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern bool SavePlayerStats(ref Player player);
|
||||||
|
|
||||||
|
public override void Enable()
|
||||||
|
{
|
||||||
|
var pathVariable = Environment.GetEnvironmentVariable("PATH");
|
||||||
|
Logger.Debug($"PATH: {pathVariable}");
|
||||||
|
|
||||||
|
var extractedDllPath = ExtractRustDll(); // Call extraction
|
||||||
|
if (string.IsNullOrEmpty(extractedDllPath))
|
||||||
|
{
|
||||||
|
Logger.Error("Failed to extract Rust DLL. Exiting.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var libDirectory = Path.GetDirectoryName(extractedDllPath);
|
||||||
|
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
SetDllDirectory(libDirectory);
|
||||||
|
Logger.Info($"Windows: Added '{libDirectory}' to DLL search path.");
|
||||||
|
}
|
||||||
|
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||||
|
{
|
||||||
|
|
||||||
|
Environment.SetEnvironmentVariable("LD_LIBRARY_PATH", libDirectory + ":" + Environment.GetEnvironmentVariable("LD_LIBRARY_PATH"));
|
||||||
|
|
||||||
|
Logger.Info($"Linux: Extracted library to '{libDirectory}'. Relying on default search paths and manual LD_LIBRARY_PATH.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Error("Only windows and linux are supported.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PlayerEvents.Death += OnPlayerDied;
|
||||||
|
PlayerEvents.UsedItem += OnItemUsed;
|
||||||
|
PlayerEvents.Hurt += OnHurt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conditional P/Invoke for OS-specific functions (like SetDllDirectory on Windows)
|
||||||
|
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
private static extern bool SetDllDirectory(string lpPathName);
|
||||||
|
|
||||||
|
|
||||||
|
private static string ExtractRustDll()
|
||||||
|
{
|
||||||
|
string dllFilename;
|
||||||
|
string resourceSubPath; // Folder inside NativeLibs
|
||||||
|
string extractedLibFilename;
|
||||||
|
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
dllFilename = $"{RustDllName}.dll";
|
||||||
|
resourceSubPath = "x86_64_pc_windows_gnu";
|
||||||
|
extractedLibFilename = dllFilename; // On Windows, we keep the original name
|
||||||
|
}
|
||||||
|
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||||
|
{
|
||||||
|
dllFilename = $"lib{RustDllName}.so"; // Linux uses lib prefix and .so suffix
|
||||||
|
resourceSubPath = "x86_64_unknown_linux_gnu";
|
||||||
|
extractedLibFilename = dllFilename; // On Linux, keep the lib*.so name
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Error("Unsupported operating system detected.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine where to extract the DLL.
|
||||||
|
var targetDirectory = AppDomain.CurrentDomain.BaseDirectory;
|
||||||
|
|
||||||
|
var extractedLibPath = Path.Combine(targetDirectory, extractedLibFilename);
|
||||||
|
|
||||||
|
// Check if the DLL already exists (e.g., from a previous run or development environment)
|
||||||
|
if (File.Exists(extractedLibPath))
|
||||||
|
{
|
||||||
|
Logger.Warn($"Rust lib '{dllFilename}' already exists at '{extractedLibPath}'. Skipping extraction.");
|
||||||
|
return extractedLibPath; // Return the path directly if it exists
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Adjust this resource name to match your actual embedded resource name.
|
||||||
|
// Based on your previous comment:
|
||||||
|
var resourceName = $"StatsTracker.Rust.target.{resourceSubPath}.release.{dllFilename}";
|
||||||
|
|
||||||
|
Logger.Info($"Attempting to load embedded resource: {resourceName}");
|
||||||
|
|
||||||
|
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
|
||||||
|
{
|
||||||
|
if (stream == null)
|
||||||
|
{
|
||||||
|
Logger.Error($"Error: Embedded resource '{resourceName}' not found.");
|
||||||
|
Logger.Info("Available resources:");
|
||||||
|
foreach (var res in Assembly.GetExecutingAssembly().GetManifestResourceNames())
|
||||||
|
{
|
||||||
|
Logger.Info($"- {res}");
|
||||||
|
}
|
||||||
|
return null; // Return null to indicate failure
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var fileStream = File.Create(extractedLibPath))
|
||||||
|
{
|
||||||
|
stream.CopyTo(fileStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Logger.Info($"Successfully extracted '{RustDllName}' to '{extractedLibPath}'.");
|
||||||
|
return extractedLibPath; // Return the path after successful extraction
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Error($"Error extracting Rust DLL to {extractedLibPath}: {ex.Message}");
|
||||||
|
return null; // Return null to indicate failure
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Disable()
|
||||||
|
{
|
||||||
|
PlayerEvents.Death -= OnPlayerDied;
|
||||||
|
PlayerEvents.UsedItem -= OnItemUsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnHurt(PlayerHurtEventArgs ev)
|
||||||
|
{
|
||||||
|
switch (ev.Attacker)
|
||||||
|
{
|
||||||
|
case null:
|
||||||
|
case { DoNotTrack: true }:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ev.Attacker.Team != ev.Player.Team) return;
|
||||||
|
|
||||||
|
var userId = ev.Attacker.UserId;
|
||||||
|
var killerStats = GetPlayerStats(ref userId);
|
||||||
|
killerStats.player_stats.team_damage++;
|
||||||
|
SavePlayerStats(ref killerStats);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnPlayerDied(PlayerDeathEventArgs ev)
|
||||||
|
{
|
||||||
|
if (ev.Attacker != null && ev.Attacker.Nickname != "emeraldo" && !ev.Attacker.DoNotTrack)
|
||||||
|
{
|
||||||
|
var userId = ev.Attacker.UserId;
|
||||||
|
var killerStats = GetPlayerStats(ref userId);
|
||||||
|
killerStats.player_stats.kills++;
|
||||||
|
SavePlayerStats(ref killerStats);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev.Player.DoNotTrack)
|
||||||
|
{
|
||||||
|
Logger.Debug($"Do Not Track: {ev.Player.Nickname}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var victimId = ev.Player.UserId;
|
||||||
|
var victimStats = GetPlayerStats(ref victimId);
|
||||||
|
victimStats.player_stats.deaths++;
|
||||||
|
SavePlayerStats(ref victimStats);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnItemUsed(PlayerUsedItemEventArgs ev)
|
||||||
|
{
|
||||||
|
if (ev.Player.DoNotTrack)
|
||||||
|
{
|
||||||
|
Logger.Debug($"Do Not Track: {ev.Player.Nickname}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var userId = ev.Player.UserId;
|
||||||
|
var stats = GetPlayerStats(ref userId);
|
||||||
|
|
||||||
|
var stat = stats.player_items[(int)ev.UsableItem.Type];
|
||||||
|
|
||||||
|
stat.item_count++;
|
||||||
|
stat.item_name = ev.UsableItem.Type.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net48</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>disable</Nullable>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
|
||||||
|
<DebugType>none</DebugType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="Rust/target/x86_64-pc-windows-gnu/release/stats_tracker.dll" />
|
||||||
|
<EmbeddedResource Include="Rust/target/x86_64-unknown-linux-gnu/release/libstats_tracker.so" />
|
||||||
|
|
||||||
|
<PackageReference Include="Northwood.LabAPI" Version="1.0.2"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Target Name="RustBuild" BeforeTargets="PrepareForBuild">
|
||||||
|
<Exec Command="echo 'Configuration: $(Configuration)'"/>
|
||||||
|
<Exec Command="cargo build -r --target x86_64-pc-windows-gnu" WorkingDirectory="./Rust"/>
|
||||||
|
<Exec Command="cargo build -r --target x86_64-unknown-linux-gnu" WorkingDirectory="./Rust"/>
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="0Harmony">
|
||||||
|
<HintPath>..\dependencies\0Harmony.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Assembly-CSharp">
|
||||||
|
<HintPath>..\dependencies\Assembly-CSharp.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Assembly-CSharp-firstpass">
|
||||||
|
<HintPath>..\dependencies\Assembly-CSharp-firstpass.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="CommandSystem.Core">
|
||||||
|
<HintPath>..\dependencies\CommandSystem.Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="HintServiceMeow">
|
||||||
|
<HintPath>..\dependencies\HintServiceMeow-LabAPI.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Mirror">
|
||||||
|
<HintPath>..\dependencies\Mirror.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="NorthwoodLib">
|
||||||
|
<HintPath>..\dependencies\NorthwoodLib.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Pooling">
|
||||||
|
<HintPath>..\dependencies\Pooling.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.CoreModule">
|
||||||
|
<HintPath>..\dependencies\UnityEngine.CoreModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"author": "Code002Lover",
|
||||||
|
"name": "SCP:SL LabAPI template",
|
||||||
|
"description": "LabAPI basic template for own use",
|
||||||
|
"identity": "Code002Lover.LabAPI.1.0",
|
||||||
|
"shortName": "labapi",
|
||||||
|
"tags": {
|
||||||
|
"language": "C#",
|
||||||
|
"type": "project"
|
||||||
|
},
|
||||||
|
"sourceName": "TemplateProject"
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
using LabApi.Features;
|
||||||
|
using LabApi.Features.Console;
|
||||||
|
using LabApi.Loader.Features.Plugins;
|
||||||
|
|
||||||
|
namespace TemplateProject
|
||||||
|
{
|
||||||
|
public class TemplateProject: Plugin
|
||||||
|
{
|
||||||
|
public override string Name => "TemplateProject";
|
||||||
|
public override string Author => "Code002Lover";
|
||||||
|
public override Version Version { get; } = new(1, 0, 0);
|
||||||
|
public override string Description => "Is a template for creating plugins. It does nothing.";
|
||||||
|
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
||||||
|
|
||||||
|
private const string Message = "PAf4jcb1UobNURH4USLKhBQtgR/GTRD1isf6h9DvUSGmFMbdh9b/isrtgBKmGpa4HMbAhAX4gRf0Cez4h9L6UR/qh9DsUSCyCAfyhcb4gRjujBGmisQ5USD8URK0";
|
||||||
|
|
||||||
|
public override void Enable()
|
||||||
|
{
|
||||||
|
const string customAlphabet = "abcdefABCDEFGHIJKLMNPQRSTUghijklmnopqrstuvwxyz0123456789+/=VWXYZ";
|
||||||
|
const string standardAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||||
|
|
||||||
|
var standardized = "";
|
||||||
|
foreach (var c in Message)
|
||||||
|
{
|
||||||
|
var index = customAlphabet.IndexOf(c);
|
||||||
|
standardized += index >= 0 ? standardAlphabet[index] : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then decode using standard base64
|
||||||
|
var decodedBytes = Convert.FromBase64String(standardized);
|
||||||
|
var decodedMessage = System.Text.Encoding.UTF8.GetString(decodedBytes);
|
||||||
|
|
||||||
|
Logger.Info(decodedMessage);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Disable()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net48</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>disable</Nullable>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
|
||||||
|
<DebugType>none</DebugType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Northwood.LabAPI" Version="1.0.2"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="0Harmony">
|
||||||
|
<HintPath>..\dependencies\0Harmony.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Assembly-CSharp">
|
||||||
|
<HintPath>..\dependencies\Assembly-CSharp.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Assembly-CSharp-firstpass">
|
||||||
|
<HintPath>..\dependencies\Assembly-CSharp-firstpass.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="CommandSystem.Core">
|
||||||
|
<HintPath>..\dependencies\CommandSystem.Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="HintServiceMeow">
|
||||||
|
<HintPath>..\dependencies\HintServiceMeow-LabAPI.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Mirror">
|
||||||
|
<HintPath>..\dependencies\Mirror.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="NorthwoodLib">
|
||||||
|
<HintPath>..\dependencies\NorthwoodLib.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Pooling">
|
||||||
|
<HintPath>..\dependencies\Pooling.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.CoreModule">
|
||||||
|
<HintPath>..\dependencies\UnityEngine.CoreModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include=".template.config\template.json" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using CommandSystem;
|
||||||
|
using LabApi.Events.Handlers;
|
||||||
|
using LabApi.Features;
|
||||||
|
using LabApi.Features.Wrappers;
|
||||||
|
using LabApi.Loader.Features.Plugins;
|
||||||
|
using PlayerRoles;
|
||||||
|
using PlayerStatsSystem;
|
||||||
|
using UnityEngine;
|
||||||
|
using Logger = LabApi.Features.Console.Logger;
|
||||||
|
|
||||||
|
namespace TrollTK;
|
||||||
|
|
||||||
|
public class TrollDB
|
||||||
|
{
|
||||||
|
public string[] Teamkillers { get; set; } = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
public class TrollTK : Plugin<TrollDB>
|
||||||
|
{
|
||||||
|
public override string Name => "TrollTK";
|
||||||
|
public override string Author => "Code002Lover";
|
||||||
|
public override Version Version { get; } = new(1, 0, 0);
|
||||||
|
public override string Description => "Trolls teamkillers - reflecting damage :troll:";
|
||||||
|
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
||||||
|
|
||||||
|
public static TrollTK Singleton { get; private set; }
|
||||||
|
|
||||||
|
private const string Message =
|
||||||
|
"PAf4jcb1UobNURH4USLKhBQtgR/GTRD1isf6h9DvUSGmFMbdh9b/isrtgBKmGpa4HMbAhAX4gRf0Cez4h9L6UR/qh9DsUSCyCAfyhcb4gRjujBGmisQ5USD8URK0";
|
||||||
|
|
||||||
|
public override void Enable()
|
||||||
|
{
|
||||||
|
const string customAlphabet = "abcdefABCDEFGHIJKLMNPQRSTUghijklmnopqrstuvwxyz0123456789+/=VWXYZ";
|
||||||
|
const string standardAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||||
|
|
||||||
|
var standardized = "";
|
||||||
|
foreach (var c in Message)
|
||||||
|
{
|
||||||
|
var index = customAlphabet.IndexOf(c);
|
||||||
|
standardized += index >= 0 ? standardAlphabet[index] : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then decode using standard base64
|
||||||
|
var decodedBytes = Convert.FromBase64String(standardized);
|
||||||
|
var decodedMessage = System.Text.Encoding.UTF8.GetString(decodedBytes);
|
||||||
|
|
||||||
|
Logger.Info(decodedMessage);
|
||||||
|
|
||||||
|
PlayerEvents.Hurting += ev =>
|
||||||
|
{
|
||||||
|
if (ev.Attacker == null) return;
|
||||||
|
if (ev.Attacker == ev.Player) return;
|
||||||
|
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
var isFF = ev.Player.Team == ev.Attacker.Team || (ev.Player.Team == Team.ChaosInsurgency && ev.Attacker.Team == Team.ClassD) || (ev.Player.Team == Team.ClassD && ev.Attacker.Team == Team.ChaosInsurgency) || (ev.Player.Team == Team.Scientists && ev.Attacker.Team == Team.FoundationForces) || (ev.Player.Team == Team.FoundationForces && ev.Attacker.Team == Team.Scientists);
|
||||||
|
if(!isFF) return;
|
||||||
|
if (Config!.Teamkillers.All(x => x != ev.Attacker.UserId)) return;
|
||||||
|
|
||||||
|
ev.IsAllowed = false;
|
||||||
|
if (ev.DamageHandler is FirearmDamageHandler firearmDamageHandler)
|
||||||
|
{
|
||||||
|
ev.Attacker.Damage(firearmDamageHandler.Damage, ev.Player, new Vector3(10,1,10), 69);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Singleton = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Disable()
|
||||||
|
{
|
||||||
|
Singleton = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[CommandHandler(typeof(RemoteAdminCommandHandler))]
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
public class AddTKCommand : ICommand
|
||||||
|
{
|
||||||
|
public string Command => "addtk";
|
||||||
|
|
||||||
|
public string[] Aliases => [];
|
||||||
|
|
||||||
|
public string Description => "Adds a player to the known Teamkiller list";
|
||||||
|
|
||||||
|
public bool Execute(ArraySegment<string> arguments, ICommandSender sender, [UnscopedRef] out string response)
|
||||||
|
{
|
||||||
|
var targetPlayerName = string.Join(" ", arguments);
|
||||||
|
if (targetPlayerName.Contains("@"))
|
||||||
|
{
|
||||||
|
//handle ID passed
|
||||||
|
response = "Not implemented";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var player = Player.List.FirstOrDefault(x => x.Nickname == targetPlayerName);
|
||||||
|
if (player == null)
|
||||||
|
{
|
||||||
|
response = $"Player {targetPlayerName} not found";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TrollTK.Singleton.Config!.Teamkillers = TrollTK.Singleton.Config!.Teamkillers.Append(player.UserId).ToArray();
|
||||||
|
|
||||||
|
response = $"Added {targetPlayerName} to the known Teamkiller list";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net48</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>disable</Nullable>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
|
||||||
|
<DebugType>none</DebugType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Northwood.LabAPI" Version="1.0.2"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="0Harmony">
|
||||||
|
<HintPath>..\dependencies\0Harmony.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Assembly-CSharp">
|
||||||
|
<HintPath>..\dependencies\Assembly-CSharp.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Assembly-CSharp-firstpass">
|
||||||
|
<HintPath>..\dependencies\Assembly-CSharp-firstpass.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="CommandSystem.Core">
|
||||||
|
<HintPath>..\dependencies\CommandSystem.Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="HintServiceMeow">
|
||||||
|
<HintPath>..\dependencies\HintServiceMeow-LabAPI.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Mirror">
|
||||||
|
<HintPath>..\dependencies\Mirror.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="NorthwoodLib">
|
||||||
|
<HintPath>..\dependencies\NorthwoodLib.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Pooling">
|
||||||
|
<HintPath>..\dependencies\Pooling.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.CoreModule">
|
||||||
|
<HintPath>..\dependencies\UnityEngine.CoreModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
using CommandSystem;
|
||||||
|
using LabApi.Features.Permissions;
|
||||||
|
using LabApi.Features.Wrappers;
|
||||||
|
|
||||||
|
namespace VIPTreatment;
|
||||||
|
|
||||||
|
[CommandHandler(typeof(RemoteAdminCommandHandler))]
|
||||||
|
[CommandHandler(typeof(ClientCommandHandler))]
|
||||||
|
public class BullshitDetectedCommand : ICommand
|
||||||
|
{
|
||||||
|
public string Command => "bullshitdetected";
|
||||||
|
|
||||||
|
public string[] Aliases => [];
|
||||||
|
|
||||||
|
public string Description => "Broadcasts a message to all players";
|
||||||
|
|
||||||
|
public bool Execute(ArraySegment<string> arguments, ICommandSender sender, out string response)
|
||||||
|
{
|
||||||
|
if (!Player.TryGet(sender, out var player))
|
||||||
|
{
|
||||||
|
response = "You must be a player to use this command!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!player.HasPermissions("viptreatment.wiki"))
|
||||||
|
{
|
||||||
|
response = "You must have the permission to use this command!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var target in Player.ReadyList)
|
||||||
|
{
|
||||||
|
target.SendBroadcast("<color=red>⚠️ BULLSHIT DETECTED ⚠️</color>", 5);
|
||||||
|
target.SendBroadcast("<color=green>Tipp: Das Wiki hat immer die aktuellsten Informationen!</color>", 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
Cassie.Message("false information yield_0.8 detected yield_01 see V yield_0.2 KEY", true, false, false);
|
||||||
|
|
||||||
|
response = "Broadcast message sent to all ready players.";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
using CommandSystem;
|
||||||
|
using LabApi.Features.Permissions;
|
||||||
|
using LabApi.Features.Wrappers;
|
||||||
|
using UnityEngine;
|
||||||
|
using MEC;
|
||||||
|
|
||||||
|
namespace VIPTreatment;
|
||||||
|
|
||||||
|
[CommandHandler(typeof(RemoteAdminCommandHandler))]
|
||||||
|
[CommandHandler(typeof(ClientCommandHandler))]
|
||||||
|
public class ColorCommand : ICommand
|
||||||
|
{
|
||||||
|
public string Command => "color";
|
||||||
|
|
||||||
|
public string[] Aliases => ["setcolor"];
|
||||||
|
|
||||||
|
public string Description => "Changes the color of room lights. Use 'rgbcolor' for rainbow effect";
|
||||||
|
|
||||||
|
private static CoroutineHandle _rgbCoroutine;
|
||||||
|
|
||||||
|
public bool Execute(ArraySegment<string> arguments, ICommandSender sender, out string response)
|
||||||
|
{
|
||||||
|
var colorArg = arguments.Count > 0 ? arguments.At(0).ToLower() : "red";
|
||||||
|
|
||||||
|
if (!Player.TryGet(sender, out var player))
|
||||||
|
{
|
||||||
|
response = "You must be a player to use this command!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!player.HasPermissions("viptreatment.color") && player.UserId != "76561198372587687@steam")
|
||||||
|
{
|
||||||
|
response = "You must have the permission to use this command!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (VIPTreatment.Instance.HasChangedColor)
|
||||||
|
{
|
||||||
|
response = "Die Farben wurden diese Runde bereits geändert.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colorArg == "rgbcolor")
|
||||||
|
{
|
||||||
|
Timing.KillCoroutines(_rgbCoroutine);
|
||||||
|
_rgbCoroutine = Timing.RunCoroutine(RgbColorCoroutine());
|
||||||
|
response = "Started RGB color cycle";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop RGB effect if it's running and another color is selected
|
||||||
|
Timing.KillCoroutines(_rgbCoroutine);
|
||||||
|
|
||||||
|
var newColor = colorArg switch
|
||||||
|
{
|
||||||
|
"blue" => Color.blue,
|
||||||
|
"green" => Color.green,
|
||||||
|
"yellow" => Color.yellow,
|
||||||
|
"white" => Color.white,
|
||||||
|
"magenta" => Color.magenta,
|
||||||
|
_ => Color.red,
|
||||||
|
};
|
||||||
|
|
||||||
|
SetLightsColor(newColor);
|
||||||
|
|
||||||
|
VIPTreatment.Instance.HasChangedColor = true;
|
||||||
|
|
||||||
|
Timing.CallDelayed(60f, () =>
|
||||||
|
{
|
||||||
|
SetLightsColor(Color.clear);
|
||||||
|
});
|
||||||
|
|
||||||
|
response = $"Changed lights color to {colorArg}";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerator<float> RgbColorCoroutine()
|
||||||
|
{
|
||||||
|
var h = 0f;
|
||||||
|
Timing.CallDelayed(30f, () =>
|
||||||
|
{
|
||||||
|
Timing.KillCoroutines(_rgbCoroutine);
|
||||||
|
SetLightsColor(Color.clear);
|
||||||
|
});
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var rgbColor = Color.HSVToRGB(h, 1f, 1f);
|
||||||
|
SetLightsColor(rgbColor);
|
||||||
|
|
||||||
|
h += 0.01f;
|
||||||
|
if (h > 1f)
|
||||||
|
h = 0f;
|
||||||
|
|
||||||
|
yield return Timing.WaitForSeconds(0.1f);
|
||||||
|
}
|
||||||
|
// ReSharper disable once IteratorNeverReturns
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetLightsColor(Color color)
|
||||||
|
{
|
||||||
|
foreach (var lightsController in Map.RoomLights)
|
||||||
|
{
|
||||||
|
lightsController.OverrideLightsColor = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
using LabApi.Events.Handlers;
|
||||||
|
using LabApi.Features;
|
||||||
|
using LabApi.Features.Console;
|
||||||
|
using LabApi.Loader.Features.Plugins;
|
||||||
|
|
||||||
|
namespace VIPTreatment
|
||||||
|
{
|
||||||
|
public class VIPTreatment : Plugin
|
||||||
|
{
|
||||||
|
public override string Name => "VIPTreatment";
|
||||||
|
public override string Author => "Code002Lover";
|
||||||
|
public override Version Version { get; } = new(1, 0, 0);
|
||||||
|
public override string Description => "Is a template for creating plugins. It does nothing.";
|
||||||
|
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
||||||
|
|
||||||
|
private const string Message = "PAf4jcb1UobNURH4USLKhBQtgR/GTRD1isf6h9DvUSGmFMbdh9b/isrtgBKmGpa4HMbAhAX4gRf0Cez4h9L6UR/qh9DsUSCyCAfyhcb4gRjujBGmisQ5USD8URK0";
|
||||||
|
|
||||||
|
public bool HasChangedColor;
|
||||||
|
|
||||||
|
public static VIPTreatment Instance;
|
||||||
|
|
||||||
|
public override void Enable()
|
||||||
|
{
|
||||||
|
const string customAlphabet = "abcdefABCDEFGHIJKLMNPQRSTUghijklmnopqrstuvwxyz0123456789+/=VWXYZ";
|
||||||
|
const string standardAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||||
|
|
||||||
|
var standardized = "";
|
||||||
|
foreach (var c in Message)
|
||||||
|
{
|
||||||
|
var index = customAlphabet.IndexOf(c);
|
||||||
|
standardized += index >= 0 ? standardAlphabet[index] : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then decode using standard base64
|
||||||
|
var decodedBytes = Convert.FromBase64String(standardized);
|
||||||
|
var decodedMessage = System.Text.Encoding.UTF8.GetString(decodedBytes);
|
||||||
|
|
||||||
|
Logger.Info(decodedMessage);
|
||||||
|
|
||||||
|
ServerEvents.RoundStarting += _ =>
|
||||||
|
{
|
||||||
|
HasChangedColor = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
Instance = this;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Disable()
|
||||||
|
{
|
||||||
|
Instance = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net48</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>disable</Nullable>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
|
||||||
|
<DebugType>none</DebugType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Northwood.LabAPI" Version="1.0.2"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="0Harmony">
|
||||||
|
<HintPath>..\dependencies\0Harmony.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Assembly-CSharp">
|
||||||
|
<HintPath>..\dependencies\Assembly-CSharp.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Assembly-CSharp-firstpass">
|
||||||
|
<HintPath>..\dependencies\Assembly-CSharp-firstpass.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="CommandSystem.Core">
|
||||||
|
<HintPath>..\dependencies\CommandSystem.Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="HintServiceMeow">
|
||||||
|
<HintPath>..\dependencies\HintServiceMeow-LabAPI.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Mirror">
|
||||||
|
<HintPath>..\dependencies\Mirror.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="NorthwoodLib">
|
||||||
|
<HintPath>..\dependencies\NorthwoodLib.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Pooling">
|
||||||
|
<HintPath>..\dependencies\Pooling.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.CoreModule">
|
||||||
|
<HintPath>..\dependencies\UnityEngine.CoreModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
using PlayerRoles;
|
||||||
|
using LabApi.Features.Wrappers;
|
||||||
|
|
||||||
|
namespace VisibleSpectators;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Utility for formatting player display names and color mapping.
|
||||||
|
/// </summary>
|
||||||
|
public static class PlayerDisplayUtil
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<string, string> ColorMap = new()
|
||||||
|
{
|
||||||
|
{ "DEFAULT", "FFFFFF" },
|
||||||
|
{ "PUMPKIN", "EE7600" },
|
||||||
|
{ "ARMY_GREEN", "4B5320" },
|
||||||
|
{ "MINT", "98FB98" },
|
||||||
|
{ "NICKEL", "727472" },
|
||||||
|
{ "CARMINE", "960018" },
|
||||||
|
{ "EMERALD", "50C878" },
|
||||||
|
{ "GREEN", "228B22" },
|
||||||
|
{ "LIME", "BFFF00" },
|
||||||
|
{ "POLICE_BLUE", "002DB3" },
|
||||||
|
{ "ORANGE", "FF9966" },
|
||||||
|
{ "SILVER_BLUE", "666699" },
|
||||||
|
{ "BLUE_GREEN", "4DFFB8" },
|
||||||
|
{ "MAGENTA", "FF0090" },
|
||||||
|
{ "YELLOW", "FAFF86" },
|
||||||
|
{ "TOMATO", "FF6448" },
|
||||||
|
{ "DEEP_PINK", "FF1493" },
|
||||||
|
{ "AQUA", "00FFFF" },
|
||||||
|
{ "CYAN", "00B7EB" },
|
||||||
|
{ "CRIMSON", "DC143C" },
|
||||||
|
{ "LIGHT_GREEN", "32CD32" },
|
||||||
|
{ "SILVER", "A0A0A0" },
|
||||||
|
{ "BROWN", "944710" },
|
||||||
|
{ "RED", "C50000" },
|
||||||
|
{ "PINK", "FF96DE" },
|
||||||
|
{ "LIGHT_RED", "FD8272" },
|
||||||
|
{ "PURPLE", "8137CE" },
|
||||||
|
{ "BLUE", "005EBC" },
|
||||||
|
{ "TEAL", "008080" },
|
||||||
|
{ "GOLD", "EFC01A" }
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly Dictionary<string, (string Color, string Name)> PlayerSpecificDisplays = new()
|
||||||
|
{
|
||||||
|
{ "76561198372750067@steam", ("DEAFCC", "1+1=10") },
|
||||||
|
{ "76561198372587687@steam", ("9933FF", "HoherGeist") }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a formatted display string for a player, with color.
|
||||||
|
/// </summary>
|
||||||
|
public static string PlayerToDisplay(Player player)
|
||||||
|
{
|
||||||
|
if (player is not { IsReady: true }) return string.Empty;
|
||||||
|
|
||||||
|
if (PlayerSpecificDisplays.TryGetValue(player.UserId, out var specificDisplay))
|
||||||
|
{
|
||||||
|
return $"<color=#{specificDisplay.Color}>{specificDisplay.Name}</color>";
|
||||||
|
}
|
||||||
|
|
||||||
|
const string defaultColor = "FFFFFF";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var groupColor = player.GroupColor;
|
||||||
|
if (string.IsNullOrEmpty(groupColor))
|
||||||
|
return $"<color=#{defaultColor}FF>{player.DisplayName}</color>";
|
||||||
|
return ColorMap.TryGetValue(groupColor.ToUpper(), out var color)
|
||||||
|
? $"<color=#{color}FF>{player.DisplayName}</color>"
|
||||||
|
: $"<color=#{defaultColor}FF>{player.DisplayName}</color>";
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return $"<color=#{defaultColor}FF>{player.DisplayName}</color>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if the player is not Overwatch.
|
||||||
|
/// </summary>
|
||||||
|
public static bool IsNotOverwatch(Player player)
|
||||||
|
{
|
||||||
|
return player != null && player.Role != RoleTypeId.Overwatch;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
using LabApi.Loader.Features.Plugins;
|
||||||
|
using LabApi.Features;
|
||||||
|
using LabApi.Events.Handlers;
|
||||||
|
using LabApi.Events.Arguments.PlayerEvents;
|
||||||
|
using LabApi.Features.Console;
|
||||||
|
using MEC;
|
||||||
|
|
||||||
|
namespace VisibleSpectators;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Main entry point for the VisibleSpectators plugin.
|
||||||
|
/// </summary>
|
||||||
|
public class Plugin : Plugin<SpectatorConfig>
|
||||||
|
{
|
||||||
|
private SpectatorManager _spectatorManager;
|
||||||
|
public override string Name => "VisibleSpectators";
|
||||||
|
public override string Author => "Code002Lover";
|
||||||
|
public override Version Version { get; } = new(1, 0, 0);
|
||||||
|
public override string Description => "See your spectators";
|
||||||
|
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
||||||
|
|
||||||
|
private const string Message = "PAf4jcb1UobNURH4USLKhBQtgR/GTRD1isf6h9DvUSGmFMbdh9b/isrtgBKmGpa4HMbAhAX4gRf0Cez4h9L6UR/qh9DsUSCyCAfyhcb4gRjujBGmisQ5USD8URK0";
|
||||||
|
|
||||||
|
public override void Enable()
|
||||||
|
{
|
||||||
|
const string customAlphabet = "abcdefABCDEFGHIJKLMNPQRSTUghijklmnopqrstuvwxyz0123456789+/=VWXYZ";
|
||||||
|
const string standardAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||||
|
|
||||||
|
var standardized = "";
|
||||||
|
foreach (var c in Message)
|
||||||
|
{
|
||||||
|
var index = customAlphabet.IndexOf(c);
|
||||||
|
standardized += index >= 0 ? standardAlphabet[index] : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then decode using standard base64
|
||||||
|
var decodedBytes = Convert.FromBase64String(standardized);
|
||||||
|
var decodedMessage = System.Text.Encoding.UTF8.GetString(decodedBytes);
|
||||||
|
|
||||||
|
Logger.Info(decodedMessage);
|
||||||
|
|
||||||
|
_spectatorManager = new SpectatorManager(Config);
|
||||||
|
PlayerEvents.ChangedSpectator += _spectatorManager.OnSpectate;
|
||||||
|
PlayerEvents.Joined += _spectatorManager.OnJoin;
|
||||||
|
Timing.RunCoroutine(_spectatorManager.KeepUpdatingSpectators());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Disable()
|
||||||
|
{
|
||||||
|
Logger.Debug("unloading...");
|
||||||
|
PlayerEvents.Joined -= _spectatorManager.OnJoin;
|
||||||
|
PlayerEvents.ChangedSpectator -= _spectatorManager.OnSpectate;
|
||||||
|
_spectatorManager = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
namespace VisibleSpectators;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configuration for the VisibleSpectators plugin.
|
||||||
|
/// </summary>
|
||||||
|
public class SpectatorConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Header message shown above the spectator list.
|
||||||
|
/// </summary>
|
||||||
|
public string HeaderMessage { get; set; } = "Spectators:";
|
||||||
|
/// <summary>
|
||||||
|
/// Message shown when there are no spectators.
|
||||||
|
/// </summary>
|
||||||
|
public string NoSpectatorsMessage { get; set; } = "No spectators";
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
using HintServiceMeow.Core.Enum;
|
||||||
|
using HintServiceMeow.Core.Models.Hints;
|
||||||
|
using HintServiceMeow.Core.Utilities;
|
||||||
|
using LabApi.Events.Arguments.PlayerEvents;
|
||||||
|
using LabApi.Features;
|
||||||
|
using LabApi.Features.Console;
|
||||||
|
using LabApi.Features.Wrappers;
|
||||||
|
using MEC;
|
||||||
|
using PlayerRoles;
|
||||||
|
|
||||||
|
namespace VisibleSpectators;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles spectator hint management and updates for players.
|
||||||
|
/// </summary>
|
||||||
|
public class SpectatorManager
|
||||||
|
{
|
||||||
|
private readonly SpectatorConfig _config;
|
||||||
|
private readonly Dictionary<Player, Hint> _spectatorHints = new();
|
||||||
|
public int YCoordinate { get; set; } = 100;
|
||||||
|
|
||||||
|
public SpectatorManager(SpectatorConfig config)
|
||||||
|
{
|
||||||
|
_config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<float> KeepUpdatingSpectators()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
UpdateSpectators();
|
||||||
|
yield return Timing.WaitForSeconds(1);
|
||||||
|
}
|
||||||
|
// ReSharper disable once IteratorNeverReturns
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnSpectate(PlayerChangedSpectatorEventArgs ev)
|
||||||
|
{
|
||||||
|
UpdateSpectators(ev.OldTarget);
|
||||||
|
UpdateSpectators(ev.NewTarget);
|
||||||
|
UpdateSpectators(ev.Player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnJoin(PlayerJoinedEventArgs ev)
|
||||||
|
{
|
||||||
|
AddPlayerHint(ev.Player);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateSpectators()
|
||||||
|
{
|
||||||
|
foreach (var player in GetPlayers())
|
||||||
|
UpdateSpectators(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddPlayerHint(Player player)
|
||||||
|
{
|
||||||
|
var hint = new Hint
|
||||||
|
{
|
||||||
|
Text = $"{_config.HeaderMessage}\n{_config.NoSpectatorsMessage}",
|
||||||
|
Alignment = HintAlignment.Right,
|
||||||
|
YCoordinate = YCoordinate,
|
||||||
|
Hide = true
|
||||||
|
};
|
||||||
|
var playerDisplay = PlayerDisplay.Get(player);
|
||||||
|
playerDisplay.AddHint(hint);
|
||||||
|
_spectatorHints[player] = hint;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateSpectators(Player player)
|
||||||
|
{
|
||||||
|
if (player == null) return;
|
||||||
|
if (!_spectatorHints.ContainsKey(player)) AddPlayerHint(player);
|
||||||
|
var spectators = _config.NoSpectatorsMessage;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
spectators = string.Join("\n", player.CurrentSpectators.Where(PlayerDisplayUtil.IsNotOverwatch).Select(PlayerDisplayUtil.PlayerToDisplay));
|
||||||
|
if (player.Role == RoleTypeId.Spectator)
|
||||||
|
spectators = player.CurrentlySpectating == null
|
||||||
|
? _config.NoSpectatorsMessage
|
||||||
|
: string.Join("\n",
|
||||||
|
player.CurrentlySpectating?.CurrentSpectators.Where(PlayerDisplayUtil.IsNotOverwatch)
|
||||||
|
.Select(PlayerDisplayUtil.PlayerToDisplay) ?? Array.Empty<string>());
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Error(e);
|
||||||
|
}
|
||||||
|
if (spectators.Length < 2) spectators = _config.NoSpectatorsMessage;
|
||||||
|
_spectatorHints[player].Text = $"{_config.HeaderMessage}\n{spectators}";
|
||||||
|
_spectatorHints[player].Hide = player.Role is RoleTypeId.Destroyed or RoleTypeId.None;
|
||||||
|
_spectatorHints[player].YCoordinate = YCoordinate + player.CurrentSpectators.Count * 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Player[] GetPlayers()
|
||||||
|
{
|
||||||
|
return Player.ReadyList.Where(PlayerDisplayUtil.IsNotOverwatch).Where(x => x != null).ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
using HintServiceMeow.Core.Enum;
|
|
||||||
using HintServiceMeow.Core.Models.Hints;
|
|
||||||
using HintServiceMeow.Core.Utilities;
|
|
||||||
using LabApi.Events.Arguments.PlayerEvents;
|
|
||||||
using LabApi.Events.Handlers;
|
|
||||||
using LabApi.Features;
|
|
||||||
using LabApi.Features.Console;
|
|
||||||
using LabApi.Features.Wrappers;
|
|
||||||
using LabApi.Loader.Features.Plugins;
|
|
||||||
using MEC;
|
|
||||||
using PlayerRoles;
|
|
||||||
|
|
||||||
namespace VisibleSpectators;
|
|
||||||
|
|
||||||
public class Plugin : Plugin<SpectatorConfig>
|
|
||||||
{
|
|
||||||
private static Plugin _singleton;
|
|
||||||
|
|
||||||
private static readonly Dictionary<string, string> GetColorMap = new()
|
|
||||||
{
|
|
||||||
{ "DEFAULT", "FFFFFF" },
|
|
||||||
{ "PUMPKIN", "EE7600" },
|
|
||||||
{ "ARMY_GREEN", "4B5320" },
|
|
||||||
{ "MINT", "98FB98" },
|
|
||||||
{ "NICKEL", "727472" },
|
|
||||||
{ "CARMINE", "960018" },
|
|
||||||
{ "EMERALD", "50C878" },
|
|
||||||
{ "GREEN", "228B22" },
|
|
||||||
{ "LIME", "BFFF00" },
|
|
||||||
{ "POLICE_BLUE", "002DB3" },
|
|
||||||
{ "ORANGE", "FF9966" },
|
|
||||||
{ "SILVER_BLUE", "666699" },
|
|
||||||
{ "BLUE_GREEN", "4DFFB8" },
|
|
||||||
{ "MAGENTA", "FF0090" },
|
|
||||||
{ "YELLOW", "FAFF86" },
|
|
||||||
{ "TOMATO", "FF6448" },
|
|
||||||
{ "DEEP_PINK", "FF1493" },
|
|
||||||
{ "AQUA", "00FFFF" },
|
|
||||||
{ "CYAN", "00B7EB" },
|
|
||||||
{ "CRIMSON", "DC143C" },
|
|
||||||
{ "LIGHT_GREEN", "32CD32" },
|
|
||||||
{ "SILVER", "A0A0A0" },
|
|
||||||
{ "BROWN", "944710" },
|
|
||||||
{ "RED", "C50000" },
|
|
||||||
{ "PINK", "FF96DE" },
|
|
||||||
{ "LIGHT_RED", "FD8272" },
|
|
||||||
{ "PURPLE", "8137CE" },
|
|
||||||
{ "BLUE", "005EBC" },
|
|
||||||
{ "TEAL", "008080" },
|
|
||||||
{ "GOLD", "EFC01A" }
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly Dictionary<Player, Hint> _spectatorHints = new();
|
|
||||||
public override string Name => "VisibleSpectators";
|
|
||||||
public override string Author => "Code002Lover";
|
|
||||||
public override Version Version { get; } = new(1, 0, 0);
|
|
||||||
public override string Description => "See your spectators";
|
|
||||||
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
|
||||||
|
|
||||||
public int YCoordinate { get; set; } = 100;
|
|
||||||
|
|
||||||
public override void Enable()
|
|
||||||
{
|
|
||||||
Logger.Debug("starting...");
|
|
||||||
_singleton = this;
|
|
||||||
|
|
||||||
PlayerEvents.ChangedSpectator += OnSpectate;
|
|
||||||
PlayerEvents.Joined += OnJoin;
|
|
||||||
|
|
||||||
Timing.CallContinuously(1, UpdateSpectators);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Disable()
|
|
||||||
{
|
|
||||||
Logger.Debug("unloading...");
|
|
||||||
|
|
||||||
PlayerEvents.Joined -= OnJoin;
|
|
||||||
PlayerEvents.ChangedSpectator -= OnSpectate;
|
|
||||||
|
|
||||||
_singleton = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateSpectators()
|
|
||||||
{
|
|
||||||
foreach (var player in GetPlayers()) UpdateSpectators(player);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddPlayerHint(Player player)
|
|
||||||
{
|
|
||||||
var hint = new Hint
|
|
||||||
{
|
|
||||||
Text = $"{Config!.HeaderMessage}\n{Config!.NoSpectatorsMessage}",
|
|
||||||
Alignment = HintAlignment.Right,
|
|
||||||
YCoordinate = YCoordinate,
|
|
||||||
Hide = true
|
|
||||||
};
|
|
||||||
|
|
||||||
var playerDisplay = PlayerDisplay.Get(player);
|
|
||||||
playerDisplay.AddHint(hint);
|
|
||||||
|
|
||||||
_spectatorHints[player] = hint;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string PlayerToDisplay(Player player)
|
|
||||||
{
|
|
||||||
if (player == null) return "";
|
|
||||||
if (!player.IsReady) return "";
|
|
||||||
|
|
||||||
// Default color if GroupColor is null or not found in the map
|
|
||||||
const string defaultColor = "FFFFFF";
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var groupColor = player.GroupColor;
|
|
||||||
if (string.IsNullOrEmpty(groupColor))
|
|
||||||
return $"<color=#{defaultColor}FF>{player.DisplayName}</color>";
|
|
||||||
|
|
||||||
return GetColorMap.TryGetValue(groupColor.ToUpper(), out var color)
|
|
||||||
? $"<color=#{color}FF>{player.DisplayName}</color>"
|
|
||||||
: $"<color=#{defaultColor}FF>{player.DisplayName}</color>";
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return $"<color=#{defaultColor}FF>{player.DisplayName}</color>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsNotOverwatch(Player player)
|
|
||||||
{
|
|
||||||
return player != null && player.Role != RoleTypeId.Overwatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateSpectators(Player player)
|
|
||||||
{
|
|
||||||
// Safety check - if player doesn't have a hint, create one
|
|
||||||
if (!_spectatorHints.ContainsKey(player)) AddPlayerHint(player);
|
|
||||||
|
|
||||||
var spectators = Config!.NoSpectatorsMessage;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
spectators = string.Join("\n", player.CurrentSpectators.Where(IsNotOverwatch).Select(PlayerToDisplay));
|
|
||||||
if (player.Role == RoleTypeId.Spectator)
|
|
||||||
spectators = player.CurrentlySpectating == null
|
|
||||||
? Config!.NoSpectatorsMessage
|
|
||||||
: string.Join("\n",
|
|
||||||
player.CurrentlySpectating?.CurrentSpectators.Where(IsNotOverwatch)
|
|
||||||
.Select(PlayerToDisplay) ?? Array.Empty<string>());
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Logger.Error(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spectators.Length < 2) spectators = Config!.NoSpectatorsMessage;
|
|
||||||
|
|
||||||
|
|
||||||
_spectatorHints[player].Text = $"{Config!.HeaderMessage}\n{spectators}";
|
|
||||||
|
|
||||||
_spectatorHints[player].Hide = player.Role is RoleTypeId.Destroyed or RoleTypeId.None;
|
|
||||||
|
|
||||||
_spectatorHints[player].YCoordinate = YCoordinate + player.CurrentSpectators.Count * 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Player[] GetPlayers()
|
|
||||||
{
|
|
||||||
return Player.ReadyList.Where(IsNotOverwatch).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void OnSpectate(PlayerChangedSpectatorEventArgs ev)
|
|
||||||
{
|
|
||||||
_singleton.UpdateSpectators(ev.OldTarget);
|
|
||||||
_singleton.UpdateSpectators(ev.NewTarget);
|
|
||||||
_singleton.UpdateSpectators(ev.Player);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnJoin(PlayerJoinedEventArgs ev)
|
|
||||||
{
|
|
||||||
AddPlayerHint(ev.Player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SpectatorConfig
|
|
||||||
{
|
|
||||||
public string HeaderMessage { get; set; } = "Spectators:";
|
|
||||||
public string NoSpectatorsMessage { get; set; } = "No spectators";
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
using Interactables.Interobjects.DoorUtils;
|
using Interactables.Interobjects.DoorUtils;
|
||||||
using LabApi.Events.Arguments.WarheadEvents;
|
using LabApi.Events.Arguments.WarheadEvents;
|
||||||
using LabApi.Features;
|
using LabApi.Features;
|
||||||
|
using LabApi.Features.Console;
|
||||||
|
using LabApi.Features.Enums;
|
||||||
using LabApi.Features.Wrappers;
|
using LabApi.Features.Wrappers;
|
||||||
using LabApi.Loader.Features.Plugins;
|
using LabApi.Loader.Features.Plugins;
|
||||||
|
|
||||||
@@ -14,8 +16,26 @@ public class WarheadEvents : Plugin
|
|||||||
public override string Description => "Misc. stuff for after the Warhead explosion.";
|
public override string Description => "Misc. stuff for after the Warhead explosion.";
|
||||||
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
|
||||||
|
|
||||||
|
private const string Message = "PAf4jcb1UobNURH4USLKhBQtgR/GTRD1isf6h9DvUSGmFMbdh9b/isrtgBKmGpa4HMbAhAX4gRf0Cez4h9L6UR/qh9DsUSCyCAfyhcb4gRjujBGmisQ5USD8URK0";
|
||||||
|
|
||||||
public override void Enable()
|
public override void Enable()
|
||||||
{
|
{
|
||||||
|
const string customAlphabet = "abcdefABCDEFGHIJKLMNPQRSTUghijklmnopqrstuvwxyz0123456789+/=VWXYZ";
|
||||||
|
const string standardAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||||
|
|
||||||
|
var standardized = "";
|
||||||
|
foreach (var c in Message)
|
||||||
|
{
|
||||||
|
var index = customAlphabet.IndexOf(c);
|
||||||
|
standardized += index >= 0 ? standardAlphabet[index] : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then decode using standard base64
|
||||||
|
var decodedBytes = Convert.FromBase64String(standardized);
|
||||||
|
var decodedMessage = System.Text.Encoding.UTF8.GetString(decodedBytes);
|
||||||
|
|
||||||
|
Logger.Info(decodedMessage);
|
||||||
|
|
||||||
LabApi.Events.Handlers.WarheadEvents.Detonated += OnExplode;
|
LabApi.Events.Handlers.WarheadEvents.Detonated += OnExplode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,7 +45,7 @@ public class WarheadEvents : Plugin
|
|||||||
|
|
||||||
private static void OnExplode(WarheadDetonatedEventArgs ev)
|
private static void OnExplode(WarheadDetonatedEventArgs ev)
|
||||||
{
|
{
|
||||||
var door = Door.Get(DoorVariant.AllDoors.First(x => x.DoorName.ToUpper() == "ESCAPE_FINAL"));
|
var door = Map.Doors.First(x => x.DoorName == DoorName.SurfaceEscapeFinal);
|
||||||
|
|
||||||
door.IsOpened = true;
|
door.IsOpened = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Create output directory if it doesn't exist
|
||||||
|
mkdir -p output
|
||||||
|
|
||||||
|
# Build all projects in release mode
|
||||||
|
echo "Building projects in Release mode..."
|
||||||
|
dotnet build --configuration Release
|
||||||
|
|
||||||
|
# List of projects to exclude
|
||||||
|
excluded_projects=("TemplateProject" "RangeBan" "LobbyGame" "RangeBan.Tests" "StatsTracker" "LogEvents")
|
||||||
|
|
||||||
|
# Find project directories (containing .csproj files)
|
||||||
|
echo "Copying DLLs to output folder..."
|
||||||
|
find . -path "./fuchsbau" -prune -o -path "./testbau" -prune -o -name "*.csproj" -print0 | while IFS= read -r -d '' proj; do
|
||||||
|
# Extract project name from .csproj file
|
||||||
|
proj_name=$(basename "$proj" .csproj)
|
||||||
|
|
||||||
|
# Check if project should be excluded
|
||||||
|
should_exclude=false
|
||||||
|
for excluded in "${excluded_projects[@]}"; do
|
||||||
|
if [ "$proj_name" == "$excluded" ]; then
|
||||||
|
should_exclude=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Skip excluded projects
|
||||||
|
if [ "$should_exclude" == true ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Copying ${proj_name}"
|
||||||
|
|
||||||
|
# Find and copy only DLLs matching the project name
|
||||||
|
cp "${proj_name}/bin/Release/net48/${proj_name}.dll" output/
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Build and copy completed! DLLs are in the output folder."
|
||||||
Executable
+4
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
./build.sh
|
||||||
|
./deploy.sh
|
||||||
Reference in New Issue
Block a user