Example #1
0
        //Calculates bonus EXP for Links and Chains
        public static void AddBattleBonusEXP(Player attacker, BattleNpc defender, CommandResultContainer actionContainer)
        {
            ushort baseExp = GetBaseEXP(attacker, defender);

            //Only bother calculating the rest if there's actually exp to be gained.
            //0 exp sends no message
            if (baseExp > 0)
            {
                int totalBonus = 0;//GetMod(Modifier.bonusEXP)

                var linkCount = defender.GetMobMod(MobModifier.LinkCount);
                totalBonus += GetLinkBonus((byte)Math.Min(linkCount, 255));

                StatusEffect effect         = attacker.statusEffects.GetStatusEffectById((uint)StatusEffectId.EXPChain);
                ushort       expChainNumber = 0;
                uint         timeLimit      = 100;
                if (effect != null)
                {
                    expChainNumber = effect.GetTier();
                    timeLimit      = (uint)(GetChainTimeLimit(expChainNumber));
                    actionContainer?.AddEXPAction(new CommandResult(attacker.actorId, 33919, 0, expChainNumber, (byte)timeLimit));
                }

                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);

                actionContainer?.AddEXPActions(attacker.AddExp(baseExp, (byte)attacker.GetClass(), (byte)(totalBonus.Min(255))));
            }
        }
Example #2
0
        public virtual void OnDamageTaken(Character attacker, CommandResult action, CommandResultContainer actionContainer = null)
        {
            switch (action.hitType)
            {
            case (HitType.Miss):
                OnEvade(attacker, action, actionContainer);
                break;

            case (HitType.Parry):
                OnParry(attacker, action, actionContainer);
                break;

            case (HitType.Block):
                OnBlock(attacker, action, actionContainer);
                break;
            }

            statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnDamageTaken, "onDamageTaken", attacker, this, action);

            //TP gain formula seems to be something like 5 * e ^ ( -0.667 * [defender's level] ) * damage taken, rounded up
            //This should be completely accurate at level 50, but isn't totally accurate at lower levels.
            //Don't know if store tp impacts this
            double tpModifier = 5 * Math.Pow(Math.E, (-0.0667 * GetLevel()));

            AddTP((int)Math.Ceiling(tpModifier * action.amount));


            if (charaWork.parameterSave.hp[0] < 1)
            {
                Die(Program.Tick, actionContainer);
            }
        }
Example #3
0
 public override void OnDamageTaken(Character attacker, 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);
 }
Example #4
0
        public override void Die(DateTime tick, CommandResultContainer actionContainer = null)
        {
            if (IsAlive())
            {
                // todo: does retail
                if (lastAttacker is Pet && lastAttacker.aiContainer.GetController <PetController>() != null && lastAttacker.aiContainer.GetController <PetController>().GetPetMaster() is Player)
                {
                    lastAttacker = lastAttacker.aiContainer.GetController <PetController>().GetPetMaster();
                }

                if (lastAttacker is Player)
                {
                    //I think this is, or should be odne in DoBattleAction. Packet capture had the message in the same packet as an attack
                    // <actor> defeat/defeats <target>
                    if (actionContainer != null)
                    {
                        actionContainer.AddEXPAction(new CommandResult(actorId, 30108, 0));
                    }
                    if (lastAttacker.currentParty != null && lastAttacker.currentParty is Party)
                    {
                        foreach (var memberId in ((Party)lastAttacker.currentParty).members)
                        {
                            var partyMember = zone.FindActorInArea <Character>(memberId);
                            // onDeath(monster, player, killer)
                            lua.LuaEngine.CallLuaBattleFunction(this, "onDeath", this, partyMember, lastAttacker);

                            // todo: add actual experience calculation and exp bonus values.
                            if (partyMember is Player)
                            {
                                BattleUtils.AddBattleBonusEXP((Player)partyMember, this, actionContainer);
                            }
                        }
                    }
                    else
                    {
                        // onDeath(monster, player, killer)
                        lua.LuaEngine.CallLuaBattleFunction(this, "onDeath", this, lastAttacker, lastAttacker);
                        //((Player)lastAttacker).QueuePacket(BattleActionX01Packet.BuildPacket(lastAttacker.actorId, 0, 0, new BattleAction(actorId, 30108, 0)));
                    }
                }

                if (positionUpdates != null)
                {
                    positionUpdates.Clear();
                }
                aiContainer.InternalDie(tick, despawnTime);
                //this.ResetMoveSpeeds();
                // todo: reset cooldowns

                lua.LuaEngine.GetInstance().OnSignal("mobkill");
            }
            else
            {
                var err = String.Format("[{0}][{1}] {2} {3} {4} {5} tried to die ded", actorId, GetUniqueId(), positionX, positionY, positionZ, GetZone().GetName());
                Program.Log.Error(err);
                //throw new Exception(err);
            }
        }
