# Conflicts:
#	data/scripts/modifiers.lua
This commit is contained in:
Tahir Akhlaq 2017-12-10 15:07:28 +00:00
commit cd60c571ac
54 changed files with 935 additions and 268 deletions

View file

@ -40,6 +40,28 @@
<PropertyGroup>
<RunPostBuildEvent>Always</RunPostBuildEvent>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="Cyotek.Collections.Generic.CircularBuffer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=58daa28b0b2de221, processorArchitecture=MSIL">
<HintPath>..\packages\Cyotek.CircularBuffer.1.0.0.0\lib\net20\Cyotek.Collections.Generic.CircularBuffer.dll</HintPath>

View file

@ -511,7 +511,6 @@ namespace FFXIVClassic_Map_Server
battleNpc.SetMod((uint)Modifier.Defense, reader.GetUInt32("def"));
battleNpc.SetMod((uint)Modifier.Evasion, reader.GetUInt32("eva"));
battleNpc.dropListId = reader.GetUInt32("dropListId");
battleNpc.spellListId = reader.GetUInt32("spellListId");
battleNpc.skillListId = reader.GetUInt32("skillListId");
@ -644,12 +643,48 @@ namespace FFXIVClassic_Map_Server
battleNpc.SetMod((uint)Modifier.Defense, reader.GetUInt32("def"));
battleNpc.SetMod((uint)Modifier.Evasion, reader.GetUInt32("eva"));
if (battleNpc.poolMods != null)
{
foreach (var a in battleNpc.poolMods.mobModList)
{
battleNpc.SetMobMod(a.Value.id, (long)(a.Value.value));
}
foreach (var a in battleNpc.poolMods.modList)
{
battleNpc.SetMod(a.Key, (long)(a.Value.value));
}
}
if (battleNpc.genusMods != null)
{
foreach (var a in battleNpc.genusMods.mobModList)
{
battleNpc.SetMobMod(a.Key, (long)(a.Value.value));
}
foreach (var a in battleNpc.genusMods.modList)
{
battleNpc.SetMod(a.Key, (long)(a.Value.value));
}
}
if(battleNpc.spawnMods != null)
{
foreach (var a in battleNpc.spawnMods.mobModList)
{
battleNpc.SetMobMod(a.Key, (long)(a.Value.value));
}
foreach (var a in battleNpc.spawnMods.modList)
{
battleNpc.SetMod(a.Key, (long)(a.Value.value));
}
}
battleNpc.dropListId = reader.GetUInt32("dropListId");
battleNpc.spellListId = reader.GetUInt32("spellListId");
battleNpc.skillListId = reader.GetUInt32("skillListId");
battleNpc.SetMaxHP(1000);
battleNpc.SetHP(1000);
battleNpc.SetBattleNpcId(reader.GetUInt32("bnpcId"));
battleNpc.SetRespawnTime(reader.GetUInt32("respawnTime"));
battleNpc.CalculateBaseStats();
battleNpc.RecalculateStats();
//battleNpc.SetMod((uint)Modifier.ResistFire, )
@ -679,7 +714,7 @@ namespace FFXIVClassic_Map_Server
try
{
conn.Open();
var query = $"SELECT {primaryKey}, modId, modVal, isMobMod FROM {tableName} GROUP BY {primaryKey};";
var query = $"SELECT {primaryKey}, modId, modVal, isMobMod FROM {tableName}";
MySqlCommand cmd = new MySqlCommand(query, conn);
@ -688,9 +723,9 @@ namespace FFXIVClassic_Map_Server
while (reader.Read())
{
var id = reader.GetUInt32(primaryKey);
ModifierList modList = new ModifierList(id);
ModifierList modList = list.TryGetValue(id, out modList) ? modList : new ModifierList(id);
modList.SetModifier(reader.GetUInt16("modId"), reader.GetInt64("modVal"), reader.GetBoolean("isMobMod"));
list.Add(id, modList);
list[id] = modList;
}
}
}

View file

@ -438,9 +438,6 @@ namespace FFXIVClassic_Map_Server.Actors
updateFlags = ActorUpdateFlags.None;
zone.BroadcastPacketsAroundActor(this, packets);
SetActorPropetyPacket hpInfo = new SetActorPropetyPacket("charaWork/exp");
hpInfo.AddTarget();
}
}

View file

@ -419,8 +419,8 @@ namespace FFXIVClassic_Map_Server.Actors
public virtual List<BattleNpc> GetMonsters()
{
return GetAllActors<BattleNpc>();
}
}
public virtual List<Ally> GetAllies()
{
return GetAllActors<Ally>();
@ -500,8 +500,8 @@ namespace FFXIVClassic_Map_Server.Actors
npc = new Npc(mActorList.Count + 1, actorClass, uniqueId, this, x, y, z, rot, state, animId, null);
npc.LoadEventConditions(actorClass.eventConditions);
npc.SetMaxHP(300);
npc.SetHP(300);
//npc.SetMaxHP(30000);
//npc.SetHP(30000);
AddActorToZone(npc);
@ -669,12 +669,12 @@ namespace FFXIVClassic_Map_Server.Actors
{
foreach (Actor a in mActorList.Values.ToList())
a.Update(tick);
if ((tick - lastUpdateScript).TotalMilliseconds > 1500)
{
LuaEngine.GetInstance().CallLuaFunctionForReturn(LuaEngine.GetScriptPath(this), "onUpdate", true, this, tick);
//LuaEngine.GetInstance().CallLuaFunctionForReturn(LuaEngine.GetScriptPath(this), "onUpdate", true, this, tick);
lastUpdateScript = tick;
}
}
}
}

