using System;
using System.Collections.Generic;
using System.Linq;
using ACE.Entity.Models;
using ACE.Server.WorldObjects;
using ACE.Server.WorldObjects.Managers;
namespace ACE.Server.Entity
{
///
/// The result of adding an enchantment,
/// and where it fits into the current stack
///
public class AddEnchantmentResult
{
///
/// The resulting enchantment that was added or refreshed
/// This is set in EnchantmentManager.Add()
///
public PropertiesEnchantmentRegistry Enchantment;
///
/// Determines how this enchantment relates
/// to the most powerful spell in this category
/// for the surpassing / refreshing / surpassed by message
///
public StackType StackType;
///
/// A list of existing enchantments in this stack
///
public List Surpass;
public List Refresh;
public List Surpassed;
///
/// The most powerful spells in this stack,
/// for each stack type
///
public Spell SurpassSpell;
public Spell RefreshSpell;
public Spell SurpassedSpell;
///
/// This handles situations where the same spell can come
/// from both a creature and item source
///
public PropertiesEnchantmentRegistry RefreshCaster { get; set; }
public ushort TopLayerId { get; set; }
public ushort NextLayerId => (ushort)(TopLayerId + 1);
public AddEnchantmentResult() { }
public AddEnchantmentResult(StackType stackType)
{
StackType = stackType;
}
public void BuildStack(List entries, Spell spell, WorldObject caster, bool equip = false, bool isWeaponSpell = false)
{
Surpass = new List();
Refresh = new List();
Surpassed = new List();
var powerLevel = spell.Power;
foreach (var entry in entries.OrderByDescending(i => i.PowerLevel))
{
if (powerLevel > entry.PowerLevel)
{
// surpassing existing spell
Surpass.Add(entry);
}
else if (powerLevel == entry.PowerLevel)
{
// refreshing existing spell
if (spell.Id == entry.SpellId)
{
Refresh.Add(entry);
// this could be valid
// consider the case: i equip an item that casts strength 6 on me
// then i cast strength 6 on myself
// the self-cast would find the existing spell from the item, but it wouldn't refresh that one
// it should cast to its own layer?
//if (Refresh.Count > 1)
//Console.WriteLine($"AddEnchantmentResult.BuildStack(): multiple refresh entries");
}
else
{
// handle special case to prevent message: Pumpkin Shield casts Web of Defense on you, refreshing Aura of Defense
var spellDuration = equip ? double.PositiveInfinity : spell.Duration;
if (!equip && caster is Player player && player.AugmentationIncreasedSpellDuration > 0 && !isWeaponSpell)
spellDuration *= 1.0f + player.AugmentationIncreasedSpellDuration * 0.2f;
var entryDuration = entry.Duration == -1 ? double.PositiveInfinity : entry.Duration;
if (spellDuration > entryDuration || spellDuration == entryDuration && !SpellSet.SetSpells.Contains(entry.SpellId))
Surpass.Add(entry);
else if (spellDuration < entryDuration)
Surpassed.Add(entry);
else
{
// fallback on spell id, for overlapping set spells in multiple sets, where the different 'level' names each have the same spellLevel and powerLevel?
// ie. for Gauntlet Damage Boost I and II
// this bug still exists in acclient visual enchantment display, unknown whether this bug existed on retail server
if (spell.Id > entry.SpellId)
Surpass.Add(entry);
else
Surpassed.Add(entry);
}
}
}
else if (powerLevel < entry.PowerLevel)
{
// surpassed by existing spell
Surpassed.Add(entry);
}
if (entry.LayerId > TopLayerId)
TopLayerId = entry.LayerId;
}
SetStackType();
SetSpell();
if (Refresh.Count > 0)
SetRefreshCaster(caster);
}
public void SetStackType()
{
if (Surpassed.Count > 0)
StackType = StackType.Surpassed;
else if (Refresh.Count > 0)
StackType = StackType.Refresh;
else if (Surpass.Count > 0)
StackType = StackType.Surpass;
}
public void SetSpell()
{
if (Surpass.Count > 0)
SurpassSpell = new Spell(Surpass[0].SpellId, false);
if (Refresh.Count > 0)
RefreshSpell = new Spell(Refresh[0].SpellId, false);
if (Surpassed.Count > 0)
SurpassedSpell = new Spell(Surpassed[0].SpellId, false);
}
public void SetRefreshCaster(WorldObject caster)
{
// the same spells from different casters should definitely be written to separate layers,
// but it's questionable if retail sent 'refreshing' or 'surpassing' here
// these messages were seen in retail pcaps:
// - Acid Tachi cast Aura of Incantation of Blood Drinker Self on you, surpassing Aura of Incantation of Blood Drinker Self
// - Scalemail Leggings cast Minor Light Weapon Aptitude on you, surpassing Minor Light Weapon Aptitude
// - Little Thor cast Aura of Incantation of Spirit Drinker Other on you, surpassing Aura of Incantation of Spirit Drinker Other
// which seems to indicate the surpass verb was tied directly to whether or not a new layer was added
// however, at the same time, i would expect to see a ton of references in the pcaps for multiple monsters casting the same spells,
// such as 'Monster cast Imperil VI on you, surpassing Imperil VI' when 2 monsters have both cast imperil
// i am seeing a total of 0 references for this monster scenario
foreach (var refresh in Refresh)
{
if (refresh.CasterObjectId == caster.Guid.Full)
RefreshCaster = refresh;
}
}
}
}