mirror of
https://bitbucket.org/Ioncannon/project-meteor-server.git
synced 2025-04-02 19:42:05 -04:00
305 lines
10 KiB
C#
305 lines
10 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using FFXIVClassic.Common;
|
|
using FFXIVClassic_Map_Server.Actors;
|
|
using FFXIVClassic_Map_Server.actors.chara.npc;
|
|
using FFXIVClassic_Map_Server.actors;
|
|
using FFXIVClassic_Map_Server.actors.chara;
|
|
using FFXIVClassic_Map_Server.actors.chara.ai;
|
|
using FFXIVClassic_Map_Server.actors.chara.ai.controllers;
|
|
using FFXIVClassic_Map_Server.packets.send.actor;
|
|
using FFXIVClassic_Map_Server.actors.chara.ai.state;
|
|
using FFXIVClassic_Map_Server.utils;
|
|
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;
|
|
|
|
namespace FFXIVClassic_Map_Server.Actors
|
|
{
|
|
[Flags]
|
|
enum AggroType
|
|
{
|
|
None = 0x00,
|
|
Sight = 0x01,
|
|
Scent = 0x02,
|
|
LowHp = 0x04,
|
|
IgnoreLevelDifference = 0x08
|
|
}
|
|
|
|
class BattleNpc : Npc
|
|
{
|
|
public HateContainer hateContainer;
|
|
public AggroType aggroType;
|
|
public bool neutral;
|
|
private uint despawnTime;
|
|
private uint respawnTime;
|
|
private uint spawnDistance;
|
|
|
|
public Character lastAttacker;
|
|
|
|
public BattleNpc(int actorNumber, ActorClass actorClass, string uniqueId, Area spawnedArea, float posX, float posY, float posZ, float rot,
|
|
ushort actorState, uint animationId, string customDisplayName)
|
|
: base(actorNumber, actorClass, uniqueId, spawnedArea, posX, posY, posZ, rot, actorState, animationId, customDisplayName)
|
|
{
|
|
this.aiContainer = new AIContainer(this, new BattleNpcController(this), new PathFind(this), new TargetFind(this));
|
|
|
|
this.currentSubState = SetActorStatePacket.SUB_STATE_MONSTER;
|
|
//this.currentMainState = SetActorStatePacket.MAIN_STATE_ACTIVE;
|
|
|
|
//charaWork.property[2] = 1;
|
|
//npcWork.hateType = 1;
|
|
|
|
this.hateContainer = new HateContainer(this);
|
|
this.allegiance = CharacterTargetingAllegiance.BattleNpcs;
|
|
|
|
spawnX = posX;
|
|
spawnY = posY;
|
|
spawnZ = posZ;
|
|
|
|
// todo: read these from db also
|
|
aggroType = AggroType.Sight;
|
|
this.moveState = 2;
|
|
ResetMoveSpeeds();
|
|
despawnTime = 10;
|
|
respawnTime = 30;
|
|
CalculateBaseStats();
|
|
}
|
|
|
|
public override List<SubPacket> GetSpawnPackets(Player player, ushort spawnType)
|
|
{
|
|
List<SubPacket> subpackets = new List<SubPacket>();
|
|
if (IsAlive())
|
|
{
|
|
subpackets.Add(CreateAddActorPacket());
|
|
subpackets.AddRange(GetEventConditionPackets());
|
|
subpackets.Add(CreateSpeedPacket());
|
|
subpackets.Add(CreateSpawnPositonPacket(0x0));
|
|
|
|
subpackets.Add(CreateAppearancePacket());
|
|
|
|
subpackets.Add(CreateNamePacket());
|
|
subpackets.Add(CreateStatePacket());
|
|
subpackets.Add(CreateIdleAnimationPacket());
|
|
subpackets.Add(CreateInitStatusPacket());
|
|
subpackets.Add(CreateSetActorIconPacket());
|
|
subpackets.Add(CreateIsZoneingPacket());
|
|
subpackets.Add(CreateScriptBindPacket(player));
|
|
}
|
|
return subpackets;
|
|
}
|
|
|
|
public uint GetAggroType()
|
|
{
|
|
return (uint)aggroType;
|
|
}
|
|
|
|
public void SetAggroType(uint aggroType)
|
|
{
|
|
this.aggroType = (AggroType)aggroType;
|
|
}
|
|
|
|
public override void Update(DateTime tick)
|
|
{
|
|
this.aiContainer.Update(tick);
|
|
this.statusEffects.Update(tick);
|
|
}
|
|
|
|
public override void PostUpdate(DateTime tick, List<SubPacket> packets = null)
|
|
{
|
|
// todo: should probably add another flag for battleTemp since all this uses reflection
|
|
packets = new List<SubPacket>();
|
|
if ((updateFlags & ActorUpdateFlags.HpTpMp) != 0)
|
|
{
|
|
var propPacketUtil = new ActorPropertyPacketUtil("charaWork.parameterSave", this);
|
|
|
|
propPacketUtil.AddProperty("charaWork.parameterSave.hp[0]");
|
|
propPacketUtil.AddProperty("charaWork.parameterSave.hpMax[0]");
|
|
propPacketUtil.AddProperty("charaWork.parameterSave.state_mainSkill[0]");
|
|
propPacketUtil.AddProperty("charaWork.parameterSave.state_mainSkillLevel");
|
|
|
|
propPacketUtil.AddProperty("charaWork.battleTemp.castGauge_speed[0]");
|
|
propPacketUtil.AddProperty("charaWork.battleTemp.castGauge_speed[1]");
|
|
packets.AddRange(propPacketUtil.Done());
|
|
}
|
|
base.PostUpdate(tick, packets);
|
|
}
|
|
|
|
public override bool CanAttack()
|
|
{
|
|
// todo:
|
|
return true;
|
|
}
|
|
|
|
public override bool CanCast(Character target, BattleCommand spell)
|
|
{
|
|
// todo:
|
|
return false;
|
|
}
|
|
|
|
public override bool CanWeaponSkill(Character target, BattleCommand skill)
|
|
{
|
|
// todo:
|
|
return false;
|
|
}
|
|
|
|
public override bool CanUseAbility(Character target, BattleCommand ability)
|
|
{
|
|
// todo:
|
|
return false;
|
|
}
|
|
|
|
public uint GetDespawnTime()
|
|
{
|
|
return despawnTime;
|
|
}
|
|
|
|
public void SetDespawnTime(uint seconds)
|
|
{
|
|
despawnTime = seconds;
|
|
}
|
|
|
|
public uint GetRespawnTime()
|
|
{
|
|
return respawnTime;
|
|
}
|
|
|
|
public void SetRespawnTime(uint seconds)
|
|
{
|
|
respawnTime = seconds;
|
|
}
|
|
|
|
///<summary> // todo: create an action object? </summary>
|
|
public bool OnAttack(AttackState state)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public override void Spawn(DateTime tick)
|
|
{
|
|
if (respawnTime > 0)
|
|
{
|
|
base.Spawn(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();
|
|
|
|
updateFlags |= ActorUpdateFlags.AllNpc;
|
|
}
|
|
}
|
|
|
|
public override void Die(DateTime tick)
|
|
{
|
|
if (IsAlive())
|
|
{
|
|
if (lastAttacker is Pet && ((Pet)lastAttacker).master is Player)
|
|
{
|
|
lastAttacker = ((Pet)lastAttacker).master;
|
|
}
|
|
|
|
if (lastAttacker is Player)
|
|
{
|
|
if (lastAttacker.currentParty != null && lastAttacker.currentParty is Party)
|
|
{
|
|
foreach (var memberId in ((Party)lastAttacker.currentParty).members)
|
|
{
|
|
var partyMember = zone.FindActorInArea<Player>(memberId);
|
|
// onDeath(monster, player, killer)
|
|
lua.LuaEngine.CallLuaBattleFunction(this, "onDeath", this, partyMember, lastAttacker);
|
|
// <actor> defeat/defeats <target>
|
|
((Player)lastAttacker).QueuePacket(BattleActionX01Packet.BuildPacket(lastAttacker.actorId, 0, 0, new BattleAction(actorId, 30108, 0)));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// onDeath(monster, player, killer)
|
|
lua.LuaEngine.CallLuaBattleFunction(this, "onDeath", this, lastAttacker, lastAttacker);
|
|
((Player)lastAttacker).QueuePacket(BattleActionX01Packet.BuildPacket(lastAttacker.actorId, 0, 0, new BattleAction(actorId, 30108, 0)));
|
|
}
|
|
}
|
|
aiContainer.InternalDie(tick, despawnTime);
|
|
aiContainer.pathFind.Clear();
|
|
this.ResetMoveSpeeds();
|
|
|
|
// todo: reset cooldowns
|
|
}
|
|
else
|
|
{
|
|
var err = $"[{actorId}][{GetUniqueId()}] {positionX} {positionY} {positionZ} {GetZone().GetName()} tried to die ded";
|
|
Program.Log.Error(err);
|
|
//throw new Exception(err);
|
|
}
|
|
}
|
|
|
|
public override void Despawn(DateTime tick)
|
|
{
|
|
// todo: probably didnt need to make a new state...
|
|
aiContainer.InternalDespawn(tick, respawnTime);
|
|
lua.LuaEngine.CallLuaBattleFunction(this, "onDespawn", this);
|
|
this.isAtSpawn = true;
|
|
}
|
|
|
|
public void OnRoam(DateTime tick)
|
|
{
|
|
// todo: move this to battlenpccontroller..
|
|
bool foundActor = false;
|
|
|
|
// leash back to spawn
|
|
if (!IsCloseToSpawn())
|
|
{
|
|
if (!isMovingToSpawn)
|
|
{
|
|
aiContainer.Reset();
|
|
isMovingToSpawn = true;
|
|
}
|
|
else
|
|
{
|
|
if (target == null && !aiContainer.pathFind.IsFollowingPath())
|
|
aiContainer.pathFind.PathInRange(spawnX, spawnY, spawnZ, 1.5f, 15.0f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this.isMovingToSpawn = false;
|
|
lua.LuaEngine.CallLuaBattleFunction(this, "onRoam", this);
|
|
}
|
|
}
|
|
|
|
public bool IsCloseToSpawn()
|
|
{
|
|
return this.isAtSpawn = Utils.DistanceSquared(positionX, positionY, positionZ, spawnX, spawnY, spawnZ) <= 2500.0f;
|
|
}
|
|
|
|
public override void OnAttack(State state, BattleAction action, ref BattleAction 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)
|
|
action.animation = 0x11001000; // (temporary) wolf anim
|
|
}
|
|
|
|
public override void OnSpawn()
|
|
{
|
|
base.OnSpawn();
|
|
}
|
|
|
|
public override void OnDeath()
|
|
{
|
|
base.OnDeath();
|
|
}
|
|
|
|
public override void OnDespawn()
|
|
{
|
|
base.OnDespawn();
|
|
}
|
|
}
|
|
}
|