mirror of
https://bitbucket.org/Ioncannon/project-meteor-server.git
synced 2025-04-02 19:42:05 -04:00
Merged in takhlaq/ffxiv-classic-server (pull request #61)
Combat changes and fixes. Approved-by: Filip Maj <filipmaj@gmail.com>
This commit is contained in:
commit
ec85cfd590
@ -7,6 +7,7 @@ namespace FFXIVClassic.Common
|
||||
public static class Utils
|
||||
{
|
||||
private static readonly uint[] _lookup32 = CreateLookup32();
|
||||
private static readonly DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
private static uint[] CreateLookup32()
|
||||
{
|
||||
@ -106,6 +107,11 @@ namespace FFXIVClassic.Common
|
||||
return unixTimeStamp;
|
||||
}
|
||||
|
||||
public static DateTime UnixTimeStampToDateTime(uint timestamp)
|
||||
{
|
||||
return epoch.AddSeconds(timestamp);
|
||||
}
|
||||
|
||||
public static ulong SwapEndian(ulong input)
|
||||
{
|
||||
return 0x00000000000000FF & (input >> 56) |
|
||||
|
@ -925,13 +925,13 @@ namespace FFXIVClassic_Map_Server
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
var id = reader.GetUInt32(0);
|
||||
var duration = reader.GetUInt32(1);
|
||||
var magnitude = reader.GetUInt64(2);
|
||||
var tick = reader.GetUInt32(3);
|
||||
var tier = reader.GetByte(4);
|
||||
var extra = reader.GetUInt64(5);
|
||||
|
||||
var id = reader.GetUInt32("statusId");
|
||||
var duration = reader.GetUInt32("duration");
|
||||
var magnitude = reader.GetUInt64("magnitude");
|
||||
var tick = reader.GetUInt32("tick");
|
||||
var tier = reader.GetByte("tier");
|
||||
var extra = reader.GetUInt64("extra");
|
||||
|
||||
var effect = Server.GetWorldManager().GetStatusEffect(id);
|
||||
if (effect != null)
|
||||
{
|
||||
@ -942,7 +942,7 @@ namespace FFXIVClassic_Map_Server
|
||||
effect.SetExtra(extra);
|
||||
|
||||
// dont wanna send ton of messages on login (i assume retail doesnt)
|
||||
player.statusEffects.AddStatusEffect(effect, null, true);
|
||||
player.statusEffects.AddStatusEffect(effect, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2287,7 +2287,7 @@ namespace FFXIVClassic_Map_Server
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
var query = @"SELECT id, name, flags, overwrite, tickMs FROM server_statuseffects;";
|
||||
var query = @"SELECT id, name, flags, overwrite, tickMs, hidden, silentOnGain, silentOnLoss, statusGainTextId, statusLossTextId FROM server_statuseffects;";
|
||||
|
||||
MySqlCommand cmd = new MySqlCommand(query, conn);
|
||||
|
||||
@ -2300,7 +2300,14 @@ namespace FFXIVClassic_Map_Server
|
||||
var flags = reader.GetUInt32("flags");
|
||||
var overwrite = reader.GetByte("overwrite");
|
||||
var tickMs = reader.GetUInt32("tickMs");
|
||||
var effect = new StatusEffect(id, name, flags, overwrite, tickMs);
|
||||
var hidden = reader.GetBoolean("hidden");
|
||||
var silentOnGain = reader.GetBoolean("silentOnGain");
|
||||
var silentOnLoss = reader.GetBoolean("silentOnLoss");
|
||||
var statusGainTextId = reader.GetUInt16("statusGainTextId");
|
||||
var statusLossTextId = reader.GetUInt16("statusLossTextId");
|
||||
|
||||
var effect = new StatusEffect(id, name, flags, overwrite, tickMs, hidden, silentOnGain, silentOnLoss, statusGainTextId, statusLossTextId);
|
||||
|
||||
lua.LuaEngine.LoadStatusEffectScript(effect);
|
||||
effects.Add(id, effect);
|
||||
}
|
||||
@ -2392,8 +2399,8 @@ namespace FFXIVClassic_Map_Server
|
||||
battleCommand.job = reader.GetByte("classJob");
|
||||
battleCommand.level = reader.GetByte("lvl");
|
||||
battleCommand.requirements = (BattleCommandRequirements)reader.GetUInt16("requirements");
|
||||
battleCommand.mainTarget = (ValidTarget)reader.GetByte("mainTarget");
|
||||
battleCommand.validTarget = (ValidTarget)reader.GetByte("validTarget");
|
||||
battleCommand.mainTarget = (ValidTarget)reader.GetUInt16("mainTarget");
|
||||
battleCommand.validTarget = (ValidTarget)reader.GetUInt16("validTarget");
|
||||
battleCommand.aoeType = (TargetFindAOEType)reader.GetByte("aoeType");
|
||||
battleCommand.basePotency = reader.GetUInt16("basePotency");
|
||||
battleCommand.numHits = reader.GetByte("numHits");
|
||||
@ -2410,8 +2417,8 @@ namespace FFXIVClassic_Map_Server
|
||||
battleCommand.castTimeMs = reader.GetUInt32("castTime");
|
||||
battleCommand.maxRecastTimeSeconds = reader.GetUInt32("recastTime");
|
||||
battleCommand.recastTimeMs = battleCommand.maxRecastTimeSeconds * 1000;
|
||||
battleCommand.mpCost = reader.GetUInt16("mpCost");
|
||||
battleCommand.tpCost = reader.GetUInt16("tpCost");
|
||||
battleCommand.mpCost = reader.GetInt16("mpCost");
|
||||
battleCommand.tpCost = reader.GetInt16("tpCost");
|
||||
battleCommand.animationType = reader.GetByte("animationType");
|
||||
battleCommand.effectAnimation = reader.GetUInt16("effectAnimation");
|
||||
battleCommand.modelAnimation = reader.GetUInt16("modelAnimation");
|
||||
@ -2433,7 +2440,26 @@ namespace FFXIVClassic_Map_Server
|
||||
battleCommand.actionType = (ActionType)reader.GetInt16("actionType");
|
||||
battleCommand.accuracyModifier = reader.GetFloat("accuracyMod");
|
||||
battleCommand.worldMasterTextId = reader.GetUInt16("worldMasterTextId");
|
||||
lua.LuaEngine.LoadBattleCommandScript(battleCommand, "weaponskill");
|
||||
|
||||
string folderName = "";
|
||||
|
||||
switch (battleCommand.commandType)
|
||||
{
|
||||
case CommandType.AutoAttack:
|
||||
folderName = "autoattack";
|
||||
break;
|
||||
case CommandType.WeaponSkill:
|
||||
folderName = "weaponskill";
|
||||
break;
|
||||
case CommandType.Ability:
|
||||
folderName = "ability";
|
||||
break;
|
||||
case CommandType.Spell:
|
||||
folderName = "magic";
|
||||
break;
|
||||
}
|
||||
|
||||
lua.LuaEngine.LoadBattleCommandScript(battleCommand, folderName);
|
||||
battleCommandDict.Add(id, battleCommand);
|
||||
|
||||
Tuple<byte, short> tuple = Tuple.Create<byte, short>(battleCommand.job, battleCommand.level);
|
||||
|
@ -478,7 +478,7 @@ namespace FFXIVClassic_Map_Server
|
||||
battleNpcGenusMods.TryGetValue(battleNpc.genusId, out battleNpc.genusMods);
|
||||
battleNpcSpawnMods.TryGetValue(battleNpc.GetBattleNpcId(), out battleNpc.spawnMods);
|
||||
|
||||
battleNpc.SetMod((uint)Modifier.Speed, reader.GetByte("speed"));
|
||||
battleNpc.SetMod((uint)Modifier.MovementSpeed, reader.GetByte("speed"));
|
||||
battleNpc.neutral = reader.GetByte("aggroType") == 0;
|
||||
|
||||
battleNpc.SetDetectionType(reader.GetUInt32("detection"));
|
||||
@ -603,7 +603,7 @@ namespace FFXIVClassic_Map_Server
|
||||
reader.GetUInt16("actorState"), reader.GetUInt32("animationId"), "");
|
||||
|
||||
battleNpc.SetBattleNpcId(reader.GetUInt32("bnpcId"));
|
||||
battleNpc.SetMod((uint)Modifier.Speed, reader.GetByte("speed"));
|
||||
battleNpc.SetMod((uint)Modifier.MovementSpeed, reader.GetByte("speed"));
|
||||
battleNpc.neutral = reader.GetByte("aggroType") == 0;
|
||||
|
||||
// set mob mods
|
||||
|
@ -29,6 +29,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
Stats = 0x100,
|
||||
Status = 0x200,
|
||||
StatusTime = 0x400,
|
||||
Hotbar = 0x800,
|
||||
|
||||
AllNpc = 0xDF,
|
||||
AllPlayer = 0x13F
|
||||
@ -92,6 +93,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
this.moveSpeeds[1] = SetActorSpeedPacket.DEFAULT_WALK;
|
||||
this.moveSpeeds[2] = SetActorSpeedPacket.DEFAULT_RUN;
|
||||
this.moveSpeeds[3] = SetActorSpeedPacket.DEFAULT_ACTIVE;
|
||||
positionUpdates = new List<Vector3>();
|
||||
}
|
||||
|
||||
public void SetPushCircleRange(string triggerName, float size)
|
||||
@ -650,7 +652,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
|
||||
public void LookAt(Actor actor)
|
||||
{
|
||||
if (actor != null && actor != this)
|
||||
if (actor != null)
|
||||
{
|
||||
LookAt(actor.positionX, actor.positionZ);
|
||||
}
|
||||
@ -670,16 +672,20 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
|
||||
public void LookAt(float x, float z)
|
||||
{
|
||||
var rot1 = this.rotation;
|
||||
//Don't rotate if the lookat position is same as our current position
|
||||
if (positionX != x || positionZ != z)
|
||||
{
|
||||
var rot1 = this.rotation;
|
||||
|
||||
var dX = this.positionX - x;
|
||||
var dY = this.positionZ - z;
|
||||
var rot2 = Math.Atan2(dY, dX);
|
||||
var dRot = Math.PI - rot2 + Math.PI / 2;
|
||||
var dX = this.positionX - x;
|
||||
var dY = this.positionZ - z;
|
||||
var rot2 = Math.Atan2(dY, dX);
|
||||
var dRot = Math.PI - rot2 + Math.PI / 2;
|
||||
|
||||
// pending move, dont need to unset it
|
||||
this.updateFlags |= ActorUpdateFlags.Position;
|
||||
rotation = (float)dRot;
|
||||
// pending move, dont need to unset it
|
||||
this.updateFlags |= ActorUpdateFlags.Position;
|
||||
rotation = (float)dRot;
|
||||
}
|
||||
}
|
||||
|
||||
// todo: is this legit?
|
||||
|
@ -149,8 +149,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;
|
||||
@ -238,8 +238,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
public void DoBattleAction(ushort commandId, uint animationId, CommandResult[] results)
|
||||
{
|
||||
int currentIndex = 0;
|
||||
//AoE abilities only ever hit 16 people, so we probably won't need this loop anymore
|
||||
//Apparently aoe are limited to 8?
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (results.Length - currentIndex >= 10)
|
||||
@ -253,9 +252,6 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
}
|
||||
else
|
||||
break;
|
||||
|
||||
//I think aoe effects play on all hit enemies. Firaga does at least
|
||||
//animationId = 0; //If more than one packet is sent out, only send the animation once to avoid double playing.
|
||||
}
|
||||
}
|
||||
|
||||
@ -317,7 +313,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
else
|
||||
modifiers.Add((Modifier)modifier, val);
|
||||
|
||||
if (modifier <= 35)
|
||||
if (modifier >= 3 && modifier <= 35)
|
||||
updateFlags |= ActorUpdateFlags.Stats;
|
||||
}
|
||||
|
||||
@ -344,6 +340,29 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
SetMod(modifier, newVal);
|
||||
}
|
||||
|
||||
public void MultiplyMod(Modifier modifier, double val)
|
||||
{
|
||||
MultiplyMod((uint)modifier, val);
|
||||
}
|
||||
|
||||
public void MultiplyMod(uint modifier, double val)
|
||||
{
|
||||
double newVal = GetMod(modifier) * val;
|
||||
SetMod(modifier, newVal);
|
||||
}
|
||||
|
||||
public void DivideMod(Modifier modifier, double val)
|
||||
{
|
||||
DivideMod((uint)modifier, val);
|
||||
}
|
||||
|
||||
public void DivideMod(uint modifier, double val)
|
||||
{
|
||||
double newVal = GetMod(modifier) / val;
|
||||
SetMod(modifier, newVal);
|
||||
}
|
||||
|
||||
|
||||
public virtual void OnPath(Vector3 point)
|
||||
{
|
||||
//lua.LuaEngine.CallLuaBattleFunction(this, "onPath", this, point);
|
||||
@ -380,7 +399,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
|
||||
if ((updateFlags & ActorUpdateFlags.SubState) != 0)
|
||||
{
|
||||
//packets.Add(SetActorSubStatePacket.BuildPacket(actorId, currentSubState));
|
||||
packets.Add(SetActorSubStatePacket.BuildPacket(actorId, currentSubState));
|
||||
//packets.Add(CommandResultX00Packet.BuildPacket(actorId, 0x72000062, 0));
|
||||
//packets.Add(CommandResultX01Packet.BuildPacket(actorId, 0x7C000062, 21001, new CommandResult(actorId, 0, 1)));
|
||||
|
||||
@ -390,6 +409,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
|
||||
if ((updateFlags & ActorUpdateFlags.Status) != 0)
|
||||
{
|
||||
|
||||
List<SubPacket> statusPackets = statusEffects.GetStatusPackets();
|
||||
packets.AddRange(statusPackets);
|
||||
statusPackets.Clear();
|
||||
@ -428,24 +448,15 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual bool CanCast(Character target, BattleCommand spell)
|
||||
public virtual bool CanUse(Character target, BattleCommand skill, CommandResult error = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool CanWeaponSkill(Character target, BattleCommand skill)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool CanUseAbility(Character target, BattleCommand ability)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual uint GetAttackDelayMs()
|
||||
{
|
||||
return (uint)GetMod((uint)Modifier.AttackDelay);
|
||||
return (uint)GetMod((uint)Modifier.Delay);
|
||||
}
|
||||
|
||||
public virtual uint GetAttackRange()
|
||||
@ -523,8 +534,8 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
aiContainer.Reset();
|
||||
// todo: reset hp/mp/tp etc here
|
||||
ChangeState(SetActorStatePacket.MAIN_STATE_PASSIVE);
|
||||
charaWork.parameterSave.hp = charaWork.parameterSave.hpMax;
|
||||
charaWork.parameterSave.mp = charaWork.parameterSave.mpMax;
|
||||
SetHP((uint) GetMaxHP());
|
||||
SetMP((uint) GetMaxMP());
|
||||
RecalculateStats();
|
||||
}
|
||||
|
||||
@ -609,8 +620,8 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
|
||||
public void SetMP(uint mp)
|
||||
{
|
||||
charaWork.parameterSave.mpMax = (short)mp;
|
||||
if (mp > charaWork.parameterSave.hpMax[0])
|
||||
charaWork.parameterSave.mp = (short)mp;
|
||||
if (mp > charaWork.parameterSave.mpMax)
|
||||
SetMaxMP(mp);
|
||||
|
||||
updateFlags |= ActorUpdateFlags.HpTpMp;
|
||||
@ -621,8 +632,9 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
charaWork.parameterSave.mp = (short)mp;
|
||||
updateFlags |= ActorUpdateFlags.HpTpMp;
|
||||
}
|
||||
|
||||
// todo: the following functions are virtuals since we want to check hidden item bonuses etc on player for certain conditions
|
||||
public virtual void AddHP(int hp)
|
||||
public virtual void AddHP(int hp, CommandResultContainer resultContainer = null)
|
||||
{
|
||||
// dont wanna die ded, don't want to send update if hp isn't actually changed
|
||||
if (IsAlive() && hp != 0)
|
||||
@ -634,6 +646,9 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
charaWork.parameterSave.hp[0] = (short)addHp;
|
||||
|
||||
updateFlags |= ActorUpdateFlags.HpTpMp;
|
||||
|
||||
if (charaWork.parameterSave.hp[0] < 1)
|
||||
Die(Program.Tick, resultContainer);
|
||||
}
|
||||
}
|
||||
|
||||
@ -675,9 +690,9 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
}
|
||||
}
|
||||
|
||||
public void DelHP(int hp)
|
||||
public void DelHP(int hp, CommandResultContainer resultContainer = null)
|
||||
{
|
||||
AddHP((short)-hp);
|
||||
AddHP((short)-hp, resultContainer);
|
||||
}
|
||||
|
||||
public void DelMP(int mp)
|
||||
@ -743,7 +758,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)
|
||||
@ -821,55 +836,55 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
|
||||
}
|
||||
|
||||
public virtual void OnDamageDealt(Character defender, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
public virtual void OnDamageDealt(Character defender, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
{
|
||||
switch (action.hitType)
|
||||
{
|
||||
case (HitType.Miss):
|
||||
OnMiss(this, action, actionContainer);
|
||||
OnMiss(defender, skill, action, actionContainer);
|
||||
break;
|
||||
case (HitType.Crit):
|
||||
OnCrit(defender, skill, action, actionContainer);
|
||||
OnHit(defender, skill, action, actionContainer);
|
||||
break;
|
||||
default:
|
||||
OnHit(defender, action, actionContainer);
|
||||
OnHit(defender, skill, action, actionContainer);
|
||||
break;
|
||||
}
|
||||
|
||||
//TP is only gained from autoattacks and abilities
|
||||
if (action.commandType == CommandType.AutoAttack || action.commandType == CommandType.Ability)
|
||||
if ((action.commandType == CommandType.AutoAttack || action.commandType == CommandType.Ability) && action.hitType != HitType.Miss)
|
||||
{
|
||||
//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));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnDamageTaken(Character attacker, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
public virtual void OnDamageTaken(Character attacker, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
{
|
||||
switch (action.hitType)
|
||||
{
|
||||
case (HitType.Miss):
|
||||
OnEvade(attacker, action, actionContainer);
|
||||
OnEvade(attacker, skill, action, actionContainer);
|
||||
break;
|
||||
case (HitType.Parry):
|
||||
OnParry(attacker, action, actionContainer);
|
||||
OnParry(attacker, skill, action, actionContainer);
|
||||
break;
|
||||
case (HitType.Block):
|
||||
OnBlock(attacker, action, actionContainer);
|
||||
OnBlock(attacker, skill, action, actionContainer);
|
||||
break;
|
||||
}
|
||||
|
||||
statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnDamageTaken, "onDamageTaken", attacker, this, action);
|
||||
|
||||
statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnDamageTaken, "onDamageTaken", attacker, this, skill, action, actionContainer);
|
||||
|
||||
//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);
|
||||
}
|
||||
|
||||
public UInt64 GetTempVar(string name)
|
||||
@ -978,8 +993,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
|
||||
@ -1013,36 +1027,41 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
}
|
||||
|
||||
//Called when this character evades attacker's action
|
||||
public void OnEvade(Character attacker, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
public void OnEvade(Character attacker, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
{
|
||||
SetProc((ushort)HitType.Evade);
|
||||
statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnEvade, "onEvade", attacker, this, action, actionContainer);
|
||||
statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnEvade, "onEvade", attacker, this, skill, action, actionContainer);
|
||||
}
|
||||
|
||||
//Called when this character blocks attacker's action
|
||||
public void OnBlock(Character attacker, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
public void OnBlock(Character attacker, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
{
|
||||
SetProc((ushort)HitType.Block);
|
||||
statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnBlock, "onBlock", attacker, this, action, actionContainer);
|
||||
statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnBlock, "onBlock", attacker, this, skill, action, actionContainer);
|
||||
}
|
||||
|
||||
//Called when this character parries attacker's action
|
||||
public void OnParry(Character attacker, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
public void OnParry(Character attacker, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
{
|
||||
SetProc((ushort)HitType.Parry);
|
||||
statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnParry, "onParry", attacker, this, action, actionContainer);
|
||||
statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnParry, "onParry", attacker, this, skill, action, actionContainer);
|
||||
}
|
||||
|
||||
//Called when this character misses
|
||||
public void OnMiss(Character defender, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
public void OnMiss(Character defender, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
{
|
||||
SetProc((ushort)HitType.Miss);
|
||||
statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnMiss, "onMiss", this, defender, action, actionContainer);
|
||||
statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnMiss, "onMiss", this, defender, skill, action, actionContainer);
|
||||
}
|
||||
|
||||
public void OnHit(Character defender, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
public void OnHit(Character defender, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
{
|
||||
statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnHit, "onHit", this, defender, action, actionContainer);
|
||||
statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnHit, "onHit", this, defender, skill, action, actionContainer);
|
||||
}
|
||||
|
||||
public void OnCrit(Character defender, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
{
|
||||
statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnCrit, "onCrit", this, defender, skill, action, actionContainer);
|
||||
}
|
||||
|
||||
//The order of messages that appears after using a command is:
|
||||
@ -1056,7 +1075,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
//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
|
||||
@ -1092,7 +1111,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
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)
|
||||
if (action.ActionLanded())
|
||||
{
|
||||
hitTarget = true;
|
||||
hitCount++;
|
||||
@ -1116,18 +1135,20 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
actions.AddAction(new CommandResult(actorId, 30202, 0));
|
||||
}
|
||||
|
||||
DelMP(command.CalculateMpCost(this));
|
||||
DelTP(command.CalculateTpCost(this));
|
||||
|
||||
//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
|
||||
//Only reset combo if the command is a spell or weaponskill, since abilities can be used between combo skills
|
||||
else if (command.commandType == CommandType.Spell || command.commandType == CommandType.WeaponSkill)
|
||||
((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());
|
||||
}
|
||||
@ -1135,8 +1156,8 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
public List<Character> GetPartyMembersInRange(uint range)
|
||||
{
|
||||
TargetFind targetFind = new TargetFind(this);
|
||||
targetFind.SetAOEType(ValidTarget.PartyMember, TargetFindAOEType.Circle, TargetFindAOETarget.Self, range, 0, 10, 0, 0);
|
||||
targetFind.FindWithinArea(this, ValidTarget.PartyMember, TargetFindAOETarget.Self);
|
||||
targetFind.SetAOEType(ValidTarget.Party, TargetFindAOEType.Circle, TargetFindAOETarget.Self, range, 0, 10, 0, 0);
|
||||
targetFind.FindWithinArea(this, ValidTarget.Party, TargetFindAOETarget.Self);
|
||||
return targetFind.GetTargets();
|
||||
}
|
||||
|
||||
|
@ -7,88 +7,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
|
||||
}
|
||||
}
|
||||
|
@ -109,8 +109,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
public uint castTimeMs; //cast time in milliseconds
|
||||
public uint recastTimeMs; //recast time in milliseconds
|
||||
public uint maxRecastTimeSeconds; //maximum recast time in seconds
|
||||
public ushort mpCost;
|
||||
public ushort tpCost;
|
||||
public short mpCost; //short in case these casts can have negative cost
|
||||
public short tpCost; //short because there are certain cases where we want weaponskills to have negative costs (such as Feint)
|
||||
public byte animationType;
|
||||
public ushort effectAnimation;
|
||||
public ushort modelAnimation;
|
||||
@ -188,10 +188,10 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
return castTimeMs == 0;
|
||||
}
|
||||
|
||||
//Checks whether the skill can be used on the given target
|
||||
public bool IsValidMainTarget(Character user, Character target)
|
||||
//Checks whether the skill can be used on the given targets, uses error to return specific text ids for errors
|
||||
public bool IsValidMainTarget(Character user, Character target, CommandResult error = null)
|
||||
{
|
||||
targetFind = new TargetFind(user);
|
||||
targetFind = new TargetFind(user, target);
|
||||
|
||||
if (aoeType == TargetFindAOEType.Box)
|
||||
{
|
||||
@ -204,6 +204,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
|
||||
/*
|
||||
worldMasterTextId
|
||||
32511 Target does not exist
|
||||
32512 cannot be performed on a KO'd target.
|
||||
32513 can only be performed on a KO'd target.
|
||||
32514 cannot be performed on yourself.
|
||||
@ -211,117 +212,112 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
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,
|
||||
32556 unable to execute [weaponskill]. Conditions for use are not met.
|
||||
32519 can only be performed on an enemy.
|
||||
32547 That command cannot be performed on the current target.
|
||||
32548 That command cannot be performed on a party member
|
||||
*/
|
||||
|
||||
// cant target dead
|
||||
if ((mainTarget & (ValidTarget.Corpse | ValidTarget.CorpseOnly)) == 0 && target.IsDead())
|
||||
if (target == null)
|
||||
{
|
||||
// cannot be perfomed on
|
||||
if (user is Player)
|
||||
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32512, 0x20, (uint)id);
|
||||
error?.SetTextId(32511);
|
||||
return false;
|
||||
}
|
||||
|
||||
//level too high
|
||||
if (level > user.GetLevel())
|
||||
//This skill can't be used on a corpse and target is dead
|
||||
if ((mainTarget & ValidTarget.Corpse) == 0 && target.IsDead())
|
||||
{
|
||||
if (user is Player)
|
||||
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32527, 0x20, (uint)id);
|
||||
//return false;
|
||||
}
|
||||
|
||||
//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);
|
||||
error?.SetTextId(32512);
|
||||
return false;
|
||||
}
|
||||
|
||||
//costs too much tp
|
||||
if (CalculateTpCost(user) > user.GetTP())
|
||||
//This skill must be used on a corpse and target is alive
|
||||
if ((mainTarget & ValidTarget.CorpseOnly) != 0 && target.IsAlive())
|
||||
{
|
||||
if (user is Player)
|
||||
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32546, 0x20, (uint)id);
|
||||
error?.SetTextId(32513);
|
||||
return false;
|
||||
}
|
||||
|
||||
// todo: calculate cost based on modifiers also (probably in BattleUtils)
|
||||
if (BattleUtils.CalculateSpellCost(user, target, this) > user.GetMP())
|
||||
//This skill can't be used on self and target is self
|
||||
if ((mainTarget & ValidTarget.Self) == 0 && target == user)
|
||||
{
|
||||
if (user is Player)
|
||||
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32545, 0x20, (uint)id);
|
||||
error?.SetTextId(32514);
|
||||
return false;
|
||||
}
|
||||
|
||||
// todo: check target requirements
|
||||
if (requirements != BattleCommandRequirements.None)
|
||||
//This skill must be used on self and target isn't self
|
||||
if ((mainTarget & ValidTarget.SelfOnly) != 0 && target != user)
|
||||
{
|
||||
if (false)
|
||||
{
|
||||
// Unable to execute [@SHEET(xtx/command,$E8(1),2)]. Conditions for use are not met.
|
||||
if (user is Player)
|
||||
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32556, 0x20, (uint)id);
|
||||
return false;
|
||||
}
|
||||
error?.SetTextId(32515);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// todo: i dont care to message for each scenario, just the most common ones..
|
||||
if ((mainTarget & ValidTarget.CorpseOnly) != 0)
|
||||
//This skill can't be used on an ally and target is an ally
|
||||
if ((mainTarget & ValidTarget.Ally) == 0 && target.allegiance == user.allegiance)
|
||||
{
|
||||
if (target != null && target.IsAlive())
|
||||
{
|
||||
if (user is Player)
|
||||
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32513, 0x20, (uint)id);
|
||||
return false;
|
||||
}
|
||||
error?.SetTextId(32516);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((mainTarget & ValidTarget.Enemy) != 0)
|
||||
//This skill must be used on an ally and target is not an ally
|
||||
if ((mainTarget & ValidTarget.AllyOnly) != 0 && target.allegiance != user.allegiance)
|
||||
{
|
||||
if (target == user || target != null &&
|
||||
user.allegiance == target.allegiance)
|
||||
{
|
||||
if (user is Player)
|
||||
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32519, 0x20, (uint)id);
|
||||
return false;
|
||||
}
|
||||
error?.SetTextId(32517);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((mainTarget & ValidTarget.Ally) != 0)
|
||||
//This skill can't be used on an enemu and target is an enemy
|
||||
if ((mainTarget & ValidTarget.Enemy) == 0 && target.allegiance != user.allegiance)
|
||||
{
|
||||
if (target == null || target.allegiance != user.allegiance)
|
||||
{
|
||||
if (user is Player)
|
||||
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32517, 0x20, (uint)id);
|
||||
return false;
|
||||
}
|
||||
error?.SetTextId(32518);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((mainTarget & ValidTarget.PartyMember) != 0)
|
||||
//This skill must be used on an enemy and target is an ally
|
||||
if ((mainTarget & ValidTarget.EnemyOnly) != 0 && target.allegiance == user.allegiance)
|
||||
{
|
||||
if (target == null || target.currentParty != user.currentParty)
|
||||
{
|
||||
if (user is Player)
|
||||
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32547, 0x20, (uint)id);
|
||||
return false;
|
||||
}
|
||||
error?.SetTextId(32519);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((mainTarget & ValidTarget.Player) != 0)
|
||||
//This skill can't be used on party members and target is a party member
|
||||
if ((mainTarget & ValidTarget.Party) == 0 && target.currentParty == user.currentParty)
|
||||
{
|
||||
if (!(target is Player))
|
||||
{
|
||||
if (user is Player)
|
||||
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32517, 0x20, (uint)id);
|
||||
return false;
|
||||
}
|
||||
error?.SetTextId(32548);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;// targetFind.CanTarget(target, true, true, true); //this will be done later
|
||||
//This skill must be used on party members and target is not a party member
|
||||
if ((mainTarget & ValidTarget.PartyOnly) != 0 && target.currentParty != user.currentParty)
|
||||
{
|
||||
error?.SetTextId(32547);
|
||||
return false;
|
||||
}
|
||||
|
||||
//This skill can't be used on NPCs and target is an npc
|
||||
if ((mainTarget & ValidTarget.NPC) == 0 && target.isStatic)
|
||||
{
|
||||
error?.SetTextId(32547);
|
||||
return false;
|
||||
}
|
||||
|
||||
//This skill must be used on NPCs and target is not an npc
|
||||
if ((mainTarget & ValidTarget.NPCOnly) != 0 && !target.isStatic)
|
||||
{
|
||||
error?.SetTextId(32547);
|
||||
return false;
|
||||
}
|
||||
|
||||
// todo: why is player always zoning?
|
||||
// cant target if zoning
|
||||
if (target is Player && ((Player)target).playerSession.isUpdatesLocked)
|
||||
{
|
||||
user.aiContainer.ChangeTarget(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target.zone != user.zone)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public ushort CalculateMpCost(Character user)
|
||||
@ -363,15 +359,15 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
//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)
|
||||
public short CalculateTpCost(Character user)
|
||||
{
|
||||
ushort tp = tpCost;
|
||||
short 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));
|
||||
tp = (short)Math.Ceiling((float)tpCost * (1 - player.playerWork.comboCostBonusRate));
|
||||
}
|
||||
|
||||
return tp;
|
||||
@ -386,5 +382,10 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
{
|
||||
return (ushort) commandType;
|
||||
}
|
||||
|
||||
public ushort GetActionType()
|
||||
{
|
||||
return (ushort) actionType;
|
||||
}
|
||||
}
|
||||
}
|
@ -320,28 +320,28 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
|
||||
// custom effects here
|
||||
// status for having procs fall off
|
||||
EvadeProc = 253003,
|
||||
BlockProc = 253004,
|
||||
ParryProc = 253005,
|
||||
MissProc = 253006,
|
||||
EXPChain = 253007
|
||||
EvadeProc = 300000,
|
||||
BlockProc = 300001,
|
||||
ParryProc = 300002,
|
||||
MissProc = 300003,
|
||||
EXPChain = 300004
|
||||
}
|
||||
|
||||
[Flags]
|
||||
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.
|
||||
@ -368,9 +368,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
|
||||
@ -387,19 +385,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 = 30328; //The text id used when the status is gained. 30328: [Command] grants you the effect of [status] (Used for buffs)
|
||||
private ushort statusLossTextId = 30331; //The text id used when the status effect falls off when its time runs out. 30331: You are no longer under the effect of [status] (Used for buffs)
|
||||
public LuaScript script;
|
||||
|
||||
HitEffect animationEffect;
|
||||
@ -433,26 +434,36 @@ 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, ushort statusGainTextId, ushort statusLossTextId)
|
||||
{
|
||||
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;
|
||||
this.statusGainTextId = statusGainTextId;
|
||||
this.statusLossTextId = statusLossTextId;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@ -548,9 +559,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()
|
||||
@ -558,6 +574,16 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
return hidden;
|
||||
}
|
||||
|
||||
public ushort GetStatusGainTextId()
|
||||
{
|
||||
return statusGainTextId;
|
||||
}
|
||||
|
||||
public ushort GetStatusLossTextId()
|
||||
{
|
||||
return statusLossTextId;
|
||||
}
|
||||
|
||||
public void SetStartTime(DateTime time)
|
||||
{
|
||||
this.startTime = time;
|
||||
@ -566,7 +592,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
|
||||
@ -629,9 +663,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)
|
||||
@ -639,6 +678,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;
|
||||
|
@ -30,39 +30,41 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
|
||||
public void Update(DateTime tick)
|
||||
{
|
||||
CommandResultContainer resultContainer = new CommandResultContainer();
|
||||
|
||||
//Regen/Refresh/Regain effects tick every 3 seconds
|
||||
if ((DateTime.Now - lastTick).Seconds >= 3)
|
||||
{
|
||||
RegenTick(tick);
|
||||
RegenTick(tick, resultContainer);
|
||||
lastTick = DateTime.Now;
|
||||
}
|
||||
// list of effects to remove
|
||||
|
||||
// if (owner is Player) UpdateTimeAtIndex(4, 4294967295);
|
||||
// list of effects to remove
|
||||
var removeEffects = new List<StatusEffect>();
|
||||
for (int i = 0; i < effects.Values.Count; i++)
|
||||
var effectsList = effects.Values;
|
||||
for (int i = effectsList.Count - 1; i >= 0; i--)
|
||||
{
|
||||
// effect's update function returns true if effect has completed
|
||||
if (effects.Values.ElementAt(i).Update(tick))
|
||||
removeEffects.Add(effects.Values.ElementAt(i));
|
||||
|
||||
if (effectsList.ElementAt(i).Update(tick, resultContainer))
|
||||
removeEffects.Add(effectsList.ElementAt(i));
|
||||
}
|
||||
|
||||
// remove effects from this list
|
||||
foreach (var effect in removeEffects)
|
||||
{
|
||||
RemoveStatusEffect(effect);
|
||||
RemoveStatusEffect(effect, resultContainer, effect.GetStatusLossTextId());
|
||||
}
|
||||
|
||||
if (sendUpdate)
|
||||
resultContainer.CombineLists();
|
||||
|
||||
if (resultContainer.GetList().Count > 0)
|
||||
{
|
||||
owner.zone.BroadcastPacketsAroundActor(owner, owner.GetActorStatusPackets());
|
||||
sendUpdate = false;
|
||||
owner.DoBattleAction(0, 0x7c000062, resultContainer.GetList());
|
||||
}
|
||||
}
|
||||
|
||||
//regen/refresh/regain
|
||||
public void RegenTick(DateTime tick)
|
||||
public void RegenTick(DateTime tick, CommandResultContainer resultContainer)
|
||||
{
|
||||
ushort dotTick = (ushort) owner.GetMod(Modifier.RegenDown);
|
||||
ushort regenTick = (ushort) owner.GetMod(Modifier.Regen);
|
||||
@ -72,18 +74,24 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
//DoTs tick before regen and the full dot damage is displayed, even if some or all of it is nullified by regen. Only effects like stoneskin actually alter the number shown
|
||||
if (dotTick > 0)
|
||||
{
|
||||
CommandResult action = new CommandResult(owner.actorId, 30331, (uint)(HitEffect.HitEffectType | HitEffect.Hit), dotTick);
|
||||
//Unsure why 10105 is the textId used
|
||||
//Also unsure why magicshield is used
|
||||
CommandResult action = new CommandResult(owner.actorId, 10105, (uint)(HitEffect.MagicEffectType | HitEffect.MagicShield | HitEffect.NoResist), dotTick);
|
||||
utils.BattleUtils.HandleStoneskin(owner, action);
|
||||
// todo: figure out how to make red numbers appear for enemies getting hurt by dots
|
||||
//owner.DelHP(action.amount);
|
||||
utils.BattleUtils.DamageTarget(owner, owner, action, null);
|
||||
owner.DoBattleAction(0, 0, action);
|
||||
resultContainer.AddAction(action);
|
||||
owner.DelHP(action.amount, resultContainer);
|
||||
}
|
||||
|
||||
//DoTs are the only effect to show numbers, so that doesnt need to be handled for these
|
||||
owner.AddHP(regenTick);
|
||||
owner.AddMP(refreshtick);
|
||||
owner.AddTP(regainTick);
|
||||
if (regenTick != 0)
|
||||
owner.AddHP(regenTick);
|
||||
|
||||
if (refreshtick != 0)
|
||||
owner.AddMP(refreshtick);
|
||||
|
||||
if (regainTick != 0)
|
||||
owner.AddTP(regainTick);
|
||||
}
|
||||
|
||||
public bool HasStatusEffect(uint id)
|
||||
@ -96,55 +104,59 @@ 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);
|
||||
if (se != null)
|
||||
{
|
||||
worldmasterTextId = se.GetStatusGainTextId();
|
||||
}
|
||||
|
||||
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);
|
||||
if (se != null)
|
||||
{
|
||||
se.SetTier(tier);
|
||||
worldmasterTextId = se.GetStatusGainTextId();
|
||||
}
|
||||
|
||||
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);
|
||||
if (se != null)
|
||||
{
|
||||
se.SetMagnitude(magnitude);
|
||||
se.SetTier(tier);
|
||||
worldmasterTextId = se.GetStatusGainTextId();
|
||||
}
|
||||
|
||||
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);
|
||||
worldmasterTextId = se.GetStatusGainTextId();
|
||||
}
|
||||
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
|
||||
@ -166,9 +178,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
|
||||
@ -181,13 +194,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())
|
||||
{
|
||||
@ -212,15 +221,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
|
||||
@ -230,54 +243,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)
|
||||
@ -288,14 +267,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;
|
||||
@ -321,6 +300,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)
|
||||
{
|
||||
@ -429,7 +418,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();
|
||||
@ -223,7 +223,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
|
||||
|
||||
protected virtual void Move()
|
||||
{
|
||||
if (!owner.aiContainer.CanFollowPath())
|
||||
if (!owner.aiContainer.CanFollowPath() || owner.statusEffects.HasStatusEffectsByFlag(StatusEffectFlags.PreventMovement))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
@ -10,21 +10,28 @@ using FFXIVClassic_Map_Server.actors.group;
|
||||
|
||||
namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
{
|
||||
// https://github.com/Windower/POLUtils/blob/master/PlayOnline.FFXI/Enums.cs
|
||||
[Flags]
|
||||
public enum ValidTarget : ushort
|
||||
{
|
||||
None = 0x00,
|
||||
Self = 0x01,
|
||||
Player = 0x02,
|
||||
PartyMember = 0x04,
|
||||
Ally = 0x08,
|
||||
NPC = 0x10,
|
||||
Enemy = 0x20,
|
||||
Unknown = 0x40,
|
||||
Object = 0x60,
|
||||
CorpseOnly = 0x80,
|
||||
Corpse = 0x9D // CorpseOnly + NPC + Ally + Partymember + Self
|
||||
Self = 0x01, //Can be used on self (if this flag isn't set and target is self, return false)
|
||||
SelfOnly = 0x02, //Must be used on self (if this flag is set and target isn't self, return false)
|
||||
Party = 0x4, //Can be used on party members
|
||||
PartyOnly = 0x8, //Must be used on party members
|
||||
Ally = 0x10, //Can be used on allies
|
||||
AllyOnly = 0x20, //Must be used on allies
|
||||
NPC = 0x40, //Can be used on static NPCs
|
||||
NPCOnly = 0x80, //Must be used on static NPCs
|
||||
Enemy = 0x100, //Can be used on enemies
|
||||
EnemyOnly = 0x200, //Must be used on enemies
|
||||
Object = 0x400, //Can be used on objects
|
||||
ObjectOnly = 0x800, //Must be used on objects
|
||||
Corpse = 0x1000, //Can be used on corpses
|
||||
CorpseOnly = 0x2000, //Must be used on corpses
|
||||
|
||||
//These are only used for ValidTarget, not MainTarget
|
||||
MainTargetParty = 0x4000, //Can be used on main target's party (This will basically always be true.)
|
||||
MainTargetPartyOnly = 0x8000, //Must be used on main target's party (This is for Protect basically.)
|
||||
}
|
||||
|
||||
/// <summary> Targeting from/to different entity types </summary>
|
||||
@ -66,12 +73,13 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
class TargetFind
|
||||
{
|
||||
private Character owner;
|
||||
private Character masterTarget; // if target is a pet, this is the owner
|
||||
private Character mainTarget; //This is the target that the skill is being used on
|
||||
private Character masterTarget; //If mainTarget is a pet, this is the owner
|
||||
private TargetFindCharacterType findType;
|
||||
private ValidTarget validTarget;
|
||||
private TargetFindAOETarget aoeTarget;
|
||||
private TargetFindAOEType aoeType;
|
||||
private Vector3 aoeTargetPosition; //This is the center of circle an cone AOEs and the position where line aoes come out
|
||||
private Vector3 aoeTargetPosition; //This is the center of circle of cone AOEs and the position where line aoes come out. If we have mainTarget this might not be needed?
|
||||
private float aoeTargetRotation; //This is the direction the aoe target is facing
|
||||
private float maxDistance; //Radius for circle and cone AOEs, length for line AOEs
|
||||
private float minDistance; //Minimum distance to that target must be to be able to be hit
|
||||
@ -82,14 +90,16 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
private float param;
|
||||
private List<Character> targets;
|
||||
|
||||
public TargetFind(Character owner)
|
||||
public TargetFind(Character owner, Character mainTarget = null)
|
||||
{
|
||||
this.owner = owner;
|
||||
Reset();
|
||||
this.owner = owner;
|
||||
this.mainTarget = mainTarget == null ? owner : mainTarget;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
this.mainTarget = owner;
|
||||
this.findType = TargetFindCharacterType.None;
|
||||
this.validTarget = ValidTarget.Enemy;
|
||||
this.aoeType = TargetFindAOEType.None;
|
||||
@ -201,11 +211,6 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
|
||||
//if (targets.Count > 8)
|
||||
//targets.RemoveRange(8, targets.Count - 8);
|
||||
|
||||
//Curaga starts with lowest health players, so the targets are definitely sorted at least for some abilities
|
||||
//Other aoe abilities might be sorted by distance?
|
||||
//Protect is random
|
||||
targets.Sort(delegate (Character a, Character b) { return a.GetHP().CompareTo(b.GetHP()); });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -323,41 +328,57 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
if (target == null || !retarget && targets.Contains(target))
|
||||
return false;
|
||||
|
||||
//This skill can't be used on self and target is self, return false
|
||||
if ((validTarget & ValidTarget.Self) == 0 && target == owner)
|
||||
if (target == null)
|
||||
return false;
|
||||
|
||||
//This skill can't be used on NPCs and target is an NPC, return false
|
||||
if ((validTarget & ValidTarget.NPC) == 0 && target.isStatic)
|
||||
return false;
|
||||
|
||||
//This skill can't be used on corpses and target is dead, return false
|
||||
//This skill can't be used on a corpse and target is dead
|
||||
if ((validTarget & ValidTarget.Corpse) == 0 && target.IsDead())
|
||||
return false;
|
||||
|
||||
//This skill must be used on Allies and target is not an ally, return false
|
||||
if ((validTarget & ValidTarget.Ally) != 0 && target.allegiance != owner.allegiance)
|
||||
return false;
|
||||
|
||||
|
||||
//This skill can't be used on players and target is a player, return false
|
||||
//Do we need a player flag? Ally/Enemy flags probably serve the same purpose
|
||||
//if ((validTarget & ValidTarget.Player) == 0 && target is Player)
|
||||
//return false;
|
||||
|
||||
|
||||
//This skill must be used on enemies an target is not an enemy
|
||||
if ((validTarget & ValidTarget.Enemy) != 0 && target.allegiance == owner.allegiance)
|
||||
return false;
|
||||
|
||||
//This skill must be used on a party member and target is not in owner's party, return false
|
||||
if ((validTarget & ValidTarget.PartyMember) != 0 && target.currentParty != owner.currentParty)
|
||||
return false;
|
||||
|
||||
//This skill must be used on a corpse and target is alive, return false
|
||||
//This skill must be used on a corpse and target is alive
|
||||
if ((validTarget & ValidTarget.CorpseOnly) != 0 && target.IsAlive())
|
||||
return false;
|
||||
|
||||
//This skill can't be used on self and target is self
|
||||
if ((validTarget & ValidTarget.Self) == 0 && target == owner)
|
||||
return false;
|
||||
|
||||
//This skill must be used on self and target isn't self
|
||||
if ((validTarget & ValidTarget.SelfOnly) != 0 && target != owner)
|
||||
return false;
|
||||
|
||||
//This skill can't be used on an ally and target is an ally
|
||||
if ((validTarget & ValidTarget.Ally) == 0 && target.allegiance == owner.allegiance)
|
||||
return false;
|
||||
|
||||
//This skill must be used on an ally and target is not an ally
|
||||
if ((validTarget & ValidTarget.AllyOnly) != 0 && target.allegiance != owner.allegiance)
|
||||
return false;
|
||||
|
||||
//This skill can't be used on an enemu and target is an enemy
|
||||
if ((validTarget & ValidTarget.Enemy) == 0 && target.allegiance != owner.allegiance)
|
||||
return false;
|
||||
|
||||
//This skill must be used on an enemy and target is an ally
|
||||
if ((validTarget & ValidTarget.EnemyOnly) != 0 && target.allegiance == owner.allegiance)
|
||||
return false;
|
||||
|
||||
//This skill can't be used on party members and target is a party member
|
||||
if ((validTarget & ValidTarget.Party) == 0 && target.currentParty == owner.currentParty)
|
||||
return false;
|
||||
|
||||
//This skill must be used on party members and target is not a party member
|
||||
if ((validTarget & ValidTarget.PartyOnly) != 0 && target.currentParty != owner.currentParty)
|
||||
return false;
|
||||
|
||||
//This skill can't be used on NPCs and target is an npc
|
||||
if ((validTarget & ValidTarget.NPC) == 0 && target.isStatic)
|
||||
return false;
|
||||
|
||||
//This skill must be used on NPCs and target is not an npc
|
||||
if ((validTarget & ValidTarget.NPCOnly) != 0 && !target.isStatic)
|
||||
return false;
|
||||
|
||||
// todo: why is player always zoning?
|
||||
// cant target if zoning
|
||||
if (target is Player && ((Player)target).playerSession.isUpdatesLocked)
|
||||
@ -372,6 +393,15 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
if (validTarget == ValidTarget.Self && aoeType == TargetFindAOEType.None && owner != target)
|
||||
return false;
|
||||
|
||||
//This skill can't be used on main target's party members and target is a party member of main target
|
||||
if ((validTarget & ValidTarget.MainTargetParty) == 0 && target.currentParty == mainTarget.currentParty)
|
||||
return false;
|
||||
|
||||
//This skill must be used on main target's party members and target is not a party member of main target
|
||||
if ((validTarget & ValidTarget.MainTargetPartyOnly) != 0 && target.currentParty != mainTarget.currentParty)
|
||||
return false;
|
||||
|
||||
|
||||
// this is fuckin retarded, think of a better way l8r
|
||||
if (!ignoreAOE)
|
||||
{
|
||||
|
@ -15,10 +15,11 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
|
||||
{
|
||||
this.startTime = DateTime.Now;
|
||||
this.skill = Server.GetWorldManager().GetBattleCommand(skillId);
|
||||
var returnCode = lua.LuaEngine.CallLuaBattleCommandFunction(owner, skill, "ability", "onAbilityPrepare", owner, target, skill);
|
||||
var returnCode = skill.CallLuaFunction(owner, "onAbilityPrepare", owner, target, skill);
|
||||
|
||||
this.target = target != null ? target : owner;
|
||||
this.target = (skill.mainTarget & ValidTarget.SelfOnly) != 0 ? owner : target;
|
||||
|
||||
errorResult = new CommandResult(owner.actorId, 32553, 0);
|
||||
if (returnCode == 0)
|
||||
{
|
||||
OnStart();
|
||||
@ -32,7 +33,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
|
||||
|
||||
public override void OnStart()
|
||||
{
|
||||
var returnCode = lua.LuaEngine.CallLuaBattleCommandFunction(owner, skill, "ability", "onAbilityStart", owner, target, skill);
|
||||
var returnCode = skill.CallLuaFunction(owner, "onAbilityStart", owner, target, skill);
|
||||
|
||||
if (returnCode != 0)
|
||||
{
|
||||
|
@ -100,26 +100,35 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
|
||||
|
||||
//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 fuckin 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());
|
||||
//This is all temporary until the skill sheet is finishd and the different auto attacks are added to the database
|
||||
//Some mobs have multiple unique auto attacks that they switch between as well as ranged auto attacks, so we'll need a way to handle that
|
||||
//For now, just use a temporary hardcoded BattleCommand that's the same for everyone.
|
||||
BattleCommand attackCommand = new BattleCommand(22104, "Attack");
|
||||
attackCommand.range = 5;
|
||||
attackCommand.rangeHeight = 10;
|
||||
attackCommand.worldMasterTextId = 0x765D;
|
||||
attackCommand.mainTarget = (ValidTarget)768;
|
||||
attackCommand.validTarget = (ValidTarget)17152;
|
||||
attackCommand.commandType = CommandType.AutoAttack;
|
||||
attackCommand.numHits = (byte)owner.GetMod(Modifier.HitCount);
|
||||
attackCommand.basePotency = 100;
|
||||
ActionProperty property = (owner.GetMod(Modifier.AttackType) != 0) ? (ActionProperty)owner.GetMod(Modifier.AttackType) : ActionProperty.Slashing;
|
||||
attackCommand.actionProperty = property;
|
||||
attackCommand.actionType = ActionType.Physical;
|
||||
|
||||
uint anim = (17 << 24 | 1 << 12);
|
||||
|
||||
if (owner is Player)
|
||||
anim = (25 << 24 | 1 << 12);
|
||||
|
||||
attackCommand.battleAnimation = anim;
|
||||
|
||||
if (owner.CanUse(target, attackCommand))
|
||||
{
|
||||
attackCommand.targetFind.FindWithinArea(target, attackCommand.validTarget, attackCommand.aoeTarget);
|
||||
owner.DoBattleCommand(attackCommand, "autoattack");
|
||||
}
|
||||
}
|
||||
|
||||
public override void TryInterrupt()
|
||||
|
@ -12,7 +12,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;
|
||||
|
@ -18,32 +18,28 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
|
||||
this.startPos = owner.GetPosAsVector3();
|
||||
this.startTime = DateTime.Now;
|
||||
this.spell = Server.GetWorldManager().GetBattleCommand(spellId);
|
||||
var returnCode = lua.LuaEngine.CallLuaBattleCommandFunction(owner, spell, "magic", "onMagicPrepare", owner, target, spell);
|
||||
|
||||
//Modify spell based on status effects. Need to do it here because they can modify cast times
|
||||
List<StatusEffect> effects = owner.statusEffects.GetStatusEffectsByFlag((uint)(StatusEffectFlags.ActivateOnCastStart));
|
||||
var returnCode = spell.CallLuaFunction(owner, "onMagicPrepare", owner, target, spell);
|
||||
|
||||
//modify skill based on status effects
|
||||
//Do this here to allow buffs like Resonance to increase range before checking CanCast()
|
||||
foreach (var effect in effects)
|
||||
lua.LuaEngine.CallLuaStatusEffectFunction(owner, effect, "onMagicCast", owner, effect, spell);
|
||||
owner.statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnCastStart, "onMagicCast", owner, spell);
|
||||
|
||||
this.target = target != null ? target : owner;
|
||||
this.target = (spell.mainTarget & ValidTarget.SelfOnly) != 0 ? owner : target;
|
||||
|
||||
if (returnCode == 0 && owner.CanCast(this.target, spell))
|
||||
errorResult = new CommandResult(owner.actorId, 32553, 0);
|
||||
if (returnCode == 0 && owner.CanUse(this.target, spell, errorResult))
|
||||
{
|
||||
OnStart();
|
||||
}
|
||||
else
|
||||
{
|
||||
errorResult = new CommandResult(owner.actorId, 32553, 0);
|
||||
interrupt = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnStart()
|
||||
{
|
||||
var returnCode = lua.LuaEngine.CallLuaBattleCommandFunction(owner, spell, "magic", "onMagicStart", owner, target, spell);
|
||||
var returnCode = spell.CallLuaFunction(owner, "onMagicStart", owner, target, spell);
|
||||
|
||||
if (returnCode != 0)
|
||||
{
|
||||
@ -62,7 +58,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
|
||||
Player p = (Player)owner;
|
||||
if (spell.comboStep == 1 || ((p.playerWork.comboNextCommandId[0] == spell.id || p.playerWork.comboNextCommandId[1] == spell.id)))
|
||||
{
|
||||
lua.LuaEngine.CallLuaBattleCommandFunction(owner, spell, "magic", "onCombo", owner, target, spell);
|
||||
spell.CallLuaFunction(owner, "onCombo", owner, target, spell);
|
||||
spell.isCombo = true;
|
||||
}
|
||||
}
|
||||
@ -168,7 +164,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
|
||||
|
||||
private bool CanCast()
|
||||
{
|
||||
return owner.CanCast(target, spell) && spell.IsValidMainTarget(owner, target) && !HasMoved();
|
||||
return owner.CanUse(target, spell);
|
||||
}
|
||||
|
||||
private bool HasMoved()
|
||||
|
@ -14,11 +14,14 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
|
||||
base(owner, target)
|
||||
{
|
||||
this.startTime = DateTime.Now;
|
||||
//this.target = skill.targetFind.
|
||||
this.skill = Server.GetWorldManager().GetBattleCommand(skillId);
|
||||
var returnCode = lua.LuaEngine.CallLuaBattleCommandFunction(owner, skill, "weaponskill", "onSkillPrepare", owner, target, skill);
|
||||
|
||||
if (returnCode == 0 && owner.CanWeaponSkill(target, skill))
|
||||
var returnCode = skill.CallLuaFunction(owner, "onSkillPrepare", owner, target, skill);
|
||||
|
||||
this.target = (skill.mainTarget & ValidTarget.SelfOnly) != 0 ? owner : target;
|
||||
|
||||
errorResult = new CommandResult(owner.actorId, 32553, 0);
|
||||
if (returnCode == 0 && owner.CanUse(this.target, skill, errorResult))
|
||||
{
|
||||
OnStart();
|
||||
}
|
||||
@ -31,7 +34,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
|
||||
|
||||
public override void OnStart()
|
||||
{
|
||||
var returnCode = lua.LuaEngine.CallLuaBattleCommandFunction(owner, skill, "weaponskill", "onSkillStart", owner, target, skill);
|
||||
var returnCode = skill.CallLuaFunction(owner, "onSkillStart", owner, target, skill);
|
||||
|
||||
if (returnCode != 0)
|
||||
{
|
||||
@ -48,8 +51,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
|
||||
{
|
||||
//If there is a position bonus
|
||||
if (skill.positionBonus != BattleCommandPositionBonus.None)
|
||||
//lua.LuaEngine.CallLuaBattleCommandFunction(owner, skill, "weaponskill", "onPositional", owner, target, skill);
|
||||
skill.CallLuaFunction(owner, "onPositional", owner, target, skill);
|
||||
skill.CallLuaFunction(owner, "weaponskill", "onPositional", owner, target, skill);
|
||||
|
||||
//Combo stuff
|
||||
if (owner is Player)
|
||||
@ -61,7 +63,6 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
|
||||
//If owner is a player and the skill being used is part of the current combo
|
||||
if (p.playerWork.comboNextCommandId[0] == skill.id || p.playerWork.comboNextCommandId[1] == skill.id)
|
||||
{
|
||||
lua.LuaEngine.CallLuaBattleCommandFunction(owner, skill, "weaponskill", "onCombo", owner, target, skill);
|
||||
skill.CallLuaFunction(owner, "onCombo", owner, target, skill);
|
||||
skill.isCombo = true;
|
||||
}
|
||||
@ -161,7 +162,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
|
||||
|
||||
private bool CanUse()
|
||||
{
|
||||
return owner.CanWeaponSkill(target, skill) && skill.IsValidMainTarget(owner, target);
|
||||
return owner.CanUse(target, skill);
|
||||
}
|
||||
|
||||
public BattleCommand GetWeaponSkill()
|
||||
|
@ -12,37 +12,55 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
|
||||
static class BattleUtils
|
||||
{
|
||||
|
||||
public static Dictionary<HitType, ushort> SingleHitTypeTextIds = new Dictionary<HitType, ushort>()
|
||||
public static Dictionary<HitType, ushort> PhysicalHitTypeTextIds = new Dictionary<HitType, ushort>()
|
||||
{
|
||||
{ HitType.Miss, 30311 },
|
||||
{ HitType.Evade, 30310 },
|
||||
{ HitType.Parry, 30308 },
|
||||
{ HitType.Block, 30306 },
|
||||
{ HitType.Resist, 30310 }, //Resists seem to use the evade text id
|
||||
{ HitType.Hit, 30301 },
|
||||
{ HitType.Crit, 30302 }
|
||||
};
|
||||
|
||||
public static Dictionary<HitType, ushort> MagicalHitTypeTextIds = new Dictionary<HitType, ushort>()
|
||||
{
|
||||
{ HitType.SingleResist,30318 },
|
||||
{ HitType.DoubleResist,30317 },
|
||||
{ HitType.TripleResist, 30316 },//Triple Resists seem to use the same text ID as full resists
|
||||
{ HitType.FullResist,30316 },
|
||||
{ HitType.Hit, 30319 },
|
||||
{ HitType.Crit, 30392 } //Unsure why crit is separated from the rest of the ids
|
||||
};
|
||||
|
||||
public static Dictionary<HitType, ushort> MultiHitTypeTextIds = new Dictionary<HitType, ushort>()
|
||||
{
|
||||
{ HitType.Miss, 30449 }, //The attack misses.
|
||||
{ HitType.Evade, 0 }, //Evades were removed before multi hit skills got their own messages, so this doesnt exist
|
||||
{ HitType.Parry, 30448 }, //[Target] parries, taking x points of damage.
|
||||
{ HitType.Block, 30447 }, //[Target] blocks, taking x points of damage.
|
||||
{ HitType.Resist, 0 }, //No spells are multi-hit, so this doesn't exist
|
||||
{ HitType.Hit, 30443 }, //[Target] tales x points of damage
|
||||
{ HitType.Crit, 30444 } //Critical! [Target] takes x points of damage.
|
||||
};
|
||||
|
||||
public static Dictionary<HitType, HitEffect> HitTypeEffects = new Dictionary<HitType, HitEffect>()
|
||||
public static Dictionary<HitType, HitEffect> HitTypeEffectsPhysical = new Dictionary<HitType, HitEffect>()
|
||||
{
|
||||
{ HitType.Miss, 0 },
|
||||
{ HitType.Evade, HitEffect.Evade },
|
||||
{ HitType.Parry, HitEffect.Parry },
|
||||
{ HitType.Block, HitEffect.Block },
|
||||
{ HitType.Resist, HitEffect.RecoilLv1 },//Probably don't need this, resists are handled differently to the rest
|
||||
{ HitType.Hit, HitEffect.Hit },
|
||||
{ HitType.Crit, HitEffect.Crit }
|
||||
{ HitType.Crit, HitEffect.Crit | HitEffect.CriticalHit }
|
||||
};
|
||||
|
||||
//Magic attacks can't miss, be blocked, or parried. Resists are technically evades
|
||||
public static Dictionary<HitType, HitEffect> HitTypeEffectsMagical = new Dictionary<HitType, HitEffect>()
|
||||
{
|
||||
{ HitType.SingleResist, HitEffect.WeakResist },
|
||||
{ HitType.DoubleResist, HitEffect.WeakResist },
|
||||
{ HitType.TripleResist, HitEffect.WeakResist },
|
||||
{ HitType.FullResist, HitEffect.FullResist },
|
||||
{ HitType.Hit, HitEffect.NoResist },
|
||||
{ HitType.Crit, HitEffect.Crit }
|
||||
};
|
||||
|
||||
public static Dictionary<KnockbackType, HitEffect> KnockbackEffects = new Dictionary<KnockbackType, HitEffect>()
|
||||
@ -201,7 +219,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
|
||||
//Or we could have HitTypes for DoubleResist, TripleResist, and FullResist that get used here.
|
||||
public static void CalculateResistDamage(Character attacker, Character defender, BattleCommand skill, CommandResult action)
|
||||
{
|
||||
double percentResist = 0.5;
|
||||
//Every tier of resist is a 25% reduction in damage. ie SingleResist is 25% damage taken down, Double is 50% damage taken down, etc
|
||||
double percentResist = 0.25 * (action.hitType - HitType.SingleResist + 1);
|
||||
|
||||
action.amountMitigated = (ushort)(action.amount * (1 - percentResist));
|
||||
action.amount = (ushort)(action.amount * percentResist);
|
||||
@ -217,7 +236,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
|
||||
defender.SubtractMod((uint)Modifier.Stoneskin, mitigation);
|
||||
}
|
||||
|
||||
public static void DamageTarget(Character attacker, Character defender, CommandResult action, CommandResultContainer actionContainer= null)
|
||||
public static void DamageTarget(Character attacker, Character defender, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer= null)
|
||||
{
|
||||
if (defender != null)
|
||||
{
|
||||
@ -230,9 +249,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
|
||||
bnpc.lastAttacker = attacker;
|
||||
}
|
||||
|
||||
defender.DelHP((short)action.amount);
|
||||
attacker.OnDamageDealt(defender, action, actionContainer);
|
||||
defender.OnDamageTaken(attacker, action, actionContainer);
|
||||
defender.DelHP((short)action.amount, actionContainer);
|
||||
attacker.OnDamageDealt(defender, skill, action, actionContainer);
|
||||
defender.OnDamageTaken(attacker, skill, action, actionContainer);
|
||||
|
||||
// todo: other stuff too
|
||||
if (defender is BattleNpc)
|
||||
@ -248,13 +267,13 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
|
||||
}
|
||||
}
|
||||
|
||||
public static void HealTarget(Character caster, Character target, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
public static void HealTarget(Character caster, Character target, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
{
|
||||
if (target != null)
|
||||
{
|
||||
target.AddHP(action.amount);
|
||||
target.AddHP(action.amount, actionContainer);
|
||||
|
||||
target.statusEffects.CallLuaFunctionByFlag((uint) StatusEffectFlags.ActivateOnHealed, "onHealed", caster, target, action, actionContainer);
|
||||
target.statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnHealed, "onHealed", caster, target, skill, action, actionContainer);
|
||||
}
|
||||
}
|
||||
|
||||
@ -279,7 +298,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;
|
||||
@ -324,7 +343,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());
|
||||
@ -355,11 +374,26 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
|
||||
return false;
|
||||
}
|
||||
|
||||
//This probably isn't totally correct but it's close enough for now.
|
||||
//Full Resists seem to be calculated in a different way because the resist rates don't seem to line up with kanikan's testing (their tests didn't show any full resists)
|
||||
//Non-spells with elemental damage can be resisted, it just doesnt say in the chat that they were. As far as I can tell, all mob-specific attacks are considered not to be spells
|
||||
public static bool TryResist(Character attacker, Character defender, BattleCommand skill, CommandResult action)
|
||||
{
|
||||
if ((Program.Random.NextDouble() * 100) <= action.resistRate)
|
||||
//The rate degrades for each check. Meaning with 100% resist, the attack will always be resisted, but it won't necessarily be a triple or full resist
|
||||
//Rates beyond 100 still increase the chance for higher resist tiers though
|
||||
double rate = action.resistRate;
|
||||
|
||||
int i = -1;
|
||||
|
||||
while ((Program.Random.NextDouble() * 100) <= rate && i < 4)
|
||||
{
|
||||
action.hitType = HitType.Resist;
|
||||
rate /= 2;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i != -1)
|
||||
{
|
||||
action.hitType = (HitType) ((int) HitType.SingleResist + i);
|
||||
CalculateResistDamage(attacker, defender, skill, action);
|
||||
return true;
|
||||
}
|
||||
@ -425,6 +459,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
|
||||
FinishActionStatus(caster, target, skill, action, actionContainer);
|
||||
break;
|
||||
default:
|
||||
action.effectId = (uint) HitEffect.AnimationEffectType;
|
||||
actionContainer.AddAction(action);
|
||||
break;
|
||||
}
|
||||
@ -450,7 +485,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
|
||||
}
|
||||
|
||||
//Actions have different text ids depending on whether they're a part of a multi-hit ws or not.
|
||||
Dictionary<HitType, ushort> textIds = SingleHitTypeTextIds;
|
||||
Dictionary<HitType, ushort> textIds = PhysicalHitTypeTextIds;
|
||||
|
||||
//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.
|
||||
@ -473,23 +508,27 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
|
||||
|
||||
actionContainer.AddAction(action);
|
||||
action.enmity = (ushort) (action.enmity * (skill != null ? skill.enmityModifier : 1));
|
||||
|
||||
//Damage the target
|
||||
DamageTarget(attacker, defender, action, actionContainer);
|
||||
DamageTarget(attacker, defender, skill, action, actionContainer);
|
||||
}
|
||||
|
||||
public static void FinishActionSpell(Character attacker, Character defender, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
{
|
||||
//I'm assuming that like physical attacks stoneskin is taken into account before mitigation
|
||||
HandleStoneskin(defender, action);
|
||||
|
||||
//Determine the hit type of the action
|
||||
if (!TryMiss(attacker, defender, skill, action))
|
||||
//Spells don't seem to be able to miss, instead magic acc/eva is used for resists (which are generally called evades in game)
|
||||
//Unlike blocks and parries, crits do not go through resists.
|
||||
if (!TryResist(attacker, defender, skill, action))
|
||||
{
|
||||
HandleStoneskin(defender, action);
|
||||
if (!TryCrit(attacker, defender, skill, action))
|
||||
if (!TryResist(attacker, defender, skill, action))
|
||||
action.hitType = HitType.Hit;
|
||||
action.hitType = HitType.Hit;
|
||||
}
|
||||
|
||||
//There are no multi-hit spells
|
||||
action.worldMasterTextId = SingleHitTypeTextIds[action.hitType];
|
||||
//There are no multi-hit spells, so we don't need to take that into account
|
||||
action.worldMasterTextId = MagicalHitTypeTextIds[action.hitType];
|
||||
|
||||
//Set the hit effect
|
||||
SetHitEffectSpell(attacker, defender, skill, action);
|
||||
@ -500,7 +539,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
|
||||
|
||||
actionContainer.AddAction(action);
|
||||
|
||||
DamageTarget(attacker, defender, action, actionContainer);
|
||||
DamageTarget(attacker, defender, skill, action, actionContainer);
|
||||
}
|
||||
|
||||
public static void FinishActionHeal(Character attacker, Character defender, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
@ -510,7 +549,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
|
||||
|
||||
actionContainer.AddAction(action);
|
||||
|
||||
HealTarget(attacker, defender, action, actionContainer);
|
||||
HealTarget(attacker, defender, skill, action, actionContainer);
|
||||
}
|
||||
|
||||
public static void FinishActionStatus(Character attacker, Character defender, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
@ -542,10 +581,10 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
|
||||
hitEffect |= HitEffect.RecoilLv3;
|
||||
}
|
||||
|
||||
hitEffect |= HitTypeEffects[hitType];
|
||||
hitEffect |= HitTypeEffectsPhysical[hitType];
|
||||
|
||||
//For combos that land, add the combo effect
|
||||
if (skill != null && skill.isCombo && hitType > HitType.Evade && hitType != HitType.Evade && !skill.comboEffectAdded)
|
||||
if (skill != null && skill.isCombo && action.ActionLanded() && !skill.comboEffectAdded)
|
||||
{
|
||||
hitEffect |= (HitEffect)(skill.comboStep << 15);
|
||||
skill.comboEffectAdded = true;
|
||||
@ -555,7 +594,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
|
||||
if (hitType >= HitType.Parry)
|
||||
{
|
||||
//Protect / Shell only show on physical/ magical attacks respectively.
|
||||
if (defender.statusEffects.HasStatusEffect(StatusEffectId.Protect))
|
||||
if (defender.statusEffects.HasStatusEffect(StatusEffectId.Protect) || defender.statusEffects.HasStatusEffect(StatusEffectId.Protect2))
|
||||
if (action != null)
|
||||
hitEffect |= HitEffect.Protect;
|
||||
|
||||
@ -572,20 +611,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
|
||||
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];
|
||||
hitEffect |= HitTypeEffectsMagical[hitType];
|
||||
|
||||
if (skill != null && skill.isCombo && !skill.comboEffectAdded)
|
||||
{
|
||||
@ -594,16 +621,15 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
|
||||
}
|
||||
|
||||
//if attack hit the target, take into account protective status effects
|
||||
if (hitType >= HitType.Block)
|
||||
if (action.ActionLanded())
|
||||
{
|
||||
//Protect / Shell only show on physical/ magical attacks respectively.
|
||||
//The magic hit effect category only has a flag for shell (and another shield effect that seems unused)
|
||||
//Even though traited protect gives magic defense, the shell effect doesn't play on attacks
|
||||
//This also means stoneskin doesnt show, but it does reduce damage
|
||||
if (defender.statusEffects.HasStatusEffect(StatusEffectId.Shell))
|
||||
if (action != null)
|
||||
hitEffect |= HitEffect.Shell;
|
||||
|
||||
if (defender.statusEffects.HasStatusEffect(StatusEffectId.Stoneskin))
|
||||
if (action != null)
|
||||
hitEffect |= HitEffect.Stoneskin;
|
||||
hitEffect |= HitEffect.MagicShell;
|
||||
}
|
||||
action.effectId = (uint)hitEffect;
|
||||
}
|
||||
@ -654,7 +680,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
|
||||
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)
|
||||
if (skill != null && skill.statusId != 0 && (action.ActionLanded()) && rand < skill.statusChance)
|
||||
{
|
||||
StatusEffect effect = Server.GetWorldManager().GetStatusEffect(skill.statusId);
|
||||
//Because combos might change duration or tier
|
||||
@ -670,7 +696,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
|
||||
{
|
||||
//If we need an extra action to show the status text
|
||||
if (isAdditional)
|
||||
results.AddAction(target.actorId, 30328, skill.statusId | (uint) HitEffect.StatusEffectType);
|
||||
results.AddAction(target.actorId, effect.GetStatusGainTextId(), skill.statusId | (uint) HitEffect.StatusEffectType);
|
||||
}
|
||||
else
|
||||
action.worldMasterTextId = 32002;//Is this right?
|
||||
@ -733,7 +759,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
|
||||
return 0;
|
||||
|
||||
int baseLevel = Math.Min(player.GetLevel(), mob.GetLevel());
|
||||
ushort baseEXP = BASEEXP[baseLevel];
|
||||
ushort baseEXP = BASEEXP[baseLevel - 1];
|
||||
|
||||
double dlvlModifier = 1.0;
|
||||
|
||||
@ -864,10 +890,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))));
|
||||
}
|
||||
|
@ -181,7 +181,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool CanCast(Character target, BattleCommand spell)
|
||||
public override bool CanUse(Character target, BattleCommand spell, CommandResult error = null)
|
||||
{
|
||||
// todo:
|
||||
if (target == null)
|
||||
@ -202,18 +202,6 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool CanWeaponSkill(Character target, BattleCommand skill)
|
||||
{
|
||||
// todo:
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool CanUseAbility(Character target, BattleCommand ability)
|
||||
{
|
||||
// todo:
|
||||
return false;
|
||||
}
|
||||
|
||||
public uint GetDespawnTime()
|
||||
{
|
||||
return despawnTime;
|
||||
@ -256,8 +244,6 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
this.hateContainer.ClearHate();
|
||||
zone.BroadcastPacketsAroundActor(this, GetSpawnPackets(null, 0x01));
|
||||
zone.BroadcastPacketsAroundActor(this, GetInitPackets());
|
||||
charaWork.parameterSave.hp = charaWork.parameterSave.hpMax;
|
||||
charaWork.parameterSave.mp = charaWork.parameterSave.mpMax;
|
||||
RecalculateStats();
|
||||
|
||||
OnSpawn();
|
||||
@ -280,6 +266,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
// <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)
|
||||
@ -303,6 +290,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
|
||||
if (positionUpdates != null)
|
||||
positionUpdates.Clear();
|
||||
|
||||
aiContainer.InternalDie(tick, despawnTime);
|
||||
//this.ResetMoveSpeeds();
|
||||
// todo: reset cooldowns
|
||||
@ -446,11 +434,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -139,6 +139,8 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
private List<Director> ownedDirectors = new List<Director>();
|
||||
private Director loginInitDirector = null;
|
||||
|
||||
List<ushort> hotbarSlotsToUpdate = new List<ushort>();
|
||||
|
||||
public PlayerWork playerWork = new PlayerWork();
|
||||
|
||||
public Session playerSession;
|
||||
@ -759,10 +761,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);
|
||||
}
|
||||
|
||||
@ -1018,6 +1021,12 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
|
||||
//Check if bonus point available... set
|
||||
|
||||
//Remove buffs that fall off when changing class
|
||||
CommandResultContainer resultContainer = new CommandResultContainer();
|
||||
statusEffects.RemoveStatusEffectsByFlags((uint)StatusEffectFlags.LoseOnClassChange, resultContainer);
|
||||
resultContainer.CombineLists();
|
||||
DoBattleAction(0, 0x7c000062, resultContainer.GetList());
|
||||
|
||||
//Set rested EXP
|
||||
charaWork.parameterSave.state_mainSkill[0] = classId;
|
||||
charaWork.parameterSave.state_mainSkillLevel = charaWork.battleSave.skillLevel[classId-1];
|
||||
@ -1956,7 +1965,14 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
}
|
||||
|
||||
QueuePackets(propPacketUtil.Done());
|
||||
}
|
||||
|
||||
if ((updateFlags & ActorUpdateFlags.Hotbar) != 0)
|
||||
{
|
||||
UpdateHotbar(hotbarSlotsToUpdate);
|
||||
hotbarSlotsToUpdate.Clear();
|
||||
|
||||
updateFlags ^= ActorUpdateFlags.Hotbar;
|
||||
}
|
||||
|
||||
|
||||
@ -1972,12 +1988,11 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
//Update commands and recast timers for the entire hotbar
|
||||
public void UpdateHotbar()
|
||||
{
|
||||
List<ushort> slotsToUpdate = new List<ushort>();
|
||||
for (ushort i = charaWork.commandBorder; i < charaWork.commandBorder + 30; i++)
|
||||
{
|
||||
slotsToUpdate.Add(i);
|
||||
hotbarSlotsToUpdate.Add(i);
|
||||
}
|
||||
UpdateHotbar(slotsToUpdate);
|
||||
updateFlags |= ActorUpdateFlags.Hotbar;
|
||||
}
|
||||
|
||||
//Updates the hotbar and recast timers for only certain hotbar slots
|
||||
@ -2049,7 +2064,6 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
ushort lowHotbarSlot = (ushort)(hotbarSlot - charaWork.commandBorder);
|
||||
ushort maxRecastTime = (ushort)(ability != null ? ability.maxRecastTimeSeconds : 5);
|
||||
uint recastEnd = Utils.UnixTimeStampUTC() + maxRecastTime;
|
||||
List<ushort> slotsToUpdate = new List<ushort>();
|
||||
|
||||
Database.EquipAbility(this, classId, (ushort) (hotbarSlot - charaWork.commandBorder), commandId, recastEnd);
|
||||
//If the class we're equipping for is the current class (need to find out if state_mainSkill is supposed to change when you're a job)
|
||||
@ -2061,8 +2075,8 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
charaWork.parameterTemp.maxCommandRecastTime[lowHotbarSlot] = maxRecastTime;
|
||||
charaWork.parameterSave.commandSlot_recastTime[lowHotbarSlot] = recastEnd;
|
||||
|
||||
slotsToUpdate.Add(hotbarSlot);
|
||||
UpdateHotbar(slotsToUpdate);
|
||||
hotbarSlotsToUpdate.Add(hotbarSlot);
|
||||
updateFlags |= ActorUpdateFlags.Hotbar;
|
||||
}
|
||||
|
||||
|
||||
@ -2098,25 +2112,23 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
Database.EquipAbility(this, GetCurrentClassOrJob(), (ushort)(lowHotbarSlot2), 0xA0F00000 ^ charaWork.command[hotbarSlot2], charaWork.parameterSave.commandSlot_recastTime[lowHotbarSlot2]);
|
||||
|
||||
//Update slots on client
|
||||
List<ushort> slotsToUpdate = new List<ushort>();
|
||||
slotsToUpdate.Add(hotbarSlot1);
|
||||
slotsToUpdate.Add(hotbarSlot2);
|
||||
UpdateHotbar(slotsToUpdate);
|
||||
hotbarSlotsToUpdate.Add(hotbarSlot1);
|
||||
hotbarSlotsToUpdate.Add(hotbarSlot2);
|
||||
updateFlags |= ActorUpdateFlags.Hotbar;
|
||||
}
|
||||
|
||||
public void UnequipAbility(ushort hotbarSlot, bool printMessage = true)
|
||||
{
|
||||
List<ushort> slotsToUpdate = new List<ushort>();
|
||||
ushort trueHotbarSlot = (ushort)(hotbarSlot + charaWork.commandBorder - 1);
|
||||
ushort trueHotbarSlot = (ushort)(hotbarSlot + charaWork.commandBorder);
|
||||
uint commandId = charaWork.command[trueHotbarSlot];
|
||||
Database.UnequipAbility(this, (ushort)(trueHotbarSlot - charaWork.commandBorder));
|
||||
Database.UnequipAbility(this, hotbarSlot);
|
||||
charaWork.command[trueHotbarSlot] = 0;
|
||||
slotsToUpdate.Add(trueHotbarSlot);
|
||||
hotbarSlotsToUpdate.Add(trueHotbarSlot);
|
||||
|
||||
if(printMessage)
|
||||
if (printMessage && commandId != 0)
|
||||
SendGameMessage(Server.GetWorldManager().GetActor(), 30604, 0x20, 0, 0xA0F00000 ^ commandId);
|
||||
|
||||
UpdateHotbar(slotsToUpdate);
|
||||
updateFlags |= ActorUpdateFlags.Hotbar;
|
||||
}
|
||||
|
||||
//Finds the first hotbar slot with a given commandId.
|
||||
@ -2290,110 +2302,75 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool CanCast(Character target, BattleCommand spell)
|
||||
//Do we need separate functions? they check the same things
|
||||
public override bool CanUse(Character target, BattleCommand skill, CommandResult error = null)
|
||||
{
|
||||
//Might want to do these with a CommandResult instead to be consistent with the rest of command stuff
|
||||
if (GetHotbarTimer(spell.id) > Utils.UnixTimeStampUTC())
|
||||
{
|
||||
// todo: this needs confirming
|
||||
// Please wait a moment and try again.
|
||||
SendGameMessage(Server.GetWorldManager().GetActor(), 32535, 0x20, (uint)spell.id);
|
||||
return false;
|
||||
}
|
||||
if (target == null)
|
||||
{
|
||||
// Target does not exist.
|
||||
SendGameMessage(Server.GetWorldManager().GetActor(), 32511, 0x20, (uint)spell.id);
|
||||
return false;
|
||||
}
|
||||
if (Utils.XZDistance(positionX, positionZ, target.positionX, target.positionZ) > spell.range)
|
||||
{
|
||||
// The target is too far away.
|
||||
SendGameMessage(Server.GetWorldManager().GetActor(), 32539, 0x20, (uint)spell.id);
|
||||
return false;
|
||||
}
|
||||
if (Utils.XZDistance(positionX, positionZ, target.positionX, target.positionZ) < spell.minRange)
|
||||
{
|
||||
// The target is too close.
|
||||
SendGameMessage(Server.GetWorldManager().GetActor(), 32538, 0x20, (uint)spell.id);
|
||||
return false;
|
||||
}
|
||||
if (target.positionY - positionY > (spell.rangeHeight / 2))
|
||||
{
|
||||
// The target is too far above you.
|
||||
SendGameMessage(Server.GetWorldManager().GetActor(), 32540, 0x20, (uint)spell.id);
|
||||
return false;
|
||||
}
|
||||
if (positionY - target.positionY > (spell.rangeHeight / 2))
|
||||
{
|
||||
// The target is too far below you.
|
||||
SendGameMessage(Server.GetWorldManager().GetActor(), 32541, 0x20, (uint)spell.id);
|
||||
return false;
|
||||
}
|
||||
if (!IsValidTarget(target, spell.mainTarget) || !spell.IsValidMainTarget(this, target))
|
||||
if (!skill.IsValidMainTarget(this, target, error) || !IsValidTarget(target, skill.mainTarget))
|
||||
{
|
||||
// error packet is set in IsValidTarget
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool CanWeaponSkill(Character target, BattleCommand skill)
|
||||
{
|
||||
// todo: see worldmaster ids 32558~32557 for proper ko message and stuff
|
||||
//Might want to do these with a BattleAction instead to be consistent with the rest of command stuff
|
||||
if (GetHotbarTimer(skill.id) > Utils.UnixTimeStampUTC())
|
||||
{
|
||||
// todo: this needs confirming
|
||||
// Please wait a moment and try again.
|
||||
SendGameMessage(Server.GetWorldManager().GetActor(), 32535, 0x20, (uint)skill.id);
|
||||
error?.SetTextId(32535);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target == null)
|
||||
if (Utils.XZDistance(positionX, positionZ, target.positionX, target.positionZ) > skill.range)
|
||||
{
|
||||
// Target does not exist.
|
||||
SendGameMessage(Server.GetWorldManager().GetActor(), 32511, 0x20, (uint)skill.id);
|
||||
// The target is too far away.
|
||||
error?.SetTextId(32539);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Utils.XZDistance(positionX, positionZ, target.positionX, target.positionZ) < skill.minRange)
|
||||
{
|
||||
// The target is too close.
|
||||
error?.SetTextId(32538);
|
||||
return false;
|
||||
}
|
||||
|
||||
//Original game checked height difference before horizontal distance
|
||||
if (target.positionY - positionY > (skill.rangeHeight / 2))
|
||||
{
|
||||
// The target is too far above you.
|
||||
SendGameMessage(Server.GetWorldManager().GetActor(), 32540, 0x20, (uint)skill.id);
|
||||
error?.SetTextId(32540);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (positionY - target.positionY > (skill.rangeHeight / 2))
|
||||
{
|
||||
// The target is too far below you.
|
||||
SendGameMessage(Server.GetWorldManager().GetActor(), 32541, 0x20, (uint)skill.id);
|
||||
error?.SetTextId(32541);
|
||||
return false;
|
||||
}
|
||||
|
||||
var targetDist = Utils.XZDistance(positionX, positionZ, target.positionX, target.positionZ);
|
||||
|
||||
if (targetDist > skill.range)
|
||||
if (skill.CalculateMpCost(this) > GetMP())
|
||||
{
|
||||
// The target is out of range.
|
||||
SendGameMessage(Server.GetWorldManager().GetActor(), 32537, 0x20, (uint)skill.id);
|
||||
// You do not have enough MP.
|
||||
error?.SetTextId(32545);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (targetDist < skill.minRange)
|
||||
if (skill.CalculateTpCost(this) > GetTP())
|
||||
{
|
||||
// The target is too close.
|
||||
SendGameMessage(Server.GetWorldManager().GetActor(), 32538, 0x20, (uint)skill.id);
|
||||
// You do not have enough TP.
|
||||
error?.SetTextId(32546);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!IsValidTarget(target, skill.validTarget) || !skill.IsValidMainTarget(this, target))
|
||||
//Proc requirement
|
||||
if (skill.procRequirement != BattleCommandProcRequirement.None && !charaWork.battleTemp.timingCommandFlag[(int)skill.procRequirement - 1])
|
||||
{
|
||||
// error packet is set in IsValidTarget
|
||||
//Conditions for use are not met
|
||||
error?.SetTextId(32556);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2598,7 +2575,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
|
||||
@ -2634,10 +2611,10 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
}
|
||||
|
||||
var hasShield = equip.GetItemAtSlot(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
|
||||
@ -2647,13 +2624,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));
|
||||
|
@ -2,6 +2,7 @@
|
||||
using FFXIVClassic_Map_Server.dataobjects;
|
||||
using FFXIVClassic_Map_Server.packets.send.group;
|
||||
using FFXIVClassic_Map_Server.packets.send.groups;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FFXIVClassic_Map_Server.actors.group
|
||||
@ -126,13 +127,14 @@ namespace FFXIVClassic_Map_Server.actors.group
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (GetMemberCount() - currentIndex >= 64)
|
||||
int memberCount = Math.Min(GetMemberCount(), members.Count);
|
||||
if (memberCount - currentIndex >= 64)
|
||||
session.QueuePacket(GroupMembersX64Packet.buildPacket(session.id, session.GetActor().zoneId, time, members, ref currentIndex));
|
||||
else if (GetMemberCount() - currentIndex >= 32)
|
||||
else if (memberCount - currentIndex >= 32)
|
||||
session.QueuePacket(GroupMembersX32Packet.buildPacket(session.id, session.GetActor().zoneId, time, members, ref currentIndex));
|
||||
else if (GetMemberCount() - currentIndex >= 16)
|
||||
else if (memberCount - currentIndex >= 16)
|
||||
session.QueuePacket(GroupMembersX16Packet.buildPacket(session.id, session.GetActor().zoneId, time, members, ref currentIndex));
|
||||
else if (GetMemberCount() - currentIndex > 0)
|
||||
else if (memberCount - currentIndex > 0)
|
||||
session.QueuePacket(GroupMembersX08Packet.buildPacket(session.id, session.GetActor().zoneId, time, members, ref currentIndex));
|
||||
else
|
||||
break;
|
||||
|
@ -8,13 +8,26 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
|
||||
//These flags can be stacked and mixed, but the client will prioritize certain flags over others.
|
||||
[Flags]
|
||||
public enum HitEffect : uint
|
||||
{
|
||||
//All HitEffects have the last byte 0x8
|
||||
{
|
||||
//This is used for physical attacks
|
||||
HitEffectType = 8 << 24,
|
||||
//This is used for additioanl effect hits. Only difference from HitEffectType is that it does not play audio.
|
||||
AdditionalEffectType = 24 << 24,
|
||||
//Status effects use 32 << 24
|
||||
StatusEffectType = 32 << 24,
|
||||
//Magic effects use 48 << 24
|
||||
//When losing a status effect while using a skill, this prevents the hit effect from playing on the actor playing the animation
|
||||
StatusLossType = 40 << 24,
|
||||
//Magic effects use 48 << 24, this is also used for when statuses are lost on attack
|
||||
MagicEffectType = 48 << 24,
|
||||
//This places the number on the user regardless of the target this hit effect is for, used for things like bloodbath
|
||||
SelfHealType = 72 << 24,
|
||||
//Plays the effect animation with no text or additional effects. Unsure if there are any flags. Used for things like Convert
|
||||
AnimationEffectType = 96 << 24,
|
||||
|
||||
//Each Type has it's own set of flags. These should be split into their own enums,
|
||||
//but for now just keep them all under HitEffect so we don't have to change anything.
|
||||
|
||||
//HitEffectType flags
|
||||
|
||||
//Not setting RecoilLv2 or RecoilLv3 results in the weaker RecoilLv1.
|
||||
//These are the recoil animations that play on the target, ranging from weak to strong.
|
||||
@ -54,15 +67,10 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
|
||||
//Another effect plays when both Protect and Shell flags are activated.
|
||||
//Not sure what this effect is.
|
||||
//Random guess: if the attack was a hybrid of both physical and magical and the target had both Protect and Shell buffs applied.
|
||||
Protect = 1 << 6 | HitEffectType,
|
||||
Shell = 1 << 7 | HitEffectType,
|
||||
Protect = 1 << 6,
|
||||
Shell = 1 << 7,
|
||||
ProtectShellSpecial = Protect | Shell,
|
||||
|
||||
// Required for heal text to be blue, not sure if that's all it's used for
|
||||
Heal = 1 << 8,
|
||||
MP = 1 << 9, //Causes "MP" text to appear when used with MagicEffectType. | with Heal to make text blue
|
||||
TP = 1 << 10,//Causes "TP" text to appear when used with MagicEffectType. | with Heal to make text blue
|
||||
|
||||
//If only HitEffect1 is set out of the hit effects, the "Evade!" pop-up text triggers along with the evade visual.
|
||||
//If no hit effects are set, the "Miss!" pop-up is triggered and no hit visual is played.
|
||||
HitEffect1 = 1 << 9,
|
||||
@ -103,17 +111,69 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
|
||||
UnknownShieldEffect = HitEffect5 | HitEffect4,
|
||||
Stoneskin = HitEffect5 | HitEffect4 | HitEffect1,
|
||||
|
||||
//Unknown = 1 << 14, -- Not sure what this flag does; might be another HitEffect.
|
||||
|
||||
//A special effect when performing appropriate skill combos in succession.
|
||||
//Ex: Thunder (SkillCombo1 Effect) -> Thundara (SkillCombo2 Effect) -> Thundaga (SkillCombo3 Effect)
|
||||
//Special Note: SkillCombo4 was never actually used in 1.0 since combos only chained up to 3 times maximum.
|
||||
SkillCombo1 = 1 << 15,
|
||||
SkillCombo2 = 1 << 16,
|
||||
SkillCombo3 = SkillCombo1 | SkillCombo2,
|
||||
SkillCombo4 = 1 << 17
|
||||
SkillCombo4 = 1 << 17,
|
||||
|
||||
//Flags beyond here are unknown/untested.
|
||||
//This is used in the absorb effect for some reason
|
||||
Unknown = 1 << 19,
|
||||
|
||||
//AdditionalEffectType flags
|
||||
//The AdditionalEffectType is used for the additional effects some weapons have.
|
||||
//These effect ids do not repeat the effect of the attack and will not show without a preceding HitEffectType or MagicEffectType
|
||||
|
||||
//It's unclear what this is for. The ifrit fight capture has a BLM using the garuda weapon
|
||||
//and this flag is set every time but has no apparent effect.
|
||||
UnknownAdditionalFlag = 1,
|
||||
|
||||
//These play effects on the target
|
||||
FireEffect = 1 << 10,
|
||||
IceEffect = 2 << 10,
|
||||
WindEffect = 3 << 10,
|
||||
EarthEffect = 4 << 10,
|
||||
LightningEffect = 5 << 10,
|
||||
WaterEffect = 6 << 10,
|
||||
AstralEffect = 7 << 10, //Possibly for blind?
|
||||
UmbralEffect = 8 << 10, //Posibly for poison?
|
||||
|
||||
//Unknown status effect effects
|
||||
StatusEffect1 = 12 << 10,
|
||||
StatusEffect2 = 13 << 10,
|
||||
|
||||
HPAbsorbEffect = 14 << 10,
|
||||
MPAbsorbEffect = 15 << 10,
|
||||
TPAbsorbEffect = 16 << 10,
|
||||
TripleAbsorbEffect = 17 << 10, //Not sure about this
|
||||
MoogleEffect = 18 << 10,
|
||||
|
||||
//MagicEffectType Flags
|
||||
//THese are used for magic effects that deal or heal damage as well as damage over time effects
|
||||
//Crit is the same as HitEffectType
|
||||
FullResist = 0,
|
||||
WeakResist = 1 << 0, //Used for level 1, 2, and 3 resists probably
|
||||
NoResist = 1 << 1,
|
||||
|
||||
MagicShell = 1 << 4, //Used when casting on target with shell effects. MagicEffectType doesnt have a flag for protect or stoneskin
|
||||
MagicShield = 1 << 5, //When used with an command that has an animation, this plays a purple shield effect. DoTs also have this flag set (at least on ifrit) but they have no animations so it doesnt show
|
||||
|
||||
// Required for heal text to be blue, not sure if that's all it's used for
|
||||
Heal = 1 << 8,
|
||||
MP = 1 << 9, //Causes "MP" text to appear when used with MagicEffectType. | with Heal to make text blue
|
||||
TP = 1 << 10, //Causes "TP" text to appear when used with MagicEffectType. | with Heal to make text blue
|
||||
|
||||
//SelfHealType flags
|
||||
//This category causes numbers to appear on the user rather regardless of the target associated with the hit effect and do not play an animation
|
||||
//These determine the text that displays (HP has no text)
|
||||
SelfHealHP = 0,
|
||||
SelfHealMP = 1 << 0, //Shows MP text on self. | with SelfHeal to make blue
|
||||
SelfHealTP = 1 << 1, //Shows TP text on self. | with SelfHeal to make blue
|
||||
|
||||
//Causes self healing numbers to be blue
|
||||
SelfHeal = 1 << 10,
|
||||
}
|
||||
|
||||
//Mixing some of these flags will cause the client to crash.
|
||||
@ -134,9 +194,12 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
|
||||
Evade = 1,
|
||||
Parry = 2,
|
||||
Block = 3,
|
||||
Resist = 4,
|
||||
Hit = 5,
|
||||
Crit = 6
|
||||
SingleResist = 4,
|
||||
DoubleResist = 5,
|
||||
TripleResist = 6,
|
||||
FullResist = 7,
|
||||
Hit = 8,
|
||||
Crit = 9
|
||||
}
|
||||
|
||||
//Type of action
|
||||
@ -326,5 +389,16 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
|
||||
{
|
||||
return (ushort)hitType;
|
||||
}
|
||||
|
||||
public void SetTextId(ushort id)
|
||||
{
|
||||
worldMasterTextId = id;
|
||||
}
|
||||
|
||||
//Whether this action didn't miss, and wasn't evaded or resisted
|
||||
public bool ActionLanded()
|
||||
{
|
||||
return hitType > HitType.Evade && hitType != HitType.SingleResist && hitType != HitType.DoubleResist && hitType != HitType.FullResist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
|
||||
}
|
||||
|
||||
//Just to make scripting simpler
|
||||
//These have to be split into the normal actions and absorb actions because they use different flags
|
||||
//AddMP/HP/TPAction are for actions where the targetID is the person being targeted by command. Like Sanguine Rite would use AddMPAction
|
||||
public void AddMPAction(uint targetId, ushort worldMasterTextId, ushort amount)
|
||||
{
|
||||
uint effectId = (uint) (HitEffect.MagicEffectType | HitEffect.MP | HitEffect.Heal);
|
||||
@ -28,13 +30,38 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
|
||||
|
||||
public void AddHPAction(uint targetId, ushort worldMasterTextId, ushort amount)
|
||||
{
|
||||
uint effectId = (uint)(HitEffect.MagicEffectType | HitEffect.Heal);
|
||||
uint effectId = (uint) (HitEffect.MagicEffectType | HitEffect.Heal);
|
||||
AddAction(targetId, worldMasterTextId, effectId, amount);
|
||||
}
|
||||
|
||||
public void AddTPAction(uint targetId, ushort worldMasterTextId, ushort amount)
|
||||
{
|
||||
uint effectId = (uint)(HitEffect.MagicEffectType | HitEffect.TP);
|
||||
uint effectId = (uint) (HitEffect.MagicEffectType | HitEffect.TP | HitEffect.Heal);
|
||||
AddAction(targetId, worldMasterTextId, effectId, amount);
|
||||
}
|
||||
|
||||
//These are used for skills where the targetId is the person using a command. For example casting with parsimony would use AddMPAbsorbAction
|
||||
public void AddMPAbsorbAction(uint targetId, ushort worldMasterTextId, ushort amount)
|
||||
{
|
||||
uint effectId = (uint) (HitEffect.SelfHealType | HitEffect.SelfHealMP | HitEffect.SelfHeal);
|
||||
AddAction(targetId, worldMasterTextId, effectId, amount);
|
||||
}
|
||||
|
||||
public void AddHPAbsorbAction(uint targetId, ushort worldMasterTextId, ushort amount)
|
||||
{
|
||||
uint effectId = (uint) (HitEffect.SelfHealType | HitEffect.SelfHeal | HitEffect.SelfHeal);
|
||||
AddAction(targetId, worldMasterTextId, effectId, amount);
|
||||
}
|
||||
|
||||
public void AddTPAbsorbAction(uint targetId, ushort worldMasterTextId, ushort amount)
|
||||
{
|
||||
uint effectId = (uint) (HitEffect.SelfHealType | HitEffect.SelfHealTP | HitEffect.SelfHeal);
|
||||
AddAction(targetId, worldMasterTextId, effectId, amount);
|
||||
}
|
||||
|
||||
public void AddHitAction(uint targetId, ushort worldMasterTextId, ushort amount)
|
||||
{
|
||||
uint effectId = (uint) (HitEffect.HitEffectType | HitEffect.Hit);
|
||||
AddAction(targetId, worldMasterTextId, effectId, amount);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
using FFXIVClassic.Common;
|
||||
using FFXIVClassic_World_Server.Packets.Send.Subpackets.Groups;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FFXIVClassic_World_Server.DataObjects.Group
|
||||
@ -104,13 +105,14 @@ namespace FFXIVClassic_World_Server.DataObjects.Group
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (GetMemberCount() - currentIndex >= 64)
|
||||
int memberCount = Math.Min(GetMemberCount(), members.Count);
|
||||
if (memberCount - currentIndex >= 64)
|
||||
session.clientConnection.QueuePacket(GroupMembersX64Packet.buildPacket(session.sessionId, session.currentZoneId, time, members, ref currentIndex));
|
||||
else if (GetMemberCount() - currentIndex >= 32)
|
||||
else if (memberCount - currentIndex >= 32)
|
||||
session.clientConnection.QueuePacket(GroupMembersX32Packet.buildPacket(session.sessionId, session.currentZoneId, time, members, ref currentIndex));
|
||||
else if (GetMemberCount() - currentIndex >= 16)
|
||||
else if (memberCount - currentIndex >= 16)
|
||||
session.clientConnection.QueuePacket(GroupMembersX16Packet.buildPacket(session.sessionId, session.currentZoneId, time, members, ref currentIndex));
|
||||
else if (GetMemberCount() - currentIndex > 0)
|
||||
else if (memberCount - currentIndex > 0)
|
||||
session.clientConnection.QueuePacket(GroupMembersX08Packet.buildPacket(session.sessionId, session.currentZoneId, time, members, ref currentIndex));
|
||||
else
|
||||
break;
|
||||
|
@ -61,4 +61,47 @@ TargetFindAOEType =
|
||||
Circle = 1,
|
||||
Cone = 2,
|
||||
Box = 3
|
||||
}
|
||||
|
||||
StatusEffectFlags =
|
||||
{
|
||||
None = 0,
|
||||
|
||||
--Loss flags - Do we need loseonattacking/caststart? Could just be done with activate flags
|
||||
LoseOnDeath = bit32.lshift(1, 0), -- effects removed on death
|
||||
LoseOnZoning = bit32.lshift(1, 1), -- effects removed on zoning
|
||||
LoseOnEsuna = bit32.lshift(1, 2), -- effects which can be removed with esuna (debuffs)
|
||||
LoseOnDispel = bit32.lshift(1, 3), -- some buffs which player might be able to dispel from mob
|
||||
LoseOnLogout = bit32.lshift(1, 4), -- effects removed on logging out
|
||||
LoseOnAttacking = bit32.lshift(1, 5), -- effects removed when owner attacks another entity
|
||||
LoseOnCastStart = bit32.lshift(1, 6), -- effects removed when owner starts casting
|
||||
LoseOnAggro = bit32.lshift(1, 7), -- effects removed when owner gains enmity (swiftsong)
|
||||
LoseOnClassChange = bit32.lshift(1, 8), --Effect falls off whhen changing class
|
||||
|
||||
--Activate flags
|
||||
ActivateOnCastStart = bit32.lshift(1, 9), --Activates when a cast starts.
|
||||
ActivateOnCommandStart = bit32.lshift(1, 10), --Activates when a command is used, before iterating over targets. Used for things like power surge, excruciate.
|
||||
ActivateOnCommandFinish = bit32.lshift(1, 11), --Activates when the command is finished, after all targets have been iterated over. Used for things like Excruciate and Resonance falling off.
|
||||
ActivateOnPreactionTarget = bit32.lshift(1, 12), --Activates after initial rates are calculated for an action against owner
|
||||
ActivateOnPreactionCaster = bit32.lshift(1, 13), --Activates after initial rates are calculated for an action by owner
|
||||
ActivateOnDamageTaken = bit32.lshift(1, 14),
|
||||
ActivateOnHealed = bit32.lshift(1, 15),
|
||||
|
||||
--Should these be rolled into DamageTaken?
|
||||
ActivateOnMiss = bit32.lshift(1, 16), --Activates when owner misses
|
||||
ActivateOnEvade = bit32.lshift(1, 17), --Activates when owner evades
|
||||
ActivateOnParry = bit32.lshift(1, 18), --Activates when owner parries
|
||||
ActivateOnBlock = bit32.lshift(1, 19), --Activates when owner evades
|
||||
ActivateOnHit = bit32.lshift(1, 20), --Activates when owner hits
|
||||
ActivateOnCrit = bit32.lshift(1, 21), --Activates when owner crits
|
||||
|
||||
--Prevent flags. Sleep/stun/petrify/etc combine these
|
||||
PreventSpell = bit32.lshift(1, 22), -- effects which prevent using spells, such as silence
|
||||
PreventWeaponSkill = bit32.lshift(1, 23), -- effects which prevent using weaponskills, such as pacification
|
||||
PreventAbility = bit32.lshift(1, 24), -- effects which prevent using abilities, such as amnesia
|
||||
PreventAttack = bit32.lshift(1, 25), -- effects which prevent basic attacks
|
||||
PreventMovement = bit32.lshift(1, 26), -- effects which prevent movement such as bind, still allows turning in place
|
||||
PreventTurn = bit32.lshift(1, 27), -- effects which prevent turning, such as stun
|
||||
PreventUntarget = bit32.lshift(1, 28), -- effects which prevent changing targets, such as fixation
|
||||
Stance = bit32.lshift(1, 29) -- effects that do not have a timer
|
||||
}
|
5
data/scripts/commands/EsunaMagic.lua
Normal file
5
data/scripts/commands/EsunaMagic.lua
Normal file
@ -0,0 +1,5 @@
|
||||
function onEventStarted(player, command, triggerName, arg1, arg2, arg3, arg4, targetActor, arg5, arg6, arg7, arg8)
|
||||
player.Cast(command.actorId, targetActor);
|
||||
|
||||
player:endEvent();
|
||||
end
|
@ -92,6 +92,14 @@ function onEventStarted(player, actor, triggerName, isTeleport)
|
||||
if (choice == 1) then
|
||||
player:PlayAnimation(0x4000FFB);
|
||||
player:SendGameMessage(worldMaster, 34104, 0x20);
|
||||
|
||||
--bandaid fix for returning while dead, missing things like weakness and the heal number
|
||||
if (player:GetHP() == 0) then
|
||||
player:SetHP(player.GetMaxHP());
|
||||
player:ChangeState(0);
|
||||
player:PlayAnimation(0x01000066);
|
||||
end
|
||||
|
||||
if (isInn) then
|
||||
--Return to Inn
|
||||
if (player:GetHomePointInn() == 1) then
|
||||
@ -107,12 +115,6 @@ function onEventStarted(player, actor, triggerName, isTeleport)
|
||||
if (destination ~= nil) then
|
||||
randoPos = getRandomPointInBand(destination[2], destination[4], 3, 5);
|
||||
rotation = getAngleFacing(randoPos.x, randoPos.y, destination[2], destination[4]);
|
||||
--bandaid fix for returning while dead, missing things like weakness and the heal number
|
||||
if (player:GetHP() == 0) then
|
||||
player:SetHP(player.GetMaxHP());
|
||||
player:ChangeState(0);
|
||||
player:PlayAnimation(0x01000066);
|
||||
end
|
||||
GetWorldManager():DoZoneChange(player, destination[1], nil, 0, 2, randoPos.x, destination[3], randoPos.y, rotation);
|
||||
|
||||
end
|
||||
|
@ -12,8 +12,12 @@ end;
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--Only the bard gets the Battle Voice effect
|
||||
if caster == target then
|
||||
actionContainer.AddAction(caster.statusEffects.AddStatusForBattleAction(223253, 1, 0, 30));
|
||||
local effect = GetWorldManager():GetStatusEffect(223253);
|
||||
effect.SetDuration(30);
|
||||
caster.statusEffects.AddStatusEffect(effect, caster, actionContainer);
|
||||
end
|
||||
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
local effect = GetWorldManager():GetStatusEffect(223029);
|
||||
effect.SetDuration(60);
|
||||
caster.statusEffects.AddStatusEffect(effect, caster, actionContainer);
|
||||
end;
|
@ -14,6 +14,18 @@ function onAbilityStart(caster, target, ability)
|
||||
end;
|
||||
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
--223207: Berserk
|
||||
--223208: Rampage
|
||||
--Remove Rampage effect. I'm assuming no message is sent like LNC surges
|
||||
caster.statusEffects.RemoveStatusEffect(223208);
|
||||
|
||||
--If caster has berserk already, remove it and send a message.
|
||||
local buff = caster.statusEffects.GetStatusEffectById(223207)
|
||||
|
||||
if buff ~= nil then
|
||||
caster.statusEffects.RemoveStatusEffect(buff, actionContainer, 30329);
|
||||
else
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
end
|
||||
end;
|
@ -22,22 +22,18 @@ function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--If we have a buff then Blissful Mind removes that buff and restores MP. Otherwise, it adds the Blissful Mind effect
|
||||
if buff ~= nil then
|
||||
local amount = buff.GetExtra();
|
||||
local remAction = caster.statusEffects.RemoveStatusEffectForBattleAction(buff, 30329);
|
||||
|
||||
caster.AddMP(amount);
|
||||
|
||||
actionContainer.AddMPAction(caster.actorId, 33007, amount);
|
||||
actionContainer.AddAction(remAction);
|
||||
caster.statusEffects.RemoveStatusEffect(buff, actionContainer, 30329);
|
||||
else
|
||||
--Blissful mind takes 25% of CURRENT HP and begins storing MP up to that point, at which point the buff changes to indicate its full
|
||||
local amount = caster.GetHP() * 0.25;
|
||||
|
||||
caster.DelHP(amount);
|
||||
caster.DelHP(amount, actionContainer);
|
||||
skill.statusMagnitude = amount;
|
||||
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
|
||||
end
|
||||
|
||||
end;
|
@ -14,5 +14,7 @@ function onAbilityStart(caster, target, ability)
|
||||
end;
|
||||
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
target.hateContainer.UpdateHate(caster, -840);
|
||||
--Need a way to get all targets with hate for player
|
||||
--target.hateContainer.UpdateHate(caster, -840);
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
end;
|
@ -10,6 +10,12 @@ function onAbilityStart(caster, target, ability)
|
||||
end;
|
||||
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
local buff = caster.statusEffects.GetStatusEffectById(223227)
|
||||
|
||||
if buff ~= nil then
|
||||
caster.statusEffects.RemoveStatusEffect(buff, actionContainer, 30329);
|
||||
else
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
end
|
||||
end;
|
20
data/scripts/commands/ability/convert.lua
Normal file
20
data/scripts/commands/ability/convert.lua
Normal file
@ -0,0 +1,20 @@
|
||||
require("global");
|
||||
require("ability");
|
||||
|
||||
function onAbilityPrepare(caster, target, ability)
|
||||
return 0;
|
||||
end;
|
||||
|
||||
function onAbilityStart(caster, target, ability)
|
||||
return 0;
|
||||
end;
|
||||
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
newMP = math.min(caster.GetHP(), caster.GetMaxMP())
|
||||
newHP = math.min(caster.GetMP(), caster.GetMaxHP())
|
||||
caster.SetHP(newHP)
|
||||
caster.SetMP(newMP)
|
||||
|
||||
--Set effect id
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
end;
|
@ -17,7 +17,7 @@ function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
coverTier = 2;
|
||||
end
|
||||
|
||||
actionContainer.AddAction(caster.statusEffects.AddStatusForBattleAction(223063, coverTier, skill.statusDuration));
|
||||
caster.statusEffects.AddStatusEffect(223063, coverTier, 0, 15, 0);
|
||||
|
||||
--Apply Covered to target
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
|
@ -1,18 +1,23 @@
|
||||
require("global");
|
||||
require("ability");
|
||||
|
||||
|
||||
function onAbilityPrepare(caster, target, ability)
|
||||
return 0;
|
||||
end;
|
||||
|
||||
function onAbilityStart(caster, target, ability)
|
||||
return 0;
|
||||
end;
|
||||
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--Need a better way to do this
|
||||
|
||||
for i = 223212,223217 do
|
||||
local remAction = caster.statusEffects.RemoveStatusEffectForBattleAction(i, 30329)
|
||||
local buff = caster.statusEffects.GetStatusEffectById(i);
|
||||
|
||||
if remAction ~= nil then
|
||||
actionContainer.AddAction(remAction);
|
||||
if buff ~= nil then
|
||||
caster.statusEffects.RemoveStatusEffect(buff, actionContainer, 30329);
|
||||
skill.statusTier = 2;
|
||||
break;
|
||||
end
|
||||
|
19
data/scripts/commands/ability/fists_of_wind.lua
Normal file
19
data/scripts/commands/ability/fists_of_wind.lua
Normal file
@ -0,0 +1,19 @@
|
||||
require("global");
|
||||
require("ability");
|
||||
|
||||
function onAbilityPrepare(caster, target, ability)
|
||||
return 0;
|
||||
end;
|
||||
|
||||
function onAbilityStart(caster, target, ability)
|
||||
--27124: Enhanced Fists of Fire
|
||||
if caster.HasTrait(27124) then
|
||||
ability.statusTier = 2;
|
||||
end
|
||||
return 0;
|
||||
end;
|
||||
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
end;
|
@ -12,6 +12,6 @@ end;
|
||||
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--Take off 1/3 of attack delay. Not sure if this is the exact amount HF reduces by
|
||||
action.statusMagnitude = 0.33 * caster.GetMod(modifiersGlobal.AttackDelay);
|
||||
skill.statusMagnitude = 0.33 * caster.GetMod(modifiersGlobal.Delay);
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
end;
|
@ -20,9 +20,9 @@ function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--223213: Power Surge II
|
||||
--223212: Power Surge III
|
||||
--No message is sent when PS is removed by Life Surge
|
||||
caster.statusEffects.RemoveStatusEffect(223212, true);
|
||||
caster.statusEffects.RemoveStatusEffect(223213, true);
|
||||
caster.statusEffects.RemoveStatusEffect(223214, true);
|
||||
caster.statusEffects.RemoveStatusEffect(223212);
|
||||
caster.statusEffects.RemoveStatusEffect(223213);
|
||||
caster.statusEffects.RemoveStatusEffect(223214);
|
||||
|
||||
|
||||
--Using this ability moves to the next LS buff
|
||||
@ -45,7 +45,6 @@ function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
end
|
||||
|
||||
if not (removeId == 0) then
|
||||
--caster.statusEffects.RemoveStatusEffect(removeId, true);
|
||||
caster.statusEffects.ReplaceEffect(caster.statusEffects.GetStatusEffectById(removeId), skill.statusId, skill.statusTier, skill.statusMagnitude, skill.statusDuration);
|
||||
end
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
|
@ -14,11 +14,17 @@ function onAbilityStart(caster, target, ability)
|
||||
end;
|
||||
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--Need a better way to do this
|
||||
actionContainer.AddAction(caster.statusEffects.RemoveStatusEffectForBattleAction(223215));
|
||||
actionContainer.AddAction(caster.statusEffects.RemoveStatusEffectForBattleAction(223216));
|
||||
actionContainer.AddAction(caster.statusEffects.RemoveStatusEffectForBattleAction(223217));
|
||||
caster.statusEffects.RemoveStatusEffect(223215);
|
||||
caster.statusEffects.RemoveStatusEffect(223216);
|
||||
caster.statusEffects.RemoveStatusEffect(223217);
|
||||
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
--If caster has any of the power surge effects
|
||||
local buff = caster.statusEffects.GetStatusEffectById(223212) or caster.statusEffects.GetStatusEffectById(223213) or caster.statusEffects.GetStatusEffectById(223214);
|
||||
|
||||
if buff ~= nil then
|
||||
caster.statusEffects.RemoveStatusEffect(buff, actionContainer, 30329);
|
||||
else
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
end
|
||||
end;
|
@ -12,18 +12,21 @@ end;
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--QS gives 300 TP by default.
|
||||
skill.statusMagnitude = 300;
|
||||
--I'm assuming that with raging strikes, that increases to 500.
|
||||
--and traited that increases again to 750 (or 450 without RS)
|
||||
if caster.statusEffects.HasStatusEffect(223221) then
|
||||
actionContainer.AddAction(caster.statusEffects.RemoveStatusEffectForBattleAction(223221));
|
||||
skill.statusMagnitude = 500;
|
||||
end
|
||||
|
||||
--27241: Enhanced Quelling Strike: Increases TP gained from QS by 50%
|
||||
if caster.HasTrait(27241) then
|
||||
skill.statusMagnitude = skill.statusMagnitude * 1.5;
|
||||
end
|
||||
|
||||
--When raging strikes is active, Quelling Strikes removes it and immediately restores 100 TP for each tier ofr Raging Strikes.
|
||||
local buff = caster.statusEffects.GetStatusEffectById(223221)
|
||||
|
||||
if buff ~= nil then
|
||||
skill.tpCost = -100 * (buff.GetTier() - 1);
|
||||
--QS doesn't send a message
|
||||
caster.statusEffects.RemoveStatusEffect(buff);
|
||||
end
|
||||
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
end;
|
@ -6,14 +6,23 @@ function onAbilityPrepare(caster, target, ability)
|
||||
end;
|
||||
|
||||
function onAbilityStart(caster, target, ability)
|
||||
ability.statusMagnitude = 100;
|
||||
--27243: Enhanced Raging Strike: Increases effect of Raging Strike by 50%
|
||||
if caster.HasTrait(27241) then
|
||||
ability.statusTier = 2;
|
||||
ability.statusMagnitude = ability.statusMagnitude * 1.5;
|
||||
end
|
||||
return 0;
|
||||
end;
|
||||
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
--If caster has raging strike, remove it and send message, otherwise apply it.
|
||||
local buff = caster.statusEffects.GetStatusEffectById(223221)
|
||||
|
||||
if buff ~= nil then
|
||||
--30329: Your Raging Strike removes your Raging Strike effect.
|
||||
caster.statusEffects.RemoveStatusEffect(buff, actionContainer, 30329);
|
||||
else
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
end
|
||||
end;
|
@ -14,6 +14,18 @@ function onAbilityStart(caster, target, ability)
|
||||
end;
|
||||
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
--223207: Berserk
|
||||
--223208: Rampage
|
||||
--Remove Berserk effect. I'm assuming no message is sent like LNC surges
|
||||
caster.statusEffects.RemoveStatusEffect(223207);
|
||||
|
||||
--If caster has rampage already, remove it and send a message.
|
||||
local buff = caster.statusEffects.GetStatusEffectById(223208)
|
||||
|
||||
if buff ~= nil then
|
||||
caster.statusEffects.RemoveStatusEffect(buff, actionContainer, 30329);
|
||||
else
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
end
|
||||
end;
|
@ -1,11 +1,11 @@
|
||||
require("global");
|
||||
require("weaponskill");
|
||||
require("ability");
|
||||
|
||||
function onSkillPrepare(caster, target, skill)
|
||||
function onAbilityPrepare(caster, target, ability)
|
||||
return 0;
|
||||
end;
|
||||
|
||||
function onSkillStart(caster, target, skill)
|
||||
function onAbilityStart(caster, target, ability)
|
||||
return 0;
|
||||
end;
|
||||
|
@ -12,9 +12,9 @@ end;
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--Is this before or after status is gained?
|
||||
--Will probably need to switch to a flag for this because it might include more than just these 3 effects.
|
||||
actionContainer.AddAction(caster.statusEffects.RemoveStatusEffectForBattleAction(228011));
|
||||
actionContainer.AddAction(caster.statusEffects.RemoveStatusEffectForBattleAction(228013));
|
||||
actionContainer.AddAction(caster.statusEffects.RemoveStatusEffectForBattleAction(228021));
|
||||
caster.statusEffects.RemoveStatusEffect(228011, actionContainer, 30329);
|
||||
caster.statusEffects.RemoveStatusEffect(228013, actionContainer, 30329);
|
||||
caster.statusEffects.RemoveStatusEffect(228021, actionContainer, 30329);
|
||||
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
end;
|
17
data/scripts/commands/autoattack/default.lua
Normal file
17
data/scripts/commands/autoattack/default.lua
Normal file
@ -0,0 +1,17 @@
|
||||
require("global");
|
||||
|
||||
function onSkillPrepare(caster, target, skill)
|
||||
return 0;
|
||||
end;
|
||||
|
||||
function onSkillStart(caster, target, skill)
|
||||
return 0;
|
||||
end;
|
||||
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--calculate ws damage
|
||||
action.amount = skill.basePotency;
|
||||
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
end;
|
@ -2,7 +2,7 @@ require("global");
|
||||
|
||||
properties = {
|
||||
permissions = 0,
|
||||
parameters = "sssss",
|
||||
parameters = "sss",
|
||||
description =
|
||||
[[
|
||||
Adds experience <qty> to player or <targetname>.
|
||||
@ -11,18 +11,19 @@ Adds experience <qty> to player or <targetname>.
|
||||
]],
|
||||
}
|
||||
|
||||
function onTrigger(player, argc, commandId, animationId, textId, effectId, amount)
|
||||
function onTrigger(player, argc, animType, modelAnim, effectId)
|
||||
local sender = "[battleaction] ";
|
||||
|
||||
if player then
|
||||
cid = tonumber(commandId) or 0;
|
||||
aid = tonumber(animationId) or 0;
|
||||
tid = tonumber(textId) or 0;
|
||||
print(effectId)
|
||||
eid = tonumber(effectId) or 0;
|
||||
amt = tonumber(amount) or 0;
|
||||
|
||||
player:DoBattleActionAnimation(cid, aid, tid, eid, amt);
|
||||
local actor = GetWorldManager():GetActorInWorld(player.currentTarget) or nil;
|
||||
if player and actor then
|
||||
aid = tonumber(animType) or 0
|
||||
mid = tonumber(modelAnim) or 0
|
||||
eid = tonumber(effectId) or 0
|
||||
local id = bit32.lshift(aid, 24);
|
||||
id = bit32.bor(id, bit32.lshift(mid, 12));
|
||||
id = bit32.bor(id, eid)
|
||||
print((tonumber(id)))
|
||||
player:DoBattleAction(30301, id);
|
||||
else
|
||||
print(sender.."unable to add experience, ensure player name is valid.");
|
||||
end;
|
||||
|
42
data/scripts/commands/gm/equipactions.lua
Normal file
42
data/scripts/commands/gm/equipactions.lua
Normal file
@ -0,0 +1,42 @@
|
||||
require("global");
|
||||
require("modifiers");
|
||||
properties = {
|
||||
permissions = 0,
|
||||
parameters = "s",
|
||||
description =
|
||||
[[
|
||||
equips all your class and job actions
|
||||
]],
|
||||
}
|
||||
|
||||
classToActions = {
|
||||
[2] = { Start = 27100, End = 27119},
|
||||
[3] = { Start = 27140, End = 27159},
|
||||
[4] = { Start = 27180, End = 27199},
|
||||
[7] = { Start = 27220, End = 27239},
|
||||
[8] = { Start = 27260, End = 27279},
|
||||
[22] = { Start = 27300, End = 27319},
|
||||
[23] = { Start = 27340, End = 27359}
|
||||
}
|
||||
|
||||
function onTrigger(player, argc)
|
||||
local messageId = MESSAGE_TYPE_SYSTEM_ERROR;
|
||||
local sender = "equipactions";
|
||||
|
||||
classId = player.GetClass()
|
||||
|
||||
if classToActions[classId] then
|
||||
s = classToActions[classId].Start
|
||||
e = classToActions[classId].End
|
||||
print('h')
|
||||
for i = 0, 30 do
|
||||
player.UnequipAbility(i, false)
|
||||
end
|
||||
|
||||
for commandid = s, e do
|
||||
if GetWorldManager():GetBattleCommand(commandid) then
|
||||
player:EquipAbilityInFirstOpenSlot(player:GetCurrentClassOrJob(), commandid);
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -20,16 +20,22 @@ function onTrigger(player, argc, slot, wId, eId, vId, cId)
|
||||
vId = tonumber(vId) or 0;
|
||||
cId = tonumber(cId) or 0;
|
||||
|
||||
if player and argc > 0 then
|
||||
if argc > 2 then
|
||||
player:GraphicChange(slot, wId, eId, vId, cId);
|
||||
player:SendMessage(messageID, sender, string.format("Changing appearance on slot %u", slot));
|
||||
local actor = GetWorldManager():GetActorInWorld(player.currentTarget) or nil;
|
||||
if player and actor then
|
||||
if player and argc > 0 then
|
||||
|
||||
-- player.appearanceIds[5] = player.achievementPoints;
|
||||
if argc > 2 then
|
||||
actor:GraphicChange(slot, wId, eId, vId, cId);
|
||||
--player.achievementPoints = player.achievementPoints + 1;
|
||||
actor:SendMessage(messageID, sender, string.format("Changing appearance on slot %u", slot));
|
||||
actor:SendMessage(messageID, sender, string.format("points %u", player.appearanceIds[5]));
|
||||
else
|
||||
actor.appearanceIds[slot] = wId;
|
||||
end
|
||||
actor:SendAppearance();
|
||||
else
|
||||
player:GraphicChange(slot, wId);
|
||||
end
|
||||
player:SendAppearance();
|
||||
else
|
||||
player:SendMessage(messageID, sender, "No parameters sent! Usage: "..properties.description);
|
||||
player:SendMessage(messageID, sender, "No parameters sent! Usage: "..properties.description);
|
||||
end;
|
||||
end;
|
||||
|
||||
end;
|
135
data/scripts/commands/gm/spawnnpc.lua
Normal file
135
data/scripts/commands/gm/spawnnpc.lua
Normal file
@ -0,0 +1,135 @@
|
||||
require("global");
|
||||
require("modifiers");
|
||||
properties = {
|
||||
permissions = 0,
|
||||
parameters = "sss",
|
||||
description =
|
||||
[[
|
||||
yolo
|
||||
]],
|
||||
}
|
||||
|
||||
local modelIds =
|
||||
{
|
||||
["titan"] = 2107401,
|
||||
["ifrit"] = 2107302,
|
||||
["ifrithotair"] = 2207310,
|
||||
["nail"] = 2207307,
|
||||
["garuda"] = 2209501,
|
||||
["garudahelper"] = 2209516,
|
||||
["plume"] = 2209502,
|
||||
["monolith"] = 2209506,
|
||||
["mog"] = 2210408,
|
||||
["nael"] = 2210902,
|
||||
["meteor"] = 2210903,
|
||||
["cactuar"] = 2200905,
|
||||
["morbol"] = 2201002,
|
||||
["drake"] = 2202209,
|
||||
["ogre"] = 2202502,
|
||||
["treant"] = 2202801,
|
||||
["couerl"] = 2203203,
|
||||
["wyvern"] = 2203801,
|
||||
["clouddragon"] = 2208101,
|
||||
["golem"] = 2208901,
|
||||
["atomos"] = 2111002,
|
||||
["chimera"] = 2308701,
|
||||
["salamander"] = 2201302,
|
||||
["ahriman"] = 2201704,
|
||||
["rat"] = 9111275,
|
||||
["bat"] = 2104113,
|
||||
["chigoe"] = 2105613,
|
||||
["hedgemole"] = 2105709,
|
||||
["gnat"] = 2200604,
|
||||
["bird"] = 2201208,
|
||||
["puk"] = 2200112,
|
||||
["angler"] = 2204507,
|
||||
["snurble"] = 2204403,
|
||||
["lemur"] = 2200505,
|
||||
["doe"] = 2200303,
|
||||
["hippogryph"] = 2200405,
|
||||
["trap"] = 2202710,
|
||||
["goat"] = 2102312,
|
||||
["dodo"] = 9111263,
|
||||
["imp"] = 2202607,
|
||||
["spriggan"] = 2290036,
|
||||
["cyclops"] = 2210701,
|
||||
["raptor"] = 2200205,
|
||||
["wolf"] = 2201429,
|
||||
["fungus"] = 2205907,
|
||||
["basilisk"] = 2200708,
|
||||
["bomb"] = 2201611,
|
||||
["jellyfish"] = 2105415,
|
||||
["slug"] = 2104205,
|
||||
["coblyn"] = 2202103,
|
||||
["ghost"] = 2204317,
|
||||
["crab"] = 2107613,
|
||||
["yarzon"] = 2205520,
|
||||
["elemental"] = 2105104,
|
||||
["boar"] = 2201505,
|
||||
["kobold"] = 2206629,
|
||||
["sylph"] = 2206702,
|
||||
["ixal"] = 2206434,
|
||||
["amaljaa"] = 2206502,
|
||||
["qiqirn"] = 2206304,
|
||||
["apkallu"] = 2202902,
|
||||
["goobbue"] = 2103301,
|
||||
["garlean"] = 2207005,
|
||||
["flan"] = 2103404,
|
||||
["swarm"] = 2105304,
|
||||
["goblin"] = 2210301,
|
||||
["buffalo"] = 2200802,
|
||||
["skeleton"] = 2201902,
|
||||
["zombie"] = 2201807,
|
||||
["toad"] = 2203107,
|
||||
["wisp"] = 2209903,
|
||||
["juggernaut"] = 6000243,
|
||||
["mammet"] = 6000246,
|
||||
["lantern"] = 1200329,
|
||||
["helper"] = 2310605,
|
||||
["diremite"] = 2101108,
|
||||
["gong"] = 1200050,
|
||||
}
|
||||
|
||||
function onTrigger(player, argc, name, width, height, blockCount)
|
||||
local messageID = MESSAGE_TYPE_SYSTEM_ERROR;
|
||||
local sender = "spawnnpc";
|
||||
|
||||
if player and (modelIds[name] != nil) then
|
||||
local pos = player:GetPos();
|
||||
local x = tonumber(pos[0]);
|
||||
local y = tonumber(pos[1]);
|
||||
local z = tonumber(pos[2]);
|
||||
local rot = tonumber(pos[3]);
|
||||
local zone = pos[4];
|
||||
local w = tonumber(width) or 0;
|
||||
|
||||
local h = tonumber(height) or 0;
|
||||
local blocks = tonumber(blockCount) or 0;
|
||||
for b = 0, blocks do
|
||||
for i = 0, w do
|
||||
for j = 0, h do
|
||||
local actor = player.GetZone().SpawnActor(2104001, 'ass', x + (i * 1), y, z + (j * 1), rot, 0, 0, true);
|
||||
actor.ChangeNpcAppearance(modelIds[name]);
|
||||
actor.SetMaxHP(5000);
|
||||
actor.SetHP(5000);
|
||||
actor.SetMod(modifiersGlobal.CanBlock, 1);
|
||||
actor.SetMod(modifiersGlobal.AttackRange, 3);
|
||||
actor.SetMod(modifiersGlobal.MovementSpeed, 5);
|
||||
actor.SetMobMod(mobModifiersGlobal.Roams, 1);
|
||||
actor.SetMobMod(mobModifiersGlobal.RoamDelay, 10);
|
||||
actor.charaWork.parameterSave.state_mainSkillLevel = 52;
|
||||
actor.moveState = 3;
|
||||
end;
|
||||
end;
|
||||
|
||||
x = x + 500
|
||||
end;
|
||||
return;
|
||||
elseif player and (modelIds[name] == nil) then
|
||||
player:SendMessage(messageID, sender, "That name isn't valid");
|
||||
else
|
||||
print("I don't even know how you managed this")
|
||||
end
|
||||
|
||||
return;
|
||||
end;
|
24
data/scripts/commands/gm/vdragon.lua
Normal file
24
data/scripts/commands/gm/vdragon.lua
Normal file
@ -0,0 +1,24 @@
|
||||
require("global");
|
||||
require("utils");
|
||||
|
||||
properties = {
|
||||
permissions = 0,
|
||||
parameters = "sssss",
|
||||
description =
|
||||
[[
|
||||
Angle stuff!
|
||||
!anglestuff
|
||||
]],
|
||||
}
|
||||
|
||||
function onTrigger(player, argc)
|
||||
local sender = "[battleaction] ";
|
||||
|
||||
if player and player.currentTarget then
|
||||
local actor = GetWorldManager():GetActorInWorld(player.currentTarget) or nil;
|
||||
actor.Ability(23459, actor.actorId);
|
||||
|
||||
else
|
||||
print(sender.."unable to add experience, ensure player name is valid.");
|
||||
end;
|
||||
end;
|
@ -163,11 +163,12 @@ function onTrigger(player, argc, width, height, blockCount)
|
||||
actor.ChangeNpcAppearance(2200905);
|
||||
actor.SetMaxHP(5000);
|
||||
actor.SetHP(5000);
|
||||
actor.SetMod(modifiersGlobal.HasShield, 1);
|
||||
actor.SetMod(modifiersGlobal.CanBlock, 1);
|
||||
actor.SetMod(modifiersGlobal.AttackRange, 3);
|
||||
actor.SetMod(modifiersGlobal.Speed, 5);
|
||||
actor.SetMod(modifiersGlobal.MovementSpeed, 5);
|
||||
actor.SetMobMod(mobModifiersGlobal.Roams, 1);
|
||||
actor.SetMobMod(mobModifiersGlobal.RoamDelay, 3);
|
||||
actor.SetMobMod(mobModifiersGlobal.RoamDelay, 10);
|
||||
actor.charaWork.parameterSave.state_mainSkillLevel = 52;
|
||||
actor.moveState = 3;
|
||||
end
|
||||
end
|
||||
|
@ -12,7 +12,7 @@ end;
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--calculate damage
|
||||
action.amount = skill.basePotency;
|
||||
action.statusMagnitude = 15;
|
||||
skill.statusMagnitude = 15;
|
||||
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
|
@ -12,22 +12,16 @@ end;
|
||||
--Increased damage and conversion to single target
|
||||
function onCombo(caster, target, spell)
|
||||
spell.aoeType = 0;
|
||||
spell.potency = spell.potency * 1.5;
|
||||
spell.basePotency = spell.basePotency * 1.5;
|
||||
end;
|
||||
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--Dispels an effect on each target.
|
||||
local effects = target.statusEffects.GetStatusEffectsByFlag2(16); --lose on dispel
|
||||
if effects != nil then
|
||||
target.statusEffects.RemoveStatusEffect(effects[0]);
|
||||
end;
|
||||
|
||||
--calculate damage
|
||||
action.amount = skill.basePotency;
|
||||
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
|
||||
--Try to apply status effect
|
||||
action.TryStatus(caster, target, skill, actionContainer, true);
|
||||
--Dispels an effect on each target.
|
||||
target.statusEffects.RemoveStatusEffect(GetRandomEffectByFlag(8), actionContainer, 30336);
|
||||
end;
|
@ -1,5 +1,6 @@
|
||||
require("global");
|
||||
require("magic");
|
||||
require("modifiers");
|
||||
|
||||
function onMagicPrepare(caster, target, spell)
|
||||
return 0;
|
||||
@ -14,7 +15,7 @@ function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--2.5 HP per Healing Magic Potency
|
||||
--0.5 HP per MND
|
||||
--this is WITH WHM AF chest, don't know formula without AF. AF seems to increase healing by 7-10%?
|
||||
action.amount = 2.5 * caster.GetMod(modifiersGlobal.MagicHeal) + 0.5 * (caster.GetMod(modifiersGlobal.Mind));
|
||||
action.amount = 2.5 * caster.GetMod(modifiersGlobal.HealingMagicPotency) + 0.5 * (caster.GetMod(modifiersGlobal.Mind));
|
||||
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
|
@ -30,7 +30,7 @@ function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
hpPerMND = 0.25;
|
||||
end
|
||||
|
||||
action.amount = hpPerHMP * caster.GetMod(modifiersGlobal.MagicHeal) + hpPerMND * (caster.GetMod(modifiersGlobal.Mind));
|
||||
action.amount = hpPerHMP * caster.GetMod(modifiersGlobal.HealingMagicPotency) + hpPerMND * (caster.GetMod(modifiersGlobal.Mind));
|
||||
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
|
22
data/scripts/commands/magic/esuna.lua
Normal file
22
data/scripts/commands/magic/esuna.lua
Normal file
@ -0,0 +1,22 @@
|
||||
require("global");
|
||||
require("magic");
|
||||
require("battleutils");
|
||||
|
||||
function onMagicPrepare(caster, target, spell)
|
||||
if not target.statusEffects.HasStatusEffectsByFlag(StatusEffectFlags.LoseOnEsuna) then
|
||||
return -1
|
||||
end
|
||||
|
||||
return 0;
|
||||
end;
|
||||
|
||||
function onMagicStart(caster, target, spell)
|
||||
return 0;
|
||||
end;
|
||||
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
|
||||
removeEffect = target.statusEffects.GetRandomEffectByFlag(StatusEffectFlags.LoseOnEsuna)
|
||||
|
||||
target.statusEffects.RemoveStatusEffect(removeEffect, actionContainer, 30331);
|
||||
end;
|
@ -11,7 +11,7 @@ end;
|
||||
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--calculate damage
|
||||
action.amount = skill.basePotency;
|
||||
action.amount = 5000;-- skill.basePotency;
|
||||
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
|
@ -12,6 +12,7 @@ end;
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--calculate damage
|
||||
action.amount = skill.basePotency;
|
||||
skill.statusMagnitude = 20;
|
||||
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
|
@ -24,6 +24,6 @@ function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
if caster != target then
|
||||
caster.AddHP(action.amount / 2)
|
||||
--33012: You recover [amount] HP.
|
||||
actionContainer.AddHPAction(caster.actorId, 33012, (action.amount / 2));
|
||||
actionContainer.AddHPAbsorbAction(caster.actorId, 33012, (action.amount / 2));
|
||||
end
|
||||
end;
|
@ -12,7 +12,7 @@ end;
|
||||
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--Actual amount of def/mdef will be calculated in OnGain
|
||||
skill.statusMagnitude = caster.GetMod(modifiersGlobal.MagicEnhancePotency);
|
||||
skill.statusMagnitude = caster.GetMod(modifiersGlobal.EnhancementMagicPotency);
|
||||
|
||||
--27365: Enhanced Protect: Increases magic defense gained from Protect.
|
||||
if caster.HasTrait(27365) then
|
||||
|
@ -15,4 +15,5 @@ end;
|
||||
|
||||
--Not sure how raise works yet.
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
action.DoAction(caster, target, skill, actionContainer)
|
||||
end;
|
@ -25,7 +25,7 @@ function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
intercept = intercept * 1.25;
|
||||
end
|
||||
|
||||
local regenTick = (slope * caster.GetMod(modifiersGlobal.MagicEnhancePotency)) + intercept) + 1;
|
||||
local regenTick = (slope * caster.GetMod(modifiersGlobal.EnhancementMagicPotency)) + intercept + 1;
|
||||
|
||||
spell.statusMagnitude = regenTick;
|
||||
|
||||
|
@ -12,7 +12,7 @@ end;
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--calculate damage
|
||||
action.amount = skill.basePotency;
|
||||
action.statusMagnitude = 50;
|
||||
skill.statusMagnitude = 50;
|
||||
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
|
@ -1,5 +1,6 @@
|
||||
require("global");
|
||||
require("magic");
|
||||
require("modifiers")
|
||||
|
||||
function onMagicPrepare(caster, target, spell)
|
||||
return 0;
|
||||
@ -19,7 +20,7 @@ function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
hpPerPoint = 1.96;
|
||||
end
|
||||
|
||||
spell.statusMagnitude = hpPerPoint * caster.GetMod(modifiersGlobal.MagicEnhancePotency);
|
||||
skill.statusMagnitude = hpPerPoint * caster.GetMod(modifiersGlobal.EnhancementMagicPotency);
|
||||
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
|
35
data/scripts/commands/magic/swiftsong.lua
Normal file
35
data/scripts/commands/magic/swiftsong.lua
Normal file
@ -0,0 +1,35 @@
|
||||
require("global");
|
||||
require("magic");
|
||||
|
||||
function onMagicPrepare(caster, target, skill)
|
||||
return 0;
|
||||
end;
|
||||
|
||||
function onMagicStart(caster, target, skill)
|
||||
return 0;
|
||||
end;
|
||||
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--223224: Swiftsong
|
||||
--223254: Ballad Of Magi
|
||||
--223256: Minuet of Rigor
|
||||
--If target has one of these effects that was from this caster, remove it
|
||||
local oldSong;
|
||||
local paeon = target.statusEffects.GetStatusEffectById(223255);
|
||||
local ballad = target.statusEffects.GetStatusEffectById(223254);
|
||||
local minuet = target.statusEffects.GetStatusEffectById(223256);
|
||||
if paeon and paeon.GetSource() == caster then
|
||||
oldSong = paeon;
|
||||
elseif ballad and ballad.GetSource() == caster then
|
||||
oldSong = ballad;
|
||||
elseif minuet and minuet.GetSource() == caster then
|
||||
oldSong = minuet;
|
||||
end
|
||||
|
||||
if oldSong then
|
||||
target.statusEffects.RemoveStatusEffect(oldSong);
|
||||
end
|
||||
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
end;
|
17
data/scripts/commands/weaponskill/breath_of_the_dragon.lua
Normal file
17
data/scripts/commands/weaponskill/breath_of_the_dragon.lua
Normal file
@ -0,0 +1,17 @@
|
||||
require("global");
|
||||
require("ability");
|
||||
|
||||
function onAbilityPrepare(caster, target, ability)
|
||||
return 0;
|
||||
end;
|
||||
|
||||
function onAbilityStart(caster, target, ability)
|
||||
return 0;
|
||||
end;
|
||||
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
action.amount = skill.basePotency;
|
||||
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
end;
|
17
data/scripts/commands/weaponskill/breath_of_the_ram.lua
Normal file
17
data/scripts/commands/weaponskill/breath_of_the_ram.lua
Normal file
@ -0,0 +1,17 @@
|
||||
require("global");
|
||||
require("ability");
|
||||
|
||||
function onAbilityPrepare(caster, target, ability)
|
||||
return 0;
|
||||
end;
|
||||
|
||||
function onAbilityStart(caster, target, ability)
|
||||
return 0;
|
||||
end;
|
||||
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
action.amount = skill.basePotency;
|
||||
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
end;
|
@ -12,10 +12,7 @@ end;
|
||||
--Dispel
|
||||
--Does dispel have a text id?
|
||||
function onCombo(caster, target, skill)
|
||||
local effects = target.statusEffects.GetStatusEffectsByFlag(16); --lose on dispel
|
||||
if effects != nil then
|
||||
target.statusEffects.RemoveStatusEffect(effects[0]);
|
||||
end;
|
||||
return 0;
|
||||
end;
|
||||
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
@ -25,6 +22,7 @@ function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
|
||||
--Try to apply status effect
|
||||
action.TryStatus(caster, target, skill, actionContainer, true);
|
||||
if skill.isCombo then
|
||||
target.statusEffects.RemoveStatusEffect(GetRandomEffectByFlag(8), actionContainer, 30336);
|
||||
end
|
||||
end;
|
@ -1,5 +1,6 @@
|
||||
require("global");
|
||||
require("weaponskill");
|
||||
require("utils");
|
||||
|
||||
function onSkillPrepare(caster, target, skill)
|
||||
return 0;
|
||||
@ -16,7 +17,7 @@ end;
|
||||
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--calculate ws damage
|
||||
action.amount = skill.basePotency;
|
||||
action.amount = 5000;--skill.basePotency;
|
||||
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
|
@ -21,6 +21,9 @@ function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
|
||||
--Try to apply status effect
|
||||
action.TryStatus(caster, target, skill, actionContainer, true);
|
||||
--Status only seems to apply on the first hit
|
||||
if(action.ActionLanded() and action.hitNum == 1) then
|
||||
--Try to apply status effect
|
||||
action.TryStatus(caster, target, skill, actionContainer, true);
|
||||
end
|
||||
end;
|
@ -1,20 +1,16 @@
|
||||
require("global");
|
||||
require("weaponskill");
|
||||
require("modifiers")
|
||||
|
||||
function onSkillPrepare(caster, target, skill)
|
||||
return 0;
|
||||
end;
|
||||
|
||||
function onSkillStart(caster, target, skill)
|
||||
return 0;
|
||||
end;
|
||||
|
||||
--Increased crit rate
|
||||
function onCombo(caster, target, skill)
|
||||
--Get Berserk statuseffect
|
||||
local berserk = caster.statusEffects.GetStatusEffectById(223160);
|
||||
|
||||
--if it isn't nil, remove the AP and Defense mods and reset extra to 0, increase potency
|
||||
--if it isn't nil, remove the AP and Defense mods and reset extra to 0, increase accuracy
|
||||
if berserk != nil then
|
||||
local apPerHit = 20;
|
||||
local defPerHit = 20;
|
||||
@ -23,14 +19,20 @@ function onCombo(caster, target, skill)
|
||||
apPerHit = 24;
|
||||
end
|
||||
|
||||
attacker.SubtractMod(modifiersGlobal.Attack, apPerHit * berserk.GetExtra());
|
||||
attacker.Add(modifiersGlobal.Defense, defPerHit * berserk.GetExtra());
|
||||
caster.SubtractMod(modifiersGlobal.Attack, apPerHit * berserk.GetExtra());
|
||||
caster.Add(modifiersGlobal.Defense, defPerHit * berserk.GetExtra());
|
||||
|
||||
berserk.SetExtra(0);
|
||||
|
||||
--This is about 50% crit. Don't know if that's what it gave on retail but seems kind of reasonable
|
||||
skill.critRateBonus = 300;
|
||||
skill.accuracyModifier = 50;
|
||||
end;
|
||||
|
||||
return 0;
|
||||
end;
|
||||
|
||||
--Increased crit rate
|
||||
function onCombo(caster, target, skill)
|
||||
--This is about 25% crit. Don't know if that's what it gave on retail but seems kind of reasonable
|
||||
skill.critRateBonus = 200;
|
||||
end;
|
||||
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
|
@ -15,7 +15,7 @@ end;
|
||||
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--calculate ws damage
|
||||
skill.Potency = 100;
|
||||
skill.basePotency = 100;
|
||||
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
|
@ -1,11 +1,27 @@
|
||||
require("global");
|
||||
require("weaponskill");
|
||||
require("modifiers")
|
||||
|
||||
function onSkillPrepare(caster, target, skill)
|
||||
return 0;
|
||||
end;
|
||||
|
||||
--Resets rampage to increase damage
|
||||
function onSkillStart(caster, target, skill)
|
||||
--Get Rampage statuseffect
|
||||
local rampage = caster.statusEffects.GetStatusEffectById(223208);
|
||||
|
||||
--if it isn't nil, remove the AP and Defense mods and reset extra to 0, increase potency
|
||||
if rampage != nil then
|
||||
local parryPerDT = 20;
|
||||
local delayMsPerDT = 100;
|
||||
|
||||
caster.SubtractMod(modifiersGlobal.Parry, parryPerDT * rampage.GetExtra());
|
||||
caster.AddMod(modifiersGlobal.Delay, delayMsPerDT * rampage.GetExtra());
|
||||
|
||||
rampage.SetExtra(0);
|
||||
skill.basePotency = skill.basePotency * 1.5;
|
||||
end;
|
||||
return 0;
|
||||
end;
|
||||
|
||||
@ -15,6 +31,10 @@ function onCombo(caster, target, skill)
|
||||
end;
|
||||
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
if target.target == caster then
|
||||
skill.statusId = 223015
|
||||
end;
|
||||
|
||||
--calculate ws damage
|
||||
action.amount = skill.basePotency;
|
||||
|
||||
@ -23,4 +43,6 @@ function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
|
||||
--Try to apply status effect
|
||||
action.TryStatus(caster, target, skill, actionContainer, true);
|
||||
|
||||
skill.statusId = 0;
|
||||
end;
|
@ -28,7 +28,7 @@ function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
--1.21: Equation used to calculate amount of MP adjusted.
|
||||
--fug
|
||||
--This might mean max MP isn't involved and the difference was between patches. need to recheck videos
|
||||
if action.GetHitType() > HitType.Evade and (action.param == HitDirection.Right or action.param == HitDirection.Left) then
|
||||
if action.ActionLanded() and (action.param == HitDirection.Right or action.param == HitDirection.Left) then
|
||||
local mpToReturn = 0;
|
||||
|
||||
if skill.isCombo then
|
||||
@ -39,6 +39,6 @@ function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
|
||||
caster.AddMP(mpToReturn);
|
||||
--30452: You recover x MP.
|
||||
actionContainer.AddMPAction(caster.actorId, 30452, mpToReturn);
|
||||
actionContainer.AddMPAbsorbAction(caster.actorId, 30452, mpToReturn);
|
||||
end
|
||||
end;
|
17
data/scripts/commands/weaponskill/voice_of_the_dragon.lua
Normal file
17
data/scripts/commands/weaponskill/voice_of_the_dragon.lua
Normal file
@ -0,0 +1,17 @@
|
||||
require("global");
|
||||
require("ability");
|
||||
|
||||
function onAbilityPrepare(caster, target, ability)
|
||||
return 0;
|
||||
end;
|
||||
|
||||
function onAbilityStart(caster, target, ability)
|
||||
return 0;
|
||||
end;
|
||||
|
||||
function onSkillFinish(caster, target, skill, action, actionContainer)
|
||||
action.amount = skill.basePotency;
|
||||
|
||||
--DoAction handles rates, buffs, dealing damage
|
||||
action.DoAction(caster, target, skill, actionContainer);
|
||||
end;
|
@ -1,5 +1,6 @@
|
||||
require("global");
|
||||
require("weaponskill");
|
||||
require("modifiers")
|
||||
|
||||
function onSkillPrepare(caster, target, skill)
|
||||
return 0;
|
||||
@ -23,8 +24,8 @@ function onCombo(caster, target, skill)
|
||||
apPerHit = 24;
|
||||
end
|
||||
|
||||
attacker.SubtractMod(modifiersGlobal.Attack, apPerHit * berserk.GetExtra());
|
||||
attacker.Add(modifiersGlobal.Defense, defPerHit * berserk.GetExtra());
|
||||
caster.SubtractMod(modifiersGlobal.Attack, apPerHit * berserk.GetExtra());
|
||||
caster.Add(modifiersGlobal.Defense, defPerHit * berserk.GetExtra());
|
||||
|
||||
berserk.SetExtra(0);
|
||||
skill.basePotency = skill.basePotency * 1.5;
|
||||
|
@ -3,17 +3,17 @@ require("utils")
|
||||
|
||||
--Forces a full block (0 damage taken)
|
||||
function onPreAction(effect, caster, target, skill, action, actionContainer)
|
||||
--If action hit from the rear and is a weaponskill ation
|
||||
--Can aegis boon block rear attacks or non-physical attacks?
|
||||
action.blockRate = 100.0;
|
||||
end;
|
||||
|
||||
--Heals for the amount of HP blocked, up to a certain point. I don't know what determines the cap but it seems to be 703 at level 50. Unsure if it scales down based on level, dlvl, or if that's an arbitrary cap added.
|
||||
function onBlock(effect, attacker, defender, action, actionContainer)
|
||||
function onBlock(effect, attacker, defender, skill, action, actionContainer)
|
||||
--Amount blocked
|
||||
local absorbAmount = math.Clamp(action.amountMitigated, 0, 703);
|
||||
|
||||
--33008: You recover x HP from Aegis Boon
|
||||
defender.AddHP(absorbAmount);
|
||||
actionContainer.AddHPAction(defender.actorId, 33008, absorbAmount);
|
||||
actionContainer.AddAction(defender.statusEffects.RemoveStatusEffectForBattleAction(effect));
|
||||
defender.statusEffects.RemoveStatusEffect(effect, actionContainer);
|
||||
end;
|
@ -1,10 +1,10 @@
|
||||
require("modifiers")
|
||||
|
||||
--Doesn't do flat damage. 20 on Lv 50 Truffle Hog, 11 on Coincounter, 7 on nael hard, 19 on 52 fachan
|
||||
function onGain(owner, effect)
|
||||
function onGain(owner, effect, actionContainer)
|
||||
owner.AddMod(modifiersGlobal.RegenDown, effect.GetMagnitude());
|
||||
end;
|
||||
|
||||
function onLose(owner, effect)
|
||||
function onLose(owner, effect, actionContainer)
|
||||
owner.AddMod(modifiersGlobal.RegenDown, effect.GetMagnitude());
|
||||
end;
|
@ -1,10 +1,10 @@
|
||||
require("modifiers")
|
||||
|
||||
function onGain(owner, effect)
|
||||
function onGain(owner, effect, actionContainer)
|
||||
--Only one song per bard can be active, need to figure out a good way to do this
|
||||
owner.AddMod(modifiersGlobal.Refresh, effect.GetMagnitude());
|
||||
end;
|
||||
|
||||
function onLose(owner, effect)
|
||||
function onLose(owner, effect, actionContainer)
|
||||
owner.SubtractMod(modifiersGlobal.Refresh, effect.GetMagnitude());
|
||||
end;
|
@ -8,6 +8,6 @@ end;
|
||||
function onCommandFinish(effect, owner, skill, actionContainer)
|
||||
--27259: Light Shot
|
||||
if skill.id == 27259 then
|
||||
actionContainer.AddAction(owner.statusEffects.RemoveStatusEffectForBattleAction(effect));
|
||||
owner.statusEffects.RemoveStatusEffect(effect, actionContainer, 30331, false);
|
||||
end
|
||||
end;
|
@ -1,3 +1,8 @@
|
||||
require("modifiers")
|
||||
|
||||
--BV doesn't really do anything i think
|
||||
--BV doesn't really do anything i think
|
||||
function onGain(owner, effect, actionContainer)
|
||||
end;
|
||||
|
||||
function onLose(owner, effect, actionContainer)
|
||||
end;
|
@ -1,13 +1,12 @@
|
||||
require("modifiers");
|
||||
|
||||
function onGain(owner, effect)
|
||||
owner.statusEffects.RemoveStatusEffect(223208);
|
||||
function onGain(owner, effect, actionContainer)
|
||||
end
|
||||
|
||||
--Increases attack power and reduces defense with each successful attack
|
||||
--Does this include weaponskills?
|
||||
--Is this on every hit or every succesfull skill useage?
|
||||
function onHit(effect, attacker, defender, action, actionContainer)
|
||||
function onHit(effect, attacker, defender, skill, action, actionContainer)
|
||||
--Trait increases effect by 20%. Does this include the reduced defense,
|
||||
--does this increase the cap or the rate at which you get AP or both?
|
||||
|
||||
@ -32,7 +31,7 @@ function onHit(effect, attacker, defender, action, actionContainer)
|
||||
end
|
||||
end;
|
||||
|
||||
function onDamageTaken(effect, attacker, defender, action, actionContainer)
|
||||
function onDamageTaken(effect, attacker, defender, skill, action, actionContainer)
|
||||
local apPerHit = 20;
|
||||
local defPerHit = 20;
|
||||
|
||||
@ -45,7 +44,7 @@ function onDamageTaken(effect, attacker, defender, action, actionContainer)
|
||||
effect.SetExtra(0);
|
||||
end
|
||||
|
||||
function onLose(owner, effect)
|
||||
function onLose(owner, effect, actionContainer)
|
||||
local apPerHit = 20;
|
||||
local defPerHit = 20;
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
require("modifiers");
|
||||
|
||||
function onGain(target, effect)
|
||||
function onGain(owner, effect, actionContainer)
|
||||
end;
|
||||
|
||||
function onLose(target, effect)
|
||||
function onLose(owner, effect, actionContainer)
|
||||
end;
|
@ -1,9 +1,9 @@
|
||||
require("modifiers")
|
||||
|
||||
function onGain(owner, effect)
|
||||
function onGain(owner, effect, actionContainer)
|
||||
owner.SubtractMod(modifiersGlobal.Accuracy, effect.GetMagnitude());
|
||||
end;
|
||||
|
||||
function onLose(owner, effect)
|
||||
function onLose(owner, effect, actionContainer)
|
||||
owner.AddMod(modifiersGlobal.Accuracy, effect.GetMagnitude());
|
||||
end;
|
@ -4,12 +4,12 @@ require("battleutils")
|
||||
--Forces crit of a single WS action from rear.
|
||||
function onPreAction(effect, caster, target, skill, action, actionContainer)
|
||||
--If action hit from the rear and is a weaponskill ation
|
||||
if (action.param == HitDirection.Rear and action.commandType == CommandType.WeaponSkill) then
|
||||
if (action.param == HitDirection.Rear and skill.GetCommandType() == CommandType.WeaponSkill) then
|
||||
--Set action's crit rate to 100%
|
||||
action.critRate = 100.0;
|
||||
end
|
||||
|
||||
--Remove status and add message
|
||||
actionsList.AddAction(target.statusEffects.RemoveForBattleAction(effect));
|
||||
target.statusEffects.RemoveStatusEffect(effect, actionContainer);
|
||||
end;
|
||||
|
||||
|
17
data/scripts/effects/blindside2.lua
Normal file
17
data/scripts/effects/blindside2.lua
Normal file
@ -0,0 +1,17 @@
|
||||
require("modifiers")
|
||||
require("battleutils")
|
||||
|
||||
--Forces crit of a single WS action from rear.
|
||||
function onPreAction(effect, caster, target, skill, action, actionContainer)
|
||||
--If action hit from the rear and is a weaponskill ation
|
||||
if (action.param == HitDirection.Rear and skill.GetCommandType() == CommandType.WeaponSkill) then
|
||||
--Set action's crit rate to 100%
|
||||
action.critRate = 100.0;
|
||||
end
|
||||
|
||||
--Figure out INT bonus for tier 2
|
||||
|
||||
--Remove status and add message
|
||||
target.statusEffects.RemoveStatusEffect(effect, actionContainer);
|
||||
end;
|
||||
|
@ -2,15 +2,13 @@
|
||||
--Based on a few videos it seems like it heals for 0.5% of max MP every second, traited. This is an early guess but it seems correct
|
||||
--Untraited is less clear. It could be 0.25%, 0.30%, or 0.40%. Guessing it's 0.30
|
||||
|
||||
function onTick(owner, effect)
|
||||
function onTick(owner, effect, actionContainer)
|
||||
local percentPerSecond = 0.0030;
|
||||
|
||||
if effect.GetTier() == 2 then
|
||||
percentPerSecond = 0.005;
|
||||
end
|
||||
|
||||
print(effect.GetExtra());
|
||||
|
||||
local amount = percentPerSecond * owner.GetMaxMP() + 0.25;
|
||||
effect.SetExtra(effect.GetExtra() + amount);
|
||||
if effect.GetExtra() >= effect.GetMagnitude() then
|
||||
|
@ -1,3 +1,3 @@
|
||||
function onLose(target, effect)
|
||||
target:SetProc(1, false);
|
||||
function onLose(owner, effect, actionContainer)
|
||||
owner:SetProc(1, false);
|
||||
end;
|
@ -3,7 +3,7 @@ require("battleUtils")
|
||||
--Takes 10% of hp rounded down when using a weaponskill
|
||||
--Random guess, but increases damage by 10% (12.5% traited)?
|
||||
function onPreAction(effect, caster, target, skill, action, actionContainer)
|
||||
if skill.commandType == CommandType.Weaponskill then
|
||||
if skill.GetCommandType() == CommandType.Weaponskill then
|
||||
local hpToRemove = math.floor(caster.GetHP() * 0.10);
|
||||
local modifier = 1.10;
|
||||
|
||||
@ -12,9 +12,9 @@ function onPreAction(effect, caster, target, skill, action, actionContainer)
|
||||
end
|
||||
|
||||
action.amount = action.amount * modifier;
|
||||
caster.DelHP(hpToRemove);
|
||||
caster.DelHP(hpToRemove, actionContainer);
|
||||
|
||||
--Remove status and add message
|
||||
actionContainer.AddAction(target.statusEffects.RemoveForBattleAction(effect));
|
||||
--Remove status and add message
|
||||
caster.statusEffects.RemoveStatusEffect(effect, actionContainer, 30331, true);
|
||||
end
|
||||
end;
|
@ -1,7 +1,8 @@
|
||||
require("modifiers");
|
||||
require("battleutils")
|
||||
|
||||
--Absorb HP on next WS or ability
|
||||
function onHit(effect, attacker, defender, action, actionContainer)
|
||||
function onHit(effect, attacker, defender, skill, action, actionContainer)
|
||||
|
||||
--1.21: Absorb HP amount no longer affected by player VIT rating.
|
||||
--Bloodbath seems based on both defener and attacker's stats, even after 1.21.
|
||||
@ -11,14 +12,14 @@ function onHit(effect, attacker, defender, action, actionContainer)
|
||||
--Possibly magic resist? Slashing resist?
|
||||
|
||||
--For now using 1.0 as baseline since that seems to be the average
|
||||
if action.commandType == CommandType.Weaponskill or action.commandType == CommandType.Ability then
|
||||
if skill.GetCommandType() == CommandType.Weaponskill or skill.GetCommandType() == CommandType.Ability then
|
||||
local absorbModifier = 1.0
|
||||
local absorbAmount = action.amount * absorbModifier;
|
||||
|
||||
attacker.AddHP(absorbAmount);
|
||||
--30332: You absorb hp from target
|
||||
actionContainer.AddHPAction(defender.actorId, 30332, absorbAmount)
|
||||
actionContainer.AddHPAbsorbAction(defender.actorId, 30332, absorbAmount)
|
||||
--Bloodbath is lost after absorbing hp
|
||||
actionContainer.AddAction(defender.statusEffects.RemoveStatusEffectForBattleAction(effect));
|
||||
defender.statusEffects.RemoveStatusEffect(effect,actionContainer, 30331, false);
|
||||
end
|
||||
end;
|
@ -8,13 +8,14 @@ require("modifiers")
|
||||
--Bloodletter is apparently impacted by PIE
|
||||
--http://forum.square-enix.com/ffxiv/threads/35795-STR-DEX-PIE-ATK-Testing/page2
|
||||
--Chance to land is also impacted by PIE
|
||||
function onGain(owner, effect)
|
||||
--This is because PIE increases Enfeebling Magic Potency which impacts additional effect damage and land rates
|
||||
function onGain(owner, effect, actionContainer)
|
||||
owner.AddMod(modifiersGlobal.RegenDown, 15);
|
||||
end
|
||||
|
||||
--Additional damage is 570 at level 50
|
||||
--https://ffxiv.gamerescape.com/w/index.php?title=Bloodletter&oldid=298020
|
||||
function onLose(owner, effect)
|
||||
function onLose(owner, effect, actionContainer)
|
||||
owner.SubtractMod(modifiersGlobal.RegenDown, 15);
|
||||
owner.DelHP(570);
|
||||
owner.DelHP(570, actionContainer);
|
||||
end
|
||||
|
@ -1,10 +1,10 @@
|
||||
require("modifiers")
|
||||
|
||||
--Bloodletter2 is the uncomboed version of Bloodletter. It doesn't deal any additional damage when it falls off but has the same tick damage
|
||||
function onGain(owner, effect)
|
||||
function onGain(owner, effect, actionContainer)
|
||||
owner.AddMod(modifiersGlobal.RegenDown, 15);
|
||||
end
|
||||
|
||||
function onLose(owner, effect)
|
||||
function onLose(owner, effect, actionContainer)
|
||||
owner.SubtractMod(modifiersGlobal.RegenDown, 15);
|
||||
end
|
||||
|
@ -1,12 +1,12 @@
|
||||
require("modifiers")
|
||||
|
||||
function onGain(target, effect)
|
||||
function onGain(owner, effect, actionContainer)
|
||||
--Multiples Attack Magic Potency by 1.2 and Healing Magic Potency by 0.8
|
||||
target.SetMod(modifiersGlobal.MagicAttack, target.GetMod(modifiersGlobal.MagicAttack) * 1.2);
|
||||
target.SetMod(modifiersGlobal.MagicHeal, target.GetMod(modifiersGlobal.MagicHeal) * 0.8);
|
||||
owner.MultiplyMod(modifiersGlobal.AttackMagicPotency, 1.2);
|
||||
owner.MultiplyMod(modifiersGlobal.HealingMagicPotency, 0.8);
|
||||
end;
|
||||
|
||||
function onLose(target, effect)
|
||||
target.SetMod(modifiersGlobal.MagicAttack, target.GetMod(modifiersGlobal.MagicAttack) / 1.2);
|
||||
target.SetMod(modifiersGlobal.MagicHeal, target.GetMod(modifiersGlobal.MagicHeal) / 0.8);
|
||||
function onLose(owner, effect, actionContainer)
|
||||
owner.DivideMod(modifiersGlobal.AttackMagicPotency, 1.2);
|
||||
owner.DivideMod(modifiersGlobal.HealingMagicPotency, 0.8);
|
||||
end;
|
@ -1,10 +1,10 @@
|
||||
require("modifiers")
|
||||
|
||||
function onHit(effect, attacker, defender, action, actionContainer)
|
||||
function onHit(effect, attacker, defender, skill, action, actionContainer)
|
||||
local enmity = action.enmity;
|
||||
action.enmity = 0;
|
||||
|
||||
defender.hateContainer.UpdateHate(effect.GetSource(), enmity);
|
||||
--Does collusion send a message?
|
||||
actionContainer.AddAction(attacker.statusEffects.RemoveStatusEffectForBattleAction(effect));
|
||||
defender.statusEffects.RemoveStatusEffect(effect, actionContainer, 30331, false);
|
||||
end;
|
@ -1,3 +1,6 @@
|
||||
function onLose(target, effect)
|
||||
target:SetCombos();
|
||||
function onGain(owner, effect, actionContainer)
|
||||
end;
|
||||
|
||||
function onLose(owner, effect, actionContainer)
|
||||
owner:SetCombos();
|
||||
end;
|
@ -1,7 +1,7 @@
|
||||
require("modifiers")
|
||||
|
||||
--Enahnced Cover: Restores 25% of damage taken as MP. Does not send a message
|
||||
function onDamageTaken(effect, attacker, defender, action, actionContainer)
|
||||
function onDamageTaken(effect, attacker, defender, skill, action, actionContainer)
|
||||
if effect.GetTier() == 2 then
|
||||
defender.AddMP(0.25 * action.amount);
|
||||
end
|
||||
|
@ -5,9 +5,9 @@ require("battleutils")
|
||||
--There isn't really any information on this, but due to the fact it falls off BEFORE the target is hit,
|
||||
--I'm assuming it increases a spell's accuracy modifier instead of giving actual magic accuracy
|
||||
function onCommandStart(effect, owner, skill, actionContainer)
|
||||
if skill.actionType == ActionType.Magic then
|
||||
if skill.GetActionType() == ActionType.Magic then
|
||||
--50 is random guess.
|
||||
skill.accuracyModifier = skill.accuracyModifier + 50;
|
||||
actionContainer.AddAction(owner.RemoveStatusEffectForBattleAction(effect));
|
||||
owner.statusEffects.RemoveStatusEffect(effect, actionContainer, 30331, false);
|
||||
end
|
||||
end
|
@ -5,12 +5,13 @@ require("battleutils")
|
||||
function onPreAction(effect, caster, target, skill, action, actionContainer)
|
||||
--Evade single ranged or magic attack
|
||||
--Traited allows for physical attacks
|
||||
if target.allegiance != caster.allegiance and (skill.isRanged or action.actionType == ActionType.Magic) then
|
||||
if target.allegiance != caster.allegiance and (skill.isRanged or skill.GetActionType() == ActionType.Magic) then
|
||||
--Unsure if decoy forces a miss/resist or if this is the one case where the evade hittype is used
|
||||
--Set action's hit rate to 0
|
||||
action.hirRate = 0.0;
|
||||
|
||||
action.hitRate = 0.0;
|
||||
action.resistRate = 750;
|
||||
--Remove status and add message
|
||||
actionContainer.AddAction(target.statusEffects.RemoveStatusEffectForBattleAction(effect));
|
||||
defender.statusEffects.RemoveStatusEffect(effect, actionContainer, 30331, false);
|
||||
end
|
||||
|
||||
end;
|
@ -5,11 +5,12 @@ require("battleutils")
|
||||
function onPreAction(effect, caster, target, skill, action, actionContainer)
|
||||
--Evade single ranged or magic attack
|
||||
--Traited allows for physical attacks
|
||||
if target.allegiance != caster.allegiance and (skill.isRanged or action.actionType == ActionType.Magic or action.actionType == ActionType.Physical) then
|
||||
if target.allegiance != caster.allegiance and (skill.isRanged or skill.GetActionType() == ActionType.Magic or skill.GetActionType() == ActionType.Physical) then
|
||||
--Set action's hit rate to 0
|
||||
action.hirRate = 0.0;
|
||||
action.hitRate = 0.0;
|
||||
action.resistRate = 400;
|
||||
--Remove status and add message
|
||||
actionContainer.AddAction(target.statusEffects.RemoveStatusEffectForBattleAction(effect));
|
||||
defender.statusEffects.RemoveStatusEffect(effect, actionContainer, 30331, false);
|
||||
end
|
||||
|
||||
end;
|
@ -1,5 +1,5 @@
|
||||
function onGain(target, effect)
|
||||
function onGain(owner, effect, actionContainer)
|
||||
end;
|
||||
|
||||
function onLose(target, effect)
|
||||
function onLose(owner, effect, actionContainer)
|
||||
end;
|
@ -1,9 +1,9 @@
|
||||
require("modifiers")
|
||||
|
||||
function onGain(owner, effect)
|
||||
function onGain(owner, effect, actionContainer)
|
||||
owner.SubtractMod(modifiersGlobal.Defense, effect.GetMagnitude());
|
||||
end
|
||||
|
||||
function onLose(owner, effect)
|
||||
function onLose(owner, effect, actionContainer)
|
||||
owner.AddMod(modifiersGlobal.Defense, effect.GetMagnitude());
|
||||
end
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user