Esempio n. 1
0
        /// <summary>Clones the stats object for duplication with separation</summary>
        public new StatsWarrior Clone()
        {
            StatsWarrior clone  = (StatsWarrior)this.MemberwiseClone();
            StatsWarrior retVal = new StatsWarrior();

            //
            retVal.Accumulate(base.Clone());
            //
            retVal._rawAdditiveWarriorData              = (float[])clone._rawAdditiveWarriorData.Clone();
            retVal._rawMultiplicativeWarriorData        = (float[])clone._rawMultiplicativeWarriorData.Clone();
            retVal._rawInverseMultiplicativeWarriorData = (float[])clone._rawInverseMultiplicativeWarriorData.Clone();
            retVal._rawNoStackWarriorData = (float[])clone._rawNoStackWarriorData.Clone();
            //
            return(retVal);
        }
Esempio n. 2
0
 public void Accumulate(StatsWarrior data, float weight)
 {
     #region Base
     if (data.sparseIndices != null)
     {
         int i = 0;
         for (int a = 0; a < data.sparseAdditiveCount; a++, i++)
         {
             int index = data.sparseIndices[i];
             rawAdditiveData[index] += weight * data.rawAdditiveData[index];
         }
         for (int a = 0; a < data.sparseMultiplicativeCount; a++, i++)
         {
             int index = data.sparseIndices[i];
             rawMultiplicativeData[index] = (1 + rawMultiplicativeData[index]) * (1 + weight * data.rawMultiplicativeData[index]) - 1;
         }
         for (int a = 0; a < data.sparseInverseMultiplicativeCount; a++, i++)
         {
             int index = data.sparseIndices[i];
             rawInverseMultiplicativeData[index] = 1 - (1 - rawInverseMultiplicativeData[index]) * (1 - weight * data.rawInverseMultiplicativeData[index]);
         }
         for (int a = 0; a < data.sparseNoStackCount; a++, i++)
         {
             int index = data.sparseIndices[i];
             float value = weight * data.rawNoStackData[index];
             if (value > rawNoStackData[index]) rawNoStackData[index] = value;
         }
     }
     else
     {
         float[] add = data.rawAdditiveData;
         for (int i = 0; i < rawAdditiveData.Length; i++)
         {
             rawAdditiveData[i] += weight * add[i];
         }
         add = data.rawMultiplicativeData;
         for (int i = 0; i < rawMultiplicativeData.Length; i++)
         {
             rawMultiplicativeData[i] = (1 + rawMultiplicativeData[i]) * (1 + weight * add[i]) - 1;
         }
         add = data.rawInverseMultiplicativeData;
         for (int i = 0; i < rawInverseMultiplicativeData.Length; i++)
         {
             rawInverseMultiplicativeData[i] = 1 - (1 - rawInverseMultiplicativeData[i]) * (1 - weight * add[i]);
         }
         add = data.rawNoStackData;
         for (int i = 0; i < rawNoStackData.Length; i++)
         {
             if (weight * add[i] > rawNoStackData[i]) rawNoStackData[i] = weight * add[i];
         }
     }
     #endregion
     #region Warrior
     if (data._sparseIndicesWarrior != null)
     {
         int i = 0;
         for (int a = 0; a < data._sparseAdditiveWarriorCount; a++, i++)
         {
             int index = data._sparseIndicesWarrior[i];
             _rawAdditiveWarriorData[index] += weight * data._rawAdditiveWarriorData[index];
         }
         for (int a = 0; a < data._sparseMultiplicativeWarriorCount; a++, i++)
         {
             int index = data._sparseIndicesWarrior[i];
             _rawMultiplicativeWarriorData[index] = (1 + _rawMultiplicativeWarriorData[index]) * (1 + weight * data._rawMultiplicativeWarriorData[index]) - 1;
         }
         for (int a = 0; a < data._sparseInverseMultiplicativeWarriorCount; a++, i++)
         {
             int index = data._sparseIndicesWarrior[i];
             _rawInverseMultiplicativeWarriorData[index] = 1 - (1 - _rawInverseMultiplicativeWarriorData[index]) * (1 - weight * data._rawInverseMultiplicativeWarriorData[index]);
         }
         for (int a = 0; a < data._sparseNoStackWarriorCount; a++, i++)
         {
             int index = data._sparseIndicesWarrior[i];
             float value = weight * data._rawNoStackWarriorData[index];
             if (value > _rawNoStackWarriorData[index]) _rawNoStackWarriorData[index] = value;
         }
     }
     else
     {
         float[] add = data._rawAdditiveWarriorData;
         for (int i = 0; i < _rawAdditiveWarriorData.Length; i++)
         {
             _rawAdditiveWarriorData[i] += weight * add[i];
         }
         add = data._rawMultiplicativeWarriorData;
         for (int i = 0; i < _rawMultiplicativeWarriorData.Length; i++)
         {
             _rawMultiplicativeWarriorData[i] = (1 + _rawMultiplicativeWarriorData[i]) * (1 + weight * add[i]) - 1;
         }
         add = data._rawInverseMultiplicativeWarriorData;
         for (int i = 0; i < _rawInverseMultiplicativeWarriorData.Length; i++)
         {
             _rawInverseMultiplicativeWarriorData[i] = 1 - (1 - _rawInverseMultiplicativeWarriorData[i]) * (1 - weight * add[i]);
         }
         add = data._rawNoStackWarriorData;
         for (int i = 0; i < _rawNoStackWarriorData.Length; i++)
         {
             if (weight * add[i] > _rawNoStackWarriorData[i]) _rawNoStackWarriorData[i] = weight * add[i];
         }
     }
     #endregion
     if (data._rawSpecialEffectDataSize > 0)
     {
         EnsureSpecialEffectCapacity(_rawSpecialEffectDataSize + data._rawSpecialEffectDataSize);
         Array.Copy(data._rawSpecialEffectData, 0, _rawSpecialEffectData, _rawSpecialEffectDataSize, data._rawSpecialEffectDataSize);
         _rawSpecialEffectDataSize += data._rawSpecialEffectDataSize;
     }
 }
