added mob name colour update

- added mobmods (need to be loaded from db)
- added Zone.GetBattleNpcById
- added missing IsValidTarget check in AttackState
This commit is contained in:
Tahir Akhlaq 2017-09-10 03:41:58 +01:00
parent 9077c60b96
commit ce5030acd1
18 changed files with 434 additions and 22 deletions

View File

@ -109,6 +109,7 @@
<Compile Include="actors\chara\Modifier.cs" /> <Compile Include="actors\chara\Modifier.cs" />
<Compile Include="actors\chara\npc\ActorClass.cs" /> <Compile Include="actors\chara\npc\ActorClass.cs" />
<Compile Include="actors\chara\npc\BattleNpc.cs" /> <Compile Include="actors\chara\npc\BattleNpc.cs" />
<Compile Include="actors\chara\npc\MobModifier.cs" />
<Compile Include="actors\chara\npc\NpcWork.cs" /> <Compile Include="actors\chara\npc\NpcWork.cs" />
<Compile Include="actors\chara\AetheryteWork.cs" /> <Compile Include="actors\chara\AetheryteWork.cs" />
<Compile Include="actors\chara\npc\Pet.cs" /> <Compile Include="actors\chara\npc\Pet.cs" />

View File

@ -154,6 +154,7 @@ namespace FFXIVClassic_Map_Server
case 0x00CC: case 0x00CC:
LockTargetPacket lockTarget = new LockTargetPacket(subpacket.data); LockTargetPacket lockTarget = new LockTargetPacket(subpacket.data);
session.GetActor().currentLockedTarget = lockTarget.actorID; session.GetActor().currentLockedTarget = lockTarget.actorID;
// todo: this really needs figuring out..
session.GetActor().isAutoAttackEnabled = lockTarget.otherVal == 0x00000040; session.GetActor().isAutoAttackEnabled = lockTarget.otherVal == 0x00000040;
break; break;

View File

