mirror of
https://bitbucket.org/Ioncannon/project-meteor-server.git
synced 2025-07-23 19:10:28 +02:00
stubbed some more states
- stubbed some ability stuff - moved packet things to loop instead of session only - added mob roaming and aggro - todo: fix target find/detection/pathfinding speed/line of sight/line aoe length etc - todo: see "// todo:" in code
This commit is contained in:
parent
c7b87c0d89
commit
68657e1edc
33 changed files with 1459 additions and 444 deletions
|
@ -71,13 +71,17 @@ namespace FFXIVClassic_Map_Server.Actors
|
|||
public Group currentParty = null;
|
||||
public ContentGroup currentContentGroup = null;
|
||||
|
||||
public DateTime lastAiUpdate;
|
||||
//public DateTime lastAiUpdate;
|
||||
|
||||
public AIContainer aiContainer;
|
||||
public StatusEffectContainer statusEffects;
|
||||
public float meleeRange;
|
||||
protected uint attackDelayMs;
|
||||
|
||||
public CharacterTargetingAllegiance allegiance;
|
||||
|
||||
public Pet pet;
|
||||
|
||||
public Character(uint actorID) : base(actorID)
|
||||
{
|
||||
//Init timer array to "notimer"
|
||||
|
@ -85,6 +89,11 @@ namespace FFXIVClassic_Map_Server.Actors
|
|||
charaWork.statusShownTime[i] = 0xFFFFFFFF;
|
||||
|
||||
this.statusEffects = new StatusEffectContainer(this);
|
||||
|
||||
// todo: move this somewhere more appropriate
|
||||
attackDelayMs = 4200;
|
||||
meleeRange = 2.5f;
|
||||
ResetMoveSpeeds();
|
||||
}
|
||||
|
||||
public SubPacket CreateAppearancePacket()
|
||||
|
@ -153,45 +162,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
|||
|
||||
public void PathTo(float x, float y, float z, float stepSize = 0.70f, int maxPath = 40, float polyRadius = 0.0f)
|
||||
{
|
||||
var pos = new Vector3(positionX, positionY, positionZ);
|
||||
var dest = new Vector3(x, y, z);
|
||||
|
||||
var sw = new System.Diagnostics.Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
var path = utils.NavmeshUtils.GetPath(((Zone)GetZone()), pos, dest, stepSize, maxPath, polyRadius);
|
||||
|
||||
if (path != null)
|
||||
{
|
||||
if (oldPositionX == 0.0f && oldPositionY == 0.0f && oldPositionZ == 0.0f)
|
||||
{
|
||||
oldPositionX = positionX;
|
||||
oldPositionY = positionY;
|
||||
oldPositionZ = positionZ;
|
||||
}
|
||||
|
||||
// todo: something went wrong
|
||||
if (path.Count == 0)
|
||||
{
|
||||
positionX = oldPositionX;
|
||||
positionY = oldPositionY;
|
||||
positionZ = oldPositionZ;
|
||||
}
|
||||
|
||||
positionUpdates = path;
|
||||
|
||||
this.hasMoved = true;
|
||||
this.isAtSpawn = false;
|
||||
|
||||
sw.Stop();
|
||||
((Zone)zone).pathCalls++;
|
||||
((Zone)zone).pathCallTime += sw.ElapsedMilliseconds;
|
||||
|
||||
if (path.Count == 1)
|
||||
Program.Log.Info($"mypos: {positionX} {positionY} {positionZ} | targetPos: {x} {y} {z} | step {stepSize} | maxPath {maxPath} | polyRadius {polyRadius}");
|
||||
|
||||
Program.Log.Error("[{0}][{1}] Created {2} points in {3} milliseconds", actorId, actorName, path.Count, sw.ElapsedMilliseconds);
|
||||
}
|
||||
aiContainer?.pathFind?.PreparePath(x, y, z, stepSize, maxPath, polyRadius);
|
||||
}
|
||||
|
||||
public void FollowTarget(Actor target, float stepSize = 1.2f, int maxPath = 25, float radius = 0.0f)
|
||||
|
@ -204,204 +175,118 @@ namespace FFXIVClassic_Map_Server.Actors
|
|||
{
|
||||
this.target = target;
|
||||
}
|
||||
this.moveState = player.moveState;
|
||||
this.moveSpeeds = player.moveSpeeds;
|
||||
// todo: move this to own function thing
|
||||
this.oldMoveState = this.moveState;
|
||||
this.moveState = 2;
|
||||
updateFlags |= ActorUpdateFlags.Position | ActorUpdateFlags.Speed;
|
||||
//this.moveSpeeds = player.moveSpeeds;
|
||||
|
||||
PathTo(player.positionX, player.positionY, player.positionZ, stepSize, maxPath, radius);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPath(Vector3 point)
|
||||
public virtual void OnPath(Vector3 point)
|
||||
{
|
||||
if (positionUpdates != null && positionUpdates.Count > 0)
|
||||
{
|
||||
if (point == positionUpdates[positionUpdates.Count - 1])
|
||||
{
|
||||
var myPos = new Vector3(positionX, positionY, positionZ);
|
||||
//point = NavmeshUtils.GetPath((Zone)zone, myPos, point, 0.35f, 1, 0.000001f, true)?[0];
|
||||
}
|
||||
}
|
||||
lua.LuaEngine.CallLuaBattleAction(this, "onPath", this, point);
|
||||
|
||||
updateFlags |= ActorUpdateFlags.Position;
|
||||
this.isAtSpawn = false;
|
||||
}
|
||||
|
||||
public override void Update(DateTime tick)
|
||||
{
|
||||
// todo: actual ai controllers
|
||||
// todo: mods to control different params instead of hardcode
|
||||
// todo: other ai helpers
|
||||
|
||||
// time elapsed since last ai update
|
||||
|
||||
this.aiContainer?.Update(tick);
|
||||
|
||||
/*
|
||||
var diffTime = (tick - lastAiUpdate);
|
||||
|
||||
if (this is Player)
|
||||
{
|
||||
// todo: handle player stuff here
|
||||
}
|
||||
else
|
||||
{
|
||||
// todo: handle mobs only?
|
||||
//if (this.isStatic)
|
||||
// return;
|
||||
|
||||
// todo: this too
|
||||
if (diffTime.Milliseconds >= 10)
|
||||
{
|
||||
bool foundActor = false;
|
||||
|
||||
// leash back to spawn
|
||||
if (!isMovingToSpawn && this.oldPositionX != 0.0f && this.oldPositionY != 0.0f && this.oldPositionZ != 0.0f)
|
||||
{
|
||||
//var spawnDistanceSq = Utils.DistanceSquared(positionX, positionY, positionZ, oldPositionX, oldPositionY, oldPositionZ);
|
||||
|
||||
// todo: actual spawn leash and modifiers read from table
|
||||
// set a leash to path back to spawn even if have target
|
||||
// (50 yalms)
|
||||
if (Utils.DistanceSquared(positionX, positionY, positionZ, oldPositionX, oldPositionY, oldPositionZ) >= 3025)
|
||||
{
|
||||
this.isMovingToSpawn = true;
|
||||
this.target = null;
|
||||
this.lastMoveUpdate = this.lastMoveUpdate.AddSeconds(-5);
|
||||
this.hasMoved = false;
|
||||
ClearPositionUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
// check if player
|
||||
if (target != null && target is Player)
|
||||
{
|
||||
var player = target as Player;
|
||||
|
||||
// deaggro if zoning/logging
|
||||
// todo: player.isZoning seems to be busted
|
||||
if (player.playerSession.isUpdatesLocked)
|
||||
{
|
||||
target = null;
|
||||
ClearPositionUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
Player closestPlayer = null;
|
||||
float closestPlayerDistanceSq = 1000.0f;
|
||||
|
||||
// dont bother checking for any in-range players if going back to spawn
|
||||
if (!this.isMovingToSpawn)
|
||||
{
|
||||
foreach (var actor in zone.GetActorsAroundActor(this, 65))
|
||||
{
|
||||
if (actor is Player && actor != this)
|
||||
{
|
||||
var player = actor as Player;
|
||||
|
||||
// skip if zoning/logging
|
||||
// todo: player.isZoning seems to be busted
|
||||
if (player != null && player.playerSession.isUpdatesLocked)
|
||||
continue;
|
||||
|
||||
// find distance between self and target
|
||||
var distanceSq = Utils.DistanceSquared(positionX, positionY, positionZ, player.positionX, player.positionY, player.positionZ);
|
||||
|
||||
int maxDistanceSq = player == target ? 900 : 100;
|
||||
|
||||
// check target isnt too far
|
||||
// todo: create cone thing for IsFacing
|
||||
if (distanceSq <= maxDistanceSq && distanceSq <= closestPlayerDistanceSq && (IsFacing(player) || true))
|
||||
{
|
||||
closestPlayerDistanceSq = distanceSq;
|
||||
closestPlayer = player;
|
||||
foundActor = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// found a target
|
||||
if (foundActor)
|
||||
{
|
||||
// make sure we're not already moving so we dont spam packets
|
||||
if (!hasMoved)
|
||||
{
|
||||
// todo: include model size and mob specific distance checks
|
||||
if (closestPlayerDistanceSq >= 9)
|
||||
{
|
||||
FollowTarget(closestPlayer, 2.5f, 4);
|
||||
}
|
||||
// too close, spread out
|
||||
else if (closestPlayerDistanceSq <= 0.85f)
|
||||
{
|
||||
QueuePositionUpdate(target.FindRandomPointAroundActor(0.65f, 0.85f));
|
||||
}
|
||||
|
||||
// we have a target, face them
|
||||
if (target != null)
|
||||
{
|
||||
LookAt(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// time elapsed since last move update
|
||||
var diffMove = (tick - lastMoveUpdate);
|
||||
|
||||
// todo: modifier for DelayBeforeRoamToSpawn
|
||||
// player disappeared
|
||||
if (!foundActor && diffMove.Seconds >= 5)
|
||||
{
|
||||
// dont path if havent moved before
|
||||
if (!hasMoved && oldPositionX != 0.0f && oldPositionY != 0.0f && oldPositionZ != 0.0f)
|
||||
{
|
||||
// check within spawn radius
|
||||
this.isAtSpawn = Utils.DistanceSquared(positionX, positionY, positionZ, oldPositionX, oldPositionY, oldPositionZ) <= 625.0f;
|
||||
|
||||
// make sure we have no target
|
||||
if (this.target == null)
|
||||
{
|
||||
// path back to spawn
|
||||
if (!this.isAtSpawn)
|
||||
{
|
||||
PathTo(oldPositionX, oldPositionY, oldPositionZ, 2.8f);
|
||||
}
|
||||
// within spawn range, find a random point
|
||||
else if (diffMove.Seconds >= 15)
|
||||
{
|
||||
// todo: polyRadius isnt euclidean distance..
|
||||
// pick a random point within 10 yalms of spawn
|
||||
PathTo(oldPositionX, oldPositionY, oldPositionZ, 2.5f, 7, 2.5f);
|
||||
|
||||
// face destination
|
||||
if (positionUpdates.Count > 0)
|
||||
{
|
||||
var destinationPos = positionUpdates[positionUpdates.Count - 1];
|
||||
LookAt(destinationPos.X, destinationPos.Y);
|
||||
}
|
||||
if (this.isMovingToSpawn)
|
||||
{
|
||||
this.isMovingToSpawn = false;
|
||||
this.ResetMoveSpeedsToDefault();
|
||||
this.ChangeState(SetActorStatePacket.MAIN_STATE_DEAD2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// todo: this is retarded. actually no it isnt, i didnt deaggro if out of range..
|
||||
target = null;
|
||||
}
|
||||
// update last ai update time to now
|
||||
lastAiUpdate = DateTime.Now;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
public override void PostUpdate(DateTime tick, List<SubPacket> packets = null)
|
||||
{
|
||||
if (updateFlags != ActorUpdateFlags.None)
|
||||
{
|
||||
packets = packets ?? new List<SubPacket>();
|
||||
|
||||
if ((updateFlags & ActorUpdateFlags.Appearance) != 0)
|
||||
{
|
||||
packets.Add(new SetActorAppearancePacket(modelId, appearanceIds).BuildPacket(actorId));
|
||||
}
|
||||
|
||||
// todo: should probably add another flag for battleTemp since all this uses reflection
|
||||
if ((updateFlags & ActorUpdateFlags.HpTpMp) != 0)
|
||||
{
|
||||
var propPacketUtil = new ActorPropertyPacketUtil("charaWork.parameterSave", this);
|
||||
|
||||
//Parameters
|
||||
|
||||
propPacketUtil.AddProperty("charaWork.parameterSave.hp[0]");
|
||||
propPacketUtil.AddProperty("charaWork.parameterSave.hpMax[0]");
|
||||
propPacketUtil.AddProperty("charaWork.parameterSave.mp");
|
||||
propPacketUtil.AddProperty("charaWork.parameterSave.mpMax");
|
||||
propPacketUtil.AddProperty("charaWork.parameterTemp.tp");
|
||||
propPacketUtil.AddProperty("charaWork.parameterSave.state_mainSkill[0]");
|
||||
propPacketUtil.AddProperty("charaWork.parameterSave.state_mainSkillLevel");
|
||||
|
||||
//General Parameters
|
||||
for (int i = 3; i < charaWork.battleTemp.generalParameter.Length; i++)
|
||||
{
|
||||
if (charaWork.battleTemp.generalParameter[i] != 0)
|
||||
propPacketUtil.AddProperty(String.Format("charaWork.battleTemp.generalParameter[{0}]", i));
|
||||
}
|
||||
|
||||
propPacketUtil.AddProperty("charaWork.battleTemp.castGauge_speed[0]");
|
||||
propPacketUtil.AddProperty("charaWork.battleTemp.castGauge_speed[1]");
|
||||
packets.AddRange(propPacketUtil.Done());
|
||||
}
|
||||
base.PostUpdate(tick, packets);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool CanAttack()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool CanCast()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual uint GetAttackDelayMs()
|
||||
{
|
||||
return attackDelayMs;
|
||||
}
|
||||
|
||||
public bool Engage(uint targid = 0)
|
||||
{
|
||||
// todo: attack the things
|
||||
targid = targid == 0 ? currentTarget: targid;
|
||||
if (targid != 0)
|
||||
{
|
||||
var targ = Server.GetWorldManager().GetActorInWorld(targid);
|
||||
if (targ is Character)
|
||||
aiContainer.Engage((Character)targ);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Disengage()
|
||||
{
|
||||
if (aiContainer != null)
|
||||
{
|
||||
aiContainer.Disengage();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual void Spawn(DateTime tick)
|
||||
{
|
||||
|
||||
// todo: reset hp/mp/tp etc here
|
||||
RecalculateHpMpTp();
|
||||
}
|
||||
|
||||
public virtual void Die(DateTime tick)
|
||||
{
|
||||
|
||||
// todo: actual despawn timer
|
||||
aiContainer.InternalDie(tick, 10);
|
||||
}
|
||||
|
||||
protected virtual void Despawn(DateTime tick)
|
||||
|
@ -418,6 +303,54 @@ namespace FFXIVClassic_Map_Server.Actors
|
|||
{
|
||||
return !IsDead();
|
||||
}
|
||||
|
||||
public virtual short GetHP()
|
||||
{
|
||||
// todo:
|
||||
return charaWork.parameterSave.hp[0];
|
||||
}
|
||||
|
||||
public virtual short GetMaxHP()
|
||||
{
|
||||
return charaWork.parameterSave.hpMax[0];
|
||||
}
|
||||
|
||||
public virtual byte GetHPP()
|
||||
{
|
||||
return (byte)(charaWork.parameterSave.hp[0] / charaWork.parameterSave.hpMax[0]);
|
||||
}
|
||||
|
||||
public virtual void AddHP(short hp)
|
||||
{
|
||||
// todo: +/- hp and die
|
||||
// todo: battlenpcs probably have way more hp?
|
||||
var addHp = charaWork.parameterSave.hp[0] + hp;
|
||||
addHp = addHp.Clamp(short.MinValue, charaWork.parameterSave.hpMax[0]);
|
||||
charaWork.parameterSave.hp[0] = (short)addHp;
|
||||
|
||||
if (charaWork.parameterSave.hp[0] < 1)
|
||||
Die(Program.Tick);
|
||||
|
||||
updateFlags |= ActorUpdateFlags.HpTpMp;
|
||||
}
|
||||
|
||||
public virtual void DelHP(short hp)
|
||||
{
|
||||
AddHP((short)-hp);
|
||||
}
|
||||
|
||||
// todo: should this include stats too?
|
||||
public virtual void RecalculateHpMpTp()
|
||||
{
|
||||
// todo: recalculate stats and crap
|
||||
updateFlags |= ActorUpdateFlags.HpTpMp;
|
||||
}
|
||||
|
||||
public virtual float GetSpeed()
|
||||
{
|
||||
// todo: for battlenpc/player calculate speed
|
||||
return moveSpeeds[2];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue