diff --git a/FFXIVClassic Map Server/actors/chara/Character.cs b/FFXIVClassic Map Server/actors/chara/Character.cs index 97e5caad..d128b66e 100644 --- a/FFXIVClassic Map Server/actors/chara/Character.cs +++ b/FFXIVClassic Map Server/actors/chara/Character.cs @@ -64,12 +64,15 @@ namespace FFXIVClassic_Map_Server.Actors public DateTime lastAiUpdate; public AIContainer aiContainer; + public StatusEffects statusEffects; public Character(uint actorID) : base(actorID) { //Init timer array to "notimer" for (int i = 0; i < charaWork.statusShownTime.Length; i++) charaWork.statusShownTime[i] = 0xFFFFFFFF; + + this.statusEffects = new StatusEffects(this); } public SubPacket CreateAppearancePacket(uint playerActorId) diff --git a/FFXIVClassic Map Server/actors/chara/ai/AIContainer.cs b/FFXIVClassic Map Server/actors/chara/ai/AIContainer.cs index 87e1f5e9..c2a77edb 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/AIContainer.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/AIContainer.cs @@ -45,6 +45,14 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai // todo: action queues controller?.Update(tick); + State currState; + while (states.Count > 0 && (currState = states.Peek()).Update(tick)) + { + if (currState == GetCurrentState()) + { + + } + } } public void CheckCompletedStates() diff --git a/FFXIVClassic Map Server/actors/chara/ai/PathFind.cs b/FFXIVClassic Map Server/actors/chara/ai/PathFind.cs index 80a6a41c..f77e9df2 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/PathFind.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/PathFind.cs @@ -4,6 +4,10 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using FFXIVClassic_Map_Server.Actors; +using FFXIVClassic_Map_Server; +using FFXIVClassic_Map_Server.utils; +using FFXIVClassic.Common; +using FFXIVClassic_Map_Server.actors.area; namespace FFXIVClassic_Map_Server.actors.chara.ai { @@ -15,5 +19,49 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai { this.owner = owner; } + + // todo: is this class even needed? + public void PathTo(float x, float y, float z, float stepSize = 0.70f, int maxPath = 40, float polyRadius = 0.0f) + { + var pos = new Vector3(owner.positionX, owner.positionY, owner.positionZ); + var dest = new Vector3(x, y, z); + var zone = (Zone)owner.GetZone(); + var sw = new System.Diagnostics.Stopwatch(); + sw.Start(); + + var path = NavmeshUtils.GetPath(zone, pos, dest, stepSize, maxPath, polyRadius); + + if (path != null) + { + if (owner.oldPositionX == 0.0f && owner.oldPositionY == 0.0f && owner.oldPositionZ == 0.0f) + { + owner.oldPositionX = owner.positionX; + owner.oldPositionY = owner.positionY; + owner.oldPositionZ = owner.positionZ; + } + + // todo: something went wrong + if (path.Count == 0) + { + owner.positionX = owner.oldPositionX; + owner.positionY = owner.oldPositionY; + owner.positionZ = owner.oldPositionZ; + } + + owner.positionUpdates = path; + + owner.hasMoved = true; + owner.isAtSpawn = false; + + sw.Stop(); + zone.pathCalls++; + zone.pathCallTime += sw.ElapsedMilliseconds; + + if (path.Count == 1) + Program.Log.Info($"mypos: {owner.positionX} {owner.positionY} {owner.positionZ} | targetPos: {x} {y} {z} | step {stepSize} | maxPath {maxPath} | polyRadius {polyRadius}"); + + Program.Log.Error("[{0}][{1}] Created {2} points in {3} milliseconds", owner.actorId, owner.actorName, path.Count, sw.ElapsedMilliseconds); + } + } } } diff --git a/FFXIVClassic Map Server/actors/chara/ai/state/AttackState.cs b/FFXIVClassic Map Server/actors/chara/ai/state/AttackState.cs index 84f04227..8f5b8931 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/state/AttackState.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/state/AttackState.cs @@ -4,7 +4,8 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using FFXIVClassic_Map_Server.Actors; - +using FFXIVClassic_Map_Server.packets.send.actor; +using FFXIVClassic_Map_Server.packets.send.actor.battle; namespace FFXIVClassic_Map_Server.actors.chara.ai.state { class AttackState : State @@ -21,24 +22,84 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state } - public override void Update(DateTime time) + public override bool Update(DateTime tick) { + TryInterrupt(); + if (interrupt) + { + OnInterrupt(); + return true; + } + + // todo: check weapon delay/haste etc and use that + if ((tick - startTime).Milliseconds >= 0) + { + OnComplete(); + return true; + } + return false; } public override void OnInterrupt() { - + // todo: send paralyzed/sleep message etc. } public override void OnComplete() { + var damage = FFXIVClassic_Map_Server.actors.chara.ai.utils.AttackUtils.CalculateDamage(owner, target); + + lua.LuaEngine.GetInstance().CallLuaFunction(owner, target, "onAttack", false, damage); + + //var packet = BattleAction1Packet.BuildPacket(owner.actorId, target.actorId); + + // todo: find a better place to put this? + if (owner.GetState() != SetActorStatePacket.MAIN_STATE_ACTIVE) + owner.ChangeState(SetActorStatePacket.MAIN_STATE_ACTIVE); + isCompleted = true; } public override void TryInterrupt() { - + if (owner.statusEffects.HasStatusEffectsByFlag((uint)StatusEffectFlags.PreventAction)) + { + // todo: sometimes paralyze can let you attack, get random percentage of actually letting you attack + var list = owner.statusEffects.GetStatusEffectsByFlag((uint)StatusEffectFlags.PreventAction); + uint effectId = 0; + if (list.Count > 0) + { + // todo: actually check proc rate/random chance of whatever effect + effectId = list[0].GetEffectId(); + } + this.errorPacket = BattleActionX01Packet.BuildPacket(target.actorId, owner.actorId, target.actorId, 0, effectId, 0, 0, 0, 0); + owner.zone.BroadcastPacketAroundActor(owner, errorPacket); + errorPacket = null; + interrupt = true; + return; + } + else if (target.zone != owner.zone) + { + interrupt = true; + return; + } + else if (owner.aiContainer.IsDead()) + { + // todo: this really shouldnt ever hit since we'd be clearing states on death + interrupt = true; + return; + } + interrupt = CanAttack(); + } + + private bool CanAttack() + { + if (target.aiContainer.IsDead()) + { + return false; + } + return true; } } } diff --git a/FFXIVClassic Map Server/actors/chara/ai/state/State.cs b/FFXIVClassic Map Server/actors/chara/ai/state/State.cs index f3f1cd42..14cd49c8 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/state/State.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/state/State.cs @@ -30,7 +30,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state this.interrupt = false; } - public virtual void Update(DateTime tick) { } + public virtual bool Update(DateTime tick) { return true; } public virtual void OnStart() { } public virtual void OnInterrupt() { } public virtual void OnComplete() { isCompleted = true; } @@ -54,5 +54,10 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state return isCompleted; } + public void ChangeTarget(Character target) + { + this.target = target; + } + } } diff --git a/FFXIVClassic Map Server/actors/chara/ai/utils/AttackUtils.cs b/FFXIVClassic Map Server/actors/chara/ai/utils/AttackUtils.cs index 77071406..36d5bcde 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/utils/AttackUtils.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/utils/AttackUtils.cs @@ -16,7 +16,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils } public static int CalculateBaseDamage(Character attacker, Character defender) { - return 0; + // todo: actually calculate damage + return Program.Random.Next(10) * 10; } } -} +} \ No newline at end of file diff --git a/FFXIVClassic Map Server/actors/chara/npc/Mob.cs b/FFXIVClassic Map Server/actors/chara/npc/Mob.cs index 73b227f6..5c89488e 100644 --- a/FFXIVClassic Map Server/actors/chara/npc/Mob.cs +++ b/FFXIVClassic Map Server/actors/chara/npc/Mob.cs @@ -16,7 +16,6 @@ namespace FFXIVClassic_Map_Server.Actors class Mob : Npc { public HateContainer hateContainer; - public StatusEffects statusEffects; public Mob(int actorNumber, ActorClass actorClass, string uniqueId, Area spawnedArea, float posX, float posY, float posZ, float rot, ushort actorState, uint animationId, string customDisplayName) @@ -25,7 +24,6 @@ namespace FFXIVClassic_Map_Server.Actors this.aiContainer = new AIContainer(this, new MobController(this), new PathFind(this), new TargetFind(this)); this.currentSubState = SetActorStatePacket.SUB_STATE_MONSTER; this.hateContainer = new HateContainer(this); - this.statusEffects = new StatusEffects(this); } } } diff --git a/FFXIVClassic Map Server/actors/chara/player/Player.cs b/FFXIVClassic Map Server/actors/chara/player/Player.cs index 63eb4845..b5460784 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Player.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Player.cs @@ -141,8 +141,6 @@ namespace FFXIVClassic_Map_Server.Actors public Session playerSession; - public StatusEffects statusEffects; - public Player(Session cp, uint actorID) : base(actorID) { playerSession = cp; @@ -253,7 +251,6 @@ namespace FFXIVClassic_Map_Server.Actors lastPlayTimeUpdate = Utils.UnixTimeStampUTC(); this.aiContainer = new AIContainer(this, new PlayerController(this), null, new TargetFind(this)); - this.statusEffects = new StatusEffects(this); } public List Create0x132Packets(uint playerActorId) diff --git a/FFXIVClassic Map Server/lua/LuaEngine.cs b/FFXIVClassic Map Server/lua/LuaEngine.cs index 65f146d3..e24f87f4 100644 --- a/FFXIVClassic Map Server/lua/LuaEngine.cs +++ b/FFXIVClassic Map Server/lua/LuaEngine.cs @@ -320,40 +320,41 @@ namespace FFXIVClassic_Map_Server.lua return null; } - public void CallLuaFunction(Player player, Actor target, string funcName, bool optional, params object[] args) + public void CallLuaFunction(Actor actor, Actor target, string funcName, bool optional, params object[] args) { + bool isPlayer = actor is Player; //Need a seperate case for NPCs cause that child/parent thing. - if (target is Npc) + if (target is Npc && isPlayer) { - CallLuaFunctionNpc(player, (Npc)target, funcName, optional, args); + CallLuaFunctionNpc((Player)actor, (Npc)target, funcName, optional, args); return; } object[] args2 = new object[args.Length + 2]; Array.Copy(args, 0, args2, 2, args.Length); - args2[0] = player; + args2[0] = actor; args2[1] = target; string luaPath = GetScriptPath(target); LuaScript script = LoadScript(luaPath); if (script != null) { - if (!script.Globals.Get(funcName).IsNil()) + if (!script.Globals.Get(funcName).IsNil() && isPlayer) { Coroutine coroutine = script.CreateCoroutine(script.Globals[funcName]).Coroutine; DynValue value = coroutine.Resume(args2); - ResolveResume(player, coroutine, value); + ResolveResume((Player)actor, coroutine, value); } else { if (!optional) - SendError(player, String.Format("Could not find function '{0}' for actor {1}.", funcName, target.GetName())); + SendError((Player)actor, String.Format("Could not find function '{0}' for actor {1}.", funcName, target.GetName())); } } else { - if (!(target is Area) && !optional) - SendError(player, String.Format("Could not find script for actor {0}.", target.GetName())); + if (!(target is Area) && !optional && isPlayer) + SendError((Player)actor, String.Format("Could not find script for actor {0}.", target.GetName())); } } @@ -372,16 +373,18 @@ namespace FFXIVClassic_Map_Server.lua CallLuaFunction(player, target, "onEventStarted", false, LuaUtils.CreateLuaParamObjectList(lparams)); } - private DynValue ResolveResume(Player player, Coroutine coroutine, DynValue value) + private DynValue ResolveResume(Actor actor, Coroutine coroutine, DynValue value) { + var isPlayer = actor is Player; + if (value == null || value.IsVoid()) return value; - if (value.String != null && value.String.Equals("_WAIT_EVENT")) + if (isPlayer && value.String != null && value.String.Equals("_WAIT_EVENT")) { - GetInstance().AddWaitEventCoroutine(player, coroutine); + GetInstance().AddWaitEventCoroutine((Player)actor, coroutine); } - else if (value.Tuple != null && value.Tuple.Length >= 1 && value.Tuple[0].String != null) + else if (isPlayer && value.Tuple != null && value.Tuple.Length >= 1 && value.Tuple[0].String != null) { switch (value.Tuple[0].String) {