Skip to content

Commit df9e91d

Browse files
committed
Extended the CustomItems API with CustomUsables
1 parent 47409d4 commit df9e91d

4 files changed

Lines changed: 301 additions & 0 deletions

File tree

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
using LabExtended.API.CustomItems;
2+
using LabExtended.API.CustomItems.Behaviours;
3+
4+
using UnityEngine;
5+
6+
namespace LabExtended.API.CustomUsables.Behaviours;
7+
8+
/// <summary>
9+
/// Inventory behaviour of custom usable items.
10+
/// </summary>
11+
public class CustomUsableInventoryBehaviour : CustomItemInventoryBehaviour
12+
{
13+
/// <summary>
14+
/// Gets the last use time of this item (or null if not used).
15+
/// <remarks>Returns time since startup (in seconds) - <see cref="UnityEngine.Time.realtimeSinceStartup"/></remarks>
16+
/// </summary>
17+
public float? LastUse { get; internal set; }
18+
19+
/// <summary>
20+
/// Gets the amount of seconds that have passed since the player last used the item (or null if the item was not used).
21+
/// </summary>
22+
public float? TimeSinceLastUse
23+
{
24+
get
25+
{
26+
if (!LastUse.HasValue)
27+
return null;
28+
29+
return Time.realtimeSinceStartup - LastUse.Value;
30+
}
31+
}
32+
33+
/// <summary>
34+
/// Gets the item's remaining use duration (in seconds).
35+
/// </summary>
36+
public float RemainingUse { get; internal set; }
37+
38+
/// <summary>
39+
/// Gets the item's remaining cooldown (in seconds).
40+
/// </summary>
41+
public float RemainingCooldown { get; internal set; }
42+
43+
/// <summary>
44+
/// Gets or sets the item's use duration (in seconds).
45+
/// </summary>
46+
public float UseDuration { get; set; }
47+
48+
/// <summary>
49+
/// Gets or sets the item's cooldown after use (in seconds).
50+
/// </summary>
51+
public float CooldownDuration { get; set; }
52+
53+
/// <summary>
54+
/// Whether or not the item is being used.
55+
/// </summary>
56+
public bool IsUsing { get; internal set; }
57+
58+
/// <summary>
59+
/// Whether or not the item is ready to be used.
60+
/// </summary>
61+
public bool IsReady
62+
{
63+
get
64+
{
65+
if (IsUsing)
66+
return false;
67+
68+
if (RemainingCooldown > 0f)
69+
return false;
70+
71+
return true;
72+
}
73+
}
74+
75+
/// <summary>
76+
/// Gets called when the player starts using the item.
77+
/// <param name="duration">The use duration for the player (in seconds).</param>
78+
/// </summary>
79+
/// <returns>true if the player should be allowed to use the item</returns>
80+
public virtual bool OnUsing(ref float duration) => true;
81+
82+
/// <summary>
83+
/// Gets called when the player finishes using the item.
84+
/// </summary>
85+
/// <param name="cooldown">The cooldown duration for the player (in seconds).</param>
86+
public virtual void OnUsed(ref float cooldown) { }
87+
88+
/// <summary>
89+
/// Gets called before the player cancels the item's usage.
90+
/// </summary>
91+
/// <param name="byUser">true if the usage was cancelled by the player, false if it was cancelled due to the player's held item changing</param>
92+
/// <returns>true if the player should be allowed to cancel the item's usage</returns>
93+
public virtual bool OnCancelling(bool byUser) => true;
94+
95+
/// <summary>
96+
/// Gets called when the player cancels the item's usage.
97+
/// </summary>
98+
/// <param name="cooldown">The cooldown duration for the player (in seconds).</param>
99+
/// <param name="byUser">true if the usage was cancelled by the player, false if it was cancelled due to the player's held item changing</param>
100+
public virtual void OnCancelled(ref float cooldown, bool byUser) { }
101+
102+
/// <summary>
103+
/// Gets called when the player's item usage cooldown expires.
104+
/// </summary>
105+
public virtual void OnCooldownOver() { }
106+
107+
/// <inheritdoc cref="CustomItemBehaviour.OnUpdate"/>
108+
public override void OnUpdate()
109+
{
110+
base.OnUpdate();
111+
112+
if (IsUsing)
113+
{
114+
if (!IsSelected)
115+
{
116+
InternalCancel(false);
117+
}
118+
else
119+
{
120+
RemainingUse -= Time.deltaTime;
121+
122+
if (RemainingUse > 0f)
123+
return;
124+
125+
var cooldown = CooldownDuration;
126+
127+
OnUsed(ref cooldown);
128+
129+
LastUse = Time.realtimeSinceStartup;
130+
131+
RemainingCooldown = cooldown;
132+
RemainingUse = 0f;
133+
134+
IsUsing = false;
135+
}
136+
}
137+
else
138+
{
139+
if (RemainingCooldown != 0f)
140+
{
141+
RemainingCooldown -= Time.deltaTime;
142+
143+
if (RemainingCooldown > 0f)
144+
return;
145+
146+
RemainingCooldown = 0f;
147+
148+
OnCooldownOver();
149+
}
150+
}
151+
}
152+
153+
internal void InternalStart(float duration)
154+
{
155+
RemainingCooldown = 0f;
156+
RemainingUse = duration;
157+
158+
IsUsing = true;
159+
}
160+
161+
internal void InternalCancel(bool byUser)
162+
{
163+
if (OnCancelling(byUser))
164+
{
165+
var cooldown = CooldownDuration;
166+
167+
OnCancelled(ref cooldown, byUser);
168+
169+
RemainingCooldown = cooldown;
170+
RemainingUse = 0f;
171+
172+
IsUsing = false;
173+
174+
LastUse = Time.realtimeSinceStartup;
175+
}
176+
}
177+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using InventorySystem.Items.Usables;
2+
3+
using LabExtended.API.CustomItems;
4+
using LabExtended.API.CustomItems.Behaviours;
5+
6+
using LabExtended.API.CustomUsables.Behaviours;
7+
using LabExtended.API.CustomUsables.Properties;
8+
9+
namespace LabExtended.API.CustomUsables;
10+
11+
/// <summary>
12+
/// Custom Item handler targeting usable items.
13+
/// </summary>
14+
public abstract class CustomUsableHandler : CustomItemHandler
15+
{
16+
/// <inheritdoc cref="CustomItemHandler.InventoryProperties"/>
17+
public CustomUsableInventoryProperties UsableInventoryProperties => InventoryProperties as CustomUsableInventoryProperties;
18+
19+
internal override void InternalInitializeItem(CustomItemInventoryBehaviour item, CustomItemPickupBehaviour? pickup)
20+
{
21+
base.InternalInitializeItem(item, pickup);
22+
23+
if (UsableInventoryProperties != null && item.Item is UsableItem usableItem)
24+
{
25+
if (UsableInventoryProperties.UseDuration == -1f)
26+
UsableInventoryProperties.UseDuration = usableItem.UseTime;
27+
28+
if (item is CustomUsableInventoryBehaviour usableInventory)
29+
{
30+
usableInventory.UseDuration = UsableInventoryProperties.UseDuration;
31+
usableInventory.CooldownDuration = UsableInventoryProperties.CooldownDuration;
32+
}
33+
}
34+
}
35+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using LabExtended.API.CustomItems.Properties;
2+
3+
namespace LabExtended.API.CustomUsables.Properties;
4+
5+
/// <summary>
6+
/// Inventory properties of custom usable items.
7+
/// </summary>
8+
public class CustomUsableInventoryProperties : CustomItemInventoryProperties
9+
{
10+
/// <summary>
11+
/// Gets or sets the item's use duration (in seconds).
12+
/// <remarks>Setting this to -1 will force the use of base-game use duration.</remarks>
13+
/// </summary>
14+
public float UseDuration { get; set; } = 5f;
15+
16+
/// <summary>
17+
/// Gets or sets the item's cooldown duration (in seconds).
18+
/// </summary>
19+
public float CooldownDuration { get; set; } = 2f;
20+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using HarmonyLib;
2+
using InventorySystem.Items.Usables;
3+
4+
using LabExtended.API;
5+
6+
using LabExtended.API.CustomItems;
7+
using LabExtended.API.CustomUsables.Behaviours;
8+
9+
using Mirror;
10+
using Utils.Networking;
11+
12+
namespace LabExtended.Patches.Functions.Items;
13+
14+
/// <summary>
15+
/// Implements use logic for custom usable items.
16+
/// </summary>
17+
public static class UsableItemStatusPatch
18+
{
19+
[HarmonyPatch(typeof(UsableItemsController), nameof(UsableItemsController.ServerReceivedStatus))]
20+
private static bool Prefix(NetworkConnectionToClient conn, StatusMessage msg)
21+
{
22+
if (!ExPlayer.TryGet(conn, out var player))
23+
return true;
24+
25+
if (player.Inventory.CurrentItem is not UsableItem usableItem
26+
|| usableItem.ItemSerial != msg.ItemSerial
27+
|| !CustomItemUtils.TryGetBehaviour<CustomUsableInventoryBehaviour>(usableItem.ItemSerial,
28+
out var behaviour))
29+
return true;
30+
31+
if (msg.Status is StatusMessage.StatusType.Start)
32+
{
33+
if (behaviour.RemainingCooldown > 0f)
34+
{
35+
conn.Send(new ItemCooldownMessage(msg.ItemSerial, behaviour.RemainingCooldown));
36+
return false;
37+
}
38+
39+
if (!behaviour.IsReady)
40+
return false;
41+
42+
var duration = behaviour.UseDuration;
43+
44+
behaviour.IsSelected = true;
45+
46+
if (!behaviour.OnUsing(ref duration))
47+
return false;
48+
49+
behaviour.InternalStart(duration);
50+
51+
usableItem.OnUsingStarted();
52+
53+
msg.SendToAuthenticated();
54+
}
55+
else
56+
{
57+
if (behaviour.IsUsing)
58+
{
59+
behaviour.InternalCancel(true);
60+
61+
usableItem.OnUsingCancelled();
62+
63+
msg.SendToAuthenticated();
64+
}
65+
}
66+
67+
return false;
68+
}
69+
}

0 commit comments

Comments
 (0)