Example #5
0
        public override void OnComplete()
        {
            //BattleAction action = new BattleAction(target.actorId, 0x765D, (uint) HitEffect.Hit, 0, (byte) HitDirection.None);
            errorResult = null;

            // todo: implement auto attack damage bonus in Character.OnAttack

            /*
             * ≪Auto-attack Damage Bonus≫
             * Class        Bonus 1       Bonus 2
             * Pugilist     Intelligence  Strength
             * Gladiator    Mind          Strength
             * Marauder     Vitality      Strength
             * Archer       Dexterity     Piety
             * Lancer       Piety         Strength
             * Conjurer     Mind          Piety
             * Thaumaturge  Mind          Piety
             * The above damage bonus also applies to “Shot” attacks by archers.
             */
            // handle paralyze/intimidate/sleep/whatever in Character.OnAttack


            // todo: Change this to use a BattleCommand like the other states

            //List<BattleAction> actions = new List<BattleAction>();
            CommandResultContainer actions = new CommandResultContainer();

            var i = 0;

            for (int hitNum = 0; hitNum < 1 /* owner.GetMod((uint) Modifier.HitCount)*/; hitNum++)
            {
                CommandResult action = new CommandResult(target.actorId, 0x765D, (uint)HitEffect.Hit, 100, (byte)HitDirection.None, (byte)hitNum);
                action.commandType    = CommandType.AutoAttack;
                action.actionType     = ActionType.Physical;
                action.actionProperty = (ActionProperty)owner.GetMod(Modifier.AttackType);
                // evasion, miss, dodge, etc to be handled in script, calling helpers from scripts/weaponskills.lua
                // temporary evade/miss/etc function to test hit effects
                action.DoAction(owner, target, null, actions);
            }

            // todo: this is f****n stupid, probably only need *one* error packet, not an error for each action
            CommandResult[] errors = (CommandResult[])actions.GetList().ToArray().Clone();
            CommandResult   error  = null;// new BattleAction(0, null, 0, 0);
            //owner.DoActions(null, actions.GetList(), ref error);
            //owner.OnAttack(this, actions[0], ref errorResult);
            var anim = (uint)(17 << 24 | 1 << 12);

            owner.DoBattleAction(22104, anim, actions.GetList());
        }
Example #6
0
        public virtual void OnDamageDealt(Character defender, CommandResult action, CommandResultContainer actionContainer = null)
        {
            switch (action.hitType)
            {
            case (HitType.Miss):
                OnMiss(this, action, actionContainer);
                break;

            default:
                OnHit(defender, action, actionContainer);
                break;
            }

            //TP is only gained from autoattacks and abilities
            if (action.commandType == CommandType.AutoAttack || action.commandType == CommandType.Ability)
            {
                //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);
                AddTP((int)(weaponDelay * 100 * storeTPPercent));
            }
        }
