using System;
using System.Collections.Generic;
using System.Linq;
using ACE.Entity;
using ACE.Entity.Enum;
using ACE.Entity.Enum.Properties;
using ACE.Entity.Models;
using ACE.Server.Entity;
using ACE.Server.Managers;
using ACE.Server.Network.GameEvent.Events;
using ACE.Server.Network.GameMessages.Messages;
namespace ACE.Server.WorldObjects
{
public class Allegiance: WorldObject
{
///
/// The top of the AllegianceNode tree
///
public AllegianceNode Monarch;
///
/// The total # of players in the Allegiance
///
public int TotalMembers => Members.Count;
///
/// A lookup table of Players => AllegianceNodes
///
public Dictionary Members;
public Dictionary Officers;
///
/// Approved vassals for adding to locked allegiances
///
// TODO now that the new biota model uses a dictionary for this, see if we can remove this duplicate dictionary
public Dictionary ApprovedVassals => Biota.PropertiesAllegiance.GetApprovedVassals(BiotaDatabaseLock);
///
/// Handles booting players from allegiance chat
///
public Dictionary ChatFilters { get; set; }
///
/// A list of players who are banned from joining.
///
//public HashSet BanList { get; set; }
// TODO now that the new biota model uses a dictionary for this, see if we can remove this duplicate dictionary
public Dictionary BanList => Biota.PropertiesAllegiance.GetBanList(BiotaDatabaseLock);
///
/// Returns the list of allegiance members who are currently online
///
public List OnlinePlayers
{
get
{
var onlinePlayers = new List();
foreach (var member in Members)
{
var player = PlayerManager.GetOnlinePlayer(member.Key);
if (player != null)
onlinePlayers.Add(player);
}
return onlinePlayers;
}
}
///
/// A new biota be created taking all of its values from weenie.
///
public Allegiance(Weenie weenie, ObjectGuid guid) : base(weenie, guid)
{
//Console.WriteLine($"Allegiance({weenie.ClassId}, {guid}): weenie constructor");
InitializePropertyDictionaries();
}
///
/// Restore a WorldObject from the database.
///
public Allegiance(Biota biota) : base(biota)
{
//Console.WriteLine($"Allegiance({biota.Id:X8}): biota constructor");
if (MonarchId == null)
{
Console.WriteLine($"Allegiance({biota.Id:X8}): constructor called with no monarch");
return;
}
InitializePropertyDictionaries();
Init(new ObjectGuid(MonarchId.Value));
}
public Allegiance(ObjectGuid monarch)
{
//Console.WriteLine($"Allegiance({monarch}): monarch constructor");
Init(monarch);
}
private void InitializePropertyDictionaries()
{
if (Biota.PropertiesAllegiance == null)
Biota.PropertiesAllegiance = new Dictionary();
}
///
/// Constructs a new Allegiance from a Monarch
///
public void Init(ObjectGuid monarch)
{
Monarch = new AllegianceNode(monarch, this);
// find all players with this monarch
var members = AllegianceManager.FindAllPlayers(monarch);
var patronVassals = BuildPatronVassals(members);
Monarch.BuildChain(this, members, patronVassals);
BuildMembers(Monarch);
//Console.WriteLine("TotalMembers: " + TotalMembers);
BuildOfficers();
ChatFilters = new Dictionary();
}
///
/// Build a mapping of patron guids => vassal guids
///
public Dictionary> BuildPatronVassals(List members)
{
var patronVassals = new Dictionary>();
foreach (var member in members)
{
var patronId = member.PatronId;
if (patronId == null)
continue;
if (!patronVassals.TryGetValue(patronId.Value, out var vassals))
{
vassals = new List();
patronVassals.Add(patronId.Value, vassals);
}
vassals.Add(member);
}
return patronVassals;
}
///
/// Builds the lookup table of Players => AllegianceNodes
///
public void BuildMembers(AllegianceNode node)
{
if (Monarch.PlayerGuid.Equals(node.PlayerGuid))
Members = new Dictionary();
Members.Add(node.PlayerGuid, node);
foreach (var vassal in node.Vassals.Values)
BuildMembers(vassal);
}
public void BuildOfficers()
{
Officers = Members.Where(i => i.Value.Player.AllegianceOfficerRank != null).ToDictionary(i => i.Key, i => i.Value);
}
///
/// An allegiance is defined by its monarch
///
public override bool Equals(object obj)
{
if (obj is Allegiance allegiance)
return Monarch.PlayerGuid.Full == allegiance.Monarch.PlayerGuid.Full;
return false;
}
public override int GetHashCode()
{
return Monarch.PlayerGuid.Full.GetHashCode();
}
public string AllegianceName
{
get => GetProperty(PropertyString.AllegianceName);
set { if (value == null) RemoveProperty(PropertyString.AllegianceName); else SetProperty(PropertyString.AllegianceName, value); }
}
public string AllegianceMotd
{
get => GetProperty(PropertyString.AllegianceMotd);
set { if (value == null) RemoveProperty(PropertyString.AllegianceMotd); else SetProperty(PropertyString.AllegianceMotd, value); }
}
public string AllegianceMotdSetBy
{
get => GetProperty(PropertyString.AllegianceMotdSetBy);
set { if (value == null) RemoveProperty(PropertyString.AllegianceMotdSetBy); else SetProperty(PropertyString.AllegianceMotdSetBy, value); }
}
public string AllegianceSpeakerTitle
{
get => GetProperty(PropertyString.AllegianceSpeakerTitle);
set { if (value == null) RemoveProperty(PropertyString.AllegianceSpeakerTitle); else SetProperty(PropertyString.AllegianceSpeakerTitle, value); }
}
public string AllegianceSeneschalTitle
{
get => GetProperty(PropertyString.AllegianceSeneschalTitle);
set { if (value == null) RemoveProperty(PropertyString.AllegianceSeneschalTitle); else SetProperty(PropertyString.AllegianceSeneschalTitle, value); }
}
public string AllegianceCastellanTitle
{
get => GetProperty(PropertyString.AllegianceCastellanTitle);
set { if (value == null) RemoveProperty(PropertyString.AllegianceCastellanTitle); else SetProperty(PropertyString.AllegianceCastellanTitle, value); }
}
///
/// Returns TRUE if playerGuid is a member
///
public bool IsMember(ObjectGuid playerGuid)
{
return Members.ContainsKey(playerGuid);
}
///
/// Returns TRUE if playerGuid is an officer
///
public bool IsOfficer(ObjectGuid playerGuid)
{
return Officers.ContainsKey(playerGuid);
}
///
/// Returns TRUE if playerGuid is an officer of minimum rank
///
public bool IsOfficerRank(ObjectGuid playerGuid, int officerRank)
{
Officers.TryGetValue(playerGuid, out var node);
return node != null && node.Player.AllegianceOfficerRank >= officerRank;
}
public bool IsSpeaker(ObjectGuid playerGuid)
{
return IsOfficerRank(playerGuid, 1);
}
public bool IsSeneschal(ObjectGuid playerGuid)
{
return IsOfficerRank(playerGuid, 2);
}
public bool IsCastellan(ObjectGuid playerGuid)
{
return IsOfficerRank(playerGuid, 3);
}
public string GetOfficerTitle(AllegianceOfficerLevel officerRank)
{
switch (officerRank)
{
case AllegianceOfficerLevel.Speaker:
return string.IsNullOrEmpty(AllegianceSpeakerTitle) ? "Speaker" : AllegianceSpeakerTitle;
case AllegianceOfficerLevel.Seneschal:
return string.IsNullOrEmpty(AllegianceSeneschalTitle) ? "Seneschal" : AllegianceSeneschalTitle;
case AllegianceOfficerLevel.Castellan:
return string.IsNullOrEmpty(AllegianceCastellanTitle) ? "Castellan" : AllegianceCastellanTitle;
default:
return "";
}
}
public bool HasCustomTitles => AllegianceSpeakerTitle != null || AllegianceSeneschalTitle != null || AllegianceCastellanTitle != null;
public House GetHouse()
{
if (Monarch.Player.HouseInstance == null)
return null;
// is landblock loaded?
var houseGuid = Monarch.Player.HouseInstance.Value;
var landblock = (ushort)((houseGuid >> 12) & 0xFFFF);
var landblockId = new LandblockId((uint)(landblock << 16 | 0xFFFF));
var isLoaded = LandblockManager.IsLoaded(landblockId);
if (isLoaded)
{
var loaded = LandblockManager.GetLandblock(landblockId, false);
return loaded.GetObject(new ObjectGuid(houseGuid)) as House;
}
// load an offline copy
return House.Load(Monarch.Player.HouseInstance.Value);
}
///
/// Returns TRUE if input player guid has an active chat filter
///
public bool IsFiltered(ObjectGuid playerGuid)
{
if (!ChatFilters.TryGetValue(playerGuid, out var filter))
return false;
if (filter > DateTime.UtcNow)
return true;
// filter has expired
ChatFilters.Remove(playerGuid);
return false;
}
///
/// Updates any dynamic properties if they have changed for allegiance members
///
public void UpdateProperties()
{
foreach (var member in Members)
{
var player = PlayerManager.FindByGuid(member.Key);
var onlinePlayer = PlayerManager.GetOnlinePlayer(member.Key);
if (player == null) continue;
var updated = false;
// if changed, update monarch id
if ((player.MonarchId ?? 0) != member.Value.Allegiance.MonarchId)
{
player.UpdateProperty(PropertyInstanceId.Monarch, member.Value.Allegiance.MonarchId, true);
updated = true;
}
// if changed, update rank
if ((player.AllegianceRank ?? 0) != member.Value.Rank)
{
player.AllegianceRank = (int)member.Value.Rank;
if (onlinePlayer != null)
onlinePlayer.Session.Network.EnqueueSend(new GameMessagePrivateUpdatePropertyInt(onlinePlayer, PropertyInt.AllegianceRank, player.AllegianceRank.Value));
updated = true;
}
if (updated)
player.SaveBiotaToDatabase();
if (onlinePlayer != null)
onlinePlayer.Session.Network.EnqueueSend(new GameEventAllegianceUpdate(onlinePlayer.Session, this, member.Value), new GameEventAllegianceAllegianceUpdateDone(onlinePlayer.Session));
}
}
public void ShowMembers()
{
Console.WriteLine($"Total members: {Members.Count}");
foreach (var member in Members)
{
var player = PlayerManager.FindByGuid(member.Key, out bool isOnline);
var prefix = isOnline ? "* " : "";
Console.WriteLine($"{prefix}{player.Name}");
}
}
public void ShowInfo()
{
Monarch.ShowInfo();
}
public void AddBan(uint playerGuid)
{
var entity = Biota.PropertiesAllegiance.GetFirstOrDefaultByCharacterId(playerGuid, BiotaDatabaseLock);
if (entity == null)
Biota.PropertiesAllegiance.AddOrUpdateAllegiance(playerGuid, true, false, BiotaDatabaseLock);
else
Biota.PropertiesAllegiance.AddOrUpdateAllegiance(playerGuid, true, entity.ApprovedVassal, BiotaDatabaseLock);
// ChangesDetected = true doesn't work here,
// since the Allegiance WO is not associated with a landblock
SaveBiotaToDatabase();
}
public bool RemoveBan(uint playerGuid)
{
var entity = Biota.PropertiesAllegiance.GetFirstOrDefaultByCharacterId(playerGuid, BiotaDatabaseLock);
if (entity == null)
return false;
if (entity.ApprovedVassal)
{
Biota.PropertiesAllegiance.AddOrUpdateAllegiance(playerGuid, false, true, BiotaDatabaseLock);
SaveBiotaToDatabase();
return true;
}
var removed = Biota.PropertiesAllegiance.TryRemoveAllegiance(playerGuid, BiotaDatabaseLock);
if (removed)
SaveBiotaToDatabase();
return removed;
}
public bool IsBanned(uint playerGuid)
{
return BanList.ContainsKey(playerGuid);
}
public void AddApprovedVassal(uint playerGuid)
{
var entity = Biota.PropertiesAllegiance.GetFirstOrDefaultByCharacterId(playerGuid, BiotaDatabaseLock);
if (entity == null)
Biota.PropertiesAllegiance.AddOrUpdateAllegiance(playerGuid, false, true, BiotaDatabaseLock);
else
Biota.PropertiesAllegiance.AddOrUpdateAllegiance(playerGuid, entity.Banned, true, BiotaDatabaseLock);
// ChangesDetected = true doesn't work here,
// since the Allegiance WO is not associated with a landblock
SaveBiotaToDatabase();
}
public bool RemoveApprovedVassal(uint playerGuid)
{
var entity = Biota.PropertiesAllegiance.GetFirstOrDefaultByCharacterId(playerGuid, BiotaDatabaseLock);
if (entity == null)
return false;
if (entity.Banned)
{
Biota.PropertiesAllegiance.AddOrUpdateAllegiance(playerGuid, true, false, BiotaDatabaseLock);
SaveBiotaToDatabase();
return true;
}
var removed = Biota.PropertiesAllegiance.TryRemoveAllegiance(playerGuid, BiotaDatabaseLock);
if (removed)
SaveBiotaToDatabase();
return removed;
}
public bool HasApprovedVassal(uint playerGuid)
{
return ApprovedVassals.ContainsKey(playerGuid);
}
}
}