@ -25,6 +25,7 @@ using System.Diagnostics;
using FFXIVClassic_Map_Server.actors.director; using FFXIVClassic_Map_Server.actors.director;
using FFXIVClassic_Map_Server.actors.chara.ai; using FFXIVClassic_Map_Server.actors.chara.ai;
using FFXIVClassic_Map_Server.actors.chara; using FFXIVClassic_Map_Server.actors.chara;
using FFXIVClassic_Map_Server.Actors.Chara;
namespace FFXIVClassic_Map_Server namespace FFXIVClassic_Map_Server
{ {
@ -427,7 +428,7 @@ namespace FFXIVClassic_Map_Server
{ {
conn.Open(); conn.Open();
var query = @" var query = @"
SELECT bsl.groupId, bsl.positionX, bsl.positionY, bsl.positionZ, bsl.rotation, SELECT bsl.bnpcId, bsl.groupId, bsl.positionX, bsl.positionY, bsl.positionZ, bsl.rotation,
bgr.groupId, bgr.poolId, bgr.actorClassId, bgr.scriptName, bgr.minLevel, bgr.maxLevel, bgr.respawnTime, bgr.hp, bgr.mp, bgr.groupId, bgr.poolId, bgr.actorClassId, bgr.scriptName, bgr.minLevel, bgr.maxLevel, bgr.respawnTime, bgr.hp, bgr.mp,
bgr.dropListId, bgr.allegiance, bgr.spawnType, bgr.animationId, bgr.actorState, bgr.privateAreaName, bgr.privateAreaLevel, bgr.zoneId, bgr.dropListId, bgr.allegiance, bgr.spawnType, bgr.animationId, bgr.actorState, bgr.privateAreaName, bgr.privateAreaLevel, bgr.zoneId,
bpo.poolId, bpo.genusId, bpo.currentJob, bpo.combatSkill, bpo.combatDelay, bpo.combatDmgMult, bpo.aggroType, bpo.poolId, bpo.genusId, bpo.currentJob, bpo.combatSkill, bpo.combatDelay, bpo.combatDmgMult, bpo.aggroType,
@ -439,7 +440,7 @@ namespace FFXIVClassic_Map_Server
INNER JOIN server_battlenpc_groups bgr ON bsl.groupId = bgr.groupId INNER JOIN server_battlenpc_groups bgr ON bsl.groupId = bgr.groupId
INNER JOIN server_battlenpc_pools bpo ON bgr.poolId = bpo.poolId INNER JOIN server_battlenpc_pools bpo ON bgr.poolId = bpo.poolId
INNER JOIN server_battlenpc_genus bge ON bpo.genusId = bge.genusId INNER JOIN server_battlenpc_genus bge ON bpo.genusId = bge.genusId
WHERE bgr.zoneId = @zoneId GROUP BY bsl.bnpcIndex; WHERE bgr.zoneId = @zoneId GROUP BY bsl.bnpcId;
"; ";
var count = 0; var count = 0;
@ -457,20 +458,19 @@ namespace FFXIVClassic_Map_Server
int actorId = zone.GetActorCount() + 1; int actorId = zone.GetActorCount() + 1;
// todo: add to private areas, set up immunity, mob linking, // todo: add to private areas, set up immunity, mob linking,
// - load skill/spell/drop lists, set npcWork.hateType, // - load skill/spell/drop lists, set detection icon, load pool/family/group mods
var battleNpc = new BattleNpc(actorId, Server.GetWorldManager().GetActorClass(reader.GetUInt32("actorClassId")), var battleNpc = new BattleNpc(actorId, Server.GetWorldManager().GetActorClass(reader.GetUInt32("actorClassId")),
reader.GetString("scriptName"), zone, reader.GetFloat("positionX"), reader.GetFloat("positionY"), reader.GetFloat("positionZ"), reader.GetFloat("rotation"), reader.GetString("scriptName"), zone, reader.GetFloat("positionX"), reader.GetFloat("positionY"), reader.GetFloat("positionZ"), reader.GetFloat("rotation"),
reader.GetUInt16("actorState"), reader.GetUInt32("animationId"), ""); reader.GetUInt16("actorState"), reader.GetUInt32("animationId"), "");
battleNpc.SetBattleNpcId(reader.GetUInt32("bnpcId"));
battleNpc.neutral = reader.GetByte("aggroType") == 0; battleNpc.neutral = reader.GetByte("aggroType") == 0;
battleNpc.SetDetectionType(reader.GetUInt32("detection")); battleNpc.SetDetectionType(reader.GetUInt32("detection"));
battleNpc.kindredType = (KindredType)reader.GetUInt32("kindredId"); battleNpc.kindredType = (KindredType)reader.GetUInt32("kindredId");
battleNpc.npcSpawnType = (NpcSpawnType)reader.GetUInt32("spawnType"); battleNpc.npcSpawnType = (NpcSpawnType)reader.GetUInt32("spawnType");
// todo: set hateType to appropriate detectionType thing
//battleNpc.npcWork.hateType
battleNpc.charaWork.parameterSave.state_mainSkill[0] = reader.GetByte("currentJob"); battleNpc.charaWork.parameterSave.state_mainSkill[0] = reader.GetByte("currentJob");
battleNpc.charaWork.parameterSave.state_mainSkillLevel = (short)Program.Random.Next(reader.GetByte("minLevel"), reader.GetByte("maxLevel")); battleNpc.charaWork.parameterSave.state_mainSkillLevel = (short)Program.Random.Next(reader.GetByte("minLevel"), reader.GetByte("maxLevel"));
@ -527,6 +527,112 @@ namespace FFXIVClassic_Map_Server
z.SpawnAllActors(true); z.SpawnAllActors(true);
} }
public void SpawnBattleNpcById(uint id)
{
// todo: this is stupid duplicate code and really needs to die, think of a better way later
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
{
try
{
conn.Open();
var query = @"
SELECT bsl.bnpcId, bsl.groupId, bsl.positionX, bsl.positionY, bsl.positionZ, bsl.rotation,
bgr.groupId, bgr.poolId, bgr.actorClassId, bgr.scriptName, bgr.minLevel, bgr.maxLevel, bgr.respawnTime, bgr.hp, bgr.mp,
bgr.dropListId, bgr.allegiance, bgr.spawnType, bgr.animationId, bgr.actorState, bgr.privateAreaName, bgr.privateAreaLevel, bgr.zoneId,
bpo.poolId, bpo.genusId, bpo.currentJob, bpo.combatSkill, bpo.combatDelay, bpo.combatDmgMult, bpo.aggroType,
bpo.immunity, bpo.linkType, bpo.skillListId, bpo.spellListId,
bge.genusId, bge.modelSize, bge.kindredId, bge.detection, bge.hpp, bge.mpp, bge.tpp, bge.str, bge.vit, bge.dex,
bge.int, bge.mnd, bge.pie, bge.att, bge.acc, bge.def, bge.eva, bge.slash, bge.pierce, bge.h2h, bge.blunt,
bge.fire, bge.ice, bge.wind, bge.lightning, bge.earth, bge.water
FROM server_battlenpc_spawn_locations bsl
INNER JOIN server_battlenpc_groups bgr ON bsl.groupId = bgr.groupId
INNER JOIN server_battlenpc_pools bpo ON bgr.poolId = bpo.poolId
INNER JOIN server_battlenpc_genus bge ON bpo.genusId = bge.genusId
WHERE bsl.bnpcId = @bnpcId GROUP BY bsl.bnpcId;
";
var count = 0;
MySqlCommand cmd = new MySqlCommand(query, conn);
cmd.Parameters.AddWithValue("@bnpcId", id);
using (MySqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
var zone = Server.GetWorldManager().GetZone(reader.GetUInt16("zoneId"));
int actorId = zone.GetActorCount() + 1;
var bnpc = zone.GetBattleNpcById(id);
if (bnpc != null)
{
bnpc.ForceRespawn();
break;
}
// todo: add to private areas, set up immunity, mob linking,
// - load skill/spell/drop lists, set detection icon, load pool/family/group mods
var battleNpc = new BattleNpc(actorId, Server.GetWorldManager().GetActorClass(reader.GetUInt32("actorClassId")),
reader.GetString("scriptName"), zone, reader.GetFloat("positionX"), reader.GetFloat("positionY"), reader.GetFloat("positionZ"), reader.GetFloat("rotation"),
reader.GetUInt16("actorState"), reader.GetUInt32("animationId"), "");
battleNpc.SetBattleNpcId(reader.GetUInt32("bnpcId"));
battleNpc.neutral = reader.GetByte("aggroType") == 0;
battleNpc.SetDetectionType(reader.GetUInt32("detection"));
battleNpc.kindredType = (KindredType)reader.GetUInt32("kindredId");
battleNpc.npcSpawnType = (NpcSpawnType)reader.GetUInt32("spawnType");
battleNpc.charaWork.parameterSave.state_mainSkill[0] = reader.GetByte("currentJob");
battleNpc.charaWork.parameterSave.state_mainSkillLevel = (short)Program.Random.Next(reader.GetByte("minLevel"), reader.GetByte("maxLevel"));
battleNpc.allegiance = (CharacterTargetingAllegiance)reader.GetByte("allegiance");
// todo: setup private areas and other crap and
// set up rest of stat resists
battleNpc.SetMod((uint)Modifier.Hp, reader.GetUInt32("hp"));
battleNpc.SetMod((uint)Modifier.HpPercent, reader.GetUInt32("hpp"));
battleNpc.SetMod((uint)Modifier.Mp, reader.GetUInt32("mp"));
battleNpc.SetMod((uint)Modifier.MpPercent, reader.GetUInt32("mpp"));
battleNpc.SetMod((uint)Modifier.TpPercent, reader.GetUInt32("tpp"));
battleNpc.SetMod((uint)Modifier.Strength, reader.GetUInt32("str"));
battleNpc.SetMod((uint)Modifier.Vitality, reader.GetUInt32("vit"));
battleNpc.SetMod((uint)Modifier.Dexterity, reader.GetUInt32("dex"));
battleNpc.SetMod((uint)Modifier.Intelligence, reader.GetUInt32("int"));
battleNpc.SetMod((uint)Modifier.Mind, reader.GetUInt32("mnd"));
battleNpc.SetMod((uint)Modifier.Piety, reader.GetUInt32("pie"));
battleNpc.SetMod((uint)Modifier.Attack, reader.GetUInt32("att"));
battleNpc.SetMod((uint)Modifier.Accuracy, reader.GetUInt32("acc"));
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");
battleNpc.SetBattleNpcId(reader.GetUInt32("bnpcId"));
//battleNpc.SetMod((uint)Modifier.ResistFire, )
zone.AddActorToZone(battleNpc);
count++;
}
}
Program.Log.Info("WorldManager.SpawnBattleNpcById spawned BattleNpc {0}.", id);
}
catch (MySqlException e)
{
Program.Log.Error(e.ToString());
}
finally
{
conn.Dispose();
}
}
}
//Moves the actor to the new zone if exists. No packets are sent nor position changed. Merged zone is removed. //Moves the actor to the new zone if exists. No packets are sent nor position changed. Merged zone is removed.
public void DoSeamlessZoneChange(Player player, uint destinationZoneId) public void DoSeamlessZoneChange(Player player, uint destinationZoneId)
{ {

View File

@ -25,9 +25,10 @@ namespace FFXIVClassic_Map_Server.Actors
Name = 0x08, Name = 0x08,
Appearance = 0x10, Appearance = 0x10,
Speed = 0x20, Speed = 0x20,
Work = 0x40,
AllNpc = 0x2F, AllNpc = 0x6F,
AllPlayer = 0x3F AllPlayer = 0x9F
} }
class Actor class Actor

View File

@ -526,6 +526,16 @@ namespace FFXIVClassic_Map_Server.Actors
} }
} }
public BattleNpc GetBattleNpcById(uint id)
{
foreach (var bnpc in GetAllActors<BattleNpc>())
{
if (bnpc.GetBattleNpcId() == id)
return bnpc;
}
return null;
}
public void DespawnActor(string uniqueId) public void DespawnActor(string uniqueId)
{ {
RemoveActorFromZone(FindActorInZoneByUniqueID(uniqueId)); RemoveActorFromZone(FindActorInZoneByUniqueID(uniqueId));

View File

@ -159,7 +159,6 @@ namespace FFXIVClassic_Map_Server.Actors
ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil("charaWork/currentContentGroup", this); ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil("charaWork/currentContentGroup", this);
propPacketUtil.AddProperty("charaWork.currentContentGroup"); propPacketUtil.AddProperty("charaWork.currentContentGroup");
zone.BroadcastPacketsAroundActor(this, propPacketUtil.Done()); zone.BroadcastPacketsAroundActor(this, propPacketUtil.Done());
} }
public List<SubPacket> GetActorStatusPackets() public List<SubPacket> GetActorStatusPackets()
@ -593,6 +592,7 @@ 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); //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);
// todo: call onAttack/onDamageTaken // todo: call onAttack/onDamageTaken
target.DelHP(action.amount); target.DelHP(action.amount);
if (target is BattleNpc) if (target is BattleNpc)
@ -605,6 +605,9 @@ namespace FFXIVClassic_Map_Server.Actors
// damage is handled in script // damage is handled in script
this.DelMP(spell.mpCost); // mpCost can be set in script e.g. if caster has something for free spells this.DelMP(spell.mpCost); // 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);
if (target is BattleNpc) if (target is BattleNpc)
((BattleNpc)target).lastAttacker = this; ((BattleNpc)target).lastAttacker = this;
} }
@ -615,6 +618,9 @@ namespace FFXIVClassic_Map_Server.Actors
// damage is handled in script // damage is handled in script
this.DelTP(skill.tpCost); this.DelTP(skill.tpCost);
foreach (var action in actions)
zone.FindActorInArea<BattleNpc>(action.targetId)?.OnDamageTaken(this, action);
if (target is BattleNpc) if (target is BattleNpc)
((BattleNpc)target).lastAttacker = this; ((BattleNpc)target).lastAttacker = this;
} }
@ -623,6 +629,9 @@ namespace FFXIVClassic_Map_Server.Actors
{ {
if (target is BattleNpc) if (target is BattleNpc)
((BattleNpc)target).lastAttacker = this; ((BattleNpc)target).lastAttacker = this;
foreach (var action in actions)
zone.FindActorInArea<BattleNpc>(action.targetId)?.OnDamageTaken(this, action);
} }
public virtual void OnSpawn() public virtual void OnSpawn()
@ -638,6 +647,11 @@ namespace FFXIVClassic_Map_Server.Actors
public virtual void OnDespawn() public virtual void OnDespawn()
{ {
}
public virtual void OnDamageTaken(Character attacker, BattleAction action)
{
} }
#endregion #endregion
} }