Example #7
0
        public static void SetHitEffectSpell(Character attacker, Character defender, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
        {
            var     hitEffect = HitEffect.MagicEffectType;
            HitType hitType   = action.hitType;

            //Recoil levels for spells are a bit different than physical. Recoil levels are used for resists.
            //Lv1 is for larger resists, Lv2 is for smaller resists and Lv3 is for no resists. Crit is still used for crits
            if (hitType == HitType.Resist)
            {
                //todo: calculate resist levels and figure out what the difference between Lv1 and 2 in retail was. For now assuming a full resist with 0 damage dealt is Lv1, all other resists Lv2
                if (action.amount == 0)
                {
                    hitEffect |= HitEffect.RecoilLv1;
                }
                else
                {
                    hitEffect |= HitEffect.RecoilLv2;
                }
            }
            else
            {
                hitEffect |= HitEffect.RecoilLv3;
            }

            hitEffect |= HitTypeEffects[hitType];

            if (skill != null && skill.isCombo && !skill.comboEffectAdded)
            {
                hitEffect |= (HitEffect)(skill.comboStep << 15);
                skill.comboEffectAdded = true;
            }

            //if attack hit the target, take into account protective status effects
            if (hitType >= HitType.Block)
            {
                //Protect / Shell only show on physical/ magical attacks respectively.
                if (defender.statusEffects.HasStatusEffect(StatusEffectId.Shell))
                {
                    if (action != null)
                    {
                        hitEffect |= HitEffect.Shell;
                    }
                }

                if (defender.statusEffects.HasStatusEffect(StatusEffectId.Stoneskin))
                {
                    if (action != null)
                    {
                        hitEffect |= HitEffect.Stoneskin;
                    }
                }
            }
            action.effectId = (uint)hitEffect;
        }
Example #8
0
        public static void DamageTarget(Character attacker, Character defender, CommandResult action, CommandResultContainer actionContainer = null)
        {
            if (defender != null)
            {
                defender.DelHP((short)action.amount);
                attacker.OnDamageDealt(defender, action, actionContainer);
                defender.OnDamageTaken(attacker, action, actionContainer);

                // todo: other stuff too
                if (defender is BattleNpc)
                {
                    var bnpc = defender as BattleNpc;
                    if (!bnpc.hateContainer.HasHateForTarget(attacker))
                    {
                        bnpc.hateContainer.AddBaseHate(attacker);
                    }
                    bnpc.hateContainer.UpdateHate(attacker, action.enmity);
                    bnpc.lastAttacker = attacker;
                }
            }
        }
Example #9
0
        //The order of messages that appears after using a command is:

        //1. Cast start messages. (ie "You begin casting... ")
        //2. Messages from buffs that activate before the command actually starts, like Power Surge or Presence of Mind. (This may be wrong and these could be the same as 4.)
        //3. If the command is a multi-hit command, this is where the "You use [command] on [target]" message goes

        //Then, for each hit:
        //4. Buffs that activate before a command hits, like Blindside
        //5. The hit itself. For single hit commands this message is "Your [command] hits [target] for x damage" for multi hits it's "[Target] takes x points of damage"
        //6. Stoneskin falling off
        //6. Buffs that activate after a command hits, like Aegis Boon and Divine Veil

        //After all hits
        //7. If it's a multi-hit command there's a "{numhits]fold attack..." message or if all hits miss an "All attacks missed" message
        //8. Buffs that fall off after the skill ends, like Excruciate

        //For every target defeated:
        //8. Defeat message
        //9. EXP message
        //10. EXP chain message


        //folder is probably temporary until move to cached scripts is complete
        public void DoBattleCommand(BattleCommand command, string folder)
        {
            //List<BattleAction> actions = new List<BattleAction>();
            CommandResultContainer actions = new CommandResultContainer();

            var  targets   = command.targetFind.GetTargets();
            bool hitTarget = false;

            if (targets.Count > 0)
            {
                statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnCommandStart, "onCommandStart", this, command, actions);

                foreach (var chara in targets)
                {
                    ushort hitCount    = 0;
                    ushort totalDamage = 0;
                    for (int hitNum = 1; hitNum <= command.numHits; hitNum++)
                    {
                        var action = new CommandResult(chara.actorId, command, (byte)GetHitDirection(chara), (byte)hitNum);

                        //uncached script
                        lua.LuaEngine.CallLuaBattleCommandFunction(this, command, folder, "onSkillFinish", this, chara, command, action, actions);
                        //cached script
                        //skill.CallLuaFunction(owner, "onSkillFinish", this, chara, command, action, actions);
                        if (action.hitType > HitType.Evade && action.hitType != HitType.Resist)
                        {
                            hitTarget = true;
                            hitCount++;
                            totalDamage += action.amount;
                        }
                    }

                    if (command.numHits > 1)
                    {
                        //30442: [hitCount]fold Attack! [chara] takes a total of totalDamage points of damage.
                        //30450: All attacks miss!
                        ushort textId = (ushort)(hitTarget ? 30442 : 30450);
                        actions.AddAction(new CommandResult(chara.actorId, textId, 0, totalDamage, (byte)hitCount));
                    }
                }

                statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnCommandFinish, "onCommandFinish", this, command, actions);
            }
            else
            {
                actions.AddAction(new CommandResult(actorId, 30202, 0));
            }

            //Now that we know if we hit the target we can check if the combo continues
            if (this is Player)
            {
                if (command.isCombo && hitTarget)
                {
                    ((Player)this).SetCombos(command.comboNextCommandId);
                }
                else
                {
                    ((Player)this).SetCombos();
                }
            }

            CommandResult error = new CommandResult(actorId, 0, 0);

            DelMP(command.CalculateMpCost(this));
            DelTP(command.CalculateTpCost(this));
            actions.CombineLists();
            DoBattleAction(command.id, command.battleAnimation, actions.GetList());
        }
Example #10
0
 //AdditionalActions is the list of actions that EXP/Chain messages are added to
 public virtual void Die(DateTime tick, CommandResultContainer actionContainer = null)
 {
     // todo: actual despawn timer
     aiContainer.InternalDie(tick, 10);
     ChangeSpeed(0.0f, 0.0f, 0.0f, 0.0f);
 }