View file

@ -132,6 +132,18 @@ namespace FFXIVClassic_Map_Server.actors.area
return actor;
}
}
foreach (List<PrivateAreaContent> paList in contentAreas.Values)
{
foreach (PrivateArea pa in paList)
{
Actor actor = pa.FindActorInArea(id);
if (actor != null)
return actor;
}
}
return null;
}
else
@ -184,7 +196,7 @@ namespace FFXIVClassic_Map_Server.actors.area
{
if (this.pathCalls > 0)
{
Program.Log.Debug("Number of pathfinding calls {0} average time {1}ms", pathCalls, (float)(pathCallTime / pathCalls));
Program.Log.Debug("Number of pathfinding calls {0} average time {1}ms", pathCalls, (float)(pathCallTime / pathCalls));
}
lastUpdate = tick;
}

View file

@ -229,10 +229,10 @@ namespace FFXIVClassic_Map_Server.Actors
public void DoBattleAction(ushort commandId, uint animationId, BattleAction[] actions)
{
int currentIndex = 0;
//AoE abilities only ever hit 16 people, so we probably won't need this loop anymore
while (true)
{
if (actions.Length - currentIndex >= 18)
if (actions.Length - currentIndex >= 10)
zone.BroadcastPacketAroundActor(this, BattleActionX18Packet.BuildPacket(actorId, animationId, commandId, actions, ref currentIndex));
else if (actions.Length - currentIndex > 1)
zone.BroadcastPacketAroundActor(this, BattleActionX10Packet.BuildPacket(actorId, animationId, commandId, actions, ref currentIndex));
@ -243,7 +243,9 @@ namespace FFXIVClassic_Map_Server.Actors
}
else
break;
animationId = 0; //If more than one packet is sent out, only send the animation once to avoid double playing.
//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.
}
}
@ -298,7 +300,7 @@ namespace FFXIVClassic_Map_Server.Actors
public virtual void OnPath(Vector3 point)
{
lua.LuaEngine.CallLuaBattleFunction(this, "onPath", this, point);
//lua.LuaEngine.CallLuaBattleFunction(this, "onPath", this, point);
updateFlags |= ActorUpdateFlags.Position;
this.isAtSpawn = false;
@ -332,7 +334,7 @@ namespace FFXIVClassic_Map_Server.Actors
if ((updateFlags & ActorUpdateFlags.HpTpMp) != 0)
{
var propPacketUtil = new ActorPropertyPacketUtil("charaWork.parameterSave", this);
var propPacketUtil = new ActorPropertyPacketUtil("charaWork/stateAtQuicklyForAll", this);
propPacketUtil.AddProperty("charaWork.parameterSave.mp");
propPacketUtil.AddProperty("charaWork.parameterSave.mpMax");
@ -455,6 +457,7 @@ namespace FFXIVClassic_Map_Server.Actors
{
// todo: actual despawn timer
aiContainer.InternalDie(tick, 10);
ChangeSpeed(0.0f, 0.0f, 0.0f, 0.0f);
}
public virtual void Despawn(DateTime tick)
@ -528,6 +531,20 @@ namespace FFXIVClassic_Map_Server.Actors
updateFlags |= ActorUpdateFlags.HpTpMp;
}
public void SetMP(uint mp)
{
charaWork.parameterSave.mpMax = (short)mp;
if (mp > charaWork.parameterSave.hpMax[0])
SetMaxMP(mp);
updateFlags |= ActorUpdateFlags.HpTpMp;
}
public void SetMaxMP(uint mp)
{
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)
{
@ -547,7 +564,7 @@ namespace FFXIVClassic_Map_Server.Actors
}
}
public short GetJob()
public short GetClass()
{
return charaWork.parameterSave.state_mainSkill[0];
}
@ -599,8 +616,30 @@ namespace FFXIVClassic_Map_Server.Actors
public void RecalculateStats()
{
if (GetMod((uint)Modifier.Hp) != 0)
uint hpMod = (uint) GetMod((uint)Modifier.Hp);
if (hpMod != 0)
{
SetMaxHP(hpMod);
uint hpp = (uint) GetMod((uint) Modifier.HpPercent);
uint hp = hpMod;
if(hpp != 0)
{
hp = (uint) Math.Ceiling(((float)hpp / 100.0f) * hpMod);
}
SetHP(hp);
}
uint mpMod = (uint)GetMod((uint)Modifier.Mp);
if (mpMod != 0)
{
SetMaxMP(mpMod);
uint mpp = (uint)GetMod((uint)Modifier.MpPercent);
uint mp = mpMod;
if (mpp != 0)
{
mp = (uint)Math.Ceiling(((float)mpp / 100.0f) * mpMod);
}
SetMP(mp);
}
// todo: recalculate stats and crap
updateFlags |= ActorUpdateFlags.HpTpMp;
@ -635,14 +674,8 @@ namespace FFXIVClassic_Map_Server.Actors
//var packet = BattleActionX01Packet.BuildPacket(owner.actorId, owner.actorId, target.actorId, (uint)0x19001000, (uint)0x8000604, (ushort)0x765D, (ushort)BattleActionX01PacketCommand.Attack, (ushort)damage, (byte)0x1);
}
target.OnDamageTaken(this, action, DamageTakenType.Ability);
// todo: call onAttack/onDamageTaken
target.DelHP(action.amount);
if (target is BattleNpc)
{
((BattleNpc)target).lastAttacker = this;
((BattleNpc)target).hateContainer.UpdateHate(this, action.amount);
}
BattleUtils.DamageTarget(this, target, action, DamageTakenType.Attack);
AddTP(115);
target.AddTP(100);
}
@ -651,13 +684,14 @@ namespace FFXIVClassic_Map_Server.Actors
{
var spell = ((MagicState)state).GetSpell();
// damage is handled in script
this.DelMP(spell.mpCost); // mpCost can be set in script e.g. if caster has something for free spells
var spellCost = spell.CalculateCost((uint)this.GetLevel());
this.DelMP(spellCost); // mpCost can be set in script e.g. if caster has something for free spells
foreach (var action in actions)
zone.FindActorInArea<Character>(action.targetId).OnDamageTaken(this, action, DamageTakenType.Magic);
foreach (BattleAction action in actions)
if (zone.FindActorInArea<Character>(action.targetId) is Character chara)
BattleUtils.DamageTarget(this, chara, action, DamageTakenType.Magic);
if (target is BattleNpc)
((BattleNpc)target).lastAttacker = this;
lua.LuaEngine.GetInstance().OnSignal("spellUsed");
}
public virtual void OnWeaponSkill(State state, BattleAction[] actions, ref BattleAction[] errors)
@ -666,11 +700,11 @@ namespace FFXIVClassic_Map_Server.Actors
// damage is handled in script
this.DelTP(skill.tpCost);
foreach (var action in actions)
zone.FindActorInArea<BattleNpc>(action.targetId)?.OnDamageTaken(this, action, DamageTakenType.Weaponskill);
foreach (BattleAction action in actions)
if (zone.FindActorInArea<Character>(action.targetId) is Character chara)
BattleUtils.DamageTarget(this, chara, action, DamageTakenType.Weaponskill);
if (target is BattleNpc)
((BattleNpc)target).lastAttacker = this;
lua.LuaEngine.GetInstance().OnSignal("weaponskillUsed");
}
public virtual void OnAbility(State state, BattleAction[] actions, ref BattleAction[] errors)
@ -756,22 +790,22 @@ namespace FFXIVClassic_Map_Server.Actors
public bool IsDiscipleOfWar()
{
return currentJob < CLASSID_THM;
return GetClass() < CLASSID_THM;
}
public bool IsDiscipleOfMagic()
{
return currentJob >= CLASSID_THM && currentJob < CLASSID_CRP;
return GetClass() >= CLASSID_THM && currentJob < CLASSID_CRP;
}
public bool IsDiscipleOfHand()
{
return currentJob >= CLASSID_CRP && currentJob < CLASSID_MIN;
return GetClass() >= CLASSID_CRP && currentJob < CLASSID_MIN;
}
public bool IsDiscipleOfLand()
{
return currentJob >= CLASSID_MIN;
return GetClass() >= CLASSID_MIN;
}
#endregion lua helpers
#endregion ai stuff

