using Terraria; using Terraria.ID; using TShockAPI; using TShockAPI.Hooks; using TerrariaApi.Server; using TerrariaHealingPlugin.Mappings; using TerrariaHealingPlugin.Models; namespace TerrariaHealingPlugin; [ApiVersion(2, 1)] public class HealingPlugin : TerrariaPlugin { public override string Author => "Sneefaria Maintainers"; public override string Name => "HealingPlugin"; public override string Description => "A healing plugin, duh"; public override Version Version => new Version(1, 0, 0); private DateTime _lastHealCheck = DateTime.UtcNow; private DateTime _lastBuffCheck = DateTime.UtcNow; private List _players = new List(); private HealingMappings _healingItemMappings = new HealingMappings(); private EquipMappings _equipMappings = new EquipMappings(); public HealingPlugin(Main game) : base(game) { } public override void Initialize() { TShock.Log.Info($"Initializing {Name} v{Version} by {Author}"); ServerApi.Hooks.ServerJoin.Register(this, OnServerJoin); ServerApi.Hooks.GameUpdate.Register(this, OnUpdate); ServerApi.Hooks.NetSendData.Register(this, OnSendNetData); ServerApi.Hooks.NetGetData.Register(this, OnGetNetData); GetDataHandlers.PlayerSlot.Register(OnPlayerSlot); // GetDataHandlers.PlayerHP.Register(OnPlayerHPChange); GetDataHandlers.PlayerBuff.Register(OnPlayerBuff); PlayerHooks.PlayerPostLogin += OnPlayerPostLogin; } protected override void Dispose(bool disposing) { if (disposing) { ServerApi.Hooks.ServerJoin.Deregister(this, OnServerJoin); // ServerApi.Hooks.GameUpdate.Deregister(this, OnUpdate); ServerApi.Hooks.NetSendData.Deregister(this, OnSendNetData); ServerApi.Hooks.NetGetData.Deregister(this, OnGetNetData); PlayerHooks.PlayerPostLogin -= OnPlayerPostLogin; } base.Dispose(disposing); } private void OnPlayerPostLogin(PlayerPostLoginEventArgs e) { ApplyRegenerationBuff(e.Player); } // FIXME: not at all complete, needs some work private void OnPlayerBuff(object sender, GetDataHandlers.PlayerBuffEventArgs args) { // Console.WriteLine($"Player buff arguments: {args}"); TSPlayer player = args.Player; int buffType = args.Type; Console.WriteLine($"Buff type: {buffType}"); bool isEquippableBuff = _equipMappings.mappings.ContainsKey(buffType); bool isHealingItemBuff = _healingItemMappings.mappings.ContainsKey(buffType); Console.WriteLine($"Player: {player.Name}, Buff Type: {buffType}, Is Equippable Buff: {isEquippableBuff}"); if (isEquippableBuff) { var multiplier = _equipMappings.GetHealingMultiplier(buffType); player.SendSuccessMessage($"Equippable buff detected! Applying x{multiplier} multiplier to healing effects."); args.Handled = true; } } private void OnGetNetData(GetDataEventArgs args) { // nothing here, yet ;) } private void OnPlayerHPChange(object sender, GetDataHandlers.PlayerHPEventArgs args) { TSPlayer player = args.Player; int previousHP = player.TPlayer.statLife; int newHP = args.Current; short healingAmount = (short)(newHP - previousHP); bool isHealingItem = IsHealingValueValid(healingAmount); if (healingAmount > 0 && isHealingItem) { args.Handled = true; } } private void OnPlayerSlot(object sender, GetDataHandlers.PlayerSlotEventArgs args) { TSPlayer player = args.Player; short slot = args.Slot; short newStack = args.Stack; short itemType = args.Type; bool isHealingItem = IsHealingItem(itemType); bool isItemConsumed = IsItemBeingConsumed(player, slot, newStack); if (isHealingItem && isItemConsumed) { var multiplier = _healingItemMappings.GetHealingMultiplier(itemType); var baseHeal = GetBaseHealAmount(itemType); var enhancedHeal = (int)(baseHeal * multiplier); var totalHealing = enhancedHeal + baseHeal; player.Heal(enhancedHeal); player.SendSuccessMessage($"Enhanced healing! Restored {enhancedHeal} HP (x{multiplier} multiplier)"); } args.Handled = true; } private bool IsHealingItem(int itemType) { return _healingItemMappings.mappings.ContainsKey(itemType); } private bool IsHealingValueValid(short healAmount) { return _healingItemMappings.mappings.ContainsValue(healAmount); } private int GetBaseHealAmount(int itemType) { return _healingItemMappings.mappings.TryGetValue(itemType, out var healAmount) ? healAmount : 0; } private bool IsItemBeingConsumed(TSPlayer player, int slot, short newStack) { Terraria.Item item = player.TPlayer.inventory[slot]; bool isMouse = slot != 58; // is slot 58 the mouse? bool isNewStackLessThanOld = item.stack > newStack; bool isUseAction = player.TPlayer.ConsumeItem(item.netID); Console.WriteLine($"Is the slot the mouse slot?: {!isMouse}"); Console.WriteLine($"Is the new stack of items less than the item stack?: {isNewStackLessThanOld}"); Console.WriteLine($"Is the action consuming an item?: {isUseAction}"); // return isMouse && isNewStackLessThanOld && isUseAction; if (isNewStackLessThanOld && isUseAction) { return true; } if (slot < 58 && isNewStackLessThanOld) { return true; } return false; } private void OnSendNetData(SendDataEventArgs args) { // empty, on purpose :D } private void OnUpdate(EventArgs args) { if ((DateTime.UtcNow - _lastBuffCheck).TotalSeconds >= 30) { foreach (TSPlayer player in TShock.Players) { if (player?.Active == true) { ApplyRegenerationBuff(player); } } _lastBuffCheck = DateTime.UtcNow; } } // TODO: method might be used for later // private void ApplyRegenerationToAllPlayers() // { // foreach (TSPlayer player in TShock.Players) // { // if (player?.Active == true && player.ConnectionAlive) // { // ApplyRegenerationBuff(player); // } // } // } private void ApplyRegenerationBuff(TSPlayer player) { if (player?.Active == true && player.ConnectionAlive) { player.SetBuff(BuffID.Regeneration, 3600, bypass: true); } } private void OnServerJoin(JoinEventArgs args) { var player = TShock.Players[args.Who]; if (player != null) { _players.Add(new ServerPlayer(player)); TShock.Log.Info($"Player {player.Name} joined with IP: {player.IP}"); player.SendSuccessMessage($"Welcome {player.Name} to Sneefaria! Enjoy the regen buff!", Colors.RarityOrange); } } }