Esempio n. 3
0
 /// <summary>Clones the stats object for duplication with separation</summary>
 public new StatsWarrior Clone() {
     StatsWarrior clone = (StatsWarrior)this.MemberwiseClone();
     StatsWarrior retVal = new StatsWarrior();
     //
     retVal.Accumulate(base.Clone());
     //
     retVal._rawAdditiveWarriorData = (float[])clone._rawAdditiveWarriorData.Clone();
     retVal._rawMultiplicativeWarriorData = (float[])clone._rawMultiplicativeWarriorData.Clone();
     retVal._rawInverseMultiplicativeWarriorData = (float[])clone._rawInverseMultiplicativeWarriorData.Clone();
     retVal._rawNoStackWarriorData = (float[])clone._rawNoStackWarriorData.Clone();
     //
     return retVal;
 }
        private StatsWarrior GetSpecialEffectStats(Player player)
        {
            StatsWarrior statsSpecialEffects = new StatsWarrior();

            Dictionary<Trigger, float> triggerIntervals = new Dictionary<Trigger, float>();
            Dictionary<Trigger, float> triggerChances   = new Dictionary<Trigger, float>();

            player.DefendModel = new DefendModel(player);
            player.AttackModel = new AttackModel(player, AttackModelMode.Optimal);

            float effectiveMasteryRating        = Lookup.MaxEffectiveMasteryRating(player);
            float effectiveBuffedMasteryRating  = effectiveMasteryRating * (1.0f - 10.0f / player.CalcOpts.ShieldBlockInterval) +
                                                    Lookup.MaxEffectiveBuffedMasteryRating(player) * (10.0f / player.CalcOpts.ShieldBlockInterval);
            
            triggerIntervals[Trigger.Use]                   = 0.0f;
            triggerIntervals[Trigger.MeleeAttack]           = player.AttackModel.WeaponAttacksPerSecond;
            triggerIntervals[Trigger.MeleeHit]              = triggerIntervals[Trigger.MeleeAttack];
            triggerIntervals[Trigger.MeleeCrit]             = triggerIntervals[Trigger.MeleeAttack];
            triggerIntervals[Trigger.PhysicalHit]           = triggerIntervals[Trigger.MeleeAttack];
            triggerIntervals[Trigger.PhysicalAttack]        = triggerIntervals[Trigger.MeleeAttack];
            triggerIntervals[Trigger.PhysicalCrit]          = triggerIntervals[Trigger.MeleeAttack];
            triggerIntervals[Trigger.ExecuteHit]            = triggerIntervals[Trigger.MeleeAttack];
            triggerIntervals[Trigger.DoTTick]               = (player.Talents.DeepWounds > 0) ? 2.0f : 0.0f;
            triggerIntervals[Trigger.DamageDone]            = triggerIntervals[Trigger.MeleeAttack] + triggerIntervals[Trigger.DoTTick];
            triggerIntervals[Trigger.DamageOrHealingDone]   = triggerIntervals[Trigger.DamageDone];
            triggerIntervals[Trigger.DamageTaken]           = 1.0f / player.DefendModel.AttackerSwingsPerSecond;
            triggerIntervals[Trigger.DamageAvoided]         = triggerIntervals[Trigger.DamageTaken];
            triggerIntervals[Trigger.DamageParried]         = triggerIntervals[Trigger.DamageTaken];
            triggerIntervals[Trigger.DamageTakenPutsMeBelow35PercHealth] = triggerIntervals[Trigger.DamageTaken];
            triggerIntervals[Trigger.ShieldBlock]           = 60f - (player.Talents.ShieldMastery * 10f);

            triggerChances[Trigger.Use]                     = 1.0f;
            triggerChances[Trigger.MeleeAttack]             = 1.0f;
            triggerChances[Trigger.MeleeHit]                = player.AttackModel.HitsPerSecond / player.AttackModel.WeaponAttacksPerSecond;
            triggerChances[Trigger.MeleeCrit]               = player.AttackModel.CritsPerSecond / player.AttackModel.WeaponAttacksPerSecond;
            triggerChances[Trigger.PhysicalAttack]          = 1.0f;
            triggerChances[Trigger.PhysicalHit]             = triggerChances[Trigger.MeleeHit];
            triggerChances[Trigger.PhysicalCrit]            = triggerChances[Trigger.MeleeCrit];
            triggerChances[Trigger.ExecuteHit]              = triggerChances[Trigger.MeleeHit];
            triggerChances[Trigger.DoTTick]                 = (player.Talents.DeepWounds > 0) ? 1.0f : 0.0f;
            triggerChances[Trigger.DamageDone]              = (player.AttackModel.HitsPerSecond + ((player.Talents.DeepWounds > 0) ? 2.0f : 0.0f))
                                                                / (player.AttackModel.WeaponAttacksPerSecond + ((player.Talents.DeepWounds > 0) ? 1.0f : 0.0f));
            triggerChances[Trigger.DamageOrHealingDone]     = triggerChances[Trigger.DamageDone];
            triggerChances[Trigger.DamageTaken]             = player.DefendModel.AttackerHitsPerSecond / player.DefendModel.AttackerSwingsPerSecond;
            triggerChances[Trigger.DamageAvoided]           = player.DefendModel.DefendTable.AnyAvoid;
            triggerChances[Trigger.DamageParried]           = player.DefendModel.DefendTable.Parry;
            triggerChances[Trigger.DamageTakenPutsMeBelow35PercHealth] = triggerChances[Trigger.DamageTaken] * 0.35f;
            triggerChances[Trigger.ShieldBlock]             = 1.0f;

            foreach (SpecialEffect effect in player.Stats.SpecialEffects())
            {
                if (RelevantTriggers.Contains(effect.Trigger))
                {
                    // Effective Mastery Capping on Large Proc Effects
                    if ((effect.Trigger == Trigger.Use && effect.Stats.MasteryRating > effectiveMasteryRating) || effect.Stats.MasteryRating > effectiveBuffedMasteryRating)
                    {
                        Stats cappedStats = new Stats();
                        cappedStats.Accumulate(effect.Stats);
                        // Assume Use Effects Bypass Shield Block Collision
                        if (effect.Trigger == Trigger.Use)
                            cappedStats.MasteryRating = effectiveMasteryRating;
                        else
                            cappedStats.MasteryRating = effectiveBuffedMasteryRating;
                        // calculate average up-time of this trinket
                        float averageUpTime = 0.0f;
                        if (effect.Trigger == Trigger.ExecuteHit)
                            averageUpTime = effect.GetAverageFactor(triggerIntervals[effect.Trigger], triggerChances[effect.Trigger], player.AttackModel.WeaponSpeed, player.BossOpts.BerserkTimer * (float)player.BossOpts.Under20Perc);
                        else
                            averageUpTime = effect.GetAverageFactor(triggerIntervals[effect.Trigger], triggerChances[effect.Trigger], player.AttackModel.WeaponSpeed, player.BossOpts.BerserkTimer);
                        // accumulate the capped stats from the trinket into our final stats
                        statsSpecialEffects.Accumulate(cappedStats, averageUpTime);
                    }
                    else
                    {
                        if (effect.Trigger == Trigger.ExecuteHit)
                            effect.AccumulateAverageStats(statsSpecialEffects, triggerIntervals, triggerChances, player.AttackModel.WeaponSpeed, player.BossOpts.BerserkTimer * (float)player.BossOpts.Under20Perc);
                        else
                            effect.AccumulateAverageStats(statsSpecialEffects, triggerIntervals, triggerChances, player.AttackModel.WeaponSpeed, player.BossOpts.BerserkTimer);
                    }
                }
            }

            // Base Stats
            statsSpecialEffects.Stamina = (float)Math.Floor(statsSpecialEffects.Stamina * (1.0f + player.Stats.BonusStaminaMultiplier));
            statsSpecialEffects.Strength = (float)Math.Floor(statsSpecialEffects.Strength * (1.0f + player.Stats.BonusStrengthMultiplier));
            statsSpecialEffects.Agility = (float)Math.Floor(statsSpecialEffects.Agility * (1.0f + player.Stats.BonusAgilityMultiplier));

            return statsSpecialEffects;
        }
        private void AccumulateCharacterStats(Player player, Item additionalItem)
        {
            float baseStrength, baseBonusMasteryBlockPercentage, baseParryRatingFromStrength;

            // Items and Buffs
            StatsWarrior statsItemsBuffs = new StatsWarrior();
            AccumulateItemStats(statsItemsBuffs, player.Character, additionalItem);
            statsItemsBuffs.Accumulate(GetBuffsStats(player)); // AccumulateBuffsStats(statsItemsBuffs, player.Character.ActiveBuffs);
            player.Stats.Accumulate(statsItemsBuffs);

            // Race Stats
            StatsWarrior statsRace = new StatsWarrior();
            statsRace.Accumulate(BaseStats.GetBaseStats(player.Character.Level, CharacterClass.Warrior, player.Character.Race));
            statsRace.Expertise += BaseStats.GetRacialExpertise(player.Character, ItemSlot.MainHand);
            player.Stats.Accumulate(statsRace);

            // Talents
            StatsWarrior statsTalents = new StatsWarrior() {
                Block = 0.15f, // Sentinel
                BonusStaminaMultiplier = (1.0f + 0.15f) * (1.0f + (Character.ValidateArmorSpecialization(player.Character, ItemType.Plate) ? 0.05f : 0.0f)) - 1.0f, // Sentinel & Plate Specialization
                BaseArmorMultiplier = player.Talents.Toughness * 0.03f,
            };
            if (player.Talents.HoldTheLine > 0)
                statsTalents.AddSpecialEffect(_SE_HoldTheLine[player.Talents.HoldTheLine]);
            if (player.Talents.BastionOfDefense > 0)
                statsTalents.AddSpecialEffect(_SE_BastionOfDefense[player.Talents.BastionOfDefense]);
            player.Stats.Accumulate(statsTalents);

            // Base Stats
            player.Stats.BaseAgility = statsRace.Agility;
            player.Stats.Stamina = (float)Math.Floor((statsRace.Stamina + statsTalents.Stamina) * (1.0f + player.Stats.BonusStaminaMultiplier));
            player.Stats.Stamina += (float)Math.Floor(statsItemsBuffs.Stamina * (1.0f + player.Stats.BonusStaminaMultiplier));
            player.Stats.Strength = baseStrength = (float)Math.Floor((statsRace.Strength + statsTalents.Strength) * (1.0f + player.Stats.BonusStrengthMultiplier));
            player.Stats.Strength += (float)Math.Floor(statsItemsBuffs.Strength * (1.0f + player.Stats.BonusStrengthMultiplier));
            player.Stats.Agility = (float)Math.Floor((statsRace.Agility + statsTalents.Agility) * (1.0f + player.Stats.BonusAgilityMultiplier));
            player.Stats.Agility += (float)Math.Floor(statsItemsBuffs.Agility * (1.0f + player.Stats.BonusAgilityMultiplier));

            // First-Pass Avoidance for Accurate Special Effect Trigger Chances
            baseBonusMasteryBlockPercentage = Lookup.BonusMasteryBlockPercentage(player);
            baseParryRatingFromStrength = (player.Stats.Strength - baseStrength) * 0.27f; // Parry Rating conversion ignores base Strength
            player.Stats.Block += baseBonusMasteryBlockPercentage;
            player.Stats.ParryRating += baseParryRatingFromStrength; 

            // Calculate Procs and Special Effects
            player.Stats.Accumulate(GetSpecialEffectStats(player));

            // Highest Stat Effects
            if (player.Stats.Strength > player.Stats.Agility)
                player.Stats.Strength += (float)Math.Floor((player.Stats.HighestStat + player.Stats.Paragon) * (1.0f + player.Stats.BonusStrengthMultiplier));
            else
                player.Stats.Agility += (float)Math.Floor((player.Stats.HighestStat + player.Stats.Paragon) * (1.0f + player.Stats.BonusAgilityMultiplier));
            if (player.Stats.HighestSecondaryStat > 0)
            {
                float paragonValue = player.Stats.HighestSecondaryStat; // how much paragon to add
                player.Stats.HighestSecondaryStat = 0f; // remove Paragon stat, since it's not needed
                if (player.Stats.CritRating > player.Stats.HasteRating && player.Stats.CritRating > player.Stats.MasteryRating)
                    player.Stats.CritRating += paragonValue;
                else if (player.Stats.HasteRating > player.Stats.CritRating && player.Stats.HasteRating > player.Stats.MasteryRating)
                    player.Stats.HasteRating += paragonValue;
                else 
                        player.Stats.MasteryRating += paragonValue;
            }
            // Defensive Stats, Add Difference of First-Pass and Second-Pass Block/Parry
            player.Stats.Armor = (float)Math.Ceiling(player.Stats.Armor * (1.0f + player.Stats.BaseArmorMultiplier));
            player.Stats.Armor += (float)Math.Ceiling(player.Stats.BonusArmor * (1.0f + player.Stats.BonusArmorMultiplier));
            player.Stats.Block += (Lookup.BonusMasteryBlockPercentage(player) - baseBonusMasteryBlockPercentage);
            player.Stats.ParryRating += (((player.Stats.Strength - baseStrength) * 0.27f) - baseParryRatingFromStrength); // Parry Rating conversion ignores base Strength

            player.Stats.NatureResistance += player.Stats.NatureResistanceBuff;
            player.Stats.FireResistance += player.Stats.FireResistanceBuff;
            player.Stats.FrostResistance += player.Stats.FrostResistanceBuff;
            player.Stats.ShadowResistance += player.Stats.ShadowResistanceBuff;
            player.Stats.ArcaneResistance += player.Stats.ArcaneResistanceBuff;

            // Final Derived Stats
            player.Stats.Health += StatConversion.GetHealthFromStamina(player.Stats.Stamina, CharacterClass.Warrior) + player.Stats.BattlemasterHealthProc;
            player.Stats.Health = (float)Math.Floor(player.Stats.Health * (1.0f + player.Stats.BonusHealthMultiplier));
            player.Stats.AttackPower += player.Stats.Strength * 2.0f + (player.Stats.Health * 0.1f * player.CalcOpts.AverageVengeance);
            player.Stats.AttackPower = (float)Math.Floor(player.Stats.AttackPower * (1.0f + player.Stats.BonusAttackPowerMultiplier));
        }
