Ported .Net Core commits:

hondacrx:
- Initial commit: Switch to .Net Core 2.0
- Fix build and removed not needed files
Fabi:
- Updated solution platforms.
- Changed folder structure.
- Change library target framework to netstandard2.0.
- Updated solution platforms again...
- Removed windows specific kernel32 function usage (Ctrl-C handler).
This commit is contained in:
Fabian
2017-10-26 17:23:44 +02:00
parent 227702e19c
commit a3dc7b3f48
844 changed files with 26064 additions and 1824 deletions
@@ -0,0 +1,192 @@
/*
* Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Framework.Constants;
using Game.DataStorage;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Game.Entities
{
public class CinematicManager : IDisposable
{
public CinematicManager(Player playerref)
{
player = playerref;
m_cinematicDiff = 0;
m_lastCinematicCheck = 0;
m_activeCinematicCameraId = 0;
m_cinematicLength = 0;
m_cinematicCamera = null;
m_remoteSightPosition = new Position(0.0f, 0.0f, 0.0f);
m_CinematicObject = null;
}
public virtual void Dispose()
{
if (m_cinematicCamera != null && m_activeCinematicCameraId != 0)
EndCinematic();
}
public void BeginCinematic()
{
// Sanity check for active camera set
if (m_activeCinematicCameraId == 0)
return;
var list = M2Storage.GetFlyByCameras(m_activeCinematicCameraId);
if (!list.Empty())
{
// Initialize diff, and set camera
m_cinematicDiff = 0;
m_cinematicCamera = list;
if (!m_cinematicCamera.Empty())
{
FlyByCamera firstCamera = m_cinematicCamera.FirstOrDefault();
Position pos = new Position(firstCamera.locations.X, firstCamera.locations.Y, firstCamera.locations.Z, firstCamera.locations.W);
if (!pos.IsPositionValid())
return;
player.GetMap().LoadGrid(firstCamera.locations.X, firstCamera.locations.Y);
m_CinematicObject = player.SummonCreature(1, pos.posX, pos.posY, pos.posZ, 0.0f, TempSummonType.TimedDespawn, 5 * Time.Minute * Time.InMilliseconds);
if (m_CinematicObject)
{
m_CinematicObject.setActive(true);
player.SetViewpoint(m_CinematicObject, true);
}
// Get cinematic length
m_cinematicLength = m_cinematicCamera.LastOrDefault().timeStamp;
}
}
}
public void EndCinematic()
{
if (m_activeCinematicCameraId == 0)
return;
m_cinematicDiff = 0;
m_cinematicCamera = null;
m_activeCinematicCameraId = 0;
if (m_CinematicObject)
{
WorldObject vpObject = player.GetViewpoint();
if (vpObject)
if (vpObject == m_CinematicObject)
player.SetViewpoint(m_CinematicObject, false);
m_CinematicObject.AddObjectToRemoveList();
}
}
public void UpdateCinematicLocation(uint diff)
{
if (m_activeCinematicCameraId == 0 || m_cinematicCamera == null || m_cinematicCamera.Count == 0)
return;
Position lastPosition = new Position();
uint lastTimestamp = 0;
Position nextPosition = new Position();
uint nextTimestamp = 0;
// Obtain direction of travel
foreach (FlyByCamera cam in m_cinematicCamera)
{
if (cam.timeStamp > m_cinematicDiff)
{
nextPosition = new Position(cam.locations.X, cam.locations.Y, cam.locations.Z, cam.locations.W);
nextTimestamp = cam.timeStamp;
break;
}
lastPosition = new Position(cam.locations.X, cam.locations.Y, cam.locations.Z, cam.locations.W);
lastTimestamp = cam.timeStamp;
}
float angle = lastPosition.GetAngle(nextPosition);
angle -= lastPosition.GetOrientation();
if (angle < 0)
angle += 2 * MathFunctions.PI;
// Look for position around 2 second ahead of us.
int workDiff = (int)m_cinematicDiff;
// Modify result based on camera direction (Humans for example, have the camera point behind)
workDiff += (int)((2 * Time.InMilliseconds) * Math.Cos(angle));
// Get an iterator to the last entry in the cameras, to make sure we don't go beyond the end
var endItr = m_cinematicCamera.LastOrDefault();
if (endItr != null && workDiff > endItr.timeStamp)
workDiff = (int)endItr.timeStamp;
// Never try to go back in time before the start of cinematic!
if (workDiff < 0)
workDiff = (int)m_cinematicDiff;
// Obtain the previous and next waypoint based on timestamp
foreach (FlyByCamera cam in m_cinematicCamera)
{
if (cam.timeStamp >= workDiff)
{
nextPosition = new Position(cam.locations.X, cam.locations.Y, cam.locations.Z, cam.locations.W);
nextTimestamp = cam.timeStamp;
break;
}
lastPosition = new Position(cam.locations.X, cam.locations.Y, cam.locations.Z, cam.locations.W);
lastTimestamp = cam.timeStamp;
}
// Never try to go beyond the end of the cinematic
if (workDiff > nextTimestamp)
workDiff = (int)nextTimestamp;
// Interpolate the position for this moment in time (or the adjusted moment in time)
uint timeDiff = nextTimestamp - lastTimestamp;
uint interDiff = (uint)(workDiff - lastTimestamp);
float xDiff = nextPosition.posX - lastPosition.posX;
float yDiff = nextPosition.posY - lastPosition.posY;
float zDiff = nextPosition.posZ - lastPosition.posZ;
Position interPosition = new Position(lastPosition.posX + (xDiff * (interDiff / timeDiff)), lastPosition.posY +
(yDiff * (interDiff / timeDiff)), lastPosition.posZ + (zDiff * (interDiff / timeDiff)));
// Advance (at speed) to this position. The remote sight object is used
// to send update information to player in cinematic
if (m_CinematicObject && interPosition.IsPositionValid())
m_CinematicObject.MonsterMoveWithSpeed(interPosition.posX, interPosition.posY, interPosition.posZ, 500.0f, false, true);
// If we never received an end packet 10 seconds after the final timestamp then force an end
if (m_cinematicDiff > m_cinematicLength + 10 * Time.InMilliseconds)
EndCinematic();
}
uint GetActiveCinematicCamera() { return m_activeCinematicCameraId; }
public void SetActiveCinematicCamera(uint cinematicCameraId = 0) { m_activeCinematicCameraId = cinematicCameraId; }
public bool IsOnCinematic() { return (m_cinematicCamera != null); }
// Remote location information
Player player;
public uint m_cinematicDiff;
public uint m_lastCinematicCheck;
public uint m_activeCinematicCameraId;
public uint m_cinematicLength;
List<FlyByCamera> m_cinematicCamera;
Position m_remoteSightPosition;
TempSummon m_CinematicObject;
}
}
@@ -0,0 +1,862 @@
/*
* Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Framework.Constants;
using Framework.Database;
using Game.DataStorage;
using Game.Network.Packets;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Game.Entities
{
public class CollectionMgr
{
public static void LoadMountDefinitions()
{
uint oldMSTime = Time.GetMSTime();
SQLResult result = DB.World.Query("SELECT spellId, otherFactionSpellId FROM mount_definitions");
if (result.IsEmpty())
{
Log.outInfo(LogFilter.ServerLoading, "Loaded 0 mount definitions. DB table `mount_definitions` is empty.");
return;
}
do
{
uint spellId = result.Read<uint>(0);
uint otherFactionSpellId = result.Read<uint>(1);
if (Global.DB2Mgr.GetMount(spellId) == null)
{
Log.outError(LogFilter.Sql, "Mount spell {0} defined in `mount_definitions` does not exist in Mount.db2, skipped", spellId);
continue;
}
if (otherFactionSpellId != 0 && Global.DB2Mgr.GetMount(otherFactionSpellId) == null)
{
Log.outError(LogFilter.Sql, "otherFactionSpellId {0} defined in `mount_definitions` for spell {1} does not exist in Mount.db2, skipped", otherFactionSpellId, spellId);
continue;
}
FactionSpecificMounts[spellId] = otherFactionSpellId;
} while (result.NextRow());
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} mount definitions in {1} ms", FactionSpecificMounts.Count, Time.GetMSTimeDiffToNow(oldMSTime));
}
public CollectionMgr(WorldSession owner)
{
_owner = owner;
_appearances = new System.Collections.BitSet(0);
}
public void LoadToys()
{
foreach (var value in _toys.Keys)
_owner.GetPlayer().AddDynamicValue(PlayerDynamicFields.Toys, value);
}
public bool AddToy(uint itemId, bool isFavourite = false)
{
if (UpdateAccountToys(itemId, isFavourite))
{
_owner.GetPlayer().AddDynamicValue(PlayerDynamicFields.Toys, itemId);
return true;
}
return false;
}
public void LoadAccountToys(SQLResult result)
{
if (result.IsEmpty())
return;
do
{
uint itemId = result.Read<uint>(0);
bool isFavourite = result.Read<bool>(1);
_toys[itemId] = isFavourite;
} while (result.NextRow());
}
public void SaveAccountToys(SQLTransaction trans)
{
PreparedStatement stmt = null;
foreach (var pair in _toys)
{
stmt = DB.Login.GetPreparedStatement(LoginStatements.REP_ACCOUNT_TOYS);
stmt.AddValue(0, _owner.GetBattlenetAccountId());
stmt.AddValue(1, pair.Key);
stmt.AddValue(2, pair.Value);
trans.Append(stmt);
}
}
bool UpdateAccountToys(uint itemId, bool isFavourite = false)
{
if (_toys.ContainsKey(itemId))
return false;
_toys.Add(itemId, isFavourite);
return true;
}
public void ToySetFavorite(uint itemId, bool favorite)
{
if (!_toys.ContainsKey(itemId))
return;
_toys[itemId] = favorite;
}
public void OnItemAdded(Item item)
{
if (Global.DB2Mgr.GetHeirloomByItemId(item.GetEntry()) != null)
AddHeirloom(item.GetEntry(), 0);
AddItemAppearance(item);
}
public void LoadAccountHeirlooms(SQLResult result)
{
if (result.IsEmpty())
return;
do
{
uint itemId = result.Read<uint>(0);
HeirloomPlayerFlags flags = (HeirloomPlayerFlags)result.Read<uint>(1);
HeirloomRecord heirloom = Global.DB2Mgr.GetHeirloomByItemId(itemId);
if (heirloom == null)
continue;
uint bonusId = 0;
if (flags.HasAnyFlag(HeirloomPlayerFlags.BonusLevel110))
bonusId = heirloom.ItemBonusListID[2];
else if (flags.HasAnyFlag(HeirloomPlayerFlags.BonusLevel100))
bonusId = heirloom.ItemBonusListID[1];
else if (flags.HasAnyFlag(HeirloomPlayerFlags.BonusLevel90))
bonusId = heirloom.ItemBonusListID[0];
_heirlooms[itemId] = new HeirloomData(flags, bonusId);
} while (result.NextRow());
}
public void SaveAccountHeirlooms(SQLTransaction trans)
{
PreparedStatement stmt;
foreach (var heirloom in _heirlooms)
{
stmt = DB.Login.GetPreparedStatement(LoginStatements.REP_ACCOUNT_HEIRLOOMS);
stmt.AddValue(0, _owner.GetBattlenetAccountId());
stmt.AddValue(1, heirloom.Key);
stmt.AddValue(2, heirloom.Value.flags);
trans.Append(stmt);
}
}
bool UpdateAccountHeirlooms(uint itemId, HeirloomPlayerFlags flags)
{
if (_heirlooms.ContainsKey(itemId))
return false;
_heirlooms.Add(itemId, new HeirloomData(flags, 0));
return true;
}
public uint GetHeirloomBonus(uint itemId)
{
var data = _heirlooms.LookupByKey(itemId);
if (data != null)
return data.bonusId;
return 0;
}
public void LoadHeirlooms()
{
foreach (var item in _heirlooms)
{
_owner.GetPlayer().AddDynamicValue(PlayerDynamicFields.Heirlooms, item.Key);
_owner.GetPlayer().AddDynamicValue(PlayerDynamicFields.HeirloomsFlags, (uint)item.Value.flags);
}
}
public void AddHeirloom(uint itemId, HeirloomPlayerFlags flags)
{
if (UpdateAccountHeirlooms(itemId, flags))
{
_owner.GetPlayer().AddDynamicValue(PlayerDynamicFields.Heirlooms, itemId);
_owner.GetPlayer().AddDynamicValue(PlayerDynamicFields.HeirloomsFlags, (uint)flags);
}
}
public void UpgradeHeirloom(uint itemId, uint castItem)
{
Player player = _owner.GetPlayer();
if (!player)
return;
HeirloomRecord heirloom = Global.DB2Mgr.GetHeirloomByItemId(itemId);
if (heirloom == null)
return;
var data = _heirlooms.LookupByKey(itemId);
if (data == null)
return;
HeirloomPlayerFlags flags = data.flags;
uint bonusId = 0;
if (heirloom.UpgradeItemID[0] == castItem)
{
flags |= HeirloomPlayerFlags.BonusLevel90;
bonusId = heirloom.ItemBonusListID[0];
}
if (heirloom.UpgradeItemID[1] == castItem)
{
flags |= HeirloomPlayerFlags.BonusLevel100;
bonusId = heirloom.ItemBonusListID[1];
}
if (heirloom.UpgradeItemID[2] == castItem)
{
flags |= HeirloomPlayerFlags.BonusLevel110;
bonusId = heirloom.ItemBonusListID[2];
}
foreach (Item item in player.GetItemListByEntry(itemId, true))
item.AddBonuses(bonusId);
// Get heirloom offset to update only one part of dynamic field
var fields = player.GetDynamicValues(PlayerDynamicFields.Heirlooms);
ushort offset = (ushort)Array.IndexOf(fields, itemId);
player.SetDynamicValue(PlayerDynamicFields.HeirloomsFlags, offset, (uint)flags);
data.flags = flags;
data.bonusId = bonusId;
}
public void CheckHeirloomUpgrades(Item item)
{
Player player = _owner.GetPlayer();
if (!player)
return;
// Check already owned heirloom for upgrade kits
HeirloomRecord heirloom = Global.DB2Mgr.GetHeirloomByItemId(item.GetEntry());
if (heirloom != null)
{
var data = _heirlooms.LookupByKey(item.GetEntry());
if (data == null)
return;
// Check for heirloom pairs (normal - heroic, heroic - mythic)
uint heirloomItemId = heirloom.NextDifficultyItemID;
uint newItemId = 0;
HeirloomRecord heirloomDiff;
while ((heirloomDiff = Global.DB2Mgr.GetHeirloomByItemId(heirloomItemId)) != null)
{
if (player.GetItemByEntry(heirloomDiff.ItemID))
newItemId = heirloomDiff.ItemID;
HeirloomRecord heirloomSub = Global.DB2Mgr.GetHeirloomByItemId(heirloomDiff.NextDifficultyItemID);
if (heirloomSub != null)
{
heirloomItemId = heirloomSub.ItemID;
continue;
}
break;
}
if (newItemId != 0)
{
var heirloomFields = player.GetDynamicValues(PlayerDynamicFields.Heirlooms);
ushort offset = (ushort)Array.IndexOf(heirloomFields, item.GetEntry());
player.SetDynamicValue(PlayerDynamicFields.Heirlooms, offset, newItemId);
player.SetDynamicValue(PlayerDynamicFields.HeirloomsFlags, offset, 0);
_heirlooms.Remove(item.GetEntry());
_heirlooms[newItemId] = null;
return;
}
var fields = item.GetDynamicValues(ItemDynamicFields.BonusListIds);
foreach (uint bonusId in fields)
if (bonusId != data.bonusId)
item.ClearDynamicValue(ItemDynamicFields.BonusListIds);
if (!fields.Contains(data.bonusId))
item.AddBonuses(data.bonusId);
}
}
public bool CanApplyHeirloomXpBonus(uint itemId, uint level)
{
if (Global.DB2Mgr.GetHeirloomByItemId(itemId) == null)
return false;
var data = _heirlooms.LookupByKey(itemId);
if (data == null)
return false;
if (data.flags.HasAnyFlag(HeirloomPlayerFlags.BonusLevel110))
return level <= 110;
if (data.flags.HasAnyFlag(HeirloomPlayerFlags.BonusLevel100))
return level <= 100;
if (data.flags.HasAnyFlag(HeirloomPlayerFlags.BonusLevel90))
return level <= 90;
return level <= 60;
}
public void LoadMounts()
{
foreach (var m in _mounts.ToList())
AddMount(m.Key, m.Value, false, false);
}
public void LoadAccountMounts(SQLResult result)
{
if (result.IsEmpty())
return;
do
{
uint mountSpellId = result.Read<uint>(0);
MountStatusFlags flags = (MountStatusFlags)result.Read<byte>(1);
if (Global.DB2Mgr.GetMount(mountSpellId) == null)
continue;
_mounts[mountSpellId] = flags;
} while (result.NextRow());
}
public void SaveAccountMounts(SQLTransaction trans)
{
foreach (var mount in _mounts)
{
PreparedStatement stmt = DB.Login.GetPreparedStatement(LoginStatements.REP_ACCOUNT_MOUNTS);
stmt.AddValue(0, _owner.GetBattlenetAccountId());
stmt.AddValue(1, mount.Key);
stmt.AddValue(2, mount.Value);
trans.Append(stmt);
}
}
public bool AddMount(uint spellId, MountStatusFlags flags, bool factionMount = false, bool learned = false)
{
Player player = _owner.GetPlayer();
if (!player)
return false;
MountRecord mount = Global.DB2Mgr.GetMount(spellId);
if (mount == null)
return false;
var value = FactionSpecificMounts.LookupByKey(spellId);
if (value != 0 && !factionMount)
AddMount(value, flags, true, learned);
_mounts[spellId] = flags;
// Mount condition only applies to using it, should still learn it.
if (mount.PlayerConditionId != 0)
{
PlayerConditionRecord playerCondition = CliDB.PlayerConditionStorage.LookupByKey(mount.PlayerConditionId);
if (!ConditionManager.IsPlayerMeetingCondition(player, playerCondition))
return false;
}
if (!learned)
{
if (!factionMount)
SendSingleMountUpdate(spellId, flags);
if (!player.HasSpell(spellId))
player.LearnSpell(spellId, true);
}
return true;
}
public void MountSetFavorite(uint spellId, bool favorite)
{
if (!_mounts.ContainsKey(spellId))
return;
if (favorite)
_mounts[spellId] |= MountStatusFlags.IsFavorite;
else
_mounts[spellId] &= ~MountStatusFlags.IsFavorite;
SendSingleMountUpdate(spellId, _mounts[spellId]);
}
void SendSingleMountUpdate(uint spellId, MountStatusFlags mountStatusFlags)
{
Player player = _owner.GetPlayer();
if (!player)
return;
AccountMountUpdate mountUpdate = new AccountMountUpdate();
mountUpdate.IsFullUpdate = false;
mountUpdate.Mounts.Add(spellId, mountStatusFlags);
player.SendPacket(mountUpdate);
}
public void LoadItemAppearances()
{
foreach (uint blockValue in _appearances.ToBlockRange())
{
_owner.GetPlayer().AddDynamicValue(PlayerDynamicFields.Transmog, blockValue);
}
foreach (var value in _temporaryAppearances.Keys)
_owner.GetPlayer().AddDynamicValue(PlayerDynamicFields.ConditionalTransmog, value);
}
public void LoadAccountItemAppearances(SQLResult knownAppearances, SQLResult favoriteAppearances)
{
if (!knownAppearances.IsEmpty())
{
uint[] blocks = new uint[1];
do
{
ushort blobIndex = knownAppearances.Read<ushort>(0);
if (blobIndex >= blocks.Length)
Array.Resize(ref blocks, blobIndex + 1);
blocks[blobIndex] = knownAppearances.Read<uint>(1);
} while (knownAppearances.NextRow());
_appearances = new System.Collections.BitSet(blocks);
}
if (!favoriteAppearances.IsEmpty())
{
do
{
_favoriteAppearances[favoriteAppearances.Read<uint>(0)] = FavoriteAppearanceState.Unchanged;
} while (favoriteAppearances.NextRow());
}
// Static item appearances known by every player
uint[] hiddenAppearanceItems =
{
134110, // Hidden Helm
134111, // Hidden Cloak
134112, // Hidden Shoulder
142503, // Hidden Shirt
142504, // Hidden Tabard
143539 // Hidden Belt
};
foreach (uint hiddenItem in hiddenAppearanceItems)
{
ItemModifiedAppearanceRecord hiddenAppearance = Global.DB2Mgr.GetItemModifiedAppearance(hiddenItem, 0);
//ASSERT(hiddenAppearance);
if (_appearances.Length <= hiddenAppearance.Id)
_appearances.Length = (int)hiddenAppearance.Id + 1;
_appearances.Set((int)hiddenAppearance.Id, true);
}
}
public void SaveAccountItemAppearances(SQLTransaction trans)
{
PreparedStatement stmt;
ushort blockIndex = 0;
foreach (uint blockValue in _appearances.ToBlockRange())
{
if (blockValue != 0) // this table is only appended/bits are set (never cleared) so don't save empty blocks
{
stmt = DB.Login.GetPreparedStatement(LoginStatements.INS_BNET_ITEM_APPEARANCES);
stmt.AddValue(0, _owner.GetBattlenetAccountId());
stmt.AddValue(1, blockIndex);
stmt.AddValue(2, blockValue);
trans.Append(stmt);
}
++blockIndex;
}
foreach (var key in _favoriteAppearances.Keys)
{
var appearanceState = _favoriteAppearances[key];
switch (appearanceState)
{
case FavoriteAppearanceState.New:
stmt = DB.Login.GetPreparedStatement(LoginStatements.INS_BNET_ITEM_FAVORITE_APPEARANCE);
stmt.AddValue(0, _owner.GetBattlenetAccountId());
stmt.AddValue(1, key);
trans.Append(stmt);
appearanceState = FavoriteAppearanceState.Unchanged;
break;
case FavoriteAppearanceState.Removed:
stmt = DB.Login.GetPreparedStatement(LoginStatements.DEL_BNET_ITEM_FAVORITE_APPEARANCE);
stmt.AddValue(0, _owner.GetBattlenetAccountId());
stmt.AddValue(1, key);
trans.Append(stmt);
_favoriteAppearances.Remove(key);
break;
case FavoriteAppearanceState.Unchanged:
break;
}
}
}
uint[] PlayerClassByArmorSubclass =
{
(int)Class.ClassMaskAllPlayable, //ITEM_SUBCLASS_ARMOR_MISCELLANEOUS
(1 << ((int)Class.Priest - 1)) | (1 << ((int)Class.Mage - 1)) | (1 << ((int)Class.Warlock - 1)), //ITEM_SUBCLASS_ARMOR_CLOTH
(1 << ((int)Class.Rogue - 1)) | (1 << ((int)Class.Monk - 1)) | (1 << ((int)Class.Druid - 1)) | (1 << ((int)Class.DemonHunter - 1)), //ITEM_SUBCLASS_ARMOR_LEATHER
(1 << ((int)Class.Hunter - 1)) | (1 << ((int)Class.Shaman - 1)), //ITEM_SUBCLASS_ARMOR_MAIL
(1 << ((int)Class.Warrior - 1)) | (1 << ((int)Class.Paladin - 1)) | (1 << ((int)Class.Deathknight - 1)), //ITEM_SUBCLASS_ARMOR_PLATE
(int)Class.ClassMaskAllPlayable, //ITEM_SUBCLASS_ARMOR_BUCKLER
(1 << ((int)Class.Warrior - 1)) | (1 << ((int)Class.Paladin - 1)) | (1 << ((int)Class.Shaman - 1)), //ITEM_SUBCLASS_ARMOR_SHIELD
1 << ((int)Class.Paladin - 1), //ITEM_SUBCLASS_ARMOR_LIBRAM
1 << ((int)Class.Druid - 1), //ITEM_SUBCLASS_ARMOR_IDOL
1 << ((int)Class.Shaman - 1), //ITEM_SUBCLASS_ARMOR_TOTEM
1 << ((int)Class.Deathknight - 1), //ITEM_SUBCLASS_ARMOR_SIGIL
(1 << ((int)Class.Paladin - 1)) | (1 << ((int)Class.Deathknight - 1)) | (1 << ((int)Class.Shaman - 1)) | (1 << ((int)Class.Druid - 1)), //ITEM_SUBCLASS_ARMOR_RELIC
};
public void AddItemAppearance(Item item)
{
if (!item.IsSoulBound())
return;
ItemModifiedAppearanceRecord itemModifiedAppearance = item.GetItemModifiedAppearance();
if (!CanAddAppearance(itemModifiedAppearance))
return;
if (Convert.ToBoolean(item.GetUInt32Value(ItemFields.Flags) & (uint)(ItemFieldFlags.BopTradeable | ItemFieldFlags.Refundable)))
{
AddTemporaryAppearance(item.GetGUID(), itemModifiedAppearance);
return;
}
AddItemAppearance(itemModifiedAppearance);
}
public void AddItemAppearance(uint itemId, uint appearanceModId = 0)
{
ItemModifiedAppearanceRecord itemModifiedAppearance = Global.DB2Mgr.GetItemModifiedAppearance(itemId, appearanceModId);
if (!CanAddAppearance(itemModifiedAppearance))
return;
AddItemAppearance(itemModifiedAppearance);
}
bool CanAddAppearance(ItemModifiedAppearanceRecord itemModifiedAppearance)
{
if (itemModifiedAppearance == null)
return false;
if (itemModifiedAppearance.SourceType == 6 || itemModifiedAppearance.SourceType == 9)
return false;
if (!CliDB.ItemSearchNameStorage.ContainsKey(itemModifiedAppearance.ItemID))
return false;
ItemTemplate itemTemplate = Global.ObjectMgr.GetItemTemplate(itemModifiedAppearance.ItemID);
if (itemTemplate == null)
return false;
if (_owner.GetPlayer().CanUseItem(itemTemplate) != InventoryResult.Ok)
return false;
if (itemTemplate.GetFlags2().HasAnyFlag(ItemFlags2.NoSourceForItemVisual) || itemTemplate.GetQuality() == ItemQuality.Artifact)
return false;
switch (itemTemplate.GetClass())
{
case ItemClass.Weapon:
{
if (!Convert.ToBoolean(_owner.GetPlayer().GetWeaponProficiency() & (1 << (int)itemTemplate.GetSubClass())))
return false;
if (itemTemplate.GetSubClass() == (int)ItemSubClassWeapon.Exotic ||
itemTemplate.GetSubClass() == (int)ItemSubClassWeapon.Exotic2 ||
itemTemplate.GetSubClass() == (int)ItemSubClassWeapon.Miscellaneous ||
itemTemplate.GetSubClass() == (int)ItemSubClassWeapon.Thrown ||
itemTemplate.GetSubClass() == (int)ItemSubClassWeapon.Spear ||
itemTemplate.GetSubClass() == (int)ItemSubClassWeapon.FishingPole)
return false;
break;
}
case ItemClass.Armor:
{
switch (itemTemplate.GetInventoryType())
{
case InventoryType.Body:
case InventoryType.Shield:
case InventoryType.Cloak:
case InventoryType.Tabard:
case InventoryType.Holdable:
break;
case InventoryType.Head:
case InventoryType.Shoulders:
case InventoryType.Chest:
case InventoryType.Waist:
case InventoryType.Legs:
case InventoryType.Feet:
case InventoryType.Wrists:
case InventoryType.Hands:
case InventoryType.Robe:
if ((ItemSubClassArmor)itemTemplate.GetSubClass() == ItemSubClassArmor.Miscellaneous)
return false;
break;
default:
return false;
}
if (itemTemplate.GetInventoryType() != InventoryType.Cloak)
if (!Convert.ToBoolean(PlayerClassByArmorSubclass[itemTemplate.GetSubClass()] & _owner.GetPlayer().getClassMask()))
return false;
break;
}
default:
return false;
}
if (itemTemplate.GetQuality() < ItemQuality.Uncommon)
if (!itemTemplate.GetFlags2().HasAnyFlag(ItemFlags2.IgnoreQualityForItemVisualSource) || !itemTemplate.GetFlags3().HasAnyFlag(ItemFlags3.ActsAsTransmogHiddenVisualOption))
return false;
if (itemModifiedAppearance.Id < _appearances.Count && _appearances.Get((int)itemModifiedAppearance.Id))
return false;
return true;
}
//todo check this
void AddItemAppearance(ItemModifiedAppearanceRecord itemModifiedAppearance)
{
if (_appearances.Count <= itemModifiedAppearance.Id)
{
uint numBlocks = (uint)(_appearances.Count << 2);
_appearances.Length = (int)itemModifiedAppearance.Id + 1;
numBlocks = (uint)(_appearances.Count << 2) - numBlocks;
while (numBlocks-- != 0)
_owner.GetPlayer().AddDynamicValue(PlayerDynamicFields.Transmog, 0);
}
_appearances.Set((int)itemModifiedAppearance.Id, true);
uint blockIndex = itemModifiedAppearance.Id / 32;
uint bitIndex = itemModifiedAppearance.Id % 32;
uint currentMask = _owner.GetPlayer().GetDynamicValue(PlayerDynamicFields.Transmog, (ushort)blockIndex);
_owner.GetPlayer().SetDynamicValue(PlayerDynamicFields.Transmog, (ushort)blockIndex, (uint)((int)currentMask | (1 << (int)bitIndex)));
var temporaryAppearance = _temporaryAppearances.LookupByKey(itemModifiedAppearance.Id);
if (!temporaryAppearance.Empty())
{
_owner.GetPlayer().RemoveDynamicValue(PlayerDynamicFields.ConditionalTransmog, itemModifiedAppearance.Id);
_temporaryAppearances.Remove(itemModifiedAppearance.Id);
}
ItemRecord item = CliDB.ItemStorage.LookupByKey(itemModifiedAppearance.ItemID);
if (item != null)
{
int transmogSlot = Item.ItemTransmogrificationSlots[(int)item.inventoryType];
if (transmogSlot >= 0)
_owner.GetPlayer().UpdateCriteria(CriteriaTypes.AppearanceUnlockedBySlot, (ulong)transmogSlot, 1);
}
var sets = Global.DB2Mgr.GetTransmogSetsForItemModifiedAppearance(itemModifiedAppearance.Id);
foreach (TransmogSetRecord set in sets)
if (IsSetCompleted(set.Id))
_owner.GetPlayer().UpdateCriteria(CriteriaTypes.TransmogSetUnlocked, set.TransmogSetGroupID);
}
void AddTemporaryAppearance(ObjectGuid itemGuid, ItemModifiedAppearanceRecord itemModifiedAppearance)
{
var itemsWithAppearance = _temporaryAppearances[itemModifiedAppearance.Id];
if (itemsWithAppearance.Empty())
_owner.GetPlayer().AddDynamicValue(PlayerDynamicFields.ConditionalTransmog, itemModifiedAppearance.Id);
itemsWithAppearance.Add(itemGuid);
}
public void RemoveTemporaryAppearance(Item item)
{
ItemModifiedAppearanceRecord itemModifiedAppearance = item.GetItemModifiedAppearance();
if (itemModifiedAppearance == null)
return;
var guid = _temporaryAppearances.LookupByKey(itemModifiedAppearance.Id);
if (guid.Empty())
return;
guid.Remove(item.GetGUID());
if (guid.Empty())
{
_owner.GetPlayer().RemoveDynamicValue(PlayerDynamicFields.ConditionalTransmog, itemModifiedAppearance.Id);
_temporaryAppearances.Remove(itemModifiedAppearance.Id);
}
}
public Tuple<bool, bool> HasItemAppearance(uint itemModifiedAppearanceId)
{
if (itemModifiedAppearanceId < _appearances.Count && _appearances.Get((int)itemModifiedAppearanceId))
return Tuple.Create(true, false);
if (_temporaryAppearances.ContainsKey(itemModifiedAppearanceId))
return Tuple.Create(true, true);
return Tuple.Create(false, false);
}
public List<ObjectGuid> GetItemsProvidingTemporaryAppearance(uint itemModifiedAppearanceId)
{
return _temporaryAppearances.LookupByKey(itemModifiedAppearanceId);
}
public void SetAppearanceIsFavorite(uint itemModifiedAppearanceId, bool apply)
{
var apperanceState = _favoriteAppearances.LookupByKey(itemModifiedAppearanceId);
if (apply)
{
if (!_favoriteAppearances.ContainsKey(itemModifiedAppearanceId))
_favoriteAppearances[itemModifiedAppearanceId] = FavoriteAppearanceState.New;
else if (apperanceState == FavoriteAppearanceState.Removed)
apperanceState = FavoriteAppearanceState.Unchanged;
else
return;
}
else if (_favoriteAppearances.ContainsKey(itemModifiedAppearanceId))
{
if (apperanceState == FavoriteAppearanceState.New)
_favoriteAppearances.Remove(itemModifiedAppearanceId);
else
apperanceState = FavoriteAppearanceState.Removed;
}
else
return;
_favoriteAppearances[itemModifiedAppearanceId] = apperanceState;
TransmogCollectionUpdate transmogCollectionUpdate = new TransmogCollectionUpdate();
transmogCollectionUpdate.IsFullUpdate = false;
transmogCollectionUpdate.IsSetFavorite = apply;
transmogCollectionUpdate.FavoriteAppearances.Add(itemModifiedAppearanceId);
_owner.SendPacket(transmogCollectionUpdate);
}
public void SendFavoriteAppearances()
{
TransmogCollectionUpdate transmogCollectionUpdate = new TransmogCollectionUpdate();
transmogCollectionUpdate.IsFullUpdate = true;
foreach (var pair in _favoriteAppearances)
if (pair.Value != FavoriteAppearanceState.Removed)
transmogCollectionUpdate.FavoriteAppearances.Add(pair.Key);
_owner.SendPacket(transmogCollectionUpdate);
}
public void AddTransmogSet(uint transmogSetId)
{
var items = Global.DB2Mgr.GetTransmogSetItems(transmogSetId);
if (items.Empty())
return;
foreach (TransmogSetItemRecord item in items)
{
ItemModifiedAppearanceRecord itemModifiedAppearance = CliDB.ItemModifiedAppearanceStorage.LookupByKey(item.ItemModifiedAppearanceID);
if (itemModifiedAppearance == null)
continue;
AddItemAppearance(itemModifiedAppearance);
}
}
bool IsSetCompleted(uint transmogSetId)
{
var transmogSetItems = Global.DB2Mgr.GetTransmogSetItems(transmogSetId);
if (transmogSetItems.Empty())
return false;
int[] knownPieces = new int[EquipmentSlot.End];
for (var i = 0; i < EquipmentSlot.End; ++i)
knownPieces[i] = -1;
foreach (TransmogSetItemRecord transmogSetItem in transmogSetItems)
{
ItemModifiedAppearanceRecord itemModifiedAppearance = CliDB.ItemModifiedAppearanceStorage.LookupByKey(transmogSetItem.ItemModifiedAppearanceID);
if (itemModifiedAppearance == null)
continue;
ItemRecord item = CliDB.ItemStorage.LookupByKey(itemModifiedAppearance.ItemID);
if (item == null)
continue;
int transmogSlot = Item.ItemTransmogrificationSlots[(int)item.inventoryType];
if (transmogSlot < 0 || knownPieces[transmogSlot] == 1)
continue;
(var hasAppearance, var isTemporary) = HasItemAppearance(transmogSetItem.ItemModifiedAppearanceID);
knownPieces[transmogSlot] = (hasAppearance && !isTemporary) ? 1 : 0;
}
return !knownPieces.Contains(0);
}
public bool HasToy(uint itemId) { return _toys.ContainsKey(itemId); }
public Dictionary<uint, bool> GetAccountToys() { return _toys; }
public Dictionary<uint, HeirloomData> GetAccountHeirlooms() { return _heirlooms; }
public Dictionary<uint, MountStatusFlags> GetAccountMounts() { return _mounts; }
WorldSession _owner;
Dictionary<uint, bool> _toys = new Dictionary<uint, bool>();
Dictionary<uint, HeirloomData> _heirlooms = new Dictionary<uint, HeirloomData>();
Dictionary<uint, MountStatusFlags> _mounts = new Dictionary<uint, MountStatusFlags>();
System.Collections.BitSet _appearances;
MultiMap<uint, ObjectGuid> _temporaryAppearances = new MultiMap<uint, ObjectGuid>();
Dictionary<uint, FavoriteAppearanceState> _favoriteAppearances = new Dictionary<uint, FavoriteAppearanceState>();
static Dictionary<uint, uint> FactionSpecificMounts = new Dictionary<uint, uint>();
}
enum FavoriteAppearanceState
{
New,
Removed,
Unchanged
}
public class HeirloomData
{
public HeirloomData(HeirloomPlayerFlags _flags = 0, uint _bonusId = 0)
{
flags = _flags;
bonusId = _bonusId;
}
public HeirloomPlayerFlags flags;
public uint bonusId;
}
}
+281
View File
@@ -0,0 +1,281 @@
/*
* Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Framework.Constants;
using Game.DataStorage;
using Game.Groups;
using Game.Maps;
using Game.Scenarios;
using System.Collections.Generic;
namespace Game.Entities
{
public class KillRewarder
{
public KillRewarder(Player killer, Unit victim, bool isBattleground)
{
_killer = killer;
_victim = victim;
_group = killer.GetGroup();
_groupRate = 1.0f;
_maxNotGrayMember = null;
_count = 0;
_sumLevel = 0;
_xp = 0;
_isFullXP = false;
_maxLevel = 0;
_isBattleground = isBattleground;
_isPvP = false;
// mark the credit as pvp if victim is player
if (victim.IsTypeId(TypeId.Player))
_isPvP = true;
// or if its owned by player and its not a vehicle
else if (victim.GetCharmerOrOwnerGUID().IsPlayer())
_isPvP = !victim.IsVehicle();
_InitGroupData();
}
public void Reward()
{
// 3. Reward killer (and group, if necessary).
if (_group)
// 3.1. If killer is in group, reward group.
_RewardGroup();
else
{
// 3.2. Reward single killer (not group case).
// 3.2.1. Initialize initial XP amount based on killer's level.
_InitXP(_killer);
// To avoid unnecessary calculations and calls,
// proceed only if XP is not ZERO or player is not on Battleground
// (Battlegroundrewards only XP, that's why).
if (!_isBattleground || _xp != 0)
// 3.2.2. Reward killer.
_RewardPlayer(_killer, false);
}
// 5. Credit instance encounter.
// 6. Update guild achievements.
// 7. Credit scenario criterias
Creature victim = _victim.ToCreature();
if (victim != null)
{
if (victim.IsDungeonBoss())
{
InstanceScript instance = _victim.GetInstanceScript();
if (instance != null)
instance.UpdateEncounterStateForKilledCreature(_victim.GetEntry(), _victim);
}
uint guildId = victim.GetMap().GetOwnerGuildId();
var guild = Global.GuildMgr.GetGuildById(guildId);
if (guild != null)
guild.UpdateCriteria(CriteriaTypes.KillCreature, victim.GetEntry(), 1, 0, victim, _killer);
Scenario scenario = victim.GetScenario();
if (scenario != null)
scenario.UpdateCriteria(CriteriaTypes.KillCreature, victim.GetEntry(), 1, 0, victim, _killer);
}
}
void _InitGroupData()
{
if (_group)
{
// 2. In case when player is in group, initialize variables necessary for group calculations:
for (GroupReference refe = _group.GetFirstMember(); refe != null; refe = refe.next())
{
Player member = refe.GetSource();
if (member)
{
if (member.IsAlive() && member.IsAtGroupRewardDistance(_victim))
{
uint lvl = member.getLevel();
// 2.1. _count - number of alive group members within reward distance;
++_count;
// 2.2. _sumLevel - sum of levels of alive group members within reward distance;
_sumLevel += lvl;
// 2.3. _maxLevel - maximum level of alive group member within reward distance;
if (_maxLevel < lvl)
_maxLevel = (byte)lvl;
// 2.4. _maxNotGrayMember - maximum level of alive group member within reward distance,
// for whom victim is not gray;
uint grayLevel = Formulas.GetGrayLevel(lvl);
if (_victim.GetLevelForTarget(member) > grayLevel && (!_maxNotGrayMember || _maxNotGrayMember.getLevel() < lvl))
_maxNotGrayMember = member;
}
}
}
// 2.5. _isFullXP - flag identifying that for all group members victim is not gray,
// so 100% XP will be rewarded (50% otherwise).
_isFullXP = _maxNotGrayMember && (_maxLevel == _maxNotGrayMember.getLevel());
}
else
_count = 1;
}
void _InitXP(Player player)
{
// Get initial value of XP for kill.
// XP is given:
// * on Battlegrounds;
// * otherwise, not in PvP;
// * not if killer is on vehicle.
if (_isBattleground || (!_isPvP && _killer.GetVehicle() == null))
_xp = Formulas.XPGain(player, _victim, _isBattleground);
}
void _RewardHonor(Player player)
{
// Rewarded player must be alive.
if (player.IsAlive())
player.RewardHonor(_victim, _count, -1, true);
}
void _RewardXP(Player player, float rate)
{
uint xp = _xp;
if (_group)
{
// 4.2.1. If player is in group, adjust XP:
// * set to 0 if player's level is more than maximum level of not gray member;
// * cut XP in half if _isFullXP is false.
if (_maxNotGrayMember != null && player.IsAlive() &&
_maxNotGrayMember.getLevel() >= player.getLevel())
xp = _isFullXP ?
(uint)(xp * rate) : // Reward FULL XP if all group members are not gray.
(uint)(xp * rate / 2) + 1; // Reward only HALF of XP if some of group members are gray.
else
xp = 0;
}
if (xp != 0)
{
// 4.2.2. Apply auras modifying rewarded XP (SPELL_AURA_MOD_XP_PCT and SPELL_AURA_MOD_XP_FROM_CREATURE_TYPE).
xp = (uint)(xp * player.GetTotalAuraMultiplier(AuraType.ModXpPct));
xp = (uint)(xp * player.GetTotalAuraMultiplierByMiscValue(AuraType.ModXpFromCreatureType, (int)_victim.GetCreatureType()));
// 4.2.3. Give XP to player.
player.GiveXP(xp, _victim, _groupRate);
Pet pet = player.GetPet();
if (pet)
// 4.2.4. If player has pet, reward pet with XP (100% for single player, 50% for group case).
pet.GivePetXP(_group ? xp / 2 : xp);
}
}
void _RewardReputation(Player player, float rate)
{
// 4.3. Give reputation (player must not be on BG).
// Even dead players and corpses are rewarded.
player.RewardReputation(_victim, rate);
}
void _RewardKillCredit(Player player)
{
// 4.4. Give kill credit (player must not be in group, or he must be alive or without corpse).
if (!_group || player.IsAlive() || player.GetCorpse() == null)
{
Creature target = _victim.ToCreature();
if (target != null)
{
player.KilledMonster(target.GetCreatureTemplate(), target.GetGUID());
player.UpdateCriteria(CriteriaTypes.KillCreatureType, (ulong)target.GetCreatureType(), 1, 0, target);
}
}
}
void _RewardPlayer(Player player, bool isDungeon)
{
// 4. Reward player.
if (!_isBattleground)
{
// 4.1. Give honor (player must be alive and not on BG).
_RewardHonor(player);
// 4.1.1 Send player killcredit for quests with PlayerSlain
if (_victim.IsTypeId(TypeId.Player))
player.KilledPlayerCredit();
}
// Give XP only in PvE or in Battlegrounds.
// Give reputation and kill credit only in PvE.
if (!_isPvP || _isBattleground)
{
float rate = _group ? _groupRate * player.getLevel() / _sumLevel : 1.0f;
if (_xp != 0)
// 4.2. Give XP.
_RewardXP(player, rate);
if (!_isBattleground)
{
// If killer is in dungeon then all members receive full reputation at kill.
_RewardReputation(player, isDungeon ? 1.0f : rate);
_RewardKillCredit(player);
}
}
}
void _RewardGroup()
{
if (_maxLevel != 0)
{
if (_maxNotGrayMember != null)
// 3.1.1. Initialize initial XP amount based on maximum level of group member,
// for whom victim is not gray.
_InitXP(_maxNotGrayMember);
// To avoid unnecessary calculations and calls,
// proceed only if XP is not ZERO or player is not on Battleground
// (Battlegroundrewards only XP, that's why).
if (!_isBattleground || _xp != 0)
{
bool isDungeon = !_isPvP && CliDB.MapStorage.LookupByKey(_killer.GetMapId()).IsDungeon();
if (!_isBattleground)
{
// 3.1.2. Alter group rate if group is in raid (not for Battlegrounds).
bool isRaid = !_isPvP && CliDB.MapStorage.LookupByKey(_killer.GetMapId()).IsRaid() && _group.isRaidGroup();
_groupRate = Formulas.XPInGroupRate(_count, isRaid);
}
// 3.1.3. Reward each group member (even dead or corpse) within reward distance.
for (GroupReference refe = _group.GetFirstMember(); refe != null; refe = refe.next())
{
Player member = refe.GetSource();
if (member)
{
if (member.IsAtGroupRewardDistance(_victim))
{
_RewardPlayer(member, isDungeon);
member.UpdateCriteria(CriteriaTypes.SpecialPvpKill, 1, 0, 0, _victim);
}
}
}
}
}
}
Player _killer;
Unit _victim;
Group _group;
float _groupRate;
Player _maxNotGrayMember;
uint _count;
uint _sumLevel;
uint _xp;
bool _isFullXP;
byte _maxLevel;
bool _isBattleground;
bool _isPvP;
}
}
@@ -0,0 +1,91 @@
/*
* Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Framework.Constants;
using Game.Achievements;
using Game.DataStorage;
using Game.Guilds;
using Game.Scenarios;
namespace Game.Entities
{
public partial class Player
{
public void ResetAchievements()
{
m_achievementSys.Reset();
}
public void SendRespondInspectAchievements(Player player)
{
m_achievementSys.SendAchievementInfo(player);
}
public uint GetAchievementPoints()
{
return m_achievementSys.GetAchievementPoints();
}
public bool HasAchieved(uint achievementId)
{
return m_achievementSys.HasAchieved(achievementId);
}
public void StartCriteriaTimer(CriteriaTimedTypes type, uint entry, uint timeLost = 0)
{
m_achievementSys.StartCriteriaTimer(type, entry, timeLost);
}
public void RemoveCriteriaTimer(CriteriaTimedTypes type, uint entry)
{
m_achievementSys.RemoveCriteriaTimer(type, entry);
}
public void ResetCriteria(CriteriaTypes type, ulong miscValue1 = 0, ulong miscValue2 = 0, bool evenIfCriteriaComplete = false)
{
m_achievementSys.ResetCriteria(type, miscValue1, miscValue2, evenIfCriteriaComplete);
m_questObjectiveCriteriaMgr.ResetCriteria(type, miscValue1, miscValue2, evenIfCriteriaComplete);
}
public void UpdateCriteria(CriteriaTypes type, ulong miscValue1 = 0, ulong miscValue2 = 0, ulong miscValue3 = 0, Unit unit = null)
{
m_achievementSys.UpdateCriteria(type, miscValue1, miscValue2, miscValue3, unit, this);
m_questObjectiveCriteriaMgr.UpdateCriteria(type, miscValue1, miscValue2, miscValue3, unit, this);
// Update only individual achievement criteria here, otherwise we may get multiple updates
// from a single boss kill
if (CriteriaManager.IsGroupCriteriaType(type))
return;
Scenario scenario = GetScenario();
if (scenario != null)
scenario.UpdateCriteria(type, miscValue1, miscValue2, miscValue3, unit, this);
Guild guild = Global.GuildMgr.GetGuildById(GetGuildId());
if (guild)
guild.UpdateCriteria(type, miscValue1, miscValue2, miscValue3, unit, this);
}
public void CompletedAchievement(AchievementRecord entry)
{
m_achievementSys.CompletedAchievement(entry, this);
}
public bool ModifierTreeSatisfied(uint modifierTreeId)
{
return m_achievementSys.ModifierTreeSatisfied(modifierTreeId);
}
}
}
@@ -0,0 +1,581 @@
/*
* Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Framework.Constants;
using Game.DataStorage;
using Game.Groups;
using Game.Network.Packets;
using Game.Spells;
using System;
using System.Collections.Generic;
namespace Game.Entities
{
public partial class Player
{
void SetRegularAttackTime()
{
for (WeaponAttackType weaponAttackType = 0; weaponAttackType < WeaponAttackType.Max; ++weaponAttackType)
{
Item tmpitem = GetWeaponForAttack(weaponAttackType, true);
if (tmpitem != null && !tmpitem.IsBroken())
{
ItemTemplate proto = tmpitem.GetTemplate();
if (proto.GetDelay() != 0)
SetBaseAttackTime(weaponAttackType, proto.GetDelay());
}
else
SetBaseAttackTime(weaponAttackType, SharedConst.BaseAttackTime); // If there is no weapon reset attack time to base (might have been changed from forms)
}
}
public void RewardPlayerAndGroupAtKill(Unit victim, bool isBattleground)
{
new KillRewarder(this, victim, isBattleground).Reward();
}
public void RewardPlayerAndGroupAtEvent(uint creature_id, WorldObject pRewardSource)
{
if (pRewardSource == null)
return;
ObjectGuid creature_guid = pRewardSource.IsTypeId(TypeId.Unit) ? pRewardSource.GetGUID() : ObjectGuid.Empty;
// prepare data for near group iteration
Group group = GetGroup();
if (group)
{
for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.next())
{
Player player = refe.GetSource();
if (!player)
continue;
if (!player.IsAtGroupRewardDistance(pRewardSource))
continue; // member (alive or dead) or his corpse at req. distance
// quest objectives updated only for alive group member or dead but with not released body
if (player.IsAlive() || !player.GetCorpse())
player.KilledMonsterCredit(creature_id, creature_guid);
}
}
else
KilledMonsterCredit(creature_id, creature_guid);
}
public void AddWeaponProficiency(uint newflag) { m_WeaponProficiency |= newflag; }
public void AddArmorProficiency(uint newflag) { m_ArmorProficiency |= newflag; }
public uint GetWeaponProficiency() { return m_WeaponProficiency; }
public uint GetArmorProficiency() { return m_ArmorProficiency; }
public void SendProficiency(ItemClass itemClass, uint itemSubclassMask)
{
SetProficiency packet = new SetProficiency();
packet.ProficiencyMask = itemSubclassMask;
packet.ProficiencyClass = (byte)itemClass;
SendPacket(packet);
}
bool CanTitanGrip() { return m_canTitanGrip; }
public override bool CanUseAttackType(WeaponAttackType attacktype)
{
switch (attacktype)
{
case WeaponAttackType.BaseAttack:
return !HasFlag(UnitFields.Flags, UnitFlags.Disarmed);
case WeaponAttackType.OffAttack:
return !HasFlag(UnitFields.Flags2, UnitFlags2.DisarmOffhand);
case WeaponAttackType.RangedAttack:
return !HasFlag(UnitFields.Flags2, UnitFlags2.DisarmRanged);
}
return true;
}
float GetRatingMultiplier(CombatRating cr)
{
GtCombatRatingsRecord Rating = CliDB.CombatRatingsGameTable.GetRow(getLevel());
if (Rating == null)
return 1.0f;
float value = GetGameTableColumnForCombatRating(Rating, cr);
if (value == 0)
return 1.0f; // By default use minimum coefficient (not must be called)
return 1.0f / value;
}
public float GetRatingBonusValue(CombatRating cr)
{
float baseResult = GetFloatValue(PlayerFields.CombatRating1 + (int)cr) * GetRatingMultiplier(cr);
if (cr != CombatRating.ResiliencePlayerDamage)
return baseResult;
return (float)(1.0f - Math.Pow(0.99f, baseResult)) * 100.0f;
}
void GetDodgeFromAgility(float diminishing, float nondiminishing)
{
/*// Table for base dodge values
float[] dodge_base =
{
0.037580f, // Warrior
0.036520f, // Paladin
-0.054500f, // Hunter
-0.005900f, // Rogue
0.031830f, // Priest
0.036640f, // DK
0.016750f, // Shaman
0.034575f, // Mage
0.020350f, // Warlock
0.0f, // ??
0.049510f // Druid
};
// Crit/agility to dodge/agility coefficient multipliers; 3.2.0 increased required agility by 15%
float[] crit_to_dodge =
{
0.85f/1.15f, // Warrior
1.00f/1.15f, // Paladin
1.11f/1.15f, // Hunter
2.00f/1.15f, // Rogue
1.00f/1.15f, // Priest
0.85f/1.15f, // DK
1.60f/1.15f, // Shaman
1.00f/1.15f, // Mage
0.97f/1.15f, // Warlock (?)
0.0f, // ??
2.00f/1.15f // Druid
};
uint level = getLevel();
uint pclass = (uint)GetClass();
if (level > CliDB.GtChanceToMeleeCritStorage.GetTableRowCount())
level = CliDB.GtChanceToMeleeCritStorage.GetTableRowCount() - 1;
// Dodge per agility is proportional to crit per agility, which is available from DBC files
var dodgeRatio = CliDB.GtChanceToMeleeCritStorage.EvaluateTable(level - 1, pclass - 1);
if (dodgeRatio == null || pclass > (int)Class.Max)
return;
// @todo research if talents/effects that increase total agility by x% should increase non-diminishing part
float base_agility = GetCreateStat(Stats.Agility) * m_auraModifiersGroup[(int)UnitMods.StatAgility][(int)UnitModifierType.BasePCT];
float bonus_agility = GetStat(Stats.Agility) - base_agility;
// calculate diminishing (green in char screen) and non-diminishing (white) contribution
diminishing = 100.0f * bonus_agility * dodgeRatio.Value * crit_to_dodge[(int)pclass - 1];
nondiminishing = 100.0f * (dodge_base[(int)pclass - 1] + base_agility * dodgeRatio.Value * crit_to_dodge[pclass - 1]);
*/
}
float GetTotalPercentageModValue(BaseModGroup modGroup)
{
return m_auraBaseMod[(int)modGroup][0] + m_auraBaseMod[(int)modGroup][1];
}
public float GetExpertiseDodgeOrParryReduction(WeaponAttackType attType)
{
float baseExpertise = 7.5f;
switch (attType)
{
case WeaponAttackType.BaseAttack:
return baseExpertise + GetUInt32Value(PlayerFields.Expertise) / 4.0f;
case WeaponAttackType.OffAttack:
return baseExpertise + GetUInt32Value(PlayerFields.OffhandExpertise) / 4.0f;
default:
break;
}
return 0.0f;
}
public bool IsUseEquipedWeapon(bool mainhand)
{
// disarm applied only to mainhand weapon
return !IsInFeralForm() && (!mainhand || !HasFlag(UnitFields.Flags, UnitFlags.Disarmed));
}
public void SetCanTitanGrip(bool value, uint penaltySpellId = 0)
{
if (value == m_canTitanGrip)
return;
m_canTitanGrip = value;
m_titanGripPenaltySpellId = penaltySpellId;
}
void CheckTitanGripPenalty()
{
if (!CanTitanGrip())
return;
bool apply = IsUsingTwoHandedWeaponInOneHand();
if (apply)
{
if (!HasAura(m_titanGripPenaltySpellId))
CastSpell((Unit)null, m_titanGripPenaltySpellId, true);
}
else
RemoveAurasDueToSpell(m_titanGripPenaltySpellId);
}
bool IsTwoHandUsed()
{
Item mainItem = GetItemByPos(InventorySlots.Bag0, EquipmentSlot.MainHand);
if (!mainItem)
return false;
ItemTemplate itemTemplate = mainItem.GetTemplate();
return (itemTemplate.GetInventoryType() == InventoryType.Weapon2Hand && !CanTitanGrip()) ||
itemTemplate.GetInventoryType() == InventoryType.Ranged ||
(itemTemplate.GetInventoryType() == InventoryType.RangedRight && itemTemplate.GetClass() == ItemClass.Weapon && (ItemSubClassWeapon)itemTemplate.GetSubClass() != ItemSubClassWeapon.Wand);
}
bool IsUsingTwoHandedWeaponInOneHand()
{
Item offItem = GetItemByPos(InventorySlots.Bag0, EquipmentSlot.OffHand);
if (offItem && offItem.GetTemplate().GetInventoryType() == InventoryType.Weapon2Hand)
return true;
Item mainItem = GetItemByPos(InventorySlots.Bag0, EquipmentSlot.MainHand);
if (!mainItem || mainItem.GetTemplate().GetInventoryType() == InventoryType.Weapon2Hand)
return false;
if (!offItem)
return false;
return true;
}
public void _ApplyWeaponDamage(uint slot, Item item, bool apply)
{
ItemTemplate proto = item.GetTemplate();
WeaponAttackType attType = WeaponAttackType.BaseAttack;
float damage = 0.0f;
if (slot == EquipmentSlot.MainHand && (proto.GetInventoryType() == InventoryType.Ranged || proto.GetInventoryType() == InventoryType.RangedRight))
attType = WeaponAttackType.RangedAttack;
else if (slot == EquipmentSlot.OffHand)
attType = WeaponAttackType.OffAttack;
float minDamage, maxDamage;
item.GetDamage(this, out minDamage, out maxDamage);
if (minDamage > 0)
{
damage = apply ? minDamage : SharedConst.BaseMinDamage;
SetBaseWeaponDamage(attType, WeaponDamageRange.MinDamage, damage);
}
if (maxDamage > 0)
{
damage = apply ? maxDamage : SharedConst.BaseMaxDamage;
SetBaseWeaponDamage(attType, WeaponDamageRange.MaxDamage, damage);
}
SpellShapeshiftFormRecord shapeshift = CliDB.SpellShapeshiftFormStorage.LookupByKey(GetShapeshiftForm());
if (proto.GetDelay() != 0 && !(shapeshift != null && shapeshift.CombatRoundTime != 0))
SetBaseAttackTime(attType, apply ? proto.GetDelay() : SharedConst.BaseAttackTime);
if (CanModifyStats() && (damage != 0 || proto.GetDelay() != 0))
UpdateDamagePhysical(attType);
}
public void SetCanParry(bool value)
{
if (m_canParry == value)
return;
m_canParry = value;
UpdateParryPercentage();
}
public void SetCanBlock(bool value)
{
if (m_canBlock == value)
return;
m_canBlock = value;
UpdateBlockPercentage();
}
// duel health and mana reset methods
public void SaveHealthBeforeDuel() { healthBeforeDuel = (uint)GetHealth(); }
public void SaveManaBeforeDuel() { manaBeforeDuel = (uint)GetPower(PowerType.Mana); }
public void RestoreHealthAfterDuel() { SetHealth(healthBeforeDuel); }
public void RestoreManaAfterDuel() { SetPower(PowerType.Mana, (int)manaBeforeDuel); }
void UpdateDuelFlag(long currTime)
{
if (duel == null || duel.startTimer == 0 || currTime < duel.startTimer + 3)
return;
Global.ScriptMgr.OnPlayerDuelStart(this, duel.opponent);
SetUInt32Value(PlayerFields.DuelTeam, 1);
duel.opponent.SetUInt32Value(PlayerFields.DuelTeam, 2);
duel.startTimer = 0;
duel.startTime = currTime;
duel.opponent.duel.startTimer = 0;
duel.opponent.duel.startTime = currTime;
}
void CheckDuelDistance(long currTime)
{
if (duel == null)
return;
ObjectGuid duelFlagGUID = GetGuidValue(PlayerFields.DuelArbiter);
GameObject obj = GetMap().GetGameObject(duelFlagGUID);
if (!obj)
return;
if (duel.outOfBound == 0)
{
if (!IsWithinDistInMap(obj, 50))
{
duel.outOfBound = currTime;
SendPacket(new DuelOutOfBounds());
}
}
else
{
if (IsWithinDistInMap(obj, 40))
{
duel.outOfBound = 0;
SendPacket(new DuelInBounds());
}
else if (currTime >= (duel.outOfBound + 10))
DuelComplete(DuelCompleteType.Fled);
}
}
public void DuelComplete(DuelCompleteType type)
{
// duel not requested
if (duel == null)
return;
// Check if DuelComplete() has been called already up in the stack and in that case don't do anything else here
if (duel.isCompleted || duel.opponent.duel.isCompleted)
return;
duel.isCompleted = true;
duel.opponent.duel.isCompleted = true;
Log.outDebug(LogFilter.Player, "Duel Complete {0} {1}", GetName(), duel.opponent.GetName());
DuelComplete duelCompleted = new DuelComplete();
duelCompleted.Started = type != DuelCompleteType.Interrupted;
SendPacket(duelCompleted);
if (duel.opponent.GetSession() != null)
duel.opponent.SendPacket(duelCompleted);
if (type != DuelCompleteType.Interrupted)
{
DuelWinner duelWinner = new DuelWinner();
duelWinner.BeatenName = (type == DuelCompleteType.Won ? duel.opponent.GetName() : GetName());
duelWinner.WinnerName = (type == DuelCompleteType.Won ? GetName() : duel.opponent.GetName());
duelWinner.BeatenVirtualRealmAddress = Global.WorldMgr.GetVirtualRealmAddress();
duelWinner.WinnerVirtualRealmAddress = Global.WorldMgr.GetVirtualRealmAddress();
duelWinner.Fled = type != DuelCompleteType.Won;
SendMessageToSet(duelWinner, true);
}
Global.ScriptMgr.OnPlayerDuelEnd(duel.opponent, this, type);
switch (type)
{
case DuelCompleteType.Fled:
// if initiator and opponent are on the same team
// or initiator and opponent are not PvP enabled, forcibly stop attacking
if (duel.initiator.GetTeam() == duel.opponent.GetTeam())
{
duel.initiator.AttackStop();
duel.opponent.AttackStop();
}
else
{
if (!duel.initiator.IsPvP())
duel.initiator.AttackStop();
if (!duel.opponent.IsPvP())
duel.opponent.AttackStop();
}
break;
case DuelCompleteType.Won:
UpdateCriteria(CriteriaTypes.LoseDuel, 1);
duel.opponent.UpdateCriteria(CriteriaTypes.WinDuel, 1);
// Credit for quest Death's Challenge
if (GetClass() == Class.Deathknight && duel.opponent.GetQuestStatus(12733) == QuestStatus.Incomplete)
duel.opponent.CastSpell(duel.opponent, 52994, true);
// Honor points after duel (the winner) - ImpConfig
int amount = WorldConfig.GetIntValue(WorldCfg.HonorAfterDuel);
if (amount != 0)
duel.opponent.RewardHonor(null, 1, amount);
break;
default:
break;
}
// Victory emote spell
if (type != DuelCompleteType.Interrupted)
duel.opponent.CastSpell(duel.opponent, 52852, true);
//Remove Duel Flag object
GameObject obj = GetMap().GetGameObject(GetGuidValue(PlayerFields.DuelArbiter));
if (obj)
duel.initiator.RemoveGameObject(obj, true);
//remove auras
var itsAuras = duel.opponent.GetAppliedAuras();
foreach (var pair in itsAuras)
{
Aura aura = pair.Value.GetBase();
if (!pair.Value.IsPositive() && aura.GetCasterGUID() == GetGUID() && aura.GetApplyTime() >= duel.startTime)
duel.opponent.RemoveAura(pair);
}
var myAuras = GetAppliedAuras();
foreach (var pair in myAuras)
{
Aura aura = pair.Value.GetBase();
if (!pair.Value.IsPositive() && aura.GetCasterGUID() == duel.opponent.GetGUID() && aura.GetApplyTime() >= duel.startTime)
RemoveAura(pair);
}
// cleanup combo points
ClearComboPoints();
duel.opponent.ClearComboPoints();
//cleanups
SetGuidValue(PlayerFields.DuelArbiter, ObjectGuid.Empty);
SetUInt32Value(PlayerFields.DuelTeam, 0);
duel.opponent.SetGuidValue(PlayerFields.DuelArbiter, ObjectGuid.Empty);
duel.opponent.SetUInt32Value(PlayerFields.DuelTeam, 0);
duel.opponent.duel = null;
duel = null;
}
//PVP
public void SetPvPDeath(bool on)
{
if (on)
m_ExtraFlags |= PlayerExtraFlags.PVPDeath;
else
m_ExtraFlags &= ~PlayerExtraFlags.PVPDeath;
}
public void SetContestedPvPTimer(uint newTime) { m_contestedPvPTimer = newTime; }
public void ResetContestedPvP()
{
ClearUnitState(UnitState.AttackPlayer);
RemoveFlag(PlayerFields.Flags, PlayerFlags.ContestedPVP);
m_contestedPvPTimer = 0;
}
void UpdateAfkReport(long currTime)
{
if (m_bgData.bgAfkReportedTimer <= currTime)
{
m_bgData.bgAfkReportedCount = 0;
m_bgData.bgAfkReportedTimer = currTime + 5 * Time.Minute;
}
}
public void UpdateContestedPvP(uint diff)
{
if (m_contestedPvPTimer == 0 || IsInCombat())
return;
if (m_contestedPvPTimer <= diff)
{
ResetContestedPvP();
}
else
m_contestedPvPTimer -= diff;
}
public void UpdatePvPFlag(long currTime)
{
if (!IsPvP())
return;
if (pvpInfo.EndTimer == 0 || currTime < (pvpInfo.EndTimer + 300) || pvpInfo.IsHostile)
return;
UpdatePvP(false);
}
public void UpdatePvP(bool state, bool Override = false)
{
if (!state || Override)
{
SetPvP(state);
pvpInfo.EndTimer = 0;
}
else
{
pvpInfo.EndTimer = Time.UnixTime;
SetPvP(state);
}
}
public void UpdatePvPState(bool onlyFFA = false)
{
// @todo should we always synchronize UNIT_FIELD_BYTES_2, 1 of controller and controlled?
// no, we shouldn't, those are checked for affecting player by client
if (!pvpInfo.IsInNoPvPArea && !IsGameMaster()
&& (pvpInfo.IsInFFAPvPArea || Global.WorldMgr.IsFFAPvPRealm()))
{
if (!IsFFAPvP())
{
SetByteFlag(UnitFields.Bytes2, UnitBytes2Offsets.PvpFlag, UnitBytes2Flags.FFAPvp);
foreach (var unit in m_Controlled)
unit.SetByteValue(UnitFields.Bytes2, UnitBytes2Offsets.PvpFlag, (byte)UnitBytes2Flags.FFAPvp);
}
}
else if (IsFFAPvP())
{
RemoveByteFlag(UnitFields.Bytes2, UnitBytes2Offsets.PvpFlag, UnitBytes2Flags.FFAPvp);
foreach (var unit in m_Controlled)
unit.RemoveByteFlag(UnitFields.Bytes2, UnitBytes2Offsets.PvpFlag, UnitBytes2Flags.FFAPvp);
}
if (onlyFFA)
return;
if (pvpInfo.IsHostile) // in hostile area
{
if (!IsPvP() || pvpInfo.EndTimer != 0)
UpdatePvP(true, true);
}
else // in friendly area
{
if (IsPvP() && !HasFlag(PlayerFields.Flags, PlayerFlags.InPVP) && pvpInfo.EndTimer == 0)
pvpInfo.EndTimer = Time.UnixTime; // start toggle-off
}
}
public override void SetPvP(bool state)
{
base.SetPvP(state);
foreach (var unit in m_Controlled)
unit.SetPvP(state);
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,613 @@
/*
* Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Framework.Constants;
using Game.Achievements;
using Game.Chat;
using Game.DataStorage;
using Game.Garrisons;
using Game.Groups;
using Game.Mails;
using Game.Maps;
using Game.Misc;
using Game.Spells;
using System.Collections;
using System.Collections.Generic;
namespace Game.Entities
{
public partial class Player
{
public WorldSession GetSession() { return Session; }
public PlayerSocial GetSocial() { return m_social; }
//Gossip
public PlayerMenu PlayerTalkClass;
PlayerSocial m_social;
List<Channel> m_channels = new List<Channel>();
List<ObjectGuid> WhisperList = new List<ObjectGuid>();
public string autoReplyMsg;
//Inventory
Dictionary<ulong, EquipmentSetInfo> _equipmentSets = new Dictionary<ulong, EquipmentSetInfo>();
public List<ItemSetEffect> ItemSetEff = new List<ItemSetEffect>();
List<EnchantDuration> m_enchantDuration = new List<EnchantDuration>();
List<Item> m_itemDuration = new List<Item>();
List<ObjectGuid> m_itemSoulboundTradeable = new List<ObjectGuid>();
List<ObjectGuid> m_refundableItems = new List<ObjectGuid>();
public List<Item> ItemUpdateQueue = new List<Item>();
VoidStorageItem[] _voidStorageItems = new VoidStorageItem[SharedConst.VoidStorageMaxSlot];
Item[] m_items = new Item[(int)PlayerSlots.Count];
uint m_WeaponProficiency;
uint m_ArmorProficiency;
uint m_currentBuybackSlot;
TradeData m_trade;
//PVP
BgBattlegroundQueueID_Rec[] m_bgBattlegroundQueueID = new BgBattlegroundQueueID_Rec[SharedConst.MaxPlayerBGQueues];
BGData m_bgData;
bool m_IsBGRandomWinner;
public PvPInfo pvpInfo;
uint m_ArenaTeamIdInvited;
long m_lastHonorUpdateTime;
uint m_contestedPvPTimer;
//Groups/Raids
GroupReference m_group = new GroupReference();
GroupReference m_originalGroup = new GroupReference();
Group m_groupInvite;
GroupUpdateFlags m_groupUpdateMask;
bool m_bPassOnGroupLoot;
GroupUpdateCounter[] m_groupUpdateSequences = new GroupUpdateCounter[2];
public Dictionary<uint, InstanceBind>[] m_boundInstances = new Dictionary<uint, InstanceBind>[(int)Difficulty.Max];
Dictionary<uint, long> _instanceResetTimes = new Dictionary<uint, long>();
uint _pendingBindId;
uint _pendingBindTimer;
public bool m_InstanceValid;
Difficulty m_dungeonDifficulty;
Difficulty m_raidDifficulty;
Difficulty m_legacyRaidDifficulty;
Difficulty m_prevMapDifficulty;
//Movement
public PlayerTaxi m_taxi = new PlayerTaxi();
public byte[] m_forced_speed_changes = new byte[(int)UnitMoveType.Max];
uint m_lastFallTime;
float m_lastFallZ;
WorldLocation teleportDest;
TeleportToOptions m_teleport_options;
bool mSemaphoreTeleport_Near;
bool mSemaphoreTeleport_Far;
PlayerDelayedOperations m_DelayedOperations;
bool m_bCanDelayTeleport;
bool m_bHasDelayedTeleport;
PlayerUnderwaterState m_MirrorTimerFlags;
PlayerUnderwaterState m_MirrorTimerFlagsLast;
bool m_isInWater;
//Stats
uint m_baseSpellPower;
uint m_baseManaRegen;
uint m_baseHealthRegen;
int m_spellPenetrationItemMod;
uint m_lastPotionId;
//Spell
Dictionary<uint, PlayerSpell> m_spells = new Dictionary<uint, PlayerSpell>();
Dictionary<uint, SkillStatusData> mSkillStatus = new Dictionary<uint, SkillStatusData>();
Dictionary<uint, PlayerCurrency> _currencyStorage = new Dictionary<uint, PlayerCurrency>();
List<SpellModifier>[][] m_spellMods = new List<SpellModifier>[(int)SpellModOp.Max][];
MultiMap<uint, uint> m_overrideSpells = new MultiMap<uint, uint>();
public Spell m_spellModTakingSpell;
uint m_oldpetspell;
// Rune type / Rune timer
uint[] m_runeGraceCooldown = new uint[PlayerConst.MaxRunes];
uint[] m_lastRuneGraceTimers = new uint[PlayerConst.MaxRunes];
//Mail
List<Mail> m_mail = new List<Mail>();
Dictionary<ulong, Item> mMitems = new Dictionary<ulong, Item>();
public byte unReadMails;
long m_nextMailDelivereTime;
public bool m_mailsLoaded;
public bool m_mailsUpdated;
//Pets
public uint m_stableSlots;
uint m_temporaryUnsummonedPetNumber;
uint m_lastpetnumber;
// Player summoning
long m_summon_expire;
WorldLocation m_summon_location;
RestMgr _restMgr;
//Combat
int[] baseRatingValue = new int[(int)CombatRating.Max];
public float[][] m_auraBaseMod = new float[(int)BaseModGroup.End][];
public DuelInfo duel;
bool m_canParry;
bool m_canBlock;
bool m_canTitanGrip;
uint m_titanGripPenaltySpellId;
uint m_deathTimer;
long m_deathExpireTime;
byte m_swingErrorMsg;
uint m_combatExitTime;
uint m_regenTimerCount;
uint m_weaponChangeTimer;
//Quest
List<uint> m_timedquests = new List<uint>();
List<uint> m_weeklyquests = new List<uint>();
List<uint> m_monthlyquests = new List<uint>();
MultiMap<uint, uint> m_seasonalquests = new MultiMap<uint, uint>();
Dictionary<uint, QuestStatusData> m_QuestStatus = new Dictionary<uint, QuestStatusData>();
Dictionary<uint, QuestSaveType> m_QuestStatusSave = new Dictionary<uint, QuestSaveType>();
List<uint> m_DFQuests = new List<uint>();
List<uint> m_RewardedQuests = new List<uint>();
Dictionary<uint, QuestSaveType> m_RewardedQuestsSave = new Dictionary<uint, QuestSaveType>();
bool m_DailyQuestChanged;
bool m_WeeklyQuestChanged;
bool m_MonthlyQuestChanged;
bool m_SeasonalQuestChanged;
long m_lastDailyQuestTime;
Garrison _garrison;
CinematicManager _cinematicMgr;
// variables to save health and mana before duel and restore them after duel
ulong healthBeforeDuel;
uint manaBeforeDuel;
bool _advancedCombatLoggingEnabled;
WorldLocation _corpseLocation;
//Core
WorldSession Session;
uint m_nextSave;
byte m_cinematic;
uint m_movie;
SpecializationInfo _specializationInfo;
public List<ObjectGuid> m_clientGUIDs = new List<ObjectGuid>();
public WorldObject seerView;
// only changed for direct client control (possess, vehicle etc.), not stuff you control using pet commands
public Unit m_unitMovedByMe;
Team m_team;
public Stack<uint> m_timeSyncQueue = new Stack<uint>();
uint m_timeSyncTimer;
public uint m_timeSyncClient;
public uint m_timeSyncServer;
ReputationMgr reputationMgr;
QuestObjectiveCriteriaManager m_questObjectiveCriteriaMgr;
public AtLoginFlags atLoginFlags;
public bool m_itemUpdateQueueBlocked;
PlayerExtraFlags m_ExtraFlags;
public bool isDebugAreaTriggers { get; set; }
uint m_zoneUpdateId;
uint m_areaUpdateId;
uint m_zoneUpdateTimer;
uint m_ChampioningFaction;
byte m_grantableLevels;
byte m_fishingSteps;
// Recall position
WorldLocation m_recall_location;
WorldLocation homebind;
uint homebindAreaId;
uint m_HomebindTimer;
ResurrectionData _resurrectionData;
PlayerAchievementMgr m_achievementSys;
SceneMgr m_sceneMgr;
Dictionary<ObjectGuid /*LootObject*/, ObjectGuid /*WorldObject*/> m_AELootView = new Dictionary<ObjectGuid, ObjectGuid>();
CUFProfile[] _CUFProfiles = new CUFProfile[PlayerConst.MaxCUFProfiles];
float[] m_powerFraction = new float[(int)PowerType.MaxPerClass];
int[] m_MirrorTimer = new int[3];
ulong m_GuildIdInvited;
DeclinedName _declinedname;
Runes m_runes = new Runes();
uint m_drunkTimer;
long m_logintime;
long m_Last_tick;
uint m_PlayedTimeTotal;
uint m_PlayedTimeLevel;
Dictionary<byte, ActionButton> m_actionButtons = new Dictionary<byte, ActionButton>();
ObjectGuid m_divider;
uint m_ingametime;
PlayerCommandStates _activeCheats;
}
public class PlayerInfo
{
public uint MapId;
public uint ZoneId;
public float PositionX;
public float PositionY;
public float PositionZ;
public float Orientation;
public uint DisplayId_m;
public uint DisplayId_f;
public List<PlayerCreateInfoItem> item = new List<PlayerCreateInfoItem>();
public List<uint> customSpells = new List<uint>();
public List<uint> castSpells = new List<uint>();
public List<PlayerCreateInfoAction> action = new List<PlayerCreateInfoAction>();
public List<SkillRaceClassInfoRecord> skills = new List<SkillRaceClassInfoRecord>();
public PlayerLevelInfo[] levelInfo = new PlayerLevelInfo[WorldConfig.GetIntValue(WorldCfg.MaxPlayerLevel)];
}
public class PlayerCreateInfoItem
{
public PlayerCreateInfoItem(uint id, uint amount)
{
item_id = id;
item_amount = amount;
}
public uint item_id;
public uint item_amount;
}
public class PlayerCreateInfoAction
{
public PlayerCreateInfoAction() : this(0, 0, 0) { }
public PlayerCreateInfoAction(byte _button, uint _action, byte _type)
{
button = _button;
type = _type;
action = _action;
}
public byte button;
public byte type;
public uint action;
}
public class PlayerLevelInfo
{
public ushort[] stats = new ushort[(int)Stats.Max];
}
public class PlayerCurrency
{
public PlayerCurrencyState state;
public uint Quantity;
public uint WeeklyQuantity;
public uint TrackedQuantity;
public byte Flags;
}
struct PlayerDynamicFieldSpellModByLabel
{
public uint Mod;
public float Value;
public uint Label;
}
public class SpecializationInfo
{
public SpecializationInfo()
{
for (byte i = 0; i < PlayerConst.MaxSpecializations; ++i)
{
Talents[i] = new Dictionary<uint, PlayerSpellState>();
Glyphs[i] = new List<uint>();
}
}
public Dictionary<uint, PlayerSpellState>[] Talents = new Dictionary<uint, PlayerSpellState>[PlayerConst.MaxSpecializations];
public List<uint>[] Glyphs = new List<uint>[PlayerConst.MaxSpecializations];
public uint ResetTalentsCost;
public long ResetTalentsTime;
public uint PrimarySpecialization;
public byte ActiveGroup;
}
public class Runes
{
public void SetRuneState(byte index, bool set = true)
{
var id = CooldownOrder.LookupByIndex(index);
if (set)
{
RuneState |= (byte)(1 << index); // usable
if (id == 0)
CooldownOrder.Add(index);
}
else
{
RuneState &= (byte)~(1 << index); // on cooldown
if (id != 0)
CooldownOrder.Remove(id);
}
}
public List<byte> CooldownOrder = new List<byte>();
public uint[] Cooldown = new uint[PlayerConst.MaxRunes];
public byte RuneState; // mask of available runes
}
public class ActionButton
{
public ActionButton()
{
packedData = 0;
uState = ActionButtonUpdateState.New;
}
public ActionButtonType GetButtonType() { return (ActionButtonType)((packedData & 0xFFFFFFFF00000000) >> 56); }
public uint GetAction() { return (uint)(packedData & 0x00000000FFFFFFFF); }
public void SetActionAndType(ulong action, ActionButtonType type)
{
ulong newData = action | ((ulong)type << 56);
if (newData != packedData || uState == ActionButtonUpdateState.Deleted)
{
packedData = newData;
if (uState != ActionButtonUpdateState.New)
uState = ActionButtonUpdateState.Changed;
}
}
public ulong packedData;
public ActionButtonUpdateState uState;
}
public class ResurrectionData
{
public ObjectGuid GUID;
public WorldLocation Location = new WorldLocation();
public uint Health;
public uint Mana;
public uint Aura;
}
public struct PvPInfo
{
public bool IsHostile;
public bool IsInHostileArea; //> Marks if player is in an area which forces PvP flag
public bool IsInNoPvPArea; //> Marks if player is in a sanctuary or friendly capital city
public bool IsInFFAPvPArea; //> Marks if player is in an FFAPvP area (such as Gurubashi Arena)
public long EndTimer; //> Time when player unflags himself for PvP (flag removed after 5 minutes)
}
public class DuelInfo
{
public Player initiator;
public Player opponent;
public long startTimer;
public long startTime;
public long outOfBound;
public bool isMounted;
public bool isCompleted;
public bool IsDueling() { return opponent != null; }
}
public class AccessRequirement
{
public byte levelMin;
public byte levelMax;
public uint item;
public uint item2;
public uint quest_A;
public uint quest_H;
public uint achievement;
public string questFailedText;
}
public class EnchantDuration
{
public EnchantDuration(Item _item = null, EnchantmentSlot _slot = EnchantmentSlot.Max, uint _leftduration = 0)
{
item = _item;
slot = _slot;
leftduration = _leftduration;
}
public Item item;
public EnchantmentSlot slot;
public uint leftduration;
}
public class VoidStorageItem
{
public VoidStorageItem(ulong id, uint entry, ObjectGuid creator, ItemRandomEnchantmentId randomPropertyId, uint suffixFactor, uint upgradeId, uint fixedScalingLevel, uint artifactKnowledgeLevel, byte context, ICollection<uint> bonuses)
{
ItemId = id;
ItemEntry = entry;
CreatorGuid = creator;
ItemRandomPropertyId = randomPropertyId;
ItemSuffixFactor = suffixFactor;
ItemUpgradeId = upgradeId;
FixedScalingLevel = fixedScalingLevel;
ArtifactKnowledgeLevel = artifactKnowledgeLevel;
Context = context;
foreach (var value in bonuses)
BonusListIDs.Add(value);
}
public ulong ItemId;
public uint ItemEntry;
public ObjectGuid CreatorGuid;
public ItemRandomEnchantmentId ItemRandomPropertyId;
public uint ItemSuffixFactor;
public uint ItemUpgradeId;
public uint FixedScalingLevel;
public uint ArtifactKnowledgeLevel;
public byte Context;
public List<uint> BonusListIDs = new List<uint>();
}
public class EquipmentSetInfo
{
public EquipmentSetInfo()
{
state = EquipmentSetUpdateState.New;
Data = new EquipmentSetData();
}
public EquipmentSetUpdateState state;
public EquipmentSetData Data;
// Data sent in EquipmentSet related packets
public class EquipmentSetData
{
public EquipmentSetType Type;
public ulong Guid; // Set Identifier
public uint SetID; // Index
public uint IgnoreMask ; // Mask of EquipmentSlot
public int AssignedSpecIndex = -1; // Index of character specialization that this set is automatically equipped for
public string SetName = "";
public string SetIcon = "";
public Array<ObjectGuid> Pieces = new Array<ObjectGuid>(EquipmentSlot.End);
public Array<int> Appearances = new Array<int>(EquipmentSlot.End); // ItemModifiedAppearanceID
public Array<int> Enchants = new Array<int>(2); // SpellItemEnchantmentID
}
public enum EquipmentSetType
{
Equipment = 0,
Transmog = 1
}
}
public class BgBattlegroundQueueID_Rec
{
public BattlegroundQueueTypeId bgQueueTypeId;
public uint invitedToInstance;
public uint joinTime;
}
// Holder for Battlegrounddata
public class BGData
{
public BGData()
{
bgTypeID = BattlegroundTypeId.None;
ClearTaxiPath();
joinPos = new WorldLocation();
}
public uint bgInstanceID; //< This variable is set to bg.m_InstanceID,
// when player is teleported to BG - (it is Battleground's GUID)
public BattlegroundTypeId bgTypeID;
public List<ObjectGuid> bgAfkReporter = new List<ObjectGuid>();
public byte bgAfkReportedCount;
public long bgAfkReportedTimer;
public uint bgTeam; //< What side the player will be added to
public uint mountSpell;
public uint[] taxiPath = new uint[2];
public WorldLocation joinPos; //< From where player entered BG
public void ClearTaxiPath() { taxiPath[0] = taxiPath[1] = 0; }
public bool HasTaxiPath() { return taxiPath[0] != 0 && taxiPath[1] != 0; }
}
public class CUFProfile
{
public CUFProfile()
{
BoolOptions = new BitArray((int)CUFBoolOptions.BoolOptionsCount);
}
public CUFProfile(string name, ushort frameHeight, ushort frameWidth, byte sortBy, byte healthText, uint boolOptions,
byte topPoint, byte bottomPoint, byte leftPoint, ushort topOffset, ushort bottomOffset, ushort leftOffset)
{
ProfileName = name;
BoolOptions = new BitArray(new int[] { (int)boolOptions });
FrameHeight = frameHeight;
FrameWidth = frameWidth;
SortBy = sortBy;
HealthText = healthText;
TopPoint = topPoint;
BottomPoint = bottomPoint;
LeftPoint = leftPoint;
TopOffset = topOffset;
BottomOffset = bottomOffset;
LeftOffset = leftOffset;
}
public void SetOption(CUFBoolOptions opt, byte arg)
{
BoolOptions.Set((int)opt, arg != 0);
}
public bool GetOption(CUFBoolOptions opt)
{
return BoolOptions.Get((int)opt);
}
public ulong GetUlongOptionValue()
{
int[] array = new int[1];
BoolOptions.CopyTo(array, 0);
return (ulong)array[0];
}
public string ProfileName;
public ushort FrameHeight;
public ushort FrameWidth;
public byte SortBy;
public byte HealthText;
// LeftAlign, TopAlight, BottomAlign
public byte TopPoint;
public byte BottomPoint;
public byte LeftPoint;
// LeftOffset, TopOffset and BottomOffset
public ushort TopOffset;
public ushort BottomOffset;
public ushort LeftOffset;
public BitArray BoolOptions;
// More fields can be added to BoolOptions without changing DB schema (up to 32, currently 27)
}
struct GroupUpdateCounter
{
public ObjectGuid GroupGuid;
public int UpdateSequenceNumber;
}
}
@@ -0,0 +1,290 @@
/*
* Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Framework.Constants;
using Game.Groups;
using Game.Maps;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
namespace Game.Entities
{
public partial class Player
{
Player GetNextRandomRaidMember(float radius)
{
Group group = GetGroup();
if (!group)
return null;
List<Player> nearMembers = new List<Player>();
for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.next())
{
Player Target = refe.GetSource();
// IsHostileTo check duel and controlled by enemy
if (Target && Target != this && IsWithinDistInMap(Target, radius) &&
!Target.HasInvisibilityAura() && !IsHostileTo(Target))
nearMembers.Add(Target);
}
if (nearMembers.Empty())
return null;
int randTarget = RandomHelper.IRand(0, nearMembers.Count - 1);
return nearMembers[randTarget];
}
public PartyResult CanUninviteFromGroup(ObjectGuid guidMember = default(ObjectGuid))
{
Group grp = GetGroup();
if (!grp)
return PartyResult.NotInGroup;
if (grp.isLFGGroup())
{
ObjectGuid gguid = grp.GetGUID();
if (Global.LFGMgr.GetKicksLeft(gguid) == 0)
return PartyResult.PartyLfgBootLimit;
LfgState state = Global.LFGMgr.GetState(gguid);
if (Global.LFGMgr.IsVoteKickActive(gguid))
return PartyResult.PartyLfgBootInProgress;
if (grp.GetMembersCount() <= SharedConst.LFGKickVotesNeeded)
return PartyResult.PartyLfgBootTooFewPlayers;
if (state == LfgState.FinishedDungeon)
return PartyResult.PartyLfgBootDungeonComplete;
if (grp.isRollLootActive())
return PartyResult.PartyLfgBootLootRolls;
// @todo Should also be sent when anyone has recently left combat, with an aprox ~5 seconds timer.
for (GroupReference refe = grp.GetFirstMember(); refe != null; refe = refe.next())
if (refe.GetSource() && refe.GetSource().IsInCombat())
return PartyResult.PartyLfgBootInCombat;
/* Missing support for these types
return ERR_PARTY_LFG_BOOT_COOLDOWN_S;
return ERR_PARTY_LFG_BOOT_NOT_ELIGIBLE_S;
*/
}
else
{
if (!grp.IsLeader(GetGUID()) && !grp.IsAssistant(GetGUID()))
return PartyResult.NotLeader;
if (InBattleground())
return PartyResult.InviteRestricted;
if (grp.IsLeader(guidMember))
return PartyResult.NotLeader;
}
return PartyResult.Ok;
}
public bool isUsingLfg()
{
return Global.LFGMgr.GetState(GetGUID()) != LfgState.None;
}
bool inRandomLfgDungeon()
{
if (Global.LFGMgr.selectedRandomLfgDungeon(GetGUID()))
{
Map map = GetMap();
return Global.LFGMgr.inLfgDungeonMap(GetGUID(), map.GetId(), map.GetDifficultyID());
}
return false;
}
public void SetBattlegroundOrBattlefieldRaid(Group group, byte subgroup)
{
//we must move references from m_group to m_originalGroup
SetOriginalGroup(GetGroup(), GetSubGroup());
m_group.unlink();
m_group.link(group, this);
m_group.setSubGroup(subgroup);
}
public void RemoveFromBattlegroundOrBattlefieldRaid()
{
//remove existing reference
m_group.unlink();
Group group = GetOriginalGroup();
if (group)
{
m_group.link(group, this);
m_group.setSubGroup(GetOriginalSubGroup());
}
SetOriginalGroup(null);
}
public void SetOriginalGroup(Group group, byte subgroup = 0)
{
if (!group)
m_originalGroup.unlink();
else
{
m_originalGroup.link(group, this);
m_originalGroup.setSubGroup(subgroup);
}
}
public void SetGroup(Group group, byte subgroup = 0)
{
if (!group)
m_group.unlink();
else
{
m_group.link(group, this);
m_group.setSubGroup(subgroup);
}
UpdateObjectVisibility(false);
}
public void SetPartyType(GroupCategory category, GroupType type)
{
Contract.Assert(category < GroupCategory.Max);
byte value = GetByteValue(PlayerFields.Bytes3, PlayerFieldOffsets.Bytes3OffsetPartyType);
value &= (byte)~((byte)0xFF << ((byte)category * 4));
value |= (byte)((byte)type << ((byte)category * 4));
SetByteValue(PlayerFields.Bytes3, PlayerFieldOffsets.Bytes3OffsetPartyType, value);
}
public void ResetGroupUpdateSequenceIfNeeded(Group group)
{
GroupCategory category = group.GetGroupCategory();
// Rejoining the last group should not reset the sequence
if (m_groupUpdateSequences[(int)category].GroupGuid != group.GetGUID())
{
var groupUpdate = m_groupUpdateSequences[(int)category];
groupUpdate.GroupGuid = group.GetGUID();
groupUpdate.UpdateSequenceNumber = 1;
}
}
public int NextGroupUpdateSequenceNumber(GroupCategory category)
{
var groupUpdate = m_groupUpdateSequences[(int)category];
return groupUpdate.UpdateSequenceNumber++;
}
public bool IsAtGroupRewardDistance(WorldObject pRewardSource)
{
if (!pRewardSource)
return false;
WorldObject player = GetCorpse();
if (!player || IsAlive())
player = this;
if (player.GetMapId() != pRewardSource.GetMapId() || player.GetInstanceId() != pRewardSource.GetInstanceId())
return false;
if (player.GetMap().IsDungeon())
return true;
return pRewardSource.GetDistance(player) <= WorldConfig.GetFloatValue(WorldCfg.GroupXpDistance);
}
public Group GetGroupInvite() { return m_groupInvite; }
public void SetGroupInvite(Group group) { m_groupInvite = group; }
public Group GetGroup() { return m_group.getTarget(); }
public GroupReference GetGroupRef() { return m_group; }
public byte GetSubGroup() { return m_group.getSubGroup(); }
public GroupUpdateFlags GetGroupUpdateFlag() { return m_groupUpdateMask; }
public void SetGroupUpdateFlag(GroupUpdateFlags flag) { m_groupUpdateMask |= flag; }
public void RemoveGroupUpdateFlag(GroupUpdateFlags flag) { m_groupUpdateMask &= ~flag; }
public Group GetOriginalGroup() { return m_originalGroup.getTarget(); }
public GroupReference GetOriginalGroupRef() { return m_originalGroup; }
public byte GetOriginalSubGroup() { return m_originalGroup.getSubGroup(); }
public void SetPassOnGroupLoot(bool bPassOnGroupLoot) { m_bPassOnGroupLoot = bPassOnGroupLoot; }
public bool GetPassOnGroupLoot() { return m_bPassOnGroupLoot; }
public bool IsGroupVisibleFor(Player p)
{
switch (WorldConfig.GetIntValue(WorldCfg.GroupVisibility))
{
default:
return IsInSameGroupWith(p);
case 1:
return IsInSameRaidWith(p);
case 2:
return GetTeam() == p.GetTeam();
}
}
public bool IsInSameGroupWith(Player p)
{
return p == this || (GetGroup() &&
GetGroup() == p.GetGroup() && GetGroup().SameSubGroup(this, p));
}
public bool IsInSameRaidWith(Player p)
{
return p == this || (GetGroup() != null && GetGroup() == p.GetGroup());
}
public void UninviteFromGroup()
{
Group group = GetGroupInvite();
if (!group)
return;
group.RemoveInvite(this);
if (group.GetMembersCount() <= 1) // group has just 1 member => disband
{
if (group.IsCreated())
group.Disband(true);
else
group.RemoveAllInvites();
}
}
public void RemoveFromGroup(RemoveMethod method = RemoveMethod.Default) { RemoveFromGroup(GetGroup(), GetGUID(), method); }
public static void RemoveFromGroup(Group group, ObjectGuid guid, RemoveMethod method = RemoveMethod.Default, ObjectGuid kicker = default(ObjectGuid), string reason = null)
{
if (!group)
return;
group.RemoveMember(guid, method, kicker, reason);
}
void SendUpdateToOutOfRangeGroupMembers()
{
if (m_groupUpdateMask == GroupUpdateFlags.None)
return;
Group group = GetGroup();
if (group)
group.UpdatePlayerOutOfRange(this);
m_groupUpdateMask = GroupUpdateFlags.None;
Pet pet = GetPet();
if (pet)
pet.ResetGroupUpdateFlag();
}
}
}
File diff suppressed because it is too large Load Diff
+789
View File
@@ -0,0 +1,789 @@
/*
* Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Framework.Constants;
using Framework.Database;
using Game.DataStorage;
using Game.Groups;
using Game.Guilds;
using Game.Maps;
using Game.Network.Packets;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Game.Entities
{
public partial class Player
{
public override void SetMap(Map map)
{
base.SetMap(map);
}
public Difficulty GetDifficultyID(MapRecord mapEntry)
{
if (!mapEntry.IsRaid())
return m_dungeonDifficulty;
MapDifficultyRecord defaultDifficulty = Global.DB2Mgr.GetDefaultMapDifficulty(mapEntry.Id);
if (defaultDifficulty == null)
return m_legacyRaidDifficulty;
DifficultyRecord difficulty = CliDB.DifficultyStorage.LookupByKey(defaultDifficulty.DifficultyID);
if (difficulty == null || difficulty.Flags.HasAnyFlag(DifficultyFlags.Legacy))
return m_legacyRaidDifficulty;
return m_raidDifficulty;
}
public Difficulty GetDungeonDifficultyID() { return m_dungeonDifficulty; }
public Difficulty GetRaidDifficultyID() { return m_raidDifficulty; }
public Difficulty GetLegacyRaidDifficultyID() { return m_legacyRaidDifficulty; }
public void SetDungeonDifficultyID(Difficulty dungeon_difficulty) { m_dungeonDifficulty = dungeon_difficulty; }
public void SetRaidDifficultyID(Difficulty raid_difficulty) { m_raidDifficulty = raid_difficulty; }
public void SetLegacyRaidDifficultyID(Difficulty raid_difficulty) { m_legacyRaidDifficulty = raid_difficulty; }
public static Difficulty CheckLoadedDungeonDifficultyID(Difficulty difficulty)
{
DifficultyRecord difficultyEntry = CliDB.DifficultyStorage.LookupByKey(difficulty);
if (difficultyEntry == null)
return Difficulty.Normal;
if (difficultyEntry.InstanceType != MapTypes.Instance)
return Difficulty.Normal;
if (!difficultyEntry.Flags.HasAnyFlag(DifficultyFlags.CanSelect))
return Difficulty.Normal;
return difficulty;
}
public static Difficulty CheckLoadedRaidDifficultyID(Difficulty difficulty)
{
DifficultyRecord difficultyEntry = CliDB.DifficultyStorage.LookupByKey(difficulty);
if (difficultyEntry == null)
return Difficulty.NormalRaid;
if (difficultyEntry.InstanceType != MapTypes.Raid)
return Difficulty.NormalRaid;
if (!difficultyEntry.Flags.HasAnyFlag(DifficultyFlags.CanSelect) || difficultyEntry.Flags.HasAnyFlag(DifficultyFlags.Legacy))
return Difficulty.NormalRaid;
return difficulty;
}
public static Difficulty CheckLoadedLegacyRaidDifficultyID(Difficulty difficulty)
{
DifficultyRecord difficultyEntry = CliDB.DifficultyStorage.LookupByKey(difficulty);
if (difficultyEntry == null)
return Difficulty.Raid10N;
if (difficultyEntry.InstanceType != MapTypes.Raid)
return Difficulty.Raid10N;
if (!difficultyEntry.Flags.HasAnyFlag(DifficultyFlags.CanSelect) || !difficultyEntry.Flags.HasAnyFlag(DifficultyFlags.Legacy))
return Difficulty.Raid10N;
return difficulty;
}
public void SendRaidGroupOnlyMessage(RaidGroupReason reason, int delay)
{
RaidGroupOnly raidGroupOnly = new RaidGroupOnly();
raidGroupOnly.Delay = delay;
raidGroupOnly.Reason = reason;
SendPacket(raidGroupOnly);
}
void UpdateArea(uint newArea)
{
// FFA_PVP flags are area and not zone id dependent
// so apply them accordingly
m_areaUpdateId = newArea;
AreaTableRecord area = CliDB.AreaTableStorage.LookupByKey(newArea);
pvpInfo.IsInFFAPvPArea = area != null && area.Flags[0].HasAnyFlag(AreaFlags.Arena);
UpdatePvPState(true);
UpdateAreaDependentAuras(newArea);
UpdateAreaAndZonePhase();
// previously this was in UpdateZone (but after UpdateArea) so nothing will break
pvpInfo.IsInNoPvPArea = false;
if (area != null && area.IsSanctuary()) // in sanctuary
{
SetByteFlag(UnitFields.Bytes2, UnitBytes2Offsets.PvpFlag, UnitBytes2Flags.Sanctuary);
pvpInfo.IsInNoPvPArea = true;
if (duel == null)
CombatStopWithPets();
}
else
RemoveByteFlag(UnitFields.Bytes2, UnitBytes2Offsets.PvpFlag, UnitBytes2Flags.Sanctuary);
AreaFlags areaRestFlag = (GetTeam() == Team.Alliance) ? AreaFlags.RestZoneAlliance : AreaFlags.RestZoneHorde;
if (area != null && area.Flags[0].HasAnyFlag(areaRestFlag))
_restMgr.SetRestFlag(RestFlag.FactionArea);
else
_restMgr.RemoveRestFlag(RestFlag.FactionArea);
}
public void UpdateZone(uint newZone, uint newArea)
{
if (m_zoneUpdateId != newZone)
{
Global.OutdoorPvPMgr.HandlePlayerLeaveZone(this, m_zoneUpdateId);
Global.OutdoorPvPMgr.HandlePlayerEnterZone(this, newZone);
Global.BattleFieldMgr.HandlePlayerLeaveZone(this, m_zoneUpdateId);
Global.BattleFieldMgr.HandlePlayerEnterZone(this, newZone);
SendInitWorldStates(newZone, newArea); // only if really enters to new zone, not just area change, works strange...
Guild guild = GetGuild();
if (guild)
guild.UpdateMemberData(this, GuildMemberData.ZoneId, newZone);
}
// group update
if (GetGroup())
{
SetGroupUpdateFlag(GroupUpdateFlags.Full);
Pet pet = GetPet();
if (pet)
pet.SetGroupUpdateFlag(GroupUpdatePetFlags.Full);
}
m_zoneUpdateId = newZone;
m_zoneUpdateTimer = 1 * Time.InMilliseconds;
// zone changed, so area changed as well, update it
UpdateArea(newArea);
AreaTableRecord zone = CliDB.AreaTableStorage.LookupByKey(newZone);
if (zone == null)
return;
if (WorldConfig.GetBoolValue(WorldCfg.Weather))
GetMap().GetOrGenerateZoneDefaultWeather(newZone);
GetMap().SendZoneDynamicInfo(newZone, this);
Global.ScriptMgr.OnPlayerUpdateZone(this, newZone, newArea);
// in PvP, any not controlled zone (except zone.team == 6, default case)
// in PvE, only opposition team capital
switch ((AreaTeams)zone.FactionGroupMask)
{
case AreaTeams.Ally:
pvpInfo.IsInHostileArea = GetTeam() != Team.Alliance && (Global.WorldMgr.IsPvPRealm() || zone.Flags[0].HasAnyFlag(AreaFlags.Capital));
break;
case AreaTeams.Horde:
pvpInfo.IsInHostileArea = GetTeam() != Team.Horde && (Global.WorldMgr.IsPvPRealm() || zone.Flags[0].HasAnyFlag(AreaFlags.Capital));
break;
case AreaTeams.None:
// overwrite for Battlegrounds, maybe batter some zone flags but current known not 100% fit to this
pvpInfo.IsInHostileArea = Global.WorldMgr.IsPvPRealm() || InBattleground() || zone.Flags[0].HasAnyFlag(AreaFlags.Wintergrasp);
break;
default: // 6 in fact
pvpInfo.IsInHostileArea = false;
break;
}
// Treat players having a quest flagging for PvP as always in hostile area
pvpInfo.IsHostile = pvpInfo.IsInHostileArea || HasPvPForcingQuest();
if (zone.Flags[0].HasAnyFlag(AreaFlags.Capital)) // Is in a capital city
{
if (!pvpInfo.IsInHostileArea || zone.IsSanctuary())
_restMgr.SetRestFlag(RestFlag.City);
pvpInfo.IsInNoPvPArea = true;
}
else
_restMgr.RemoveRestFlag(RestFlag.City);
UpdatePvPState();
// remove items with area/map limitations (delete only for alive player to allow back in ghost mode)
// if player resurrected at teleport this will be applied in resurrect code
if (IsAlive())
DestroyZoneLimitedItem(true, newZone);
// check some item equip limitations (in result lost CanTitanGrip at talent reset, for example)
AutoUnequipOffhandIfNeed();
// recent client version not send leave/join channel packets for built-in local channels
UpdateLocalChannels(newZone);
UpdateZoneDependentAuras(newZone);
UpdateAreaAndZonePhase();
}
public InstanceBind GetBoundInstance(uint mapid, Difficulty difficulty, bool withExpired = false)
{
// some instances only have one difficulty
MapDifficultyRecord mapDiff = Global.DB2Mgr.GetDownscaledMapDifficultyData(mapid, ref difficulty);
if (mapDiff == null)
return null;
var bind = m_boundInstances[(int)difficulty].LookupByKey(mapid);
if (bind != null)
if (bind.extendState != 0 || withExpired)
return bind;
return null;
}
public Dictionary<uint, InstanceBind> GetBoundInstances(Difficulty difficulty) { return m_boundInstances[(int)difficulty]; }
public InstanceSave GetInstanceSave(uint mapid)
{
MapRecord mapEntry = CliDB.MapStorage.LookupByKey(mapid);
InstanceBind pBind = GetBoundInstance(mapid, GetDifficultyID(mapEntry));
InstanceSave pSave = pBind?.save;
if (pBind == null || !pBind.perm)
{
Group group = GetGroup();
if (group)
{
InstanceBind groupBind = group.GetBoundInstance(GetDifficultyID(mapEntry), mapid);
if (groupBind != null)
pSave = groupBind.save;
}
}
return pSave;
}
public void UnbindInstance(uint mapid, Difficulty difficulty, bool unload = false)
{
var bound = m_boundInstances[(int)difficulty].LookupByKey(mapid);
if (bound != null)
{
if (!unload)
{
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_INSTANCE_BY_INSTANCE_GUID);
stmt.AddValue(0, GetGUID().GetCounter());
stmt.AddValue(1, bound.save.GetInstanceId());
DB.Characters.Execute(stmt);
}
if (bound.perm)
GetSession().SendCalendarRaidLockout(bound.save, false);
bound.save.RemovePlayer(this); // save can become invalid
m_boundInstances[(int)difficulty].Remove(mapid);
}
}
public void UnbindInstance(KeyValuePair<uint, InstanceBind> pair, Difficulty difficulty, bool unload)
{
if (pair.Value != null)
{
if (!unload)
{
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_INSTANCE_BY_INSTANCE_GUID);
stmt.AddValue(0, GetGUID().GetCounter());
stmt.AddValue(1, pair.Value.save.GetInstanceId());
DB.Characters.Execute(stmt);
}
if (pair.Value.perm)
GetSession().SendCalendarRaidLockout(pair.Value.save, false);
pair.Value.save.RemovePlayer(this); // save can become invalid
m_boundInstances[(int)difficulty].Remove(pair.Key);
}
}
public InstanceBind BindToInstance(InstanceSave save, bool permanent, BindExtensionState extendState = BindExtensionState.Normal, bool load = false)
{
if (save != null)
{
InstanceBind bind = new InstanceBind();
if (m_boundInstances[(int)save.GetDifficultyID()].ContainsKey(save.GetMapId()))
bind = m_boundInstances[(int)save.GetDifficultyID()][save.GetMapId()];
if (extendState == BindExtensionState.Keep) // special flag, keep the player's current extend state when updating for new boss down
{
if (save == bind.save)
extendState = bind.extendState;
else
extendState = BindExtensionState.Normal;
}
if (!load)
{
PreparedStatement stmt;
if (bind.save != null)
{
// update the save when the group kills a boss
if (permanent != bind.perm || save != bind.save || extendState != bind.extendState)
{
stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHAR_INSTANCE);
stmt.AddValue(0, save.GetInstanceId());
stmt.AddValue(1, permanent);
stmt.AddValue(2, extendState);
stmt.AddValue(3, GetGUID().GetCounter());
stmt.AddValue(4, bind.save.GetInstanceId());
DB.Characters.Execute(stmt);
}
}
else
{
stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_CHAR_INSTANCE);
stmt.AddValue(0, GetGUID().GetCounter());
stmt.AddValue(1, save.GetInstanceId());
stmt.AddValue(2, permanent);
stmt.AddValue(3, extendState);
DB.Characters.Execute(stmt);
}
}
if (bind.save != save)
{
if (bind.save != null)
bind.save.RemovePlayer(this);
save.AddPlayer(this);
}
if (permanent)
save.SetCanReset(false);
bind.save = save;
bind.perm = permanent;
bind.extendState = extendState;
if (!load)
Log.outDebug(LogFilter.Maps, "Player.BindToInstance: Player '{0}' ({1}) is now bound to map (ID: {2}, Instance {3}, Difficulty {4})", GetName(), GetGUID().ToString(), save.GetMapId(), save.GetInstanceId(), save.GetDifficultyID());
Global.ScriptMgr.OnPlayerBindToInstance(this, save.GetDifficultyID(), save.GetMapId(), permanent, extendState);
m_boundInstances[(int)save.GetDifficultyID()][save.GetMapId()] = bind;
return bind;
}
return null;
}
public void BindToInstance()
{
InstanceSave mapSave = Global.InstanceSaveMgr.GetInstanceSave(_pendingBindId);
if (mapSave == null) //it seems sometimes mapSave is NULL, but I did not check why
return;
InstanceSaveCreated data = new InstanceSaveCreated();
data.Gm = IsGameMaster();
SendPacket(data);
if (!IsGameMaster())
{
BindToInstance(mapSave, true, BindExtensionState.Keep);
GetSession().SendCalendarRaidLockout(mapSave, true);
}
}
public void SetPendingBind(uint instanceId, uint bindTimer)
{
_pendingBindId = instanceId;
_pendingBindTimer = bindTimer;
}
public void SendRaidInfo()
{
InstanceInfoPkt instanceInfo = new InstanceInfoPkt();
long now = Time.UnixTime;
for (byte i = 0; i < (int)Difficulty.Max; ++i)
{
foreach (var pair in m_boundInstances[i])
{
InstanceBind bind = pair.Value;
if (bind.perm)
{
InstanceSave save = pair.Value.save;
InstanceLockInfos lockInfos;
lockInfos.InstanceID = save.GetInstanceId();
lockInfos.MapID = save.GetMapId();
lockInfos.DifficultyID = (uint)save.GetDifficultyID();
if (bind.extendState != BindExtensionState.Extended)
lockInfos.TimeRemaining = (int)(save.GetResetTime() - now);
else
lockInfos.TimeRemaining = (int)(Global.InstanceSaveMgr.GetSubsequentResetTime(save.GetMapId(), save.GetDifficultyID(), save.GetResetTime()) - now);
lockInfos.CompletedMask = 0;
Map map = Global.MapMgr.FindMap(save.GetMapId(), save.GetInstanceId());
if (map != null)
{
InstanceScript instanceScript = ((InstanceMap)map).GetInstanceScript();
if (instanceScript != null)
lockInfos.CompletedMask = instanceScript.GetCompletedEncounterMask();
}
lockInfos.Locked = bind.extendState != BindExtensionState.Expired;
lockInfos.Extended = bind.extendState == BindExtensionState.Extended;
instanceInfo.LockList.Add(lockInfos);
}
}
}
SendPacket(instanceInfo);
}
public bool Satisfy(AccessRequirement ar, uint target_map, bool report = false)
{
if (!IsGameMaster() && ar != null)
{
byte LevelMin = 0;
byte LevelMax = 0;
MapRecord mapEntry = CliDB.MapStorage.LookupByKey(target_map);
if (mapEntry == null)
return false;
if (!WorldConfig.GetBoolValue(WorldCfg.InstanceIgnoreLevel))
{
if (ar.levelMin != 0 && getLevel() < ar.levelMin)
LevelMin = ar.levelMin;
if (ar.levelMax != 0 && getLevel() > ar.levelMax)
LevelMax = ar.levelMax;
}
uint missingItem = 0;
if (ar.item != 0)
{
if (!HasItemCount(ar.item) &&
(ar.item2 == 0 || !HasItemCount(ar.item2)))
missingItem = ar.item;
}
else if (ar.item2 != 0 && !HasItemCount(ar.item2))
missingItem = ar.item2;
if (Global.DisableMgr.IsDisabledFor(DisableType.Map, target_map, this))
{
GetSession().SendNotification("{0}", Global.ObjectMgr.GetCypherString(CypherStrings.InstanceClosed));
return false;
}
uint missingQuest = 0;
if (GetTeam() == Team.Alliance && ar.quest_A != 0 && !GetQuestRewardStatus(ar.quest_A))
missingQuest = ar.quest_A;
else if (GetTeam() == Team.Horde && ar.quest_H != 0 && !GetQuestRewardStatus(ar.quest_H))
missingQuest = ar.quest_H;
uint missingAchievement = 0;
Player leader = this;
ObjectGuid leaderGuid = GetGroup() != null ? GetGroup().GetLeaderGUID() : GetGUID();
if (leaderGuid != GetGUID())
leader = Global.ObjAccessor.FindPlayer(leaderGuid);
if (ar.achievement != 0)
if (leader == null || !leader.HasAchieved(ar.achievement))
missingAchievement = ar.achievement;
Difficulty target_difficulty = GetDifficultyID(mapEntry);
MapDifficultyRecord mapDiff = Global.DB2Mgr.GetDownscaledMapDifficultyData(target_map, ref target_difficulty);
if (LevelMin != 0 || LevelMax != 0 || missingItem != 0 || missingQuest != 0 || missingAchievement != 0)
{
if (report)
{
if (missingQuest != 0 && !string.IsNullOrEmpty(ar.questFailedText))
SendSysMessage("{0}", ar.questFailedText);
else if (mapDiff.Message_lang.HasString(Global.WorldMgr.GetDefaultDbcLocale())) // if (missingAchievement) covered by this case
SendTransferAborted(target_map, TransferAbortReason.Difficulty, (byte)target_difficulty);
else if (missingItem != 0)
GetSession().SendNotification(Global.ObjectMgr.GetCypherString(CypherStrings.LevelMinrequiredAndItem), LevelMin, Global.ObjectMgr.GetItemTemplate(missingItem).GetName());
else if (LevelMin != 0)
GetSession().SendNotification(Global.ObjectMgr.GetCypherString(CypherStrings.LevelMinrequired), LevelMin);
}
return false;
}
}
return true;
}
bool IsInstanceLoginGameMasterException()
{
if (!CanBeGameMaster())
return false;
SendSysMessage(CypherStrings.InstanceLoginGamemasterException);
return true;
}
public bool CheckInstanceValidity(bool isLogin)
{
// game masters' instances are always valid
if (IsGameMaster())
return true;
// non-instances are always valid
Map map = GetMap();
if (!map || !map.IsDungeon())
return true;
// raid instances require the player to be in a raid group to be valid
if (map.IsRaid() && !WorldConfig.GetBoolValue(WorldCfg.InstanceIgnoreRaid))
if (!GetGroup() || !GetGroup().isRaidGroup())
return false;
Group group = GetGroup();
if (group)
{
// check if player's group is bound to this instance
InstanceBind bind = group.GetBoundInstance(map.GetDifficultyID(), map.GetId());
if (bind == null || bind.save == null || bind.save.GetInstanceId() != map.GetInstanceId())
return false;
var players = map.GetPlayers();
if (!players.Empty())
foreach (var otherPlayer in players)
{
if (otherPlayer.IsGameMaster())
continue;
if (!otherPlayer.m_InstanceValid) // ignore players that currently have a homebind timer active
continue;
if (group != otherPlayer.GetGroup())
return false;
}
}
else
{
// instance is invalid if we are not grouped and there are other players
if (map.GetPlayersCountExceptGMs() > 1)
return false;
// check if the player is bound to this instance
InstanceBind bind = GetBoundInstance(map.GetId(), map.GetDifficultyID());
if (bind == null || bind.save == null || bind.save.GetInstanceId() != map.GetInstanceId())
return false;
}
return true;
}
public bool CheckInstanceCount(uint instanceId)
{
if (_instanceResetTimes.Count < WorldConfig.GetIntValue(WorldCfg.MaxInstancesPerHour))
return true;
return _instanceResetTimes.ContainsKey(instanceId);
}
public void AddInstanceEnterTime(uint instanceId, long enterTime)
{
if (!_instanceResetTimes.ContainsKey(instanceId))
_instanceResetTimes.Add(instanceId, enterTime + Time.Hour);
}
public void SendDungeonDifficulty(int forcedDifficulty = -1)
{
DungeonDifficultySet dungeonDifficultySet = new DungeonDifficultySet();
dungeonDifficultySet.DifficultyID = forcedDifficulty == -1 ? (int)GetDungeonDifficultyID() : forcedDifficulty;
SendPacket(dungeonDifficultySet);
}
public void SendRaidDifficulty(bool legacy, int forcedDifficulty = -1)
{
RaidDifficultySet raidDifficultySet = new RaidDifficultySet();
raidDifficultySet.DifficultyID = forcedDifficulty == -1 ? (int)(legacy ? GetLegacyRaidDifficultyID() : GetRaidDifficultyID()) : forcedDifficulty;
raidDifficultySet.Legacy = legacy;
SendPacket(raidDifficultySet);
}
public void SendResetFailedNotify(uint mapid)
{
SendPacket(new ResetFailedNotify());
}
// Reset all solo instances and optionally send a message on success for each
public void ResetInstances(InstanceResetMethod method, bool isRaid, bool isLegacy)
{
// method can be INSTANCE_RESET_ALL, INSTANCE_RESET_CHANGE_DIFFICULTY, INSTANCE_RESET_GROUP_JOIN
// we assume that when the difficulty changes, all instances that can be reset will be
Difficulty diff = GetDungeonDifficultyID();
if (isRaid)
{
if (!isLegacy)
diff = GetRaidDifficultyID();
else
diff = GetLegacyRaidDifficultyID();
}
foreach (var pair in m_boundInstances[(int)diff].ToList())
{
InstanceSave p = pair.Value.save;
MapRecord entry = CliDB.MapStorage.LookupByKey(pair.Key);
if (entry == null || entry.IsRaid() != isRaid || !p.CanReset())
continue;
if (method == InstanceResetMethod.All)
{
// the "reset all instances" method can only reset normal maps
if (entry.InstanceType == MapTypes.Raid || diff == Difficulty.Heroic)
continue;
}
// if the map is loaded, reset it
Map map = Global.MapMgr.FindMap(p.GetMapId(), p.GetInstanceId());
if (map != null && map.IsDungeon())
if (!map.ToInstanceMap().Reset(method))
continue;
// since this is a solo instance there should not be any players inside
if (method == InstanceResetMethod.All || method == InstanceResetMethod.ChangeDifficulty)
SendResetInstanceSuccess(p.GetMapId());
p.DeleteFromDB();
m_boundInstances[(int)diff].Remove(pair.Key);
// the following should remove the instance save from the manager and delete it as well
p.RemovePlayer(this);
}
}
public void SendResetInstanceSuccess(uint MapId)
{
InstanceReset data = new InstanceReset();
data.MapID = MapId;
SendPacket(data);
}
public void SendResetInstanceFailed(ResetFailedReason reason, uint MapId)
{
InstanceResetFailed data = new InstanceResetFailed();
data.MapID = MapId;
data.ResetFailedReason = reason;
SendPacket(data);
}
public void SendTransferAborted(uint mapid, TransferAbortReason reason, byte arg = 0)
{
TransferAborted transferAborted = new TransferAborted();
transferAborted.MapID = mapid;
transferAborted.Arg = arg;
transferAborted.TransfertAbort = reason;
SendPacket(transferAborted);
}
public void SendInstanceResetWarning(uint mapid, Difficulty difficulty, uint time, bool welcome)
{
// type of warning, based on the time remaining until reset
InstanceResetWarningType type;
if (welcome)
type = InstanceResetWarningType.Welcome;
else if (time > 21600)
type = InstanceResetWarningType.Welcome;
else if (time > 3600)
type = InstanceResetWarningType.WarningHours;
else if (time > 300)
type = InstanceResetWarningType.WarningMin;
else
type = InstanceResetWarningType.WarningMinSoon;
RaidInstanceMessage raidInstanceMessage = new RaidInstanceMessage();
raidInstanceMessage.Type = type;
raidInstanceMessage.MapID = mapid;
raidInstanceMessage.DifficultyID = difficulty;
InstanceBind bind = GetBoundInstance(mapid, difficulty);
if (bind != null)
raidInstanceMessage.Locked = bind.perm;
else
raidInstanceMessage.Locked = false;
raidInstanceMessage.Extended = false;
SendPacket(raidInstanceMessage);
}
public override void UpdateUnderwaterState(Map m, float x, float y, float z)
{
LiquidData liquid_status;
ZLiquidStatus res = m.getLiquidStatus(x, y, z, MapConst.MapAllLiquidTypes, out liquid_status);
if (res == 0)
{
m_MirrorTimerFlags &= ~(PlayerUnderwaterState.InWater | PlayerUnderwaterState.InLava | PlayerUnderwaterState.InSlime | PlayerUnderwaterState.InDarkWater);
if (_lastLiquid != null && _lastLiquid.SpellID != 0)
RemoveAurasDueToSpell(_lastLiquid.SpellID);
_lastLiquid = null;
return;
}
uint liqEntry = liquid_status.entry;
if (liqEntry != 0)
{
LiquidTypeRecord liquid = CliDB.LiquidTypeStorage.LookupByKey(liqEntry);
if (_lastLiquid != null && _lastLiquid.SpellID != 0 && _lastLiquid != liquid)
RemoveAurasDueToSpell(_lastLiquid.SpellID);
if (liquid != null && liquid.SpellID != 0)
{
if (res.HasAnyFlag(ZLiquidStatus.UnderWater | ZLiquidStatus.InWater))
{
if (!HasAura(liquid.SpellID))
CastSpell(this, liquid.SpellID, true);
}
else
RemoveAurasDueToSpell(liquid.SpellID);
}
_lastLiquid = liquid;
}
else if (_lastLiquid != null && _lastLiquid.SpellID != 0)
{
RemoveAurasDueToSpell(_lastLiquid.SpellID);
_lastLiquid = null;
}
// All liquids type - check under water position
if (liquid_status.type_flags.HasAnyFlag<uint>(MapConst.MapLiquidTypeWater | MapConst.MapLiquidTypeOcean | MapConst.MapLiquidTypeMagma | MapConst.MapLiquidTypeSlime))
{
if (res.HasAnyFlag(ZLiquidStatus.UnderWater))
m_MirrorTimerFlags |= PlayerUnderwaterState.InWater;
else
m_MirrorTimerFlags &= ~PlayerUnderwaterState.InWater;
}
// Allow travel in dark water on taxi or transport
if (liquid_status.type_flags.HasAnyFlag<uint>(MapConst.MapLiquidTypeDarkWater) && !IsInFlight() && GetTransport() == null)
m_MirrorTimerFlags |= PlayerUnderwaterState.InDarkWater;
else
m_MirrorTimerFlags &= ~PlayerUnderwaterState.InDarkWater;
// in lava check, anywhere in lava level
if (liquid_status.type_flags.HasAnyFlag<uint>(MapConst.MapLiquidTypeMagma))
{
if (res.HasAnyFlag(ZLiquidStatus.UnderWater | ZLiquidStatus.InWater | ZLiquidStatus.WaterWalk))
m_MirrorTimerFlags |= PlayerUnderwaterState.InLava;
else
m_MirrorTimerFlags &= ~PlayerUnderwaterState.InLava;
}
// in slime check, anywhere in slime level
if (liquid_status.type_flags.HasAnyFlag<uint>(MapConst.MapLiquidTypeSlime))
{
if (res.HasAnyFlag(ZLiquidStatus.UnderWater | ZLiquidStatus.InWater | ZLiquidStatus.WaterWalk))
m_MirrorTimerFlags |= PlayerUnderwaterState.InSlime;
else
m_MirrorTimerFlags &= ~PlayerUnderwaterState.InSlime;
}
}
}
}
+753
View File
@@ -0,0 +1,753 @@
/*
* Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Framework.Constants;
using Framework.Database;
using Game.Arenas;
using Game.BattleFields;
using Game.BattleGrounds;
using Game.DataStorage;
using Game.Network.Packets;
using Game.PvP;
using System;
using System.Collections.Generic;
namespace Game.Entities
{
public partial class Player
{
//PvP
public void UpdateHonorFields()
{
// called when rewarding honor and at each save
long now = Time.UnixTime;
long today = (Time.UnixTime / Time.Day) * Time.Day;
if (m_lastHonorUpdateTime < today)
{
long yesterday = today - Time.Day;
// update yesterday's contribution
if (m_lastHonorUpdateTime >= yesterday)
{
// this is the first update today, reset today's contribution
ushort killsToday = GetUInt16Value(PlayerFields.Kills, 0);
SetUInt16Value(PlayerFields.Kills, 0, 0);
SetUInt16Value(PlayerFields.Kills, 1, killsToday);
}
else
{
// no honor/kills yesterday or today, reset
SetUInt32Value(PlayerFields.Kills, 0);
}
}
m_lastHonorUpdateTime = now;
}
public bool RewardHonor(Unit victim, uint groupsize, int honor = -1, bool pvptoken = false)
{
// do not reward honor in arenas, but enable onkill spellproc
if (InArena())
{
if (!victim || victim == this || !victim.IsTypeId(TypeId.Player))
return false;
if (GetBGTeam() == victim.ToPlayer().GetBGTeam())
return false;
return true;
}
// 'Inactive' this aura prevents the player from gaining honor points and BattlegroundTokenizer
if (HasAura(BattlegroundConst.SpellAuraPlayerInactive))
return false;
ObjectGuid victim_guid = ObjectGuid.Empty;
uint victim_rank = 0;
// need call before fields update to have chance move yesterday data to appropriate fields before today data change.
UpdateHonorFields();
// do not reward honor in arenas, but return true to enable onkill spellproc
if (InBattleground() && GetBattleground() && GetBattleground().isArena())
return true;
// Promote to float for calculations
float honor_f = honor;
if (honor_f <= 0)
{
if (!victim || victim == this || victim.HasAuraType(AuraType.NoPvpCredit))
return false;
victim_guid = victim.GetGUID();
Player plrVictim = victim.ToPlayer();
if (plrVictim)
{
if (GetTeam() == plrVictim.GetTeam() && !Global.WorldMgr.IsFFAPvPRealm())
return false;
byte k_level = (byte)getLevel();
byte k_grey = (byte)Formulas.GetGrayLevel(k_level);
byte v_level = (byte)victim.GetLevelForTarget(this);
if (v_level <= k_grey)
return false;
// PLAYER_CHOSEN_TITLE VALUES DESCRIPTION
// [0] Just name
// [1..14] Alliance honor titles and player name
// [15..28] Horde honor titles and player name
// [29..38] Other title and player name
// [39+] Nothing
uint victim_title = victim.GetUInt32Value(PlayerFields.ChosenTitle);
// Get Killer titles, CharTitlesEntry.bit_index
// Ranks:
// title[1..14] . rank[5..18]
// title[15..28] . rank[5..18]
// title[other] . 0
if (victim_title == 0)
victim_guid.Clear(); // Don't show HK: <rank> message, only log.
else if (victim_title < 15)
victim_rank = victim_title + 4;
else if (victim_title < 29)
victim_rank = victim_title - 14 + 4;
else
victim_guid.Clear(); // Don't show HK: <rank> message, only log.
honor_f = (float)Math.Ceiling(Formulas.hk_honor_at_level_f(k_level) * (v_level - k_grey) / (k_level - k_grey));
// count the number of playerkills in one day
ApplyModUInt16Value(PlayerFields.Kills, 0, 1, true);
// and those in a lifetime
ApplyModUInt32Value(PlayerFields.LifetimeHonorableKills, 1, true);
UpdateCriteria(CriteriaTypes.EarnHonorableKill);
UpdateCriteria(CriteriaTypes.HkClass, (uint)victim.GetClass());
UpdateCriteria(CriteriaTypes.HkRace, (uint)victim.GetRace());
UpdateCriteria(CriteriaTypes.HonorableKillAtArea, GetAreaId());
UpdateCriteria(CriteriaTypes.HonorableKill, 1, 0, 0, victim);
}
else
{
if (!victim.ToCreature().IsRacialLeader())
return false;
honor_f = 100.0f; // ??? need more info
victim_rank = 19; // HK: Leader
}
}
if (victim != null)
{
if (groupsize > 1)
honor_f /= groupsize;
// apply honor multiplier from aura (not stacking-get highest)
MathFunctions.AddPct(ref honor_f, GetMaxPositiveAuraModifier(AuraType.ModHonorGainPct));
honor_f += _restMgr.GetRestBonusFor(RestTypes.Honor, (uint)honor_f);
}
honor_f *= WorldConfig.GetFloatValue(WorldCfg.RateHonor);
// Back to int now
honor = (int)honor_f;
// honor - for show honor points in log
// victim_guid - for show victim name in log
// victim_rank [1..4] HK: <dishonored rank>
// victim_rank [5..19] HK: <alliance\horde rank>
// victim_rank [0, 20+] HK: <>
PvPCredit data = new PvPCredit();
data.Honor = honor;
data.OriginalHonor = honor;
data.Target = victim_guid;
data.Rank = victim_rank;
SendPacket(data);
AddHonorXP((uint)honor);
if (InBattleground() && honor > 0)
{
Battleground bg = GetBattleground();
if (bg != null)
{
bg.UpdatePlayerScore(this, ScoreType.BonusHonor, (uint)honor, false); //false: prevent looping
}
}
if (WorldConfig.GetBoolValue(WorldCfg.PvpTokenEnable) && pvptoken)
{
if (!victim || victim == this || victim.HasAuraType(AuraType.NoPvpCredit))
return true;
if (victim.IsTypeId(TypeId.Player))
{
// Check if allowed to receive it in current map
int MapType = WorldConfig.GetIntValue(WorldCfg.PvpTokenMapType);
if ((MapType == 1 && !InBattleground() && !IsFFAPvP())
|| (MapType == 2 && !IsFFAPvP())
|| (MapType == 3 && !InBattleground()))
return true;
uint itemId = WorldConfig.GetUIntValue(WorldCfg.PvpTokenId);
uint count = WorldConfig.GetUIntValue(WorldCfg.PvpTokenCount);
if (AddItem(itemId, count))
SendSysMessage("You have been awarded a token for slaying another player.");
}
}
return true;
}
void _InitHonorLevelOnLoadFromDB(uint honor, uint honorLevel, uint prestigeLevel)
{
SetUInt32Value(PlayerFields.HonorLevel, honorLevel);
SetUInt32Value(PlayerFields.Prestige, prestigeLevel);
UpdateHonorNextLevel();
AddHonorXP(honor);
if (CanPrestige())
Prestige();
}
void RewardPlayerWithRewardPack(uint rewardPackID)
{
RewardPlayerWithRewardPack(CliDB.RewardPackStorage.LookupByKey(rewardPackID));
}
void RewardPlayerWithRewardPack(RewardPackRecord rewardPackEntry)
{
if (rewardPackEntry == null)
return;
CharTitlesRecord charTitlesEntry = CliDB.CharTitlesStorage.LookupByKey(rewardPackEntry.TitleID);
if (charTitlesEntry != null)
SetTitle(charTitlesEntry);
ModifyMoney(rewardPackEntry.Money);
var rewardPackXItems = Global.DB2Mgr.GetRewardPackItemsByRewardID(rewardPackEntry.Id);
if (rewardPackXItems != null)
{
foreach (RewardPackXItemRecord rewardPackXItem in rewardPackXItems)
AddItem(rewardPackXItem.ItemID, rewardPackXItem.Amount);
}
}
public void AddHonorXP(uint xp)
{
uint currentHonorXP = GetUInt32Value(PlayerFields.Honor);
uint nextHonorLevelXP = GetUInt32Value(PlayerFields.HonorNextLevel);
uint newHonorXP = currentHonorXP + xp;
uint honorLevel = GetHonorLevel();
if (xp < 1 || getLevel() < PlayerConst.LevelMinHonor || IsMaxHonorLevelAndPrestige())
return;
while (newHonorXP >= nextHonorLevelXP)
{
newHonorXP -= nextHonorLevelXP;
if (honorLevel < PlayerConst.MaxHonorLevel)
SetHonorLevel((byte)(honorLevel + 1));
honorLevel = GetHonorLevel();
nextHonorLevelXP = GetUInt32Value(PlayerFields.HonorNextLevel);
}
SetUInt32Value(PlayerFields.Honor, IsMaxHonorLevelAndPrestige() ? 0 : newHonorXP);
}
void SetHonorLevel(byte level)
{
byte oldHonorLevel = (byte)GetHonorLevel();
byte prestige = (byte)GetPrestigeLevel();
if (level == oldHonorLevel)
return;
uint rewardPackID = Global.DB2Mgr.GetRewardPackIDForPvpRewardByHonorLevelAndPrestige(level, prestige);
RewardPlayerWithRewardPack(rewardPackID);
SetUInt32Value(PlayerFields.HonorLevel, level);
UpdateHonorNextLevel();
UpdateCriteria(CriteriaTypes.HonorLevelReached);
// This code is here because no link was found between those items and this reward condition in the db2 files.
// Interesting CriteriaTree found: Tree ids: 51140, 51156 (criteria id 31773, modifier tree id 37759)
if (level == 50 && prestige == 1)
{
if (GetTeam() == Team.Alliance)
AddItem(138992, 1);
else
AddItem(138996, 1);
}
if (CanPrestige())
Prestige();
}
public void Prestige()
{
SetUInt32Value(PlayerFields.Prestige, GetPrestigeLevel() + 1);
SetUInt32Value(PlayerFields.HonorLevel, 1);
UpdateHonorNextLevel();
UpdateCriteria(CriteriaTypes.PrestigeReached);
}
public bool CanPrestige()
{
if (GetSession().GetExpansion() >= Expansion.Legion && getLevel() >= PlayerConst.LevelMinHonor && GetHonorLevel() >= PlayerConst.MaxHonorLevel && GetPrestigeLevel() < Global.DB2Mgr.GetMaxPrestige())
return true;
return false;
}
bool IsMaxPrestige()
{
return GetPrestigeLevel() == Global.DB2Mgr.GetMaxPrestige();
}
void UpdateHonorNextLevel()
{
uint prestige = Math.Min(16 - 1, GetPrestigeLevel());
SetUInt32Value(PlayerFields.HonorNextLevel, (uint)CliDB.HonorLevelGameTable.GetRow(GetHonorLevel()).Prestige[prestige]);
}
public uint GetPrestigeLevel() { return GetUInt32Value(PlayerFields.Prestige); }
public uint GetHonorLevel() { return GetUInt32Value(PlayerFields.HonorLevel); }
public bool IsMaxHonorLevelAndPrestige() { return IsMaxPrestige() && GetHonorLevel() == PlayerConst.MaxHonorLevel; }
//BGs
public Battleground GetBattleground()
{
if (GetBattlegroundId() == 0)
return null;
return Global.BattlegroundMgr.GetBattleground(GetBattlegroundId(), m_bgData.bgTypeID);
}
public bool InBattlegroundQueue()
{
for (byte i = 0; i < SharedConst.MaxPlayerBGQueues; ++i)
if (m_bgBattlegroundQueueID[i].bgQueueTypeId != BattlegroundQueueTypeId.None)
return true;
return false;
}
public BattlegroundQueueTypeId GetBattlegroundQueueTypeId(uint index)
{
if (index < SharedConst.MaxPlayerBGQueues)
return m_bgBattlegroundQueueID[index].bgQueueTypeId;
return BattlegroundQueueTypeId.None;
}
public uint GetBattlegroundQueueIndex(BattlegroundQueueTypeId bgQueueTypeId)
{
for (byte i = 0; i < SharedConst.MaxPlayerBGQueues; ++i)
if (m_bgBattlegroundQueueID[i].bgQueueTypeId == bgQueueTypeId)
return i;
return SharedConst.MaxPlayerBGQueues;
}
public bool IsInvitedForBattlegroundQueueType(BattlegroundQueueTypeId bgQueueTypeId)
{
for (byte i = 0; i < SharedConst.MaxPlayerBGQueues; ++i)
if (m_bgBattlegroundQueueID[i].bgQueueTypeId == bgQueueTypeId)
return m_bgBattlegroundQueueID[i].invitedToInstance != 0;
return false;
}
public bool InBattlegroundQueueForBattlegroundQueueType(BattlegroundQueueTypeId bgQueueTypeId)
{
return GetBattlegroundQueueIndex(bgQueueTypeId) < SharedConst.MaxPlayerBGQueues;
}
public void SetBattlegroundId(uint val, BattlegroundTypeId bgTypeId)
{
m_bgData.bgInstanceID = val;
m_bgData.bgTypeID = bgTypeId;
}
public uint AddBattlegroundQueueId(BattlegroundQueueTypeId val)
{
for (byte i = 0; i < SharedConst.MaxPlayerBGQueues; ++i)
{
if (m_bgBattlegroundQueueID[i].bgQueueTypeId == BattlegroundQueueTypeId.None || m_bgBattlegroundQueueID[i].bgQueueTypeId == val)
{
m_bgBattlegroundQueueID[i].bgQueueTypeId = val;
m_bgBattlegroundQueueID[i].invitedToInstance = 0;
m_bgBattlegroundQueueID[i].joinTime = Time.GetMSTime();
return i;
}
}
return SharedConst.MaxPlayerBGQueues;
}
public bool HasFreeBattlegroundQueueId()
{
for (byte i = 0; i < SharedConst.MaxPlayerBGQueues; ++i)
if (m_bgBattlegroundQueueID[i].bgQueueTypeId == BattlegroundQueueTypeId.None)
return true;
return false;
}
public void RemoveBattlegroundQueueId(BattlegroundQueueTypeId val)
{
for (byte i = 0; i < SharedConst.MaxPlayerBGQueues; ++i)
{
if (m_bgBattlegroundQueueID[i].bgQueueTypeId == val)
{
m_bgBattlegroundQueueID[i].bgQueueTypeId = BattlegroundQueueTypeId.None;
m_bgBattlegroundQueueID[i].invitedToInstance = 0;
m_bgBattlegroundQueueID[i].joinTime = 0;
return;
}
}
}
public void SetInviteForBattlegroundQueueType(BattlegroundQueueTypeId bgQueueTypeId, uint instanceId)
{
for (byte i = 0; i < SharedConst.MaxPlayerBGQueues; ++i)
if (m_bgBattlegroundQueueID[i].bgQueueTypeId == bgQueueTypeId)
m_bgBattlegroundQueueID[i].invitedToInstance = instanceId;
}
public bool IsInvitedForBattlegroundInstance(uint instanceId)
{
for (byte i = 0; i < SharedConst.MaxPlayerBGQueues; ++i)
if (m_bgBattlegroundQueueID[i].invitedToInstance == instanceId)
return true;
return false;
}
public WorldLocation GetBattlegroundEntryPoint() { return m_bgData.joinPos; }
public bool InBattleground() { return m_bgData.bgInstanceID != 0; }
public uint GetBattlegroundId() { return m_bgData.bgInstanceID; }
public BattlegroundTypeId GetBattlegroundTypeId() { return m_bgData.bgTypeID; }
public uint GetBattlegroundQueueJoinTime(BattlegroundQueueTypeId bgQueueTypeId)
{
for (byte i = 0; i < SharedConst.MaxPlayerBGQueues; ++i)
if (m_bgBattlegroundQueueID[i].bgQueueTypeId == bgQueueTypeId)
return m_bgBattlegroundQueueID[i].joinTime;
return 0;
}
public bool CanUseBattlegroundObject(GameObject gameobject)
{
// It is possible to call this method with a null pointer, only skipping faction check.
if (gameobject)
{
FactionTemplateRecord playerFaction = GetFactionTemplateEntry();
FactionTemplateRecord faction = CliDB.FactionTemplateStorage.LookupByKey(gameobject.GetUInt32Value(GameObjectFields.Faction));
if (playerFaction != null && faction != null && !playerFaction.IsFriendlyTo(faction))
return false;
}
// BUG: sometimes when player clicks on flag in AB - client won't send gameobject_use, only gameobject_report_use packet
// Note: Mount, stealth and invisibility will be removed when used
return (!isTotalImmune() && // Damage immune
!HasAura(BattlegroundConst.SpellRecentlyDroppedFlag) && // Still has recently held flag debuff
IsAlive()); // Alive
}
public bool CanCaptureTowerPoint()
{
return (!HasStealthAura() && // not stealthed
!HasInvisibilityAura() && // not invisible
IsAlive()); // live player
}
public void SetBattlegroundEntryPoint()
{
// Taxi path store
if (!m_taxi.empty())
{
m_bgData.mountSpell = 0;
m_bgData.taxiPath[0] = m_taxi.GetTaxiSource();
m_bgData.taxiPath[1] = m_taxi.GetTaxiDestination();
// On taxi we don't need check for dungeon
m_bgData.joinPos = new WorldLocation(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
}
else
{
m_bgData.ClearTaxiPath();
// Mount spell id storing
if (IsMounted())
{
var auras = GetAuraEffectsByType(AuraType.Mounted);
if (!auras.Empty())
m_bgData.mountSpell = auras[0].GetId();
}
else
m_bgData.mountSpell = 0;
// If map is dungeon find linked graveyard
if (GetMap().IsDungeon())
{
WorldSafeLocsRecord entry = Global.ObjectMgr.GetClosestGraveYard(GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId(), GetTeam());
if (entry != null)
m_bgData.joinPos = new WorldLocation(entry.MapID, entry.Loc.X, entry.Loc.Y, entry.Loc.Z, 0.0f);
else
Log.outError(LogFilter.Player, "SetBattlegroundEntryPoint: Dungeon map {0} has no linked graveyard, setting home location as entry point.", GetMapId());
}
// If new entry point is not BG or arena set it
else if (!GetMap().IsBattlegroundOrArena())
m_bgData.joinPos = new WorldLocation(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
}
if (m_bgData.joinPos.GetMapId() == 0xFFFFFFFF) // In error cases use homebind position
m_bgData.joinPos = new WorldLocation(GetHomebind());
}
public void SetBGTeam(Team team)
{
m_bgData.bgTeam = (uint)team;
SetByteValue(PlayerFields.Bytes4, PlayerFieldOffsets.Bytes4OffsetArenaFaction, (byte)(team == Team.Alliance ? 1 : 0));
}
public Team GetBGTeam()
{
return m_bgData.bgTeam != 0 ? (Team)m_bgData.bgTeam : GetTeam();
}
public void LeaveBattleground(bool teleportToEntryPoint = true)
{
Battleground bg = GetBattleground();
if (bg)
{
bg.RemovePlayerAtLeave(GetGUID(), teleportToEntryPoint, true);
// call after remove to be sure that player resurrected for correct cast
if (bg.isBattleground() && !IsGameMaster() && WorldConfig.GetBoolValue(WorldCfg.BattlegroundCastDeserter))
{
if (bg.GetStatus() == BattlegroundStatus.InProgress || bg.GetStatus() == BattlegroundStatus.WaitJoin)
{
//lets check if player was teleported from BG and schedule delayed Deserter spell cast
if (IsBeingTeleportedFar())
{
ScheduleDelayedOperation(PlayerDelayedOperations.SpellCastDeserter);
return;
}
CastSpell(this, 26013, true); // Deserter
}
}
}
}
public bool CanJoinToBattleground(Battleground bg)
{
// check Deserter debuff
if (HasAura(26013))
return false;
if (bg.isArena() && !GetSession().HasPermission(RBACPermissions.JoinArenas))
return false;
if (bg.IsRandom() && !GetSession().HasPermission(RBACPermissions.JoinRandomBg))
return false;
if (!GetSession().HasPermission(RBACPermissions.JoinNormalBg))
return false;
return true;
}
public void ClearAfkReports() { m_bgData.bgAfkReporter.Clear(); }
bool CanReportAfkDueToLimit()
{
// a player can complain about 15 people per 5 minutes
if (m_bgData.bgAfkReportedCount++ >= 15)
return false;
return true;
}
/// <summary>
/// This player has been blamed to be inactive in a Battleground
/// </summary>
/// <param name="reporter"></param>
public void ReportedAfkBy(Player reporter)
{
ReportPvPPlayerAFKResult reportAfkResult = new ReportPvPPlayerAFKResult();
reportAfkResult.Offender = GetGUID();
Battleground bg = GetBattleground();
// Battleground also must be in progress!
if (!bg || bg != reporter.GetBattleground() || GetTeam() != reporter.GetTeam() || bg.GetStatus() != BattlegroundStatus.InProgress)
{
reporter.SendPacket(reportAfkResult);
return;
}
// check if player has 'Idle' or 'Inactive' debuff
if (!m_bgData.bgAfkReporter.Contains(reporter.GetGUID()) && !HasAura(43680) && !HasAura(43681) && reporter.CanReportAfkDueToLimit())
{
m_bgData.bgAfkReporter.Add(reporter.GetGUID());
// by default 3 players have to complain to apply debuff
if (m_bgData.bgAfkReporter.Count >= WorldConfig.GetIntValue(WorldCfg.BattlegroundReportAfk))
{
// cast 'Idle' spell
CastSpell(this, 43680, true);
m_bgData.bgAfkReporter.Clear();
reportAfkResult.NumBlackMarksOnOffender = (byte)m_bgData.bgAfkReporter.Count;
reportAfkResult.NumPlayersIHaveReported = reporter.m_bgData.bgAfkReportedCount;
reportAfkResult.Result = ReportPvPPlayerAFKResult.ResultCode.Success;
}
}
reporter.SendPacket(reportAfkResult);
}
public bool GetRandomWinner() { return m_IsBGRandomWinner; }
public void SetRandomWinner(bool isWinner)
{
m_IsBGRandomWinner = isWinner;
if (m_IsBGRandomWinner)
{
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_BATTLEGROUND_RANDOM);
stmt.AddValue(0, GetGUID().GetCounter());
DB.Characters.Execute(stmt);
}
}
public bool GetBGAccessByLevel(BattlegroundTypeId bgTypeId)
{
// get a template bg instead of running one
Battleground bg = Global.BattlegroundMgr.GetBattlegroundTemplate(bgTypeId);
if (!bg)
return false;
// limit check leel to dbc compatible level range
uint level = getLevel();
if (level > WorldConfig.GetIntValue(WorldCfg.MaxPlayerLevel))
level = WorldConfig.GetUIntValue(WorldCfg.MaxPlayerLevel);
if (level < bg.GetMinLevel() || level > bg.GetMaxLevel())
return false;
return true;
}
void SendBGWeekendWorldStates()
{
foreach (var bl in CliDB.BattlemasterListStorage.Values)
{
if (bl.HolidayWorldState != 0)
{
if (Global.BattlegroundMgr.IsBGWeekend((BattlegroundTypeId)bl.Id))
SendUpdateWorldState(bl.HolidayWorldState, 1);
else
SendUpdateWorldState(bl.HolidayWorldState, 0);
}
}
}
public void SendPvpRewards()
{
//WorldPacket packet(SMSG_REQUEST_PVP_REWARDS_RESPONSE, 24);
//SendPacket(packet);
}
//Battlefields
void SendBattlefieldWorldStates()
{
// Send misc stuff that needs to be sent on every login, like the battle timers.
if (WorldConfig.GetBoolValue(WorldCfg.WintergraspEnable))
{
BattleField wg = Global.BattleFieldMgr.GetBattlefieldByBattleId(1);//Wintergrasp battle
if (wg != null)
{
SendUpdateWorldState(3801, (uint)(wg.IsWarTime() ? 0 : 1));
uint timer = wg.IsWarTime() ? 0 : (wg.GetTimer() / 1000); // 0 - Time to next battle
SendUpdateWorldState(4354, (uint)(Time.UnixTime + timer));
}
}
}
//Arenas
public void SetArenaTeamInfoField(byte slot, ArenaTeamInfoType type, uint value)
{
SetUInt32Value(PlayerFields.ArenaTeamInfo11 + (slot * (int)ArenaTeamInfoType.End) + (int)type, value);
}
public void SetInArenaTeam(uint ArenaTeamId, byte slot, byte type)
{
SetArenaTeamInfoField(slot, ArenaTeamInfoType.Id, ArenaTeamId);
SetArenaTeamInfoField(slot, ArenaTeamInfoType.Type, type);
}
public static uint GetArenaTeamIdFromDB(ObjectGuid guid, byte type)
{
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_ARENA_TEAM_ID_BY_PLAYER_GUID);
stmt.AddValue(0, guid.GetCounter());
stmt.AddValue(1, type);
SQLResult result = DB.Characters.Query(stmt);
if (result.IsEmpty())
return 0;
return result.Read<uint>(0);
}
public static void LeaveAllArenaTeams(ObjectGuid guid)
{
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_PLAYER_ARENA_TEAMS);
stmt.AddValue(0, guid.GetCounter());
SQLResult result = DB.Characters.Query(stmt);
if (result.IsEmpty())
return;
do
{
uint arenaTeamId = result.Read<uint>(0);
if (arenaTeamId != 0)
{
ArenaTeam arenaTeam = Global.ArenaTeamMgr.GetArenaTeamById(arenaTeamId);
if (arenaTeam != null)
arenaTeam.DelMember(guid, true);
}
}
while (result.NextRow());
}
public uint GetArenaTeamId(byte slot) { return GetUInt32Value(PlayerFields.ArenaTeamInfo11 + (slot * (int)ArenaTeamInfoType.End) + (int)ArenaTeamInfoType.Id); }
public uint GetArenaPersonalRating(byte slot) { return GetUInt32Value(PlayerFields.ArenaTeamInfo11 + (slot * (int)ArenaTeamInfoType.End) + (int)ArenaTeamInfoType.PersonalRating); }
public void SetArenaTeamIdInvited(uint ArenaTeamId) { m_ArenaTeamIdInvited = ArenaTeamId; }
public uint GetArenaTeamIdInvited() { return m_ArenaTeamIdInvited; }
public uint GetRBGPersonalRating() { return 0; }
//OutdoorPVP
public bool IsOutdoorPvPActive()
{
return IsAlive() && !HasInvisibilityAura() && !HasStealthAura() && IsPvP() && !HasUnitMovementFlag(MovementFlag.Flying) && !IsInFlight();
}
public OutdoorPvP GetOutdoorPvP()
{
return Global.OutdoorPvPMgr.GetOutdoorPvPToZoneId(GetZoneId());
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,555 @@
/*
* Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Framework.Constants;
using Framework.Database;
using Game.DataStorage;
using Game.Network.Packets;
using Game.Spells;
using System.Collections.Generic;
namespace Game.Entities
{
public partial class Player
{
public void InitTalentForLevel()
{
uint level = getLevel();
// talents base at level diff (talents = level - 9 but some can be used already)
if (level < PlayerConst.MinSpecializationLevel)
ResetTalentSpecialization();
uint talentTiers = CalculateTalentsTiers();
if (level < 15)
{
// Remove all talent points
ResetTalents(true);
}
else
{
if (!GetSession().HasPermission(RBACPermissions.SkipCheckMoreTalentsThanAllowed))
{
for (uint t = talentTiers; t < PlayerConst.MaxTalentTiers; ++t)
for (uint c = 0; c < PlayerConst.MaxTalentColumns; ++c)
foreach (TalentRecord talent in Global.DB2Mgr.GetTalentsByPosition(GetClass(), t, c))
RemoveTalent(talent);
}
}
SetUInt32Value(PlayerFields.MaxTalentTiers, talentTiers);
if (!GetSession().PlayerLoading())
SendTalentsInfoData(); // update at client
}
public bool AddTalent(TalentRecord talent, byte spec, bool learning)
{
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(talent.SpellID);
if (spellInfo == null)
{
Log.outError(LogFilter.Spells, "Player.AddTalent: Spell (ID: {0}) does not exist.", talent.SpellID);
return false;
}
if (!Global.SpellMgr.IsSpellValid(spellInfo, this, false))
{
Log.outError(LogFilter.Spells, "Player.AddTalent: Spell (ID: {0}) is invalid", talent.SpellID);
return false;
}
if (talent.OverridesSpellID != 0)
AddOverrideSpell(talent.OverridesSpellID, talent.SpellID);
if (GetTalentMap(spec).ContainsKey(talent.Id))
GetTalentMap(spec)[talent.Id] = PlayerSpellState.Unchanged;
else
GetTalentMap(spec)[talent.Id] = learning ? PlayerSpellState.New : PlayerSpellState.Unchanged;
return true;
}
public void RemoveTalent(TalentRecord talent)
{
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(talent.SpellID);
if (spellInfo == null)
return;
RemoveSpell(talent.SpellID, true);
// search for spells that the talent teaches and unlearn them
foreach (SpellEffectInfo effect in spellInfo.GetEffectsForDifficulty(Difficulty.None))
if (effect != null && effect.TriggerSpell > 0 && effect.Effect == SpellEffectName.LearnSpell)
RemoveSpell(effect.TriggerSpell, true);
if (talent.OverridesSpellID != 0)
RemoveOverrideSpell(talent.OverridesSpellID, talent.SpellID);
var talentMap = GetTalentMap(GetActiveTalentGroup());
// if this talent rank can be found in the PlayerTalentMap, mark the talent as removed so it gets deleted
if (talentMap.ContainsKey(talent.Id))
talentMap[talent.Id] = PlayerSpellState.Removed;
}
public TalentLearnResult LearnTalent(uint talentId, ref int spellOnCooldown)
{
if (IsInCombat())
return TalentLearnResult.FailedAffectingCombat;
if (IsDead() || GetMap().IsBattlegroundOrArena())
return TalentLearnResult.FailedCantDoThatRightNow;
if (GetUInt32Value(PlayerFields.CurrentSpecId) == 0)
return TalentLearnResult.FailedNoPrimaryTreeSelected;
TalentRecord talentInfo = CliDB.TalentStorage.LookupByKey(talentId);
if (talentInfo == null)
return TalentLearnResult.FailedUnknown;
if (talentInfo.SpecID != 0 && talentInfo.SpecID != GetUInt32Value(PlayerFields.CurrentSpecId))
return TalentLearnResult.FailedUnknown;
// prevent learn talent for different class (cheating)
if (talentInfo.ClassID != (byte)GetClass())
return TalentLearnResult.FailedUnknown;
// check if we have enough talent points
if (talentInfo.TierID >= GetUInt32Value(PlayerFields.MaxTalentTiers))
return TalentLearnResult.FailedUnknown;
// TODO: prevent changing talents that are on cooldown
// Check if there is a different talent for us to learn in selected slot
// Example situation:
// Warrior talent row 2 slot 0
// Talent.dbc has an entry for each specialization
// but only 2 out of 3 have SpecID != 0
// We need to make sure that if player is in one of these defined specs he will not learn the other choice
TalentRecord bestSlotMatch = null;
foreach (TalentRecord talent in Global.DB2Mgr.GetTalentsByPosition(GetClass(), talentInfo.TierID, talentInfo.ColumnIndex))
{
if (talent.SpecID == 0)
bestSlotMatch = talent;
else if (talent.SpecID == GetUInt32Value(PlayerFields.CurrentSpecId))
{
bestSlotMatch = talent;
break;
}
}
if (talentInfo != bestSlotMatch)
return TalentLearnResult.FailedUnknown;
// Check if player doesn't have any talent in current tier
for (uint c = 0; c < PlayerConst.MaxTalentColumns; ++c)
{
foreach (TalentRecord talent in Global.DB2Mgr.GetTalentsByPosition(GetClass(), talentInfo.TierID, c))
{
//Todo test me
if (talent.SpecID != GetUInt32Value(PlayerFields.CurrentSpecId))
continue;
if (HasTalent(talent.Id, GetActiveTalentGroup()) && !HasFlag(PlayerFields.Flags, PlayerFlags.Resting) && HasFlag(UnitFields.Flags, UnitFlags.ImmuneToNpc))
return TalentLearnResult.FailedRestArea;
if (GetSpellHistory().HasCooldown(talent.SpellID))
{
spellOnCooldown = (int)talent.SpellID;
return TalentLearnResult.FailedCantRemoveTalent;
}
RemoveTalent(talent);
}
}
// spell not set in talent.dbc
uint spellid = talentInfo.SpellID;
if (spellid == 0)
{
Log.outError(LogFilter.Player, "Player.LearnTalent: Talent.dbc has no spellInfo for talent: {0} (spell id = 0)", talentId);
return TalentLearnResult.FailedUnknown;
}
// already known
if (HasTalent(talentId, GetActiveTalentGroup()) || HasSpell(spellid))
return TalentLearnResult.FailedUnknown;
if (!AddTalent(talentInfo, GetActiveTalentGroup(), true))
return TalentLearnResult.FailedUnknown;
LearnSpell(spellid, false);
Log.outDebug(LogFilter.Misc, "Player.LearnTalent: TalentID: {0} Spell: {1} Group: {2}", talentId, spellid, GetActiveTalentGroup());
return TalentLearnResult.LearnOk;
}
public void ResetTalentSpecialization()
{
// Reset only talents that have different spells for each spec
Class class_ = GetClass();
for (uint t = 0; t < PlayerConst.MaxTalentTiers; ++t)
{
for (uint c = 0; c < PlayerConst.MaxTalentColumns; ++c)
{
if (Global.DB2Mgr.GetTalentsByPosition(class_, t, c).Count > 1)
{
foreach (TalentRecord talent in Global.DB2Mgr.GetTalentsByPosition(class_, t, c))
RemoveTalent(talent);
}
}
}
RemoveSpecializationSpells();
ChrSpecializationRecord defaultSpec = Global.DB2Mgr.GetDefaultChrSpecializationForClass(GetClass());
SetPrimarySpecialization(defaultSpec.Id);
SetActiveTalentGroup(defaultSpec.OrderIndex);
SetUInt32Value(PlayerFields.CurrentSpecId, defaultSpec.Id);
LearnSpecializationSpells();
SendTalentsInfoData();
UpdateItemSetAuras(false);
}
bool HasTalent(uint talnetId, byte group)
{
return GetTalentMap(group).ContainsKey(talnetId) && GetTalentMap(group)[talnetId] != PlayerSpellState.Removed;
}
uint GetTalentResetCost() { return _specializationInfo.ResetTalentsCost; }
void SetTalentResetCost(uint cost) { _specializationInfo.ResetTalentsCost = cost; }
long GetTalentResetTime() { return _specializationInfo.ResetTalentsTime; }
void SetTalentResetTime(long time_) { _specializationInfo.ResetTalentsTime = time_; }
uint GetPrimarySpecialization() { return _specializationInfo.PrimarySpecialization; }
void SetPrimarySpecialization(uint spec) { _specializationInfo.PrimarySpecialization = spec; }
public byte GetActiveTalentGroup() { return _specializationInfo.ActiveGroup; }
void SetActiveTalentGroup(byte group) { _specializationInfo.ActiveGroup = group; }
// Loot Spec
public void SetLootSpecId(uint id) { SetUInt32Value(PlayerFields.LootSpecId, id); }
uint GetLootSpecId() { return GetUInt32Value(PlayerFields.LootSpecId); }
public uint GetDefaultSpecId()
{
return Global.DB2Mgr.GetDefaultChrSpecializationForClass(GetClass()).Id;
}
public void ActivateTalentGroup(ChrSpecializationRecord spec)
{
if (GetActiveTalentGroup() == spec.OrderIndex)
return;
if (IsNonMeleeSpellCast(false))
InterruptNonMeleeSpells(false);
SQLTransaction trans = new SQLTransaction();
_SaveActions(trans);
DB.Characters.CommitTransaction(trans);
// TO-DO: We need more research to know what happens with warlock's reagent
Pet pet = GetPet();
if (pet)
RemovePet(pet, PetSaveMode.NotInSlot);
ClearAllReactives();
UnsummonAllTotems();
ExitVehicle();
RemoveAllControlled();
// Let client clear his current Actions
SendActionButtons(2);
foreach (var talentInfo in CliDB.TalentStorage.Values)
{
// unlearn only talents for character class
// some spell learned by one class as normal spells or know at creation but another class learn it as talent,
// to prevent unexpected lost normal learned spell skip another class talents
if (talentInfo.ClassID != (int)GetClass())
continue;
if (talentInfo.SpellID == 0)
continue;
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(talentInfo.SpellID);
if (spellInfo == null)
continue;
RemoveSpell(talentInfo.SpellID, true);
// search for spells that the talent teaches and unlearn them
foreach (SpellEffectInfo effect in spellInfo.GetEffectsForDifficulty(Difficulty.None))
if (effect != null && effect.TriggerSpell > 0 && effect.Effect == SpellEffectName.LearnSpell)
RemoveSpell(effect.TriggerSpell, true);
if (talentInfo.OverridesSpellID != 0)
RemoveOverrideSpell(talentInfo.OverridesSpellID, talentInfo.SpellID);
}
// Remove spec specific spells
RemoveSpecializationSpells();
foreach (uint glyphId in GetGlyphs(GetActiveTalentGroup()))
RemoveAurasDueToSpell(CliDB.GlyphPropertiesStorage.LookupByKey(glyphId).SpellID);
SetActiveTalentGroup(spec.OrderIndex);
SetUInt32Value(PlayerFields.CurrentSpecId, spec.Id);
if (GetPrimarySpecialization() == 0)
SetPrimarySpecialization(spec.Id);
foreach (var talentInfo in CliDB.TalentStorage.Values)
{
// learn only talents for character class
if (talentInfo.ClassID != (int)GetClass())
continue;
if (talentInfo.SpellID == 0)
continue;
if (HasTalent(talentInfo.SpellID, GetActiveTalentGroup()))
{
LearnSpell(talentInfo.SpellID, false); // add the talent to the PlayerSpellMap
if (talentInfo.OverridesSpellID != 0)
AddOverrideSpell(talentInfo.OverridesSpellID, talentInfo.SpellID);
}
}
LearnSpecializationSpells();
if (CanUseMastery())
{
for (uint i = 0; i < PlayerConst.MaxMasterySpells; ++i)
{
uint mastery = spec.MasterySpellID[i];
if (mastery != 0)
LearnSpell(mastery, false);
}
}
InitTalentForLevel();
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_ACTIONS_SPEC);
stmt.AddValue(0, GetGUID().GetCounter());
stmt.AddValue(1, GetActiveTalentGroup());
_LoadActions(DB.Characters.Query(stmt));
SendActionButtons(1);
UpdateDisplayPower();
PowerType pw = getPowerType();
if (pw != PowerType.Mana)
SetPower(PowerType.Mana, 0); // Mana must be 0 even if it isn't the active power type.
SetPower(pw, 0);
UpdateItemSetAuras(false);
// update visible transmog
for (byte i = EquipmentSlot.Start; i < EquipmentSlot.End; ++i)
{
Item equippedItem = GetItemByPos(InventorySlots.Bag0, i);
if (equippedItem)
SetVisibleItemSlot(i, equippedItem);
}
foreach (uint glyphId in GetGlyphs(spec.OrderIndex))
CastSpell(this, CliDB.GlyphPropertiesStorage.LookupByKey(glyphId).SpellID, true);
ActiveGlyphs activeGlyphs = new ActiveGlyphs();
foreach (uint glyphId in GetGlyphs(spec.OrderIndex))
{
List<uint> bindableSpells = Global.DB2Mgr.GetGlyphBindableSpells(glyphId);
foreach (uint bindableSpell in bindableSpells)
if (HasSpell(bindableSpell) && !m_overrideSpells.ContainsKey(bindableSpell))
activeGlyphs.Glyphs.Add(new GlyphBinding(bindableSpell, (ushort)glyphId));
}
activeGlyphs.IsFullUpdate = true;
SendPacket(activeGlyphs);
}
public Dictionary<uint, PlayerSpellState> GetTalentMap(uint spec) { return _specializationInfo.Talents[spec]; }
public List<uint> GetGlyphs(byte spec) { return _specializationInfo.Glyphs[spec]; }
public uint GetNextResetTalentsCost()
{
// The first time reset costs 1 gold
if (GetTalentResetCost() < 1 * MoneyConstants.Gold)
return 1 * MoneyConstants.Gold;
// then 5 gold
else if (GetTalentResetCost() < 5 * MoneyConstants.Gold)
return 5 * MoneyConstants.Gold;
// After that it increases in increments of 5 gold
else if (GetTalentResetCost() < 10 * MoneyConstants.Gold)
return 10 * MoneyConstants.Gold;
else
{
ulong months = (ulong)(Global.WorldMgr.GetGameTime() - GetTalentResetTime()) / Time.Month;
if (months > 0)
{
// This cost will be reduced by a rate of 5 gold per month
uint new_cost = (uint)(GetTalentResetCost() - 5 * MoneyConstants.Gold * months);
// to a minimum of 10 gold.
return new_cost < 10 * MoneyConstants.Gold ? 10 * MoneyConstants.Gold : new_cost;
}
else
{
// After that it increases in increments of 5 gold
uint new_cost = GetTalentResetCost() + 5 * MoneyConstants.Gold;
// until it hits a cap of 50 gold.
if (new_cost > 50 * MoneyConstants.Gold)
new_cost = 50 * MoneyConstants.Gold;
return new_cost;
}
}
}
public bool ResetTalents(bool noCost = false)
{
Global.ScriptMgr.OnPlayerTalentsReset(this, noCost);
// not need after this call
if (HasAtLoginFlag(AtLoginFlags.ResetTalents))
RemoveAtLoginFlag(AtLoginFlags.ResetTalents, true);
uint cost = 0;
if (!noCost && !WorldConfig.GetBoolValue(WorldCfg.NoResetTalentCost))
{
cost = GetNextResetTalentsCost();
if (!HasEnoughMoney(cost))
{
SendBuyError(BuyResult.NotEnoughtMoney, null, 0);
return false;
}
}
RemovePet(null, PetSaveMode.NotInSlot, true);
foreach (var talentInfo in CliDB.TalentStorage.Values)
{
// unlearn only talents for character class
// some spell learned by one class as normal spells or know at creation but another class learn it as talent,
// to prevent unexpected lost normal learned spell skip another class talents
if (talentInfo.ClassID != (uint)GetClass())
continue;
// skip non-existant talent ranks
if (talentInfo.SpellID == 0)
continue;
RemoveTalent(talentInfo);
}
SQLTransaction trans = new SQLTransaction();
_SaveTalents(trans);
_SaveSpells(trans);
DB.Characters.CommitTransaction(trans);
if (!noCost)
{
ModifyMoney(-cost);
UpdateCriteria(CriteriaTypes.GoldSpentForTalents, cost);
UpdateCriteria(CriteriaTypes.NumberOfTalentResets, 1);
SetTalentResetCost(cost);
SetTalentResetTime(Time.UnixTime);
}
return true;
}
public void SendTalentsInfoData()
{
UpdateTalentData packet = new UpdateTalentData();
packet.Info.PrimarySpecialization = GetPrimarySpecialization();
packet.Info.ActiveGroup = GetActiveTalentGroup();
for (byte i = 0; i < PlayerConst.MaxSpecializations; ++i)
{
ChrSpecializationRecord spec = Global.DB2Mgr.GetChrSpecializationByIndex(GetClass(), i);
if (spec == null)
continue;
var talents = GetTalentMap(i);
UpdateTalentData.TalentGroupInfo groupInfoPkt = new UpdateTalentData.TalentGroupInfo();
groupInfoPkt.SpecID = spec.Id;
foreach (var pair in talents)
{
if (pair.Value == PlayerSpellState.Removed)
continue;
TalentRecord talentInfo = CliDB.TalentStorage.LookupByKey(pair.Key);
if (talentInfo == null)
{
Log.outError(LogFilter.Player, "Player {0} has unknown talent id: {1}", GetName(), pair.Key);
continue;
}
if (talentInfo.ClassID != (uint)GetClass())
continue;
SpellInfo spellEntry = Global.SpellMgr.GetSpellInfo(talentInfo.SpellID);
if (spellEntry == null)
{
Log.outError(LogFilter.Player, "Player {0} has unknown talent spell: {1}", GetName(), talentInfo.SpellID);
continue;
}
groupInfoPkt.TalentIDs.Add((ushort)pair.Key);
}
packet.Info.TalentGroups.Add(groupInfoPkt);
}
SendPacket(packet);
}
public void SendRespecWipeConfirm(ObjectGuid guid, uint cost)
{
RespecWipeConfirm respecWipeConfirm = new RespecWipeConfirm();
respecWipeConfirm.RespecMaster = guid;
respecWipeConfirm.Cost = cost;
respecWipeConfirm.RespecType = SpecResetType.Talents;
SendPacket(respecWipeConfirm);
}
uint CalculateTalentsTiers()
{
uint[] rowLevels = new uint[0];
switch (GetClass())
{
case Class.Deathknight:
rowLevels = new uint[] { 57, 58, 59, 60, 75, 90, 100 };
break;
case Class.DemonHunter:
rowLevels = new uint[] { 99, 100, 102, 104, 106, 108, 110 };
break;
default:
rowLevels = new uint[] { 15, 30, 45, 60, 75, 90, 100 };
break;
}
for (uint i = PlayerConst.MaxTalentTiers; i != 0; --i)
if (getLevel() >= rowLevels[i - 1])
return i;
return 0;
}
}
}
File diff suppressed because it is too large Load Diff
+244
View File
@@ -0,0 +1,244 @@
/*
* Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Framework.Collections;
using Framework.Constants;
using Game.DataStorage;
using Game.Network.Packets;
using System.Collections.Generic;
using System.Text;
namespace Game.Entities
{
public class PlayerTaxi
{
public void InitTaxiNodesForLevel(Race race, Class chrClass, uint level)
{
// class specific initial known nodes
var factionMask = Player.TeamForRace(race) == Team.Horde ? CliDB.HordeTaxiNodesMask : CliDB.AllianceTaxiNodesMask;
switch (chrClass)
{
case Class.Deathknight:
{
for (byte i = 0; i < PlayerConst.TaxiMaskSize; ++i)
m_taximask[i] |= (byte)(CliDB.OldContinentsNodesMask[i] & factionMask[i]);
break;
}
}
// race specific initial known nodes: capital and taxi hub masks
switch (race)
{
case Race.Human:
case Race.Dwarf:
case Race.NightElf:
case Race.Gnome:
case Race.Draenei:
case Race.Worgen:
case Race.PandarenAlliance:
SetTaximaskNode(2); // Stormwind, Elwynn
SetTaximaskNode(6); // Ironforge, Dun Morogh
SetTaximaskNode(26); // Lor'danel, Darkshore
SetTaximaskNode(27); // Rut'theran Village, Teldrassil
SetTaximaskNode(49); // Moonglade (Alliance)
SetTaximaskNode(94); // The Exodar
SetTaximaskNode(456); // Dolanaar, Teldrassil
SetTaximaskNode(457); // Darnassus, Teldrassil
SetTaximaskNode(582); // Goldshire, Elwynn
SetTaximaskNode(589); // Eastvale Logging Camp, Elwynn
SetTaximaskNode(619); // Kharanos, Dun Morogh
SetTaximaskNode(620); // Gol'Bolar Quarry, Dun Morogh
SetTaximaskNode(624); // Azure Watch, Azuremyst Isle
break;
case Race.Orc:
case Race.Undead:
case Race.Tauren:
case Race.Troll:
case Race.BloodElf:
case Race.Goblin:
case Race.PandarenHorde:
SetTaximaskNode(11); // Undercity, Tirisfal
SetTaximaskNode(22); // Thunder Bluff, Mulgore
SetTaximaskNode(23); // Orgrimmar, Durotar
SetTaximaskNode(69); // Moonglade (Horde)
SetTaximaskNode(82); // Silvermoon City
SetTaximaskNode(384); // The Bulwark, Tirisfal
SetTaximaskNode(402); // Bloodhoof Village, Mulgore
SetTaximaskNode(460); // Brill, Tirisfal Glades
SetTaximaskNode(536); // Sen'jin Village, Durotar
SetTaximaskNode(537); // Razor Hill, Durotar
SetTaximaskNode(625); // Fairbreeze Village, Eversong Woods
SetTaximaskNode(631); // Falconwing Square, Eversong Woods
break;
}
// new continent starting masks (It will be accessible only at new map)
switch (Player.TeamForRace(race))
{
case Team.Alliance:
SetTaximaskNode(100);
break;
case Team.Horde:
SetTaximaskNode(99);
break;
}
// level dependent taxi hubs
if (level >= 68)
SetTaximaskNode(213); //Shattered Sun Staging Area
}
public void LoadTaxiMask(string data)
{
var split = new StringArray(data, ' ');
byte index = 0;
for (var i = 0; index < PlayerConst.TaxiMaskSize && i != split.Length; ++i, ++index)
{
// load and set bits only for existing taxi nodes
m_taximask[index] = (byte)(CliDB.TaxiNodesMask[index] & uint.Parse(split[i]));
}
}
public void AppendTaximaskTo(ShowTaxiNodes data, bool all)
{
if (all)
data.Nodes = CliDB.TaxiNodesMask; // all existed nodes
else
data.Nodes = m_taximask; // known nodes
}
public bool LoadTaxiDestinationsFromString(string values, Team team)
{
ClearTaxiDestinations();
var split = new StringArray(values, ' ');
for (var i = 0; i < split.Length; ++i)
{
uint node = uint.Parse(split[i]);
AddTaxiDestination(node);
}
if (m_TaxiDestinations.Empty())
return true;
// Check integrity
if (m_TaxiDestinations.Count < 2)
return false;
for (int i = 1; i < m_TaxiDestinations.Count; ++i)
{
uint cost;
uint path;
Global.ObjectMgr.GetTaxiPath(m_TaxiDestinations[i - 1], m_TaxiDestinations[i], out path, out cost);
if (path == 0)
return false;
}
// can't load taxi path without mount set (quest taxi path?)
if (Global.ObjectMgr.GetTaxiMountDisplayId(GetTaxiSource(), team, true) == 0)
return false;
return true;
}
public string SaveTaxiDestinationsToString()
{
if (m_TaxiDestinations.Empty())
return "";
StringBuilder ss = new StringBuilder();
for (int i = 0; i < m_TaxiDestinations.Count; ++i)
ss.AppendFormat("{0} ", m_TaxiDestinations[i]);
return ss.ToString();
}
public uint GetCurrentTaxiPath()
{
if (m_TaxiDestinations.Count < 2)
return 0;
uint path;
uint cost;
Global.ObjectMgr.GetTaxiPath(m_TaxiDestinations[0], m_TaxiDestinations[1], out path, out cost);
return path;
}
public bool RequestEarlyLanding()
{
if (m_TaxiDestinations.Count <= 2)
return false;
// start from first destination - m_TaxiDestinations[0] is the current starting node
for (var i = 1; i < m_TaxiDestinations.Count; ++i)
{
if (IsTaximaskNodeKnown(m_TaxiDestinations[i]))
{
if (++i == m_TaxiDestinations.Count - 1)
return false; // if we are left with only 1 known node on the path don't change the spline, its our final destination anyway
m_TaxiDestinations.RemoveRange(i, m_TaxiDestinations.Count - 1);
return true;
}
}
return false;
}
public bool IsTaximaskNodeKnown(uint nodeidx)
{
byte field = (byte)((nodeidx - 1) / 8);
uint submask = (uint)(1 << (int)((nodeidx - 1) % 8));
return (m_taximask[field] & submask) == submask;
}
public bool SetTaximaskNode(uint nodeidx)
{
byte field = (byte)((nodeidx - 1) / 8);
uint submask = (uint)(1 << (int)((nodeidx - 1) % 8));
if ((m_taximask[field] & submask) != submask)
{
m_taximask[field] |= (byte)submask;
return true;
}
else
return false;
}
public void ClearTaxiDestinations() { m_TaxiDestinations.Clear(); }
public void AddTaxiDestination(uint dest) { m_TaxiDestinations.Add(dest); }
void SetTaxiDestination(List<uint> nodes)
{
m_TaxiDestinations.Clear();
m_TaxiDestinations.AddRange(nodes);
}
public uint GetTaxiSource() { return m_TaxiDestinations.Empty() ? 0 : m_TaxiDestinations[0]; }
public uint GetTaxiDestination() { return m_TaxiDestinations.Count < 2 ? 0 : m_TaxiDestinations[1]; }
public uint NextTaxiDestination()
{
m_TaxiDestinations.RemoveAt(0);
return GetTaxiDestination();
}
public List<uint> GetPath() { return m_TaxiDestinations; }
public bool empty() { return m_TaxiDestinations.Empty(); }
public byte[] m_taximask = new byte[PlayerConst.TaxiMaskSize];
List<uint> m_TaxiDestinations = new List<uint>();
}
}
+165
View File
@@ -0,0 +1,165 @@
using Framework.Constants;
namespace Game.Entities
{
public class RestMgr
{
public RestMgr(Player player)
{
_player = player;
}
public void SetRestBonus(RestTypes restType, float restBonus)
{
byte rest_rested_offset;
byte rest_state_offset;
PlayerFields next_level_xp_field;
bool affectedByRaF = false;
switch (restType)
{
case RestTypes.XP:
// Reset restBonus (XP only) for max level players
if (_player.getLevel() >= WorldConfig.GetIntValue(WorldCfg.MaxPlayerLevel))
restBonus = 0;
rest_rested_offset = PlayerFieldOffsets.RestRestedXp;
rest_state_offset = PlayerFieldOffsets.RestStateXp;
next_level_xp_field = PlayerFields.NextLevelXp;
affectedByRaF = true;
break;
case RestTypes.Honor:
// Reset restBonus (Honor only) for players with max honor level.
if (_player.IsMaxHonorLevelAndPrestige())
restBonus = 0;
rest_rested_offset = PlayerFieldOffsets.RestRestedHonor;
rest_state_offset = PlayerFieldOffsets.RestStateHonor;
next_level_xp_field = PlayerFields.HonorNextLevel;
break;
default:
return;
}
if (restBonus < 0)
restBonus = 0;
float rest_bonus_max = (float)(_player.GetUInt32Value(next_level_xp_field)) * 1.5f / 2;
if (restBonus > rest_bonus_max)
_restBonus[(int)restType] = rest_bonus_max;
else
_restBonus[(int)restType] = restBonus;
// update data for client
if (affectedByRaF && _player.GetsRecruitAFriendBonus(true) && (_player.GetSession().IsARecruiter() || _player.GetSession().GetRecruiterId() != 0))
_player.SetUInt32Value(PlayerFields.RestInfo + rest_state_offset, (uint)PlayerRestState.RAFLinked);
else
{
if (_restBonus[(int)restType] > 10)
_player.SetUInt32Value(PlayerFields.RestInfo + rest_state_offset, (uint)PlayerRestState.Rested);
else if (_restBonus[(int)restType] <= 1)
_player.SetUInt32Value(PlayerFields.RestInfo + rest_state_offset, (uint)PlayerRestState.NotRAFLinked);
}
// RestTickUpdate
_player.SetUInt32Value(PlayerFields.RestInfo + rest_rested_offset, (uint)_restBonus[(int)restType]);
}
public void AddRestBonus(RestTypes restType, float restBonus)
{
// Don't add extra rest bonus to max level players. Note: Might need different condition in next expansion for honor XP (PLAYER_LEVEL_MIN_HONOR perhaps).
if (_player.getLevel() >= WorldConfig.GetIntValue(WorldCfg.MaxPlayerLevel))
restBonus = 0;
float totalRestBonus = GetRestBonus(restType) + restBonus;
SetRestBonus(restType, totalRestBonus);
}
public void SetRestFlag(RestFlag restFlag, uint triggerId = 0)
{
RestFlag oldRestMask = _restFlagMask;
_restFlagMask |= restFlag;
if (oldRestMask == 0 && _restFlagMask != 0) // only set flag/time on the first rest state
{
_restTime = Time.UnixTime;
_player.SetFlag(PlayerFields.Flags, PlayerFlags.Resting);
}
if (triggerId != 0)
_innAreaTriggerId = triggerId;
}
public void RemoveRestFlag(RestFlag restFlag)
{
RestFlag oldRestMask = _restFlagMask;
_restFlagMask &= ~restFlag;
if (oldRestMask != 0 && _restFlagMask == 0) // only remove flag/time on the last rest state remove
{
_restTime = 0;
_player.RemoveFlag(PlayerFields.Flags, PlayerFlags.Resting);
}
}
public uint GetRestBonusFor(RestTypes restType, uint xp)
{
uint rested_bonus = (uint)GetRestBonus(restType); // xp for each rested bonus
if (rested_bonus > xp) // max rested_bonus == xp or (r+x) = 200% xp
rested_bonus = xp;
SetRestBonus(restType, GetRestBonus(restType) - rested_bonus);
Log.outDebug(LogFilter.Player, "RestMgr.GetRestBonus: Player '{0}' ({1}) gain {2} xp (+{3} Rested Bonus). Rested points={4}",
_player.GetGUID().ToString(), _player.GetName(), xp + rested_bonus, rested_bonus, GetRestBonus(restType));
return rested_bonus;
}
public void Update(uint now)
{
if (RandomHelper.randChance(3) && _restTime > 0) // freeze update
{
long timeDiff = now - _restTime;
if (timeDiff >= 10)
{
_restTime = now;
float bubble = 0.125f * WorldConfig.GetFloatValue(WorldCfg.RateRestIngame);
AddRestBonus(RestTypes.XP, timeDiff * CalcExtraPerSec(RestTypes.XP, bubble));
}
}
}
public void LoadRestBonus(RestTypes restType, PlayerRestState state, float restBonus)
{
_restBonus[(int)restType] = restBonus;
_player.SetUInt32Value(PlayerFields.RestInfo + (int)restType * 2, (uint)state);
_player.SetUInt32Value(PlayerFields.RestInfo + (int)restType * 2 + 1, (uint)restBonus);
}
public float CalcExtraPerSec(RestTypes restType, float bubble)
{
switch (restType)
{
case RestTypes.Honor:
return (_player.GetUInt32Value(PlayerFields.HonorNextLevel)) / 72000.0f * bubble;
case RestTypes.XP:
return (_player.GetUInt32Value(PlayerFields.NextLevelXp)) / 72000.0f * bubble;
default:
return 0.0f;
}
}
public float GetRestBonus(RestTypes restType) { return _restBonus[(int)restType]; }
public bool HasRestFlag(RestFlag restFlag) { return (_restFlagMask & restFlag) != 0; }
public uint GetInnTriggerId() { return _innAreaTriggerId; }
Player _player;
long _restTime;
uint _innAreaTriggerId;
float[] _restBonus = new float[(int)RestTypes.Max];
RestFlag _restFlagMask;
}
}
+246
View File
@@ -0,0 +1,246 @@
/*
* Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Framework.Constants;
using Game.DataStorage;
using Game.Network.Packets;
using System;
using System.Collections.Generic;
namespace Game.Entities
{
public class SceneMgr
{
public SceneMgr(Player player)
{
_player = player;
_standaloneSceneInstanceID = 0;
_isDebuggingScenes = false;
}
public uint PlayScene(uint sceneId, Position position = null)
{
SceneTemplate sceneTemplate = Global.ObjectMgr.GetSceneTemplate(sceneId);
return PlaySceneByTemplate(sceneTemplate, position);
}
uint PlaySceneByTemplate(SceneTemplate sceneTemplate, Position position = null)
{
if (sceneTemplate == null)
return 0;
SceneScriptPackageRecord entry = CliDB.SceneScriptPackageStorage.LookupByKey(sceneTemplate.ScenePackageId);
if (entry == null)
return 0;
// By default, take player position
if (position == null)
position = GetPlayer();
uint sceneInstanceID = GetNewStandaloneSceneInstanceID();
if (_isDebuggingScenes)
GetPlayer().SendSysMessage(CypherStrings.CommandSceneDebugPlay, sceneInstanceID, sceneTemplate.ScenePackageId, sceneTemplate.PlaybackFlags);
PlayScene playScene = new PlayScene();
playScene.SceneID = sceneTemplate.SceneId;
playScene.PlaybackFlags = (uint)sceneTemplate.PlaybackFlags;
playScene.SceneInstanceID = sceneInstanceID;
playScene.SceneScriptPackageID = sceneTemplate.ScenePackageId;
playScene.Location = position;
playScene.TransportGUID = GetPlayer().GetTransGUID();
GetPlayer().SendPacket(playScene);
AddInstanceIdToSceneMap(sceneInstanceID, sceneTemplate);
Global.ScriptMgr.OnSceneStart(GetPlayer(), sceneInstanceID, sceneTemplate);
return sceneInstanceID;
}
public uint PlaySceneByPackageId(uint sceneScriptPackageId, SceneFlags playbackflags = SceneFlags.Unk16, Position position = null)
{
SceneTemplate sceneTemplate = new SceneTemplate();
sceneTemplate.SceneId = 0;
sceneTemplate.ScenePackageId = sceneScriptPackageId;
sceneTemplate.PlaybackFlags = playbackflags;
sceneTemplate.ScriptId = 0;
return PlaySceneByTemplate(sceneTemplate, position);
}
void CancelScene(uint sceneInstanceID, bool removeFromMap = true)
{
if (removeFromMap)
RemoveSceneInstanceId(sceneInstanceID);
CancelScene cancelScene = new CancelScene();
cancelScene.SceneInstanceID = sceneInstanceID;
GetPlayer().SendPacket(cancelScene);
}
public void OnSceneTrigger(uint sceneInstanceID, string triggerName)
{
if (!HasScene(sceneInstanceID))
return;
if (_isDebuggingScenes)
GetPlayer().SendSysMessage(CypherStrings.CommandSceneDebugTrigger, sceneInstanceID, triggerName);
SceneTemplate sceneTemplate = GetSceneTemplateFromInstanceId(sceneInstanceID);
Global.ScriptMgr.OnSceneTrigger(GetPlayer(), sceneInstanceID, sceneTemplate, triggerName);
}
public void OnSceneCancel(uint sceneInstanceID)
{
if (!HasScene(sceneInstanceID))
return;
if (_isDebuggingScenes)
GetPlayer().SendSysMessage(CypherStrings.CommandSceneDebugCancel, sceneInstanceID);
SceneTemplate sceneTemplate = GetSceneTemplateFromInstanceId(sceneInstanceID);
// Must be done before removing aura
RemoveSceneInstanceId(sceneInstanceID);
if (sceneTemplate.SceneId != 0)
RemoveAurasDueToSceneId(sceneTemplate.SceneId);
Global.ScriptMgr.OnSceneCancel(GetPlayer(), sceneInstanceID, sceneTemplate);
if (sceneTemplate.PlaybackFlags.HasAnyFlag(SceneFlags.CancelAtEnd))
CancelScene(sceneInstanceID, false);
}
public void OnSceneComplete(uint sceneInstanceID)
{
if (!HasScene(sceneInstanceID))
return;
if (_isDebuggingScenes)
GetPlayer().SendSysMessage(CypherStrings.CommandSceneDebugComplete, sceneInstanceID);
SceneTemplate sceneTemplate = GetSceneTemplateFromInstanceId(sceneInstanceID);
// Must be done before removing aura
RemoveSceneInstanceId(sceneInstanceID);
if (sceneTemplate.SceneId != 0)
RemoveAurasDueToSceneId(sceneTemplate.SceneId);
Global.ScriptMgr.OnSceneComplete(GetPlayer(), sceneInstanceID, sceneTemplate);
if (sceneTemplate.PlaybackFlags.HasAnyFlag(SceneFlags.CancelAtEnd))
CancelScene(sceneInstanceID, false);
}
bool HasScene(uint sceneInstanceID, uint sceneScriptPackageId = 0)
{
var sceneTempalte = _scenesByInstance.LookupByKey(sceneInstanceID);
if (sceneTempalte != null)
return sceneScriptPackageId == 0 || sceneScriptPackageId == sceneTempalte.ScenePackageId;
return false;
}
void AddInstanceIdToSceneMap(uint sceneInstanceID, SceneTemplate sceneTemplate)
{
_scenesByInstance[sceneInstanceID] = sceneTemplate;
}
public void CancelSceneBySceneId(uint sceneId)
{
List<uint> instancesIds = new List<uint>();
foreach (var pair in _scenesByInstance)
if (pair.Value.SceneId == sceneId)
instancesIds.Add(pair.Key);
foreach (uint sceneInstanceID in instancesIds)
CancelScene(sceneInstanceID);
}
public void CancelSceneByPackageId(uint sceneScriptPackageId)
{
List<uint> instancesIds = new List<uint>();
foreach (var sceneTemplate in _scenesByInstance)
if (sceneTemplate.Value.ScenePackageId == sceneScriptPackageId)
instancesIds.Add(sceneTemplate.Key);
foreach (uint sceneInstanceID in instancesIds)
CancelScene(sceneInstanceID);
}
void RemoveSceneInstanceId(uint sceneInstanceID)
{
_scenesByInstance.Remove(sceneInstanceID);
}
void RemoveAurasDueToSceneId(uint sceneId)
{
var scenePlayAuras = GetPlayer().GetAuraEffectsByType(AuraType.PlayScene);
foreach (var scenePlayAura in scenePlayAuras)
{
if (scenePlayAura.GetMiscValue() == sceneId)
{
GetPlayer().RemoveAura(scenePlayAura.GetBase());
break;
}
}
}
SceneTemplate GetSceneTemplateFromInstanceId(uint sceneInstanceID)
{
return _scenesByInstance.LookupByKey(sceneInstanceID);
}
public uint GetActiveSceneCount(uint sceneScriptPackageId = 0)
{
uint activeSceneCount = 0;
foreach (var sceneTemplate in _scenesByInstance.Values)
if (sceneScriptPackageId == 0 || sceneTemplate.ScenePackageId == sceneScriptPackageId)
++activeSceneCount;
return activeSceneCount;
}
Player GetPlayer() { return _player; }
void RecreateScene(uint sceneScriptPackageId, SceneFlags playbackflags = SceneFlags.Unk16, Position position = null)
{
CancelSceneByPackageId(sceneScriptPackageId);
PlaySceneByPackageId(sceneScriptPackageId, playbackflags, position);
}
public Dictionary<uint, SceneTemplate> GetSceneTemplateByInstanceMap() { return _scenesByInstance; }
uint GetNewStandaloneSceneInstanceID() { return ++_standaloneSceneInstanceID; }
public void ToggleDebugSceneMode() { _isDebuggingScenes = !_isDebuggingScenes; }
public bool IsInDebugSceneMode() { return _isDebuggingScenes; }
Player _player;
Dictionary<uint, SceneTemplate> _scenesByInstance = new Dictionary<uint, SceneTemplate>();
uint _standaloneSceneInstanceID;
bool _isDebuggingScenes;
}
}
+360
View File
@@ -0,0 +1,360 @@
/*
* Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Framework.Constants;
using Framework.Database;
using Game.Network;
using Game.Network.Packets;
using System;
using System.Collections.Generic;
namespace Game.Entities
{
public class SocialManager : Singleton<SocialManager>
{
SocialManager() { }
public const int FriendLimit = 50;
public const int IgnoreLimit = 50;
public void GetFriendInfo(Player player, ObjectGuid friendGUID, FriendInfo friendInfo)
{
if (!player)
return;
friendInfo.Status = FriendStatus.Offline;
friendInfo.Area = 0;
friendInfo.Level = 0;
friendInfo.Class = 0;
Player target = Global.ObjAccessor.FindPlayer(friendGUID);
if (!target)
return;
var playerFriendInfo = player.GetSocial()._playerSocialMap.LookupByKey(friendGUID);
if (playerFriendInfo != null)
friendInfo.Note = playerFriendInfo.Note;
// PLAYER see his team only and PLAYER can't see MODERATOR, GAME MASTER, ADMINISTRATOR characters
// MODERATOR, GAME MASTER, ADMINISTRATOR can see all
if (!player.GetSession().HasPermission(RBACPermissions.WhoSeeAllSecLevels) &&
target.GetSession().GetSecurity() > (AccountTypes)WorldConfig.GetIntValue(WorldCfg.GmLevelInWhoList))
return;
// player can see member of other team only if CONFIG_ALLOW_TWO_SIDE_WHO_LIST
if (target.GetTeam() != player.GetTeam() && !player.GetSession().HasPermission(RBACPermissions.TwoSideWhoList))
return;
if (target.IsVisibleGloballyFor(player))
{
if (target.isDND())
friendInfo.Status = FriendStatus.DND;
else if (target.isAFK())
friendInfo.Status = FriendStatus.AFK;
else
friendInfo.Status = FriendStatus.Online;
friendInfo.Area = target.GetZoneId();
friendInfo.Level = target.getLevel();
friendInfo.Class = target.GetClass();
}
}
public void SendFriendStatus(Player player, FriendsResult result, ObjectGuid friendGuid, bool broadcast = false)
{
FriendInfo fi = new FriendInfo();
GetFriendInfo(player, friendGuid, fi);
FriendStatusPkt friendStatus = new FriendStatusPkt();
friendStatus.Initialize(friendGuid, result, fi);
if (broadcast)
BroadcastToFriendListers(player, friendStatus);
else
player.SendPacket(friendStatus);
}
void BroadcastToFriendListers(Player player, ServerPacket packet)
{
if (!player)
return;
AccountTypes gmSecLevel = (AccountTypes)WorldConfig.GetIntValue(WorldCfg.GmLevelInWhoList);
foreach (var pair in _socialMap)
{
var info = pair.Value._playerSocialMap.LookupByKey(player.GetGUID());
if (info != null && info.Flags.HasAnyFlag(SocialFlag.Friend))
{
Player target = Global.ObjAccessor.FindPlayer(pair.Key);
if (!target || !target.IsInWorld)
continue;
WorldSession session = target.GetSession();
if (!session.HasPermission(RBACPermissions.WhoSeeAllSecLevels) && player.GetSession().GetSecurity() > gmSecLevel)
continue;
if (target.GetTeam() != player.GetTeam() && !session.HasPermission(RBACPermissions.TwoSideWhoList))
continue;
if (player.IsVisibleGloballyFor(target))
session.SendPacket(packet);
}
}
}
public PlayerSocial LoadFromDB(SQLResult result, ObjectGuid guid)
{
PlayerSocial social = new PlayerSocial();
social.SetPlayerGUID(guid);
if (!result.IsEmpty())
{
do
{
ObjectGuid friendGuid = ObjectGuid.Create(HighGuid.Player, result.Read<ulong>(0));
ObjectGuid friendAccountGuid = ObjectGuid.Create(HighGuid.WowAccount, result.Read<uint>(1));
SocialFlag flags = (SocialFlag)result.Read<byte>(2);
social._playerSocialMap[friendGuid] = new FriendInfo(friendAccountGuid, flags, result.Read<string>(3));
}
while (result.NextRow());
}
_socialMap[guid] = social;
return social;
}
public void RemovePlayerSocial(ObjectGuid guid) { _socialMap.Remove(guid); }
Dictionary<ObjectGuid, PlayerSocial> _socialMap = new Dictionary<ObjectGuid, PlayerSocial>();
}
public class PlayerSocial
{
uint GetNumberOfSocialsWithFlag(SocialFlag flag)
{
uint counter = 0;
foreach (var pair in _playerSocialMap)
if (pair.Value.Flags.HasAnyFlag(flag))
++counter;
return counter;
}
public bool AddToSocialList(ObjectGuid friendGuid, SocialFlag flag)
{
// check client limits
if (GetNumberOfSocialsWithFlag(flag) >= (((flag & SocialFlag.Friend) != 0) ? SocialManager.FriendLimit : SocialManager.IgnoreLimit))
return false;
var friendInfo = _playerSocialMap.LookupByKey(friendGuid);
if (friendInfo != null)
{
friendInfo.Flags |= flag;
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHARACTER_SOCIAL_FLAGS);
stmt.AddValue(0, friendInfo.Flags);
stmt.AddValue(1, GetPlayerGUID().GetCounter());
stmt.AddValue(2, friendGuid.GetCounter());
DB.Characters.Execute(stmt);
}
else
{
FriendInfo fi = new FriendInfo();
fi.Flags |= flag;
_playerSocialMap[friendGuid] = fi;
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_CHARACTER_SOCIAL);
stmt.AddValue(0, GetPlayerGUID().GetCounter());
stmt.AddValue(1, friendGuid.GetCounter());
stmt.AddValue(2, flag);
DB.Characters.Execute(stmt);
}
return true;
}
public void RemoveFromSocialList(ObjectGuid friendGuid, SocialFlag flag)
{
var friendInfo = _playerSocialMap.LookupByKey(friendGuid);
if (friendInfo == null) // not exist
return;
friendInfo.Flags &= ~flag;
if (friendInfo.Flags == 0)
{
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHARACTER_SOCIAL);
stmt.AddValue(0, GetPlayerGUID().GetCounter());
stmt.AddValue(1, friendGuid.GetCounter());
DB.Characters.Execute(stmt);
_playerSocialMap.Remove(friendGuid);
}
else
{
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHARACTER_SOCIAL_FLAGS);
stmt.AddValue(0, friendInfo.Flags);
stmt.AddValue(1, GetPlayerGUID().GetCounter());
stmt.AddValue(2, friendGuid.GetCounter());
DB.Characters.Execute(stmt);
}
}
public void SetFriendNote(ObjectGuid friendGuid, string note)
{
if (!_playerSocialMap.ContainsKey(friendGuid)) // not exist
return;
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHARACTER_SOCIAL_NOTE);
stmt.AddValue(0, note);
stmt.AddValue(1, GetPlayerGUID().GetCounter());
stmt.AddValue(2, friendGuid.GetCounter());
DB.Characters.Execute(stmt);
_playerSocialMap[friendGuid].Note = note;
}
public void SendSocialList(Player player, SocialFlag flags)
{
if (!player)
return;
ContactList contactList = new ContactList();
contactList.Flags = flags;
foreach (var v in _playerSocialMap)
{
if (!v.Value.Flags.HasAnyFlag(flags))
continue;
Global.SocialMgr.GetFriendInfo(player, v.Key, v.Value);
contactList.Contacts.Add(new ContactInfo(v.Key, v.Value));
// client's friends list and ignore list limit
if (contactList.Contacts.Count >= (((flags & SocialFlag.Friend) != 0) ? SocialManager.FriendLimit : SocialManager.IgnoreLimit))
break;
}
player.SendPacket(contactList);
}
bool _HasContact(ObjectGuid guid, SocialFlag flags)
{
var friendInfo = _playerSocialMap.LookupByKey(guid);
if (friendInfo != null)
return friendInfo.Flags.HasAnyFlag(flags);
return false;
}
public bool HasFriend(ObjectGuid friendGuid)
{
return _HasContact(friendGuid, SocialFlag.Friend);
}
public bool HasIgnore(ObjectGuid ignoreGuid)
{
return _HasContact(ignoreGuid, SocialFlag.Ignored);
}
ObjectGuid GetPlayerGUID() { return m_playerGUID; }
public void SetPlayerGUID(ObjectGuid guid) { m_playerGUID = guid; }
public Dictionary<ObjectGuid, FriendInfo> _playerSocialMap = new Dictionary<ObjectGuid, FriendInfo>();
ObjectGuid m_playerGUID;
}
public class FriendInfo
{
public FriendInfo()
{
Status = FriendStatus.Offline;
Note = "";
}
public FriendInfo(ObjectGuid accountGuid, SocialFlag flags, string note)
{
WowAccountGuid = accountGuid;
Status = FriendStatus.Offline;
Flags = flags;
Note = note;
}
public ObjectGuid WowAccountGuid;
public FriendStatus Status;
public SocialFlag Flags;
public uint Area;
public uint Level;
public Class Class;
public string Note;
}
public enum FriendStatus
{
Offline = 0x00,
Online = 0x01,
AFK = 0x02,
DND = 0x04,
RAF = 0x08
}
public enum SocialFlag
{
Friend = 0x01,
Ignored = 0x02,
Muted = 0x04, // guessed
Unk = 0x08, // Unknown - does not appear to be RaF
All = Friend | Ignored | Muted
}
public enum FriendsResult
{
DbError = 0x00,
ListFull = 0x01,
Online = 0x02,
Offline = 0x03,
NotFound = 0x04,
Removed = 0x05,
AddedOnline = 0x06,
AddedOffline = 0x07,
Already = 0x08,
Self = 0x09,
Enemy = 0x0a,
IgnoreFull = 0x0b,
IgnoreSelf = 0x0c,
IgnoreNotFound = 0x0d,
IgnoreAlready = 0x0e,
IgnoreAdded = 0x0f,
IgnoreRemoved = 0x10,
IgnoreAmbiguous = 0x11, // That Name Is Ambiguous, Type More Of The Player'S Server Name
MuteFull = 0x12,
MuteSelf = 0x13,
MuteNotFound = 0x14,
MuteAlready = 0x15,
MuteAdded = 0x16,
MuteRemoved = 0x17,
MuteAmbiguous = 0x18, // That Name Is Ambiguous, Type More Of The Player'S Server Name
Unk1 = 0x19, // no message at client
Unk2 = 0x1A,
Unk3 = 0x1B,
Unknown = 0x1C // Unknown friend response from server
}
}
+186
View File
@@ -0,0 +1,186 @@
/*
* Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Framework.Constants;
using Game.Network.Packets;
namespace Game.Entities
{
public class TradeData
{
public TradeData(Player player, Player trader)
{
m_player = player;
m_trader = trader;
m_clientStateIndex = 1;
m_serverStateIndex = 1;
}
public TradeData GetTraderData()
{
return m_trader.GetTradeData();
}
public Item GetItem(TradeSlots slot)
{
return !m_items[(int)slot].IsEmpty() ? m_player.GetItemByGuid(m_items[(int)slot]) : null;
}
public bool HasItem(ObjectGuid itemGuid)
{
for (byte i = 0; i < (byte)TradeSlots.Count; ++i)
if (m_items[i] == itemGuid)
return true;
return false;
}
public TradeSlots GetTradeSlotForItem(ObjectGuid itemGuid)
{
for (TradeSlots i = 0; i < TradeSlots.Count; ++i)
if (m_items[(int)i] == itemGuid)
return i;
return TradeSlots.Invalid;
}
public Item GetSpellCastItem()
{
return !m_spellCastItem.IsEmpty() ? m_player.GetItemByGuid(m_spellCastItem) : null;
}
public void SetItem(TradeSlots slot, Item item, bool update = false)
{
ObjectGuid itemGuid = item ? item.GetGUID() : ObjectGuid.Empty;
if (m_items[(int)slot] == itemGuid && !update)
return;
m_items[(int)slot] = itemGuid;
SetAccepted(false);
GetTraderData().SetAccepted(false);
UpdateServerStateIndex();
Update();
// need remove possible trader spell applied to changed item
if (slot == TradeSlots.NonTraded)
GetTraderData().SetSpell(0);
// need remove possible player spell applied (possible move reagent)
SetSpell(0);
}
public uint GetSpell() { return m_spell; }
public void SetSpell(uint spell_id, Item castItem = null)
{
ObjectGuid itemGuid = castItem ? castItem.GetGUID() : ObjectGuid.Empty;
if (m_spell == spell_id && m_spellCastItem == itemGuid)
return;
m_spell = spell_id;
m_spellCastItem = itemGuid;
SetAccepted(false);
GetTraderData().SetAccepted(false);
UpdateServerStateIndex();
Update(true); // send spell info to item owner
Update(false); // send spell info to caster self
}
public void SetMoney(ulong money)
{
if (m_money == money)
return;
if (!m_player.HasEnoughMoney(money))
{
TradeStatusPkt info = new TradeStatusPkt();
info.Status = TradeStatus.Failed;
info.BagResult = InventoryResult.NotEnoughMoney;
m_player.GetSession().SendTradeStatus(info);
return;
}
m_money = money;
SetAccepted(false);
GetTraderData().SetAccepted(false);
UpdateServerStateIndex();
Update(true);
}
void Update(bool forTarget = true)
{
if (forTarget)
m_trader.GetSession().SendUpdateTrade(true); // player state for trader
else
m_player.GetSession().SendUpdateTrade(false); // player state for player
}
public void SetAccepted(bool state, bool crosssend = false)
{
m_accepted = state;
if (!state)
{
TradeStatusPkt info = new TradeStatusPkt();
info.Status = TradeStatus.Unaccepted;
if (crosssend)
m_trader.GetSession().SendTradeStatus(info);
else
m_player.GetSession().SendTradeStatus(info);
}
}
public Player GetTrader() { return m_trader; }
public bool HasSpellCastItem() { return !m_spellCastItem.IsEmpty(); }
public ulong GetMoney() { return m_money; }
public bool IsAccepted() { return m_accepted; }
public bool IsInAcceptProcess() { return m_acceptProccess; }
public void SetInAcceptProcess(bool state) { m_acceptProccess = state; }
public uint GetClientStateIndex() { return m_clientStateIndex; }
public void UpdateClientStateIndex() { ++m_clientStateIndex; }
public uint GetServerStateIndex() { return m_serverStateIndex; }
public void UpdateServerStateIndex() { m_serverStateIndex = RandomHelper.Rand32(); }
Player m_player;
Player m_trader;
bool m_accepted;
bool m_acceptProccess;
ulong m_money;
uint m_spell;
ObjectGuid m_spellCastItem;
ObjectGuid[] m_items = new ObjectGuid[(int)TradeSlots.Count];
uint m_clientStateIndex;
uint m_serverStateIndex;
}
}