View file

@ -5,6 +5,7 @@ using System.Text;
using System.Threading.Tasks;
using FFXIVClassic_Map_Server.Actors;
using FFXIVClassic_Map_Server.actors.chara.npc;
using FFXIVClassic.Common;
namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
{
@ -17,10 +18,11 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
{
this.owner = owner;
}
// server really likes to hang whenever scripts iterate area's actorlist
protected override void DoCombatTick(DateTime tick, List<Character> contentGroupCharas = null)
protected List<Character> GetContentGroupCharas()
{
List<Character> contentGroupCharas = null;
if (owner.currentContentGroup != null)
{
contentGroupCharas = new List<Character>(owner.currentContentGroup.GetMemberCount());
@ -32,20 +34,46 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
contentGroupCharas.Add(chara);
}
}
return contentGroupCharas;
}
//Iterate over players in the group and if they are fighting, assist them
protected override void TryAggro(DateTime tick)
{
//lua.LuaEngine.CallLuaBattleFunction(owner, "tryAggro", owner, GetContentGroupCharas());
foreach(Character chara in GetContentGroupCharas())
{
if(chara.IsPlayer())
{
if(owner.aiContainer.GetTargetFind().CanTarget((Character) chara.target) && chara.target is BattleNpc && ((BattleNpc)chara.target).hateContainer.HasHateForTarget(chara))
{
owner.Engage(chara.target.actorId);
owner.hateContainer.AddBaseHate((Character) chara.target);
break;
}
}
}
//base.TryAggro(tick);
}
// server really likes to hang whenever scripts iterate area's actorlist
protected override void DoCombatTick(DateTime tick, List<Character> contentGroupCharas = null)
{
if (contentGroupCharas == null)
{
contentGroupCharas = GetContentGroupCharas();
}
base.DoCombatTick(tick, contentGroupCharas);
}
protected override void DoRoamTick(DateTime tick, List<Character> contentGroupCharas = null)
{
if (owner.currentContentGroup != null)
if (contentGroupCharas == null)
{
contentGroupCharas = new List<Character>(owner.currentContentGroup.GetMemberCount());
foreach (var charaId in owner.currentContentGroup.GetMembers())
{
var chara = owner.zone.FindActorInArea<Character>(charaId);
if (chara != null)
contentGroupCharas.Add(chara);
}
contentGroupCharas = GetContentGroupCharas();
}
base.DoRoamTick(tick, contentGroupCharas);
}

