1
Fork 0
mirror of https://bitbucket.org/Ioncannon/project-meteor-server.git synced 2025-04-25 06:07:46 +00:00

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

This commit is contained in:
yogurt 2017-12-08 00:58:39 -06:00
parent 520ae7a119
commit 1275c8b5da
61 changed files with 1226 additions and 223 deletions

View file

@ -511,7 +511,6 @@ namespace FFXIVClassic_Map_Server
battleNpc.SetMod((uint)Modifier.Defense, reader.GetUInt32("def")); battleNpc.SetMod((uint)Modifier.Defense, reader.GetUInt32("def"));
battleNpc.SetMod((uint)Modifier.Evasion, reader.GetUInt32("eva")); battleNpc.SetMod((uint)Modifier.Evasion, reader.GetUInt32("eva"));
battleNpc.dropListId = reader.GetUInt32("dropListId"); battleNpc.dropListId = reader.GetUInt32("dropListId");
battleNpc.spellListId = reader.GetUInt32("spellListId"); battleNpc.spellListId = reader.GetUInt32("spellListId");
battleNpc.skillListId = reader.GetUInt32("skillListId"); 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.Defense, reader.GetUInt32("def"));
battleNpc.SetMod((uint)Modifier.Evasion, reader.GetUInt32("eva")); 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.dropListId = reader.GetUInt32("dropListId");
battleNpc.spellListId = reader.GetUInt32("spellListId"); battleNpc.spellListId = reader.GetUInt32("spellListId");
battleNpc.skillListId = reader.GetUInt32("skillListId"); battleNpc.skillListId = reader.GetUInt32("skillListId");
battleNpc.SetMaxHP(1000);
battleNpc.SetHP(1000);
battleNpc.SetBattleNpcId(reader.GetUInt32("bnpcId")); battleNpc.SetBattleNpcId(reader.GetUInt32("bnpcId"));
battleNpc.SetRespawnTime(reader.GetUInt32("respawnTime"));
battleNpc.CalculateBaseStats(); battleNpc.CalculateBaseStats();
battleNpc.RecalculateStats(); battleNpc.RecalculateStats();
//battleNpc.SetMod((uint)Modifier.ResistFire, ) //battleNpc.SetMod((uint)Modifier.ResistFire, )
@ -679,7 +714,7 @@ namespace FFXIVClassic_Map_Server
try try
{ {
conn.Open(); 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); MySqlCommand cmd = new MySqlCommand(query, conn);
@ -688,9 +723,9 @@ namespace FFXIVClassic_Map_Server
while (reader.Read()) while (reader.Read())
{ {
var id = reader.GetUInt32(primaryKey); 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")); modList.SetModifier(reader.GetUInt16("modId"), reader.GetInt64("modVal"), reader.GetBoolean("isMobMod"));
list.Add(id, modList); list[id] = modList;
} }
} }
} }

View file

@ -438,9 +438,6 @@ namespace FFXIVClassic_Map_Server.Actors
updateFlags = ActorUpdateFlags.None; updateFlags = ActorUpdateFlags.None;
zone.BroadcastPacketsAroundActor(this, packets); zone.BroadcastPacketsAroundActor(this, packets);
SetActorPropetyPacket hpInfo = new SetActorPropetyPacket("charaWork/exp");
hpInfo.AddTarget();
} }
} }

View file

@ -421,6 +421,11 @@ namespace FFXIVClassic_Map_Server.Actors
return GetAllActors<BattleNpc>(); return GetAllActors<BattleNpc>();
} }
public virtual List<Ally> GetAllies()
{
return GetAllActors<Ally>();
}
public void BroadcastPacketsAroundActor(Actor actor, List<SubPacket> packets) public void BroadcastPacketsAroundActor(Actor actor, List<SubPacket> packets)
{ {
foreach (SubPacket packet in packets) foreach (SubPacket packet in 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 = new Npc(mActorList.Count + 1, actorClass, uniqueId, this, x, y, z, rot, state, animId, null);
npc.LoadEventConditions(actorClass.eventConditions); npc.LoadEventConditions(actorClass.eventConditions);
npc.SetMaxHP(300); //npc.SetMaxHP(30000);
npc.SetHP(300); //npc.SetHP(30000);
AddActorToZone(npc); AddActorToZone(npc);
@ -666,7 +671,7 @@ namespace FFXIVClassic_Map_Server.Actors
a.Update(tick); a.Update(tick);
var deltaTime = (tick - Program.LastTick).TotalMilliseconds; var deltaTime = (tick - Program.LastTick).TotalMilliseconds;
LuaEngine.GetInstance().CallLuaFunction(null, this, "onUpdate", true, deltaTime, this); //LuaEngine.GetInstance().CallLuaFunction(null, this, "onUpdate", true, this, deltaTime);
} }
} }

View file

@ -132,6 +132,18 @@ namespace FFXIVClassic_Map_Server.actors.area
return actor; return actor;
} }
} }
foreach (List<PrivateAreaContent> paList in contentAreas.Values)
{
foreach (PrivateArea pa in paList)
{
Actor actor = pa.FindActorInArea(id);
if (actor != null)
return actor;
}
}
return null; return null;
} }
else else

View file

