Skip to content

Commit

Permalink
Snow pile (Exiled-Team#2346)
Browse files Browse the repository at this point in the history
* snow pile

* list fix

---------

Co-authored-by: Yamato <[email protected]>
  • Loading branch information
VALERA771 and louis1706 authored Dec 22, 2023
1 parent 9c3973f commit 4902db1
Show file tree
Hide file tree
Showing 7 changed files with 297 additions and 1 deletion.
2 changes: 2 additions & 0 deletions Exiled.API/Features/Map.cs
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,8 @@ internal static void ClearCache()

Ragdoll.BasicRagdollToRagdoll.Clear();

Snowpile.BaseToWrapper.Clear();

Firearm.ItemTypeToFirearmInstance.Clear();
Firearm.BaseCodesValue.Clear();
Firearm.AvailableAttachmentsValue.Clear();
Expand Down
2 changes: 1 addition & 1 deletion Exiled.API/Features/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1164,7 +1164,7 @@ public bool IsSpawnProtected
/// Gets a <see cref="IEnumerable{T}"/> of <see cref="Player"/> filtered based on a predicate.
/// </summary>
/// <param name="predicate">The condition to satisfy.</param>
/// <returns>A <see cref="IEnumerable{T}"/> of <see cref="Player"/> which contains elements that satify the condition.</returns>
/// <returns>A <see cref="IEnumerable{T}"/> of <see cref="Player"/> which contains elements that satisfy the condition.</returns>
public static IEnumerable<Player> Get(Func<Player, bool> predicate) => List.Where(predicate);

/// <summary>
Expand Down
121 changes: 121 additions & 0 deletions Exiled.API/Features/Snowpile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// -----------------------------------------------------------------------
// <copyright file="Snowpile.cs" company="Exiled Team">
// Copyright (c) Exiled Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

namespace Exiled.API.Features
{
using System;
using System.Collections.Generic;
using System.Linq;

using Exiled.API.Interfaces;
using UnityEngine;

using Basepile = global::Snowpile;

/// <summary>
/// A wrapper for <see cref="Basepile"/>.
/// </summary>
public class Snowpile : IWorldSpace, IWrapper<Basepile>
{
/// <summary>
/// A <see cref="Dictionary{TKey,TValue}"/> with <see cref="Basepile"/> to <see cref="Snowpile"/>.
/// </summary>
internal static readonly Dictionary<Basepile, Snowpile> BaseToWrapper = new();

/// <summary>
/// Initializes a new instance of the <see cref="Snowpile"/> class.
/// </summary>
/// <param name="snowpile">The <see cref="Basepile"/> instance.</param>
public Snowpile(Basepile snowpile)
{
Base = snowpile;

BaseToWrapper.Add(snowpile, this);
}

/// <summary>
/// Gets the list of all snowpiles.
/// </summary>
public static IReadOnlyCollection<Snowpile> List => BaseToWrapper.Values;

/// <inheritdoc/>
public Basepile Base { get; }

/// <inheritdoc/>
public Vector3 Position
{
get => Base._position;
set => Base.Network_position = value;
}

/// <inheritdoc/>
public Quaternion Rotation
{
get => Base._rotation;
set => Base.Network_rotation = value;
}

/// <summary>
/// Gets or sets amount of uses that this snowpile can handle.
/// </summary>
public int RemainingUses
{
get => Base._remainingUses;
set => Base.Network_remainingUses = value;
}

/// <summary>
/// Gets or sets a value indicating whether or not this snowpile should regenerate amount of usages.
/// </summary>
public bool Regenerate
{
get => Base._regenerate;
set => Base._regenerate = value;
}

/// <summary>
/// Gets or sets a current timer of regeneration.
/// </summary>
public float RegenerationTimer
{
get => Base._regenerationTimer;
set => Base._regenerationTimer = value;
}

/// <summary>
/// Gets or sets time how much snowpile should regenerate 1 snow ball.
/// </summary>
public float RegenerationDuration
{
get => Base._regenerationDuration;
set => Base._regenerationDuration = value;
}

/// <summary>
/// Gets or sets maximal amount of uses that this snowpile can has..
/// </summary>
public int InitUses
{
get => Base._initialUses;
set => Base._initialUses = value;
}

/// <summary>
/// Gets a <see cref="Snowpile"/> which is connected to it's base game analog.
/// </summary>
/// <param name="snowpile">Base game analog.</param>
/// <returns>A <see cref="Snowpile"/> instance or <see langword="null"/>.</returns>
public static Snowpile Get(Basepile snowpile) => BaseToWrapper.TryGetValue(snowpile, out Snowpile pile) ? pile : new Snowpile(snowpile);

/// <summary>
/// Gets a <see cref="IEnumerable{T}"/> of <see cref="Snowpile"/> filtered based on a predicate.
/// </summary>
/// <param name="predicate">The condition to satisfy.</param>
/// <returns>A <see cref="IEnumerable{T}"/> of <see cref="Player"/> which contains elements that satisfy the condition.</returns>
public static IEnumerable<Snowpile> Get(Func<Snowpile, bool> predicate) => List.Where(predicate);
}
}
47 changes: 47 additions & 0 deletions Exiled.Events/EventArgs/Player/InteractingSnowpileEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// -----------------------------------------------------------------------
// <copyright file="InteractingSnowpileEventArgs.cs" company="Exiled Team">
// Copyright (c) Exiled Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

namespace Exiled.Events.EventArgs.Player
{
using Exiled.API.Features;
using Exiled.Events.EventArgs.Interfaces;

/// <summary>
/// Contains all information before a <see cref="API.Features.Player"/> is interacting with snowpile.
/// </summary>
public class InteractingSnowpileEventArgs : IPlayerEvent, IDeniableEvent
{
/// <summary>
/// Initializes a new instance of the <see cref="InteractingSnowpileEventArgs"/> class.
/// </summary>
/// <param name="player"><inheritdoc cref="Player"/></param>
/// <param name="snowpile"><inheritdoc cref="Snowpile"/></param>
/// <param name="isAllowed"><inheritdoc cref="IsAllowed"/></param>
public InteractingSnowpileEventArgs(Player player, Snowpile snowpile, bool isAllowed = true)
{
Player = player;
Snowpile = snowpile;
IsAllowed = isAllowed;
}

/// <summary>
/// Gets a snowpile with which player is interacting.
/// </summary>
public Snowpile Snowpile { get; }

/// <inheritdoc/>
public Player Player { get; }

/// <inheritdoc/>
public bool IsAllowed { get; set; }

/// <summary>
/// Gets or sets a value indicating whether or not should be checked if player already has a snowball.
/// </summary>
public bool ShouldCheck { get; set; } = true;
}
}
11 changes: 11 additions & 0 deletions Exiled.Events/Handlers/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,11 @@ public class Player
/// </summary>
public static Event<ChangingNicknameEventArgs> ChangingNickname { get; set; } = new();

/// <summary>
/// Invoked before a <see cref="API.Features.Player"/> is interacting with snowpile.
/// </summary>
public static Event<InteractingSnowpileEventArgs> InteractingSnowpile { get; set; } = new();

/// <summary>
/// Called before reserved slot is resolved for a <see cref="API.Features.Player"/>.
/// </summary>
Expand Down Expand Up @@ -1090,6 +1095,12 @@ public static void OnItemRemoved(ReferenceHub referenceHub, InventorySystem.Item
/// <param name="ev">The <see cref="ChangingNicknameEventArgs"/> instance.</param>
public static void OnChangingNickname(ChangingNicknameEventArgs ev) => ChangingNickname.InvokeSafely(ev);

/// <summary>
/// Called before a <see cref="API.Features.Player"/> is interacting with snowpile.
/// </summary>
/// <param name="ev">The <see cref="InteractingSnowpileEventArgs"/> instance.</param>
public static void OnInteractingSnowpile(InteractingSnowpileEventArgs ev) => InteractingSnowpile.InvokeSafely(ev);

/// <summary>
/// Called before pre-authenticating a <see cref="API.Features.Player"/>.
/// </summary>
Expand Down
90 changes: 90 additions & 0 deletions Exiled.Events/Patches/Events/Player/InteractingSnowpile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// -----------------------------------------------------------------------
// <copyright file="InteractingSnowpile.cs" company="Exiled Team">
// Copyright (c) Exiled Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

namespace Exiled.Events.Patches.Events.Player
{
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

using Exiled.API.Features;
using Exiled.API.Features.Pools;
using Exiled.Events.Attributes;
using Exiled.Events.EventArgs.Player;
using HarmonyLib;

using static HarmonyLib.AccessTools;

/// <summary>
/// Patches <see cref="global::Snowpile.ServerInteract"/>
/// to add <see cref="Handlers.Player.InteractingSnowpile"/> event.
/// </summary>
[EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.InteractingSnowpile))]
[HarmonyPatch(typeof(global::Snowpile), nameof(global::Snowpile.ServerInteract))]
internal class InteractingSnowpile
{
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
List<CodeInstruction> newInstructions = ListPool<CodeInstruction>.Pool.Get(instructions);

int offset = -1;
int index = newInstructions.FindLastIndex(x => x.operand is FieldInfo info && info == Field(typeof(ReferenceHub), nameof(ReferenceHub.inventory))) + offset;

Label skipCheckLabel = generator.DefineLabel();
Label retLabel = generator.DefineLabel();

newInstructions[index].labels.Add(skipCheckLabel);
newInstructions[newInstructions.Count - 1].labels.Add(retLabel);

LocalBuilder ev = generator.DeclareLocal(typeof(InteractingSnowpileEventArgs));

offset = 1;
index = newInstructions.FindIndex(x => x.opcode == OpCodes.Ret) + offset;

newInstructions.InsertRange(
index,
new[]
{
// Player.Get(hub);
new CodeInstruction(OpCodes.Ldarg_1).MoveLabelsFrom(newInstructions[index]),
new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),

// Snowpile.Get(this);
new(OpCodes.Ldarg_0),
new(OpCodes.Call, Method(typeof(Snowpile), nameof(Snowpile.Get), new[] { typeof(global::Snowpile) })),

// true
new(OpCodes.Ldc_I4_1),

// InteractingSnowpileEventArgs ev = new(Player, Snowpile, true);
new(OpCodes.Newobj, GetDeclaredConstructors(typeof(InteractingSnowpileEventArgs))[0]),
new(OpCodes.Dup),
new(OpCodes.Dup),
new(OpCodes.Stloc_S, ev.LocalIndex),

// Handlers.Player.OnInteractingSnowpile(ev);
new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnInteractingSnowpile))),