View file

@ -39,12 +39,20 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
public override void Update(DateTime tick)
{
lastUpdate = tick;
// todo: handle aggro/deaggro and other shit here
if (owner.aiContainer.IsEngaged())
if (!owner.aiContainer.IsEngaged())
{
DoCombatTick(tick);
TryAggro(tick);
}
else if (!owner.IsDead())
if(owner.aiContainer.IsEngaged())
{
//DoCombatTick(tick);
}
//Only move if owner isn't dead and is either too far away from their spawn point or is meant to roam
if (!owner.IsDead() && (owner.isMovingToSpawn || owner.GetMobMod((uint) MobModifier.Roams) > 0))
{
DoRoamTick(tick);
}
@ -63,6 +71,38 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
return false;
}
//If the owner isn't moving to spawn, iterate over nearby enemies and
//aggro the first one that is within 10 levels and can be detected, then engage
protected virtual void TryAggro(DateTime tick)
{
if (tick >= neutralTime && !owner.isMovingToSpawn)
{
if (!owner.neutral && owner.IsAlive())
{
foreach (var chara in owner.zone.GetActorsAroundActor<Character>(owner, 50))
{
if (owner.allegiance == chara.allegiance)
continue;
if (owner.aiContainer.pathFind.AtPoint() && owner.detectionType != DetectionType.None)
{
uint levelDifference = (uint)Math.Abs(owner.GetLevel() - chara.GetLevel());
if (levelDifference <= 10 || (owner.detectionType & DetectionType.IgnoreLevelDifference) != 0 && CanAggroTarget(chara))
{
owner.hateContainer.AddBaseHate(chara);
break;
}
}
}
}
}
if (owner.hateContainer.GetHateList().Count > 0)
{
Engage(owner.hateContainer.GetMostHatedTarget());
}
}
public override bool Engage(Character target)
{
var canEngage = this.owner.aiContainer.InternalEngage(target);
@ -77,6 +117,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
owner.ChangeState(SetActorStatePacket.MAIN_STATE_ACTIVE);
owner.moveState = 2;
//owner.SetMod((uint)Modifier.Speed, 5);
lastActionTime = DateTime.Now;
battleStartTime = lastActionTime;
// todo: adjust cooldowns with modifiers
@ -127,12 +168,6 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
protected virtual void DoRoamTick(DateTime tick, List<Character> contentGroupCharas = null)
{
if (owner.hateContainer.GetHateList().Count > 0)
{
Engage(owner.hateContainer.GetMostHatedTarget());
return;
}
if (tick >= waitTime)
{
neutralTime = tick.AddSeconds(5);
@ -148,7 +183,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
}
}
waitTime = tick.AddSeconds(10);
waitTime = tick.AddSeconds(owner.GetMobMod((uint) MobModifier.RoamDelay));
owner.OnRoam(tick);
if (CanMoveForward(0.0f) && !owner.aiContainer.pathFind.IsFollowingPath())
@ -157,31 +192,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
owner.aiContainer.pathFind.SetPathFlags(PathFindFlags.None);
owner.aiContainer.pathFind.PathInRange(owner.spawnX, owner.spawnY, owner.spawnZ, 1.5f, 50.0f);
}
lua.LuaEngine.CallLuaBattleFunction(owner, "onRoam", owner, contentGroupCharas);
}
if (tick >= neutralTime)
{
if (!owner.neutral && owner.IsAlive())
{
foreach (var chara in owner.zone.GetActorsAroundActor<Character>(owner, 50))
{
if (owner.allegiance == chara.allegiance)
continue;
if (!owner.isMovingToSpawn && owner.aiContainer.pathFind.AtPoint() && owner.detectionType != DetectionType.None)
{
uint levelDifference = (uint)Math.Abs(owner.GetLevel() - chara.GetLevel());
if (levelDifference <= 10 || (owner.detectionType & DetectionType.IgnoreLevelDifference) != 0 && CanAggroTarget(chara))
{
owner.hateContainer.AddBaseHate(chara);
break;
}
}
}
}
//lua.LuaEngine.CallLuaBattleFunction(owner, "onRoam", owner, contentGroupCharas);
}
if (owner.aiContainer.pathFind.IsFollowingPath() && owner.aiContainer.CanFollowPath())
@ -200,7 +211,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
return;
}
Move();
if ((tick - lastCombatTickScript).TotalSeconds > 2)
{
lua.LuaEngine.CallLuaBattleFunction(owner, "onCombatTick", owner, owner.target, Utils.UnixTimeStampUTC(tick), contentGroupCharas);

View file

@ -159,6 +159,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
/// </summary>
public void FindWithinArea(Character target, ValidTarget flags, TargetFindAOETarget aoeTarget)
{
targets.Clear();
validTarget = flags;
// are we creating aoe circles around target or self
if (aoeTarget == TargetFindAOETarget.Self)
@ -174,59 +175,63 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
if (masterTarget != null)
targets.Add(masterTarget);
if (IsPlayer(owner))
if (aoeType != TargetFindAOEType.None)
{
if (masterTarget is Player)
if (IsPlayer(owner))
{
findType = TargetFindCharacterType.PlayerToPlayer;
if (masterTarget.currentParty != null)
if (masterTarget is Player)
{
if ((validTarget & (ValidTarget.Ally | ValidTarget.PartyMember)) != 0)
AddAllInAlliance(masterTarget, withPet);
findType = TargetFindCharacterType.PlayerToPlayer;
if (masterTarget.currentParty != null)
{
if ((validTarget & (ValidTarget.Ally | ValidTarget.PartyMember)) != 0)
AddAllInAlliance(masterTarget, withPet);
else
AddAllInParty(masterTarget, withPet);
}
else
AddAllInParty(masterTarget, withPet);
{
AddTarget(masterTarget, withPet);
}
}
else
{
AddTarget(masterTarget, withPet);
findType = TargetFindCharacterType.PlayerToBattleNpc;
AddAllBattleNpcs(masterTarget, false);
}
}
else
{
findType = TargetFindCharacterType.PlayerToBattleNpc;
AddAllBattleNpcs(masterTarget, false);
}
}
else
{
// todo: this needs checking..
if (masterTarget is Player || owner.allegiance == CharacterTargetingAllegiance.Player)
findType = TargetFindCharacterType.BattleNpcToPlayer;
else
findType = TargetFindCharacterType.BattleNpcToBattleNpc;
// todo: configurable pet aoe buff
if (findType == TargetFindCharacterType.BattleNpcToBattleNpc && TryGetMasterTarget(target) != null)
withPet = true;
// todo: does ffxiv have call for help flag?
//if ((findFlags & ValidTarget.HitAll) != 0)
//{
// AddAllInZone(masterTarget, withPet);
//}
AddAllInAlliance(target, withPet);
if (findType == TargetFindCharacterType.BattleNpcToPlayer)
{
if (owner.allegiance == CharacterTargetingAllegiance.Player)
AddAllInZone(masterTarget, withPet);
// todo: this needs checking..
if (masterTarget is Player || owner.allegiance == CharacterTargetingAllegiance.Player)
findType = TargetFindCharacterType.BattleNpcToPlayer;
else
AddAllInHateList();
findType = TargetFindCharacterType.BattleNpcToBattleNpc;
// todo: configurable pet aoe buff
if (findType == TargetFindCharacterType.BattleNpcToBattleNpc && TryGetMasterTarget(target) != null)
withPet = true;
// todo: does ffxiv have call for help flag?
//if ((findFlags & ValidTarget.HitAll) != 0)
//{
// AddAllInZone(masterTarget, withPet);
//}
AddAllInAlliance(target, withPet);
if (findType == TargetFindCharacterType.BattleNpcToPlayer)
{
if (owner.allegiance == CharacterTargetingAllegiance.Player)
AddAllInZone(masterTarget, withPet);
else
AddAllInHateList();
}
}
}
if(targets.Count > 16)
targets.RemoveRange(16, targets.Count - 16);
}
/// <summary>
@ -288,7 +293,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
private void AddAllBattleNpcs(Character target, bool withPet)
{
var actors = owner.zone.GetActorsAroundActor<BattleNpc>(owner, 50);
int dist = (int)maxDistance;
var actors = owner.zone.GetActorsAroundActor<BattleNpc>(target, dist);
foreach (BattleNpc actor in actors)
{
@ -375,12 +381,13 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
private bool IsWithinCircle(Character target, float maxDistance)
{
// todo: make y diff modifiable?
if (Math.Abs(owner.positionX - target.positionY) > 6.0f)
return false;
//if (Math.Abs(owner.positionX - target.positionY) > 6.0f)
// return false;
if (this.targetPosition == null)
this.targetPosition = aoeTarget == TargetFindAOETarget.Self ? owner.GetPosAsVector3() : masterTarget.GetPosAsVector3();
return target.GetPosAsVector3().IsWithinCircle(targetPosition, param);
return target.GetPosAsVector3().IsWithinCircle(targetPosition, maxDistance);
}
private bool IsPlayer(Character target)

View file

@ -129,6 +129,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
private bool CanAttack()
{
return false;
if (!owner.isAutoAttackEnabled)
{
return false;

View file

@ -15,7 +15,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
: base(owner, null)
{
owner.Disengage();
owner.ChangeState(SetActorStatePacket.MAIN_STATE_DEAD2);
//owner.ChangeState(SetActorStatePacket.MAIN_STATE_DEAD2);
var deathStatePacket = SetActorStatePacket.BuildPacket(owner.actorId, SetActorStatePacket.MAIN_STATE_DEAD, owner.currentSubState);
owner.zone.BroadcastPacketAroundActor(owner, deathStatePacket);
canInterrupt = false;
startTime = tick;
despawnTime = startTime.AddSeconds(timeToFadeOut);

View file

@ -110,16 +110,16 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
var i = 0;
foreach (var chara in targets)
{
var action = new BattleAction(target.actorId, spell.worldMasterTextId, spell.battleAnimation, 0, (byte)HitDirection.None, 1);
var action = new BattleAction(chara.actorId, spell.worldMasterTextId, spell.battleAnimation, 0, (byte)HitDirection.None, 1);
action.amount = (ushort)lua.LuaEngine.CallLuaBattleCommandFunction(owner, spell, "magic", "onMagicFinish", owner, chara, spell, action);
actions[i++] = action;
}
// todo: this is fuckin stupid, probably only need *one* error packet, not an error for each action
var errors = (BattleAction[])actions.Clone();
owner.OnCast(this, actions, ref errors);
owner.DoBattleAction(spell.id, spell.battleAnimation, actions);
}
public override void TryInterrupt()

View file

@ -88,7 +88,6 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
{
skill.targetFind.FindWithinArea(target, skill.validTarget, skill.aoeTarget);
isCompleted = true;
var targets = skill.targetFind.GetTargets();
BattleAction[] actions = new BattleAction[targets.Count];
@ -100,11 +99,11 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
// evasion, miss, dodge, etc to be handled in script, calling helpers from scripts/weaponskills.lua
action.amount = (ushort)lua.LuaEngine.CallLuaBattleCommandFunction(owner, skill, "weaponskill", "onSkillFinish", owner, target, skill, action);
actions[i++] = action;
chara.Engage(chara.actorId, 1);
}
// todo: this is fuckin stupid, probably only need *one* error packet, not an error for each action
var errors = (BattleAction[])actions.Clone();
owner.OnWeaponSkill(this, actions, ref errors);
owner.DoBattleAction(skill.id, skill.battleAnimation, actions);
}

View file

@ -76,18 +76,24 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
return damage;
}
public static void DamageTarget(Character attacker, Character defender, BattleAction action)
public static void DamageTarget(Character attacker, Character defender, BattleAction action, DamageTakenType type)
{
// todo: other stuff too
if (defender is BattleNpc)
if (defender != null)
{
if (!((BattleNpc)defender).hateContainer.HasHateForTarget(attacker))
// todo: other stuff too
if (defender is BattleNpc)
{
((BattleNpc)defender).hateContainer.AddBaseHate(attacker);
var bnpc = defender as BattleNpc;
if (!bnpc.hateContainer.HasHateForTarget(attacker))
{
bnpc.hateContainer.AddBaseHate(attacker);
}
bnpc.hateContainer.UpdateHate(attacker, action.amount);
bnpc.lastAttacker = attacker;
}
((BattleNpc)defender).hateContainer.UpdateHate(attacker, action.amount);
defender.DelHP((short)action.amount);
defender.OnDamageTaken(attacker, action, type);
}
defender.DelHP((short)action.amount);
}
public static int CalculateSpellDamage(Character attacker, Character defender, BattleCommand spell)

View file

@ -172,7 +172,7 @@ namespace FFXIVClassic_Map_Server.Actors
packets = new List<SubPacket>();
if ((updateFlags & ActorUpdateFlags.HpTpMp) != 0)
{
var propPacketUtil = new ActorPropertyPacketUtil("charaWork.parameterSave", this);
var propPacketUtil = new ActorPropertyPacketUtil("charaWork/stateAtQuicklyForAll", this);
propPacketUtil.AddProperty("charaWork.parameterSave.hp[0]");
propPacketUtil.AddProperty("charaWork.parameterSave.hpMax[0]");
@ -272,6 +272,10 @@ namespace FFXIVClassic_Map_Server.Actors
if (lastAttacker is Player)
{
//I think this is, or should be odne in DoBattleAction. Packet capture had the message in the same packet as an attack
// <actor> defeat/defeats <target>
//((Player)lastAttacker).QueuePacket(BattleActionX01Packet.BuildPacket(lastAttacker.actorId, 0, 0, new BattleAction(actorId, 30108, 0)));
if (lastAttacker.currentParty != null && lastAttacker.currentParty is Party)
{
foreach (var memberId in ((Party)lastAttacker.currentParty).members)
@ -279,14 +283,9 @@ namespace FFXIVClassic_Map_Server.Actors
var partyMember = zone.FindActorInArea<Player>(memberId);
// onDeath(monster, player, killer)
lua.LuaEngine.CallLuaBattleFunction(this, "onDeath", this, partyMember, lastAttacker);
// <actor> defeat/defeats <target>
if (lastAttacker is Player)
((Player)lastAttacker).QueuePacket(BattleActionX01Packet.BuildPacket(lastAttacker.actorId, 0, 0, new BattleAction(actorId, 30108, 0)));
if(partyMember is Player)
((Player)partyMember).AddExp(1500, (byte)partyMember.GetJob(), 5);
if (partyMember is Player)
((Player)partyMember).AddExp(1500, (byte)partyMember.GetClass(), 5);
}
}
else
@ -298,6 +297,7 @@ namespace FFXIVClassic_Map_Server.Actors
}
positionUpdates?.Clear();
aiContainer.InternalDie(tick, despawnTime);
//this.ResetMoveSpeeds();
// todo: reset cooldowns
lua.LuaEngine.GetInstance().OnSignal("mobkill");

View file

@ -32,5 +32,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.npc
AbilityScript = 21, // call my script's onAbility whenever i finish using an ability
CallForHelp = 22, // actor with this id outside of target's party with this can attack me
FreeForAll = 23, // any actor can attack me
Roams = 24, // Do I walk around?
RoamDelay = 25 // What is the delay between roam ticks
}
}

View file

@ -557,6 +557,9 @@ namespace FFXIVClassic_Map_Server.Actors
if (currentContentGroup != null)
currentContentGroup.SendGroupPackets(playerSession);
if (currentParty != null)
currentParty.SendGroupPackets(playerSession);
}
private void SendRemoveInventoryPackets(List<ushort> slots)
@ -1625,7 +1628,7 @@ namespace FFXIVClassic_Map_Server.Actors
//Update Instance
List<Actor> aroundMe = new List<Actor>();
if (zone != null)
if (zone != null)
aroundMe.AddRange(zone.GetActorsAroundActor(this, 50));
if (zone2 != null)
aroundMe.AddRange(zone2.GetActorsAroundActor(this, 50));
@ -1714,6 +1717,7 @@ namespace FFXIVClassic_Map_Server.Actors
//currentParty.members.Remove(this);
if (partyGroup.members.Count == 0)
Server.GetWorldManager().NoMembersInParty((Party)currentParty);
currentParty = null;
}
@ -1755,7 +1759,7 @@ namespace FFXIVClassic_Map_Server.Actors
if ((updateFlags & ActorUpdateFlags.HpTpMp) != 0)
{
var propPacketUtil = new ActorPropertyPacketUtil("charaWork.parameterSave", this);
var propPacketUtil = new ActorPropertyPacketUtil("charaWork/stateAtQuicklyForAll", this);
// todo: should this be using job as index?
propPacketUtil.AddProperty($"charaWork.parameterSave.hp[{0}]");
@ -1768,9 +1772,6 @@ namespace FFXIVClassic_Map_Server.Actors
}
base.PostUpdate(tick, packets);
SetActorPropetyPacket hpInfo = new SetActorPropetyPacket("charaWork/exp");
hpInfo.AddTarget();
QueuePacket(hpInfo.BuildPacket(actorId));
}
public override void Die(DateTime tick)
@ -1801,7 +1802,6 @@ namespace FFXIVClassic_Map_Server.Actors
public void UpdateHotbarCommands(List<ushort> slotsToUpdate)
{
ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil("charaWork/command", this);
ActorPropertyPacketUtil compatibiltyUtil = new ActorPropertyPacketUtil("charaWork/commandDetailForSelf", this);
foreach (ushort slot in slotsToUpdate)
{
propPacketUtil.AddProperty($"charaWork.command[{slot}]");
@ -2184,9 +2184,9 @@ namespace FFXIVClassic_Map_Server.Actors
{
// 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();
// todo: should just make a thing that updates the one slot cause this is dumb as hell
// 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");
}
@ -2309,27 +2309,23 @@ namespace FFXIVClassic_Map_Server.Actors
Database.LoadHotbar(this);
}
//Gets the id of the player's current job. If they aren't a job, gets the id of their class
public byte GetCurrentClassOrJob()
{
if (currentJob != 0)
return (byte) currentJob;
return charaWork.parameterSave.state_mainSkill[0];
}
public void hpstuff(uint hp)
{
SetMaxHP(hp);
SetHP(hp);
SetHP(hp);
mpMaxBase = (ushort)hp;
charaWork.parameterSave.mpMax = (short)hp;
charaWork.parameterSave.mp = (short)hp;
AddTP(0);
//SendCharaExpInfo();
//ActorPropertyPacketUtil exp = new ActorPropertyPacketUtil("charaWork/exp", this);
SetActorPropetyPacket hpInfo = new SetActorPropetyPacket("charaWork/exp");
hpInfo.AddTarget();
QueuePacket(hpInfo.BuildPacket(actorId));
AddTP(3000);
updateFlags |= ActorUpdateFlags.HpTpMp;
}
}

