mirror of
https://bitbucket.org/Ioncannon/project-meteor-server.git
synced 2025-05-20 08:26:59 -04:00
Combat changes and bug fixes
Added the combo and proc systems Added scripts for most weaponskill and spells as well as some abilities and status effects Added support for multihit attacks Added AbilityState for abilities Added hiteffects that change based on an attack's parameters Added positionals Changed how targeting works for battlecommands Fixed bug that occurred when moving or swapping hotbar commands Fixed bug that occurred when losing status effects
This commit is contained in:
@@ -163,6 +163,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.player
|
||||
owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId));
|
||||
|
||||
list[slot] = item;
|
||||
owner.CalculateBaseStats();// RecalculateStats();
|
||||
}
|
||||
|
||||
public void ToggleDBWrite(bool flag)
|
||||
@@ -189,6 +190,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.player
|
||||
owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId));
|
||||
|
||||
list[slot] = null;
|
||||
owner.RecalculateStats();
|
||||
}
|
||||
|
||||
private void SendEquipmentPackets(ushort equipSlot, InventoryItem item)
|
||||
|
@@ -26,6 +26,7 @@ using FFXIVClassic_Map_Server.packets.send.actor.battle;
|
||||
using FFXIVClassic_Map_Server.actors.chara.ai.utils;
|
||||
using FFXIVClassic_Map_Server.actors.chara.ai.state;
|
||||
using FFXIVClassic_Map_Server.actors.chara.npc;
|
||||
using FFXIVClassic_Map_Server.actors.chara;
|
||||
|
||||
namespace FFXIVClassic_Map_Server.Actors
|
||||
{
|
||||
@@ -228,6 +229,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
|
||||
this.aiContainer = new AIContainer(this, new PlayerController(this), null, new TargetFind(this));
|
||||
allegiance = CharacterTargetingAllegiance.Player;
|
||||
CalculateBaseStats();
|
||||
}
|
||||
|
||||
public List<SubPacket> Create0x132Packets()
|
||||
@@ -361,7 +363,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
//Status Times
|
||||
for (int i = 0; i < charaWork.statusShownTime.Length; i++)
|
||||
{
|
||||
if (charaWork.statusShownTime[i] != 0xFFFFFFFF)
|
||||
if (charaWork.statusShownTime[i] != 0)
|
||||
propPacketUtil.AddProperty(String.Format("charaWork.statusShownTime[{0}]", i));
|
||||
}
|
||||
|
||||
@@ -614,6 +616,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
catch (Exception e)
|
||||
{
|
||||
this.SendMessage(SendMessagePacket.MESSAGE_TYPE_SYSTEM_ERROR, "[SendPacket]", "Unable to send packet.");
|
||||
this.SendMessage(SendMessagePacket.MESSAGE_TYPE_SYSTEM_ERROR, "[SendPacket]", e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1705,20 +1708,22 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
|
||||
Party partyGroup = (Party) currentParty;
|
||||
|
||||
for (int i = 0; i < partyGroup.members.Count; i++)
|
||||
{
|
||||
if (partyGroup.members[i] == actorId)
|
||||
{
|
||||
partyGroup.members.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
partyGroup.RemoveMember(actorId);
|
||||
|
||||
//for (int i = 0; i < partyGroup.members.Count; i++)
|
||||
//{
|
||||
// if (partyGroup.members[i] == actorId)
|
||||
// {
|
||||
// partyGroup.members.RemoveAt(i);
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
|
||||
//currentParty.members.Remove(this);
|
||||
if (partyGroup.members.Count == 0)
|
||||
Server.GetWorldManager().NoMembersInParty((Party)currentParty);
|
||||
|
||||
currentParty = null;
|
||||
|
||||
//currentParty = new Party(0, actorId);
|
||||
}
|
||||
|
||||
public void IssueChocobo(byte appearanceId, string nameResponse)
|
||||
@@ -1842,27 +1847,23 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
|
||||
//If the class we're equipping for is the current class, we can just look at charawork.command
|
||||
if(classId == charaWork.parameterSave.state_mainSkill[0])
|
||||
{
|
||||
hotbarSlot = FindFirstCommandSlotById(0);
|
||||
}
|
||||
//Otherwise, we need to check the database.
|
||||
else
|
||||
{
|
||||
hotbarSlot = (ushort) (Database.FindFirstCommandSlot(this, classId) + charaWork.commandBorder);
|
||||
}
|
||||
|
||||
EquipAbility(classId, commandId, hotbarSlot, printMessage);
|
||||
}
|
||||
|
||||
//Add commandId to classId's hotbar at hotbarSlot.
|
||||
//If classId is not the current class, do it in the database
|
||||
//hotbarSlot is 32-indexed
|
||||
//hotbarSlot starts at 32
|
||||
public void EquipAbility(byte classId, uint commandId, ushort hotbarSlot, bool printMessage = true)
|
||||
{
|
||||
var ability = Server.GetWorldManager().GetBattleCommand(commandId);
|
||||
uint trueCommandId = commandId | 0xA0F00000;
|
||||
uint trueCommandId = 0xA0F00000 + commandId;
|
||||
ushort lowHotbarSlot = (ushort)(hotbarSlot - charaWork.commandBorder);
|
||||
ushort maxRecastTime = (ushort)ability.recastTimeSeconds;
|
||||
ushort maxRecastTime = (ushort)(ability != null ? ability.maxRecastTimeSeconds : 5);
|
||||
uint recastEnd = Utils.UnixTimeStampUTC() + maxRecastTime;
|
||||
List<ushort> slotsToUpdate = new List<ushort>();
|
||||
|
||||
@@ -1889,6 +1890,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
//hotbarSlot 1 and 2 are 32-indexed.
|
||||
public void SwapAbilities(ushort hotbarSlot1, ushort hotbarSlot2)
|
||||
{
|
||||
//0 indexed hotbar slots for saving to database and recast timers
|
||||
uint lowHotbarSlot1 = (ushort)(hotbarSlot1 - charaWork.commandBorder);
|
||||
uint lowHotbarSlot2 = (ushort)(hotbarSlot2 - charaWork.commandBorder);
|
||||
|
||||
@@ -1906,10 +1908,12 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
charaWork.command[hotbarSlot2] = commandId;
|
||||
charaWork.parameterTemp.maxCommandRecastTime[lowHotbarSlot2] = recastMax;
|
||||
charaWork.parameterSave.commandSlot_recastTime[lowHotbarSlot2] = recastEnd;
|
||||
|
||||
//Save changes
|
||||
Database.EquipAbility(this, charaWork.parameterSave.state_mainSkill[0], (ushort)(lowHotbarSlot1), charaWork.command[hotbarSlot1], charaWork.parameterSave.commandSlot_recastTime[lowHotbarSlot1]);
|
||||
|
||||
//Save changes to both slots
|
||||
Database.EquipAbility(this, GetCurrentClassOrJob(), (ushort)(lowHotbarSlot1), 0xA0F00000 ^ charaWork.command[hotbarSlot1], charaWork.parameterSave.commandSlot_recastTime[lowHotbarSlot1]);
|
||||
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);
|
||||
@@ -1926,7 +1930,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
slotsToUpdate.Add(trueHotbarSlot);
|
||||
|
||||
if(printMessage)
|
||||
SendGameMessage(Server.GetWorldManager().GetActor(), 30604, 0x20, 0, commandId ^ 0xA0F00000);
|
||||
SendGameMessage(Server.GetWorldManager().GetActor(), 30604, 0x20, 0, 0xA0F00000 ^ commandId);
|
||||
|
||||
UpdateHotbar(slotsToUpdate);
|
||||
}
|
||||
@@ -1952,10 +1956,10 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
return firstSlot;
|
||||
}
|
||||
|
||||
private void UpdateHotbarTimer(uint commandId, uint recastTimeSeconds)
|
||||
private void UpdateHotbarTimer(uint commandId, uint recastTimeMs)
|
||||
{
|
||||
ushort slot = FindFirstCommandSlotById(commandId);
|
||||
charaWork.parameterSave.commandSlot_recastTime[slot - charaWork.commandBorder] = Utils.UnixTimeStampUTC(DateTime.Now.AddSeconds(recastTimeSeconds));
|
||||
charaWork.parameterSave.commandSlot_recastTime[slot - charaWork.commandBorder] = Utils.UnixTimeStampUTC(DateTime.Now.AddMilliseconds(recastTimeMs));
|
||||
var slots = new List<ushort>();
|
||||
slots.Add(slot);
|
||||
UpdateRecastTimers(slots);
|
||||
@@ -2123,7 +2127,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
SendGameMessage(Server.GetWorldManager().GetActor(), 32539, 0x20, (uint)spell.id);
|
||||
return false;
|
||||
}
|
||||
if (!IsValidTarget(target, spell.validTarget) || !spell.IsValidTarget(this, target))
|
||||
if (!IsValidTarget(target, spell.mainTarget) || !spell.IsValidMainTarget(this, target))
|
||||
{
|
||||
// error packet is set in IsValidTarget
|
||||
return false;
|
||||
@@ -2141,29 +2145,36 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
SendGameMessage(Server.GetWorldManager().GetActor(), 32535, 0x20, (uint)skill.id);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target == null)
|
||||
{
|
||||
// Target does not exist.
|
||||
SendGameMessage(Server.GetWorldManager().GetActor(), 32511, 0x20, (uint)skill.id);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Utils.Distance(positionX, positionY, positionZ, target.positionX, target.positionY, target.positionZ) > skill.range)
|
||||
{
|
||||
// The target is out of range.
|
||||
SendGameMessage(Server.GetWorldManager().GetActor(), 32539, 0x20, (uint)skill.id);
|
||||
return false;
|
||||
}
|
||||
if (!IsValidTarget(target, skill.validTarget) || !skill.IsValidTarget(this, target))
|
||||
|
||||
if (!IsValidTarget(target, skill.validTarget) || !skill.IsValidMainTarget(this, target))
|
||||
{
|
||||
// error packet is set in IsValidTarget
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnAttack(State state, BattleAction action, ref BattleAction error)
|
||||
{
|
||||
var target = state.GetTarget();
|
||||
|
||||
base.OnAttack(state, action, ref error);
|
||||
|
||||
// todo: switch based on main weap (also probably move this anim assignment somewhere else)
|
||||
action.animation = 0x19001000;
|
||||
if (error == null)
|
||||
@@ -2171,42 +2182,39 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
// melee attack animation
|
||||
//action.animation = 0x19001000;
|
||||
}
|
||||
var target = state.GetTarget();
|
||||
if (target is BattleNpc)
|
||||
{
|
||||
((BattleNpc)target).hateContainer.UpdateHate(this, action.amount);
|
||||
((BattleNpc)target).hateContainer.UpdateHate(this, action.enmity);
|
||||
}
|
||||
|
||||
LuaEngine.GetInstance().OnSignal("playerAttack");
|
||||
}
|
||||
|
||||
public override void OnCast(State state, BattleAction[] actions, ref BattleAction[] errors)
|
||||
public override void OnCast(State state, BattleAction[] actions, BattleCommand spell, ref BattleAction[] errors)
|
||||
{
|
||||
// todo: update hotbar timers to skill's recast time (also needs to be done on class change or equip crap)
|
||||
base.OnCast(state, actions, ref errors);
|
||||
|
||||
var spell = ((MagicState)state).GetSpell();
|
||||
base.OnCast(state, actions, spell, ref errors);
|
||||
// todo: should just make a thing that updates the one slot cause this is dumb as hell
|
||||
UpdateHotbarTimer(spell.id, spell.recastTimeSeconds);
|
||||
LuaEngine.GetInstance().OnSignal("spellUse");
|
||||
UpdateHotbarTimer(spell.id, spell.recastTimeMs);
|
||||
//LuaEngine.GetInstance().OnSignal("spellUse");
|
||||
}
|
||||
|
||||
public override void OnWeaponSkill(State state, BattleAction[] actions, ref BattleAction[] errors)
|
||||
public override void OnWeaponSkill(State state, BattleAction[] actions, BattleCommand skill, ref BattleAction[] errors)
|
||||
{
|
||||
// todo: update hotbar timers to skill's recast time (also needs to be done on class change or equip crap)
|
||||
base.OnWeaponSkill(state, actions, ref errors);
|
||||
var skill = ((WeaponSkillState)state).GetWeaponSkill();
|
||||
base.OnWeaponSkill(state, actions, skill, ref errors);
|
||||
|
||||
// todo: should just make a thing that updates the one slot cause this is dumb as hell
|
||||
UpdateHotbarTimer(skill.id, skill.recastTimeSeconds);
|
||||
UpdateHotbarTimer(skill.id, skill.recastTimeMs);
|
||||
// todo: this really shouldnt be called on each ws?
|
||||
lua.LuaEngine.CallLuaBattleFunction(this, "onWeaponSkill", this, state.GetTarget(), skill);
|
||||
LuaEngine.GetInstance().OnSignal("weaponskillUse");
|
||||
}
|
||||
|
||||
public override void OnAbility(State state, BattleAction[] actions, ref BattleAction[] errors)
|
||||
public override void OnAbility(State state, BattleAction[] actions, BattleCommand ability, ref BattleAction[] errors)
|
||||
{
|
||||
base.OnAbility(state, actions, ref errors);
|
||||
|
||||
base.OnAbility(state, actions, ability, ref errors);
|
||||
UpdateHotbarTimer(ability.id, ability.recastTimeMs);
|
||||
LuaEngine.GetInstance().OnSignal("abilityUse");
|
||||
}
|
||||
|
||||
@@ -2307,6 +2315,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
currentJob = jobId;
|
||||
BroadcastPacket(SetCurrentJobPacket.BuildPacket(actorId, jobId), true);
|
||||
Database.LoadHotbar(this);
|
||||
SendCharaExpInfo();
|
||||
}
|
||||
|
||||
//Gets the id of the player's current job. If they aren't a job, gets the id of their class
|
||||
@@ -2328,5 +2337,66 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
updateFlags |= ActorUpdateFlags.HpTpMp;
|
||||
}
|
||||
|
||||
public void SetCombos(int comboId1 = 0, int comboId2 = 0)
|
||||
{
|
||||
SetCombos(new int[] { comboId1, comboId2 });
|
||||
}
|
||||
|
||||
public void SetCombos(int[] comboIds)
|
||||
{
|
||||
Array.Copy(comboIds, playerWork.comboNextCommandId, 2);
|
||||
|
||||
//If we're starting or continuing a combo chain, add the status effect and combo cost bonus
|
||||
if (comboIds[0] != 0)
|
||||
{
|
||||
StatusEffect comboEffect = new StatusEffect(this, (uint) StatusEffectId.Combo, 1, 0, 13);
|
||||
comboEffect.SetOverwritable(1);
|
||||
statusEffects.AddStatusEffect(comboEffect, this, true);
|
||||
playerWork.comboCostBonusRate = 1;
|
||||
}
|
||||
//Otherwise we're ending a combo, remove the status
|
||||
else
|
||||
{
|
||||
statusEffects.RemoveStatusEffect(statusEffects.GetStatusEffectById((uint) StatusEffectId.Combo));
|
||||
playerWork.comboCostBonusRate = 0;
|
||||
}
|
||||
|
||||
ActorPropertyPacketUtil comboPropertyPacket = new ActorPropertyPacketUtil("playerWork/combo", this);
|
||||
comboPropertyPacket.AddProperty($"playerWork.comboCostBonusRate");
|
||||
comboPropertyPacket.AddProperty($"playerWork.comboNextCommandId[{0}]");
|
||||
comboPropertyPacket.AddProperty($"playerWork.comboNextCommandId[{1}]");
|
||||
QueuePackets(comboPropertyPacket.Done());
|
||||
}
|
||||
|
||||
public override void CalculateBaseStats()
|
||||
{
|
||||
base.CalculateBaseStats();
|
||||
//Add weapon property mod
|
||||
var equip = GetEquipment();
|
||||
var mainHandItem = equip.GetItemAtSlot(Equipment.SLOT_MAINHAND);
|
||||
var damageAttribute = 0;
|
||||
var attackDelay = 3000;
|
||||
var hitCount = 1;
|
||||
GetAttackDelayMs();
|
||||
if (mainHandItem != null)
|
||||
{
|
||||
var mainHandWeapon = (Server.GetItemGamedata(mainHandItem.itemId) as WeaponItem);
|
||||
damageAttribute = mainHandWeapon.damageAttributeType1;
|
||||
attackDelay = (int) (mainHandWeapon.damageInterval * 1000);
|
||||
hitCount = mainHandWeapon.frequency;
|
||||
}
|
||||
|
||||
var hasShield = equip.GetItemAtSlot(Equipment.SLOT_OFFHAND) != null ? 1 : 0;
|
||||
SetMod((uint)Modifier.HasShield, hasShield);
|
||||
|
||||
SetMod((uint)Modifier.AttackType, damageAttribute);
|
||||
SetMod((uint)Modifier.AttackDelay, attackDelay);
|
||||
SetMod((uint)Modifier.HitCount, hitCount);
|
||||
}
|
||||
public void SetCurrentJob(ushort jobId)
|
||||
{
|
||||
currentJob = jobId;
|
||||
BroadcastPacket(SetCurrentJobPacket.BuildPacket(actorId, jobId), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user