mirror of
https://bitbucket.org/Ioncannon/project-meteor-server.git
synced 2025-05-20 08:26:59 -04:00
Merge branch 'ai-open' of https://bitbucket.org/takhlaq/ffxiv-classic-server into ai-open
This commit is contained in:
@@ -10,7 +10,16 @@ using System;
|
||||
|
||||
namespace FFXIVClassic_Map_Server.Actors
|
||||
{
|
||||
class Character:Actor
|
||||
/// <summary> Which Character types am I friendly with </summary>
|
||||
enum CharacterTargetingAllegiance
|
||||
{
|
||||
/// <summary> Friendly to Players </summary>
|
||||
Player,
|
||||
/// <summary> Friendly to BattleNpcs </summary>
|
||||
BattleNpcs
|
||||
}
|
||||
|
||||
class Character : Actor
|
||||
{
|
||||
public const int SIZE = 0;
|
||||
public const int COLORINFO = 1;
|
||||
@@ -66,6 +75,8 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
public AIContainer aiContainer;
|
||||
public StatusEffects statusEffects;
|
||||
|
||||
public CharacterTargetingAllegiance allegiance;
|
||||
|
||||
public Character(uint actorID) : base(actorID)
|
||||
{
|
||||
//Init timer array to "notimer"
|
||||
|
@@ -88,6 +88,11 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
public Controller GetController()
|
||||
{
|
||||
return controller;
|
||||
}
|
||||
|
||||
public bool CanChangeState()
|
||||
{
|
||||
return states.Count == 0 || states.Peek().CanInterrupt();
|
||||
|
@@ -5,6 +5,8 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using FFXIVClassic_Map_Server.Actors;
|
||||
using FFXIVClassic.Common;
|
||||
using FFXIVClassic_Map_Server.actors.chara.ai;
|
||||
using FFXIVClassic_Map_Server.actors.chara.ai.controllers;
|
||||
|
||||
// port of dsp's ai code https://github.com/DarkstarProject/darkstar/blob/master/src/map/ai/
|
||||
|
||||
@@ -16,7 +18,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
{
|
||||
None,
|
||||
/// <summary> Able to target <see cref="Player"/>s even if not in target's party </summary>
|
||||
All,
|
||||
HitAll,
|
||||
/// <summary> Able to target all <see cref="Player"/>s in target's party/alliance </summary>
|
||||
Alliance,
|
||||
/// <summary> Able to target any <see cref="Pet"/> in target's party/alliance </summary>
|
||||
@@ -67,6 +69,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
{
|
||||
private Character owner;
|
||||
private Character target;
|
||||
private Character masterTarget; // if target is a pet, this is the owner
|
||||
private TargetFindCharacterType findType;
|
||||
private TargetFindFlags findFlags;
|
||||
private TargetFindAOEType aoeType;
|
||||
@@ -95,6 +98,16 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
this.targets = new List<Character>();
|
||||
}
|
||||
|
||||
public List<T> GetTargets<T>() where T : Character
|
||||
{
|
||||
return new List<T>(targets.OfType<T>());
|
||||
}
|
||||
|
||||
public List<Character> GetTargets()
|
||||
{
|
||||
return targets;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call this before <see cref="FindWithinArea"/> <para/>
|
||||
/// </summary>
|
||||
@@ -128,84 +141,103 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
/// <para> Call SetAOEType before calling this </para>
|
||||
/// Find targets within area set by <see cref="SetAOEType"/>
|
||||
/// </summary>
|
||||
/// <param name="withPet">Include pets?</param>
|
||||
|
||||
public void FindWithinArea(Character target, TargetFindFlags flags, bool withPet)
|
||||
public void FindWithinArea(Character target, TargetFindFlags flags)
|
||||
{
|
||||
findFlags = flags;
|
||||
// todo: maybe we should keep a snapshot which is only updated on each tick for consistency
|
||||
|
||||
// are we creating aoe circles around target or self
|
||||
if ((aoeType & TargetFindAOEType.Circle) != 0 && radiusType != TargetFindAOERadiusType.Self)
|
||||
this.targetPosition = owner.GetPosAsVector3();
|
||||
else
|
||||
this.targetPosition = new Vector3(target.positionX, target.positionY, target.positionZ);
|
||||
this.targetPosition = target.GetPosAsVector3();
|
||||
|
||||
this.findFlags = flags;
|
||||
if (aoeType == TargetFindAOEType.Box)
|
||||
masterTarget = TryGetMasterTarget(target) ?? target;
|
||||
|
||||
// todo: should i set this yet or wait til checked if valid target
|
||||
this.target = target;
|
||||
|
||||
// todo: this is stupid
|
||||
bool withPet = (flags & TargetFindFlags.Pets) != 0 || masterTarget.allegiance != owner.allegiance;
|
||||
|
||||
if (IsPlayer(owner))
|
||||
{
|
||||
FindWithinBox(withPet);
|
||||
if (masterTarget is Player)
|
||||
{
|
||||
findType = TargetFindCharacterType.PlayerToPlayer;
|
||||
|
||||
// todo: handle player parties
|
||||
if (masterTarget.currentParty != null)
|
||||
{
|
||||
if ((findFlags & TargetFindFlags.Alliance) != 0)
|
||||
AddAllInAlliance(masterTarget, withPet);
|
||||
else
|
||||
AddAllInParty(masterTarget, withPet);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddTarget(masterTarget, withPet);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
findType = TargetFindCharacterType.PlayerToBattleNpc;
|
||||
AddAllBattleNpcs(masterTarget, false);
|
||||
}
|
||||
}
|
||||
else if (aoeType == TargetFindAOEType.Circle)
|
||||
else
|
||||
{
|
||||
FindWithinCircle(withPet);
|
||||
// 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 & TargetFindFlags.HitAll) != 0)
|
||||
//{
|
||||
// AddAllInZone(masterTarget, withPet);
|
||||
//}
|
||||
|
||||
AddAllInAlliance(target, withPet);
|
||||
|
||||
if (findType == TargetFindCharacterType.BattleNpcToPlayer)
|
||||
{
|
||||
if (owner.allegiance == CharacterTargetingAllegiance.Player)
|
||||
AddAllInZone(masterTarget, withPet);
|
||||
else
|
||||
AddAllInHateList();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find targets within a box using owner's coordinates and target's coordinates as length
|
||||
/// with corners being `extents` yalms to either side of self and target
|
||||
/// </summary>
|
||||
private void FindWithinBox(bool withPet)
|
||||
private bool IsWithinBox(Character target, bool withPet)
|
||||
{
|
||||
// todo: loop over party members
|
||||
if ((findFlags & TargetFindFlags.All) != 0)
|
||||
{
|
||||
// if we have flag set to hit all characters in zone, do it
|
||||
var myPos = owner.GetPosAsVector3();
|
||||
var angle = Vector3.GetAngle(myPos, targetPosition);
|
||||
|
||||
// todo: make the distance check modifiable
|
||||
var actors = (findFlags & TargetFindFlags.ZoneWide) != 0 ? owner.zone.GetAllActors() : owner.zone.GetActorsAroundActor(owner, 30);
|
||||
var myPos = owner.GetPosAsVector3();
|
||||
var angle = Vector3.GetAngle(myPos, targetPosition);
|
||||
// todo: actually check this works..
|
||||
var myCorner = myPos.NewHorizontalVector(angle, extents);
|
||||
var myCorner2 = myPos.NewHorizontalVector(angle, -extents);
|
||||
|
||||
// todo: actually check this works..
|
||||
var myCorner = myPos.NewHorizontalVector(angle, extents);
|
||||
var myCorner2 = myPos.NewHorizontalVector(angle, -extents);
|
||||
var targetCorner = targetPosition.NewHorizontalVector(angle, extents);
|
||||
var targetCorner2 = targetPosition.NewHorizontalVector(angle, -extents);
|
||||
|
||||
var targetCorner = targetPosition.NewHorizontalVector(angle, extents);
|
||||
var targetCorner2 = targetPosition.NewHorizontalVector(angle, -extents);
|
||||
|
||||
foreach (Character actor in actors)
|
||||
{
|
||||
// dont wanna add static actors
|
||||
if (actor is Player || actor is BattleNpc)
|
||||
{
|
||||
if (actor.GetPosAsVector3().IsWithinBox(myCorner2, targetCorner))
|
||||
{
|
||||
if (CanTarget(actor))
|
||||
AddTarget(actor, withPet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return target.GetPosAsVector3().IsWithinBox(targetCorner2, myCorner);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find targets within circle area. <para/>
|
||||
/// As the name implies, it only checks horizontal coords, not vertical -
|
||||
/// effectively creating cylinder with infinite height
|
||||
/// </summary>
|
||||
private void FindWithinCircle(bool withPet)
|
||||
private bool IsWithinCone(Character target, bool withPet)
|
||||
{
|
||||
var actors = (findFlags & TargetFindFlags.ZoneWide) != 0 ? owner.zone.GetAllActors() : owner.zone.GetActorsAroundActor(owner, 30);
|
||||
|
||||
foreach (Character target in actors)
|
||||
{
|
||||
if (target is Player || target is BattleNpc)
|
||||
{
|
||||
if (target.GetPosAsVector3().IsWithinCircle(targetPosition, extents))
|
||||
AddTarget(target, withPet);
|
||||
}
|
||||
}
|
||||
// todo:
|
||||
return false;
|
||||
}
|
||||
|
||||
private void AddTarget(Character target, bool withPet)
|
||||
@@ -220,14 +252,61 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
private void AddAllInParty(Character target, bool withPet)
|
||||
{
|
||||
// todo:
|
||||
/*
|
||||
* foreach (var actor in target.currentParty.GetMembers())
|
||||
* {
|
||||
* AddTarget(actor, withPet);
|
||||
* }
|
||||
*/
|
||||
AddTarget(target, withPet);
|
||||
}
|
||||
|
||||
private void AddAllInAlliance(Character target, bool withPet)
|
||||
{
|
||||
// todo:
|
||||
/*
|
||||
* foreach (var actor in target.currentParty.GetAllianceMembers())
|
||||
* {
|
||||
* AddTarget(actor, withPet);
|
||||
* }
|
||||
*/
|
||||
AddTarget(target, withPet);
|
||||
}
|
||||
|
||||
public bool CanTarget(Character target)
|
||||
private void AddAllBattleNpcs(Character target, bool withPet)
|
||||
{
|
||||
// 70 is client render distance so we'll go with that
|
||||
var actors = (findFlags & TargetFindFlags.ZoneWide) != 0 ? owner.zone.GetAllActors<BattleNpc>() : owner.zone.GetActorsAroundActor<BattleNpc>(owner, 70);
|
||||
|
||||
// todo: should we look for Characters instead in case player is charmed by BattleNpc
|
||||
foreach (BattleNpc actor in actors)
|
||||
{
|
||||
// todo:
|
||||
AddTarget(actor, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddAllInZone(Character target, bool withPet)
|
||||
{
|
||||
var actors = owner.zone.GetAllActors<Character>();
|
||||
foreach (Character actor in actors)
|
||||
{
|
||||
AddTarget(actor, withPet);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddAllInHateList()
|
||||
{
|
||||
if (!(owner is BattleNpc))
|
||||
Program.Log.Error($"TargetFind.AddAllInHateList() owner [{owner.actorId}] {owner.customDisplayName} {owner.actorName} is not a BattleNpc");
|
||||
|
||||
foreach (var hateEntry in ((BattleNpc)owner).hateContainer.GetHateList())
|
||||
{
|
||||
AddTarget(hateEntry.Value.actor, false);
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanTarget(Character target, bool withPet = false)
|
||||
{
|
||||
// already targeted, dont target again
|
||||
if (targets.Contains(target))
|
||||
@@ -237,12 +316,55 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||
if ((findFlags & TargetFindFlags.Dead) == 0 && target.IsDead())
|
||||
return false;
|
||||
|
||||
// cant target if player is zoning
|
||||
if (target is Player && ((Player)target).playerSession.isUpdatesLocked)
|
||||
bool targetingPlayer = target is Player;
|
||||
|
||||
// cant target if zoning
|
||||
if (target.isZoning || owner.isZoning || target.zone != owner.zone || targetingPlayer && ((Player)target).playerSession.isUpdatesLocked)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
// hit everything within zone or within aoe region
|
||||
if ((findFlags & TargetFindFlags.ZoneWide) != 0 || aoeType == TargetFindAOEType.Circle && target.GetPosAsVector3().IsWithinCircle(targetPosition, extents))
|
||||
return true;
|
||||
|
||||
if (aoeType == TargetFindAOEType.Cone && IsWithinCone(target, withPet))
|
||||
return true;
|
||||
|
||||
if (aoeType == TargetFindAOEType.Box && IsWithinBox(target, withPet))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsPlayer(Character target)
|
||||
{
|
||||
if (target is Player)
|
||||
return true;
|
||||
|
||||
// treat player owned pets as players too
|
||||
return TryGetMasterTarget(target) is Player;
|
||||
}
|
||||
|
||||
private Character TryGetMasterTarget(Character target)
|
||||
{
|
||||
// if character is a player owned pet, treat as a player
|
||||
if (target.aiContainer != null)
|
||||
{
|
||||
var controller = target.aiContainer.GetController();
|
||||
if (controller != null && controller is PetController)
|
||||
return ((PetController)controller).GetPetMaster();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool IsBattleNpcOwner(Character target)
|
||||
{
|
||||
// i know i copied this from dsp but what even
|
||||
if (!(owner is Player) || target is Player)
|
||||
return true;
|
||||
|
||||
// todo: check hate list
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,10 +3,70 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using FFXIVClassic_Map_Server.Actors;
|
||||
|
||||
namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
|
||||
{
|
||||
class PetController
|
||||
class PetController : Controller
|
||||
{
|
||||
private Character petMaster;
|
||||
|
||||
public PetController(Character owner)
|
||||
{
|
||||
this.owner = owner;
|
||||
this.lastUpdate = Program.Tick;
|
||||
}
|
||||
|
||||
public override void Update(DateTime tick)
|
||||
{
|
||||
// todo: handle player stuff on tick
|
||||
}
|
||||
|
||||
public override void ChangeTarget(Character target)
|
||||
{
|
||||
base.ChangeTarget(target);
|
||||
}
|
||||
|
||||
public override bool Engage(Character target)
|
||||
{
|
||||
// todo: check distance, last swing time, status effects
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Disengage()
|
||||
{
|
||||
// todo:
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Cast(Character target, uint spellId)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void Ability(Character target, uint abilityId)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void RangedAttack(Character target)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Character GetPetMaster()
|
||||
{
|
||||
return petMaster;
|
||||
}
|
||||
|
||||
public void SetPetMaster(Character master)
|
||||
{
|
||||
petMaster = master;
|
||||
|
||||
if (master is Player)
|
||||
owner.allegiance = CharacterTargetingAllegiance.Player;
|
||||
else
|
||||
owner.allegiance = CharacterTargetingAllegiance.BattleNpcs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
this.aiContainer = new AIContainer(this, new BattleNpcController(this), new PathFind(this), new TargetFind(this));
|
||||
this.currentSubState = SetActorStatePacket.SUB_STATE_MONSTER;
|
||||
this.hateContainer = new HateContainer(this);
|
||||
this.allegiance = CharacterTargetingAllegiance.BattleNpcs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -388,7 +388,6 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
player.QueuePacket(PlayBGAnimation.BuildPacket(actorId, animationName));
|
||||
}
|
||||
|
||||
|
||||
public void Despawn()
|
||||
{
|
||||
zone.DespawnActor(this);
|
||||
|
@@ -17,7 +17,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
ushort actorState, uint animationId, string customDisplayName)
|
||||
: base(actorNumber, actorClass, uniqueId, spawnedArea, posX, posY, posZ, rot, actorState, animationId, customDisplayName)
|
||||
{
|
||||
this.aiContainer = new AIContainer(this, new BattleNpcController(this), new PathFind(this), new TargetFind(this));
|
||||
this.aiContainer = new AIContainer(this, new PetController(this), new PathFind(this), new TargetFind(this));
|
||||
this.currentSubState = SetActorStatePacket.SUB_STATE_MONSTER;
|
||||
this.hateContainer = new HateContainer(this);
|
||||
}
|
||||
|
@@ -243,6 +243,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
lastPlayTimeUpdate = Utils.UnixTimeStampUTC();
|
||||
|
||||
this.aiContainer = new AIContainer(this, new PlayerController(this), null, new TargetFind(this));
|
||||
allegiance = CharacterTargetingAllegiance.Player;
|
||||
}
|
||||
|
||||
public List<SubPacket> Create0x132Packets()
|
||||
@@ -267,9 +268,9 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
* Unknown - Bool
|
||||
* Unknown - Number
|
||||
* Unknown - Bool
|
||||
* Timer Array - 20 Number
|
||||
*/
|
||||
|
||||
* Timer Array - 20 Number
|
||||
*/
|
||||
|
||||
public override SubPacket CreateScriptBindPacket(Player requestPlayer)
|
||||
{
|
||||
List<LuaParam> lParams;
|
||||
@@ -285,7 +286,8 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
|
||||
ActorInstantiatePacket.BuildPacket(actorId, actorName, className, lParams).DebugPrintSubPacket();
|
||||
|
||||
return ActorInstantiatePacket.BuildPacket(actorId, actorName, className, lParams);
|
||||
|
||||
return ActorInstantiatePacket.BuildPacket(actorId, actorName, className, lParams);
|
||||
}
|
||||
|
||||
public override List<SubPacket> GetSpawnPackets(Player requestPlayer, ushort spawnType)
|
||||
@@ -294,21 +296,21 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
subpackets.Add(CreateAddActorPacket(8));
|
||||
if (IsMyPlayer(requestPlayer.actorId))
|
||||
subpackets.AddRange(Create0x132Packets());
|
||||
subpackets.Add(CreateSpeedPacket());
|
||||
subpackets.Add(CreateSpeedPacket());
|
||||
subpackets.Add(CreateSpawnPositonPacket(this, spawnType));
|
||||
subpackets.Add(CreateAppearancePacket());
|
||||
subpackets.Add(CreateNamePacket());
|
||||
subpackets.Add(_0xFPacket.BuildPacket(actorId));
|
||||
subpackets.Add(CreateStatePacket());
|
||||
subpackets.Add(CreateIdleAnimationPacket());
|
||||
subpackets.Add(CreateInitStatusPacket());
|
||||
subpackets.Add(CreateSetActorIconPacket());
|
||||
subpackets.Add(CreateInitStatusPacket());
|
||||
subpackets.Add(CreateSetActorIconPacket());
|
||||
subpackets.Add(CreateIsZoneingPacket());
|
||||
subpackets.AddRange(CreatePlayerRelatedPackets(requestPlayer.actorId));
|
||||
subpackets.Add(CreateScriptBindPacket(requestPlayer));
|
||||
return subpackets;
|
||||
}
|
||||
|
||||
|
||||
public List<SubPacket> CreatePlayerRelatedPackets(uint requestingPlayerActorId)
|
||||
{
|
||||
List<SubPacket> subpackets = new List<SubPacket>();
|
||||
@@ -319,9 +321,9 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
if (currentTitle != 0)
|
||||
subpackets.Add(SetPlayerTitlePacket.BuildPacket(actorId, currentTitle));
|
||||
|
||||
if (currentJob != 0)
|
||||
subpackets.Add(SetCurrentJobPacket.BuildPacket(actorId, currentJob));
|
||||
|
||||
if (currentJob != 0)
|
||||
subpackets.Add(SetCurrentJobPacket.BuildPacket(actorId, currentJob));
|
||||
|
||||
if (IsMyPlayer(requestingPlayerActorId))
|
||||
{
|
||||
subpackets.Add(SetSpecialEventWorkPacket.BuildPacket(actorId));
|
||||
@@ -347,7 +349,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
|
||||
return subpackets;
|
||||
}
|
||||
|
||||
|
||||
public override List<SubPacket> GetInitPackets()
|
||||
{
|
||||
ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil("/_init", this);
|
||||
@@ -512,8 +514,8 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
QueuePacket(SetWeatherPacket.BuildPacket(actorId, SetWeatherPacket.WEATHER_CLEAR, 1));
|
||||
|
||||
QueuePacket(SetMapPacket.BuildPacket(actorId, zone.regionId, zone.actorId));
|
||||
|
||||
QueuePackets(GetSpawnPackets(this, spawnType));
|
||||
|
||||
QueuePackets(GetSpawnPackets(this, spawnType));
|
||||
//GetSpawnPackets(actorId, spawnType).DebugPrintPacket();
|
||||
|
||||
#region Inventory & Equipment
|
||||
@@ -523,17 +525,17 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
inventories[Inventory.KEYITEMS].SendFullInventory();
|
||||
inventories[Inventory.BAZAAR].SendFullInventory();
|
||||
inventories[Inventory.MELDREQUEST].SendFullInventory();
|
||||
inventories[Inventory.LOOT].SendFullInventory();
|
||||
equipment.SendFullEquipment(false);
|
||||
inventories[Inventory.LOOT].SendFullInventory();
|
||||
equipment.SendFullEquipment(false);
|
||||
playerSession.QueuePacket(InventoryEndChangePacket.BuildPacket(actorId));
|
||||
#endregion
|
||||
|
||||
playerSession.QueuePacket(GetInitPackets());
|
||||
playerSession.QueuePacket(GetInitPackets());
|
||||
|
||||
List<SubPacket> areaMasterSpawn = zone.GetSpawnPackets();
|
||||
List<SubPacket> debugSpawn = world.GetDebugActor().GetSpawnPackets();
|
||||
List<SubPacket> worldMasterSpawn = world.GetActor().GetSpawnPackets();
|
||||
|
||||
|
||||
playerSession.QueuePacket(areaMasterSpawn);
|
||||
playerSession.QueuePacket(debugSpawn);
|
||||
playerSession.QueuePacket(worldMasterSpawn);
|
||||
@@ -551,15 +553,15 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
QueuePacket(SetPlayerItemStoragePacket.BuildPacket(actorId));
|
||||
}
|
||||
|
||||
if (zone.GetWeatherDirector() != null)
|
||||
{
|
||||
if (zone.GetWeatherDirector() != null)
|
||||
{
|
||||
playerSession.QueuePacket(zone.GetWeatherDirector().GetSpawnPackets());
|
||||
}
|
||||
|
||||
|
||||
foreach (Director director in ownedDirectors)
|
||||
{
|
||||
QueuePackets(director.GetSpawnPackets());
|
||||
QueuePackets(director.GetSpawnPackets());
|
||||
QueuePackets(director.GetInitPackets());
|
||||
}
|
||||
|
||||
@@ -592,8 +594,8 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
|
||||
public bool IsMyPlayer(uint otherActorId)
|
||||
{
|
||||
return actorId == otherActorId;
|
||||
}
|
||||
return actorId == otherActorId;
|
||||
}
|
||||
|
||||
public void QueuePacket(SubPacket packet)
|
||||
{
|
||||
@@ -601,7 +603,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
}
|
||||
|
||||
public void QueuePackets(List<SubPacket> packets)
|
||||
{
|
||||
{
|
||||
playerSession.QueuePacket(packets);
|
||||
}
|
||||
|
||||
@@ -1141,16 +1143,16 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
}
|
||||
|
||||
public void MarkGuildleve(uint id, bool abandoned, bool completed)
|
||||
{
|
||||
if (HasGuildleve(id))
|
||||
{
|
||||
if (HasGuildleve(id))
|
||||
{
|
||||
for (int i = 0; i < work.guildleveId.Length; i++)
|
||||
{
|
||||
if (work.guildleveId[i] == id)
|
||||
{
|
||||
work.guildleveChecked[i] = completed;
|
||||
work.guildleveDone[i] = abandoned;
|
||||
Database.MarkGuildleve(this, id, abandoned, completed);
|
||||
work.guildleveDone[i] = abandoned;
|
||||
Database.MarkGuildleve(this, id, abandoned, completed);
|
||||
SendGuildleveMarkClientUpdate(i);
|
||||
}
|
||||
}
|
||||
@@ -1426,9 +1428,8 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
|
||||
private void SendGuildleveMarkClientUpdate(int slot)
|
||||
{
|
||||
ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil("work/guildleve", this);
|
||||
propPacketUtil.AddProperty(String.Format("work.guildleveDone[{0}]", slot));
|
||||
propPacketUtil.AddProperty(String.Format("work.guildleveChecked[{0}]", slot));
|
||||
ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil("work/guildleve", this);
|
||||
propPacketUtil.AddProperty(String.Format("work.guildleveId[{0}]", slot));
|
||||
QueuePackets(propPacketUtil.Done());
|
||||
}
|
||||
|
||||
@@ -1447,16 +1448,16 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||
}
|
||||
}
|
||||
|
||||
public void SendDirectorPackets(Director director)
|
||||
{
|
||||
public void SendDirectorPackets(Director director)
|
||||
{
|
||||
QueuePackets(director.GetSpawnPackets());
|
||||
QueuePackets(director.GetInitPackets());
|
||||
}
|
||||
|
||||
public void RemoveDirector(Director director)
|
||||
{
|
||||
if (ownedDirectors.Contains(director))
|
||||
{
|
||||
{
|
||||
if (ownedDirectors.Contains(director))
|
||||
{
|
||||
QueuePacket(RemoveActorPacket.BuildPacket(director.actorId));
|
||||
ownedDirectors.Remove(director);
|
||||
director.RemoveMember(this);
|
||||
|
Reference in New Issue
Block a user