// if (!ev.IsAllowed)
// return;
new(OpCodes.Callvirt, PropertyGetter(typeof(InteractingSnowpileEventArgs), nameof(InteractingSnowpileEventArgs.IsAllowed))),
new(OpCodes.Brfalse_S, retLabel),

// if (!ev.ShouldCheck)
// goto skipCheckLabel;
new(OpCodes.Ldloc_S, ev.LocalIndex),
new(OpCodes.Callvirt, PropertyGetter(typeof(InteractingSnowpileEventArgs), nameof(InteractingSnowpileEventArgs.ShouldCheck))),
new(OpCodes.Brfalse_S, skipCheckLabel),
});

for (int z = 0; z < newInstructions.Count; z++)
yield return newInstructions[z];

ListPool<CodeInstruction>.Pool.Return(newInstructions);
}
}
}
25 changes: 25 additions & 0 deletions Exiled.Events/Patches/Generic/SnowpileListAdd.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// -----------------------------------------------------------------------
// <copyright file="SnowpileListAdd.cs" company="Exiled Team">
// Copyright (c) Exiled Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

namespace Exiled.Events.Patches.Generic
{
using HarmonyLib;

#pragma warning disable SA1313

/// <summary>
/// Patches <see cref="Snowpile.Awake"/> to control list.
/// </summary>
[HarmonyPatch(typeof(Snowpile), nameof(Snowpile.Awake))]
internal class SnowpileListAdd
{
private static void Postfix(Snowpile __instance)
{
_ = new Exiled.API.Features.Snowpile(__instance);
}
}
}

0 comments on commit 4902db1

Please sign in to comment.