mirror of
https://bitbucket.org/Ioncannon/project-meteor-server.git
synced 2025-04-24 05:37:46 +00:00
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:
parent
9077c60b96
commit
ce5030acd1
18 changed files with 434 additions and 22 deletions
|
@ -109,6 +109,7 @@
|
|||
<Compile Include="actors\chara\Modifier.cs" />
|
||||
<Compile Include="actors\chara\npc\ActorClass.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\AetheryteWork.cs" />
|
||||
<Compile Include="actors\chara\npc\Pet.cs" />
|
||||
|
|
|
@ -154,6 +154,7 @@ namespace FFXIVClassic_Map_Server
|
|||
case 0x00CC:
|
||||
LockTargetPacket lockTarget = new LockTargetPacket(subpacket.data);
|
||||
session.GetActor().currentLockedTarget = lockTarget.actorID;
|
||||
// todo: this really needs figuring out..
|
||||
session.GetActor().isAutoAttackEnabled = lockTarget.otherVal == 0x00000040;
|
||||
|
||||
break;
|
||||
|
|
|
@ -25,6 +25,7 @@ using System.Diagnostics;
|
|||
using FFXIVClassic_Map_Server.actors.director;
|
||||
using FFXIVClassic_Map_Server.actors.chara.ai;
|
||||
using FFXIVClassic_Map_Server.actors.chara;
|
||||
using FFXIVClassic_Map_Server.Actors.Chara;
|
||||
|
||||
namespace FFXIVClassic_Map_Server
|
||||
{
|
||||
|
@ -427,7 +428,7 @@ namespace FFXIVClassic_Map_Server
|
|||
{
|
||||
conn.Open();
|
||||
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.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,
|
||||
|
@ -439,7 +440,7 @@ namespace FFXIVClassic_Map_Server
|
|||
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 bgr.zoneId = @zoneId GROUP BY bsl.bnpcIndex;
|
||||
WHERE bgr.zoneId = @zoneId GROUP BY bsl.bnpcId;
|
||||
";
|
||||
|
||||
var count = 0;
|
||||
|
@ -457,20 +458,19 @@ namespace FFXIVClassic_Map_Server
|
|||
int actorId = zone.GetActorCount() + 1;
|
||||
|
||||
// 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")),
|
||||
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");
|
||||
|
||||
// todo: set hateType to appropriate detectionType thing
|
||||
//battleNpc.npcWork.hateType
|
||||
battleNpc.charaWork.parameterSave.state_mainSkill[0] = reader.GetByte("currentJob");
|
||||
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);
|
||||
}
|
||||
|
||||
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.
|
||||
public void DoSeamlessZoneChange(Player player, uint destinationZoneId)
|
||||
{
|
||||
|
|
|
@ -25,9 +25,10 @@ namespace FFXIVClassic_Map_Server.Actors
|
|||
Name = 0x08,
|
||||
Appearance = 0x10,
|
||||
Speed = 0x20,
|
||||
Work = 0x40,
|
||||
|
||||
AllNpc = 0x2F,
|
||||
AllPlayer = 0x3F
|
||||
AllNpc = 0x6F,
|
||||
AllPlayer = 0x9F
|
||||
}
|
||||
|
||||
class Actor
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
RemoveActorFromZone(FindActorInZoneByUniqueID(uniqueId));
|
||||
|
|
|
@ -159,7 +159,6 @@ namespace FFXIVClassic_Map_Server.Actors
|
|||
ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil("charaWork/currentContentGroup", this);
|
||||
propPacketUtil.AddProperty("charaWork.currentContentGroup");
|
||||
zone.BroadcastPacketsAroundActor(this, propPacketUtil.Done());
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
target.OnDamageTaken(this, action);
|
||||
// todo: call onAttack/onDamageTaken
|
||||
target.DelHP(action.amount);
|
||||
if (target is BattleNpc)
|
||||
|
@ -605,6 +605,9 @@ namespace FFXIVClassic_Map_Server.Actors
|
|||
// damage is handled in script
|
||||
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)
|
||||
((BattleNpc)target).lastAttacker = this;
|
||||
}
|
||||
|
@ -615,6 +618,9 @@ 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);
|
||||
|
||||
if (target is BattleNpc)
|
||||
((BattleNpc)target).lastAttacker = this;
|
||||
}
|
||||
|
@ -623,6 +629,9 @@ namespace FFXIVClassic_Map_Server.Actors
|
|||
{
|
||||
if (target is BattleNpc)
|
||||
((BattleNpc)target).lastAttacker = this;
|
||||
|
||||
foreach (var action in actions)
|
||||
zone.FindActorInArea<BattleNpc>(action.targetId)?.OnDamageTaken(this, action);
|
||||
}
|
||||
|
||||
public virtual void OnSpawn()
|
||||
|
@ -638,6 +647,11 @@ namespace FFXIVClassic_Map_Server.Actors
|
|||
public virtual void OnDespawn()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void OnDamageTaken(Character attacker, BattleAction action)
|
||||
{
|
||||
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
|||
using FFXIVClassic_Map_Server.Actors;
|
||||
using MoonSharp;
|
||||
using MoonSharp.Interpreter;
|
||||
using FFXIVClassic_Map_Server.lua;
|
||||
|
||||
namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
{
|
||||
|
@ -15,8 +16,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
|||
public uint durationMs;
|
||||
public bool checkState;
|
||||
// todo: lua function
|
||||
Script script;
|
||||
LuaScript script;
|
||||
}
|
||||
|
||||
class ActionQueue
|
||||
{
|
||||
private Character owner;
|
||||
|
@ -27,7 +29,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
|||
|
||||
public ActionQueue(Character owner)
|
||||
{
|
||||
|
||||
this.owner = owner;
|
||||
actionQueue = new Queue<Action>();
|
||||
timerQueue = new Queue<Action>();
|
||||
}
|
||||
|
||||
public void PushAction(Action action)
|
||||
|
@ -45,5 +49,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
|||
|
||||
}
|
||||
|
||||
public void CheckAction(DateTime tick)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -363,8 +363,12 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
|
|||
public override void ChangeTarget(Character target)
|
||||
{
|
||||
owner.target = target;
|
||||
owner.currentLockedTarget = target != null ? target.actorId : 0xC0000000;
|
||||
owner.currentTarget = target != null ? target.actorId : 0xC0000000;
|
||||
owner.currentLockedTarget = 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -155,7 +155,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
|
|||
owner.aiContainer.ChangeTarget(null);
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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.group;
|
||||
using FFXIVClassic_Map_Server.packets.send;
|
||||
using FFXIVClassic_Map_Server.Actors.Chara;
|
||||
|
||||
namespace FFXIVClassic_Map_Server.Actors
|
||||
{
|
||||
|
@ -56,12 +57,13 @@ namespace FFXIVClassic_Map_Server.Actors
|
|||
private uint despawnTime;
|
||||
private uint respawnTime;
|
||||
private uint spawnDistance;
|
||||
|
||||
private uint bnpcId;
|
||||
public Character lastAttacker;
|
||||
|
||||
public uint spellListId, skillListId, dropListId;
|
||||
public Dictionary<uint, BattleCommand> skillList = 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,
|
||||
ushort actorState, uint animationId, string customDisplayName)
|
||||
|
@ -110,10 +112,43 @@ namespace FFXIVClassic_Map_Server.Actors
|
|||
subpackets.Add(CreateSetActorIconPacket());
|
||||
subpackets.Add(CreateIsZoneingPacket());
|
||||
subpackets.Add(CreateScriptBindPacket(player));
|
||||
subpackets.Add(GetHateTypePacket(player));
|
||||
}
|
||||
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()
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (IsAlive())
|
||||
|
@ -308,6 +360,25 @@ namespace FFXIVClassic_Map_Server.Actors
|
|||
base.OnAttack(state, action, ref error);
|
||||
// todo: move this somewhere else prolly and change based on model/appearance (so maybe in Character.cs instead)
|
||||
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()
|
||||
|
@ -325,5 +396,38 @@ namespace FFXIVClassic_Map_Server.Actors
|
|||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
36
FFXIVClassic Map Server/actors/chara/npc/MobModifier.cs
Normal file
36
FFXIVClassic Map Server/actors/chara/npc/MobModifier.cs
Normal 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
|
||||
}
|
||||
}
|
|
@ -414,6 +414,17 @@ namespace FFXIVClassic_Map_Server.Actors
|
|||
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()
|
||||
{
|
||||
base.OnSpawn();
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
{
|
||||
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 int pushCommandSub;
|
||||
public byte pushCommandPriority;
|
||||
|
|
|
@ -25,6 +25,7 @@ using FFXIVClassic_Map_Server.actors.chara.ai.controllers;
|
|||
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;
|
||||
|
||||
namespace FFXIVClassic_Map_Server.Actors
|
||||
{
|
||||
|
@ -2037,6 +2038,47 @@ namespace FFXIVClassic_Map_Server.Actors
|
|||
SendGameMessage(Server.GetWorldManager().GetActor(), 32549, 0x20);
|
||||
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)
|
||||
|
|
27
data/scripts/commands/weaponskill/fast_blade.lua
Normal file
27
data/scripts/commands/weaponskill/fast_blade.lua
Normal 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;
|
|
@ -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 -
|
||||
]]
|
||||
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;
|
||||
healAmount = baseAmount;
|
||||
|
||||
|
@ -18,19 +19,19 @@ function magic.HandleHealingMagic(caster, target, spell, action, modifierId, mul
|
|||
local mind = caster.GetMod(Modifier.Mind);
|
||||
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
|
||||
damage = baseAmount or math.random(1,10) * 10;
|
||||
|
||||
return damage;
|
||||
end;
|
||||
|
||||
function magic.HandleEvasion(caster, target, spell, action, modifierId)
|
||||
function magic.HandleEvasion(caster, target, spell, action, statId, modifierId)
|
||||
|
||||
return false;
|
||||
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
|
||||
-- todo: damage reduction
|
||||
|
|
42
data/scripts/weaponskill.lua
Normal file
42
data/scripts/weaponskill.lua
Normal 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;
|
|
@ -23,14 +23,14 @@ DROP TABLE IF EXISTS `server_battlenpc_spawn_locations`;
|
|||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
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 '',
|
||||
`groupId` int(10) unsigned NOT NULL,
|
||||
`positionX` float NOT NULL,
|
||||
`positionY` float NOT NULL,
|
||||
`positionZ` float NOT NULL,
|
||||
`rotation` float NOT NULL,
|
||||
PRIMARY KEY (`bnpcIndex`)
|
||||
PRIMARY KEY (`bnpcId`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
|
@ -56,4 +56,4 @@ commit;
|
|||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
/*!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
|
||||
|
|
Loading…
Add table
Reference in a new issue