Esempio n. 6
0
 public void Accumulate(StatsWarrior data, float weight)
 {
     #region Base
     if (data.sparseIndices != null)
     {
         int i = 0;
         for (int a = 0; a < data.sparseAdditiveCount; a++, i++)
         {
             int index = data.sparseIndices[i];
             rawAdditiveData[index] += weight * data.rawAdditiveData[index];
         }
         for (int a = 0; a < data.sparseMultiplicativeCount; a++, i++)
         {
             int index = data.sparseIndices[i];
             rawMultiplicativeData[index] = (1 + rawMultiplicativeData[index]) * (1 + weight * data.rawMultiplicativeData[index]) - 1;
         }
         for (int a = 0; a < data.sparseInverseMultiplicativeCount; a++, i++)
         {
             int index = data.sparseIndices[i];
             rawInverseMultiplicativeData[index] = 1 - (1 - rawInverseMultiplicativeData[index]) * (1 - weight * data.rawInverseMultiplicativeData[index]);
         }
         for (int a = 0; a < data.sparseNoStackCount; a++, i++)
         {
             int   index = data.sparseIndices[i];
             float value = weight * data.rawNoStackData[index];
             if (value > rawNoStackData[index])
             {
                 rawNoStackData[index] = value;
             }
         }
     }
     else
     {
         float[] add = data.rawAdditiveData;
         for (int i = 0; i < rawAdditiveData.Length; i++)
         {
             rawAdditiveData[i] += weight * add[i];
         }
         add = data.rawMultiplicativeData;
         for (int i = 0; i < rawMultiplicativeData.Length; i++)
         {
             rawMultiplicativeData[i] = (1 + rawMultiplicativeData[i]) * (1 + weight * add[i]) - 1;
         }
         add = data.rawInverseMultiplicativeData;
         for (int i = 0; i < rawInverseMultiplicativeData.Length; i++)
         {
             rawInverseMultiplicativeData[i] = 1 - (1 - rawInverseMultiplicativeData[i]) * (1 - weight * add[i]);
         }
         add = data.rawNoStackData;
         for (int i = 0; i < rawNoStackData.Length; i++)
         {
             if (weight * add[i] > rawNoStackData[i])
             {
                 rawNoStackData[i] = weight * add[i];
             }
         }
     }
     #endregion
     #region Warrior
     if (data._sparseIndicesWarrior != null)
     {
         int i = 0;
         for (int a = 0; a < data._sparseAdditiveWarriorCount; a++, i++)
         {
             int index = data._sparseIndicesWarrior[i];
             _rawAdditiveWarriorData[index] += weight * data._rawAdditiveWarriorData[index];
         }
         for (int a = 0; a < data._sparseMultiplicativeWarriorCount; a++, i++)
         {
             int index = data._sparseIndicesWarrior[i];
             _rawMultiplicativeWarriorData[index] = (1 + _rawMultiplicativeWarriorData[index]) * (1 + weight * data._rawMultiplicativeWarriorData[index]) - 1;
         }
         for (int a = 0; a < data._sparseInverseMultiplicativeWarriorCount; a++, i++)
         {
             int index = data._sparseIndicesWarrior[i];
             _rawInverseMultiplicativeWarriorData[index] = 1 - (1 - _rawInverseMultiplicativeWarriorData[index]) * (1 - weight * data._rawInverseMultiplicativeWarriorData[index]);
         }
         for (int a = 0; a < data._sparseNoStackWarriorCount; a++, i++)
         {
             int   index = data._sparseIndicesWarrior[i];
             float value = weight * data._rawNoStackWarriorData[index];
             if (value > _rawNoStackWarriorData[index])
             {
                 _rawNoStackWarriorData[index] = value;
             }
         }
     }
     else
     {
         float[] add = data._rawAdditiveWarriorData;
         for (int i = 0; i < _rawAdditiveWarriorData.Length; i++)
         {
             _rawAdditiveWarriorData[i] += weight * add[i];
         }
         add = data._rawMultiplicativeWarriorData;
         for (int i = 0; i < _rawMultiplicativeWarriorData.Length; i++)
         {
             _rawMultiplicativeWarriorData[i] = (1 + _rawMultiplicativeWarriorData[i]) * (1 + weight * add[i]) - 1;
         }
         add = data._rawInverseMultiplicativeWarriorData;
         for (int i = 0; i < _rawInverseMultiplicativeWarriorData.Length; i++)
         {
             _rawInverseMultiplicativeWarriorData[i] = 1 - (1 - _rawInverseMultiplicativeWarriorData[i]) * (1 - weight * add[i]);
         }
         add = data._rawNoStackWarriorData;
         for (int i = 0; i < _rawNoStackWarriorData.Length; i++)
         {
             if (weight * add[i] > _rawNoStackWarriorData[i])
             {
                 _rawNoStackWarriorData[i] = weight * add[i];
             }
         }
     }
     #endregion
     if (data._rawSpecialEffectDataSize > 0)
     {
         EnsureSpecialEffectCapacity(_rawSpecialEffectDataSize + data._rawSpecialEffectDataSize);
         Array.Copy(data._rawSpecialEffectData, 0, _rawSpecialEffectData, _rawSpecialEffectDataSize, data._rawSpecialEffectDataSize);
         _rawSpecialEffectDataSize += data._rawSpecialEffectDataSize;
     }
 }