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);