/*************************************************************************
* StatCollection.cs
* -----------------
* begin : Jan 24, 2007
* copyright : (C) The WCell Team
* email : info@wcell.org
*************************************************************************/
/*************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
*************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WCell.Core;
using Cell.Core;
using WCell.RealmServer.Entities;
namespace WCell.RealmServer.Stats
{
/// <summary>
/// Manages the different categories of stat modification and allows calculators to be
/// implemented as scripts for calculating totals.
/// </summary>
public class StatCollection
{
private Unit m_owner;
private HashSet<StatChange> m_changeTracker;
private Queue<StatChange> m_changes;
private Dictionary<StatCollectionType, Dictionary<StatTypes, double>> m_stats;
private SpinWaitLock m_changeLock;
private SpinWaitLock m_flagLock;
/// <summary>
/// Creates a <see cref="StatCollection" /> object with the given <see cref="Unit" /> as the owner.
/// </summary>
/// <param name="unit">the <see cref="Unit" /> that this collection belongs to</param>
public StatCollection(Unit unit)
{
m_owner = unit;
m_changes = new Queue<StatChange>();
m_changeTracker = new HashSet<StatChange>();
m_stats = new Dictionary<StatCollectionType, Dictionary<StatTypes, double>>();
Dictionary<StatTypes, double> tempDict;
for (int i = (int)StatCollectionType.Base; i < (int)StatCollectionType.End; i++)
{
tempDict = new Dictionary<StatTypes, double>();
for (int j = (int)StatTypes.Base; j < (int)StatTypes.End; j++)
{
if ((StatCollectionType)i == StatCollectionType.Multiplicative)
{
tempDict.Add((StatTypes)j, 1.0);
}
else
{
tempDict.Add((StatTypes)j, 0.0);
}
}
m_stats.Add((StatCollectionType)i, tempDict);
}
m_changeLock = new SpinWaitLock();
m_flagLock = new SpinWaitLock();
}
/// <summary>
/// Adds a given value to the existing value for a specific <see cref="StatTypes" /> in a
/// specific <see cref="StatCollectionType">collection</see>.
/// </summary>
/// <param name="collectionType">the collection type</param>
/// <param name="statType">the stat type</param>
/// <param name="value">the amount to add</param>
public void AddToStat(StatCollectionType collectionType, StatTypes statType, double value)
{
m_changeLock.Enter();
try
{
m_stats[collectionType][statType] = m_stats[collectionType][statType] + value;
FlagChange(statType);
}
finally
{
m_changeLock.Exit();
}
}
/// <summary>
/// Adds a given value to the existing value for a specific <see cref="StatTypes" /> in a
/// specific <see cref="StatCollectionType">collection</see>.
/// </summary>
/// <param name="collectionType">the collection type</param>
/// <param name="statType">the stat type</param>
/// <param name="value">the amount to add</param>
public void AddToStat(StatCollectionType collectionType, StatTypes statType, short value)
{
m_changeLock.Enter();
try
{
m_stats[collectionType][statType] = m_stats[collectionType][statType] + (double)value;
FlagChange(statType);
}
finally
{
m_changeLock.Exit();
}
}
/// <summary>
/// Adds a given value to the existing value for a specific <see cref="StatTypes" /> in a
/// specific <see cref="StatCollectionType">collection</see>.
/// </summary>
/// <param name="collectionType">the collection type</param>
/// <param name="statType">the stat type</param>
/// <param name="value">the amount to add</param>
public void AddToStat(StatCollectionType collectionType, StatTypes statType, int value)
{
m_changeLock.Enter();
try
{
m_stats[collectionType][statType] = m_stats[collectionType][statType] + (double)value;
FlagChange(statType);
}
finally
{
m_changeLock.Exit();
}
}
/// <summary>
/// Removes a given amount from the existing value for a specific <see cref="StatTypes" /> in a
/// specific <see cref="StatCollectionType">collection</see>.
/// </summary>
/// <param name="collectionType">the collection type</param>
/// <param name="statType">the stat type</param>
/// <param name="value">the amount to remove</param>
public void RemoveFromStat(StatCollectionType collectionType, StatTypes statType, double value)
{
m_changeLock.Enter();
try
{
m_stats[collectionType][statType] = m_stats[collectionType][statType] - value;
}
finally
{
m_changeLock.Exit();
}
}
/// <summary>
/// Removes a given amount from the existing value for a specific <see cref="StatTypes" /> in a
/// specific <see cref="StatCollectionType">collection</see>.
/// </summary>
/// <param name="collectionType">the collection type</param>
/// <param name="statType">the stat type</param>
/// <param name="value">the amount to remove</param>
public void RemoveFromStat(StatCollectionType collectionType, StatTypes statType, short value)
{
m_changeLock.Enter();
try
{
m_stats[collectionType][statType] = m_stats[collectionType][statType] - (double)value;
}
finally
{
m_changeLock.Exit();
}
}
/// <summary>
/// Removes a given amount from the existing value for a specific <see cref="StatTypes" /> in a
/// specific <see cref="StatCollectionType">collection</see>.
/// </summary>
/// <param name="collectionType">the collection type</param>
/// <param name="statType">the stat type</param>
/// <param name="value">the amount to remove</param>
public void RemoveFromStat(StatCollectionType collectionType, StatTypes statType, int value)
{
m_changeLock.Enter();
try
{
m_stats[collectionType][statType] = m_stats[collectionType][statType] - (double)value;
}
finally
{
m_changeLock.Exit();
}
}
/// <summary>
/// Sets a specific value for a specific <see cref="StatTypes" /> in a specific
/// <see cref="StatCollectionTypes">collection</see>.
/// </summary>
/// <param name="collectionType">the collection type</param>
/// <param name="statType">the stat type</param>
/// <param name="value">the amount to set</param>
public void SetValue(StatCollectionType collectionType, StatTypes statType, double value)
{
m_changeLock.Enter();
try
{
m_stats[collectionType][statType] = value;
}
finally
{
m_changeLock.Exit();
}
}
/// <summary>
/// Sets a specific value for a specific <see cref="StatTypes" /> in a specific
/// <see cref="StatCollectionTypes">collection</see>.
/// </summary>
/// <param name="collectionType">the collection type</param>
/// <param name="statType">the stat type</param>
/// <param name="value">the amount to set</param>
public void SetValue(StatCollectionType collectionType, StatTypes statType, short value)
{
m_changeLock.Enter();
try
{
m_stats[collectionType][statType] = (double)value;
}
finally
{
m_changeLock.Exit();
}
}
/// <summary>
/// Sets a specific value for a specific <see cref="StatTypes" /> in a specific
/// <see cref="StatCollectionTypes">collection</see>.
/// </summary>
/// <param name="collectionType">the collection type</param>
/// <param name="statType">the stat type</param>
/// <param name="value">the amount to set</param>
public void SetValue(StatCollectionType collectionType, StatTypes statType, int value)
{
m_changeLock.Enter();
try
{
m_stats[collectionType][statType] = (double)value;
}
finally
{
m_changeLock.Exit();
}
}
/// <summary>
/// Gets the value for a specific <see cref="StatTypes" /> and a specific <see cref="StatCollectionType" />.
/// </summary>
/// <param name="collectionType">the collection type</param>
/// <param name="statType">the stat type</param>
/// <returns>the value for the given collection type/stat type</returns>
public double GetStat(StatCollectionType collectionType, StatTypes statType)
{
m_changeLock.Enter();
try
{
return m_stats[collectionType][statType];
}
finally
{
m_changeLock.Exit();
}
}
/// <summary>
/// Gets the modified (total/final) value of a specific <see cref="StatTypes" /> for the
/// <see cref="Unit">owner</see>.
/// </summary>
/// <param name="statType">the stat type</param>
/// <returns>the modified value for the given <see cref="StatTypes" />; or <see cref="Double.MinValue" />
/// if there was anexception when executing the calculator.</returns>
public double GetModified(StatTypes statType)
{
m_changeLock.Enter();
try
{
double staticVal = (m_stats[StatCollectionType.BaseModifier][statType] +
m_stats[StatCollectionType.PositiveModifier][statType] +
m_stats[StatCollectionType.NegativeModifier][statType]);
double posPercentMod = staticVal * m_stats[StatCollectionType.PositiveMultiplier][statType];
double negPercentMod = staticVal * m_stats[StatCollectionType.NegativeMultiplier][statType];
return staticVal + posPercentMod + negPercentMod;
}
finally
{
m_changeLock.Exit();
}
}
/// <summary>
/// Manually flags a stat as changed.
/// </summary>
/// <param name="statType"></param>
public void FlagChange(StatTypes statType)
{
m_flagLock.Enter();
try
{
StatChange change = new StatChange(statType);
if (!m_changeTracker.Contains(change))
{
m_changeTracker.Add(change);
m_changes.Add(change);
}
}
finally
{
m_flagLock.Exit();
}
}
/// <summary>
/// Synchonizes all the pending stat changes with the owner <see cref="Unit" />.
/// </summary>
public void SynchronizeChanges()
{
if (m_changes.Count == 0)
{
return;
}
LinkedList<StatChange> changes;
m_changeLock.Enter();
try
{
changes = new LinkedList<StatChange>(m_changes);
}
finally
{
m_flagLock.Exit();
}
foreach (StatChange change in changes)
{
change.CommitChange(m_owner);
}
}
}
}