View File

@ -6,6 +6,7 @@ using System.Threading.Tasks;
using FFXIVClassic_Map_Server.Actors; using FFXIVClassic_Map_Server.Actors;
using MoonSharp; using MoonSharp;
using MoonSharp.Interpreter; using MoonSharp.Interpreter;
using FFXIVClassic_Map_Server.lua;
namespace FFXIVClassic_Map_Server.actors.chara.ai namespace FFXIVClassic_Map_Server.actors.chara.ai
{ {
@ -15,8 +16,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
public uint durationMs; public uint durationMs;
public bool checkState; public bool checkState;
// todo: lua function // todo: lua function
Script script; LuaScript script;
} }
class ActionQueue class ActionQueue
{ {
private Character owner; private Character owner;
@ -27,7 +29,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
public ActionQueue(Character owner) public ActionQueue(Character owner)
{ {
this.owner = owner;
actionQueue = new Queue<Action>();
timerQueue = new Queue<Action>();
} }
public void PushAction(Action action) public void PushAction(Action action)
@ -45,5 +49,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
} }
public void CheckAction(DateTime tick)
{
}
} }
} }

View File

@ -363,8 +363,12 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
public override void ChangeTarget(Character target) public override void ChangeTarget(Character target)
{ {
owner.target = target; owner.target = target;
owner.currentLockedTarget = target != null ? target.actorId : 0xC0000000; owner.currentLockedTarget = target?.actorId ?? 0xC0000000;
owner.currentTarget = target != null ? target.actorId : 0xC0000000; owner.currentTarget = target?.actorId ?? 0xC0000000;
foreach (var player in owner.zone.GetActorsAroundActor<Player>(owner, 50))
player.QueuePacket(owner.GetHateTypePacket(player));
base.ChangeTarget(target); base.ChangeTarget(target);
} }
} }

