Core/GameObject: Implement ControlZone gameobject type

Port From (https://github.com/TrinityCore/TrinityCore/commit/f96f041c3edadfb5f1f09705fe699c2d7a9ed423)
This commit is contained in:
hondacrx
2024-01-31 20:53:44 -05:00
parent 7545d9395d
commit e2caec12dc
11 changed files with 998 additions and 1270 deletions
+267 -1
View File
@@ -322,6 +322,10 @@ namespace Game.Entities
m_invisibility.AddValue(InvisibilityType.Trap, 300);
}
break;
case GameObjectTypes.ControlZone:
m_goTypeImpl = new ControlZone(this);
SetActive(true);
break;
case GameObjectTypes.NewFlag:
m_goTypeImpl = new GameObjectType.NewFlag(this);
if (map.Instanceable())
@@ -1594,6 +1598,10 @@ namespace Game.Entities
Log.outError(LogFilter.Spells, $"Spell {spellId} has unhandled action {action} in effect {effectIndex}");
break;
}
// Apply side effects of type
if (m_goTypeImpl != null)
m_goTypeImpl.ActivateObject(action, param, spellCaster, spellId, effectIndex);
}
public void SetGoArtKit(uint kit)
@@ -2670,7 +2678,7 @@ namespace Game.Entities
}
public string[] GetStringIds() { return m_stringIds; }
public override string GetName(Locale locale = Locale.enUS)
{
if (locale != Locale.enUS)
@@ -3622,6 +3630,15 @@ namespace Game.Entities
return newFlag.GetTakenFromBaseTime();
}
public List<ObjectGuid> GetInsidePlayers()
{
ControlZone controlZone = m_goTypeImpl as ControlZone;
if (controlZone != null)
return controlZone.GetInsidePlayers();
return null;
}
public bool MeetsInteractCondition(Player user)
{
if (m_goInfo.GetConditionID1() == 0)
@@ -3943,6 +3960,7 @@ namespace Game.Entities
public virtual void OnStateChanged(GameObjectState oldState, GameObjectState newState) { }
public virtual void OnRelocated() { }
public virtual bool IsNeverVisibleFor(WorldObject seer, bool allowServersideObjects) { return false; }
public virtual void ActivateObject(GameObjectActions action, int param, WorldObject spellCaster = null, uint spellId = 0, int effectIndex = -1) { }
public class CustomCommand
{
@@ -4478,4 +4496,252 @@ namespace Game.Entities
public GameObjectState? State;
public bool Despawned;
}
class ControlZone : GameObjectTypeBase
{
TimeSpan _heartbeatRate;
TimeTracker _heartbeatTracker;
List<ObjectGuid> _insidePlayers = new();
int _previousTeamId;
float _value;
bool _contestedTriggered;
public ControlZone(GameObject owner) : base(owner)
{
_value = owner.GetGoInfo().ControlZone.startingValue;
if (owner.GetMap().Instanceable())
_heartbeatRate = TimeSpan.FromSeconds(1);
else if (owner.GetGoInfo().ControlZone.FrequentHeartbeat != 0)
_heartbeatRate = TimeSpan.FromSeconds(2.5);
else
_heartbeatRate = TimeSpan.FromSeconds(5);
_heartbeatTracker = new(_heartbeatRate);
_previousTeamId = GetControllingTeam();
_contestedTriggered = false;
}
public override void Update(uint diff)
{
if (_owner.HasFlag(GameObjectFlags.NotSelectable))
return;
_heartbeatTracker.Update(diff);
if (_heartbeatTracker.Passed())
{
_heartbeatTracker.Reset(_heartbeatRate);
HandleHeartbeat();
}
}
int GetControllingTeam()
{
if (_value < GetMaxHordeValue())
return TeamId.Horde;
if (_value > GetMinAllianceValue())
return TeamId.Alliance;
return TeamId.Neutral;
}
public List<ObjectGuid> GetInsidePlayers() { return _insidePlayers; }
public override void ActivateObject(GameObjectActions action, int param, WorldObject spellCaster, uint spellId, int effectIndex)
{
switch (action)
{
case GameObjectActions.MakeInert:
foreach (ObjectGuid guid in _insidePlayers)
{
Player player = Global.ObjAccessor.GetPlayer(_owner, guid);
if (player != null)
player.SendUpdateWorldState(_owner.GetGoInfo().ControlZone.worldState1, 0);
}
_insidePlayers.Clear();
break;
default:
break;
}
}
public void SetValue(float value)
{
_value = MathFunctions.RoundToInterval(ref value, 0.0f, 100.0f);
}
void HandleHeartbeat()
{
// update player list inside control zone
List<Unit> targetList = new();
SearchTargets(targetList);
int oldControllingTeam = GetControllingTeam();
float pointsGained = CalculatePointsPerSecond(targetList) * (float)_heartbeatRate.TotalMilliseconds / 1000.0f;
if (pointsGained == 0)
return;
int oldRoundedValue = (int)_value;
SetValue(_value + pointsGained);
int roundedValue = (int)_value;
if (oldRoundedValue == roundedValue)
return;
int newControllingTeam = GetControllingTeam();
int assaultingTeam = pointsGained > 0 ? TeamId.Alliance : TeamId.Horde;
if (oldControllingTeam != newControllingTeam)
_contestedTriggered = false;
if (oldControllingTeam != TeamId.Alliance && newControllingTeam == TeamId.Alliance)
TriggerEvent(_owner.GetGoInfo().ControlZone.ProgressEventAlliance);
else if (oldControllingTeam != TeamId.Horde && newControllingTeam == TeamId.Horde)
TriggerEvent(_owner.GetGoInfo().ControlZone.ProgressEventHorde);
else if (oldControllingTeam == TeamId.Horde && newControllingTeam == TeamId.Neutral)
TriggerEvent(_owner.GetGoInfo().ControlZone.NeutralEventHorde);
else if (oldControllingTeam == TeamId.Alliance && newControllingTeam == TeamId.Neutral)
TriggerEvent(_owner.GetGoInfo().ControlZone.NeutralEventAlliance);
if (roundedValue == 100 && newControllingTeam == TeamId.Alliance && assaultingTeam == TeamId.Alliance)
TriggerEvent(_owner.GetGoInfo().ControlZone.CaptureEventAlliance);
else if (roundedValue == 0 && newControllingTeam == TeamId.Horde && assaultingTeam == TeamId.Horde)
TriggerEvent(_owner.GetGoInfo().ControlZone.CaptureEventHorde);
if (oldRoundedValue == 100 && assaultingTeam == TeamId.Horde && !_contestedTriggered)
{
TriggerEvent(_owner.GetGoInfo().ControlZone.ContestedEventHorde);
_contestedTriggered = true;
}
else if (oldRoundedValue == 0 && assaultingTeam == TeamId.Alliance && !_contestedTriggered)
{
TriggerEvent(_owner.GetGoInfo().ControlZone.ContestedEventAlliance);
_contestedTriggered = true;
}
foreach (Player player in targetList)
player.SendUpdateWorldState(_owner.GetGoInfo().ControlZone.worldstate2, (uint)roundedValue);
}
void SearchTargets(List<Unit> targetList)
{
AnyUnitInObjectRangeCheck check = new(_owner, _owner.GetGoInfo().ControlZone.radius, true);
PlayerListSearcher searcher = new(_owner, targetList, check);
Cell.VisitWorldObjects(_owner, searcher, _owner.GetGoInfo().ControlZone.radius);
HandleUnitEnterExit(targetList);
}
float CalculatePointsPerSecond(List<Unit> targetList)
{
int delta = 0;
foreach (Player player in targetList)
{
if (!player.IsOutdoorPvPActive())
continue;
if (player.GetTeam() == Team.Horde)
delta--;
else
delta++;
}
uint minTime = _owner.GetGoInfo().ControlZone.minTime;
uint maxTime = _owner.GetGoInfo().ControlZone.maxTime;
uint minSuperiority = _owner.GetGoInfo().ControlZone.minSuperiority;
uint maxSuperiority = _owner.GetGoInfo().ControlZone.maxSuperiority;
if (Math.Abs(delta) < minSuperiority)
return 0;
float slope = ((float)minTime - maxTime) / (maxSuperiority - minSuperiority);
float intercept = maxTime - slope * minSuperiority;
float timeNeeded = slope * Math.Abs(delta) + intercept;
float percentageIncrease = 100.0f / timeNeeded;
if (delta < 0)
percentageIncrease *= -1;
return percentageIncrease;
}
void HandleUnitEnterExit(List<Unit> newTargetList)
{
List<ObjectGuid> exitPlayers = new(_insidePlayers);
List<Player> enteringPlayers = new();
foreach (Player unit in newTargetList)
{
if (!exitPlayers.Remove(unit.GetGUID()))
enteringPlayers.Add(unit);
_insidePlayers.Add(unit.GetGUID());
}
foreach (Player player in enteringPlayers)
{
player.SendUpdateWorldState(_owner.GetGoInfo().ControlZone.worldState1, 1);
player.SendUpdateWorldState(_owner.GetGoInfo().ControlZone.worldstate2, (uint)_value);
player.SendUpdateWorldState(_owner.GetGoInfo().ControlZone.worldstate3, _owner.GetGoInfo().ControlZone.neutralPercent);
}
foreach (ObjectGuid exitPlayerGuid in exitPlayers)
{
Player player = Global.ObjAccessor.GetPlayer(_owner, exitPlayerGuid);
if (player != null)
player.SendUpdateWorldState(_owner.GetGoInfo().ControlZone.worldState1, 0);
}
}
float GetMaxHordeValue()
{
// ex: if neutralPercent is 40; then 0 - 30 is Horde Controlled
return 50.0f - _owner.GetGoInfo().ControlZone.neutralPercent / 2.0f;
}
float GetMinAllianceValue()
{
// ex: if neutralPercent is 40; then 70 - 100 is Alliance Controlled
return 50.0f + _owner.GetGoInfo().ControlZone.neutralPercent / 2.0f;
}
void TriggerEvent(uint eventId)
{
if (eventId <= 0)
return;
GameEvents.Trigger(eventId, _owner, null);
}
public uint GetStartingValue()
{
return _owner.GetGoInfo().ControlZone.startingValue;
}
}
class SetControlZoneValue : GameObjectTypeBase.CustomCommand
{
uint? _value;
public SetControlZoneValue(uint? value = null)
{
_value = value;
}
public override void Execute(GameObjectTypeBase type)
{
ControlZone controlZone = type as ControlZone;
if (controlZone != null)
{
uint value = controlZone.GetStartingValue();
if (_value.HasValue)
value = _value.Value;
controlZone.SetValue(value);
}
}
}
}