@ -230,10 +230,10 @@ namespace FFXIVClassic_Map_Server.Actors
public void DoBattleAction(ushort commandId, uint animationId, BattleAction[] actions) public void DoBattleAction(ushort commandId, uint animationId, BattleAction[] actions)
{ {
int currentIndex = 0; int currentIndex = 0;
//AoE abilities only ever hit 16 people, so we probably won't need this loop anymore
while (true) while (true)
{ {
if (actions.Length - currentIndex >= 18) if (actions.Length - currentIndex >= 10)
zone.BroadcastPacketAroundActor(this, BattleActionX18Packet.BuildPacket(actorId, animationId, commandId, actions, ref currentIndex)); zone.BroadcastPacketAroundActor(this, BattleActionX18Packet.BuildPacket(actorId, animationId, commandId, actions, ref currentIndex));
else if (actions.Length - currentIndex > 1) else if (actions.Length - currentIndex > 1)
zone.BroadcastPacketAroundActor(this, BattleActionX10Packet.BuildPacket(actorId, animationId, commandId, actions, ref currentIndex)); zone.BroadcastPacketAroundActor(this, BattleActionX10Packet.BuildPacket(actorId, animationId, commandId, actions, ref currentIndex));
@ -244,7 +244,9 @@ namespace FFXIVClassic_Map_Server.Actors
} }
else else
break; 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) public virtual void OnPath(Vector3 point)
{ {
lua.LuaEngine.CallLuaBattleFunction(this, "onPath", this, point); //lua.LuaEngine.CallLuaBattleFunction(this, "onPath", this, point);
updateFlags |= ActorUpdateFlags.Position; updateFlags |= ActorUpdateFlags.Position;
this.isAtSpawn = false; this.isAtSpawn = false;
@ -347,7 +349,7 @@ namespace FFXIVClassic_Map_Server.Actors
if ((updateFlags & ActorUpdateFlags.HpTpMp) != 0) 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.mp");
propPacketUtil.AddProperty("charaWork.parameterSave.mpMax"); propPacketUtil.AddProperty("charaWork.parameterSave.mpMax");
@ -470,6 +472,7 @@ namespace FFXIVClassic_Map_Server.Actors
{ {
// todo: actual despawn timer // todo: actual despawn timer
aiContainer.InternalDie(tick, 10); aiContainer.InternalDie(tick, 10);
ChangeSpeed(0.0f, 0.0f, 0.0f, 0.0f);
} }
public virtual void Despawn(DateTime tick) public virtual void Despawn(DateTime tick)
@ -543,6 +546,20 @@ namespace FFXIVClassic_Map_Server.Actors
updateFlags |= ActorUpdateFlags.HpTpMp; 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 // 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) 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]; return charaWork.parameterSave.state_mainSkill[0];
} }
@ -614,8 +631,30 @@ namespace FFXIVClassic_Map_Server.Actors
public void RecalculateStats() 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 // todo: recalculate stats and crap
updateFlags |= ActorUpdateFlags.HpTpMp; 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); //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 // todo: call onAttack/onDamageTaken
target.DelHP(action.amount); BattleUtils.DamageTarget(this, target, action);
target.OnDamageTaken(this, action, DamageTakenType.Ability);
if (target is BattleNpc) if (target is BattleNpc)
((BattleNpc)target).lastAttacker = this; ((BattleNpc)target).lastAttacker = this;
@ -664,13 +704,26 @@ namespace FFXIVClassic_Map_Server.Actors
{ {
var spell = ((MagicState)state).GetSpell(); var spell = ((MagicState)state).GetSpell();
// damage is handled in script // 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) foreach (BattleAction action in actions)
zone.FindActorInArea<Character>(action.targetId).OnDamageTaken(this, action, DamageTakenType.Magic); {
if (zone.FindActorInArea<Character>(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) if (target is BattleNpc)
((BattleNpc)target).lastAttacker = this;
lua.LuaEngine.GetInstance().OnSignal("spellUsed");
} }
public virtual void OnWeaponSkill(State state, BattleAction[] actions, ref BattleAction[] errors) 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 // damage is handled in script
this.DelTP(skill.tpCost); this.DelTP(skill.tpCost);
foreach (var action in actions) foreach (BattleAction action in actions)
zone.FindActorInArea<BattleNpc>(action.targetId)?.OnDamageTaken(this, action, DamageTakenType.Weaponskill); {
if (zone.FindActorInArea<Character>(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) if (target is BattleNpc)
((BattleNpc)target).lastAttacker = this; ((BattleNpc)target).lastAttacker = this;
lua.LuaEngine.GetInstance().OnSignal("weaponskillUsed");
} }
public virtual void OnAbility(State state, BattleAction[] actions, ref BattleAction[] errors) public virtual void OnAbility(State state, BattleAction[] actions, ref BattleAction[] errors)
@ -769,22 +836,22 @@ namespace FFXIVClassic_Map_Server.Actors
public bool IsDiscipleOfWar() public bool IsDiscipleOfWar()
{ {
return currentJob < CLASSID_THM; return GetClass() < CLASSID_THM;
} }
public bool IsDiscipleOfMagic() public bool IsDiscipleOfMagic()
{ {
return currentJob >= CLASSID_THM && currentJob < CLASSID_CRP; return GetClass() >= CLASSID_THM && currentJob < CLASSID_CRP;
} }
public bool IsDiscipleOfHand() public bool IsDiscipleOfHand()
{ {
return currentJob >= CLASSID_CRP && currentJob < CLASSID_MIN; return GetClass() >= CLASSID_CRP && currentJob < CLASSID_MIN;
} }
public bool IsDiscipleOfLand() public bool IsDiscipleOfLand()
{ {
return currentJob >= CLASSID_MIN; return GetClass() >= CLASSID_MIN;
} }
#endregion lua helpers #endregion lua helpers
#endregion ai stuff #endregion ai stuff

View file

@ -5,6 +5,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using FFXIVClassic_Map_Server.Actors; using FFXIVClassic_Map_Server.Actors;
using FFXIVClassic_Map_Server.actors.chara.npc; using FFXIVClassic_Map_Server.actors.chara.npc;
using FFXIVClassic.Common;
namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
{ {
@ -18,34 +19,61 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
this.owner = owner; this.owner = owner;
} }
protected List<Character> GetContentGroupCharas()
{
List<Character> contentGroupCharas = null;
if (owner.currentContentGroup != null)
{
contentGroupCharas = new List<Character>(owner.currentContentGroup.GetMemberCount());
foreach (var charaId in owner.currentContentGroup.GetMembers())
{
var chara = owner.zone.FindActorInArea<Character>(charaId);
if (chara != null)
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 // server really likes to hang whenever scripts iterate area's actorlist
protected override void DoCombatTick(DateTime tick, List<Character> contentGroupCharas = null) protected override void DoCombatTick(DateTime tick, List<Character> contentGroupCharas = null)
{ {
if (owner.currentContentGroup != null) if (contentGroupCharas == null)
{ {
contentGroupCharas = new List<Character>(owner.currentContentGroup.GetMemberCount()); contentGroupCharas = GetContentGroupCharas();
foreach (var charaId in owner.currentContentGroup.GetMembers()) }
{
var chara = owner.zone.FindActorInArea<Character>(charaId);
if (chara != null)
contentGroupCharas.Add(chara);
}
}
base.DoCombatTick(tick, contentGroupCharas); base.DoCombatTick(tick, contentGroupCharas);
} }
protected override void DoRoamTick(DateTime tick, List<Character> contentGroupCharas = null) protected override void DoRoamTick(DateTime tick, List<Character> contentGroupCharas = null)
{ {
if (owner.currentContentGroup != null) if (contentGroupCharas == null)
{ {
contentGroupCharas = new List<Character>(owner.currentContentGroup.GetMemberCount()); contentGroupCharas = GetContentGroupCharas();
foreach (var charaId in owner.currentContentGroup.GetMembers())
{
var chara = owner.zone.FindActorInArea<Character>(charaId);
if (chara != null)
contentGroupCharas.Add(chara);
}
} }
base.DoRoamTick(tick, contentGroupCharas); base.DoRoamTick(tick, contentGroupCharas);
} }

View file

@ -39,12 +39,20 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
public override void Update(DateTime tick) public override void Update(DateTime tick)
{ {
lastUpdate = tick; lastUpdate = tick;
// todo: handle aggro/deaggro and other shit here // todo: handle aggro/deaggro and other shit here
if (!owner.aiContainer.IsEngaged())
{
TryAggro(tick);
}
if(owner.aiContainer.IsEngaged()) if(owner.aiContainer.IsEngaged())
{ {
DoCombatTick(tick); 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); DoRoamTick(tick);
} }
@ -63,6 +71,38 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
return false; 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<Character>(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) public override bool Engage(Character target)
{ {
var canEngage = this.owner.aiContainer.InternalEngage(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.ChangeState(SetActorStatePacket.MAIN_STATE_ACTIVE);
owner.moveState = 2; owner.moveState = 2;
//owner.SetMod((uint)Modifier.Speed, 5);
lastActionTime = DateTime.Now; lastActionTime = DateTime.Now;
battleStartTime = lastActionTime; battleStartTime = lastActionTime;
// todo: adjust cooldowns with modifiers // todo: adjust cooldowns with modifiers
@ -129,12 +170,6 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
protected virtual void DoRoamTick(DateTime tick, List<Character> contentGroupCharas = null) protected virtual void DoRoamTick(DateTime tick, List<Character> contentGroupCharas = null)
{ {
if (owner.hateContainer.GetHateList().Count > 0)
{
Engage(owner.hateContainer.GetMostHatedTarget());
return;
}
if (tick >= waitTime) if (tick >= waitTime)
{ {
neutralTime = tick.AddSeconds(5); 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); owner.OnRoam(tick);
if (CanMoveForward(0.0f) && !owner.aiContainer.pathFind.IsFollowingPath()) 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.SetPathFlags(PathFindFlags.None);
owner.aiContainer.pathFind.PathInRange(owner.spawnX, owner.spawnY, owner.spawnZ, 1.5f, 50.0f); owner.aiContainer.pathFind.PathInRange(owner.spawnX, owner.spawnY, owner.spawnZ, 1.5f, 50.0f);
} }
lua.LuaEngine.CallLuaBattleFunction(owner, "onRoam", owner, contentGroupCharas); //lua.LuaEngine.CallLuaBattleFunction(owner, "onRoam", owner, contentGroupCharas);
}
if (tick >= neutralTime)
{
if (!owner.neutral && owner.IsAlive())
{
foreach (var chara in owner.zone.GetActorsAroundActor<Character>(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;
}
}
}
}
} }
if (owner.aiContainer.pathFind.IsFollowingPath() && owner.aiContainer.CanFollowPath()) if (owner.aiContainer.pathFind.IsFollowingPath() && owner.aiContainer.CanFollowPath())
@ -202,8 +213,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
return; return;
} }
Move(); Move();
lua.LuaEngine.CallLuaBattleFunction(owner, "onCombatTick", owner, owner.target, Utils.UnixTimeStampUTC(tick), contentGroupCharas);
} }
protected virtual void Move() protected virtual void Move()

View file

@ -159,6 +159,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
/// </summary> /// </summary>
public void FindWithinArea(Character target, ValidTarget flags, TargetFindAOETarget aoeTarget) public void FindWithinArea(Character target, ValidTarget flags, TargetFindAOETarget aoeTarget)
{ {
targets.Clear();
validTarget = flags; validTarget = flags;
// are we creating aoe circles around target or self // are we creating aoe circles around target or self
if (aoeTarget == TargetFindAOETarget.Self) if (aoeTarget == TargetFindAOETarget.Self)
@ -174,6 +175,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
if (masterTarget != null) if (masterTarget != null)
targets.Add(masterTarget); targets.Add(masterTarget);
if (aoeType != TargetFindAOEType.None)
{
if (IsPlayer(owner)) if (IsPlayer(owner))
{ {
if (masterTarget is Player) if (masterTarget is Player)
@ -226,7 +229,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
AddAllInHateList(); AddAllInHateList();
} }
} }
}
if(targets.Count > 16)
targets.RemoveRange(16, targets.Count - 16);
} }
/// <summary> /// <summary>
@ -288,7 +293,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
private void AddAllBattleNpcs(Character target, bool withPet) private void AddAllBattleNpcs(Character target, bool withPet)
{ {
var actors = owner.zone.GetActorsAroundActor<BattleNpc>(owner, 50); int dist = (int)maxDistance;
var actors = owner.zone.GetActorsAroundActor<BattleNpc>(target, dist);
foreach (BattleNpc actor in actors) foreach (BattleNpc actor in actors)
{ {
@ -375,12 +381,13 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
private bool IsWithinCircle(Character target, float maxDistance) private bool IsWithinCircle(Character target, float maxDistance)
{ {
// todo: make y diff modifiable? // 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) if (this.targetPosition == null)
this.targetPosition = aoeTarget == TargetFindAOETarget.Self ? owner.GetPosAsVector3() : masterTarget.GetPosAsVector3(); 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) private bool IsPlayer(Character target)

View file

@ -15,7 +15,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
: base(owner, null) : base(owner, null)
{ {
owner.Disengage(); 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; canInterrupt = false;
startTime = tick; startTime = tick;
despawnTime = startTime.AddSeconds(timeToFadeOut); despawnTime = startTime.AddSeconds(timeToFadeOut);

View file

@ -110,16 +110,16 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
var i = 0; var i = 0;
foreach (var chara in targets) 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); action.amount = (ushort)lua.LuaEngine.CallLuaBattleCommandFunction(owner, spell, "magic", "onMagicFinish", owner, chara, spell, action);
actions[i++] = action; actions[i++] = action;
} }
// todo: this is fuckin stupid, probably only need *one* error packet, not an error for each action // todo: this is fuckin stupid, probably only need *one* error packet, not an error for each action
var errors = (BattleAction[])actions.Clone(); var errors = (BattleAction[])actions.Clone();
owner.OnCast(this, actions, ref errors); owner.OnCast(this, actions, ref errors);
owner.DoBattleAction(spell.id, spell.battleAnimation, actions); owner.DoBattleAction(spell.id, spell.battleAnimation, actions);
} }
public override void TryInterrupt() public override void TryInterrupt()

View file

@ -88,7 +88,6 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
{ {
skill.targetFind.FindWithinArea(target, skill.validTarget, skill.aoeTarget); skill.targetFind.FindWithinArea(target, skill.validTarget, skill.aoeTarget);
isCompleted = true; isCompleted = true;
var targets = skill.targetFind.GetTargets(); var targets = skill.targetFind.GetTargets();
BattleAction[] actions = new BattleAction[targets.Count]; BattleAction[] actions = new BattleAction[targets.Count];
@ -100,11 +99,11 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
// evasion, miss, dodge, etc to be handled in script, calling helpers from scripts/weaponskills.lua // 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); action.amount = (ushort)lua.LuaEngine.CallLuaBattleCommandFunction(owner, skill, "weaponskill", "onSkillFinish", owner, target, skill, action);
actions[i++] = 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 // todo: this is fuckin stupid, probably only need *one* error packet, not an error for each action
var errors = (BattleAction[])actions.Clone(); var errors = (BattleAction[])actions.Clone();
owner.OnWeaponSkill(this, actions, ref errors); owner.OnWeaponSkill(this, actions, ref errors);
owner.DoBattleAction(skill.id, skill.battleAnimation, actions); owner.DoBattleAction(skill.id, skill.battleAnimation, actions);
} }

View file

@ -77,6 +77,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
} }
public static void DamageTarget(Character attacker, Character defender, BattleAction action) public static void DamageTarget(Character attacker, Character defender, BattleAction action)
{
if (defender != null)
{ {
// todo: other stuff too // todo: other stuff too
if (defender is BattleNpc) if (defender is BattleNpc)
@ -89,6 +91,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
} }
defender.DelHP((short)action.amount); defender.DelHP((short)action.amount);
} }
}
public static int CalculateSpellDamage(Character attacker, Character defender, BattleCommand spell) public static int CalculateSpellDamage(Character attacker, Character defender, BattleCommand spell)
{ {

View file

@ -177,7 +177,7 @@ namespace FFXIVClassic_Map_Server.Actors
packets = new List<SubPacket>(); packets = new List<SubPacket>();
if ((updateFlags & ActorUpdateFlags.HpTpMp) != 0) 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.hp[0]");
propPacketUtil.AddProperty("charaWork.parameterSave.hpMax[0]"); propPacketUtil.AddProperty("charaWork.parameterSave.hpMax[0]");
@ -290,6 +290,10 @@ namespace FFXIVClassic_Map_Server.Actors
if (lastAttacker is Player) 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
// <actor> defeat/defeats <target>
//((Player)lastAttacker).QueuePacket(BattleActionX01Packet.BuildPacket(lastAttacker.actorId, 0, 0, new BattleAction(actorId, 30108, 0)));
if (lastAttacker.currentParty != null && lastAttacker.currentParty is Party) if (lastAttacker.currentParty != null && lastAttacker.currentParty is Party)
{ {
foreach (var memberId in ((Party)lastAttacker.currentParty).members) foreach (var memberId in ((Party)lastAttacker.currentParty).members)
@ -297,14 +301,9 @@ namespace FFXIVClassic_Map_Server.Actors
var partyMember = zone.FindActorInArea<Player>(memberId); var partyMember = zone.FindActorInArea<Player>(memberId);
// onDeath(monster, player, killer) // onDeath(monster, player, killer)
lua.LuaEngine.CallLuaBattleFunction(this, "onDeath", this, partyMember, lastAttacker); lua.LuaEngine.CallLuaBattleFunction(this, "onDeath", this, partyMember, lastAttacker);
// <actor> defeat/defeats <target>
if (lastAttacker is Player)
((Player)lastAttacker).QueuePacket(BattleActionX01Packet.BuildPacket(lastAttacker.actorId, 0, 0, new BattleAction(actorId, 30108, 0)));
if(partyMember is Player) if(partyMember is Player)
((Player)partyMember).AddExp(1500, (byte)partyMember.GetJob(), 5); ((Player)partyMember).AddExp(1500, (byte)partyMember.GetClass(), 5);
} }
} }
else else
@ -317,7 +316,6 @@ namespace FFXIVClassic_Map_Server.Actors
positionUpdates?.Clear(); positionUpdates?.Clear();
aiContainer.InternalDie(tick, despawnTime); aiContainer.InternalDie(tick, despawnTime);
this.ResetMoveSpeeds(); this.ResetMoveSpeeds();
// todo: reset cooldowns // todo: reset cooldowns
lua.LuaEngine.GetInstance().OnSignal("mobkill"); lua.LuaEngine.GetInstance().OnSignal("mobkill");
@ -381,6 +379,9 @@ namespace FFXIVClassic_Map_Server.Actors
if (GetMobMod((uint)MobModifier.AttackScript) != 0) if (GetMobMod((uint)MobModifier.AttackScript) != 0)
lua.LuaEngine.CallLuaBattleFunction(this, "onAttack", this, state.GetTarget(), action.amount); 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) 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) if (GetMobMod((uint)MobModifier.SpellScript) != 0)
foreach (var action in actions) foreach (var action in actions)
lua.LuaEngine.CallLuaBattleFunction(this, "onCast", this, zone.FindActorInArea<Character>(action.targetId), ((MagicState)state).GetSpell(), action); lua.LuaEngine.CallLuaBattleFunction(this, "onCast", this, zone.FindActorInArea<Character>(action.targetId), ((MagicState)state).GetSpell(), action);
foreach (BattleAction action in actions)
{
if (zone.FindActorInArea<Character>(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) public override void OnAbility(State state, BattleAction[] actions, ref BattleAction[] errors)

View file

@ -32,5 +32,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.npc
AbilityScript = 21, // call my script's onAbility whenever i finish using an ability 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 CallForHelp = 22, // actor with this id outside of target's party with this can attack me
FreeForAll = 23, // any actor 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
} }
} }

