From 4978813c271ffffa6f304feff1af9aae5324ca98 Mon Sep 17 00:00:00 2001 From: Tahir Akhlaq Date: Sun, 3 Sep 2017 01:01:19 +0100 Subject: [PATCH] fixed death/despawning kinda (need to figure out why hp wont tick down after respawning) --- FFXIVClassic Map Server/actors/area/Area.cs | 12 +- .../actors/chara/Character.cs | 57 +++++++-- .../actors/chara/Modifier.cs | 4 +- .../actors/chara/ai/AIContainer.cs | 23 ++-- .../actors/chara/ai/StatusEffect.cs | 6 - .../ai/controllers/BattleNpcController.cs | 8 +- .../actors/chara/ai/state/AttackState.cs | 11 +- .../actors/chara/ai/state/DeathState.cs | 19 +-- .../actors/chara/ai/state/DespawnState.cs | 15 ++- .../actors/chara/npc/BattleNpc.cs | 115 +++++++++++++++--- .../actors/chara/npc/Npc.cs | 43 +++++-- .../actors/chara/npc/Pet.cs | 1 + .../actors/chara/player/Player.cs | 21 +--- FFXIVClassic Map Server/lua/LuaEngine.cs | 14 ++- FFXIVClassic Map Server/lua/LuaUtils.cs | 6 + .../directors/Quest/QuestDirectorMan0g001.lua | 2 +- 16 files changed, 251 insertions(+), 106 deletions(-) diff --git a/FFXIVClassic Map Server/actors/area/Area.cs b/FFXIVClassic Map Server/actors/area/Area.cs index 5d2f3ec1..b27ea4f3 100644 --- a/FFXIVClassic Map Server/actors/area/Area.cs +++ b/FFXIVClassic Map Server/actors/area/Area.cs @@ -401,6 +401,16 @@ namespace FFXIVClassic_Map_Server.Actors return GetAllActors(); } + public virtual List GetPlayers() + { + return GetAllActors(); + } + + public virtual List GetMonsters() + { + return GetAllActors(); + } + public void BroadcastPacketsAroundActor(Actor actor, List packets) { foreach (SubPacket packet in packets) @@ -635,7 +645,7 @@ namespace FFXIVClassic_Map_Server.Actors a.Update(tick); var deltaTime = (tick - Program.LastTick).TotalMilliseconds; - LuaEngine.GetInstance().CallLuaFunction(null, this, "onUpdate", true, deltaTime); + LuaEngine.GetInstance().CallLuaFunction(null, this, "onUpdate", true, deltaTime, this); } } diff --git a/FFXIVClassic Map Server/actors/chara/Character.cs b/FFXIVClassic Map Server/actors/chara/Character.cs index 444e22be..939f772d 100644 --- a/FFXIVClassic Map Server/actors/chara/Character.cs +++ b/FFXIVClassic Map Server/actors/chara/Character.cs @@ -288,7 +288,7 @@ namespace FFXIVClassic_Map_Server.Actors public virtual void OnPath(Vector3 point) { - lua.LuaEngine.CallLuaBattleAction(this, "onPath", this, point); + lua.LuaEngine.CallLuaBattleFunction(this, "onPath", this, point); updateFlags |= ActorUpdateFlags.Position; this.isAtSpawn = false; @@ -428,6 +428,7 @@ namespace FFXIVClassic_Map_Server.Actors public virtual void Spawn(DateTime tick) { + aiContainer.Reset(); // todo: reset hp/mp/tp etc here ChangeState(SetActorStatePacket.MAIN_STATE_PASSIVE); charaWork.parameterSave.hp = charaWork.parameterSave.hpMax; @@ -448,23 +449,23 @@ namespace FFXIVClassic_Map_Server.Actors public bool IsDead() { - return currentMainState == SetActorStatePacket.MAIN_STATE_DEAD || currentMainState == SetActorStatePacket.MAIN_STATE_DEAD2; + return aiContainer.IsDead(); } public bool IsAlive() { - return !IsDead(); + return !aiContainer.IsDead(); } public short GetHP() { // todo: - return charaWork.parameterSave.hp[currentJob]; + return charaWork.parameterSave.hp[0]; } public short GetMaxHP() { - return charaWork.parameterSave.hpMax[currentJob]; + return charaWork.parameterSave.hpMax[0]; } public short GetMP() @@ -500,16 +501,20 @@ namespace FFXIVClassic_Map_Server.Actors // 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) { - // todo: +/- hp and die - // todo: battlenpcs probably have way more hp? - var addHp = charaWork.parameterSave.hp[currentJob] + hp; - addHp = addHp.Clamp(ushort.MinValue, charaWork.parameterSave.hpMax[currentJob]); - charaWork.parameterSave.hp[currentJob] = (short)addHp; + // dont wanna die ded + if (IsAlive()) + { + // todo: +/- hp and die + // todo: battlenpcs probably have way more hp? + var addHp = charaWork.parameterSave.hp[0] + hp; + addHp = addHp.Clamp(ushort.MinValue, charaWork.parameterSave.hpMax[0]); + charaWork.parameterSave.hp[0] = (short)addHp; - if (charaWork.parameterSave.hp[currentJob] < 1) - Die(Program.Tick); + if (charaWork.parameterSave.hp[0] < 1) + Die(Program.Tick); - updateFlags |= ActorUpdateFlags.HpTpMp; + updateFlags |= ActorUpdateFlags.HpTpMp; + } } public void AddMP(int mp) @@ -580,21 +585,47 @@ namespace FFXIVClassic_Map_Server.Actors // todo: call onAttack/onDamageTaken target.DelHP(action.amount); + if (target is BattleNpc) + ((BattleNpc)target).lastAttacker = this; } public virtual void OnCast(State state, BattleAction[] actions, ref BattleAction[] errors) { 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 + + if (target is BattleNpc) + ((BattleNpc)target).lastAttacker = this; } public virtual void OnWeaponSkill(State state, BattleAction[] actions, ref BattleAction[] errors) { var skill = ((WeaponSkillState)state).GetWeaponSkill(); + // damage is handled in script this.DelTP(skill.tpCost); + + if (target is BattleNpc) + ((BattleNpc)target).lastAttacker = this; } public virtual void OnAbility(State state, BattleAction[] actions, ref BattleAction[] errors) + { + if (target is BattleNpc) + ((BattleNpc)target).lastAttacker = this; + } + + public virtual void OnSpawn() + { + + } + + public virtual void OnDeath() + { + + } + + public virtual void OnDespawn() { } diff --git a/FFXIVClassic Map Server/actors/chara/Modifier.cs b/FFXIVClassic Map Server/actors/chara/Modifier.cs index 08558d88..d3f081b6 100644 --- a/FFXIVClassic Map Server/actors/chara/Modifier.cs +++ b/FFXIVClassic Map Server/actors/chara/Modifier.cs @@ -50,6 +50,8 @@ namespace FFXIVClassic_Map_Server.actors.chara HarvestPotency = 38, HarvestLimit = 39, - HarvestRate = 40 + HarvestRate = 40, + + Raise = 41, } } diff --git a/FFXIVClassic Map Server/actors/chara/ai/AIContainer.cs b/FFXIVClassic Map Server/actors/chara/ai/AIContainer.cs index 63457e48..c613b4b0 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/AIContainer.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/AIContainer.cs @@ -203,7 +203,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai public bool IsDead() { return owner.currentMainState == SetActorStatePacket.MAIN_STATE_DEAD || - owner.currentMainState == SetActorStatePacket.MAIN_STATE_DEAD2 || owner.GetHP() == 0; + owner.currentMainState == SetActorStatePacket.MAIN_STATE_DEAD2; } public bool IsRoaming() @@ -262,8 +262,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai public void InternalChangeTarget(Character target) { - // todo: use invalid target id - // todo: this is retarded, call entity's changetarget function + // targets are changed in the controller if (IsEngaged() || target == null) { @@ -288,7 +287,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai if (CanChangeState() || (GetCurrentState() != null && GetCurrentState().IsCompleted())) { - ForceChangeState(new AttackState(this.owner, target)); + ForceChangeState(new AttackState(owner, target)); return true; } return false; @@ -301,7 +300,10 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai owner.updateFlags |= ActorUpdateFlags.HpTpMp; ChangeTarget(null); - owner.ChangeState(SetActorStatePacket.MAIN_STATE_PASSIVE); + + if (owner.currentMainState == SetActorStatePacket.MAIN_STATE_ACTIVE) + owner.ChangeState(SetActorStatePacket.MAIN_STATE_PASSIVE); + ClearStates(); } @@ -337,11 +339,18 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai } } - public void InternalDie(DateTime tick, uint timeToFadeout) + public void InternalDie(DateTime tick, uint fadeoutTimerSeconds) + { + pathFind?.Clear(); + ClearStates(); + ForceChangeState(new DeathState(owner, tick, fadeoutTimerSeconds)); + } + + public void InternalDespawn(DateTime tick, uint respawnTimerSeconds) { ClearStates(); Disengage(); - ForceChangeState(new DeathState(owner, tick, timeToFadeout)); + ForceChangeState(new DespawnState(owner, respawnTimerSeconds)); } public void InternalRaise(Character target) diff --git a/FFXIVClassic Map Server/actors/chara/ai/StatusEffect.cs b/FFXIVClassic Map Server/actors/chara/ai/StatusEffect.cs index 8c01dbff..e6795003 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/StatusEffect.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/StatusEffect.cs @@ -384,12 +384,6 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai // todo: use tick instead of now? this.startTime = DateTime.Now; this.lastTick = startTime; - - // todo: set the effect name to be called by scripts or just lookup effects in db - // name = WorldManager.GetEffectInfo(id).Name; - // todo: check if can gain effect - // todo: call effect's onGain - // todo: broadcast effect gain packet } public StatusEffect(Character owner, StatusEffect effect) diff --git a/FFXIVClassic Map Server/actors/chara/ai/controllers/BattleNpcController.cs b/FFXIVClassic Map Server/actors/chara/ai/controllers/BattleNpcController.cs index 40712a65..8fdb1b94 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/controllers/BattleNpcController.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/controllers/BattleNpcController.cs @@ -96,6 +96,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers { var target = owner.target; base.Disengage(); + owner.statusEffects.RemoveStatusEffectsByFlags((uint)StatusEffectFlags.LoseOnDeath, true); // todo: lastActionTime = lastUpdate.AddSeconds(5); owner.isMovingToSpawn = true; @@ -104,7 +105,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers neutralTime = lastActionTime; owner.hateContainer.ClearHate(); owner.moveState = 1; - lua.LuaEngine.CallLuaBattleAction(owner, "onDisengage", owner, target, Utils.UnixTimeStampUTC(battleStartTime)); + lua.LuaEngine.CallLuaBattleFunction(owner, "onDisengage", owner, target, Utils.UnixTimeStampUTC(battleStartTime)); } public override void Cast(Character target, uint spellId) @@ -203,7 +204,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers } Move(); - lua.LuaEngine.CallLuaBattleAction(owner, "onCombatTick", owner, owner.target, Utils.UnixTimeStampUTC()); + lua.LuaEngine.CallLuaBattleFunction(owner, "onCombatTick", owner, owner.target, Utils.UnixTimeStampUTC(tick)); } private void Move() @@ -308,6 +309,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers public bool CanDetectTarget(Character target, bool forceSight = false) { + if (owner.IsDead()) + return false; + // todo: this should probably be changed to only allow detection at end of path? if (owner.aiContainer.pathFind.IsFollowingScriptedPath() || owner.aiContainer.pathFind.IsFollowingPath() && !owner.aiContainer.pathFind.AtPoint()) { diff --git a/FFXIVClassic Map Server/actors/chara/ai/state/AttackState.cs b/FFXIVClassic Map Server/actors/chara/ai/state/AttackState.cs index 870b60f5..aa540f79 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/state/AttackState.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/state/AttackState.cs @@ -33,15 +33,6 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state public override bool Update(DateTime tick) { - /* - TryInterrupt(); - - if (interrupt) - { - OnInterrupt(); - return true; - } - */ if ((target == null || owner.target != target || owner.target?.actorId != owner.currentLockedTarget) && owner.isAutoAttackEnabled) owner.aiContainer.ChangeTarget(target = owner.zone.FindActorInArea(owner.currentLockedTarget)); @@ -159,7 +150,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state // todo: shouldnt need to check if owner is dead since all states would be cleared if (owner.aiContainer.IsDead() || target.aiContainer.IsDead()) { - target = null; + owner.aiContainer.ChangeTarget(null); return false; } else if (!owner.aiContainer.GetTargetFind().CanTarget(target, false, true)) diff --git a/FFXIVClassic Map Server/actors/chara/ai/state/DeathState.cs b/FFXIVClassic Map Server/actors/chara/ai/state/DeathState.cs index bcc3f22a..c952f92e 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/state/DeathState.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/state/DeathState.cs @@ -14,8 +14,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state public DeathState(Character owner, DateTime tick, uint timeToFadeOut) : base(owner, null) { - owner.ChangeState(SetActorStatePacket.MAIN_STATE_DEAD); owner.Disengage(); + owner.ChangeState(SetActorStatePacket.MAIN_STATE_DEAD); canInterrupt = false; startTime = tick; despawnTime = startTime.AddSeconds(timeToFadeOut); @@ -23,17 +23,18 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state public override bool Update(DateTime tick) { + // todo: set a flag on chara for accept raise, play animation and spawn + if (owner.GetMod((uint)Modifier.Raise) > 0) + { + owner.Spawn(tick); + return true; + } + // todo: handle raise etc if (tick >= despawnTime) { - if (owner is BattleNpc) - { - owner.Despawn(tick); - } - else - { - // todo: queue a warp for the player - } + // todo: for players, return them to homepoint + owner.Despawn(tick); return true; } return false; diff --git a/FFXIVClassic Map Server/actors/chara/ai/state/DespawnState.cs b/FFXIVClassic Map Server/actors/chara/ai/state/DespawnState.cs index f16e8fa6..fc80251c 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/state/DespawnState.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/state/DespawnState.cs @@ -10,22 +10,21 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state { class DespawnState : State { - private DateTime endTime; - public DespawnState(Character owner, Character target, uint despawnTimeSeconds) : + private DateTime respawnTime; + public DespawnState(Character owner, uint respawnTimeSeconds) : base(owner, null) { startTime = Program.Tick; - endTime = startTime.AddSeconds(despawnTimeSeconds); + respawnTime = startTime.AddSeconds(respawnTimeSeconds); + owner.ChangeState(SetActorStatePacket.MAIN_STATE_DEAD2); + owner.OnDespawn(); } public override bool Update(DateTime tick) { - if (tick >= endTime) + if (tick >= respawnTime) { - // todo: send packet to despawn the npc, set it so npc is despawned when requesting spawn packets - owner.zone.BroadcastPacketAroundActor(owner, RemoveActorPacket.BuildPacket(owner.actorId)); - owner.QueuePositionUpdate(owner.spawnX, owner.spawnY, owner.spawnZ); - lua.LuaEngine.CallLuaBattleAction(owner, "onDespawn", owner); + owner.Spawn(tick); return true; } return false; diff --git a/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs b/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs index 37557bfc..157a9321 100644 --- a/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs +++ b/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs @@ -15,6 +15,8 @@ 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 { @@ -34,8 +36,10 @@ namespace FFXIVClassic_Map_Server.Actors 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) @@ -56,15 +60,38 @@ namespace FFXIVClassic_Map_Server.Actors spawnY = posY; spawnZ = posZ; - // todo: read this from db + // todo: read these from db also aggroType = AggroType.Sight; this.moveState = 2; ResetMoveSpeeds(); despawnTime = 10; - + respawnTime = 30; CalculateBaseStats(); } + public override List GetSpawnPackets(Player player, ushort spawnType) + { + List subpackets = new List(); + 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; @@ -135,6 +162,16 @@ namespace FFXIVClassic_Map_Server.Actors despawnTime = seconds; } + public uint GetRespawnTime() + { + return respawnTime; + } + + public void SetRespawnTime(uint seconds) + { + respawnTime = seconds; + } + /// // todo: create an action object? public bool OnAttack(AttackState state) { @@ -143,32 +180,61 @@ namespace FFXIVClassic_Map_Server.Actors public override void Spawn(DateTime tick) { - base.Spawn(tick); + if (respawnTime > 0) + { + base.Spawn(tick); - this.isMovingToSpawn = false; - this.ResetMoveSpeeds(); - this.hateContainer.ClearHate(); - this.ChangeState(SetActorStatePacket.MAIN_STATE_PASSIVE); + 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(memberId); + // onDeath(monster, player, killer) + lua.LuaEngine.CallLuaBattleFunction(this, "onDeath", this, partyMember, lastAttacker); + // defeat/defeats + ((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(); - this.positionX = oldPositionX; - this.positionY = oldPositionY; - this.positionZ = oldPositionZ; - - this.isAtSpawn = true; + // todo: reset cooldowns } else { - var err = $"[{actorId}][{customDisplayName}] {positionX} {positionY} {positionZ} {GetZoneID()} tried to die ded"; + var err = $"[{actorId}][{GetUniqueId()}] {positionX} {positionY} {positionZ} {GetZone().GetName()} tried to die ded"; Program.Log.Error(err); //throw new Exception(err); } @@ -176,9 +242,10 @@ namespace FFXIVClassic_Map_Server.Actors public override void Despawn(DateTime tick) { - aiContainer.ClearStates(); // todo: probably didnt need to make a new state... - aiContainer.ForceChangeState(new DespawnState(this, null, 0)); + aiContainer.InternalDespawn(tick, respawnTime); + lua.LuaEngine.CallLuaBattleFunction(this, "onDespawn", this); + this.isAtSpawn = true; } public void OnRoam(DateTime tick) @@ -203,6 +270,7 @@ namespace FFXIVClassic_Map_Server.Actors else { this.isMovingToSpawn = false; + lua.LuaEngine.CallLuaBattleFunction(this, "onRoam", this); } } @@ -217,5 +285,20 @@ namespace FFXIVClassic_Map_Server.Actors // 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(); + } } } diff --git a/FFXIVClassic Map Server/actors/chara/npc/Npc.cs b/FFXIVClassic Map Server/actors/chara/npc/Npc.cs index ecde9ff2..72c410b6 100644 --- a/FFXIVClassic Map Server/actors/chara/npc/Npc.cs +++ b/FFXIVClassic Map Server/actors/chara/npc/Npc.cs @@ -58,14 +58,16 @@ namespace FFXIVClassic_Map_Server.Actors charaWork.battleSave.potencial = 1.0f; - charaWork.parameterSave.state_mainSkill[0] = 3; - charaWork.parameterSave.state_mainSkill[2] = 3; - charaWork.parameterSave.state_mainSkillLevel = 2; + // todo: these really need to be read from db etc + { + charaWork.parameterSave.state_mainSkill[0] = 3; + charaWork.parameterSave.state_mainSkill[2] = 3; + charaWork.parameterSave.state_mainSkillLevel = 1; - charaWork.parameterSave.hp[0] = 500; - charaWork.parameterSave.hpMax[0] = 500; - - for (int i = 0; i < 32; i++ ) + charaWork.parameterSave.hp[0] = 80; + charaWork.parameterSave.hpMax[0] = 80; + } + for (int i = 0; i < 32; i++ ) charaWork.property[i] = (byte)(((int)actorClass.propertyFlags >> i) & 1); npcWork.pushCommand = actorClass.pushCommand; @@ -400,13 +402,30 @@ namespace FFXIVClassic_Map_Server.Actors aiContainer.Update(tick); } - //A party member list packet came, set the party - /* public void SetParty(MonsterPartyGroup group) + public override void OnSpawn() { - if (group is MonsterPartyGroup) - currentParty = group; + base.OnSpawn(); } - */ + + public override void OnDeath() + { + base.OnDeath(); + } + + public override void OnDespawn() + { + zone.BroadcastPacketAroundActor(this, RemoveActorPacket.BuildPacket(this.actorId)); + zone.BroadcastPacketAroundActor(this, RemoveActorPacket.BuildPacket(this.actorId)); + QueuePositionUpdate(spawnX, spawnY, spawnZ); + LuaEngine.CallLuaBattleFunction(this, "onDespawn", this); + } + //A party member list packet came, set the party + /* public void SetParty(MonsterPartyGroup group) + { + if (group is MonsterPartyGroup) + currentParty = group; + } + */ } } diff --git a/FFXIVClassic Map Server/actors/chara/npc/Pet.cs b/FFXIVClassic Map Server/actors/chara/npc/Pet.cs index 27e9490e..68b98194 100644 --- a/FFXIVClassic Map Server/actors/chara/npc/Pet.cs +++ b/FFXIVClassic Map Server/actors/chara/npc/Pet.cs @@ -13,6 +13,7 @@ namespace FFXIVClassic_Map_Server.Actors { class Pet : BattleNpc { + public Character master; public Pet(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) diff --git a/FFXIVClassic Map Server/actors/chara/player/Player.cs b/FFXIVClassic Map Server/actors/chara/player/Player.cs index e12bf0b3..b2d591dd 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Player.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Player.cs @@ -1781,21 +1781,6 @@ namespace FFXIVClassic_Map_Server.Actors base.PostUpdate(tick, packets); } - public override void AddHP(int hp) - { - // todo: +/- hp and die - // todo: check hidden effects and shit - var addHp = charaWork.parameterSave.hp[currentJob] + hp; - addHp = addHp.Clamp(ushort.MinValue, charaWork.parameterSave.hpMax[currentJob]); - charaWork.parameterSave.hp[currentJob] = (short)addHp; - - if (charaWork.parameterSave.hp[currentJob] < 1) - Die(Program.Tick); - - updateFlags |= ActorUpdateFlags.HpTpMp; - } - - public override void Die(DateTime tick) { // todo: death timer @@ -1981,7 +1966,7 @@ namespace FFXIVClassic_Map_Server.Actors { if (aiContainer.CanChangeState()) aiContainer.Cast(zone.FindActorInArea(targetId == 0 ? currentTarget : targetId), spellId); - else if (aiContainer.GetCurrentState() is MagicState) + else if (aiContainer.IsCurrentState()) // You are already casting. SendGameMessage(Server.GetWorldManager().GetActor(), 32536, 0x20); else @@ -2141,7 +2126,7 @@ namespace FFXIVClassic_Map_Server.Actors //action.animation = 0x19001000; } var target = state.GetTarget(); - //if (target is BattleNpc) + if (target is BattleNpc) { ((BattleNpc)target).hateContainer.UpdateHate(this, action.amount); } @@ -2164,6 +2149,8 @@ namespace FFXIVClassic_Map_Server.Actors var skill = ((WeaponSkillState)state).GetWeaponSkill(); // todo: should just make a thing that updates the one slot cause this is dumb as hell UpdateHotbarTimer(skill.id, skill.recastTimeSeconds); + // todo: this really shouldnt be called on each ws? + lua.LuaEngine.CallLuaBattleFunction(this, "onWeaponSkill", this, state.GetTarget(), skill); } } } diff --git a/FFXIVClassic Map Server/lua/LuaEngine.cs b/FFXIVClassic Map Server/lua/LuaEngine.cs index 26601f6e..7fabbb3a 100644 --- a/FFXIVClassic Map Server/lua/LuaEngine.cs +++ b/FFXIVClassic Map Server/lua/LuaEngine.cs @@ -134,9 +134,10 @@ namespace FFXIVClassic_Map_Server.lua /// // todo: this is dumb, should probably make a function for each action with different default return values /// or just make generic function and pass default value as first arg after functionName /// - public static void CallLuaBattleAction(Character actor, string functionName, params object[] args) + public static void CallLuaBattleFunction(Character actor, string functionName, params object[] args) { - string path = $"./scripts/unique/{actor.zone.zoneName}/Monster/{actor.customDisplayName}.lua"; + // todo: should use "scripts/zones/ZONE_NAME/battlenpcs/NAME.lua" instead of scripts/unique + string path = ""; // todo: should we call this for players too? if (actor is Player) @@ -144,6 +145,12 @@ namespace FFXIVClassic_Map_Server.lua // todo: check this is correct path = FILEPATH_PLAYER; } + else if (actor is Npc) + { + // todo: this is probably unnecessary as im not sure there were pets for players + if (!(actor is Pet && ((Pet)actor).master is Player)) + path = String.Format("./scripts/unique/{0}/{1}/{2}.lua", actor.zone.zoneName, actor is BattleNpc ? "Monster" : "PopulaceStandard", ((Npc)actor).GetUniqueId()); + } // dont wanna throw an error if file doesnt exist if (File.Exists(path)) { @@ -154,7 +161,7 @@ namespace FFXIVClassic_Map_Server.lua } catch (Exception e) { - Program.Log.Error($"LuaEngine.CallLuaBattleAction [{functionName}] {e.Message}"); + Program.Log.Error($"LuaEngine.CallLuaBattleFunction [{functionName}] {e.Message}"); } DynValue res = new DynValue(); @@ -167,6 +174,7 @@ namespace FFXIVClassic_Map_Server.lua public static int CallLuaStatusEffectFunction(Character actor, StatusEffect effect, string functionName, params object[] args) { + // todo: this is stupid, load the actual effect crap var name = ((StatusEffectId)effect.GetStatusEffectId()).ToString().ToLower(); string path = $"./scripts/effects/{name}.lua"; diff --git a/FFXIVClassic Map Server/lua/LuaUtils.cs b/FFXIVClassic Map Server/lua/LuaUtils.cs index a0eb86a2..f1181564 100644 --- a/FFXIVClassic Map Server/lua/LuaUtils.cs +++ b/FFXIVClassic Map Server/lua/LuaUtils.cs @@ -116,6 +116,12 @@ namespace FFXIVClassic_Map_Server public static void WriteLuaParams(BinaryWriter writer, List luaParams) { + if (luaParams == null) + { + Program.Log.Error("LuaUtils.WriteLuaParams LuaParams are null!"); + return; + } + foreach (LuaParam l in luaParams) { if (l.typeID == 0x1) diff --git a/data/scripts/directors/Quest/QuestDirectorMan0g001.lua b/data/scripts/directors/Quest/QuestDirectorMan0g001.lua index ebb44989..83b06b38 100644 --- a/data/scripts/directors/Quest/QuestDirectorMan0g001.lua +++ b/data/scripts/directors/Quest/QuestDirectorMan0g001.lua @@ -61,7 +61,7 @@ function onEventStarted(player, actor, triggerName) end -function onUpdate() +function onUpdate(deltaTime, area) end function onTalkEvent(player, npc)