mirror of
				https://bitbucket.org/Ioncannon/project-meteor-server.git
				synced 2025-05-20 08:26:59 -04:00 
			
		
		
		
	Refactor StatusEffectContainer to better handle messages using
CommandResultContainer. Alter Modifiers to be the same as ParamNames. Add LoseOnClassChange flag for status effects. Add a few missing status effects. Fix EndTime for stance status effects to stop icon from blinking.
This commit is contained in:
		| @@ -143,8 +143,8 @@ namespace FFXIVClassic_Map_Server.Actors | ||||
|             // todo: move this somewhere more appropriate | ||||
|             // todo: base this on equip and shit | ||||
|             SetMod((uint)Modifier.AttackRange, 3); | ||||
|             SetMod((uint)Modifier.AttackDelay, (Program.Random.Next(30, 60) * 100)); | ||||
|             SetMod((uint)Modifier.Speed, (uint)moveSpeeds[2]); | ||||
|             SetMod((uint)Modifier.Delay, (Program.Random.Next(30, 60) * 100)); | ||||
|             SetMod((uint)Modifier.MovementSpeed, (uint)moveSpeeds[2]); | ||||
|  | ||||
|             spawnX = positionX; | ||||
|             spawnY = positionY; | ||||
| @@ -407,6 +407,7 @@ namespace FFXIVClassic_Map_Server.Actors | ||||
|  | ||||
|                 if ((updateFlags & ActorUpdateFlags.Status) != 0) | ||||
|                 { | ||||
|  | ||||
|                     List<SubPacket> statusPackets = statusEffects.GetStatusPackets(); | ||||
|                     packets.AddRange(statusPackets); | ||||
|                     statusPackets.Clear(); | ||||
| @@ -462,7 +463,7 @@ namespace FFXIVClassic_Map_Server.Actors | ||||
|  | ||||
|         public virtual uint GetAttackDelayMs() | ||||
|         { | ||||
|             return (uint)GetMod((uint)Modifier.AttackDelay); | ||||
|             return (uint)GetMod((uint)Modifier.Delay); | ||||
|         } | ||||
|  | ||||
|         public virtual uint GetAttackRange() | ||||
| @@ -764,7 +765,7 @@ namespace FFXIVClassic_Map_Server.Actors | ||||
|         public virtual float GetSpeed() | ||||
|         { | ||||
|             // todo: for battlenpc/player calculate speed | ||||
|             return (float) GetMod((uint)Modifier.Speed); | ||||
|             return (float) GetMod((uint)Modifier.MovementSpeed); | ||||
|         } | ||||
|  | ||||
|         public virtual void OnAttack(State state, CommandResult action, ref CommandResult error) | ||||
| @@ -863,8 +864,8 @@ namespace FFXIVClassic_Map_Server.Actors | ||||
|             { | ||||
|                 //TP gained on an attack is usually 100 * delay. | ||||
|                 //Store TP seems to add .1% per point. | ||||
|                 double weaponDelay = GetMod(Modifier.AttackDelay) / 1000.0; | ||||
|                 var storeTPPercent = 1 + (GetMod(Modifier.StoreTP) * 0.001); | ||||
|                 double weaponDelay = GetMod(Modifier.Delay) / 1000.0; | ||||
|                 var storeTPPercent = 1 + (GetMod(Modifier.StoreTp) * 0.001); | ||||
|                 AddTP((int)(weaponDelay * 100 * storeTPPercent)); | ||||
|             } | ||||
|         } | ||||
| @@ -999,8 +1000,7 @@ namespace FFXIVClassic_Map_Server.Actors | ||||
|             { | ||||
|                 StatusEffect procEffect = Server.GetWorldManager().GetStatusEffect(effectId); | ||||
|                 procEffect.SetDuration(5); | ||||
|                 procEffect.SetSilent(true); | ||||
|                 statusEffects.AddStatusEffect(procEffect, this, true, true); | ||||
|                 statusEffects.AddStatusEffect(procEffect, this); | ||||
|             } | ||||
|             //Otherwise we're reseting a proc, remove the status | ||||
|             else | ||||
|   | ||||
| @@ -11,88 +11,157 @@ namespace FFXIVClassic_Map_Server.actors.chara | ||||
|     //Also, 0-35 should probably match with up BattleTemp | ||||
|     enum Modifier : UInt32 | ||||
|     { | ||||
|         NAMEPLATE_SHOWN         = 0, | ||||
|         TARGETABLE              = 1, | ||||
|         NAMEPLATE_SHOWN2        = 2, | ||||
|         //NAMEPLATE_SHOWN2 = 3, | ||||
|         //These line up with ParamNames starting at 15001 and appear on gear | ||||
|         //Health | ||||
|         Hp = 0,    //Max HP | ||||
|         Mp = 1,    //Max MP | ||||
|         Tp = 2,    //Max TP | ||||
|  | ||||
|         Strength                = 3, | ||||
|         Vitality                = 4, | ||||
|         Dexterity               = 5, | ||||
|         Intelligence            = 6, | ||||
|         Mind                    = 7, | ||||
|         Piety                   = 8, | ||||
|         //Main stats | ||||
|         Strength = 3, | ||||
|         Vitality = 4, | ||||
|         Dexterity = 5, | ||||
|         Intelligence = 6, | ||||
|         Mind = 7, | ||||
|         Piety = 8, | ||||
|  | ||||
|         ResistFire              = 9, | ||||
|         ResistIce               = 10, | ||||
|         ResistWind              = 11, | ||||
|         ResistLightning         = 12, | ||||
|         ResistEarth             = 13, | ||||
|         ResistWater             = 14, | ||||
|         //Elemental Resistances | ||||
|         FireResistance = 9,    //Lowers Fire damage taken | ||||
|         IceResistance = 10,   //Lowers Ice damage taken | ||||
|         WindResistance = 11,   //Lowers Wind damage taken | ||||
|         EarthResistance = 12,   //Lowers Earth damage taken | ||||
|         LightningResistance = 13,   //Lowers Lightning damage taken | ||||
|         WaterResistance = 14,   //Lowers Water damage taken | ||||
|  | ||||
|         Accuracy                = 15, | ||||
|         Evasion                 = 16, | ||||
|         Attack                  = 17, | ||||
|         Defense                 = 18,   //Is there a magic defense stat? 19 maybe? | ||||
|         MagicAttack             = 23, | ||||
|         MagicHeal               = 24, | ||||
|         MagicEnhancePotency     = 25, | ||||
|         MagicEnfeeblingPotency  = 26, | ||||
|         //Physical Secondary stats | ||||
|         Accuracy = 15,   //Increases chance to hit with physical attacks | ||||
|         Evasion = 16,   //Decreases chance to be hit by physical attacks | ||||
|         Attack = 17,   //Increases damage done with physical attacks | ||||
|         Defense = 18,   //Decreases damage taken from physical attacks | ||||
|  | ||||
|         MagicAccuracy           = 27, | ||||
|         MagicEvasion            = 28, | ||||
|         //Physical crit stats | ||||
|         CriticalHitRating = 19,   //Increases chance to crit with physical attacks | ||||
|         CriticalHitEvasion = 20,   //Decreases chance to be crit by physical attacks | ||||
|         CriticalHitAttackPower = 21,   //Increases damage done by critical physical attacks | ||||
|         CriticalHitResilience = 22,   //Decreases damage taken from critical physical attacks | ||||
|  | ||||
|         CraftProcessing         = 30, | ||||
|         CraftMagicProcessing    = 31, | ||||
|         CraftProcessControl     = 32, | ||||
|         //Magic secondary stats | ||||
|         AttackMagicPotency = 23,   //Increases damage done with magical attacks | ||||
|         HealingMagicPotency = 24,   //Increases healing done with magic healing | ||||
|         EnhancementMagicPotency = 25,   //Increases effect of enhancement magic | ||||
|         EnfeeblingMagicPotency = 26,   //Increases effect of enfeebling magic | ||||
|         MagicAccuracy = 27,   //Decreases chance for magic to be evaded | ||||
|         MagicEvasion = 28,   //Increases chance to evade magic | ||||
|  | ||||
|         HarvestPotency          = 33, | ||||
|         HarvestLimit            = 34, | ||||
|         HarvestRate             = 35, | ||||
|         //Crafting stats | ||||
|         Craftsmanship = 29, | ||||
|         MagicCraftsmanship = 30, | ||||
|         Control = 31, | ||||
|         Gathering = 32, | ||||
|         Output = 33, | ||||
|         Perception = 34, | ||||
|  | ||||
|         None                    = 36, | ||||
|         Hp                      = 37, | ||||
|         HpPercent               = 38, | ||||
|         Mp                      = 39, | ||||
|         MpPercent               = 40, | ||||
|         Tp                      = 41, | ||||
|         TpPercent               = 42, | ||||
|         Regen                   = 43, | ||||
|         Refresh                 = 44, | ||||
|         //Magic crit stats | ||||
|         MagicCriticalHitRating = 35,   //Increases chance to crit with magical attacks | ||||
|         MagicCriticalHitEvasion = 36,   //Decreases chance to be crit by magical attacks | ||||
|         MagicCriticalHitPotency = 37,   //Increases damage done by critical magical attacks | ||||
|         MagicCriticalHitResilience = 38,   //Decreases damage taken from critical magical attacks | ||||
|  | ||||
|         AttackRange             = 45, | ||||
|         Speed                   = 46, | ||||
|         AttackDelay             = 47, | ||||
|         //Blocking stats | ||||
|         Parry = 39,   //Increases chance to parry | ||||
|         BlockRate = 40,   //Increases chance to block | ||||
|         Block = 41,   //Reduces damage taken from blocked attacks | ||||
|  | ||||
|         Raise                   = 48, | ||||
|         MinimumHpLock           = 49,   // hp cannot fall below this value | ||||
|         AttackType              = 50,   // slashing, piercing, etc | ||||
|         BlockRate               = 51, | ||||
|         Block                   = 52, | ||||
|         CritRating              = 53, | ||||
|         HasShield               = 54,   // Need this because shields are required for blocks. Could have used BlockRate or Block but BlockRate is provided by Gallant Sollerets and Block is provided by some buffs. | ||||
|         HitCount                = 55,   // Amount of hits in an auto attack. Usually 1, 2 for h2h, 3 with spinning heel | ||||
|         //Elemental Potencies | ||||
|         FireMagicPotency = 42,   //Increases damage done by Fire Magic | ||||
|         IceMagicPotency = 43,   //Increases damage done by Ice Magic | ||||
|         WindMagicPotency = 44,   //Increases damage done by Wind Magic | ||||
|         EarthMagicPotency = 45,   //Increases damage done by Earth Magic | ||||
|         LightningMagicPotency = 46,   //Increases damage done by Lightning Magic | ||||
|         WaterMagicPotency = 47,   //Increases damage done by Water Magic | ||||
|  | ||||
|         //Flat percent increases to these rates. Probably a better way to do this | ||||
|         RawEvadeRate            = 56,    | ||||
|         RawParryRate            = 57, | ||||
|         RawBlockRate            = 58, | ||||
|         RawResistRate           = 59, | ||||
|         RawHitRate              = 60, | ||||
|         RawCritRate             = 61, | ||||
|         //Miscellaneous | ||||
|         Regen = 48,   //Restores health over time | ||||
|         Refresh = 49,   //Restores MP over time | ||||
|         StoreTp = 50,   //Increases TP gained by auto attacks and damaging abiltiies | ||||
|         Enmity = 51,   //Increases enmity gained from actions | ||||
|         Spikes = 52,   //Deals damage or status to attacker when hit | ||||
|         Haste = 53,   //Increases attack speed | ||||
|         //54 and 55 didn't have names and seem to be unused | ||||
|         ReducedDurabilityLoss = 56,   //Reduces durability loss | ||||
|         IncreasedSpiritbondGain = 57,   //Increases rate of spiritbonding | ||||
|         Damage = 58,   //Increases damage of auto attacks | ||||
|         Delay = 59,   //Increases rate of auto attacks | ||||
|         Fastcast = 60,   //Increases speed of casts | ||||
|         MovementSpeed = 61,   //Increases movement speed | ||||
|         Exp = 62,   //Increases experience gained | ||||
|         RestingHp = 63,   //? | ||||
|         RestingMp = 64,   //? | ||||
|  | ||||
|         DamageTakenDown         = 62,   // Percent damage taken down | ||||
|         StoreTP                 = 63,   //.1% extra tp per point. Lancer trait is 50 StoreTP | ||||
|         PhysicalCritRate        = 64,   //CritRating but only for physical attacks. Increases chance of critting. | ||||
|         PhysicalCritEvasion     = 65,   //Opposite of CritRating. Reduces chance of being crit by phyiscal attacks | ||||
|         PhysicalCritAttack      = 66,   //Increases damage done by Physical Critical hits | ||||
|         PhysicalCritResilience  = 67,   //Decreases damage taken by Physical Critical hits | ||||
|         Parry                   = 68,   //Increases chance to parry | ||||
|         MagicCritPotency        = 69,   //Increases  | ||||
|         Regain                  = 70,   //TP regen, should be -90 out of combat, Invigorate sets to 100+ depending on traits | ||||
|         RegenDown               = 71,   //Damage over time effects. Separate from normal Regen because of how they are displayed in game | ||||
|         Stoneskin               = 72,   //Nullifies damage | ||||
|         MinimumTpLock           = 73,   //Don't let TP fall below this, used in openings | ||||
|         KnockbackImmune         = 74    //Immune to knockback effects when above 0 | ||||
|         //Attack property resistances | ||||
|         SlashingResistance = 65,   //Reduces damage taken by slashing attacks | ||||
|         PiercingResistance = 66,   //Reduces damage taken by piercing attacks | ||||
|         BluntResistance = 67,   //Reduces damage taken by blunt attacks | ||||
|         ProjectileResistance = 68,   //Reduces damage taken by projectile attacks | ||||
|         SonicResistance = 69,   //Reduces damage taken by sonic attacks | ||||
|         BreathResistance = 70,   //Reduces damage taken by breath attacks | ||||
|         PhysicalResistance = 71,   //Reduces damage taken by physical attacks | ||||
|         MagicResistance = 72,   //Reduces damage taken by magic attacks | ||||
|  | ||||
|         //Status resistances | ||||
|         SlowResistance = 73,   //Reduces chance to be inflicted with slow by status magic | ||||
|         PetrificationResistance = 74,   //Reduces chance to be inflicted with petrification by status magic | ||||
|         ParalysisResistance = 75,   //Reduces chance to be inflicted with paralysis by status magic | ||||
|         SilenceResistance = 76,   //Reduces chance to be inflicted with silence by status magic | ||||
|         BlindResistance = 77,   //Reduces chance to be inflicted with blind by status magic | ||||
|         PoisonResistance = 78,   //Reduces chance to be inflicted with poison by status magic | ||||
|         StunResistance = 79,   //Reduces chance to be inflicted with stun by status magic | ||||
|         SleepResistance = 80,   //Reduces chance to be inflicted with sleep by status magic | ||||
|         BindResistance = 81,   //Reduces chance to be inflicted with bind by status magic | ||||
|         HeavyResistance = 82,   //Reduces chance to be inflicted with heavy by status magic | ||||
|         DoomResistance = 83,   //Reduces chance to be inflicted with doom by status magic | ||||
|  | ||||
|         //84-101 didn't have names and seem to be unused | ||||
|         //Miscellaneous | ||||
|         ConserveMp = 101,  //Chance to reduce mp used by actions | ||||
|         SpellInterruptResistance = 102,  //Reduces chance to be interrupted by damage while casting | ||||
|         DoubleDownOdds = 103,  //Increases double down odds | ||||
|         HqDiscoveryRate = 104, | ||||
|  | ||||
|  | ||||
|         //Non-gear mods | ||||
|         None = 105, | ||||
|         NAMEPLATE_SHOWN = 106, | ||||
|         TARGETABLE = 107, | ||||
|         NAMEPLATE_SHOWN2 = 108, | ||||
|  | ||||
|         HpPercent = 109, | ||||
|         MpPercent = 110, | ||||
|         TpPercent = 111, | ||||
|  | ||||
|         AttackRange = 112,  //How far away in yalms this character can attack from (probably won't need this when auto attack skills are done) | ||||
|  | ||||
|         Raise = 113, | ||||
|         MinimumHpLock = 114,  //Stops HP from falling below this value | ||||
|         MinimumMpLock = 115,  //Stops MP from falling below this value | ||||
|         MinimumTpLock = 116,  //Stops TP from falling below this value | ||||
|         AttackType = 117,  //Attack property of auto attacks (might not need this when auto attack skills are done, unsure) | ||||
|         CanBlock = 118,  //Whether the character can block attacks. (For players this is only true when they have a shield) | ||||
|         HitCount = 119,  //Amount of hits in an auto attack. Usually 1, 2 for h2h, 3 with spinning heel | ||||
|  | ||||
|         //Flat percent increases to these rates. Might not need these? | ||||
|         RawEvadeRate = 120, | ||||
|         RawParryRate = 121, | ||||
|         RawBlockRate = 122, | ||||
|         RawResistRate = 123, | ||||
|         RawHitRate = 124, | ||||
|         RawCritRate = 125, | ||||
|  | ||||
|         DamageTakenDown = 126,  //Percent damage taken down | ||||
|         Regain = 127,  //TP regen, should be -90 out of combat, Invigorate sets to 100+ depending on traits | ||||
|         RegenDown = 128,  //Damage over time effects. Separate from normal Regen because of how they are displayed in game | ||||
|         Stoneskin = 129,  //Nullifies damage | ||||
|         KnockbackImmune = 130,  //Immune to knockback effects when above 0 | ||||
|         Stealth = 131,  //Not visisble when above 0 | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -336,17 +336,17 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai | ||||
|     enum StatusEffectFlags : uint | ||||
|     { | ||||
|         None = 0, | ||||
|         Silent = 1 << 0,                            // dont display effect loss message | ||||
|  | ||||
|         //Loss flags | ||||
|         LoseOnDeath =                   1 << 1,     // effects removed on death | ||||
|         LoseOnZoning =                  1 << 2,     // effects removed on zoning | ||||
|         LoseOnEsuna =                   1 << 3,     // effects which can be removed with esuna (debuffs) | ||||
|         LoseOnDispel =                  1 << 4,     // some buffs which player might be able to dispel from mob | ||||
|         LoseOnLogout =                  1 << 5,     // effects removed on logging out | ||||
|         LoseOnAttacking =               1 << 6,     // effects removed when owner attacks another entity | ||||
|         LoseOnCastStart =               1 << 7,     // effects removed when owner starts casting | ||||
|         LoseOnAggro =                   1 << 8,     // effects removed when owner gains enmity (swiftsong) | ||||
|         //Loss flags - Do we need loseonattacking/caststart? Could just be done with activate flags | ||||
|         LoseOnDeath =                   1 << 0,     // effects removed on death | ||||
|         LoseOnZoning =                  1 << 1,     // effects removed on zoning | ||||
|         LoseOnEsuna =                   1 << 2,     // effects which can be removed with esuna (debuffs) | ||||
|         LoseOnDispel =                  1 << 3,     // some buffs which player might be able to dispel from mob | ||||
|         LoseOnLogout =                  1 << 4,     // effects removed on logging out | ||||
|         LoseOnAttacking =               1 << 5,     // effects removed when owner attacks another entity | ||||
|         LoseOnCastStart =               1 << 6,     // effects removed when owner starts casting | ||||
|         LoseOnAggro =                   1 << 7,     // effects removed when owner gains enmity (swiftsong) | ||||
|         LoseOnClassChange =             1 << 8,     //Effect falls off whhen changing class | ||||
|  | ||||
|         //Activate flags | ||||
|         ActivateOnCastStart =           1 << 9,     //Activates when a cast starts. | ||||
| @@ -373,9 +373,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai | ||||
|         PreventMovement =               1 << 26,    // effects which prevent movement such as bind, still allows turning in place | ||||
|         PreventTurn =                   1 << 27,    // effects which prevent turning, such as stun | ||||
|         PreventUntarget =               1 << 28,    // effects which prevent changing targets, such as fixation | ||||
|  | ||||
|         Stealth =                       1 << 29,    // sneak/invis | ||||
|         Stance =                        1 << 30,    // effects that do not have a timer | ||||
|         Stance =                        1 << 29     // effects that do not have a timer | ||||
|     } | ||||
|  | ||||
|     enum StatusEffectOverwrite : byte | ||||
| @@ -392,19 +390,22 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai | ||||
|         private Character owner; | ||||
|         private Character source; | ||||
|         private StatusEffectId id; | ||||
|         private string name;        // name of this effect | ||||
|         private DateTime startTime; // when was this effect added | ||||
|         private DateTime endTime;   // when this status falls off | ||||
|         private DateTime lastTick;  // when did this effect last tick | ||||
|         private uint duration;    // how long should this effect last in seconds | ||||
|         private uint tickMs;        // how often should this effect proc | ||||
|         private double magnitude;   // a value specified by scripter which is guaranteed to be used by all effects | ||||
|         private byte tier;          // same effect with higher tier overwrites this | ||||
|         private double extra;       // optional value | ||||
|         private StatusEffectFlags flags;         // death/erase/dispel etc | ||||
|         private StatusEffectOverwrite overwrite; // how to handle adding an effect with same id (see StatusEfectOverwrite) | ||||
|         private bool silent = false;             // do i send a message on losing effect  | ||||
|         private bool hidden = false; | ||||
|         private string name;                        // name of this effect | ||||
|         private DateTime startTime;                 // when was this effect added | ||||
|         private DateTime endTime;                   // when this status falls off | ||||
|         private DateTime lastTick;                  // when did this effect last tick | ||||
|         private uint duration;                      // how long should this effect last in seconds | ||||
|         private uint tickMs;                        // how often should this effect proc | ||||
|         private double magnitude;                   // a value specified by scripter which is guaranteed to be used by all effects | ||||
|         private byte tier;                          // same effect with higher tier overwrites this | ||||
|         private double extra;                       // optional value | ||||
|         private StatusEffectFlags flags;            // death/erase/dispel etc | ||||
|         private StatusEffectOverwrite overwrite;    // how to handle adding an effect with same id (see StatusEfectOverwrite) | ||||
|         private bool silentOnGain = false;          //Whether a message is sent when the status is gained | ||||
|         private bool silentOnLoss = false;          //Whether a message is sent when the status is lost | ||||
|         private bool hidden = false;                //Whether this status is shown. Used for things that aren't really status effects like exp chains and procs | ||||
|         private ushort statusGainTextId;            //The text id used when the status is gained | ||||
|         private ushort statusLossTextId;            //The text id used when the status effect falls off when its time runs out | ||||
|         public LuaScript script; | ||||
|  | ||||
|         HitEffect animationEffect; | ||||
| @@ -438,26 +439,34 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai | ||||
|             this.name = effect.name; | ||||
|             this.flags = effect.flags; | ||||
|             this.overwrite = effect.overwrite; | ||||
|             this.statusGainTextId = effect.statusGainTextId; | ||||
|             this.statusLossTextId = effect.statusLossTextId; | ||||
|             this.extra = effect.extra; | ||||
|             this.script = effect.script; | ||||
|             this.silentOnGain = effect.silentOnGain; | ||||
|             this.silentOnLoss = effect.silentOnLoss; | ||||
|             this.hidden = effect.hidden; | ||||
|         } | ||||
|  | ||||
|         public StatusEffect(uint id, string name, uint flags, uint overwrite, uint tickMs) | ||||
|         public StatusEffect(uint id, string name, uint flags, uint overwrite, uint tickMs, bool hidden, bool silentOnGain, bool silentOnLoss) | ||||
|         { | ||||
|             this.id = (StatusEffectId)id; | ||||
|             this.name = name; | ||||
|             this.flags = (StatusEffectFlags)flags; | ||||
|             this.overwrite = (StatusEffectOverwrite)overwrite; | ||||
|             this.tickMs = tickMs; | ||||
|             this.hidden = hidden; | ||||
|             this.silentOnGain = silentOnGain; | ||||
|             this.silentOnLoss = silentOnLoss; | ||||
|         } | ||||
|  | ||||
|         // return true when duration has elapsed | ||||
|         public bool Update(DateTime tick) | ||||
|         public bool Update(DateTime tick, CommandResultContainer resultContainer = null) | ||||
|         { | ||||
|             if (tickMs != 0 && (tick - lastTick).TotalMilliseconds >= tickMs) | ||||
|             { | ||||
|                 lastTick = tick; | ||||
|                 if (LuaEngine.CallLuaStatusEffectFunction(this.owner, this, "onTick", this.owner, this) > 0) | ||||
|                 if (LuaEngine.CallLuaStatusEffectFunction(this.owner, this, "onTick", this.owner, this, resultContainer) > 0) | ||||
|                     return true; | ||||
|             } | ||||
|  | ||||
| @@ -553,9 +562,14 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai | ||||
|             return (byte)overwrite; | ||||
|         } | ||||
|  | ||||
|         public bool GetSilent() | ||||
|         public bool GetSilentOnGain() | ||||
|         { | ||||
|             return silent; | ||||
|             return silentOnGain; | ||||
|         } | ||||
|  | ||||
|         public bool GetSilentOnLoss() | ||||
|         { | ||||
|             return silentOnLoss; | ||||
|         } | ||||
|  | ||||
|         public bool GetHidden() | ||||
| @@ -563,6 +577,17 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai | ||||
|             return hidden; | ||||
|         } | ||||
|  | ||||
|         public ushort GetStatusGainTextId() | ||||
|         { | ||||
|             return 30328; | ||||
|             return statusGainTextId; | ||||
|         } | ||||
|  | ||||
|         public ushort GetStatusLossTextId() | ||||
|         { | ||||
|             return statusLossTextId; | ||||
|         } | ||||
|  | ||||
|         public void SetStartTime(DateTime time) | ||||
|         { | ||||
|             this.startTime = time; | ||||
| @@ -571,7 +596,15 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai | ||||
|  | ||||
|         public void SetEndTime(DateTime time) | ||||
|         { | ||||
|             endTime = time; | ||||
|             //If it's a stance, just set endtime to highest number possible for XIV | ||||
|             if ((flags & StatusEffectFlags.Stance) != 0) | ||||
|             { | ||||
|                 endTime = Utils.UnixTimeStampToDateTime(4294967295); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 endTime = time; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         //Refresh the status, updating the end time based on the duration of the status and broadcasts the new time | ||||
| @@ -634,9 +667,14 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai | ||||
|             this.overwrite = (StatusEffectOverwrite)overwrite; | ||||
|         } | ||||
|  | ||||
|         public void SetSilent(bool silent) | ||||
|         public void SetSilentOnGain(bool silent) | ||||
|         { | ||||
|             this.silent = silent; | ||||
|             this.silentOnGain = silent; | ||||
|         } | ||||
|  | ||||
|         public void SetSilentOnLoss(bool silent) | ||||
|         { | ||||
|             this.silentOnLoss = silent; | ||||
|         } | ||||
|  | ||||
|         public void SetHidden(bool hidden) | ||||
| @@ -644,6 +682,17 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai | ||||
|             this.hidden = hidden; | ||||
|         } | ||||
|  | ||||
|         public void SetStatusGainTextId(ushort textId) | ||||
|         { | ||||
|             this.statusGainTextId = textId; | ||||
|         } | ||||
|  | ||||
|         public void SetStatusLossTextId(ushort textId) | ||||
|         { | ||||
|             this.statusLossTextId = textId; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public void SetAnimation(uint hitEffect) | ||||
|         { | ||||
|             animationEffect = (HitEffect)hitEffect; | ||||
|   | ||||
| @@ -109,55 +109,44 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai | ||||
|             return effects.ContainsKey((uint)id); | ||||
|         } | ||||
|  | ||||
|         public CommandResult AddStatusForCommandResult(uint id, byte tier = 1, ulong magnitude = 0, uint duration = 0) | ||||
|         { | ||||
|             CommandResult action = null; | ||||
|  | ||||
|             if (AddStatusEffect(id, tier, magnitude, duration)) | ||||
|                 action = new CommandResult(owner.actorId, 30328, id | (uint)HitEffect.StatusEffectType); | ||||
|  | ||||
|             return action; | ||||
|         } | ||||
|  | ||||
|         public bool AddStatusEffect(uint id) | ||||
|         public bool AddStatusEffect(uint id, CommandResultContainer actionContainer = null, ushort worldmasterTextId = 30328) | ||||
|         { | ||||
|             var se = Server.GetWorldManager().GetStatusEffect(id); | ||||
|  | ||||
|             return AddStatusEffect(se, owner); | ||||
|             return AddStatusEffect(se, owner, actionContainer, worldmasterTextId); | ||||
|         } | ||||
|  | ||||
|         public bool AddStatusEffect(uint id, byte tier) | ||||
|         public bool AddStatusEffect(uint id, byte tier, CommandResultContainer actionContainer = null, ushort worldmasterTextId = 30328) | ||||
|         { | ||||
|             var se = Server.GetWorldManager().GetStatusEffect(id); | ||||
|  | ||||
|             se.SetTier(tier); | ||||
|  | ||||
|             return AddStatusEffect(se, owner); | ||||
|             return AddStatusEffect(se, owner, actionContainer, worldmasterTextId); | ||||
|         } | ||||
|  | ||||
|         public bool AddStatusEffect(uint id,  byte tier, double magnitude) | ||||
|         public bool AddStatusEffect(uint id, byte tier, double magnitude, CommandResultContainer actionContainer = null, ushort worldmasterTextId = 30328) | ||||
|         { | ||||
|             var se = Server.GetWorldManager().GetStatusEffect(id); | ||||
|  | ||||
|             se.SetMagnitude(magnitude); | ||||
|             se.SetTier(tier); | ||||
|  | ||||
|             return AddStatusEffect(se, owner); | ||||
|             return AddStatusEffect(se, owner, actionContainer, worldmasterTextId); | ||||
|         } | ||||
|  | ||||
|         public bool AddStatusEffect(uint id, byte tier, double magnitude, uint duration, int tickMs = 3000) | ||||
|         public bool AddStatusEffect(uint id, byte tier, double magnitude, uint duration, int tickMs, CommandResultContainer actionContainer = null, ushort worldmasterTextId = 30328) | ||||
|         { | ||||
|             var se = Server.GetWorldManager().GetStatusEffect(id); | ||||
|             if (se != null) | ||||
|             { | ||||
|                 se.SetDuration(duration); | ||||
|                 se.SetStartTime(DateTime.Now); | ||||
|                 se.SetOwner(owner); | ||||
|             } | ||||
|             return AddStatusEffect(se ?? new StatusEffect(this.owner, id, magnitude, 3000, duration, tier), owner); | ||||
|             return AddStatusEffect(se ?? new StatusEffect(this.owner, id, magnitude, 3000, duration, tier), owner, actionContainer, worldmasterTextId); | ||||
|         } | ||||
|  | ||||
|         public bool AddStatusEffect(StatusEffect newEffect, Character source, bool silent = false, bool hidden = false) | ||||
|         public bool AddStatusEffect(StatusEffect newEffect, Character source, CommandResultContainer actionContainer = null, ushort worldmasterTextId = 30328) | ||||
|         { | ||||
|             /* | ||||
|                 worldMasterTextId | ||||
| @@ -179,9 +168,10 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai | ||||
|             if (canOverwrite || effect == null) | ||||
|             { | ||||
|                 // send packet to client with effect added message | ||||
|                 if (effect != null && (!silent  || !effect.GetSilent() || (effect.GetFlags() & (uint)StatusEffectFlags.Silent) == 0)) | ||||
|                 if (newEffect != null && !newEffect.GetSilentOnGain()) | ||||
|                 { | ||||
|                     // todo: send packet to client with effect added message | ||||
|                     if (actionContainer != null) | ||||
|                         actionContainer.AddAction(new CommandResult(owner.actorId, worldmasterTextId, newEffect.GetStatusEffectId() | (uint)HitEffect.StatusEffectType)); | ||||
|                 } | ||||
|  | ||||
|                 // wont send a message about losing effect here | ||||
| @@ -194,13 +184,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai | ||||
|  | ||||
|                 if (effects.Count < MAX_EFFECTS) | ||||
|                 { | ||||
|                     if(newEffect.script != null) | ||||
|                         newEffect.CallLuaFunction(this.owner, "onGain", this.owner, newEffect); | ||||
|                     else | ||||
|                         LuaEngine.CallLuaStatusEffectFunction(this.owner, newEffect, "onGain", this.owner, newEffect); | ||||
|                     newEffect.CallLuaFunction(this.owner, "onGain", this.owner, newEffect, actionContainer); | ||||
|  | ||||
|                     effects.Add(newEffect.GetStatusEffectId(), newEffect); | ||||
|                     //newEffect.SetSilent(silent); | ||||
|                     newEffect.SetHidden(hidden); | ||||
|  | ||||
|                     if (!newEffect.GetHidden()) | ||||
|                     { | ||||
| @@ -225,15 +211,19 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         public bool RemoveStatusEffect(StatusEffect effect, bool silent = false) | ||||
|         //playEffect determines whether the effect of the animation that's going to play with actionContainer is going to play on owner | ||||
|         //Generally, for abilities removing an effect, this is true and for effects removing themselves it's false. | ||||
|         public bool RemoveStatusEffect(StatusEffect effect, CommandResultContainer actionContainer = null, ushort worldmasterTextId = 30331, bool playEffect = true) | ||||
|         { | ||||
|             bool removedEffect = false; | ||||
|             if (effect != null && effects.ContainsKey(effect.GetStatusEffectId())) | ||||
|             { | ||||
|                 // send packet to client with effect remove message | ||||
|                 if (!silent && !effect.GetSilent() && (effect.GetFlags() & (uint)StatusEffectFlags.Silent) == 0) | ||||
|                 if (!effect.GetSilentOnLoss()) | ||||
|                 { | ||||
|                     owner.DoBattleAction(0, 0, new CommandResult(owner.actorId, 30331, effect.GetStatusEffectId())); | ||||
|                     //Only send a message if we're using an actioncontainer and the effect normally sends a message when it's lost | ||||
|                     if (actionContainer != null) | ||||
|                         actionContainer.AddAction(new CommandResult(owner.actorId, worldmasterTextId, effect.GetStatusEffectId() | (playEffect ? 0 : (uint)HitEffect.StatusLossType))); | ||||
|                 } | ||||
|  | ||||
|                 //hidden effects not in charawork | ||||
| @@ -243,54 +233,20 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai | ||||
|                     SetStatusAtIndex(index, 0); | ||||
|                     SetTimeAtIndex(index, 0); | ||||
|                 } | ||||
|  | ||||
|                 // function onLose(actor, effect) | ||||
|                 effects.Remove(effect.GetStatusEffectId()); | ||||
|                 if(effect.script != null) | ||||
|                     effect.CallLuaFunction(owner, "onLose", owner, effect); | ||||
|                 else | ||||
|                     LuaEngine.CallLuaStatusEffectFunction(this.owner, effect, "onLose", this.owner, effect); | ||||
|                 effect.CallLuaFunction(owner, "onLose", owner, effect, actionContainer); | ||||
|                 owner.RecalculateStats(); | ||||
|                 sendUpdate = true; | ||||
|                 removedEffect = true; | ||||
|             } | ||||
|  | ||||
|             return removedEffect; | ||||
|         } | ||||
|  | ||||
|         public bool RemoveStatusEffect(uint effectId, bool silent = false) | ||||
|         public bool RemoveStatusEffect(uint effectId, CommandResultContainer resultContainer = null, ushort worldmasterTextId = 30331, bool playEffect = true) | ||||
|         { | ||||
|             bool removedEffect = false; | ||||
|             foreach (var effect in effects.Values) | ||||
|             { | ||||
|                 if (effect.GetStatusEffectId() == effectId) | ||||
|                 { | ||||
|                     RemoveStatusEffect(effect, effect.GetSilent() || silent); | ||||
|                     removedEffect = true; | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return removedEffect; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         //Remove status effect and return the CommandResult message instead of sending it immediately | ||||
|         public CommandResult RemoveStatusEffectForCommandResult(uint effectId, ushort worldMasterTextId = 30331) | ||||
|         { | ||||
|             CommandResult action = null; | ||||
|             if (RemoveStatusEffect(effectId, true)) | ||||
|                 action = new CommandResult(owner.actorId, worldMasterTextId, effectId); | ||||
|  | ||||
|             return action; | ||||
|         } | ||||
|  | ||||
|         //Remove status effect and return the CommandResult message instead of sending it immediately | ||||
|         public CommandResult RemoveStatusEffectForCommandResult(StatusEffect effect, ushort worldMasterTextId = 30331) | ||||
|         { | ||||
|             CommandResult action = null; | ||||
|             if (RemoveStatusEffect(effect, true)) | ||||
|                 action = new CommandResult(owner.actorId, worldMasterTextId, effect.GetStatusEffectId()); | ||||
|             return action; | ||||
|             return RemoveStatusEffect(GetStatusEffectById(effectId), resultContainer, worldmasterTextId, playEffect); | ||||
|         } | ||||
|  | ||||
|         public StatusEffect CopyEffect(StatusEffect effect) | ||||
| @@ -301,14 +257,14 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai | ||||
|             return AddStatusEffect(newEffect, effect.GetSource()) ? newEffect : null; | ||||
|         } | ||||
|  | ||||
|         public bool RemoveStatusEffectsByFlags(uint flags, bool silent = false) | ||||
|         public bool RemoveStatusEffectsByFlags(uint flags, CommandResultContainer resultContainer = null) | ||||
|         { | ||||
|             // build list of effects to remove | ||||
|             var removeEffects = GetStatusEffectsByFlag(flags); | ||||
|  | ||||
|             // remove effects from main list | ||||
|             foreach (var effect in removeEffects) | ||||
|                 RemoveStatusEffect(effect, silent); | ||||
|                 RemoveStatusEffect(effect, resultContainer, effect.GetStatusLossTextId(), true); | ||||
|  | ||||
|             // removed an effect with one of these flags | ||||
|             return removeEffects.Count > 0; | ||||
| @@ -334,6 +290,16 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai | ||||
|             return list; | ||||
|         } | ||||
|  | ||||
|         public StatusEffect GetRandomEffectByFlag(uint flag) | ||||
|         { | ||||
|             var list = GetStatusEffectsByFlag(flag); | ||||
|  | ||||
|             if (list.Count > 0) | ||||
|                 return list[Program.Random.Next(list.Count)]; | ||||
|  | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         // todo: why the fuck cant c# convert enums/ | ||||
|         public bool HasStatusEffectsByFlag(StatusEffectFlags flags) | ||||
|         { | ||||
| @@ -442,7 +408,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai | ||||
|             SetStatusAtIndex(index, (ushort) (newEffectId - 200000)); | ||||
|             SetTimeAtIndex(index, time); | ||||
|  | ||||
|             return new CommandResult(owner.actorId, 30328, (uint) HitEffect.StatusEffectType | newEffectId); | ||||
|             return new CommandResult(owner.actorId, 30330, (uint) HitEffect.StatusEffectType | newEffectId); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -210,7 +210,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers | ||||
|                 Disengage(); | ||||
|                 return; | ||||
|             } | ||||
|             owner.SetMod((uint)Modifier.Speed, 5); | ||||
|             owner.SetMod((uint)Modifier.MovementSpeed, 5); | ||||
|             if ((tick - lastCombatTickScript).TotalSeconds > 3) | ||||
|             { | ||||
|                 Move(); | ||||
| @@ -363,7 +363,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers | ||||
|  | ||||
|             // todo: seems ffxiv doesnt even differentiate between sneak/invis? | ||||
|             { | ||||
|                 hasSneak = target.statusEffects.HasStatusEffectsByFlag((uint)StatusEffectFlags.Stealth); | ||||
|                 hasSneak = target.GetMod(Modifier.Stealth) > 0; | ||||
|                 hasInvisible = hasSneak; | ||||
|             } | ||||
|  | ||||
|   | ||||
| @@ -16,7 +16,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state | ||||
|         { | ||||
|             owner.Disengage(); | ||||
|             owner.ChangeState(SetActorStatePacket.MAIN_STATE_DEAD); | ||||
|             owner.statusEffects.RemoveStatusEffectsByFlags((uint)StatusEffectFlags.LoseOnDeath, true); | ||||
|             owner.statusEffects.RemoveStatusEffectsByFlags((uint)StatusEffectFlags.LoseOnDeath); | ||||
|             //var deathStatePacket = SetActorStatePacket.BuildPacket(owner.actorId, SetActorStatePacket.MAIN_STATE_DEAD2, owner.currentSubState); | ||||
|             //owner.zone.BroadcastPacketAroundActor(owner, deathStatePacket); | ||||
|             canInterrupt = false; | ||||
|   | ||||
| @@ -303,7 +303,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils | ||||
|         public static double GetParryRate(Character attacker, Character defender, BattleCommand skill, CommandResult action) | ||||
|         { | ||||
|             //Can't parry with shield, can't parry rear attacks | ||||
|             if (defender.GetMod((uint)Modifier.HasShield) != 0 || action.param == (byte) HitDirection.Rear) | ||||
|             if (defender.GetMod((uint)Modifier.CanBlock) != 0 || action.param == (byte) HitDirection.Rear) | ||||
|                 return 0; | ||||
|  | ||||
|             double parryRate = 10.0; | ||||
| @@ -348,7 +348,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils | ||||
|         public static double GetBlockRate(Character attacker, Character defender, BattleCommand skill, CommandResult action) | ||||
|         { | ||||
|             //Shields are required to block and can't block from rear. | ||||
|             if (defender.GetMod((uint)Modifier.HasShield) == 0 || action.param == (byte)HitDirection.Rear) | ||||
|             if (defender.GetMod((uint)Modifier.CanBlock) == 0 || action.param == (byte)HitDirection.Rear) | ||||
|                 return 0; | ||||
|  | ||||
|             short dlvl = (short)(defender.GetLevel() - attacker.GetLevel()); | ||||
| @@ -894,10 +894,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils | ||||
|                 totalBonus += GetChainBonus(expChainNumber); | ||||
|  | ||||
|                 StatusEffect newChain = Server.GetWorldManager().GetStatusEffect((uint)StatusEffectId.EXPChain); | ||||
|                 newChain.SetSilent(true); | ||||
|                 newChain.SetDuration(timeLimit); | ||||
|                 newChain.SetTier((byte)(Math.Min(expChainNumber + 1, 255))); | ||||
|                 attacker.statusEffects.AddStatusEffect(newChain, attacker, true, true); | ||||
|                 attacker.statusEffects.AddStatusEffect(newChain, attacker); | ||||
|  | ||||
|                 actionContainer?.AddEXPActions(attacker.AddExp(baseExp, (byte)attacker.GetClass(), (byte)(totalBonus.Min(255)))); | ||||
|             } | ||||
|   | ||||
| @@ -455,11 +455,11 @@ namespace FFXIVClassic_Map_Server.Actors | ||||
|                 mobModifiers.Add((MobModifier)mobModId, val); | ||||
|         } | ||||
|  | ||||
|         public override void OnDamageTaken(Character attacker, CommandResult action,  CommandResultContainer actionContainer = null) | ||||
|         public override void OnDamageTaken(Character attacker, BattleCommand skill, CommandResult action,  CommandResultContainer actionContainer = null) | ||||
|         { | ||||
|             if (GetMobMod((uint)MobModifier.DefendScript) != 0) | ||||
|                 lua.LuaEngine.CallLuaBattleFunction(this, "onDamageTaken", this, attacker, action.amount); | ||||
|             base.OnDamageTaken(attacker, action, actionContainer); | ||||
|             base.OnDamageTaken(attacker, skill, action, actionContainer); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -706,10 +706,11 @@ namespace FFXIVClassic_Map_Server.Actors | ||||
|             this.positionZ = destinationZ; | ||||
|             this.rotation = destinationRot; | ||||
|  | ||||
|             this.statusEffects.RemoveStatusEffectsByFlags((uint)StatusEffectFlags.LoseOnZoning); | ||||
|  | ||||
|             //Save Player | ||||
|             Database.SavePlayerPlayTime(this); | ||||
|             Database.SavePlayerPosition(this); | ||||
|             this.statusEffects.RemoveStatusEffectsByFlags((uint)StatusEffectFlags.LoseOnZoning, true); | ||||
|             Database.SavePlayerStatusEffects(this); | ||||
|         } | ||||
|  | ||||
| @@ -2476,7 +2477,7 @@ namespace FFXIVClassic_Map_Server.Actors | ||||
|                 StatusEffect comboEffect = new StatusEffect(this, Server.GetWorldManager().GetStatusEffect((uint) StatusEffectId.Combo)); | ||||
|                 comboEffect.SetDuration(13); | ||||
|                 comboEffect.SetOverwritable(1); | ||||
|                 statusEffects.AddStatusEffect(comboEffect, this, true); | ||||
|                 statusEffects.AddStatusEffect(comboEffect, this); | ||||
|                 playerWork.comboCostBonusRate = 1; | ||||
|             } | ||||
|             //Otherwise we're ending a combo, remove the status | ||||
| @@ -2512,10 +2513,10 @@ namespace FFXIVClassic_Map_Server.Actors | ||||
|             } | ||||
|  | ||||
|             var hasShield = equip.GetItemAtSlot(Equipment.SLOT_OFFHAND) != null ? 1 : 0; | ||||
|             SetMod((uint)Modifier.HasShield, hasShield); | ||||
|             SetMod((uint)Modifier.CanBlock, hasShield); | ||||
|  | ||||
|             SetMod((uint)Modifier.AttackType, damageAttribute); | ||||
|             SetMod((uint)Modifier.AttackDelay, attackDelay); | ||||
|             SetMod((uint)Modifier.Delay, attackDelay); | ||||
|             SetMod((uint)Modifier.HitCount, hitCount); | ||||
|  | ||||
|             //These stats all correlate in a 3:2 fashion | ||||
| @@ -2525,13 +2526,13 @@ namespace FFXIVClassic_Map_Server.Actors | ||||
|             AddMod((uint)Modifier.Defense, (long)(GetMod(Modifier.Vitality) * 0.667)); | ||||
|  | ||||
|             //These stats correlate in a 4:1 fashion. (Unsure if MND is accurate but it would make sense for it to be) | ||||
|             AddMod((uint)Modifier.MagicAttack, (long)((float)GetMod(Modifier.Intelligence) * 0.25)); | ||||
|             AddMod((uint)Modifier.AttackMagicPotency, (long)((float)GetMod(Modifier.Intelligence) * 0.25)); | ||||
|  | ||||
|             AddMod((uint)Modifier.MagicAccuracy, (long)((float)GetMod(Modifier.Mind) * 0.25)); | ||||
|             AddMod((uint)Modifier.MagicHeal, (long)((float)GetMod(Modifier.Mind) * 0.25)); | ||||
|             AddMod((uint)Modifier.HealingMagicPotency, (long)((float)GetMod(Modifier.Mind) * 0.25)); | ||||
|  | ||||
|             AddMod((uint)Modifier.MagicEvasion, (long)((float)GetMod(Modifier.Piety) * 0.25)); | ||||
|             AddMod((uint)Modifier.MagicEnfeeblingPotency, (long)((float)GetMod(Modifier.Piety) * 0.25)); | ||||
|             AddMod((uint)Modifier.EnfeeblingMagicPotency, (long)((float)GetMod(Modifier.Piety) * 0.25)); | ||||
|  | ||||
|             //VIT correlates to HP in a 1:1 fashion | ||||
|             AddMod((uint)Modifier.Hp, (long)((float)Modifier.Vitality)); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user