View file

@ -557,6 +557,9 @@ namespace FFXIVClassic_Map_Server.Actors
if (currentContentGroup != null) if (currentContentGroup != null)
currentContentGroup.SendGroupPackets(playerSession); currentContentGroup.SendGroupPackets(playerSession);
if (currentParty != null)
currentParty.SendGroupPackets(playerSession);
} }
private void SendRemoveInventoryPackets(List<ushort> slots) private void SendRemoveInventoryPackets(List<ushort> slots)
@ -1714,6 +1717,7 @@ namespace FFXIVClassic_Map_Server.Actors
//currentParty.members.Remove(this); //currentParty.members.Remove(this);
if (partyGroup.members.Count == 0) if (partyGroup.members.Count == 0)
Server.GetWorldManager().NoMembersInParty((Party)currentParty); Server.GetWorldManager().NoMembersInParty((Party)currentParty);
currentParty = null; currentParty = null;
} }
@ -1755,7 +1759,7 @@ namespace FFXIVClassic_Map_Server.Actors
if ((updateFlags & ActorUpdateFlags.HpTpMp) != 0) 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? // todo: should this be using job as index?
propPacketUtil.AddProperty($"charaWork.parameterSave.hp[{0}]"); propPacketUtil.AddProperty($"charaWork.parameterSave.hp[{0}]");
@ -1768,9 +1772,6 @@ namespace FFXIVClassic_Map_Server.Actors
} }
base.PostUpdate(tick, packets); base.PostUpdate(tick, packets);
SetActorPropetyPacket hpInfo = new SetActorPropetyPacket("charaWork/exp");
hpInfo.AddTarget();
QueuePacket(hpInfo.BuildPacket(actorId));
} }
public override void Die(DateTime tick) public override void Die(DateTime tick)
@ -1801,7 +1802,6 @@ namespace FFXIVClassic_Map_Server.Actors
public void UpdateHotbarCommands(List<ushort> slotsToUpdate) public void UpdateHotbarCommands(List<ushort> slotsToUpdate)
{ {
ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil("charaWork/command", this); ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil("charaWork/command", this);
ActorPropertyPacketUtil compatibiltyUtil = new ActorPropertyPacketUtil("charaWork/commandDetailForSelf", this);
foreach (ushort slot in slotsToUpdate) foreach (ushort slot in slotsToUpdate)
{ {
propPacketUtil.AddProperty($"charaWork.command[{slot}]"); 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) // 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); base.OnCast(state, actions, ref errors);
var spell = ((MagicState)state).GetSpell(); 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); UpdateHotbarTimer(spell.id, spell.recastTimeSeconds);
} }
@ -2300,11 +2300,11 @@ namespace FFXIVClassic_Map_Server.Actors
Database.LoadHotbar(this); 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() public byte GetCurrentClassOrJob()
{ {
if (currentJob != 0) if (currentJob != 0)
return (byte) currentJob; return (byte) currentJob;
return charaWork.parameterSave.state_mainSkill[0]; return charaWork.parameterSave.state_mainSkill[0];
} }
@ -2315,12 +2315,8 @@ namespace FFXIVClassic_Map_Server.Actors
mpMaxBase = (ushort)hp; mpMaxBase = (ushort)hp;
charaWork.parameterSave.mpMax = (short)hp; charaWork.parameterSave.mpMax = (short)hp;
charaWork.parameterSave.mp = (short)hp; charaWork.parameterSave.mp = (short)hp;
AddTP(0); AddTP(3000);
//SendCharaExpInfo(); updateFlags |= ActorUpdateFlags.HpTpMp;
//ActorPropertyPacketUtil exp = new ActorPropertyPacketUtil("charaWork/exp", this);
SetActorPropetyPacket hpInfo = new SetActorPropetyPacket("charaWork/exp");
hpInfo.AddTarget();
QueuePacket(hpInfo.BuildPacket(actorId));
} }
} }