View file

@ -161,6 +161,9 @@ namespace FFXIVClassic_Map_Server.actors.director
{
members.Add(actor);
if (actor is Player)
((Player)actor).AddDirector(this);
if (contentGroup != null)
contentGroup.AddMember(actor);
}

View file

@ -42,6 +42,7 @@ namespace FFXIVClassic_Map_Server.actors.group
public void Start()
{
isStarted = true;
SendGroupPacketsAll(members);
}
@ -50,7 +51,8 @@ namespace FFXIVClassic_Map_Server.actors.group
if (actor == null)
return;
members.Add(actor.actorId);
if(!members.Contains(actor.actorId))
members.Add(actor.actorId);
if (actor is Character)
((Character)actor).SetCurrentContentGroup(this);
@ -121,7 +123,6 @@ namespace FFXIVClassic_Map_Server.actors.group
}
session.QueuePacket(GroupMembersEndPacket.buildPacket(session.id, session.GetActor().zoneId, time, this));
}
public override uint GetTypeId()

View file

@ -63,12 +63,24 @@ namespace FFXIVClassic_Map_Server.actors.group
List<GroupMember> groupMembers = new List<GroupMember>();
groupMembers.Add(new GroupMember(id, -1, 0, false, true, Server.GetWorldManager().GetActorInWorld(id).customDisplayName));
foreach (uint charaId in members)
{
if (charaId != id)
groupMembers.Add(new GroupMember(charaId, -1, 0, false, true, Server.GetWorldManager().GetActorInWorld(charaId).customDisplayName));
{
var chara = Server.GetWorldManager().GetActorInWorld(charaId);
if (charaId != id && chara != null)
groupMembers.Add(new GroupMember(charaId, -1, 0, false, true, chara.customDisplayName));
}
return groupMembers;
}
public void AddMember(uint memberId)
{
members.Add(memberId);
SendGroupPacketsAll(members);
}
public void RemoveMember(uint memberId)
{
members.Remove(memberId);
SendGroupPacketsAll(members);
}
}
}