View File

@ -155,7 +155,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
owner.aiContainer.ChangeTarget(null); owner.aiContainer.ChangeTarget(null);
return false; return false;
} }
else if (!owner.aiContainer.GetTargetFind().CanTarget(target, false, true)) else if (!owner.IsValidTarget(target, ValidTarget.Enemy) || !owner.aiContainer.GetTargetFind().CanTarget(target, false, true))
{ {
return false; return false;
} }

View File

@ -17,6 +17,7 @@ using FFXIVClassic_Map_Server.packets.send.actor.battle;
using FFXIVClassic_Map_Server.actors.chara.ai.utils; using FFXIVClassic_Map_Server.actors.chara.ai.utils;
using FFXIVClassic_Map_Server.actors.group; using FFXIVClassic_Map_Server.actors.group;
using FFXIVClassic_Map_Server.packets.send; using FFXIVClassic_Map_Server.packets.send;
using FFXIVClassic_Map_Server.Actors.Chara;
namespace FFXIVClassic_Map_Server.Actors namespace FFXIVClassic_Map_Server.Actors
{ {
@ -56,12 +57,13 @@ namespace FFXIVClassic_Map_Server.Actors
private uint despawnTime; private uint despawnTime;
private uint respawnTime; private uint respawnTime;
private uint spawnDistance; private uint spawnDistance;
private uint bnpcId;
public Character lastAttacker; public Character lastAttacker;
public uint spellListId, skillListId, dropListId; public uint spellListId, skillListId, dropListId;
public Dictionary<uint, BattleCommand> skillList = new Dictionary<uint, BattleCommand>(); public Dictionary<uint, BattleCommand> skillList = new Dictionary<uint, BattleCommand>();
public Dictionary<uint, BattleCommand> spellList = new Dictionary<uint, BattleCommand>(); public Dictionary<uint, BattleCommand> spellList = new Dictionary<uint, BattleCommand>();
private Dictionary<MobModifier, Int64> mobModifiers = new Dictionary<MobModifier, Int64>();
public BattleNpc(int actorNumber, ActorClass actorClass, string uniqueId, Area spawnedArea, float posX, float posY, float posZ, float rot, public BattleNpc(int actorNumber, ActorClass actorClass, string uniqueId, Area spawnedArea, float posX, float posY, float posZ, float rot,
ushort actorState, uint animationId, string customDisplayName) ushort actorState, uint animationId, string customDisplayName)
@ -110,10 +112,43 @@ namespace FFXIVClassic_Map_Server.Actors
subpackets.Add(CreateSetActorIconPacket()); subpackets.Add(CreateSetActorIconPacket());
subpackets.Add(CreateIsZoneingPacket()); subpackets.Add(CreateIsZoneingPacket());
subpackets.Add(CreateScriptBindPacket(player)); subpackets.Add(CreateScriptBindPacket(player));
subpackets.Add(GetHateTypePacket(player));
} }
return subpackets; return subpackets;
} }
public SubPacket GetHateTypePacket(Player player)
{
npcWork.hateType = 1;
if (player != null)
{
if (aiContainer.IsEngaged())
{
npcWork.hateType = 2;
}
if (player.actorId == this.currentLockedTarget)
{
npcWork.hateType = NpcWork.HATE_TYPE_ENGAGED_PARTY;
}
else if (player.currentParty != null)
{
foreach (var memberId in ((Party)player.currentParty).members)
{
if (this.currentLockedTarget == memberId)
{
npcWork.hateType = NpcWork.HATE_TYPE_ENGAGED_PARTY;
break;
}
}
}
}
var propPacketUtil = new ActorPropertyPacketUtil("npcWork", this);
propPacketUtil.AddProperty("npcWork.hateType");
return propPacketUtil.Done()[0];
}
public uint GetDetectionType() public uint GetDetectionType()
{ {
return (uint)detectionType; return (uint)detectionType;
@ -220,6 +255,23 @@ namespace FFXIVClassic_Map_Server.Actors
} }
} }
public void ForceRespawn()
{
base.Spawn(Program.Tick);
this.isMovingToSpawn = false;
this.ResetMoveSpeeds();
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();
updateFlags |= ActorUpdateFlags.AllNpc;
}
public override void Die(DateTime tick) public override void Die(DateTime tick)
{ {
if (IsAlive()) if (IsAlive())
@ -308,6 +360,25 @@ namespace FFXIVClassic_Map_Server.Actors
base.OnAttack(state, action, ref error); base.OnAttack(state, action, ref error);
// todo: move this somewhere else prolly and change based on model/appearance (so maybe in Character.cs instead) // todo: move this somewhere else prolly and change based on model/appearance (so maybe in Character.cs instead)
action.animation = 0x11001000; // (temporary) wolf anim action.animation = 0x11001000; // (temporary) wolf anim
if (GetMobMod((uint)MobModifier.AttackScript) != 0)
lua.LuaEngine.CallLuaBattleFunction(this, "onAttack", this, state.GetTarget(), action.amount);
}
public override void OnCast(State state, BattleAction[] actions, ref BattleAction[] errors)
{
base.OnCast(state, actions, ref errors);
}
public override void OnAbility(State state, BattleAction[] actions, ref BattleAction[] errors)
{
base.OnAbility(state, actions, ref errors);
}
public override void OnWeaponSkill(State state, BattleAction[] actions, ref BattleAction[] errors)
{
base.OnWeaponSkill(state, actions, ref errors);
} }
public override void OnSpawn() public override void OnSpawn()
@ -325,5 +396,38 @@ namespace FFXIVClassic_Map_Server.Actors
{ {
base.OnDespawn(); base.OnDespawn();
} }
public uint GetBattleNpcId()
{
return bnpcId;
}
public void SetBattleNpcId(uint id)
{
this.bnpcId = id;
}
public Int64 GetMobMod(uint mobModId)
{
Int64 res;
if (mobModifiers.TryGetValue((MobModifier)mobModId, out res))
return res;
return 0;
}
public void SetMobMod(uint mobModId, Int64 val)
{
if (mobModifiers.ContainsKey((MobModifier)mobModId))
mobModifiers[(MobModifier)mobModId] = val;
else
mobModifiers.Add((MobModifier)mobModId, val);
}
public override void OnDamageTaken(Character attacker, BattleAction action)
{
if (GetMobMod((uint)MobModifier.DefendScript) != 0)
lua.LuaEngine.CallLuaBattleFunction(this, "onDamageTaken", this, attacker, action.amount);
}
} }
} }