View file

@ -161,6 +161,9 @@ namespace FFXIVClassic_Map_Server.actors.director
{ {
members.Add(actor); members.Add(actor);
if (actor is Player)
((Player)actor).AddDirector(this);
if (contentGroup != null) if (contentGroup != null)
contentGroup.AddMember(actor); contentGroup.AddMember(actor);
} }

View file

@ -42,6 +42,7 @@ namespace FFXIVClassic_Map_Server.actors.group
public void Start() public void Start()
{ {
isStarted = true; isStarted = true;
SendGroupPacketsAll(members); SendGroupPacketsAll(members);
} }
@ -50,6 +51,7 @@ namespace FFXIVClassic_Map_Server.actors.group
if (actor == null) if (actor == null)
return; return;
if(!members.Contains(actor.actorId))
members.Add(actor.actorId); members.Add(actor.actorId);
if (actor is Character) if (actor is Character)
@ -121,7 +123,6 @@ namespace FFXIVClassic_Map_Server.actors.group
} }
session.QueuePacket(GroupMembersEndPacket.buildPacket(session.id, session.GetActor().zoneId, time, this)); session.QueuePacket(GroupMembersEndPacket.buildPacket(session.id, session.GetActor().zoneId, time, this));
} }
public override uint GetTypeId() public override uint GetTypeId()