Example #11
0
 //Called when this character misses
 public void OnMiss(Character defender, CommandResult action, CommandResultContainer actionContainer = null)
 {
     SetProc((ushort)HitType.Miss);
     statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnMiss, "onMiss", this, defender, action, actionContainer);
 }
Example #12
0
 public void OnHit(Character defender, CommandResult action, CommandResultContainer actionContainer = null)
 {
     statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnHit, "onHit", this, defender, action, actionContainer);
 }
Example #13
0
        public static void HealTarget(Character caster, Character target, CommandResult action, CommandResultContainer actionContainer = null)
        {
            if (target != null)
            {
                target.AddHP(action.amount);

                target.statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnHealed, "onHealed", caster, target, action, actionContainer);
            }
        }
Example #14
0
 //Called when this character parries attacker's action
 public void OnParry(Character attacker, CommandResult action, CommandResultContainer actionContainer = null)
 {
     SetProc((ushort)HitType.Parry);
     statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnParry, "onParry", attacker, this, action, actionContainer);
 }
Example #15
0
        /*
         * Hit Effecthelpers. Different types of hit effects hits use some flags for different things, so they're split into physical, magical, heal, and status
         */
        public static void DoAction(Character caster, Character target, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
        {
            switch (action.actionType)
            {
            case (ActionType.Physical):
                FinishActionPhysical(caster, target, skill, action, actionContainer);
                break;

            case (ActionType.Magic):
                FinishActionSpell(caster, target, skill, action, actionContainer);
                break;

            case (ActionType.Heal):
                FinishActionHeal(caster, target, skill, action, actionContainer);
                break;

            case (ActionType.Status):
                FinishActionStatus(caster, target, skill, action, actionContainer);
                break;

            default:
                actionContainer.AddAction(action);
                break;
            }
        }
Example #16
0
        //IsAdditional is needed because additional actions may be required for some actions' effects
        //For instance, Goring Blade's bleed effect requires another action so the first action can still show damage numbers
        //Sentinel doesn't require an additional action because it doesn't need to show those numbers
        //this is stupid
        public static void TryStatus(Character caster, Character target, BattleCommand skill, CommandResult action, CommandResultContainer results, bool isAdditional = true)
        {
            double rand = Program.Random.NextDouble();

            //Statuses only land for non-resisted attacks and attacks that hit
            if (skill != null && skill.statusId != 0 && (action.hitType > HitType.Evade && action.hitType != HitType.Resist) && rand < skill.statusChance)
            {
                StatusEffect effect = Server.GetWorldManager().GetStatusEffect(skill.statusId);
                //Because combos might change duration or tier
                if (effect != null)
                {
                    effect.SetDuration(skill.statusDuration);
                    effect.SetTier(skill.statusTier);
                    effect.SetMagnitude(skill.statusMagnitude);
                    effect.SetOwner(target);
                    effect.SetSource(caster);

                    if (target.statusEffects.AddStatusEffect(effect, caster))
                    {
                        //If we need an extra action to show the status text
                        if (isAdditional)
                        {
                            results.AddAction(target.actorId, 30328, skill.statusId | (uint)HitEffect.StatusEffectType);
                        }
                    }
                    else
                    {
                        action.worldMasterTextId = 32002;//Is this right?
                    }
                }
                else
                {
                    //until all effects are scripted and added to db just doing this
                    if (target.statusEffects.AddStatusEffect(skill.statusId, skill.statusTier, skill.statusMagnitude, skill.statusDuration, 3000))
                    {
                        //If we need an extra action to show the status text
                        if (isAdditional)
                        {
                            results.AddAction(target.actorId, 30328, skill.statusId | (uint)HitEffect.StatusEffectType);
                        }
                    }
                    else
                    {
                        action.worldMasterTextId = 32002;//Is this right?
                    }
                }
            }
        }
Example #17
0
        public static void SetHitEffectPhysical(Character attacker, Character defender, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer)
        {
            var     hitEffect = HitEffect.HitEffectType;
            HitType hitType   = action.hitType;

            //Don't know what recoil is actually based on, just guessing
            //Crit is 2 and 3 together
            if (hitType == HitType.Crit)
            {
                hitEffect |= HitEffect.CriticalHit;
            }
            else
            {
                //It's not clear what recoil level is based on for physical attacks
                double percentDealt = (100.0 * (action.amount / defender.GetMaxHP()));
                if (percentDealt > 5.0)
                {
                    hitEffect |= HitEffect.RecoilLv2;
                }
                else if (percentDealt > 10)
                {
                    hitEffect |= HitEffect.RecoilLv3;
                }
            }

            hitEffect |= HitTypeEffects[hitType];

            //For combos that land, add the combo effect
            if (skill != null && skill.isCombo && hitType > HitType.Evade && hitType != HitType.Evade && !skill.comboEffectAdded)
            {
                hitEffect |= (HitEffect)(skill.comboStep << 15);
                skill.comboEffectAdded = true;
            }

            //if attack hit the target, take into account protective status effects
            if (hitType >= HitType.Parry)
            {
                //Protect / Shell only show on physical/ magical attacks respectively.
                if (defender.statusEffects.HasStatusEffect(StatusEffectId.Protect))
                {
                    if (action != null)
                    {
                        hitEffect |= HitEffect.Protect;
                    }
                }

                if (defender.statusEffects.HasStatusEffect(StatusEffectId.Stoneskin))
                {
                    if (action != null)
                    {
                        hitEffect |= HitEffect.Stoneskin;
                    }
                }
            }

            action.effectId = (uint)hitEffect;
        }
Example #18
0
        public static void FinishActionStatus(Character attacker, Character defender, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
        {
            //Set the hit effect
            SetHitEffectStatus(attacker, defender, skill, action);

            TryStatus(attacker, defender, skill, action, actionContainer, false);

            actionContainer.AddAction(action);
        }
Example #19
0
        public static void FinishActionHeal(Character attacker, Character defender, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
        {
            //Set the hit effect
            SetHitEffectHeal(attacker, defender, skill, action);

            actionContainer.AddAction(action);

            HealTarget(attacker, defender, action, actionContainer);
        }
Example #20
0
        public static void FinishActionSpell(Character attacker, Character defender, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
        {
            //Determine the hit type of the action
            if (!TryMiss(attacker, defender, skill, action))
            {
                HandleStoneskin(defender, action);
                if (!TryCrit(attacker, defender, skill, action))
                {
                    if (!TryResist(attacker, defender, skill, action))
                    {
                        action.hitType = HitType.Hit;
                    }
                }
            }

            //There are no multi-hit spells
            action.worldMasterTextId = SingleHitTypeTextIds[action.hitType];

            //Set the hit effect
            SetHitEffectSpell(attacker, defender, skill, action);

            HandleStoneskin(defender, action);

            CalculateSpellDamageTaken(attacker, defender, skill, action);

            actionContainer.AddAction(action);

            DamageTarget(attacker, defender, action, actionContainer);
        }
Example #21
0
        //Determine the hit type, set the hit effect, modify damage based on stoneskin and hit type, hit target
        public static void FinishActionPhysical(Character attacker, Character defender, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
        {
            //Figure out the hit type and change damage depending on hit type
            if (!TryMiss(attacker, defender, skill, action))
            {
                //Handle Stoneskin here because it seems like stoneskin mitigates damage done before taking into consideration crit/block/parry damage reductions.
                //This is based on the fact that a 0 damage attack due to stoneskin will heal for 0 with Aegis Boon, meaning Aegis Boon didn't mitigate any damage
                HandleStoneskin(defender, action);

                //Crits can't be blocked (is this true for Aegis Boon and Divine Veil?) or parried so they are checked first.
                if (!TryCrit(attacker, defender, skill, action))
                {
                    //Block and parry order don't really matter because if you can block you can't parry and vice versa
                    if (!TryBlock(attacker, defender, skill, action))
                    {
                        if (!TryParry(attacker, defender, skill, action))
                        {
                            //Finally if it's none of these, the attack was a hit
                            action.hitType = HitType.Hit;
                        }
                    }
                }
            }

            //Actions have different text ids depending on whether they're a part of a multi-hit ws or not.
            Dictionary <HitType, ushort> textIds = SingleHitTypeTextIds;

            //If this is the first hit of a multi hit command, add the "You use [command] on [target]" action
            //Needs to be done here because certain buff messages appear before it.
            if (skill != null && skill.numHits > 1)
            {
                if (action.hitNum == 1)
                {
                    actionContainer?.AddAction(new CommandResult(attacker.actorId, 30441, 0));
                }

                textIds = MultiHitTypeTextIds;
            }

            //Set the correct textId
            action.worldMasterTextId = textIds[action.hitType];

            //Set the hit effect
            SetHitEffectPhysical(attacker, defender, skill, action, actionContainer);

            //Modify damage based on defender's stats
            CalculatePhysicalDamageTaken(attacker, defender, skill, action);

            actionContainer.AddAction(action);
            action.enmity = (ushort)(action.enmity * (skill != null ? skill.enmityModifier : 1));
            //Damage the target
            DamageTarget(attacker, defender, action, actionContainer);
        }