From 1275c8b5da48113d06835207a9d2b1c3f6d5a928 Mon Sep 17 00:00:00 2001 From: yogurt Date: Fri, 8 Dec 2017 00:58:39 -0600 Subject: [PATCH] Added party to Gridania opening, fixed BattleActionx18 and made it so x18 is used for packets with more than 10 targets. Changed how death works. Added respawn time and roam modifiers. Added TryAggro functions and moved aggroing out of roaming and helpplayers. Fixed high cpu usage in zone's OnUpdate function. Fixed work value in player update --- FFXIVClassic Map Server/WorldManager.cs | 47 +++++++- FFXIVClassic Map Server/actors/Actor.cs | 3 - FFXIVClassic Map Server/actors/area/Area.cs | 11 +- FFXIVClassic Map Server/actors/area/Zone.cs | 14 ++- .../actors/chara/Character.cs | 105 ++++++++++++++---- .../chara/ai/controllers/AllyController.cs | 52 +++++++-- .../ai/controllers/BattleNpcController.cs | 81 ++++++++------ .../actors/chara/ai/helpers/TargetFind.cs | 93 +++++++++------- .../actors/chara/ai/state/DeathState.cs | 4 +- .../actors/chara/ai/state/MagicState.cs | 4 +- .../actors/chara/ai/state/WeaponSkillState.cs | 5 +- .../actors/chara/ai/utils/BattleUtils.cs | 15 ++- .../actors/chara/npc/BattleNpc.cs | 32 ++++-- .../actors/chara/npc/MobModifier.cs | 2 + .../actors/chara/player/Player.cs | 28 ++--- .../actors/director/Director.cs | 3 + .../actors/group/ContentGroup.cs | 5 +- FFXIVClassic Map Server/actors/group/Party.cs | 16 ++- .../dataobjects/ZoneConnection.cs | 2 +- FFXIVClassic Map Server/lua/LuaEngine.cs | 16 ++- .../send/Actor/SetActorPropetyPacket.cs | 1 - .../Actor/battle/BattleActionX18Packet.cs | 14 +-- data/scripts/ally.lua | 47 +++++++- data/scripts/commands/AbilityCure.lua | 13 +++ data/scripts/commands/ArrowReloadCommand.lua | 14 +++ data/scripts/commands/AttackAbility.lua | 14 +++ data/scripts/commands/ChangeJobCommand.lua | 6 + data/scripts/commands/CureMagic.lua | 3 + data/scripts/commands/CuregaMagic.lua | 14 +++ data/scripts/commands/EffectMagic.lua | 14 +++ data/scripts/commands/EsunaMagic.lua | 14 +++ data/scripts/commands/PointSearchAbility.lua | 7 ++ data/scripts/commands/RaiseMagic.lua | 14 +++ data/scripts/commands/ShotCommand.lua | 14 +++ data/scripts/commands/SongMagic.lua | 14 +++ data/scripts/commands/gm/yolo.lua | 5 +- data/scripts/commands/magic/Distortion.lua | 24 ++++ data/scripts/commands/magic/Healing Trap.lua | 24 ++++ data/scripts/commands/magic/Homing Missle.lua | 24 ++++ data/scripts/commands/magic/Return Trap.lua | 24 ++++ data/scripts/commands/magic/Stun II.lua | 24 ++++ data/scripts/commands/magic/Stun.lua | 24 ++++ data/scripts/commands/magic/Transfer Trap.lua | 24 ++++ data/scripts/commands/magic/blizzard.lua | 24 ++++ data/scripts/commands/magic/buff trap.lua | 24 ++++ data/scripts/commands/magic/burst.lua | 27 +++++ data/scripts/commands/magic/fira.lua | 27 +++++ data/scripts/commands/magic/firaga.lua | 27 +++++ data/scripts/commands/magic/fire.lua | 27 +++++ data/scripts/commands/magic/flare.lua | 27 +++++ data/scripts/commands/magic/freeze.lua | 27 +++++ data/scripts/commands/magic/holy.lua | 27 +++++ data/scripts/commands/magic/thundaga.lua | 24 ++++ data/scripts/commands/magic/thundara.lua | 24 ++++ .../commands/weaponskill/simian_thrash.lua | 26 +++++ data/scripts/content/SimpleContent30010.lua | 53 ++++++++- .../directors/Quest/QuestDirectorMan0g001.lua | 67 ++++++----- data/scripts/modifiers.lua | 79 +++++++++++++ .../unique/fst0Battle03/Monster/papalymo.lua | 5 + .../unique/fst0Battle03/Monster/yda.lua | 6 +- .../fst0Battle03/PopulaceStandard/yda.lua | 14 +-- 61 files changed, 1226 insertions(+), 223 deletions(-) create mode 100644 data/scripts/commands/AbilityCure.lua create mode 100644 data/scripts/commands/ArrowReloadCommand.lua create mode 100644 data/scripts/commands/AttackAbility.lua create mode 100644 data/scripts/commands/ChangeJobCommand.lua create mode 100644 data/scripts/commands/CureMagic.lua create mode 100644 data/scripts/commands/CuregaMagic.lua create mode 100644 data/scripts/commands/EffectMagic.lua create mode 100644 data/scripts/commands/EsunaMagic.lua create mode 100644 data/scripts/commands/PointSearchAbility.lua create mode 100644 data/scripts/commands/RaiseMagic.lua create mode 100644 data/scripts/commands/ShotCommand.lua create mode 100644 data/scripts/commands/SongMagic.lua create mode 100644 data/scripts/commands/magic/Distortion.lua create mode 100644 data/scripts/commands/magic/Healing Trap.lua create mode 100644 data/scripts/commands/magic/Homing Missle.lua create mode 100644 data/scripts/commands/magic/Return Trap.lua create mode 100644 data/scripts/commands/magic/Stun II.lua create mode 100644 data/scripts/commands/magic/Stun.lua create mode 100644 data/scripts/commands/magic/Transfer Trap.lua create mode 100644 data/scripts/commands/magic/blizzard.lua create mode 100644 data/scripts/commands/magic/buff trap.lua create mode 100644 data/scripts/commands/magic/burst.lua create mode 100644 data/scripts/commands/magic/fira.lua create mode 100644 data/scripts/commands/magic/firaga.lua create mode 100644 data/scripts/commands/magic/fire.lua create mode 100644 data/scripts/commands/magic/flare.lua create mode 100644 data/scripts/commands/magic/freeze.lua create mode 100644 data/scripts/commands/magic/holy.lua create mode 100644 data/scripts/commands/magic/thundaga.lua create mode 100644 data/scripts/commands/magic/thundara.lua create mode 100644 data/scripts/commands/weaponskill/simian_thrash.lua create mode 100644 data/scripts/modifiers.lua diff --git a/FFXIVClassic Map Server/WorldManager.cs b/FFXIVClassic Map Server/WorldManager.cs index 77c61050..2ab4afff 100644 --- a/FFXIVClassic Map Server/WorldManager.cs +++ b/FFXIVClassic Map Server/WorldManager.cs @@ -511,7 +511,6 @@ namespace FFXIVClassic_Map_Server 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"); @@ -644,12 +643,48 @@ namespace FFXIVClassic_Map_Server battleNpc.SetMod((uint)Modifier.Defense, reader.GetUInt32("def")); battleNpc.SetMod((uint)Modifier.Evasion, reader.GetUInt32("eva")); + if (battleNpc.poolMods != null) + { + foreach (var a in battleNpc.poolMods.mobModList) + { + battleNpc.SetMobMod(a.Value.id, (long)(a.Value.value)); + } + foreach (var a in battleNpc.poolMods.modList) + { + battleNpc.SetMod(a.Key, (long)(a.Value.value)); + } + } + + if (battleNpc.genusMods != null) + { + foreach (var a in battleNpc.genusMods.mobModList) + { + battleNpc.SetMobMod(a.Key, (long)(a.Value.value)); + } + foreach (var a in battleNpc.genusMods.modList) + { + battleNpc.SetMod(a.Key, (long)(a.Value.value)); + } + } + + if(battleNpc.spawnMods != null) + { + foreach (var a in battleNpc.spawnMods.mobModList) + { + battleNpc.SetMobMod(a.Key, (long)(a.Value.value)); + } + + foreach (var a in battleNpc.spawnMods.modList) + { + battleNpc.SetMod(a.Key, (long)(a.Value.value)); + } + } + battleNpc.dropListId = reader.GetUInt32("dropListId"); battleNpc.spellListId = reader.GetUInt32("spellListId"); battleNpc.skillListId = reader.GetUInt32("skillListId"); - battleNpc.SetMaxHP(1000); - battleNpc.SetHP(1000); battleNpc.SetBattleNpcId(reader.GetUInt32("bnpcId")); + battleNpc.SetRespawnTime(reader.GetUInt32("respawnTime")); battleNpc.CalculateBaseStats(); battleNpc.RecalculateStats(); //battleNpc.SetMod((uint)Modifier.ResistFire, ) @@ -679,7 +714,7 @@ namespace FFXIVClassic_Map_Server try { conn.Open(); - var query = $"SELECT {primaryKey}, modId, modVal, isMobMod FROM {tableName} GROUP BY {primaryKey};"; + var query = $"SELECT {primaryKey}, modId, modVal, isMobMod FROM {tableName}"; MySqlCommand cmd = new MySqlCommand(query, conn); @@ -688,9 +723,9 @@ namespace FFXIVClassic_Map_Server while (reader.Read()) { var id = reader.GetUInt32(primaryKey); - ModifierList modList = new ModifierList(id); + ModifierList modList = list.TryGetValue(id, out modList) ? modList : new ModifierList(id); modList.SetModifier(reader.GetUInt16("modId"), reader.GetInt64("modVal"), reader.GetBoolean("isMobMod")); - list.Add(id, modList); + list[id] = modList; } } } diff --git a/FFXIVClassic Map Server/actors/Actor.cs b/FFXIVClassic Map Server/actors/Actor.cs index cce7f1a8..313b62cc 100644 --- a/FFXIVClassic Map Server/actors/Actor.cs +++ b/FFXIVClassic Map Server/actors/Actor.cs @@ -438,9 +438,6 @@ namespace FFXIVClassic_Map_Server.Actors updateFlags = ActorUpdateFlags.None; zone.BroadcastPacketsAroundActor(this, packets); - - SetActorPropetyPacket hpInfo = new SetActorPropetyPacket("charaWork/exp"); - hpInfo.AddTarget(); } } diff --git a/FFXIVClassic Map Server/actors/area/Area.cs b/FFXIVClassic Map Server/actors/area/Area.cs index 9363319a..aa2eb23e 100644 --- a/FFXIVClassic Map Server/actors/area/Area.cs +++ b/FFXIVClassic Map Server/actors/area/Area.cs @@ -419,6 +419,11 @@ namespace FFXIVClassic_Map_Server.Actors public virtual List GetMonsters() { return GetAllActors(); + } + + public virtual List GetAllies() + { + return GetAllActors(); } public void BroadcastPacketsAroundActor(Actor actor, List packets) @@ -495,8 +500,8 @@ namespace FFXIVClassic_Map_Server.Actors npc = new Npc(mActorList.Count + 1, actorClass, uniqueId, this, x, y, z, rot, state, animId, null); npc.LoadEventConditions(actorClass.eventConditions); - npc.SetMaxHP(300); - npc.SetHP(300); + //npc.SetMaxHP(30000); + //npc.SetHP(30000); AddActorToZone(npc); @@ -666,7 +671,7 @@ namespace FFXIVClassic_Map_Server.Actors a.Update(tick); var deltaTime = (tick - Program.LastTick).TotalMilliseconds; - LuaEngine.GetInstance().CallLuaFunction(null, this, "onUpdate", true, deltaTime, this); + //LuaEngine.GetInstance().CallLuaFunction(null, this, "onUpdate", true, this, deltaTime); } } diff --git a/FFXIVClassic Map Server/actors/area/Zone.cs b/FFXIVClassic Map Server/actors/area/Zone.cs index 1286657f..07c2937f 100644 --- a/FFXIVClassic Map Server/actors/area/Zone.cs +++ b/FFXIVClassic Map Server/actors/area/Zone.cs @@ -132,6 +132,18 @@ namespace FFXIVClassic_Map_Server.actors.area return actor; } } + + foreach (List paList in contentAreas.Values) + { + foreach (PrivateArea pa in paList) + { + Actor actor = pa.FindActorInArea(id); + if (actor != null) + return actor; + } + } + + return null; } else @@ -184,7 +196,7 @@ namespace FFXIVClassic_Map_Server.actors.area { if (this.pathCalls > 0) { - Program.Log.Debug("Number of pathfinding calls {0} average time {1}ms", pathCalls, (float)(pathCallTime / pathCalls)); + Program.Log.Debug("Number of pathfinding calls {0} average time {1}ms", pathCalls, (float)(pathCallTime / pathCalls)); } lastUpdate = tick; } diff --git a/FFXIVClassic Map Server/actors/chara/Character.cs b/FFXIVClassic Map Server/actors/chara/Character.cs index fc71863d..71cd0f4e 100644 --- a/FFXIVClassic Map Server/actors/chara/Character.cs +++ b/FFXIVClassic Map Server/actors/chara/Character.cs @@ -230,10 +230,10 @@ namespace FFXIVClassic_Map_Server.Actors public void DoBattleAction(ushort commandId, uint animationId, BattleAction[] actions) { int currentIndex = 0; - + //AoE abilities only ever hit 16 people, so we probably won't need this loop anymore while (true) { - if (actions.Length - currentIndex >= 18) + if (actions.Length - currentIndex >= 10) zone.BroadcastPacketAroundActor(this, BattleActionX18Packet.BuildPacket(actorId, animationId, commandId, actions, ref currentIndex)); else if (actions.Length - currentIndex > 1) zone.BroadcastPacketAroundActor(this, BattleActionX10Packet.BuildPacket(actorId, animationId, commandId, actions, ref currentIndex)); @@ -244,7 +244,9 @@ namespace FFXIVClassic_Map_Server.Actors } else break; - animationId = 0; //If more than one packet is sent out, only send the animation once to avoid double playing. + + //I think aoe effects play on all hit enemies. Firaga does at least + //animationId = 0; //If more than one packet is sent out, only send the animation once to avoid double playing. } } @@ -313,7 +315,7 @@ namespace FFXIVClassic_Map_Server.Actors public virtual void OnPath(Vector3 point) { - lua.LuaEngine.CallLuaBattleFunction(this, "onPath", this, point); + //lua.LuaEngine.CallLuaBattleFunction(this, "onPath", this, point); updateFlags |= ActorUpdateFlags.Position; this.isAtSpawn = false; @@ -347,7 +349,7 @@ namespace FFXIVClassic_Map_Server.Actors if ((updateFlags & ActorUpdateFlags.HpTpMp) != 0) { - var propPacketUtil = new ActorPropertyPacketUtil("charaWork.parameterSave", this); + var propPacketUtil = new ActorPropertyPacketUtil("charaWork/stateAtQuicklyForAll", this); propPacketUtil.AddProperty("charaWork.parameterSave.mp"); propPacketUtil.AddProperty("charaWork.parameterSave.mpMax"); @@ -470,6 +472,7 @@ namespace FFXIVClassic_Map_Server.Actors { // todo: actual despawn timer aiContainer.InternalDie(tick, 10); + ChangeSpeed(0.0f, 0.0f, 0.0f, 0.0f); } public virtual void Despawn(DateTime tick) @@ -543,6 +546,20 @@ namespace FFXIVClassic_Map_Server.Actors updateFlags |= ActorUpdateFlags.HpTpMp; } + public void SetMP(uint mp) + { + charaWork.parameterSave.mpMax = (short)mp; + if (mp > charaWork.parameterSave.hpMax[0]) + SetMaxMP(mp); + + updateFlags |= ActorUpdateFlags.HpTpMp; + } + + public void SetMaxMP(uint mp) + { + charaWork.parameterSave.mp = (short)mp; + updateFlags |= ActorUpdateFlags.HpTpMp; + } // 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) { @@ -562,7 +579,7 @@ namespace FFXIVClassic_Map_Server.Actors } } - public short GetJob() + public short GetClass() { return charaWork.parameterSave.state_mainSkill[0]; } @@ -614,8 +631,30 @@ namespace FFXIVClassic_Map_Server.Actors public void RecalculateStats() { - if (GetMod((uint)Modifier.Hp) != 0) + uint hpMod = (uint) GetMod((uint)Modifier.Hp); + if (hpMod != 0) { + SetMaxHP(hpMod); + uint hpp = (uint) GetMod((uint) Modifier.HpPercent); + uint hp = hpMod; + if(hpp != 0) + { + hp = (uint) Math.Ceiling(((float)hpp / 100.0f) * hpMod); + } + SetHP(hp); + } + + uint mpMod = (uint)GetMod((uint)Modifier.Mp); + if (mpMod != 0) + { + SetMaxMP(mpMod); + uint mpp = (uint)GetMod((uint)Modifier.MpPercent); + uint mp = mpMod; + if (mpp != 0) + { + mp = (uint)Math.Ceiling(((float)mpp / 100.0f) * mpMod); + } + SetMP(mp); } // todo: recalculate stats and crap updateFlags |= ActorUpdateFlags.HpTpMp; @@ -650,9 +689,10 @@ 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, DamageTakenType.Ability); // todo: call onAttack/onDamageTaken - target.DelHP(action.amount); + BattleUtils.DamageTarget(this, target, action); + target.OnDamageTaken(this, action, DamageTakenType.Ability); + if (target is BattleNpc) ((BattleNpc)target).lastAttacker = this; @@ -664,13 +704,26 @@ namespace FFXIVClassic_Map_Server.Actors { 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 + var spellCost = spell.CalculateCost((uint)this.GetLevel()); + this.DelMP(spellCost); // mpCost can be set in script e.g. if caster has something for free spells - foreach (var action in actions) - zone.FindActorInArea(action.targetId).OnDamageTaken(this, action, DamageTakenType.Magic); + foreach (BattleAction action in actions) + { + if (zone.FindActorInArea(action.targetId) is Character chara) + { + if (chara != null && chara is BattleNpc) + { + ((BattleNpc)chara).hateContainer.UpdateHate(this, action.amount); + ((BattleNpc)chara).lastAttacker = this; + } + + BattleUtils.DamageTarget(this, chara, action); + } + } if (target is BattleNpc) - ((BattleNpc)target).lastAttacker = this; + + lua.LuaEngine.GetInstance().OnSignal("spellUsed"); } public virtual void OnWeaponSkill(State state, BattleAction[] actions, ref BattleAction[] errors) @@ -679,11 +732,25 @@ namespace FFXIVClassic_Map_Server.Actors // damage is handled in script this.DelTP(skill.tpCost); - foreach (var action in actions) - zone.FindActorInArea(action.targetId)?.OnDamageTaken(this, action, DamageTakenType.Weaponskill); + foreach (BattleAction action in actions) + { + if (zone.FindActorInArea(action.targetId) is Character chara) + { + if (chara != null && chara is BattleNpc) + { + ((BattleNpc)chara).hateContainer.UpdateHate(this, action.amount); + ((BattleNpc)chara).lastAttacker = this; + } + + BattleUtils.DamageTarget(this, chara, action); + } + } if (target is BattleNpc) ((BattleNpc)target).lastAttacker = this; + + + lua.LuaEngine.GetInstance().OnSignal("weaponskillUsed"); } public virtual void OnAbility(State state, BattleAction[] actions, ref BattleAction[] errors) @@ -769,22 +836,22 @@ namespace FFXIVClassic_Map_Server.Actors public bool IsDiscipleOfWar() { - return currentJob < CLASSID_THM; + return GetClass() < CLASSID_THM; } public bool IsDiscipleOfMagic() { - return currentJob >= CLASSID_THM && currentJob < CLASSID_CRP; + return GetClass() >= CLASSID_THM && currentJob < CLASSID_CRP; } public bool IsDiscipleOfHand() { - return currentJob >= CLASSID_CRP && currentJob < CLASSID_MIN; + return GetClass() >= CLASSID_CRP && currentJob < CLASSID_MIN; } public bool IsDiscipleOfLand() { - return currentJob >= CLASSID_MIN; + return GetClass() >= CLASSID_MIN; } #endregion lua helpers #endregion ai stuff diff --git a/FFXIVClassic Map Server/actors/chara/ai/controllers/AllyController.cs b/FFXIVClassic Map Server/actors/chara/ai/controllers/AllyController.cs index 5f748f72..4098e2bb 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/controllers/AllyController.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/controllers/AllyController.cs @@ -5,6 +5,7 @@ using System.Text; using System.Threading.Tasks; using FFXIVClassic_Map_Server.Actors; using FFXIVClassic_Map_Server.actors.chara.npc; +using FFXIVClassic.Common; namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers { @@ -17,10 +18,11 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers { this.owner = owner; } - - // server really likes to hang whenever scripts iterate area's actorlist - protected override void DoCombatTick(DateTime tick, List contentGroupCharas = null) + + protected List GetContentGroupCharas() { + List contentGroupCharas = null; + if (owner.currentContentGroup != null) { contentGroupCharas = new List(owner.currentContentGroup.GetMemberCount()); @@ -32,20 +34,46 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers contentGroupCharas.Add(chara); } } + + return contentGroupCharas; + } + + //Iterate over players in the group and if they are fighting, assist them + protected override void TryAggro(DateTime tick) + { + //lua.LuaEngine.CallLuaBattleFunction(owner, "tryAggro", owner, GetContentGroupCharas()); + + foreach(Character chara in GetContentGroupCharas()) + { + if(chara.IsPlayer()) + { + if(owner.aiContainer.GetTargetFind().CanTarget((Character) chara.target) && chara.target is BattleNpc && ((BattleNpc)chara.target).hateContainer.HasHateForTarget(chara)) + { + owner.Engage(chara.target.actorId); + owner.hateContainer.AddBaseHate((Character) chara.target); + break; + } + } + } + //base.TryAggro(tick); + } + + // server really likes to hang whenever scripts iterate area's actorlist + protected override void DoCombatTick(DateTime tick, List contentGroupCharas = null) + { + if (contentGroupCharas == null) + { + contentGroupCharas = GetContentGroupCharas(); + } + base.DoCombatTick(tick, contentGroupCharas); } + protected override void DoRoamTick(DateTime tick, List contentGroupCharas = null) { - if (owner.currentContentGroup != null) + if (contentGroupCharas == null) { - contentGroupCharas = new List(owner.currentContentGroup.GetMemberCount()); - foreach (var charaId in owner.currentContentGroup.GetMembers()) - { - var chara = owner.zone.FindActorInArea(charaId); - - if (chara != null) - contentGroupCharas.Add(chara); - } + contentGroupCharas = GetContentGroupCharas(); } base.DoRoamTick(tick, contentGroupCharas); } diff --git a/FFXIVClassic Map Server/actors/chara/ai/controllers/BattleNpcController.cs b/FFXIVClassic Map Server/actors/chara/ai/controllers/BattleNpcController.cs index 50280503..d823a53e 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/controllers/BattleNpcController.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/controllers/BattleNpcController.cs @@ -39,12 +39,20 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers public override void Update(DateTime tick) { lastUpdate = tick; + // todo: handle aggro/deaggro and other shit here - if (owner.aiContainer.IsEngaged()) + if (!owner.aiContainer.IsEngaged()) + { + TryAggro(tick); + } + + if(owner.aiContainer.IsEngaged()) { DoCombatTick(tick); } - else if (!owner.IsDead()) + + //Only move if owner isn't dead and is either too far away from their spawn point or is meant to roam + if (!owner.IsDead() && (owner.isMovingToSpawn || owner.GetMobMod((uint) MobModifier.Roams) > 0)) { DoRoamTick(tick); } @@ -63,6 +71,38 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers return false; } + //If the owner isn't moving to spawn, iterate over nearby enemies and + //aggro the first one that is within 10 levels and can be detected, then engage + protected virtual void TryAggro(DateTime tick) + { + if (tick >= neutralTime && !owner.isMovingToSpawn) + { + if (!owner.neutral && owner.IsAlive()) + { + foreach (var chara in owner.zone.GetActorsAroundActor(owner, 50)) + { + if (owner.allegiance == chara.allegiance) + continue; + + if (owner.aiContainer.pathFind.AtPoint() && owner.detectionType != DetectionType.None) + { + uint levelDifference = (uint)Math.Abs(owner.GetLevel() - chara.GetLevel()); + + if (levelDifference <= 10 || (owner.detectionType & DetectionType.IgnoreLevelDifference) != 0 && CanAggroTarget(chara)) + { + owner.hateContainer.AddBaseHate(chara); + break; + } + } + } + } + } + + if (owner.hateContainer.GetHateList().Count > 0) + { + Engage(owner.hateContainer.GetMostHatedTarget()); + } + } public override bool Engage(Character target) { var canEngage = this.owner.aiContainer.InternalEngage(target); @@ -77,6 +117,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers owner.ChangeState(SetActorStatePacket.MAIN_STATE_ACTIVE); owner.moveState = 2; + //owner.SetMod((uint)Modifier.Speed, 5); lastActionTime = DateTime.Now; battleStartTime = lastActionTime; // todo: adjust cooldowns with modifiers @@ -129,12 +170,6 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers protected virtual void DoRoamTick(DateTime tick, List contentGroupCharas = null) { - if (owner.hateContainer.GetHateList().Count > 0) - { - Engage(owner.hateContainer.GetMostHatedTarget()); - return; - } - if (tick >= waitTime) { neutralTime = tick.AddSeconds(5); @@ -150,7 +185,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers } } - waitTime = tick.AddSeconds(10); + waitTime = tick.AddSeconds(owner.GetMobMod((uint) MobModifier.RoamDelay)); owner.OnRoam(tick); if (CanMoveForward(0.0f) && !owner.aiContainer.pathFind.IsFollowingPath()) @@ -159,31 +194,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers owner.aiContainer.pathFind.SetPathFlags(PathFindFlags.None); owner.aiContainer.pathFind.PathInRange(owner.spawnX, owner.spawnY, owner.spawnZ, 1.5f, 50.0f); } - lua.LuaEngine.CallLuaBattleFunction(owner, "onRoam", owner, contentGroupCharas); - } - - - if (tick >= neutralTime) - { - if (!owner.neutral && owner.IsAlive()) - { - foreach (var chara in owner.zone.GetActorsAroundActor(owner, 50)) - { - if (owner.allegiance == chara.allegiance) - continue; - - if (!owner.isMovingToSpawn && owner.aiContainer.pathFind.AtPoint() && owner.detectionType != DetectionType.None) - { - uint levelDifference = (uint)Math.Abs(owner.GetLevel() - chara.GetLevel()); - - if (levelDifference <= 10 || (owner.detectionType & DetectionType.IgnoreLevelDifference) != 0 && CanAggroTarget(chara)) - { - owner.hateContainer.AddBaseHate(chara); - break; - } - } - } - } + //lua.LuaEngine.CallLuaBattleFunction(owner, "onRoam", owner, contentGroupCharas); } if (owner.aiContainer.pathFind.IsFollowingPath() && owner.aiContainer.CanFollowPath()) @@ -202,8 +213,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers return; } + Move(); - lua.LuaEngine.CallLuaBattleFunction(owner, "onCombatTick", owner, owner.target, Utils.UnixTimeStampUTC(tick), contentGroupCharas); } protected virtual void Move() diff --git a/FFXIVClassic Map Server/actors/chara/ai/helpers/TargetFind.cs b/FFXIVClassic Map Server/actors/chara/ai/helpers/TargetFind.cs index 585dafb9..ab374ea3 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/helpers/TargetFind.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/helpers/TargetFind.cs @@ -159,6 +159,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai /// public void FindWithinArea(Character target, ValidTarget flags, TargetFindAOETarget aoeTarget) { + targets.Clear(); validTarget = flags; // are we creating aoe circles around target or self if (aoeTarget == TargetFindAOETarget.Self) @@ -174,59 +175,63 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai if (masterTarget != null) targets.Add(masterTarget); - if (IsPlayer(owner)) + if (aoeType != TargetFindAOEType.None) { - if (masterTarget is Player) + if (IsPlayer(owner)) { - findType = TargetFindCharacterType.PlayerToPlayer; - - if (masterTarget.currentParty != null) + if (masterTarget is Player) { - if ((validTarget & (ValidTarget.Ally | ValidTarget.PartyMember)) != 0) - AddAllInAlliance(masterTarget, withPet); + findType = TargetFindCharacterType.PlayerToPlayer; + + if (masterTarget.currentParty != null) + { + if ((validTarget & (ValidTarget.Ally | ValidTarget.PartyMember)) != 0) + AddAllInAlliance(masterTarget, withPet); + else + AddAllInParty(masterTarget, withPet); + } else - AddAllInParty(masterTarget, withPet); + { + AddTarget(masterTarget, withPet); + } } else { - AddTarget(masterTarget, withPet); + findType = TargetFindCharacterType.PlayerToBattleNpc; + AddAllBattleNpcs(masterTarget, false); } } else { - findType = TargetFindCharacterType.PlayerToBattleNpc; - AddAllBattleNpcs(masterTarget, false); - } - } - else - { - // todo: this needs checking.. - if (masterTarget is Player || owner.allegiance == CharacterTargetingAllegiance.Player) - findType = TargetFindCharacterType.BattleNpcToPlayer; - else - findType = TargetFindCharacterType.BattleNpcToBattleNpc; - - // todo: configurable pet aoe buff - if (findType == TargetFindCharacterType.BattleNpcToBattleNpc && TryGetMasterTarget(target) != null) - withPet = true; - - // todo: does ffxiv have call for help flag? - //if ((findFlags & ValidTarget.HitAll) != 0) - //{ - // AddAllInZone(masterTarget, withPet); - //} - - AddAllInAlliance(target, withPet); - - if (findType == TargetFindCharacterType.BattleNpcToPlayer) - { - if (owner.allegiance == CharacterTargetingAllegiance.Player) - AddAllInZone(masterTarget, withPet); + // todo: this needs checking.. + if (masterTarget is Player || owner.allegiance == CharacterTargetingAllegiance.Player) + findType = TargetFindCharacterType.BattleNpcToPlayer; else - AddAllInHateList(); + findType = TargetFindCharacterType.BattleNpcToBattleNpc; + + // todo: configurable pet aoe buff + if (findType == TargetFindCharacterType.BattleNpcToBattleNpc && TryGetMasterTarget(target) != null) + withPet = true; + + // todo: does ffxiv have call for help flag? + //if ((findFlags & ValidTarget.HitAll) != 0) + //{ + // AddAllInZone(masterTarget, withPet); + //} + + AddAllInAlliance(target, withPet); + + if (findType == TargetFindCharacterType.BattleNpcToPlayer) + { + if (owner.allegiance == CharacterTargetingAllegiance.Player) + AddAllInZone(masterTarget, withPet); + else + AddAllInHateList(); + } } } - + if(targets.Count > 16) + targets.RemoveRange(16, targets.Count - 16); } /// @@ -288,7 +293,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai private void AddAllBattleNpcs(Character target, bool withPet) { - var actors = owner.zone.GetActorsAroundActor(owner, 50); + int dist = (int)maxDistance; + var actors = owner.zone.GetActorsAroundActor(target, dist); foreach (BattleNpc actor in actors) { @@ -375,12 +381,13 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai private bool IsWithinCircle(Character target, float maxDistance) { // todo: make y diff modifiable? - if (Math.Abs(owner.positionX - target.positionY) > 6.0f) - return false; + + //if (Math.Abs(owner.positionX - target.positionY) > 6.0f) + // return false; if (this.targetPosition == null) this.targetPosition = aoeTarget == TargetFindAOETarget.Self ? owner.GetPosAsVector3() : masterTarget.GetPosAsVector3(); - return target.GetPosAsVector3().IsWithinCircle(targetPosition, param); + return target.GetPosAsVector3().IsWithinCircle(targetPosition, maxDistance); } private bool IsPlayer(Character target) diff --git a/FFXIVClassic Map Server/actors/chara/ai/state/DeathState.cs b/FFXIVClassic Map Server/actors/chara/ai/state/DeathState.cs index 0bb584bf..61d76a62 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/state/DeathState.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/state/DeathState.cs @@ -15,7 +15,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state : base(owner, null) { owner.Disengage(); - owner.ChangeState(SetActorStatePacket.MAIN_STATE_DEAD2); + //owner.ChangeState(SetActorStatePacket.MAIN_STATE_DEAD2); + var deathStatePacket = SetActorStatePacket.BuildPacket(owner.actorId, SetActorStatePacket.MAIN_STATE_DEAD, owner.currentSubState); + owner.zone.BroadcastPacketAroundActor(owner, deathStatePacket); canInterrupt = false; startTime = tick; despawnTime = startTime.AddSeconds(timeToFadeOut); diff --git a/FFXIVClassic Map Server/actors/chara/ai/state/MagicState.cs b/FFXIVClassic Map Server/actors/chara/ai/state/MagicState.cs index 9d40df1b..8e28554e 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/state/MagicState.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/state/MagicState.cs @@ -110,16 +110,16 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state var i = 0; foreach (var chara in targets) { - var action = new BattleAction(target.actorId, spell.worldMasterTextId, spell.battleAnimation, 0, (byte)HitDirection.None, 1); + var action = new BattleAction(chara.actorId, spell.worldMasterTextId, spell.battleAnimation, 0, (byte)HitDirection.None, 1); action.amount = (ushort)lua.LuaEngine.CallLuaBattleCommandFunction(owner, spell, "magic", "onMagicFinish", owner, chara, spell, action); actions[i++] = action; } // todo: this is fuckin stupid, probably only need *one* error packet, not an error for each action var errors = (BattleAction[])actions.Clone(); - owner.OnCast(this, actions, ref errors); owner.DoBattleAction(spell.id, spell.battleAnimation, actions); + } public override void TryInterrupt() diff --git a/FFXIVClassic Map Server/actors/chara/ai/state/WeaponSkillState.cs b/FFXIVClassic Map Server/actors/chara/ai/state/WeaponSkillState.cs index f918c8be..8804de8a 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/state/WeaponSkillState.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/state/WeaponSkillState.cs @@ -88,7 +88,6 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state { skill.targetFind.FindWithinArea(target, skill.validTarget, skill.aoeTarget); isCompleted = true; - var targets = skill.targetFind.GetTargets(); BattleAction[] actions = new BattleAction[targets.Count]; @@ -100,12 +99,12 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state // evasion, miss, dodge, etc to be handled in script, calling helpers from scripts/weaponskills.lua action.amount = (ushort)lua.LuaEngine.CallLuaBattleCommandFunction(owner, skill, "weaponskill", "onSkillFinish", owner, target, skill, action); actions[i++] = action; + chara.Engage(chara.actorId, 1); } // todo: this is fuckin stupid, probably only need *one* error packet, not an error for each action var errors = (BattleAction[])actions.Clone(); - - owner.OnWeaponSkill(this, actions, ref errors); + owner.OnWeaponSkill(this, actions, ref errors); owner.DoBattleAction(skill.id, skill.battleAnimation, actions); } diff --git a/FFXIVClassic Map Server/actors/chara/ai/utils/BattleUtils.cs b/FFXIVClassic Map Server/actors/chara/ai/utils/BattleUtils.cs index 37a7197e..92684a0f 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/utils/BattleUtils.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/utils/BattleUtils.cs @@ -78,16 +78,19 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils public static void DamageTarget(Character attacker, Character defender, BattleAction action) { - // todo: other stuff too - if (defender is BattleNpc) + if (defender != null) { - if (!((BattleNpc)defender).hateContainer.HasHateForTarget(attacker)) + // todo: other stuff too + if (defender is BattleNpc) { - ((BattleNpc)defender).hateContainer.AddBaseHate(attacker); + if (!((BattleNpc)defender).hateContainer.HasHateForTarget(attacker)) + { + ((BattleNpc)defender).hateContainer.AddBaseHate(attacker); + } + ((BattleNpc)defender).hateContainer.UpdateHate(attacker, action.amount); } - ((BattleNpc)defender).hateContainer.UpdateHate(attacker, action.amount); + defender.DelHP((short)action.amount); } - defender.DelHP((short)action.amount); } public static int CalculateSpellDamage(Character attacker, Character defender, BattleCommand spell) diff --git a/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs b/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs index 07e8d69d..62b7a1b7 100644 --- a/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs +++ b/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs @@ -177,7 +177,7 @@ namespace FFXIVClassic_Map_Server.Actors packets = new List(); if ((updateFlags & ActorUpdateFlags.HpTpMp) != 0) { - var propPacketUtil = new ActorPropertyPacketUtil("charaWork.parameterSave", this); + var propPacketUtil = new ActorPropertyPacketUtil("charaWork/stateAtQuicklyForAll", this); propPacketUtil.AddProperty("charaWork.parameterSave.hp[0]"); propPacketUtil.AddProperty("charaWork.parameterSave.hpMax[0]"); @@ -290,6 +290,10 @@ namespace FFXIVClassic_Map_Server.Actors if (lastAttacker is Player) { + //I think this is, or should be odne in DoBattleAction. Packet capture had the message in the same packet as an attack + // defeat/defeats + //((Player)lastAttacker).QueuePacket(BattleActionX01Packet.BuildPacket(lastAttacker.actorId, 0, 0, new BattleAction(actorId, 30108, 0))); + if (lastAttacker.currentParty != null && lastAttacker.currentParty is Party) { foreach (var memberId in ((Party)lastAttacker.currentParty).members) @@ -297,14 +301,9 @@ namespace FFXIVClassic_Map_Server.Actors var partyMember = zone.FindActorInArea(memberId); // onDeath(monster, player, killer) lua.LuaEngine.CallLuaBattleFunction(this, "onDeath", this, partyMember, lastAttacker); - // defeat/defeats - - if (lastAttacker is Player) - ((Player)lastAttacker).QueuePacket(BattleActionX01Packet.BuildPacket(lastAttacker.actorId, 0, 0, new BattleAction(actorId, 30108, 0))); - - if(partyMember is Player) - ((Player)partyMember).AddExp(1500, (byte)partyMember.GetJob(), 5); + if(partyMember is Player) + ((Player)partyMember).AddExp(1500, (byte)partyMember.GetClass(), 5); } } else @@ -317,7 +316,6 @@ namespace FFXIVClassic_Map_Server.Actors positionUpdates?.Clear(); aiContainer.InternalDie(tick, despawnTime); this.ResetMoveSpeeds(); - // todo: reset cooldowns lua.LuaEngine.GetInstance().OnSignal("mobkill"); @@ -381,6 +379,9 @@ namespace FFXIVClassic_Map_Server.Actors if (GetMobMod((uint)MobModifier.AttackScript) != 0) lua.LuaEngine.CallLuaBattleFunction(this, "onAttack", this, state.GetTarget(), action.amount); + + if (target is BattleNpc) + ((BattleNpc)target).hateContainer.UpdateHate(this, action.amount); } public override void OnCast(State state, BattleAction[] actions, ref BattleAction[] errors) @@ -390,6 +391,19 @@ namespace FFXIVClassic_Map_Server.Actors if (GetMobMod((uint)MobModifier.SpellScript) != 0) foreach (var action in actions) lua.LuaEngine.CallLuaBattleFunction(this, "onCast", this, zone.FindActorInArea(action.targetId), ((MagicState)state).GetSpell(), action); + + foreach (BattleAction action in actions) + { + if (zone.FindActorInArea(action.targetId) is Character chara) + { + if (chara is BattleNpc) + { + ((BattleNpc)chara).hateContainer.UpdateHate(this, action.amount); + ((BattleNpc)chara).lastAttacker = this; + } + BattleUtils.DamageTarget(this, chara, action); + } + } } public override void OnAbility(State state, BattleAction[] actions, ref BattleAction[] errors) diff --git a/FFXIVClassic Map Server/actors/chara/npc/MobModifier.cs b/FFXIVClassic Map Server/actors/chara/npc/MobModifier.cs index 21a75d53..fe2600e6 100644 --- a/FFXIVClassic Map Server/actors/chara/npc/MobModifier.cs +++ b/FFXIVClassic Map Server/actors/chara/npc/MobModifier.cs @@ -32,5 +32,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.npc 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 + Roams = 24, // Do I walk around? + RoamDelay = 25 // What is the delay between roam ticks } } \ No newline at end of file diff --git a/FFXIVClassic Map Server/actors/chara/player/Player.cs b/FFXIVClassic Map Server/actors/chara/player/Player.cs index 5262425c..5e45bc74 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Player.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Player.cs @@ -557,6 +557,9 @@ namespace FFXIVClassic_Map_Server.Actors if (currentContentGroup != null) currentContentGroup.SendGroupPackets(playerSession); + + if (currentParty != null) + currentParty.SendGroupPackets(playerSession); } private void SendRemoveInventoryPackets(List slots) @@ -1625,7 +1628,7 @@ namespace FFXIVClassic_Map_Server.Actors //Update Instance List aroundMe = new List(); - if (zone != null) + if (zone != null) aroundMe.AddRange(zone.GetActorsAroundActor(this, 50)); if (zone2 != null) aroundMe.AddRange(zone2.GetActorsAroundActor(this, 50)); @@ -1714,6 +1717,7 @@ namespace FFXIVClassic_Map_Server.Actors //currentParty.members.Remove(this); if (partyGroup.members.Count == 0) Server.GetWorldManager().NoMembersInParty((Party)currentParty); + currentParty = null; } @@ -1755,7 +1759,7 @@ namespace FFXIVClassic_Map_Server.Actors if ((updateFlags & ActorUpdateFlags.HpTpMp) != 0) { - var propPacketUtil = new ActorPropertyPacketUtil("charaWork.parameterSave", this); + var propPacketUtil = new ActorPropertyPacketUtil("charaWork/stateAtQuicklyForAll", this); // todo: should this be using job as index? propPacketUtil.AddProperty($"charaWork.parameterSave.hp[{0}]"); @@ -1768,9 +1772,6 @@ namespace FFXIVClassic_Map_Server.Actors } base.PostUpdate(tick, packets); - SetActorPropetyPacket hpInfo = new SetActorPropetyPacket("charaWork/exp"); - hpInfo.AddTarget(); - QueuePacket(hpInfo.BuildPacket(actorId)); } public override void Die(DateTime tick) @@ -1801,7 +1802,6 @@ namespace FFXIVClassic_Map_Server.Actors public void UpdateHotbarCommands(List slotsToUpdate) { ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil("charaWork/command", this); - ActorPropertyPacketUtil compatibiltyUtil = new ActorPropertyPacketUtil("charaWork/commandDetailForSelf", this); foreach (ushort slot in slotsToUpdate) { propPacketUtil.AddProperty($"charaWork.command[{slot}]"); @@ -2184,9 +2184,9 @@ namespace FFXIVClassic_Map_Server.Actors { // todo: update hotbar timers to skill's recast time (also needs to be done on class change or equip crap) base.OnCast(state, actions, ref errors); + var spell = ((MagicState)state).GetSpell(); - // todo: should just make a thing that updates the one slot cause this is dumb as hell - + // todo: should just make a thing that updates the one slot cause this is dumb as hell UpdateHotbarTimer(spell.id, spell.recastTimeSeconds); } @@ -2300,27 +2300,23 @@ namespace FFXIVClassic_Map_Server.Actors Database.LoadHotbar(this); } + //Gets the id of the player's current job. If they aren't a job, gets the id of their class public byte GetCurrentClassOrJob() { if (currentJob != 0) return (byte) currentJob; - return charaWork.parameterSave.state_mainSkill[0]; } public void hpstuff(uint hp) { SetMaxHP(hp); - SetHP(hp); + SetHP(hp); mpMaxBase = (ushort)hp; charaWork.parameterSave.mpMax = (short)hp; charaWork.parameterSave.mp = (short)hp; - AddTP(0); - //SendCharaExpInfo(); - //ActorPropertyPacketUtil exp = new ActorPropertyPacketUtil("charaWork/exp", this); - SetActorPropetyPacket hpInfo = new SetActorPropetyPacket("charaWork/exp"); - hpInfo.AddTarget(); - QueuePacket(hpInfo.BuildPacket(actorId)); + AddTP(3000); + updateFlags |= ActorUpdateFlags.HpTpMp; } } diff --git a/FFXIVClassic Map Server/actors/director/Director.cs b/FFXIVClassic Map Server/actors/director/Director.cs index 04d1740f..e2a566a6 100644 --- a/FFXIVClassic Map Server/actors/director/Director.cs +++ b/FFXIVClassic Map Server/actors/director/Director.cs @@ -161,6 +161,9 @@ namespace FFXIVClassic_Map_Server.actors.director { members.Add(actor); + if (actor is Player) + ((Player)actor).AddDirector(this); + if (contentGroup != null) contentGroup.AddMember(actor); } diff --git a/FFXIVClassic Map Server/actors/group/ContentGroup.cs b/FFXIVClassic Map Server/actors/group/ContentGroup.cs index bebeb49c..950bbad0 100644 --- a/FFXIVClassic Map Server/actors/group/ContentGroup.cs +++ b/FFXIVClassic Map Server/actors/group/ContentGroup.cs @@ -42,6 +42,7 @@ namespace FFXIVClassic_Map_Server.actors.group public void Start() { isStarted = true; + SendGroupPacketsAll(members); } @@ -50,7 +51,8 @@ namespace FFXIVClassic_Map_Server.actors.group if (actor == null) return; - members.Add(actor.actorId); + if(!members.Contains(actor.actorId)) + members.Add(actor.actorId); if (actor is Character) ((Character)actor).SetCurrentContentGroup(this); @@ -121,7 +123,6 @@ namespace FFXIVClassic_Map_Server.actors.group } session.QueuePacket(GroupMembersEndPacket.buildPacket(session.id, session.GetActor().zoneId, time, this)); - } public override uint GetTypeId() diff --git a/FFXIVClassic Map Server/actors/group/Party.cs b/FFXIVClassic Map Server/actors/group/Party.cs index 436cc9d9..441ee0b5 100644 --- a/FFXIVClassic Map Server/actors/group/Party.cs +++ b/FFXIVClassic Map Server/actors/group/Party.cs @@ -64,11 +64,23 @@ namespace FFXIVClassic_Map_Server.actors.group groupMembers.Add(new GroupMember(id, -1, 0, false, true, Server.GetWorldManager().GetActorInWorld(id).customDisplayName)); foreach (uint charaId in members) { - if (charaId != id) - groupMembers.Add(new GroupMember(charaId, -1, 0, false, true, Server.GetWorldManager().GetActorInWorld(charaId).customDisplayName)); + var chara = Server.GetWorldManager().GetActorInWorld(charaId); + if (charaId != id && chara != null) + groupMembers.Add(new GroupMember(charaId, -1, 0, false, true, chara.customDisplayName)); } return groupMembers; } + public void AddMember(uint memberId) + { + members.Add(memberId); + SendGroupPacketsAll(members); + } + + public void RemoveMember(uint memberId) + { + members.Remove(memberId); + SendGroupPacketsAll(members); + } } } diff --git a/FFXIVClassic Map Server/dataobjects/ZoneConnection.cs b/FFXIVClassic Map Server/dataobjects/ZoneConnection.cs index 4a7fcc98..786a604d 100644 --- a/FFXIVClassic Map Server/dataobjects/ZoneConnection.cs +++ b/FFXIVClassic Map Server/dataobjects/ZoneConnection.cs @@ -24,7 +24,7 @@ namespace FFXIVClassic_Map_Server.dataobjects public void FlushQueuedSendPackets() { - if (!socket.Connected) + if (socket == null || !socket.Connected) return; while (SendPacketQueue.Count > 0) diff --git a/FFXIVClassic Map Server/lua/LuaEngine.cs b/FFXIVClassic Map Server/lua/LuaEngine.cs index 7de47460..47071010 100644 --- a/FFXIVClassic Map Server/lua/LuaEngine.cs +++ b/FFXIVClassic Map Server/lua/LuaEngine.cs @@ -478,10 +478,18 @@ namespace FFXIVClassic_Map_Server.lua { if (!script.Globals.Get(funcName).IsNil()) { - Coroutine coroutine = script.CreateCoroutine(script.Globals[funcName]).Coroutine; - DynValue value = coroutine.Resume(args2); - ResolveResume(player, coroutine, value); - + try + { + Coroutine coroutine = script.CreateCoroutine(script.Globals[funcName]).Coroutine; + DynValue value = coroutine.Resume(args2); + ResolveResume(player, coroutine, value); + } + catch(Exception e) + { + player.SendMessage(0x20, "", e.Message); + player.EndEvent(); + + } } else { diff --git a/FFXIVClassic Map Server/packets/send/Actor/SetActorPropetyPacket.cs b/FFXIVClassic Map Server/packets/send/Actor/SetActorPropetyPacket.cs index aabba72e..eec4278b 100644 --- a/FFXIVClassic Map Server/packets/send/Actor/SetActorPropetyPacket.cs +++ b/FFXIVClassic Map Server/packets/send/Actor/SetActorPropetyPacket.cs @@ -112,7 +112,6 @@ namespace FFXIVClassic_Map_Server.packets.send.actor { string[] split = name.Split('.'); int arrayIndex = 0; - if (!(split[0].Equals("work") || split[0].Equals("charaWork") || split[0].Equals("playerWork") || split[0].Equals("npcWork") || split[0].Equals("guildleveWork"))) return false; diff --git a/FFXIVClassic Map Server/packets/send/Actor/battle/BattleActionX18Packet.cs b/FFXIVClassic Map Server/packets/send/Actor/battle/BattleActionX18Packet.cs index 2fe2279f..d2411857 100644 --- a/FFXIVClassic Map Server/packets/send/Actor/battle/BattleActionX18Packet.cs +++ b/FFXIVClassic Map Server/packets/send/Actor/battle/BattleActionX18Packet.cs @@ -35,33 +35,33 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle binWriter.Write((UInt16)commandId); binWriter.Write((UInt16)0x810); //? - binWriter.Seek(0x58, SeekOrigin.Begin); + binWriter.Seek(0x28, SeekOrigin.Begin); for (int i = 0; i < max; i++) binWriter.Write((UInt32)actionList[listOffset + i].targetId); - binWriter.Seek(0xA0, SeekOrigin.Begin); + binWriter.Seek(0x70, SeekOrigin.Begin); for (int i = 0; i < max; i++) binWriter.Write((UInt16)actionList[listOffset + i].amount); - binWriter.Seek(0xC4, SeekOrigin.Begin); + binWriter.Seek(0x94, SeekOrigin.Begin); for (int i = 0; i < max; i++) binWriter.Write((UInt16)actionList[listOffset + i].worldMasterTextId); - binWriter.Seek(0xE8, SeekOrigin.Begin); + binWriter.Seek(0xB8, SeekOrigin.Begin); for (int i = 0; i < max; i++) binWriter.Write((UInt32)actionList[listOffset + i].effectId); - binWriter.Seek(0x130, SeekOrigin.Begin); + binWriter.Seek(0x100, SeekOrigin.Begin); for (int i = 0; i < max; i++) binWriter.Write((Byte)actionList[listOffset + i].param); - binWriter.Seek(0x142, SeekOrigin.Begin); + binWriter.Seek(0x112, SeekOrigin.Begin); for (int i = 0; i < max; i++) binWriter.Write((Byte)actionList[listOffset + i].unknown); listOffset += max; } - } + } return new SubPacket(OPCODE, sourceActorId, data); } diff --git a/data/scripts/ally.lua b/data/scripts/ally.lua index b8c39ff3..8e96909d 100644 --- a/data/scripts/ally.lua +++ b/data/scripts/ally.lua @@ -34,9 +34,10 @@ function allyGlobal.onDespawn(ally) end +--tryAggro serves the same purpose for now, keeping this around just in case function allyGlobal.HelpPlayers(ally, contentGroupCharas, pickRandomTarget) - if contentGroupCharas then - for _, chara in pairs(contentGroupCharas) do + if contentGroupCharas and not ally.IsEngaged() then + for chara in contentGroupCharas do if chara then -- probably a player, or another ally -- todo: queue support actions, heal, try pull hate off player etc @@ -44,12 +45,14 @@ function allyGlobal.HelpPlayers(ally, contentGroupCharas, pickRandomTarget) -- do stuff if not ally.IsEngaged() then if chara.IsEngaged() then - allyGlobal.EngageTarget(ally, chara.target, nil) + allyGlobal.EngageTarget(ally, chara.target, nil); + break; end - end + end elseif chara.IsMonster() and chara.IsEngaged() then if not ally.IsEngaged() then - allyGlobal.EngageTarget(ally, chara.target, nil) + allyGlobal.EngageTarget(ally, chara, nil); + break; end end end @@ -57,6 +60,36 @@ function allyGlobal.HelpPlayers(ally, contentGroupCharas, pickRandomTarget) end end +--Iterate over characters in contentGroup, if a player is in combat, assist them. +function allyGlobal.tryAggro(ally, contentGroupCharas) + local count = 0; + if contentGroupCharas and not ally.IsEngaged() then + for i = 0, #contentGroupCharas - 1 do + if contentGroupCharas[i] and ally then + if contentGroupCharas[i].IsPlayer() then + -- probably a player, or another ally + -- todo: queue support actions, heal, try pull hate off player etc + if contentGroupCharas[i].target then + if ally.aiContainer:GetTargetFind():CanTarget(contentGroupCharas[i].target) and contentGroupCharas[i].target.IsMonster() and contentGroupCharas[i].target.hateContainer:HasHateForTarget(contentGroupCharas[i]) then + -- do stuff + allyGlobal.EngageTarget(ally, contentGroupCharas[i].target, nil); + break; + end + end + + --[[ + elseif contentGroupCharas[i].IsMonster() and contentGroupCharas[i].IsEngaged() then + if not ally.IsEngaged() then + print("Engaging monster that is engaged"); + allyGlobal.EngageTarget(ally, contentGroupCharas[i], nil); + break; + end]] + end + end + end + end +end + function allyGlobal.HealPlayer(ally, player) end @@ -67,14 +100,16 @@ end function allyGlobal.EngageTarget(ally, target, contentGroupCharas) if contentGroupCharas then - for _, chara in pairs(contentGroupCharas) do + for chara in contentGroupCharas do if chara.IsMonster() then if chara.allegiance ~= ally.allegiance then ally.Engage(chara) + break; end end end elseif target then ally.Engage(target) + ally.hateContainer.AddBaseHate(target); end end \ No newline at end of file diff --git a/data/scripts/commands/AbilityCure.lua b/data/scripts/commands/AbilityCure.lua new file mode 100644 index 00000000..1ffb6814 --- /dev/null +++ b/data/scripts/commands/AbilityCure.lua @@ -0,0 +1,13 @@ +require("global") + +function onEventStarted(player, command, triggerName, arg1, arg2, arg3, arg4, targetActor, arg5, arg6, arg7, arg8) + + local worldManager = GetWorldManager(); + local shortCommandId = bit32.bxor(command, 2700083200); + local ability = worldManager:GetAbility(shortCommandId); + + --player:PlayAnimation(ability.modelAnimation); + + + player:endEvent(); +end \ No newline at end of file diff --git a/data/scripts/commands/ArrowReloadCommand.lua b/data/scripts/commands/ArrowReloadCommand.lua new file mode 100644 index 00000000..12f3dd34 --- /dev/null +++ b/data/scripts/commands/ArrowReloadCommand.lua @@ -0,0 +1,14 @@ +require("global") + +function onEventStarted(player, command, triggerName, arg1, arg2, arg3, arg4, targetActor, arg5, arg6, arg7, arg8) + + local worldManager = GetWorldManager(); + --local shortCommandId = command.actorId;--bit32:bxor(command.actorId, 2700083200); + local ability = worldManager:GetAbility(command.actorId); + + if ability then + player.SendBattleActionX01Packet(ability.modelAnimation, ability.effectAnimation, 0x756D, command.actorId, ability.animationType); + end + + player:endEvent(); +end \ No newline at end of file diff --git a/data/scripts/commands/AttackAbility.lua b/data/scripts/commands/AttackAbility.lua new file mode 100644 index 00000000..12f3dd34 --- /dev/null +++ b/data/scripts/commands/AttackAbility.lua @@ -0,0 +1,14 @@ +require("global") + +function onEventStarted(player, command, triggerName, arg1, arg2, arg3, arg4, targetActor, arg5, arg6, arg7, arg8) + + local worldManager = GetWorldManager(); + --local shortCommandId = command.actorId;--bit32:bxor(command.actorId, 2700083200); + local ability = worldManager:GetAbility(command.actorId); + + if ability then + player.SendBattleActionX01Packet(ability.modelAnimation, ability.effectAnimation, 0x756D, command.actorId, ability.animationType); + end + + player:endEvent(); +end \ No newline at end of file diff --git a/data/scripts/commands/ChangeJobCommand.lua b/data/scripts/commands/ChangeJobCommand.lua new file mode 100644 index 00000000..4cb38f6a --- /dev/null +++ b/data/scripts/commands/ChangeJobCommand.lua @@ -0,0 +1,6 @@ +function onEventStarted(player, caller, commandRequest, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) + + player:SetCurrentJob(17); + + player:EndEvent(); +end \ No newline at end of file diff --git a/data/scripts/commands/CureMagic.lua b/data/scripts/commands/CureMagic.lua new file mode 100644 index 00000000..f0753c01 --- /dev/null +++ b/data/scripts/commands/CureMagic.lua @@ -0,0 +1,3 @@ +function onEventStarted(player, command, triggerName, arg1, arg2, arg3, arg4, targetActor, arg5, arg6, arg7, arg8) + +end \ No newline at end of file diff --git a/data/scripts/commands/CuregaMagic.lua b/data/scripts/commands/CuregaMagic.lua new file mode 100644 index 00000000..12f3dd34 --- /dev/null +++ b/data/scripts/commands/CuregaMagic.lua @@ -0,0 +1,14 @@ +require("global") + +function onEventStarted(player, command, triggerName, arg1, arg2, arg3, arg4, targetActor, arg5, arg6, arg7, arg8) + + local worldManager = GetWorldManager(); + --local shortCommandId = command.actorId;--bit32:bxor(command.actorId, 2700083200); + local ability = worldManager:GetAbility(command.actorId); + + if ability then + player.SendBattleActionX01Packet(ability.modelAnimation, ability.effectAnimation, 0x756D, command.actorId, ability.animationType); + end + + player:endEvent(); +end \ No newline at end of file diff --git a/data/scripts/commands/EffectMagic.lua b/data/scripts/commands/EffectMagic.lua new file mode 100644 index 00000000..52830cf7 --- /dev/null +++ b/data/scripts/commands/EffectMagic.lua @@ -0,0 +1,14 @@ +require("global") + +function onEventStarted(player, command, triggerName, arg1, arg2, arg3, arg4, targetActor, arg5, arg6, arg7, arg8) + + local worldManager = GetWorldManager(); + --local shortCommandId = command.actorId;--bit32:bxor(command.actorId, 2700083200); + local ability = worldManager:GetBattleCommand(command.actorId); + + if ability then + player.SendBattleActionX01Packet(ability.modelAnimation, ability.effectAnimation); + end + + player:endEvent(); +end \ No newline at end of file diff --git a/data/scripts/commands/EsunaMagic.lua b/data/scripts/commands/EsunaMagic.lua new file mode 100644 index 00000000..12f3dd34 --- /dev/null +++ b/data/scripts/commands/EsunaMagic.lua @@ -0,0 +1,14 @@ +require("global") + +function onEventStarted(player, command, triggerName, arg1, arg2, arg3, arg4, targetActor, arg5, arg6, arg7, arg8) + + local worldManager = GetWorldManager(); + --local shortCommandId = command.actorId;--bit32:bxor(command.actorId, 2700083200); + local ability = worldManager:GetAbility(command.actorId); + + if ability then + player.SendBattleActionX01Packet(ability.modelAnimation, ability.effectAnimation, 0x756D, command.actorId, ability.animationType); + end + + player:endEvent(); +end \ No newline at end of file diff --git a/data/scripts/commands/PointSearchAbility.lua b/data/scripts/commands/PointSearchAbility.lua new file mode 100644 index 00000000..ee05e8c4 --- /dev/null +++ b/data/scripts/commands/PointSearchAbility.lua @@ -0,0 +1,7 @@ + +function onEventStarted(player, command, triggerName, arg1, arg2, arg3, arg4, targetActor, arg5, arg6, arg7, arg8) + + + player.Cast(command.actorId, targetActor); + player:endEvent(); +end \ No newline at end of file diff --git a/data/scripts/commands/RaiseMagic.lua b/data/scripts/commands/RaiseMagic.lua new file mode 100644 index 00000000..6ff5f5dd --- /dev/null +++ b/data/scripts/commands/RaiseMagic.lua @@ -0,0 +1,14 @@ +require("global") + +function onEventStarted(player, command, triggerName, arg1, arg2, arg3, arg4, targetActor, arg5, arg6, arg7, arg8) + + local worldManager = GetWorldManager(); + --local shortCommandId = command.actorId;--bit32:bxor(command.actorId, 2700083200); + local ability = worldManager:GetAbility(command.actorId); + + if ability then + player.SendBattleActionX01Packet(ability.modelAnimation, ability.effectAnimation); + end + + player:endEvent(); +end \ No newline at end of file diff --git a/data/scripts/commands/ShotCommand.lua b/data/scripts/commands/ShotCommand.lua new file mode 100644 index 00000000..6ff5f5dd --- /dev/null +++ b/data/scripts/commands/ShotCommand.lua @@ -0,0 +1,14 @@ +require("global") + +function onEventStarted(player, command, triggerName, arg1, arg2, arg3, arg4, targetActor, arg5, arg6, arg7, arg8) + + local worldManager = GetWorldManager(); + --local shortCommandId = command.actorId;--bit32:bxor(command.actorId, 2700083200); + local ability = worldManager:GetAbility(command.actorId); + + if ability then + player.SendBattleActionX01Packet(ability.modelAnimation, ability.effectAnimation); + end + + player:endEvent(); +end \ No newline at end of file diff --git a/data/scripts/commands/SongMagic.lua b/data/scripts/commands/SongMagic.lua new file mode 100644 index 00000000..12f3dd34 --- /dev/null +++ b/data/scripts/commands/SongMagic.lua @@ -0,0 +1,14 @@ +require("global") + +function onEventStarted(player, command, triggerName, arg1, arg2, arg3, arg4, targetActor, arg5, arg6, arg7, arg8) + + local worldManager = GetWorldManager(); + --local shortCommandId = command.actorId;--bit32:bxor(command.actorId, 2700083200); + local ability = worldManager:GetAbility(command.actorId); + + if ability then + player.SendBattleActionX01Packet(ability.modelAnimation, ability.effectAnimation, 0x756D, command.actorId, ability.animationType); + end + + player:endEvent(); +end \ No newline at end of file diff --git a/data/scripts/commands/gm/yolo.lua b/data/scripts/commands/gm/yolo.lua index 34ad7d25..668cc9af 100644 --- a/data/scripts/commands/gm/yolo.lua +++ b/data/scripts/commands/gm/yolo.lua @@ -152,8 +152,11 @@ function onTrigger(player, argc, skillName, level) --local x, y, z = player.GetPos(); for i = 1, 1 do - local actor = player.GetZone().SpawnActor(2207303, 'ass', x, y, z, rot, 0, 0, true ); + local actor = player.GetZone().SpawnActor(2104001, 'ass', x, y, z, rot, 0, 0, true ); + if player.currentContentGroup then + player.currentContentGroup:AddMember(actor.actorId) + end --actor.FollowTarget(player, 3.2); end; return; diff --git a/data/scripts/commands/magic/Distortion.lua b/data/scripts/commands/magic/Distortion.lua new file mode 100644 index 00000000..1d854b23 --- /dev/null +++ b/data/scripts/commands/magic/Distortion.lua @@ -0,0 +1,24 @@ +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + local damage = math.random(10, 100); + + action.worldMasterTextId = 0x765D; + + -- todo: populate a global script with statuses and modifiers + -- magic.HandleAttackMagic(caster, target, spell, action) + action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); + + if target.hateContainer then + target.hateContainer.UpdateHate(caster, damage); + end; + return damage; +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/Healing Trap.lua b/data/scripts/commands/magic/Healing Trap.lua new file mode 100644 index 00000000..1d854b23 --- /dev/null +++ b/data/scripts/commands/magic/Healing Trap.lua @@ -0,0 +1,24 @@ +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + local damage = math.random(10, 100); + + action.worldMasterTextId = 0x765D; + + -- todo: populate a global script with statuses and modifiers + -- magic.HandleAttackMagic(caster, target, spell, action) + action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); + + if target.hateContainer then + target.hateContainer.UpdateHate(caster, damage); + end; + return damage; +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/Homing Missle.lua b/data/scripts/commands/magic/Homing Missle.lua new file mode 100644 index 00000000..1d854b23 --- /dev/null +++ b/data/scripts/commands/magic/Homing Missle.lua @@ -0,0 +1,24 @@ +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + local damage = math.random(10, 100); + + action.worldMasterTextId = 0x765D; + + -- todo: populate a global script with statuses and modifiers + -- magic.HandleAttackMagic(caster, target, spell, action) + action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); + + if target.hateContainer then + target.hateContainer.UpdateHate(caster, damage); + end; + return damage; +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/Return Trap.lua b/data/scripts/commands/magic/Return Trap.lua new file mode 100644 index 00000000..1d854b23 --- /dev/null +++ b/data/scripts/commands/magic/Return Trap.lua @@ -0,0 +1,24 @@ +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + local damage = math.random(10, 100); + + action.worldMasterTextId = 0x765D; + + -- todo: populate a global script with statuses and modifiers + -- magic.HandleAttackMagic(caster, target, spell, action) + action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); + + if target.hateContainer then + target.hateContainer.UpdateHate(caster, damage); + end; + return damage; +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/Stun II.lua b/data/scripts/commands/magic/Stun II.lua new file mode 100644 index 00000000..1d854b23 --- /dev/null +++ b/data/scripts/commands/magic/Stun II.lua @@ -0,0 +1,24 @@ +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + local damage = math.random(10, 100); + + action.worldMasterTextId = 0x765D; + + -- todo: populate a global script with statuses and modifiers + -- magic.HandleAttackMagic(caster, target, spell, action) + action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); + + if target.hateContainer then + target.hateContainer.UpdateHate(caster, damage); + end; + return damage; +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/Stun.lua b/data/scripts/commands/magic/Stun.lua new file mode 100644 index 00000000..1d854b23 --- /dev/null +++ b/data/scripts/commands/magic/Stun.lua @@ -0,0 +1,24 @@ +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + local damage = math.random(10, 100); + + action.worldMasterTextId = 0x765D; + + -- todo: populate a global script with statuses and modifiers + -- magic.HandleAttackMagic(caster, target, spell, action) + action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); + + if target.hateContainer then + target.hateContainer.UpdateHate(caster, damage); + end; + return damage; +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/Transfer Trap.lua b/data/scripts/commands/magic/Transfer Trap.lua new file mode 100644 index 00000000..1d854b23 --- /dev/null +++ b/data/scripts/commands/magic/Transfer Trap.lua @@ -0,0 +1,24 @@ +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + local damage = math.random(10, 100); + + action.worldMasterTextId = 0x765D; + + -- todo: populate a global script with statuses and modifiers + -- magic.HandleAttackMagic(caster, target, spell, action) + action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); + + if target.hateContainer then + target.hateContainer.UpdateHate(caster, damage); + end; + return damage; +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/blizzard.lua b/data/scripts/commands/magic/blizzard.lua new file mode 100644 index 00000000..1d854b23 --- /dev/null +++ b/data/scripts/commands/magic/blizzard.lua @@ -0,0 +1,24 @@ +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + local damage = math.random(10, 100); + + action.worldMasterTextId = 0x765D; + + -- todo: populate a global script with statuses and modifiers + -- magic.HandleAttackMagic(caster, target, spell, action) + action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); + + if target.hateContainer then + target.hateContainer.UpdateHate(caster, damage); + end; + return damage; +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/buff trap.lua b/data/scripts/commands/magic/buff trap.lua new file mode 100644 index 00000000..1d854b23 --- /dev/null +++ b/data/scripts/commands/magic/buff trap.lua @@ -0,0 +1,24 @@ +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + local damage = math.random(10, 100); + + action.worldMasterTextId = 0x765D; + + -- todo: populate a global script with statuses and modifiers + -- magic.HandleAttackMagic(caster, target, spell, action) + action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); + + if target.hateContainer then + target.hateContainer.UpdateHate(caster, damage); + end; + return damage; +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/burst.lua b/data/scripts/commands/magic/burst.lua new file mode 100644 index 00000000..20f3edab --- /dev/null +++ b/data/scripts/commands/magic/burst.lua @@ -0,0 +1,27 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + local damage = math.random(1000, 2500); + + -- todo: populate a global script with statuses and modifiers + action.worldMasterTextId = 0x765D; + + -- todo: populate a global script with statuses and modifiers + -- magic.HandleAttackMagic(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; \ No newline at end of file diff --git a/data/scripts/commands/magic/fira.lua b/data/scripts/commands/magic/fira.lua new file mode 100644 index 00000000..fc035356 --- /dev/null +++ b/data/scripts/commands/magic/fira.lua @@ -0,0 +1,27 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(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.HandleAttackMagic(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; \ No newline at end of file diff --git a/data/scripts/commands/magic/firaga.lua b/data/scripts/commands/magic/firaga.lua new file mode 100644 index 00000000..20f3edab --- /dev/null +++ b/data/scripts/commands/magic/firaga.lua @@ -0,0 +1,27 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + local damage = math.random(1000, 2500); + + -- todo: populate a global script with statuses and modifiers + action.worldMasterTextId = 0x765D; + + -- todo: populate a global script with statuses and modifiers + -- magic.HandleAttackMagic(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; \ No newline at end of file diff --git a/data/scripts/commands/magic/fire.lua b/data/scripts/commands/magic/fire.lua new file mode 100644 index 00000000..fc035356 --- /dev/null +++ b/data/scripts/commands/magic/fire.lua @@ -0,0 +1,27 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(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.HandleAttackMagic(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; \ No newline at end of file diff --git a/data/scripts/commands/magic/flare.lua b/data/scripts/commands/magic/flare.lua new file mode 100644 index 00000000..20f3edab --- /dev/null +++ b/data/scripts/commands/magic/flare.lua @@ -0,0 +1,27 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + local damage = math.random(1000, 2500); + + -- todo: populate a global script with statuses and modifiers + action.worldMasterTextId = 0x765D; + + -- todo: populate a global script with statuses and modifiers + -- magic.HandleAttackMagic(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; \ No newline at end of file diff --git a/data/scripts/commands/magic/freeze.lua b/data/scripts/commands/magic/freeze.lua new file mode 100644 index 00000000..fc035356 --- /dev/null +++ b/data/scripts/commands/magic/freeze.lua @@ -0,0 +1,27 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(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.HandleAttackMagic(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; \ No newline at end of file diff --git a/data/scripts/commands/magic/holy.lua b/data/scripts/commands/magic/holy.lua new file mode 100644 index 00000000..20f3edab --- /dev/null +++ b/data/scripts/commands/magic/holy.lua @@ -0,0 +1,27 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + local damage = math.random(1000, 2500); + + -- todo: populate a global script with statuses and modifiers + action.worldMasterTextId = 0x765D; + + -- todo: populate a global script with statuses and modifiers + -- magic.HandleAttackMagic(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; \ No newline at end of file diff --git a/data/scripts/commands/magic/thundaga.lua b/data/scripts/commands/magic/thundaga.lua new file mode 100644 index 00000000..1d854b23 --- /dev/null +++ b/data/scripts/commands/magic/thundaga.lua @@ -0,0 +1,24 @@ +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + local damage = math.random(10, 100); + + action.worldMasterTextId = 0x765D; + + -- todo: populate a global script with statuses and modifiers + -- magic.HandleAttackMagic(caster, target, spell, action) + action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); + + if target.hateContainer then + target.hateContainer.UpdateHate(caster, damage); + end; + return damage; +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/thundara.lua b/data/scripts/commands/magic/thundara.lua new file mode 100644 index 00000000..1d854b23 --- /dev/null +++ b/data/scripts/commands/magic/thundara.lua @@ -0,0 +1,24 @@ +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + local damage = math.random(10, 100); + + action.worldMasterTextId = 0x765D; + + -- todo: populate a global script with statuses and modifiers + -- magic.HandleAttackMagic(caster, target, spell, action) + action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); + + if target.hateContainer then + target.hateContainer.UpdateHate(caster, damage); + end; + return damage; +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/simian_thrash.lua b/data/scripts/commands/weaponskill/simian_thrash.lua new file mode 100644 index 00000000..0b85c48f --- /dev/null +++ b/data/scripts/commands/weaponskill/simian_thrash.lua @@ -0,0 +1,26 @@ +require("global"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +function onSkillFinish(caster, target, skill, action) + local damage = math.random(0, 0); + + -- todo: populate a global script with statuses and modifiers + action.worldMasterTextId = 0x765D; + + -- todo: populate a global script with statuses and modifiers + -- magic.HandleAttackMagic(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; \ No newline at end of file diff --git a/data/scripts/content/SimpleContent30010.lua b/data/scripts/content/SimpleContent30010.lua index 6f1a7de6..48010b06 100644 --- a/data/scripts/content/SimpleContent30010.lua +++ b/data/scripts/content/SimpleContent30010.lua @@ -10,12 +10,12 @@ function onCreate(starterPlayer, contentArea, director) --mob3 = contentArea:SpawnActor(2201407, "mob3", 375.125, 4.4, -703.591, -1.54); yda = GetWorldManager().SpawnBattleNpcById(6, contentArea); papalymo = GetWorldManager().SpawnBattleNpcById(7, contentArea); - yda:ChangeState(2); - mob1 = GetWorldManager().SpawnBattleNpcById(3, contentArea); - mob2 = GetWorldManager().SpawnBattleNpcById(4, contentArea); + --yda:ChangeState(2); + mob1 = GetWorldManager().SpawnBattleNpcById(3, contentArea); + mob2 = GetWorldManager().SpawnBattleNpcById(4, contentArea); mob3 = GetWorldManager().SpawnBattleNpcById(5, contentArea); - starterPlayer.currentParty.members:Add(yda.actorId); - starterPlayer.currentParty.members:Add(papalymo.actorId); + starterPlayer.currentParty:AddMember(papalymo.actorId); + starterPlayer.currentParty:AddMember(yda.actorId); starterPlayer:SetMod(modifiersGlobal.MinimumHpLock, 1); @@ -35,6 +35,47 @@ end function onDestroy() - +end +--Iterating over allies using the for...in syntax throws an except and lags the server. +--This function probably isn't needed either way, it does the smae thing as TryAggro in ally +function onUpdate(tick, area) + if area then + local players = area:GetPlayers() + local mobs = area:GetMonsters() + local allies = area:GetAllies() + local resumeChecks = true + for player in players do + if player then + local exitLoop = false + + if allies then + for i = 0, #allies - 1 do + if allies[i] then + if not allies[i]:IsEngaged() then + if player:IsEngaged() and player.target then + + allies[i].neutral = false + allies[i].isAutoAttackEnabled = true + allies[i]:SetMod(modifiersGlobal.Speed, 8) + allyGlobal.EngageTarget(allies[i], player.target) + exitLoop = true + break + -- todo: support scripted paths + elseif allies[i]:GetSpeed() > 0 then + end + end + end + end + end + if exitLoop then + resumeChecks = false + break + end + end + end + if not resumeChecks then + return + end + end end \ No newline at end of file diff --git a/data/scripts/directors/Quest/QuestDirectorMan0g001.lua b/data/scripts/directors/Quest/QuestDirectorMan0g001.lua index 1c766c02..38870d2e 100644 --- a/data/scripts/directors/Quest/QuestDirectorMan0g001.lua +++ b/data/scripts/directors/Quest/QuestDirectorMan0g001.lua @@ -10,55 +10,71 @@ function init() return "/Director/Quest/QuestDirectorMan0g001"; end +--Should we be using this to spawn mobs and drop Simplecontent? function onCreateContentArea(players, director, contentArea, contentGroup) director:StartContentGroup(); end function onEventStarted(player, actor, triggerName) man0g0Quest = player:GetQuest("Man0g0"); + player:SendMessage(0x20, "", "Starting"); startTutorialMode(player); callClientFunction(player, "delegateEvent", player, man0g0Quest, "processTtrBtl001", nil, nil, nil); player:EndEvent(); + player:SendMessage(0x20, "", "Waiting for player active"); waitForSignal("playerActive"); + player:SendMessage(0x20, "", "player active"); wait(1); --If this isn't here, the scripts bugs out. TODO: Find a better alternative. kickEventContinue(player, actor, "noticeEvent", "noticeEvent"); callClientFunction(player, "delegateEvent", player, man0g0Quest, "processTtrBtl002", nil, nil, nil); + player:SendMessage(0x20, "", "processTtrBtl002 called"); player:EndEvent(); - waitForSignal("playerAttack"); - closeTutorialWidget(player); - showTutorialSuccessWidget(player, 9055); --Open TutorialSuccessWidget for attacking enemy - wait(3); - openTutorialWidget(player, CONTROLLER_KEYBOARD, TUTORIAL_TP); - waitForSignal("tpOver1000"); - closeTutorialWidget(player); - openTutorialWidget(player, CONTROLLER_KEYBOARD, TUTORIAL_WEAPONSKILLS); + --Combat portion of tutorial if player:IsDiscipleOfWar() then - waitForSignal("weaponskillUsed"); --Should be wait for weaponskillUsed signal + waitForSignal("playerAttack"); + closeTutorialWidget(player); + showTutorialSuccessWidget(player, 9055); --Open TutorialSuccessWidget for attacking enemy + openTutorialWidget(player, CONTROLLER_KEYBOARD, TUTORIAL_TP); + waitForSignal("tpOver1000"); + closeTutorialWidget(player); + openTutorialWidget(player, CONTROLLER_KEYBOARD, TUTORIAL_WEAPONSKILLS); + waitForSignal("weaponskillUsed"); + closeTutorialWidget(player); + showTutorialSuccessWidget(player, 9065); --Open TutorialSuccessWidget for weapon skill elseif player:IsDiscipleOfMagic() then - waitForSignal("spellUsed") + openTutorialWidget(player, CONTROLLER_KEYBOARD, TUTORIAL_CASTING); + waitForSignal("spellUsed"); + closeTutorialWidget(player); elseif player:IsDiscipleOfHand() then - waitForSignal("abilityUsed") + waitForSignal("abilityUsed"); elseif player:IsDiscipleOfLand() then - waitForSignal("abilityUsed") + waitForSignal("abilityUsed"); end - closeTutorialWidget(player); - showTutorialSuccessWidget(player, 9065); --Open TutorialSuccessWidget for weapon skill - waitForSignal("mobkill"); --Should be wait for mobkill + --Currently this requires the player to pass all the other signals first, need a way for signals to work out of order + waitForSignal("mobkill"); waitForSignal("mobkill"); waitForSignal("mobkill"); worldMaster = GetWorldMaster(); + wait(5);--Debug waits, get rid of these later + player:Disengage(0x0000); + wait(5); + man0g0Quest:NextPhase(10); + wait(5); + --This doesn't work + player:RemoveFromCurrentPartyAndCleanup(); player:SendDataPacket("attention", worldMaster, "", 51073, 2); - wait(7); + wait(5); player:ChangeMusic(7); - player:ChangeState(0); + wait(5); kickEventContinue(player, actor, "noticeEvent", "noticeEvent"); + wait(5); callClientFunction(player, "delegateEvent", player, man0g0Quest, "processEvent020_1", nil, nil, nil); - - player:GetZone():ContentFinished(); - player:EndEvent(); - GetWorldManager():DoZoneChange(player, 155, "PrivateAreaMasterPast", 1, 15, 175.38, -1.21, -1156.51, -2.1); + wait(5); + player:GetZone():ContentFinished(); + --player:EndEvent(); + --GetWorldManager():DoZoneChange(player, 155, "PrivateAreaMasterPast", 1, 15, 175.38, -1.21, -1156.51, -2.1); --[[ IF DoW: OpenWidget (TP) @@ -69,13 +85,12 @@ function onEventStarted(player, actor, triggerName) Success CloseWidget ELSE MAGIC: - OpenWidget (DEFEAT ENEMY) + OpenWidget (DEFEAT ENEMY) ]] - --man0g0Quest:NextPhase(10); - --player:EndEvent(); - - --GetWorldManager():DoZoneChange(player, 155, "PrivateAreaMasterPast", 1, 15, 175.38, -1.21, -1156.51, -2.1); + player:EndEvent(); + wait(5); + GetWorldManager():DoZoneChange(player, 155, "PrivateAreaMasterPast", 1, 15, 175.38, -1.21, -1156.51, -2.1); end diff --git a/data/scripts/modifiers.lua b/data/scripts/modifiers.lua new file mode 100644 index 00000000..8783e26c --- /dev/null +++ b/data/scripts/modifiers.lua @@ -0,0 +1,79 @@ +modifiersGlobal = +{ + None = 0, + Hp = 1, + HpPercent = 2, + Mp = 3, + MpPercent = 4, + Tp = 5, + TpPercent = 6, + Regen = 7, + Refresh = 8, + Strength = 9, + Vitality = 10, + Dexterity = 11, + Intelligence = 12, + Mind = 13, + Piety = 14, + Attack = 15, + Accuracy = 16, + Defense = 17, + Evasion = 18, + MagicAttack = 19, + MagicHeal = 20, -- is this needed? shouldnt it just be calc'd from mind + MagicAccuracy = 21, + MagicEvasion = 22, + MagicDefense = 23, + MagicEnhancePotency = 24, + MagicEnfeeblingPotency = 25, + ResistFire = 26, + ResistIce = 27, + ResistWind = 28, + ResistLightning = 29, + ResistEarth = 30, + ResistWater = 31, -- <3 u jorge + AttackRange = 32, + Speed = 33, + AttackDelay = 34, + + CraftProcessing = 35, + CraftMagicProcessing = 36, + CraftProcessControl = 37, + + HarvestPotency = 38, + HarvestLimit = 39, + HarvestRate = 40, + + Raise = 41, + MinimumHpLock = 42, -- hp cannot fall below this value +} + +mobModifiersGlobal = +{ + 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 + Roams = 24, -- Do I walk around? + RoamDelay = 25 -- What is the delay between roam ticks +} \ No newline at end of file diff --git a/data/scripts/unique/fst0Battle03/Monster/papalymo.lua b/data/scripts/unique/fst0Battle03/Monster/papalymo.lua index cc9807e1..afdd66d9 100644 --- a/data/scripts/unique/fst0Battle03/Monster/papalymo.lua +++ b/data/scripts/unique/fst0Battle03/Monster/papalymo.lua @@ -19,4 +19,9 @@ function onRoam(ally, contentGroupCharas) ally.neutral = false ally.animationId = 0 allyGlobal.onCombatTick(ally, nil, nil, contentGroupCharas) +end + + +function tryAggro(ally, contentGroupCharas) + allyGlobal.tryAggro(ally, contentGroupCharas) end \ No newline at end of file diff --git a/data/scripts/unique/fst0Battle03/Monster/yda.lua b/data/scripts/unique/fst0Battle03/Monster/yda.lua index 0c8a9523..4462774b 100644 --- a/data/scripts/unique/fst0Battle03/Monster/yda.lua +++ b/data/scripts/unique/fst0Battle03/Monster/yda.lua @@ -13,10 +13,14 @@ function onCombatTick(ally, target, tick, contentGroupCharas) allyGlobal.onCombatTick(ally, target, tick, contentGroupCharas) end +function tryAggro(ally, contentGroupCharas) + allyGlobal.tryAggro(ally, contentGroupCharas) +end + function onRoam(ally, contentGroupCharas) ally.detectionType = 0xFF ally.isMovingToSpawn = false ally.neutral = false ally.animationId = 0 - allyGlobal.onCombatTick(ally, contentGroupCharas) + --allyGlobal.onCombatTick(ally, contentGroupCharas) end \ No newline at end of file diff --git a/data/scripts/unique/fst0Battle03/PopulaceStandard/yda.lua b/data/scripts/unique/fst0Battle03/PopulaceStandard/yda.lua index 0f4d5f7e..6bfcb1fc 100644 --- a/data/scripts/unique/fst0Battle03/PopulaceStandard/yda.lua +++ b/data/scripts/unique/fst0Battle03/PopulaceStandard/yda.lua @@ -7,10 +7,9 @@ end function onEventStarted(player, npc, triggerName) man0g0Quest = player:GetQuest("Man0g0"); - print("hi"); - if (man0g0Quest ~= nil) then - - print("hi2"); + print("Got Quest Man0g0"); + if (man0g0Quest ~= nil) then + print("Man0g0Quest is not nil"); if (triggerName == "pushDefault") then callClientFunction(player, "delegateEvent", player, man0g0Quest, "processTtrNomal002", nil, nil, nil); elseif (triggerName == "talkDefault") then @@ -23,8 +22,8 @@ function onEventStarted(player, npc, triggerName) man0g0Quest:SaveData(); player:GetDirector("OpeningDirector"):onTalkEvent(player, npc); --Was she talked to after papalymo? - print("hi3"); else + print("Making content area"); if (man0g0Quest:GetQuestFlag(MAN0G0_FLAG_MINITUT_DONE1) == true) then player:EndEvent(); @@ -37,14 +36,15 @@ function onEventStarted(player, npc, triggerName) end director = contentArea:GetContentDirector(); - player:AddDirector(director); + player:AddDirector(director); director:StartDirector(false); player:KickEvent(director, "noticeEvent", true); player:SetLoginDirector(director); - print("hi5"); + print("Content area and director made"); GetWorldManager():DoZoneChangeContent(player, contentArea, 362.4087, 4, -703.8168, 1.5419, 16); + print("Zone Change"); return; else callClientFunction(player, "delegateEvent", player, man0g0Quest, "processEvent000_1", nil, nil, nil);