namespace MinecraftClient.Inventory;
using System;
using System.Collections.Generic;
using System.Linq;
using MinecraftClient.Protocol;
using MinecraftClient.Protocol.Message;
///
/// Represents an active status effect on an entity
///
public class EffectData
{
///
/// The type of effect
///
public Effects Effect { get; set; }
///
/// Effect amplifier (level - 1, e.g., 0 = level I, 1 = level II)
///
public int Amplifier { get; set; }
///
/// Duration in ticks (20 ticks = 1 second). -1 for infinite.
///
public int Duration { get; set; }
///
/// Effect flags (ambient, show particles, show icon)
///
public byte Flags { get; set; }
///
/// Time when the effect was applied
///
public DateTime StartTime { get; set; }
public EffectData(Effects effect, int amplifier, int duration, byte flags)
{
Effect = effect;
Amplifier = amplifier;
Duration = duration;
Flags = flags;
StartTime = DateTime.UtcNow;
}
///
/// Check if this is an infinite duration effect
///
public bool IsInfinite => Duration == -1 || Duration == int.MaxValue;
///
/// Check if the effect has expired
///
public bool IsExpired
{
get
{
if (IsInfinite) return false;
return GetElapsedTicks() >= Duration;
}
}
///
/// Get remaining duration in ticks
///
public int RemainingTicks
{
get
{
if (IsInfinite) return -1;
return Math.Max(0, Duration - GetElapsedTicks());
}
}
///
/// Get remaining duration in seconds
///
public int RemainingSeconds
{
get
{
if (IsInfinite) return -1;
return (RemainingTicks + 19) / 20;
}
}
///
/// Get the translated effect name from Minecraft translations
///
public string GetTranslatedName()
{
var key = $"effect.minecraft.{Effect.ToString().ToUnderscoreCase()}";
var translated = ChatParser.TranslateString(key);
return string.IsNullOrEmpty(translated) ? Effect.ToString() : translated;
}
///
/// Get the translated effect name with level when applicable
///
public string GetDisplayName()
{
string translatedName = GetTranslatedName();
if (Amplifier <= 0)
return translatedName;
return string.Format(Translations.effect_name_with_amplifier, translatedName,
EnchantmentMapping.ConvertLevelToRomanNumbers(Amplifier + 1));
}
///
/// Get the translated effect name prefixed with the best-fit indefinite article
///
public string GetDisplayNameWithArticle()
{
string displayName = GetDisplayName();
char? firstLetter = displayName
.TrimStart()
.FirstOrDefault(char.IsLetter);
if (firstLetter is null)
return displayName;
string article = "AEIOUaeiou".Contains(firstLetter.Value)
? Translations.effect_article_an
: Translations.effect_article_a;
return $"{article} {displayName}";
}
///
/// Get the configured short duration label for the remaining time
///
public string GetRemainingDurationText()
{
return FormatShortDuration(RemainingSeconds);
}
///
/// Get the configured short duration label for the initial effect duration
///
public string GetInitialDurationText()
{
if (IsInfinite)
return Translations.effect_duration_unlimited;
int durationSeconds = (Duration + 19) / 20;
return FormatShortDuration(durationSeconds);
}
///
/// Format a duration for compact UI output
///
/// Duration in seconds, -1 for unlimited
public static string FormatShortDuration(int seconds)
{
if (seconds < 0)
return Translations.effect_duration_short_unlimited;
if (seconds < 60)
return string.Format(Translations.effect_duration_short_seconds, seconds);
int minutes = seconds / 60;
int remainingSeconds = seconds % 60;
if (seconds < 3600)
{
return remainingSeconds == 0
? string.Format(Translations.effect_duration_short_minutes, minutes)
: string.Format(Translations.effect_duration_short_minutes_seconds, minutes, remainingSeconds);
}
int hours = seconds / 3600;
int remainingMinutes = (seconds % 3600) / 60;
return remainingMinutes == 0
? string.Format(Translations.effect_duration_short_hours, hours)
: string.Format(Translations.effect_duration_short_hours_minutes, hours, remainingMinutes);
}
private int GetElapsedTicks()
{
return (int)((DateTime.UtcNow - StartTime).TotalMilliseconds / 50);
}
}
///
/// Extension method for converting PascalCase to snake_case
///
public static class StringExtensions
{
public static string ToUnderscoreCase(this string str)
{
return string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString())).ToLower();
}
}