View file

@ -64,11 +64,23 @@ namespace FFXIVClassic_Map_Server.actors.group
groupMembers.Add(new GroupMember(id, -1, 0, false, true, Server.GetWorldManager().GetActorInWorld(id).customDisplayName)); groupMembers.Add(new GroupMember(id, -1, 0, false, true, Server.GetWorldManager().GetActorInWorld(id).customDisplayName));
foreach (uint charaId in members) foreach (uint charaId in members)
{ {
if (charaId != id) var chara = Server.GetWorldManager().GetActorInWorld(charaId);
groupMembers.Add(new GroupMember(charaId, -1, 0, false, true, Server.GetWorldManager().GetActorInWorld(charaId).customDisplayName)); if (charaId != id && chara != null)
groupMembers.Add(new GroupMember(charaId, -1, 0, false, true, chara.customDisplayName));
} }
return groupMembers; return groupMembers;
} }
public void AddMember(uint memberId)
{
members.Add(memberId);
SendGroupPacketsAll(members);
}
public void RemoveMember(uint memberId)
{
members.Remove(memberId);
SendGroupPacketsAll(members);
}
} }
} }

View file

@ -24,7 +24,7 @@ namespace FFXIVClassic_Map_Server.dataobjects
public void FlushQueuedSendPackets() public void FlushQueuedSendPackets()
{ {
if (!socket.Connected) if (socket == null || !socket.Connected)
return; return;
while (SendPacketQueue.Count > 0) while (SendPacketQueue.Count > 0)

View file

@ -477,12 +477,20 @@ namespace FFXIVClassic_Map_Server.lua
if (script != null) if (script != null)
{ {
if (!script.Globals.Get(funcName).IsNil()) if (!script.Globals.Get(funcName).IsNil())
{
try
{ {
Coroutine coroutine = script.CreateCoroutine(script.Globals[funcName]).Coroutine; Coroutine coroutine = script.CreateCoroutine(script.Globals[funcName]).Coroutine;
DynValue value = coroutine.Resume(args2); DynValue value = coroutine.Resume(args2);
ResolveResume(player, coroutine, value); ResolveResume(player, coroutine, value);
}
catch(Exception e)
{
player.SendMessage(0x20, "", e.Message);
player.EndEvent();
} }
}
else else
{ {
if (!optional) if (!optional)

View file

@ -112,7 +112,6 @@ namespace FFXIVClassic_Map_Server.packets.send.actor
{ {
string[] split = name.Split('.'); string[] split = name.Split('.');
int arrayIndex = 0; int arrayIndex = 0;
if (!(split[0].Equals("work") || split[0].Equals("charaWork") || split[0].Equals("playerWork") || split[0].Equals("npcWork") || split[0].Equals("guildleveWork"))) if (!(split[0].Equals("work") || split[0].Equals("charaWork") || split[0].Equals("playerWork") || split[0].Equals("npcWork") || split[0].Equals("guildleveWork")))
return false; return false;

View file

@ -35,27 +35,27 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
binWriter.Write((UInt16)commandId); binWriter.Write((UInt16)commandId);
binWriter.Write((UInt16)0x810); //? binWriter.Write((UInt16)0x810); //?
binWriter.Seek(0x58, SeekOrigin.Begin); binWriter.Seek(0x28, SeekOrigin.Begin);
for (int i = 0; i < max; i++) for (int i = 0; i < max; i++)
binWriter.Write((UInt32)actionList[listOffset + i].targetId); binWriter.Write((UInt32)actionList[listOffset + i].targetId);
binWriter.Seek(0xA0, SeekOrigin.Begin); binWriter.Seek(0x70, SeekOrigin.Begin);
for (int i = 0; i < max; i++) for (int i = 0; i < max; i++)
binWriter.Write((UInt16)actionList[listOffset + i].amount); binWriter.Write((UInt16)actionList[listOffset + i].amount);
binWriter.Seek(0xC4, SeekOrigin.Begin); binWriter.Seek(0x94, SeekOrigin.Begin);
for (int i = 0; i < max; i++) for (int i = 0; i < max; i++)
binWriter.Write((UInt16)actionList[listOffset + i].worldMasterTextId); binWriter.Write((UInt16)actionList[listOffset + i].worldMasterTextId);
binWriter.Seek(0xE8, SeekOrigin.Begin); binWriter.Seek(0xB8, SeekOrigin.Begin);
for (int i = 0; i < max; i++) for (int i = 0; i < max; i++)
binWriter.Write((UInt32)actionList[listOffset + i].effectId); binWriter.Write((UInt32)actionList[listOffset + i].effectId);
binWriter.Seek(0x130, SeekOrigin.Begin); binWriter.Seek(0x100, SeekOrigin.Begin);
for (int i = 0; i < max; i++) for (int i = 0; i < max; i++)
binWriter.Write((Byte)actionList[listOffset + i].param); binWriter.Write((Byte)actionList[listOffset + i].param);
binWriter.Seek(0x142, SeekOrigin.Begin); binWriter.Seek(0x112, SeekOrigin.Begin);
for (int i = 0; i < max; i++) for (int i = 0; i < max; i++)
binWriter.Write((Byte)actionList[listOffset + i].unknown); binWriter.Write((Byte)actionList[listOffset + i].unknown);

View file

@ -34,9 +34,10 @@ function allyGlobal.onDespawn(ally)
end end
--tryAggro serves the same purpose for now, keeping this around just in case
function allyGlobal.HelpPlayers(ally, contentGroupCharas, pickRandomTarget) function allyGlobal.HelpPlayers(ally, contentGroupCharas, pickRandomTarget)
if contentGroupCharas then if contentGroupCharas and not ally.IsEngaged() then
for _, chara in pairs(contentGroupCharas) do for chara in contentGroupCharas do
if chara then if chara then
-- probably a player, or another ally -- probably a player, or another ally
-- todo: queue support actions, heal, try pull hate off player etc -- todo: queue support actions, heal, try pull hate off player etc
@ -44,12 +45,14 @@ function allyGlobal.HelpPlayers(ally, contentGroupCharas, pickRandomTarget)
-- do stuff -- do stuff
if not ally.IsEngaged() then if not ally.IsEngaged() then
if chara.IsEngaged() then if chara.IsEngaged() then
allyGlobal.EngageTarget(ally, chara.target, nil) allyGlobal.EngageTarget(ally, chara.target, nil);
break;
end end
end end
elseif chara.IsMonster() and chara.IsEngaged() then elseif chara.IsMonster() and chara.IsEngaged() then
if not ally.IsEngaged() then if not ally.IsEngaged() then
allyGlobal.EngageTarget(ally, chara.target, nil) allyGlobal.EngageTarget(ally, chara, nil);
break;
end end
end end
end end
@ -57,6 +60,36 @@ function allyGlobal.HelpPlayers(ally, contentGroupCharas, pickRandomTarget)
end end
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) function allyGlobal.HealPlayer(ally, player)
end end
@ -67,14 +100,16 @@ end
function allyGlobal.EngageTarget(ally, target, contentGroupCharas) function allyGlobal.EngageTarget(ally, target, contentGroupCharas)
if contentGroupCharas then if contentGroupCharas then
for _, chara in pairs(contentGroupCharas) do for chara in contentGroupCharas do
if chara.IsMonster() then if chara.IsMonster() then
if chara.allegiance ~= ally.allegiance then if chara.allegiance ~= ally.allegiance then
ally.Engage(chara) ally.Engage(chara)
break;
end end
end end
end end
elseif target then elseif target then
ally.Engage(target) ally.Engage(target)
ally.hateContainer.AddBaseHate(target);
end end
end end

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,6 @@
function onEventStarted(player, caller, commandRequest, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
player:SetCurrentJob(17);
player:EndEvent();
end

View file

@ -0,0 +1,3 @@
function onEventStarted(player, command, triggerName, arg1, arg2, arg3, arg4, targetActor, arg5, arg6, arg7, arg8)
end

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -152,8 +152,11 @@ function onTrigger(player, argc, skillName, level)
--local x, y, z = player.GetPos(); --local x, y, z = player.GetPos();
for i = 1, 1 do 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); --actor.FollowTarget(player, 3.2);
end; end;
return; return;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -10,12 +10,12 @@ function onCreate(starterPlayer, contentArea, director)
--mob3 = contentArea:SpawnActor(2201407, "mob3", 375.125, 4.4, -703.591, -1.54); --mob3 = contentArea:SpawnActor(2201407, "mob3", 375.125, 4.4, -703.591, -1.54);
yda = GetWorldManager().SpawnBattleNpcById(6, contentArea); yda = GetWorldManager().SpawnBattleNpcById(6, contentArea);
papalymo = GetWorldManager().SpawnBattleNpcById(7, contentArea); papalymo = GetWorldManager().SpawnBattleNpcById(7, contentArea);
yda:ChangeState(2); --yda:ChangeState(2);
mob1 = GetWorldManager().SpawnBattleNpcById(3, contentArea); mob1 = GetWorldManager().SpawnBattleNpcById(3, contentArea);
mob2 = GetWorldManager().SpawnBattleNpcById(4, contentArea); mob2 = GetWorldManager().SpawnBattleNpcById(4, contentArea);
mob3 = GetWorldManager().SpawnBattleNpcById(5, contentArea); mob3 = GetWorldManager().SpawnBattleNpcById(5, contentArea);
starterPlayer.currentParty.members:Add(yda.actorId); starterPlayer.currentParty:AddMember(papalymo.actorId);
starterPlayer.currentParty.members:Add(papalymo.actorId); starterPlayer.currentParty:AddMember(yda.actorId);
starterPlayer:SetMod(modifiersGlobal.MinimumHpLock, 1); starterPlayer:SetMod(modifiersGlobal.MinimumHpLock, 1);
@ -35,6 +35,47 @@ end
function onDestroy() 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 end

View file

@ -10,55 +10,71 @@ function init()
return "/Director/Quest/QuestDirectorMan0g001"; return "/Director/Quest/QuestDirectorMan0g001";
end end
--Should we be using this to spawn mobs and drop Simplecontent?
function onCreateContentArea(players, director, contentArea, contentGroup) function onCreateContentArea(players, director, contentArea, contentGroup)
director:StartContentGroup(); director:StartContentGroup();
end end
function onEventStarted(player, actor, triggerName) function onEventStarted(player, actor, triggerName)
man0g0Quest = player:GetQuest("Man0g0"); man0g0Quest = player:GetQuest("Man0g0");
player:SendMessage(0x20, "", "Starting");
startTutorialMode(player); startTutorialMode(player);
callClientFunction(player, "delegateEvent", player, man0g0Quest, "processTtrBtl001", nil, nil, nil); callClientFunction(player, "delegateEvent", player, man0g0Quest, "processTtrBtl001", nil, nil, nil);
player:EndEvent(); player:EndEvent();
player:SendMessage(0x20, "", "Waiting for player active");
waitForSignal("playerActive"); waitForSignal("playerActive");
player:SendMessage(0x20, "", "player active");
wait(1); --If this isn't here, the scripts bugs out. TODO: Find a better alternative. wait(1); --If this isn't here, the scripts bugs out. TODO: Find a better alternative.
kickEventContinue(player, actor, "noticeEvent", "noticeEvent"); kickEventContinue(player, actor, "noticeEvent", "noticeEvent");
callClientFunction(player, "delegateEvent", player, man0g0Quest, "processTtrBtl002", nil, nil, nil); callClientFunction(player, "delegateEvent", player, man0g0Quest, "processTtrBtl002", nil, nil, nil);
player:SendMessage(0x20, "", "processTtrBtl002 called");
player:EndEvent(); player:EndEvent();
--Combat portion of tutorial
if player:IsDiscipleOfWar() then
waitForSignal("playerAttack"); waitForSignal("playerAttack");
closeTutorialWidget(player); closeTutorialWidget(player);
showTutorialSuccessWidget(player, 9055); --Open TutorialSuccessWidget for attacking enemy showTutorialSuccessWidget(player, 9055); --Open TutorialSuccessWidget for attacking enemy
wait(3);
openTutorialWidget(player, CONTROLLER_KEYBOARD, TUTORIAL_TP); openTutorialWidget(player, CONTROLLER_KEYBOARD, TUTORIAL_TP);
waitForSignal("tpOver1000"); waitForSignal("tpOver1000");
closeTutorialWidget(player); closeTutorialWidget(player);
openTutorialWidget(player, CONTROLLER_KEYBOARD, TUTORIAL_WEAPONSKILLS); openTutorialWidget(player, CONTROLLER_KEYBOARD, TUTORIAL_WEAPONSKILLS);
waitForSignal("weaponskillUsed");
if player:IsDiscipleOfWar() then
waitForSignal("weaponskillUsed"); --Should be wait for weaponskillUsed signal
elseif player:IsDiscipleOfMagic() then
waitForSignal("spellUsed")
elseif player:IsDiscipleOfHand() then
waitForSignal("abilityUsed")
elseif player:IsDiscipleOfLand() then
waitForSignal("abilityUsed")
end
closeTutorialWidget(player); closeTutorialWidget(player);
showTutorialSuccessWidget(player, 9065); --Open TutorialSuccessWidget for weapon skill showTutorialSuccessWidget(player, 9065); --Open TutorialSuccessWidget for weapon skill
elseif player:IsDiscipleOfMagic() then
openTutorialWidget(player, CONTROLLER_KEYBOARD, TUTORIAL_CASTING);
waitForSignal("spellUsed");
closeTutorialWidget(player);
elseif player:IsDiscipleOfHand() then
waitForSignal("abilityUsed");
elseif player:IsDiscipleOfLand() then
waitForSignal("abilityUsed");
end
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");
waitForSignal("mobkill"); waitForSignal("mobkill");
worldMaster = GetWorldMaster(); 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); player:SendDataPacket("attention", worldMaster, "", 51073, 2);
wait(7); wait(5);
player:ChangeMusic(7); player:ChangeMusic(7);
player:ChangeState(0); wait(5);
kickEventContinue(player, actor, "noticeEvent", "noticeEvent"); kickEventContinue(player, actor, "noticeEvent", "noticeEvent");
wait(5);
callClientFunction(player, "delegateEvent", player, man0g0Quest, "processEvent020_1", nil, nil, nil); callClientFunction(player, "delegateEvent", player, man0g0Quest, "processEvent020_1", nil, nil, nil);
wait(5);
player:GetZone():ContentFinished(); player:GetZone():ContentFinished();
player:EndEvent(); --player:EndEvent();
GetWorldManager():DoZoneChange(player, 155, "PrivateAreaMasterPast", 1, 15, 175.38, -1.21, -1156.51, -2.1); --GetWorldManager():DoZoneChange(player, 155, "PrivateAreaMasterPast", 1, 15, 175.38, -1.21, -1156.51, -2.1);
--[[ --[[
IF DoW: IF DoW:
OpenWidget (TP) OpenWidget (TP)
@ -72,10 +88,9 @@ function onEventStarted(player, actor, triggerName)
OpenWidget (DEFEAT ENEMY) OpenWidget (DEFEAT ENEMY)
]] ]]
--man0g0Quest:NextPhase(10); player:EndEvent();
--player:EndEvent(); wait(5);
GetWorldManager():DoZoneChange(player, 155, "PrivateAreaMasterPast", 1, 15, 175.38, -1.21, -1156.51, -2.1);
--GetWorldManager():DoZoneChange(player, 155, "PrivateAreaMasterPast", 1, 15, 175.38, -1.21, -1156.51, -2.1);
end end

View file

@ -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
}

View file

@ -20,3 +20,8 @@ function onRoam(ally, contentGroupCharas)
ally.animationId = 0 ally.animationId = 0
allyGlobal.onCombatTick(ally, nil, nil, contentGroupCharas) allyGlobal.onCombatTick(ally, nil, nil, contentGroupCharas)
end end
function tryAggro(ally, contentGroupCharas)
allyGlobal.tryAggro(ally, contentGroupCharas)
end

View file

@ -13,10 +13,14 @@ function onCombatTick(ally, target, tick, contentGroupCharas)
allyGlobal.onCombatTick(ally, target, tick, contentGroupCharas) allyGlobal.onCombatTick(ally, target, tick, contentGroupCharas)
end end
function tryAggro(ally, contentGroupCharas)
allyGlobal.tryAggro(ally, contentGroupCharas)
end
function onRoam(ally, contentGroupCharas) function onRoam(ally, contentGroupCharas)
ally.detectionType = 0xFF ally.detectionType = 0xFF
ally.isMovingToSpawn = false ally.isMovingToSpawn = false
ally.neutral = false ally.neutral = false
ally.animationId = 0 ally.animationId = 0
allyGlobal.onCombatTick(ally, contentGroupCharas) --allyGlobal.onCombatTick(ally, contentGroupCharas)
end end

View file

@ -7,10 +7,9 @@ end
function onEventStarted(player, npc, triggerName) function onEventStarted(player, npc, triggerName)
man0g0Quest = player:GetQuest("Man0g0"); man0g0Quest = player:GetQuest("Man0g0");
print("hi"); print("Got Quest Man0g0");
if (man0g0Quest ~= nil) then if (man0g0Quest ~= nil) then
print("Man0g0Quest is not nil");
print("hi2");
if (triggerName == "pushDefault") then if (triggerName == "pushDefault") then
callClientFunction(player, "delegateEvent", player, man0g0Quest, "processTtrNomal002", nil, nil, nil); callClientFunction(player, "delegateEvent", player, man0g0Quest, "processTtrNomal002", nil, nil, nil);
elseif (triggerName == "talkDefault") then elseif (triggerName == "talkDefault") then
@ -23,8 +22,8 @@ function onEventStarted(player, npc, triggerName)
man0g0Quest:SaveData(); man0g0Quest:SaveData();
player:GetDirector("OpeningDirector"):onTalkEvent(player, npc); player:GetDirector("OpeningDirector"):onTalkEvent(player, npc);
--Was she talked to after papalymo? --Was she talked to after papalymo?
print("hi3");
else else
print("Making content area");
if (man0g0Quest:GetQuestFlag(MAN0G0_FLAG_MINITUT_DONE1) == true) then if (man0g0Quest:GetQuestFlag(MAN0G0_FLAG_MINITUT_DONE1) == true) then
player:EndEvent(); player:EndEvent();
@ -43,8 +42,9 @@ function onEventStarted(player, npc, triggerName)
player:KickEvent(director, "noticeEvent", true); player:KickEvent(director, "noticeEvent", true);
player:SetLoginDirector(director); player:SetLoginDirector(director);
print("hi5"); print("Content area and director made");
GetWorldManager():DoZoneChangeContent(player, contentArea, 362.4087, 4, -703.8168, 1.5419, 16); GetWorldManager():DoZoneChangeContent(player, contentArea, 362.4087, 4, -703.8168, 1.5419, 16);
print("Zone Change");
return; return;
else else
callClientFunction(player, "delegateEvent", player, man0g0Quest, "processEvent000_1", nil, nil, nil); callClientFunction(player, "delegateEvent", player, man0g0Quest, "processEvent000_1", nil, nil, nil);