2017-08-02 23:06:11 +01:00
|
|
|
|
using FFXIVClassic_Map_Server.Actors;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading.Tasks;
|
2017-08-25 03:52:43 +01:00
|
|
|
|
using FFXIVClassic_Map_Server.actors.chara.player;
|
2017-08-26 04:08:26 +01:00
|
|
|
|
using FFXIVClassic.Common;
|
2018-04-18 16:06:41 -05:00
|
|
|
|
using FFXIVClassic_Map_Server.packets.send.actor.battle;
|
2017-08-28 04:45:20 +01:00
|
|
|
|
using FFXIVClassic_Map_Server.actors.chara.ai.utils;
|
2018-02-15 13:20:46 -06:00
|
|
|
|
using MoonSharp.Interpreter;
|
2017-08-02 23:06:11 +01:00
|
|
|
|
|
|
|
|
|
namespace FFXIVClassic_Map_Server.actors.chara.ai
|
|
|
|
|
{
|
|
|
|
|
|
2017-08-30 00:14:14 +01:00
|
|
|
|
public enum BattleCommandRequirements : ushort
|
2017-08-02 23:06:11 +01:00
|
|
|
|
{
|
|
|
|
|
None,
|
|
|
|
|
DiscipleOfWar = 0x01,
|
|
|
|
|
DiscipeOfMagic = 0x02,
|
|
|
|
|
HandToHand = 0x04,
|
|
|
|
|
Sword = 0x08,
|
|
|
|
|
Shield = 0x10,
|
|
|
|
|
Axe = 0x20,
|
|
|
|
|
Archery = 0x40,
|
|
|
|
|
Polearm = 0x80,
|
|
|
|
|
Thaumaturgy = 0x100,
|
|
|
|
|
Conjury = 0x200
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-30 00:14:14 +01:00
|
|
|
|
public enum BattleCommandPositionBonus : byte
|
2017-08-02 23:06:11 +01:00
|
|
|
|
{
|
|
|
|
|
None,
|
|
|
|
|
Front = 0x01,
|
|
|
|
|
Rear = 0x02,
|
|
|
|
|
Flank = 0x04
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-30 00:14:14 +01:00
|
|
|
|
public enum BattleCommandProcRequirement : byte
|
2017-08-02 23:06:11 +01:00
|
|
|
|
{
|
|
|
|
|
None,
|
2018-02-15 13:20:46 -06:00
|
|
|
|
Miss,
|
|
|
|
|
Evade,
|
|
|
|
|
Parry,
|
|
|
|
|
Block
|
2017-08-02 23:06:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-09-05 05:05:25 +01:00
|
|
|
|
public enum BattleCommandValidUser : byte
|
|
|
|
|
{
|
|
|
|
|
All,
|
|
|
|
|
Player,
|
|
|
|
|
Monster
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-15 13:20:46 -06:00
|
|
|
|
public enum BattleCommandCastType : ushort
|
|
|
|
|
{
|
|
|
|
|
None,
|
|
|
|
|
Weaponskill = 1,
|
|
|
|
|
Weaponskill2 = 2,
|
|
|
|
|
BlackMagic = 3,
|
|
|
|
|
WhiteMagic = 4,
|
|
|
|
|
SongMagic = 8
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-18 16:06:41 -05:00
|
|
|
|
|
|
|
|
|
//What type of command it is
|
|
|
|
|
[Flags]
|
|
|
|
|
public enum CommandType : ushort
|
|
|
|
|
{
|
|
|
|
|
//Type of action
|
|
|
|
|
None = 0,
|
|
|
|
|
AutoAttack = 1,
|
|
|
|
|
WeaponSkill = 2,
|
|
|
|
|
Ability =3,
|
|
|
|
|
Spell = 4
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-25 18:20:20 -05:00
|
|
|
|
public enum KnockbackType : ushort
|
|
|
|
|
{
|
|
|
|
|
None = 0,
|
|
|
|
|
Level1 = 1,
|
|
|
|
|
Level2 = 2,
|
|
|
|
|
Level3 = 3,
|
|
|
|
|
Level4 = 4,
|
|
|
|
|
Level5 = 5,
|
|
|
|
|
Clockwise1 = 6,
|
|
|
|
|
Clockwise2 = 7,
|
|
|
|
|
CounterClockwise1 = 8,
|
|
|
|
|
CounterClockwise2 = 9,
|
|
|
|
|
DrawIn = 10
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-28 04:45:20 +01:00
|
|
|
|
class BattleCommand
|
2017-08-02 23:06:11 +01:00
|
|
|
|
{
|
2017-08-25 03:52:43 +01:00
|
|
|
|
public ushort id;
|
2017-08-02 23:06:11 +01:00
|
|
|
|
public string name;
|
|
|
|
|
public byte job;
|
|
|
|
|
public byte level;
|
2017-08-30 00:14:14 +01:00
|
|
|
|
public BattleCommandRequirements requirements;
|
2018-02-15 13:20:46 -06:00
|
|
|
|
public ValidTarget mainTarget; //what the skill has to be used on. ie self for flare, enemy for ring of talons even though both are self-centere aoe
|
|
|
|
|
public ValidTarget validTarget; //what type of character the skill can hit
|
|
|
|
|
public TargetFindAOEType aoeType; //shape of aoe
|
|
|
|
|
public TargetFindAOETarget aoeTarget; //where the center of the aoe is (target/self)
|
|
|
|
|
public byte numHits; //amount of hits in the skill
|
|
|
|
|
public BattleCommandPositionBonus positionBonus; //bonus for front/flank/rear
|
|
|
|
|
public BattleCommandProcRequirement procRequirement;//if the skill requires a block/parry/evade before using
|
2018-07-02 00:45:06 -05:00
|
|
|
|
public float range; //maximum distance to target to be able to use this skill
|
|
|
|
|
public float minRange; //Minimum distance to target to be able to use this skill
|
2018-02-15 13:20:46 -06:00
|
|
|
|
|
|
|
|
|
public uint statusId; //id of statuseffect that the skill might inflict
|
|
|
|
|
public uint statusDuration; //duration of statuseffect in milliseconds
|
|
|
|
|
public float statusChance; //percent chance of status landing, 0-1.0. Usually 1.0 for buffs
|
|
|
|
|
public byte castType; //casting animation, 2 for blm, 3 for whm, 8 for brd
|
|
|
|
|
public uint castTimeMs; //cast time in milliseconds
|
|
|
|
|
public uint recastTimeMs; //recast time in milliseconds
|
|
|
|
|
public uint maxRecastTimeSeconds; //maximum recast time in seconds
|
2017-08-02 23:06:11 +01:00
|
|
|
|
public ushort mpCost;
|
|
|
|
|
public ushort tpCost;
|
|
|
|
|
public byte animationType;
|
|
|
|
|
public ushort effectAnimation;
|
|
|
|
|
public ushort modelAnimation;
|
|
|
|
|
public ushort animationDurationSeconds;
|
2017-08-25 03:52:43 +01:00
|
|
|
|
public uint battleAnimation;
|
|
|
|
|
public ushort worldMasterTextId;
|
2018-07-02 00:45:06 -05:00
|
|
|
|
public float aoeRange; //Radius for circle and cone aoes, length for box aoes
|
|
|
|
|
public float aoeMinRange; //Minimum range of aoe effect for things like Lunar Dynamo or Arrow Helix
|
|
|
|
|
public float aoeConeAngle; //Angle of aoe cones
|
|
|
|
|
public float aoeRotateAngle; //Amount aoes are rotated about the target position (usually the user's position)
|
|
|
|
|
public float rangeHeight; //Total height a skill can be used against target above or below user
|
|
|
|
|
public float rangeWidth; //Width of box aoes
|
2018-02-15 13:20:46 -06:00
|
|
|
|
public int[] comboNextCommandId = new int[2]; //next two skills in a combo
|
|
|
|
|
public short comboStep; //Where in a combo string this skill is
|
2018-04-18 16:06:41 -05:00
|
|
|
|
public CommandType commandType;
|
|
|
|
|
public ActionProperty actionProperty;
|
|
|
|
|
public ActionType actionType;
|
2018-02-15 13:20:46 -06:00
|
|
|
|
|
|
|
|
|
|
2018-04-18 16:06:41 -05:00
|
|
|
|
public byte statusTier; //tier of status to put on target
|
2018-07-02 00:45:06 -05:00
|
|
|
|
public double statusMagnitude = 0; //magnitude of status to put on target
|
2018-02-15 13:20:46 -06:00
|
|
|
|
public ushort basePotency; //damage variable
|
|
|
|
|
public float enmityModifier; //multiples by damage done to get final enmity
|
|
|
|
|
public float accuracyModifier; //modifies accuracy
|
2018-04-18 16:06:41 -05:00
|
|
|
|
public float bonusCritRate; //extra crit rate
|
2018-02-15 13:20:46 -06:00
|
|
|
|
public bool isCombo;
|
2018-07-02 00:45:06 -05:00
|
|
|
|
public bool comboEffectAdded = false; //If the combo effect is added to multiple hiteffects it plays multiple times, so this keeps track of that
|
2018-06-25 18:20:20 -05:00
|
|
|
|
public bool isRanged = false;
|
2018-04-18 16:06:41 -05:00
|
|
|
|
|
|
|
|
|
public bool actionCrit; //Whether any actions were critical hits, used for Excruciate
|
|
|
|
|
|
2018-02-15 13:20:46 -06:00
|
|
|
|
public lua.LuaScript script; //cached script
|
2017-08-02 23:06:11 +01:00
|
|
|
|
|
|
|
|
|
public TargetFind targetFind;
|
2017-09-05 05:05:25 +01:00
|
|
|
|
public BattleCommandValidUser validUser;
|
2017-08-02 23:06:11 +01:00
|
|
|
|
|
2017-08-28 04:45:20 +01:00
|
|
|
|
public BattleCommand(ushort id, string name)
|
2017-08-02 23:06:11 +01:00
|
|
|
|
{
|
2017-08-25 03:52:43 +01:00
|
|
|
|
this.id = id;
|
2017-08-02 23:06:11 +01:00
|
|
|
|
this.name = name;
|
2017-08-25 03:52:43 +01:00
|
|
|
|
this.range = 0;
|
2018-02-15 13:20:46 -06:00
|
|
|
|
this.enmityModifier = 1;
|
2018-04-18 16:06:41 -05:00
|
|
|
|
this.accuracyModifier = 0;
|
2018-02-15 13:20:46 -06:00
|
|
|
|
this.statusTier = 1;
|
|
|
|
|
this.statusChance = 50;
|
|
|
|
|
this.recastTimeMs = (uint) maxRecastTimeSeconds * 1000;
|
|
|
|
|
this.isCombo = false;
|
2017-08-02 23:06:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-08-28 04:45:20 +01:00
|
|
|
|
public BattleCommand Clone()
|
2017-08-02 23:06:11 +01:00
|
|
|
|
{
|
2017-08-28 04:45:20 +01:00
|
|
|
|
return (BattleCommand)MemberwiseClone();
|
2017-08-02 23:06:11 +01:00
|
|
|
|
}
|
2017-08-29 00:33:23 -04:00
|
|
|
|
|
2018-02-15 13:20:46 -06:00
|
|
|
|
public int CallLuaFunction(Character chara, string functionName, params object[] args)
|
|
|
|
|
{
|
|
|
|
|
if (script != null && !script.Globals.Get(functionName).IsNil())
|
|
|
|
|
{
|
|
|
|
|
DynValue res = new DynValue();
|
|
|
|
|
res = script.Call(script.Globals.Get(functionName), args);
|
|
|
|
|
if (res != null)
|
|
|
|
|
return (int)res.Number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-02 23:06:11 +01:00
|
|
|
|
public bool IsSpell()
|
|
|
|
|
{
|
2018-02-15 13:20:46 -06:00
|
|
|
|
return mpCost != 0 || castTimeMs != 0;
|
2017-08-02 23:06:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool IsInstantCast()
|
|
|
|
|
{
|
2018-02-15 13:20:46 -06:00
|
|
|
|
return castTimeMs == 0;
|
2017-08-02 23:06:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-02-15 13:20:46 -06:00
|
|
|
|
//Checks whether the skill can be used on the given target
|
|
|
|
|
public bool IsValidMainTarget(Character user, Character target)
|
2017-08-02 23:06:11 +01:00
|
|
|
|
{
|
|
|
|
|
targetFind = new TargetFind(user);
|
2018-04-18 16:06:41 -05:00
|
|
|
|
|
2017-08-22 19:47:54 +01:00
|
|
|
|
if (aoeType == TargetFindAOEType.Box)
|
|
|
|
|
{
|
2018-07-02 00:45:06 -05:00
|
|
|
|
targetFind.SetAOEBox(validTarget, aoeTarget, aoeRange, rangeWidth, aoeRotateAngle);
|
2017-08-22 19:47:54 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2018-07-02 00:45:06 -05:00
|
|
|
|
targetFind.SetAOEType(validTarget, aoeType, aoeTarget, aoeRange, aoeMinRange, rangeHeight, aoeRotateAngle, aoeConeAngle);
|
2017-08-22 19:47:54 +01:00
|
|
|
|
}
|
2017-08-26 04:08:26 +01:00
|
|
|
|
|
|
|
|
|
/*
|
2017-08-28 04:45:20 +01:00
|
|
|
|
worldMasterTextId
|
2018-02-15 13:20:46 -06:00
|
|
|
|
32512 cannot be performed on a KO'd target.
|
2017-08-26 04:08:26 +01:00
|
|
|
|
32513 can only be performed on a KO'd target.
|
|
|
|
|
32514 cannot be performed on yourself.
|
|
|
|
|
32515 can only be performed on yourself.
|
|
|
|
|
32516 cannot be performed on a friendly target.
|
|
|
|
|
32517 can only be performed on a friendly target.
|
|
|
|
|
32518 cannot be performed on an enemy.
|
|
|
|
|
32519 can only be performed on an enemy,
|
2018-02-15 13:20:46 -06:00
|
|
|
|
32556 unable to execute [weaponskill]. Conditions for use are not met.
|
2017-08-26 04:08:26 +01:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// cant target dead
|
2018-02-15 13:20:46 -06:00
|
|
|
|
if ((mainTarget & (ValidTarget.Corpse | ValidTarget.CorpseOnly)) == 0 && target.IsDead())
|
2017-08-26 04:08:26 +01:00
|
|
|
|
{
|
|
|
|
|
// cannot be perfomed on
|
2017-08-28 21:45:01 -04:00
|
|
|
|
if (user is Player)
|
|
|
|
|
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32512, 0x20, (uint)id);
|
2017-08-26 04:08:26 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2018-02-15 13:20:46 -06:00
|
|
|
|
|
|
|
|
|
//level too high
|
|
|
|
|
if (level > user.GetLevel())
|
2017-08-26 04:08:26 +01:00
|
|
|
|
{
|
2017-08-28 21:45:01 -04:00
|
|
|
|
if (user is Player)
|
|
|
|
|
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32527, 0x20, (uint)id);
|
2018-04-18 16:06:41 -05:00
|
|
|
|
//return false;
|
2017-08-26 04:08:26 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-02-15 13:20:46 -06:00
|
|
|
|
//Proc requirement
|
|
|
|
|
if (procRequirement != BattleCommandProcRequirement.None && !user.charaWork.battleTemp.timingCommandFlag[(int) procRequirement - 1])
|
|
|
|
|
{
|
|
|
|
|
if (user is Player)
|
|
|
|
|
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32556, 0x20, (uint)id);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//costs too much tp
|
|
|
|
|
if (CalculateTpCost(user) > user.GetTP())
|
2017-08-26 04:08:26 +01:00
|
|
|
|
{
|
2017-08-28 21:45:01 -04:00
|
|
|
|
if (user is Player)
|
|
|
|
|
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32546, 0x20, (uint)id);
|
2017-08-26 04:08:26 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// todo: calculate cost based on modifiers also (probably in BattleUtils)
|
2017-08-28 04:45:20 +01:00
|
|
|
|
if (BattleUtils.CalculateSpellCost(user, target, this) > user.GetMP())
|
2017-08-26 04:08:26 +01:00
|
|
|
|
{
|
2017-08-28 21:45:01 -04:00
|
|
|
|
if (user is Player)
|
|
|
|
|
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32545, 0x20, (uint)id);
|
2017-08-26 04:08:26 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// todo: check target requirements
|
2017-08-30 00:14:14 +01:00
|
|
|
|
if (requirements != BattleCommandRequirements.None)
|
2017-08-26 04:08:26 +01:00
|
|
|
|
{
|
|
|
|
|
if (false)
|
|
|
|
|
{
|
|
|
|
|
// Unable to execute [@SHEET(xtx/command,$E8(1),2)]. Conditions for use are not met.
|
2017-08-28 21:45:01 -04:00
|
|
|
|
if (user is Player)
|
|
|
|
|
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32556, 0x20, (uint)id);
|
2017-08-26 04:08:26 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-15 13:20:46 -06:00
|
|
|
|
|
2017-08-26 04:08:26 +01:00
|
|
|
|
// todo: i dont care to message for each scenario, just the most common ones..
|
2018-02-15 13:20:46 -06:00
|
|
|
|
if ((mainTarget & ValidTarget.CorpseOnly) != 0)
|
2017-08-26 04:08:26 +01:00
|
|
|
|
{
|
|
|
|
|
if (target != null && target.IsAlive())
|
|
|
|
|
{
|
2017-08-28 21:45:01 -04:00
|
|
|
|
if (user is Player)
|
|
|
|
|
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32513, 0x20, (uint)id);
|
2017-08-26 04:08:26 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-15 13:20:46 -06:00
|
|
|
|
if ((mainTarget & ValidTarget.Enemy) != 0)
|
2017-08-26 04:08:26 +01:00
|
|
|
|
{
|
|
|
|
|
if (target == user || target != null &&
|
2017-08-31 05:56:43 +01:00
|
|
|
|
user.allegiance == target.allegiance)
|
2017-08-26 04:08:26 +01:00
|
|
|
|
{
|
2017-08-28 21:45:01 -04:00
|
|
|
|
if (user is Player)
|
|
|
|
|
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32519, 0x20, (uint)id);
|
2017-08-26 04:08:26 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-15 13:20:46 -06:00
|
|
|
|
if ((mainTarget & ValidTarget.Ally) != 0)
|
2017-08-26 04:08:26 +01:00
|
|
|
|
{
|
2017-08-31 05:56:43 +01:00
|
|
|
|
if (target == null || target.allegiance != user.allegiance)
|
2017-08-26 04:08:26 +01:00
|
|
|
|
{
|
2017-08-28 21:45:01 -04:00
|
|
|
|
if (user is Player)
|
2018-02-15 13:20:46 -06:00
|
|
|
|
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32517, 0x20, (uint)id);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((mainTarget & ValidTarget.PartyMember) != 0)
|
|
|
|
|
{
|
|
|
|
|
if (target == null || target.currentParty != user.currentParty)
|
|
|
|
|
{
|
|
|
|
|
if (user is Player)
|
|
|
|
|
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32547, 0x20, (uint)id);
|
2017-08-26 04:08:26 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-02-15 13:20:46 -06:00
|
|
|
|
|
|
|
|
|
if ((mainTarget & ValidTarget.Player) != 0)
|
|
|
|
|
{
|
|
|
|
|
if (!(target is Player))
|
|
|
|
|
{
|
|
|
|
|
if (user is Player)
|
|
|
|
|
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32517, 0x20, (uint)id);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;// targetFind.CanTarget(target, true, true, true); //this will be done later
|
2017-08-02 23:06:11 +01:00
|
|
|
|
}
|
2017-08-26 04:08:26 +01:00
|
|
|
|
|
2018-02-15 13:20:46 -06:00
|
|
|
|
public ushort CalculateMpCost(Character user)
|
2017-08-26 04:08:26 +01:00
|
|
|
|
{
|
|
|
|
|
// todo: use precalculated costs instead
|
2018-02-15 13:20:46 -06:00
|
|
|
|
var level = user.GetLevel();
|
2017-08-26 04:08:26 +01:00
|
|
|
|
ushort cost = 0;
|
|
|
|
|
if (level <= 10)
|
|
|
|
|
cost = (ushort)(100 + level * 10);
|
|
|
|
|
else if (level <= 20)
|
|
|
|
|
cost = (ushort)(200 + (level - 10) * 20);
|
|
|
|
|
else if (level <= 30)
|
|
|
|
|
cost = (ushort)(400 + (level - 20) * 40);
|
|
|
|
|
else if (level <= 40)
|
|
|
|
|
cost = (ushort)(800 + (level - 30) * 70);
|
|
|
|
|
else if (level <= 50)
|
|
|
|
|
cost = (ushort)(1500 + (level - 40) * 130);
|
|
|
|
|
else if (level <= 60)
|
|
|
|
|
cost = (ushort)(2800 + (level - 50) * 200);
|
|
|
|
|
else if (level <= 70)
|
|
|
|
|
cost = (ushort)(4800 + (level - 60) * 320);
|
|
|
|
|
else
|
|
|
|
|
cost = (ushort)(8000 + (level - 70) * 500);
|
|
|
|
|
|
2018-02-15 13:20:46 -06:00
|
|
|
|
//scale the mpcost by level
|
|
|
|
|
cost = (ushort)Math.Ceiling((cost * mpCost * 0.001));
|
2017-08-28 04:45:20 +01:00
|
|
|
|
|
2018-02-15 13:20:46 -06:00
|
|
|
|
//if user is player, check if spell is a part of combo
|
|
|
|
|
if (user is Player)
|
|
|
|
|
{
|
|
|
|
|
var player = user as Player;
|
|
|
|
|
if (player.playerWork.comboNextCommandId[0] == id || player.playerWork.comboNextCommandId[1] == id)
|
|
|
|
|
cost = (ushort)Math.Ceiling(cost * (1 - player.playerWork.comboCostBonusRate));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return mpCost != 0 ? cost : (ushort)0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Calculate TP cost taking into considerating the combo bonus rate for players
|
|
|
|
|
//Should this set tpCost or should it be called like CalculateMp where it gets calculated each time?
|
|
|
|
|
//Might cause issues with the delay between starting and finishing a WS
|
|
|
|
|
public ushort CalculateTpCost(Character user)
|
|
|
|
|
{
|
|
|
|
|
ushort tp = tpCost;
|
|
|
|
|
//Calculate tp cost
|
|
|
|
|
if (user is Player)
|
|
|
|
|
{
|
|
|
|
|
var player = user as Player;
|
|
|
|
|
if (player.playerWork.comboNextCommandId[0] == id || player.playerWork.comboNextCommandId[1] == id)
|
|
|
|
|
tp = (ushort)Math.Ceiling((float)tpCost * (1 - player.playerWork.comboCostBonusRate));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tp;
|
2017-08-26 04:08:26 +01:00
|
|
|
|
}
|
2017-09-16 02:50:32 +01:00
|
|
|
|
|
|
|
|
|
public List<Character> GetTargets()
|
|
|
|
|
{
|
|
|
|
|
return targetFind?.GetTargets<Character>();
|
|
|
|
|
}
|
2018-02-15 13:20:46 -06:00
|
|
|
|
|
2018-04-18 16:06:41 -05:00
|
|
|
|
public ushort GetCommandType()
|
|
|
|
|
{
|
|
|
|
|
return (ushort) commandType;
|
|
|
|
|
}
|
2017-08-02 23:06:11 +01:00
|
|
|
|
}
|
2018-06-25 18:20:20 -05:00
|
|
|
|
}
|