View file

@ -24,7 +24,7 @@ namespace FFXIVClassic_Map_Server.dataobjects
public void FlushQueuedSendPackets()
{
if (!socket.Connected)
if (socket == null || !socket.Connected)
return;
while (SendPacketQueue.Count > 0)

View file

@ -478,10 +478,18 @@ namespace FFXIVClassic_Map_Server.lua
{
if (!script.Globals.Get(funcName).IsNil())
{
Coroutine coroutine = script.CreateCoroutine(script.Globals[funcName]).Coroutine;
DynValue value = coroutine.Resume(args2);
ResolveResume(player, coroutine, value);
try
{
Coroutine coroutine = script.CreateCoroutine(script.Globals[funcName]).Coroutine;
DynValue value = coroutine.Resume(args2);
ResolveResume(player, coroutine, value);
}
catch(Exception e)
{
player.SendMessage(0x20, "", e.Message);
player.EndEvent();
}
}
else
{

View file

@ -112,7 +112,6 @@ namespace FFXIVClassic_Map_Server.packets.send.actor
{
string[] split = name.Split('.');
int arrayIndex = 0;
if (!(split[0].Equals("work") || split[0].Equals("charaWork") || split[0].Equals("playerWork") || split[0].Equals("npcWork") || split[0].Equals("guildleveWork")))
return false;

View file

@ -35,33 +35,33 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
binWriter.Write((UInt16)commandId);
binWriter.Write((UInt16)0x810); //?
binWriter.Seek(0x58, SeekOrigin.Begin);
binWriter.Seek(0x28, SeekOrigin.Begin);
for (int i = 0; i < max; i++)
binWriter.Write((UInt32)actionList[listOffset + i].targetId);
binWriter.Seek(0xA0, SeekOrigin.Begin);
binWriter.Seek(0x70, SeekOrigin.Begin);
for (int i = 0; i < max; i++)
binWriter.Write((UInt16)actionList[listOffset + i].amount);
binWriter.Seek(0xC4, SeekOrigin.Begin);
binWriter.Seek(0x94, SeekOrigin.Begin);
for (int i = 0; i < max; i++)
binWriter.Write((UInt16)actionList[listOffset + i].worldMasterTextId);
binWriter.Seek(0xE8, SeekOrigin.Begin);
binWriter.Seek(0xB8, SeekOrigin.Begin);
for (int i = 0; i < max; i++)
binWriter.Write((UInt32)actionList[listOffset + i].effectId);
binWriter.Seek(0x130, SeekOrigin.Begin);
binWriter.Seek(0x100, SeekOrigin.Begin);
for (int i = 0; i < max; i++)
binWriter.Write((Byte)actionList[listOffset + i].param);
binWriter.Seek(0x142, SeekOrigin.Begin);
binWriter.Seek(0x112, SeekOrigin.Begin);
for (int i = 0; i < max; i++)
binWriter.Write((Byte)actionList[listOffset + i].unknown);
listOffset += max;
}
}
}
return new SubPacket(OPCODE, sourceActorId, data);
}