Files
2025-03-31 13:19:47 +02:00

324 lines
10 KiB
C#

using Barotrauma.Items.Components;
using Barotrauma;
using HarmonyLib;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;
using System;
using System.ComponentModel;
using Barotrauma.Networking;
using System.Linq;
namespace BaroMod_sjx
{
partial class ItemBoxImpl
{
[HarmonyPatch(typeof(Inventory), nameof(Inventory.DrawSlot))]
class Patch_DrawSlot
{
public class context
{
public SpriteBatch spriteBatch;
public Inventory inventory;
public Sprite? indicatorSprite;
public Sprite? emptyIndicatorSprite;
public Sprite? itemSprite;
public Rectangle conditionIndicatorArea;
public int max_value;
public int cur_value;
public Vector2 sprite_pos;
public float sprite_scale;
public float rotation;
public Color spriteColor;
public context(SpriteBatch sb, Inventory inv, Sprite? full, Sprite? empty, Sprite? item, Rectangle area, int max, int cur, Vector2 sp, float ss, float rot, Color sc)
{
spriteBatch = sb;
inventory = inv;
indicatorSprite = full;
emptyIndicatorSprite = empty;
itemSprite = item;
conditionIndicatorArea = area;
max_value = max;
cur_value = cur;
sprite_pos = sp;
sprite_scale = ss;
rotation = rot;
spriteColor = sc;
}
}
static private void Invoke_DrawItemStateIndicator(
SpriteBatch spriteBatch, Inventory inventory,
Sprite indicatorSprite, Sprite emptyIndicatorSprite, Rectangle containedIndicatorArea, float containedState,
bool pulsate = false)
{
AccessTools.Method(typeof(Inventory), "DrawItemStateIndicator")!
.Invoke(null, new object[] { spriteBatch, inventory, indicatorSprite, emptyIndicatorSprite, containedIndicatorArea, containedState, pulsate });
}
private static Sprite? GetTargetSprite(ConditionStorage conditionStorage, Inventory iv)
{
Inventory.ItemSlot target_slot;
{
Inventory.ItemSlot[] slots = (AccessTools.Field(typeof(Inventory), "slots").GetValue(iv)! as Inventory.ItemSlot[])!;
if (conditionStorage.slotIndex >= slots.Length)
{
DebugConsole.LogError($"ConditionStorage of {(iv.Owner as Item)!.Prefab.Identifier} specified index {conditionStorage.slotIndex} out of {slots.Length}!");
return null;
}
target_slot = slots[conditionStorage.slotIndex];
}
if (target_slot.Any())
{
Item i = target_slot.First();
return i.Prefab.InventoryIcon ?? i.Sprite;
}
else
{
return null;
}
}
public static bool Prefix(out context? __state,
SpriteBatch spriteBatch, Inventory inventory, VisualSlot slot, Item item, int slotIndex)
{
if (inventory != null && item != null && get_componentsByType(item).TryGetValue(typeof(ConditionStorage), out List<ItemComponent>? comps))
{
ConditionStorage conditionStorage = (comps.First() as ConditionStorage)!;
if (!conditionStorage.showIcon && !conditionStorage.showCount)
{
__state = null;
return true;
}
Rectangle rect = slot.Rect;
rect.Location += slot.DrawOffset.ToPoint();
if (slot.HighlightColor.A > 0)
{
float inflateAmount = (slot.HighlightColor.A / 255.0f) * slot.HighlightScaleUpAmount * 0.5f;
rect.Inflate(rect.Width * inflateAmount, rect.Height * inflateAmount);
}
var itemContainer = item.GetComponent<ItemContainer>();
Sprite? indicatorSprite;
Sprite? emptyIndicatorSprite;
Rectangle conditionIndicatorArea;
if (conditionStorage.showCount)
{
if (itemContainer is null)
{
DebugConsole.LogError($"Item {item.Prefab.Identifier} has ConditionStorage but no ItemContainer!");
__state = null;
return true;
}
if (itemContainer.InventoryTopSprite != null || itemContainer.InventoryBottomSprite != null)
{
__state = null;
return true;
}
int dir = slot.SubInventoryDir;
if (itemContainer.ShowContainedStateIndicator)
{
conditionIndicatorArea = new Rectangle(rect.X, rect.Bottom - (int)(10 * GUI.Scale), rect.Width, (int)(10 * GUI.Scale));
}
else
{
conditionIndicatorArea = new Rectangle(
rect.X, dir < 0 ? rect.Bottom + HUDLayoutSettings.Padding / 2 : rect.Y - HUDLayoutSettings.Padding / 2 - Inventory.ContainedIndicatorHeight,
rect.Width, Inventory.ContainedIndicatorHeight);
conditionIndicatorArea.Inflate(-4, 0);
}
GUIComponentStyle indicatorStyle = GUIStyle.GetComponentStyle("ContainedStateIndicator.Default")!;
indicatorSprite = indicatorStyle.GetDefaultSprite();
emptyIndicatorSprite = indicatorStyle.GetSprite(GUIComponent.ComponentState.Hover);
}
else
{
indicatorSprite = null;
emptyIndicatorSprite = null;
conditionIndicatorArea = new Rectangle();
}
Vector2 itemPos;
float scale;
float rotation;
Sprite? item_sprite;
Color spriteColor;
if (conditionStorage.showIcon)
{
item_sprite = GetTargetSprite(conditionStorage, itemContainer.Inventory!);
if (item_sprite != null)
{
scale = Math.Min(Math.Min((rect.Width - 10) / item_sprite.size.X, (rect.Height - 10) / item_sprite.size.Y), 2.0f);
itemPos = rect.Center.ToVector2();
if (itemPos.Y > GameMain.GraphicsHeight)
{
itemPos.Y -= Math.Min(
(itemPos.Y + item_sprite.size.Y / 2 * scale) - GameMain.GraphicsHeight,
(itemPos.Y - item_sprite.size.Y / 2 * scale) - rect.Y);
}
rotation = 0.0f;
if (slot.HighlightColor.A > 0)
{
rotation = (float)Math.Sin(slot.HighlightTimer * MathHelper.TwoPi) * slot.HighlightTimer * 0.3f;
}
spriteColor = item_sprite == item.Sprite ? item.GetSpriteColor() : item.GetInventoryIconColor();
}
else
{
scale = 1.0f;
rotation = 0.0f;
spriteColor = Color.White;
}
}
else
{
item_sprite = null;
scale = 1.0f;
rotation = 0.0f;
spriteColor = Color.White;
}
Vector2 center = rect.Center.ToVector2() + (new Vector2(conditionStorage.iconShiftX, conditionStorage.iconShiftY)) * slot.Rect.Size.ToVector2() * 0.5f;
__state = new context(spriteBatch, inventory, indicatorSprite, emptyIndicatorSprite, item_sprite,
conditionIndicatorArea, conditionStorage.maxItemCount, conditionStorage.currentItemCount, center,
scale * conditionStorage.iconScale, rotation, spriteColor);
}
else
{
__state = null;
}
return true;
}
public static void Postfix(context? __state)
{
if (__state != null)
{
__state.itemSprite?.Draw(__state.spriteBatch, __state.sprite_pos, __state.spriteColor, __state.rotation, __state.sprite_scale);
if (__state.indicatorSprite != null && __state.emptyIndicatorSprite != null)
{
Invoke_DrawItemStateIndicator(__state.spriteBatch, __state.inventory, __state.indicatorSprite, __state.emptyIndicatorSprite, __state.conditionIndicatorArea,
__state.cur_value / (float)__state.max_value);
string info_text = $"{__state.cur_value}/{__state.max_value}";
float text_scale = 0.75f;
Vector2 info_size = GUIStyle.SmallFont.MeasureString(info_text) * text_scale;
GUIStyle.SmallFont.DrawString(__state.spriteBatch, info_text, __state.conditionIndicatorArea.Center.ToVector2() - (info_size * 0.5f), Color.White, 0.0f, Vector2.Zero, text_scale, SpriteEffects.None, 0.0f);
}
}
}
}
}
partial class ConditionStorage : ItemComponent, IServerSerializable
{
private CoroutineHandle? resetPredictionCoroutine = null;
private int? last_server_update_count = null;
private float resetPredictionTimer = 1.0f;
float last_update_time = 0;
const double remove_time = 1.0;
class ItemStackedInfo
{
public Item target;
public Inventory removed_from;
public Character user;
public double timestamp;
public int slot;
public ItemStackedInfo(Item item, Character character, Inventory removedFrom, int from_slot)
{
target = item;
removed_from = removedFrom;
user = character;
slot = from_slot;
timestamp = Timing.TotalTime;
}
}
// keep a list of items removed on client side so that they can be put back into container after timeour
private List<ItemStackedInfo> removed = new List<ItemStackedInfo>();
void RemoveItem_track(Item item, Character user, Inventory removedFrom, int slot)
{
removed.Add(new ItemStackedInfo(item, user, removedFrom, slot));
}
void UpdateRemovedItems()
{
var copy = removed.CreateCopy();
foreach (var item in copy)
{
// updated from server to be removed
if (item.target.Removed)
{
removed.Remove(item);
}
// timeout for removed item. put it back.
else if (Timing.TotalTime - item.timestamp > remove_time)
{
if (!item.removed_from.TryPutItem(item.target, item.slot, false, false, item.user, false, false))
{
item.target.Drop(item.user, true, true);
}
removed.Remove(item);
}
}
if (removed.Any())
{
IsActive = true;
}
}
partial void OnCountPredictionChanged()
{
if (GameMain.Client == null || !last_server_update_count.HasValue) { return; }
if (resetPredictionCoroutine == null || !CoroutineManager.IsCoroutineRunning(resetPredictionCoroutine))
{
resetPredictionCoroutine = CoroutineManager.StartCoroutine(ResetPredictionAfterDelay());
}
}
private IEnumerable<CoroutineStatus> ResetPredictionAfterDelay()
{
while (resetPredictionTimer > 0.0f)
{
resetPredictionTimer -= CoroutineManager.DeltaTime;
yield return CoroutineStatus.Running;
}
if (last_server_update_count.HasValue) { SetItemCount(last_server_update_count.Value, false); }
resetPredictionCoroutine = null;
yield return CoroutineStatus.Success;
}
public void ClientEventRead(IReadMessage msg, float sendingTime)
{
if (last_update_time <= sendingTime)
{
last_update_time = sendingTime;
last_server_update_count = msg.ReadRangedInteger(0, maxItemCount);
SetItemCount(last_server_update_count.Value, true);
}
else
{
// discard the number, but still extract it from stream.
msg.ReadRangedInteger(0, maxItemCount);
}
}
}
}