View File

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.actors.chara.npc
{
enum MobModifier
{
None = 0,
SpawnLeash = 1, // how far can i move before i deaggro target
SightRange = 2, // how close does target need to be for me to detect by sight
SoundRange = 3, // how close does target need to be for me to detect by sound
BuffChance = 4,
HealChance = 5,
SkillUseChance = 6,
LinkRadius = 7,
MagicDelay = 8,
SpecialDelay = 9,
ExpBonus = 10, //
IgnoreSpawnLeash = 11, // pursue target forever
DrawIn = 12, // do i suck people in around me
HpScale = 13, //
Assist = 14, // gotta call the bois
NoMove = 15, // cant move
ShareTarget = 16, // use this actor's id as target id
AttackScript = 17, // call my script's onAttack whenever i attack
DefendScript = 18, // call my script's onDamageTaken whenever i take damage
SpellScript = 19, // call my script's onSpellCast whenever i finish casting
WeaponskillScript = 20, // call my script's onWeaponSkill whenever i finish using a weaponskill
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
}
}

View File

@ -414,6 +414,17 @@ namespace FFXIVClassic_Map_Server.Actors
aiContainer.Update(tick); aiContainer.Update(tick);
} }
public override void PostUpdate(DateTime tick, List<SubPacket> packets = null)
{
packets = packets ?? new List<SubPacket>();
if ((updateFlags & ActorUpdateFlags.Work) != 0)
{
}
base.PostUpdate(tick, packets);
}
public override void OnSpawn() public override void OnSpawn()
{ {
base.OnSpawn(); base.OnSpawn();

View File

@ -2,6 +2,10 @@
{ {
class NpcWork class NpcWork
{ {
public static byte HATE_TYPE_NONE = 0;
public static byte HATE_TYPE_ENGAGED = 2;
public static byte HATE_TYPE_ENGAGED_PARTY = 3;
public ushort pushCommand; public ushort pushCommand;
public int pushCommandSub; public int pushCommandSub;
public byte pushCommandPriority; public byte pushCommandPriority;

View File

@ -25,6 +25,7 @@ using FFXIVClassic_Map_Server.actors.chara.ai.controllers;
using FFXIVClassic_Map_Server.packets.send.actor.battle; using FFXIVClassic_Map_Server.packets.send.actor.battle;
using FFXIVClassic_Map_Server.actors.chara.ai.utils; using FFXIVClassic_Map_Server.actors.chara.ai.utils;
using FFXIVClassic_Map_Server.actors.chara.ai.state; using FFXIVClassic_Map_Server.actors.chara.ai.state;
using FFXIVClassic_Map_Server.actors.chara.npc;
namespace FFXIVClassic_Map_Server.Actors namespace FFXIVClassic_Map_Server.Actors
{ {
@ -2037,6 +2038,47 @@ namespace FFXIVClassic_Map_Server.Actors
SendGameMessage(Server.GetWorldManager().GetActor(), 32549, 0x20); SendGameMessage(Server.GetWorldManager().GetActor(), 32549, 0x20);
return false; return false;
} }
bool partyEngaged = false;
// todo: replace with confrontation status effect? (see how dsp does it)
if (target.aiContainer.IsEngaged())
{
if (currentParty != null)
{
if (target is BattleNpc)
{
var helpingActorId = ((BattleNpc)target).GetMobMod((uint)MobModifier.CallForHelp);
partyEngaged = this.actorId == helpingActorId || (((BattleNpc)target).GetMobMod((uint)MobModifier.FreeForAll) != 0);
}
if (!partyEngaged)
{
foreach (var memberId in ((Party)currentParty).members)
{
if (memberId == target.currentLockedTarget)
{
partyEngaged = true;
break;
}
}
}
}
else if (target.currentLockedTarget == actorId)
{
partyEngaged = true;
}
}
else
{
partyEngaged = true;
}
if (!partyEngaged)
{
// That target is already engaged.
SendGameMessage(Server.GetWorldManager().GetActor(), 32520, 0x20);
return false;
}
} }
if ((validTarget & ValidTarget.Ally) != 0 && target.allegiance != allegiance) if ((validTarget & ValidTarget.Ally) != 0 && target.allegiance != allegiance)

View File

@ -0,0 +1,27 @@
require("global");
require("weaponskill");
function onSkillPrepare(caster, target, spell)
return 0;
end;
function onSkillStart(caster, target, spell)
return 0;
end;
function onSkillFinish(caster, target, spell, action)
local damage = math.random(10, 100);
-- todo: populate a global script with statuses and modifiers
action.worldMasterTextId = 0x765D;
-- todo: populate a global script with statuses and modifiers
-- magic.HandleAttackSkill(caster, target, spell, action)
-- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636);
action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636);
if target.hateContainer then
target.hateContainer.UpdateHate(caster, damage);
end;
return damage;
end;

View File

@ -7,10 +7,11 @@ magic =
}; };
--[[ --[[
modifier - Modifier.Intelligence, Modifier.Mind (see Modifier.cs) statId - see BattleTemp.cs
modifierId - Modifier.Intelligence, Modifier.Mind (see Modifier.cs)
multiplier - multiplier -
]] ]]
function magic.HandleHealingMagic(caster, target, spell, action, modifierId, multiplier, baseAmount) function magic.HandleHealingMagic(caster, target, spell, action, statId, modifierId, multiplier, baseAmount)
potency = potency or 1.0; potency = potency or 1.0;
healAmount = baseAmount; healAmount = baseAmount;
@ -18,19 +19,19 @@ function magic.HandleHealingMagic(caster, target, spell, action, modifierId, mul
local mind = caster.GetMod(Modifier.Mind); local mind = caster.GetMod(Modifier.Mind);
end; end;
function magic.HandleAttackMagic(caster, target, spell, action, modifierId, multiplier, baseAmount) function magic.HandleAttackMagic(caster, target, spell, action, statId, modifierId, multiplier, baseAmount)
-- todo: actually handle this -- todo: actually handle this
damage = baseAmount or math.random(1,10) * 10; damage = baseAmount or math.random(1,10) * 10;
return damage; return damage;
end; end;
function magic.HandleEvasion(caster, target, spell, action, modifierId) function magic.HandleEvasion(caster, target, spell, action, statId, modifierId)
return false; return false;
end; end;
function magic.HandleStoneskin(caster, target, spell, action, modifierId, damage) function magic.HandleStoneskin(caster, target, spell, action, statId, modifierId, damage)
--[[ --[[
if target.statusEffects.HasStatusEffect(StatusEffect.Stoneskin) then if target.statusEffects.HasStatusEffect(StatusEffect.Stoneskin) then
-- todo: damage reduction -- todo: damage reduction

View File

@ -0,0 +1,42 @@
-- todo: add enums for status effects in global.lua
require("global")
weaponskill =
{
};
--[[
statId - see BattleTemp.cs
modifier - Modifier.Intelligence, Modifier.Mind (see Modifier.cs)
multiplier -
]]
function weaponskill.HandleHealingSkill(caster, target, spell, action, statId, modifierId, multiplier, baseAmount)
potency = potency or 1.0;
healAmount = baseAmount;
-- todo: shit based on mnd
local mind = caster.GetMod(Modifier.Mind);
end;
function weaponskill.HandleAttackSkill(caster, target, spell, action, statId, modifierId, multiplier, baseAmount)
-- todo: actually handle this
damage = baseAmount or math.random(1,10) * 10;
return damage;
end;
function weaponskill.HandleEvasion(caster, target, spell, action, statId, modifierId)
return false;
end;
function weaponskill.HandleStoneskin(caster, target, spell, action, statId, modifierId, damage)
--[[
if target.statusEffects.HasStatusEffect(StatusEffect.Stoneskin) then
-- todo: damage reduction
return true;
end;
]]
return false;
end;

View File

@ -23,14 +23,14 @@ DROP TABLE IF EXISTS `server_battlenpc_spawn_locations`;
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */; /*!40101 SET character_set_client = utf8 */;
CREATE TABLE `server_battlenpc_spawn_locations` ( CREATE TABLE `server_battlenpc_spawn_locations` (
`bnpcIndex` int(10) unsigned NOT NULL AUTO_INCREMENT, `bnpcId` int(10) unsigned NOT NULL AUTO_INCREMENT,
`customDisplayName` varchar(32) NOT NULL DEFAULT '', `customDisplayName` varchar(32) NOT NULL DEFAULT '',
`groupId` int(10) unsigned NOT NULL, `groupId` int(10) unsigned NOT NULL,
`positionX` float NOT NULL, `positionX` float NOT NULL,
`positionY` float NOT NULL, `positionY` float NOT NULL,
`positionZ` float NOT NULL, `positionZ` float NOT NULL,
`rotation` float NOT NULL, `rotation` float NOT NULL,
PRIMARY KEY (`bnpcIndex`) PRIMARY KEY (`bnpcId`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
@ -56,4 +56,4 @@ commit;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2017-09-07 21:54:41 -- Dump completed on 2017-09-10 2:47:43