From 0707f9b37784cce4ed5a0d9056ef3ec5cc985fc6 Mon Sep 17 00:00:00 2001 From: hondacrx Date: Thu, 24 Aug 2017 18:01:44 -0400 Subject: [PATCH] Fixed appearance validation Implemented proper facial hair validation Implemented transmog Set fix interaction of spells like Shadowmeld with Threat reducing effects --- Framework/Collections/LinkedListElement.cs | 14 +- Framework/Constants/AchievementConst.cs | 4 +- Framework/Constants/CliDBConst.cs | 18 +- Framework/Constants/ConditionConst.cs | 2 +- Framework/Constants/GuildConst.cs | 4 +- Framework/Constants/PlayerConst.cs | 2 +- Framework/Constants/Spells/SpellAuraConst.cs | 2 +- .../Database/Databases/CharacterDatabase.cs | 24 +- .../Database/Databases/HotfixDatabase.cs | 40 +- Framework/Dynamic/Reference.cs | 9 +- Game/AI/CoreAI/GameObjectAI.cs | 2 + Game/AI/CoreAI/PetAI.cs | 3 +- Game/Accounts/AccountManager.cs | 2 +- Game/Achievements/CriteriaHandler.cs | 47 +- Game/AuctionHouse/AuctionManager.cs | 47 +- Game/BattleGrounds/BattleGround.cs | 38 +- Game/BattleGrounds/BattleGroundManager.cs | 7 +- Game/BattleGrounds/Zones/ArathiBasin.cs | 158 +- Game/BattleGrounds/Zones/EyeofStorm.cs | 4 +- Game/BattleGrounds/Zones/StrandofAncients.cs | 1617 ++++++++++++++++- Game/BattleGrounds/Zones/WarsongGluch.cs | 2 +- Game/Chat/Commands/ListCommands.cs | 8 +- Game/Chat/Commands/MiscCommands.cs | 2 +- Game/Chat/Commands/ModifyCommands.cs | 31 +- Game/Chat/Commands/ReloadCommand.cs | 2 + Game/Chat/Commands/SendCommands.cs | 2 +- Game/Combat/Events.cs | 28 +- Game/Combat/HostileRegManager.cs | 54 +- Game/Conditions/ConditionManager.cs | 2 +- Game/DataStorage/CliDB.cs | 12 + Game/DataStorage/DB2Manager.cs | 92 +- Game/DataStorage/Structs/C_Records.cs | 27 +- Game/DataStorage/Structs/T_Records.cs | 34 + Game/Entities/Creature/Creature.cs | 6 +- Game/Entities/Item/Item.cs | 29 +- Game/Entities/Object/WorldObject.cs | 8 + Game/Entities/Player/CollectionManager.cs | 61 + Game/Entities/Player/Player.Achievement.cs | 2 + Game/Entities/Player/Player.DB.cs | 6 +- Game/Entities/Player/Player.Fields.cs | 1 + Game/Entities/Player/Player.Items.cs | 17 +- Game/Entities/Player/Player.Quest.cs | 44 +- Game/Entities/Player/Player.Spells.cs | 2 +- Game/Entities/Player/Player.cs | 49 +- Game/Entities/StatSystem.cs | 33 +- Game/Entities/Unit/Unit.Combat.cs | 18 +- Game/Entities/Unit/Unit.Fields.cs | 2 - Game/Entities/Unit/Unit.Spells.cs | 11 +- Game/Globals/ObjectManager.cs | 39 +- Game/Guilds/Guild.cs | 94 +- Game/Handlers/AuctionHandler.cs | 12 +- Game/Handlers/CharacterHandler.cs | 10 + Game/Handlers/ItemHandler.cs | 14 +- Game/Handlers/MailHandler.cs | 3 +- Game/Handlers/NPCHandler.cs | 26 +- Game/Maps/Instances/InstanceScript.cs | 4 +- Game/Maps/Map.cs | 17 + Game/OutdoorPVP/Zones/HellfirePeninsulaPvP.cs | 154 +- Game/Quest/Quest.cs | 4 +- Game/Quest/QuestObjectiveCriteriaManager.cs | 327 ++++ Game/Server/WorldConfig.cs | 2 - Game/Server/WorldManager.cs | 3 + Game/Spells/Spell.cs | 4 +- Game/Spells/SpellEffects.cs | 12 + Game/Spells/SpellInfo.cs | 2 +- Game/Spells/SpellManager.cs | 6 +- .../Northrend/DraktharonKeep/BossTrollgore.cs | 2 +- Scripts/World/Achievements.cs | 5 +- Scripts/World/GameObjects.cs | 119 ++ Scripts/World/NpcProfessions.cs | 104 -- WorldServer/WorldServer.conf.dist | 8 + 71 files changed, 2968 insertions(+), 632 deletions(-) create mode 100644 Game/Quest/QuestObjectiveCriteriaManager.cs diff --git a/Framework/Collections/LinkedListElement.cs b/Framework/Collections/LinkedListElement.cs index 39818842b..001ae7a27 100644 --- a/Framework/Collections/LinkedListElement.cs +++ b/Framework/Collections/LinkedListElement.cs @@ -38,13 +38,13 @@ namespace Framework.Collections public void delink() { - if (isInList()) - { - iNext.iPrev = iPrev; - iPrev.iNext = iNext; - iNext = null; - iPrev = null; - } + if (!isInList()) + return; + + iNext.iPrev = iPrev; + iPrev.iNext = iNext; + iNext = null; + iPrev = null; } public void insertBefore(LinkedListElement pElem) diff --git a/Framework/Constants/AchievementConst.cs b/Framework/Constants/AchievementConst.cs index 56830c440..4334b0049 100644 --- a/Framework/Constants/AchievementConst.cs +++ b/Framework/Constants/AchievementConst.cs @@ -76,7 +76,8 @@ namespace Framework.Constants Player = 0x1, Account = 0x2, Guild = 0x4, - Scenario = 0x8 + Scenario = 0x8, + QuestObjective = 0x10 } public enum CriteriaCondition @@ -398,6 +399,7 @@ namespace Framework.Constants // 202 - 0 criterias (Legion - 23420) CompleteWorldQuest = 203, // 204 - Special criteria type to award players for some external events? Comes with what looks like an identifier, so guessing it's not unique. + TransmogSetUnlocked = 205, TotalTypes = 208 } diff --git a/Framework/Constants/CliDBConst.cs b/Framework/Constants/CliDBConst.cs index 1fd6e29bd..80d883f65 100644 --- a/Framework/Constants/CliDBConst.cs +++ b/Framework/Constants/CliDBConst.cs @@ -899,6 +899,20 @@ namespace Framework.Constants Max } + public enum CharBaseSectionVariation : byte + { + Skin = 0, + Face = 1, + FacialHair = 2, + Hair = 3, + Underwear = 4, + CustomDisplay1 = 5, + CustomDisplay2 = 6, + CustomDisplay3 = 7, + + Max + } + public enum CharSectionFlags { Player = 0x01, @@ -923,7 +937,9 @@ namespace Framework.Constants CustomDisplay2LowRes = 12, CustomDisplay2 = 13, CustomDisplay3LowRes = 14, - CustomDisplay3 = 15 + CustomDisplay3 = 15, + + Max } public enum ChrSpecializationFlag diff --git a/Framework/Constants/ConditionConst.cs b/Framework/Constants/ConditionConst.cs index f6bee3867..de012720a 100644 --- a/Framework/Constants/ConditionConst.cs +++ b/Framework/Constants/ConditionConst.cs @@ -92,7 +92,7 @@ namespace Framework.Constants CreatureTemplateVehicle = 16, Spell = 17, SpellClickEvent = 18, - QuestAccept = 19, + QuestAvailable = 19, // Condition source type 20 unused VehicleSpell = 21, SmartEvent = 22, diff --git a/Framework/Constants/GuildConst.cs b/Framework/Constants/GuildConst.cs index 37a887bbd..0f77b2df7 100644 --- a/Framework/Constants/GuildConst.cs +++ b/Framework/Constants/GuildConst.cs @@ -23,8 +23,8 @@ namespace Framework.Constants public const int MaxBankSlots = 98; public const int BankMoneyLogsTab = 100; - public const int WithdrawMoneyUnlimited = -1; - public const int WithdrawSlotUnlimited = -1; + public const uint WithdrawMoneyUnlimited = 0xFFFFFFFF; + public const uint WithdrawSlotUnlimited = 0xFFFFFFFF; public const uint EventLogGuidUndefined = 0xFFFFFFFF; public const uint ChallengesTypes = 6; diff --git a/Framework/Constants/PlayerConst.cs b/Framework/Constants/PlayerConst.cs index 9d5d0c632..cfdadd44d 100644 --- a/Framework/Constants/PlayerConst.cs +++ b/Framework/Constants/PlayerConst.cs @@ -30,7 +30,7 @@ namespace Framework.Constants public const int ReqPrimaryTreeTalents = 31; public const int ExploredZonesSize = 256; - public const long MaxMoneyAmount = long.MaxValue; + public const ulong MaxMoneyAmount = ulong.MaxValue; public const int MaxActionButtons = 132; public const int MaxActionButtonActionValue = 0x00FFFFFF + 1; diff --git a/Framework/Constants/Spells/SpellAuraConst.cs b/Framework/Constants/Spells/SpellAuraConst.cs index 36e517b38..5def98e67 100644 --- a/Framework/Constants/Spells/SpellAuraConst.cs +++ b/Framework/Constants/Spells/SpellAuraConst.cs @@ -132,7 +132,7 @@ namespace Framework.Constants AddPctModifier = 108, AddTargetTrigger = 109, ModPowerRegenPercent = 110, - AddCasterHitTrigger = 111, + InterceptMeleeRangedAttacks = 111, OverrideClassScripts = 112, ModRangedDamageTaken = 113, ModRangedDamageTakenPct = 114, diff --git a/Framework/Database/Databases/CharacterDatabase.cs b/Framework/Database/Databases/CharacterDatabase.cs index 0186f250a..3312a14b1 100644 --- a/Framework/Database/Databases/CharacterDatabase.cs +++ b/Framework/Database/Databases/CharacterDatabase.cs @@ -92,6 +92,8 @@ namespace Framework.Database PrepareStatement(CharStatements.SEL_CHARACTER_SPELL, "SELECT spell, active, disabled FROM character_spell WHERE guid = ?"); PrepareStatement(CharStatements.SEL_CHARACTER_QUESTSTATUS, "SELECT quest, status, timer FROM character_queststatus WHERE guid = ? AND status <> 0"); PrepareStatement(CharStatements.SEL_CHARACTER_QUESTSTATUS_OBJECTIVES, "SELECT quest, objective, data FROM character_queststatus_objectives WHERE guid = ?"); + PrepareStatement(CharStatements.SEL_CHARACTER_QUESTSTATUS_OBJECTIVES_CRITERIA, "SELECT questObjectiveId FROM character_queststatus_objectives_criteria WHERE guid = ?"); + PrepareStatement(CharStatements.SEL_CHARACTER_QUESTSTATUS_OBJECTIVES_CRITERIA_PROGRESS, "SELECT criteriaId, counter, date FROM character_queststatus_objectives_criteria_progress WHERE guid = ?"); PrepareStatement(CharStatements.SEL_CHARACTER_QUESTSTATUS_DAILY, "SELECT quest, time FROM character_queststatus_daily WHERE guid = ?"); PrepareStatement(CharStatements.SEL_CHARACTER_QUESTSTATUS_WEEKLY, "SELECT quest FROM character_queststatus_weekly WHERE guid = ?"); @@ -249,8 +251,9 @@ namespace Framework.Database PrepareStatement(CharStatements.UPD_GUILD_RANK_BANK_MONEY, "UPDATE guild_rank SET BankMoneyPerDay = ? WHERE rid = ? AND guildid = ?"); // 0: uint32, 1: uint8, 2: uint32 PrepareStatement(CharStatements.UPD_GUILD_BANK_TAB_TEXT, "UPDATE guild_bank_tab SET TabText = ? WHERE guildid = ? AND TabId = ?"); // 0: string, 1: uint32, 2: uint8 - PrepareStatement(CharStatements.INS_GUILD_MEMBER_WITHDRAW, "INSERT INTO guild_member_withdraw (guid, tab0, tab1, tab2, tab3, tab4, tab5, tab6, tab7, money) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) " + - "ON DUPLICATE KEY UPDATE tab0 = VALUES (tab0), tab1 = VALUES (tab1), tab2 = VALUES (tab2), tab3 = VALUES (tab3), tab4 = VALUES (tab4), tab5 = VALUES (tab5), tab6 = VALUES (tab6), tab7 = VALUES (tab7), money = VALUES (money)"); + PrepareStatement(CharStatements.INS_GUILD_MEMBER_WITHDRAW_TABS, "INSERT INTO guild_member_withdraw (guid, tab0, tab1, tab2, tab3, tab4, tab5, tab6, tab7) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) " + + "ON DUPLICATE KEY UPDATE tab0 = VALUES (tab0), tab1 = VALUES (tab1), tab2 = VALUES (tab2), tab3 = VALUES (tab3), tab4 = VALUES (tab4), tab5 = VALUES (tab5), tab6 = VALUES (tab6), tab7 = VALUES (tab7)"); + PrepareStatement(CharStatements.INS_GUILD_MEMBER_WITHDRAW_MONEY, "INSERT INTO guild_member_withdraw (guid, money) VALUES (?, ?) ON DUPLICATE KEY UPDATE money = VALUES (money)"); PrepareStatement(CharStatements.DEL_GUILD_MEMBER_WITHDRAW, "TRUNCATE guild_member_withdraw"); // 0: uint32, 1: uint32, 2: uint32 @@ -543,6 +546,9 @@ namespace Framework.Database PrepareStatement(CharStatements.UPD_CHAR_TAXIMASK, "UPDATE characters SET taximask = ? WHERE guid = ?"); PrepareStatement(CharStatements.DEL_CHAR_QUESTSTATUS, "DELETE FROM character_queststatus WHERE guid = ?"); PrepareStatement(CharStatements.DEL_CHAR_QUESTSTATUS_OBJECTIVES, "DELETE FROM character_queststatus_objectives WHERE guid = ?"); + PrepareStatement(CharStatements.DEL_CHAR_QUESTSTATUS_OBJECTIVES_CRITERIA, "DELETE FROM character_queststatus_objectives_criteria WHERE guid = ?"); + PrepareStatement(CharStatements.DEL_CHAR_QUESTSTATUS_OBJECTIVES_CRITERIA_PROGRESS, "DELETE FROM character_queststatus_objectives_criteria_progress WHERE guid = ?"); + PrepareStatement(CharStatements.DEL_CHAR_QUESTSTATUS_OBJECTIVES_CRITERIA_PROGRESS_BY_CRITERIA, "DELETE FROM character_queststatus_objectives_criteria_progress WHERE guid = ? AND criteriaId = ?"); PrepareStatement(CharStatements.DEL_CHAR_SOCIAL_BY_GUID, "DELETE FROM character_social WHERE guid = ?"); PrepareStatement(CharStatements.DEL_CHAR_SOCIAL_BY_FRIEND, "DELETE FROM character_social WHERE friend = ?"); PrepareStatement(CharStatements.DEL_CHAR_ACHIEVEMENT_BY_ACHIEVEMENT, "DELETE FROM character_achievement WHERE achievement = ? AND guid = ?"); @@ -590,11 +596,14 @@ namespace Framework.Database PrepareStatement(CharStatements.DEL_CHAR_QUESTSTATUS_BY_QUEST, "DELETE FROM character_queststatus WHERE guid = ? AND quest = ?"); PrepareStatement(CharStatements.REP_CHAR_QUESTSTATUS_OBJECTIVES, "REPLACE INTO character_queststatus_objectives (guid, quest, objective, data) VALUES (?, ?, ?, ?)"); PrepareStatement(CharStatements.DEL_CHAR_QUESTSTATUS_OBJECTIVES_BY_QUEST, "DELETE FROM character_queststatus_objectives WHERE guid = ? AND quest = ?"); + PrepareStatement(CharStatements.INS_CHAR_QUESTSTATUS_OBJECTIVES_CRITERIA, "INSERT INTO character_queststatus_objectives_criteria (guid, questObjectiveId) VALUES (?, ?)"); + PrepareStatement(CharStatements.INS_CHAR_QUESTSTATUS_OBJECTIVES_CRITERIA_PROGRESS, "INSERT INTO character_queststatus_objectives_criteria_progress (guid, criteriaId, counter, date) VALUES (?, ?, ?, ?)"); PrepareStatement(CharStatements.INS_CHAR_QUESTSTATUS_REWARDED, "INSERT IGNORE INTO character_queststatus_rewarded (guid, quest, active) VALUES (?, ?, 1)"); PrepareStatement(CharStatements.DEL_CHAR_QUESTSTATUS_REWARDED_BY_QUEST, "DELETE FROM character_queststatus_rewarded WHERE guid = ? AND quest = ?"); PrepareStatement(CharStatements.UPD_CHAR_QUESTSTATUS_REWARDED_FACTION_CHANGE, "UPDATE character_queststatus_rewarded SET quest = ? WHERE quest = ? AND guid = ?"); PrepareStatement(CharStatements.UPD_CHAR_QUESTSTATUS_REWARDED_ACTIVE, "UPDATE character_queststatus_rewarded SET active = 1 WHERE guid = ?"); PrepareStatement(CharStatements.UPD_CHAR_QUESTSTATUS_REWARDED_ACTIVE_BY_QUEST, "UPDATE character_queststatus_rewarded SET active = 0 WHERE quest = ? AND guid = ?"); + PrepareStatement(CharStatements.DEL_INVALID_QUEST_PROGRESS_CRITERIA, "DELETE FROM character_queststatus_objectives_criteria WHERE questObjectiveId = ?"); PrepareStatement(CharStatements.DEL_CHAR_SKILL_BY_SKILL, "DELETE FROM character_skills WHERE guid = ? AND skill = ?"); PrepareStatement(CharStatements.INS_CHAR_SKILLS, "INSERT INTO character_skills (guid, skill, value, max) VALUES (?, ?, ?, ?)"); PrepareStatement(CharStatements.UPD_CHAR_SKILLS, "UPDATE character_skills SET value = ?, max = ? WHERE guid = ? AND skill = ?"); @@ -778,6 +787,8 @@ namespace Framework.Database SEL_CHARACTER_QUESTSTATUS, SEL_CHARACTER_QUESTSTATUS_OBJECTIVES, + SEL_CHARACTER_QUESTSTATUS_OBJECTIVES_CRITERIA, + SEL_CHARACTER_QUESTSTATUS_OBJECTIVES_CRITERIA_PROGRESS, SEL_CHARACTER_QUESTSTATUS_DAILY, SEL_CHARACTER_QUESTSTATUS_WEEKLY, SEL_CHARACTER_QUESTSTATUS_MONTHLY, @@ -913,7 +924,8 @@ namespace Framework.Database UPD_GUILD_BANK_MONEY, UPD_GUILD_RANK_BANK_MONEY, UPD_GUILD_BANK_TAB_TEXT, - INS_GUILD_MEMBER_WITHDRAW, + INS_GUILD_MEMBER_WITHDRAW_TABS, + INS_GUILD_MEMBER_WITHDRAW_MONEY, DEL_GUILD_MEMBER_WITHDRAW, SEL_CHAR_DATA_FOR_GUILD, DEL_GUILD_ACHIEVEMENT, @@ -1157,6 +1169,9 @@ namespace Framework.Database UPD_CHAR_TAXIMASK, DEL_CHAR_QUESTSTATUS, DEL_CHAR_QUESTSTATUS_OBJECTIVES, + DEL_CHAR_QUESTSTATUS_OBJECTIVES_CRITERIA, + DEL_CHAR_QUESTSTATUS_OBJECTIVES_CRITERIA_PROGRESS, + DEL_CHAR_QUESTSTATUS_OBJECTIVES_CRITERIA_PROGRESS_BY_CRITERIA, DEL_CHAR_SOCIAL_BY_GUID, DEL_CHAR_SOCIAL_BY_FRIEND, DEL_CHAR_ACHIEVEMENT_BY_ACHIEVEMENT, @@ -1204,11 +1219,14 @@ namespace Framework.Database DEL_CHAR_QUESTSTATUS_BY_QUEST, REP_CHAR_QUESTSTATUS_OBJECTIVES, DEL_CHAR_QUESTSTATUS_OBJECTIVES_BY_QUEST, + INS_CHAR_QUESTSTATUS_OBJECTIVES_CRITERIA, + INS_CHAR_QUESTSTATUS_OBJECTIVES_CRITERIA_PROGRESS, INS_CHAR_QUESTSTATUS_REWARDED, DEL_CHAR_QUESTSTATUS_REWARDED_BY_QUEST, UPD_CHAR_QUESTSTATUS_REWARDED_FACTION_CHANGE, UPD_CHAR_QUESTSTATUS_REWARDED_ACTIVE, UPD_CHAR_QUESTSTATUS_REWARDED_ACTIVE_BY_QUEST, + DEL_INVALID_QUEST_PROGRESS_CRITERIA, DEL_CHAR_SKILL_BY_SKILL, INS_CHAR_SKILLS, UPD_CHAR_SKILLS, diff --git a/Framework/Database/Databases/HotfixDatabase.cs b/Framework/Database/Databases/HotfixDatabase.cs index 28b67e3e0..d77c2b3cc 100644 --- a/Framework/Database/Databases/HotfixDatabase.cs +++ b/Framework/Database/Databases/HotfixDatabase.cs @@ -123,9 +123,16 @@ namespace Framework.Database "EmoteDelay3, UnkEmoteID, Language, Type, SoundID1, SoundID2, PlayerConditionID FROM broadcast_text ORDER BY ID DESC"); PrepareStatement(HotfixStatements.SEL_BROADCAST_TEXT_LOCALE, "SELECT ID, MaleText_lang, FemaleText_lang FROM broadcast_text_locale WHERE locale = ?"); + // CharacterFacialHairStyles.db2 + PrepareStatement(HotfixStatements.SEL_CHARACTER_FACIAL_HAIR_STYLES, "SELECT ID, Geoset1, Geoset2, Geoset3, Geoset4, Geoset5, RaceID, SexID, VariationID" + + " FROM character_facial_hair_styles ORDER BY ID DESC"); + + // CharBaseSection.db2 + PrepareStatement(HotfixStatements.SEL_CHAR_BASE_SECTION, "SELECT ID, Variation, ResolutionVariation, Resolution FROM char_base_section ORDER BY ID DESC"); + // CharSections.db2 - PrepareStatement(HotfixStatements.SEL_CHAR_SECTIONS, "SELECT ID, TextureFileDataID1, TextureFileDataID2, TextureFileDataID3, Flags, Race, Gender, GenType, " + - "Type, Color FROM char_sections ORDER BY ID DESC"); + PrepareStatement(HotfixStatements.SEL_CHAR_SECTIONS, "SELECT ID, TextureFileDataID1, TextureFileDataID2, TextureFileDataID3, Flags, RaceID, SexID, " + + "BaseSection, VariationIndex, ColorIndex FROM char_sections ORDER BY ID DESC"); // CharStartOutfit.db2 PrepareStatement(HotfixStatements.SEL_CHAR_START_OUTFIT, "SELECT ID, ItemID1, ItemID2, ItemID3, ItemID4, ItemID5, ItemID6, ItemID7, ItemID8, ItemID9, " + @@ -926,6 +933,21 @@ namespace Framework.Database PrepareStatement(HotfixStatements.SEL_TOY, "SELECT ItemID, Description, Flags, CategoryFilter, ID FROM toy ORDER BY ID DESC"); PrepareStatement(HotfixStatements.SEL_TOY_LOCALE, "SELECT ID, Description_lang FROM toy_locale WHERE locale = ?"); + // TransmogHoliday.db2 + PrepareStatement(HotfixStatements.SEL_TRANSMOG_HOLIDAY, "SELECT ID, HolidayID FROM transmog_holiday ORDER BY ID DESC"); + + // TransmogSet.db2 + PrepareStatement(HotfixStatements.SEL_TRANSMOG_SET, "SELECT Name, BaseSetID, UIOrder, ExpansionID, ID, Flags, QuestID, ClassMask, ItemNameDescriptionID, " + + "TransmogSetGroupID FROM transmog_set ORDER BY ID DESC"); + PrepareStatement(HotfixStatements.SEL_TRANSMOG_SET_LOCALE, "SELECT ID, Name_lang FROM transmog_set_locale WHERE locale = ?"); + + // TransmogSetGroup.db2 + PrepareStatement(HotfixStatements.SEL_TRANSMOG_SET_GROUP, "SELECT Label, ID FROM transmog_set_group ORDER BY ID DESC"); + PrepareStatement(HotfixStatements.SEL_TRANSMOG_SET_GROUP_LOCALE, "SELECT ID, Label_lang FROM transmog_set_group_locale WHERE locale = ?"); + + // TransmogSetItem.db2 + PrepareStatement(HotfixStatements.SEL_TRANSMOG_SET_ITEM, "SELECT ID, TransmogSetID, ItemModifiedAppearanceID, Flags FROM transmog_set_item ORDER BY ID DESC"); + // TransportAnimation.db2 PrepareStatement(HotfixStatements.SEL_TRANSPORT_ANIMATION, "SELECT ID, TransportID, TimeIndex, PosX, PosY, PosZ, SequenceID FROM transport_animation" + " ORDER BY ID DESC"); @@ -1050,6 +1072,10 @@ namespace Framework.Database SEL_BROADCAST_TEXT, SEL_BROADCAST_TEXT_LOCALE, + SEL_CHARACTER_FACIAL_HAIR_STYLES, + + SEL_CHAR_BASE_SECTION, + SEL_CHAR_SECTIONS, SEL_CHAR_START_OUTFIT, @@ -1462,6 +1488,16 @@ namespace Framework.Database SEL_TOY, SEL_TOY_LOCALE, + SEL_TRANSMOG_HOLIDAY, + + SEL_TRANSMOG_SET, + SEL_TRANSMOG_SET_LOCALE, + + SEL_TRANSMOG_SET_GROUP, + SEL_TRANSMOG_SET_GROUP_LOCALE, + + SEL_TRANSMOG_SET_ITEM, + SEL_TRANSPORT_ANIMATION, SEL_TRANSPORT_ROTATION, diff --git a/Framework/Dynamic/Reference.cs b/Framework/Dynamic/Reference.cs index 68bbfe295..bd3182e98 100644 --- a/Framework/Dynamic/Reference.cs +++ b/Framework/Dynamic/Reference.cs @@ -94,12 +94,9 @@ namespace Framework.Dynamic public void clearReferences() { - LinkedListElement curr; - while ((curr = base.GetFirstElement()) != null) - { - ((Reference)curr).invalidate(); - curr.delink(); // the delink might be already done by invalidate(), but doing it here again does not hurt and insures an empty list - } + Reference refe; + while ((refe = getFirst()) != null) + refe.invalidate(); } } } diff --git a/Game/AI/CoreAI/GameObjectAI.cs b/Game/AI/CoreAI/GameObjectAI.cs index 409ac4ba4..230979b49 100644 --- a/Game/AI/CoreAI/GameObjectAI.cs +++ b/Game/AI/CoreAI/GameObjectAI.cs @@ -26,6 +26,7 @@ namespace Game.AI { go = g; _scheduler = new TaskScheduler(); + _events = new EventMap(); } public virtual void UpdateAI(uint diff) { } @@ -56,6 +57,7 @@ namespace Game.AI public virtual void EventInform(uint eventId) { } protected TaskScheduler _scheduler; + protected EventMap _events; public GameObject go; } diff --git a/Game/AI/CoreAI/PetAI.cs b/Game/AI/CoreAI/PetAI.cs index c68ee938b..4cc9999a4 100644 --- a/Game/AI/CoreAI/PetAI.cs +++ b/Game/AI/CoreAI/PetAI.cs @@ -218,8 +218,7 @@ namespace Game.AI int index = RandomHelper.IRand(0, targetSpellStore.Count - 1); var tss = targetSpellStore[index]; - Spell spell = tss.Item2; - Unit target = tss.Item1; + (Unit target, Spell spell) = tss; targetSpellStore.RemoveAt(index); diff --git a/Game/Accounts/AccountManager.cs b/Game/Accounts/AccountManager.cs index 45b4a60c0..68f59f362 100644 --- a/Game/Accounts/AccountManager.cs +++ b/Game/Accounts/AccountManager.cs @@ -428,7 +428,7 @@ namespace Game public void UpdateAccountAccess(RBACData rbac, uint accountId, byte securityLevel, int realmId) { - if (rbac != null && securityLevel == rbac.GetSecurityLevel()) + if (rbac != null && securityLevel != rbac.GetSecurityLevel()) rbac.SetSecurityLevel(securityLevel); PreparedStatement stmt; diff --git a/Game/Achievements/CriteriaHandler.cs b/Game/Achievements/CriteriaHandler.cs index 768d32238..703fcf942 100644 --- a/Game/Achievements/CriteriaHandler.cs +++ b/Game/Achievements/CriteriaHandler.cs @@ -332,6 +332,16 @@ namespace Game.Achievements case CriteriaTypes.ReachGuildLevel: SetCriteriaProgress(criteria, miscValue1, referencePlayer); break; + case CriteriaTypes.TransmogSetUnlocked: + if (miscValue1 != criteria.Entry.Asset) + continue; + SetCriteriaProgress(criteria, 1, referencePlayer, ProgressType.Accumulate); + break; + case CriteriaTypes.AppearanceUnlockedBySlot: + if (miscValue2 == 0 /*login case*/ || miscValue1 != criteria.Entry.Asset) + continue; + SetCriteriaProgress(criteria, 1, referencePlayer, ProgressType.Accumulate); + break; // FIXME: not triggered in code as result, need to implement case CriteriaTypes.CompleteRaid: case CriteriaTypes.PlayArena: @@ -749,6 +759,7 @@ namespace Game.Achievements case CriteriaTypes.Currency: case CriteriaTypes.PlaceGarrisonBuilding: case CriteriaTypes.OwnBattlePetCount: + case CriteriaTypes.AppearanceUnlockedBySlot: return progress.Counter >= requiredAmount; case CriteriaTypes.CompleteAchievement: case CriteriaTypes.CompleteQuest: @@ -758,6 +769,7 @@ namespace Game.Achievements case CriteriaTypes.OwnBattlePet: case CriteriaTypes.HonorLevelReached: case CriteriaTypes.PrestigeReached: + case CriteriaTypes.TransmogSetUnlocked: return progress.Counter >= 1; case CriteriaTypes.LearnSkillLevel: return progress.Counter >= (requiredAmount * 75); @@ -1444,19 +1456,34 @@ namespace Game.Achievements scenarioCriteriaTreeIds[scenarioStep.CriteriaTreeID] = scenarioStep; } + Dictionary questObjectiveCriteriaTreeIds = new Dictionary(); + foreach (var pair in Global.ObjectMgr.GetQuestTemplates()) + { + foreach (QuestObjective objective in pair.Value.Objectives) + { + if (objective.Type != QuestObjectiveType.CriteriaTree) + continue; + + if (objective.ObjectID != 0) + questObjectiveCriteriaTreeIds[(uint)objective.ObjectID] = objective; + } + } + // Load criteria tree nodes foreach (CriteriaTreeRecord tree in CliDB.CriteriaTreeStorage.Values) { // Find linked achievement AchievementRecord achievement = GetEntry(achievementCriteriaTreeIds, tree); ScenarioStepRecord scenarioStep = GetEntry(scenarioCriteriaTreeIds, tree); - if (achievement == null && scenarioStep == null) + QuestObjective questObjective = GetEntry(questObjectiveCriteriaTreeIds, tree); + if (achievement == null && scenarioStep == null && questObjective == null) continue; CriteriaTree criteriaTree = new CriteriaTree(); criteriaTree.ID = tree.Id; criteriaTree.Achievement = achievement; criteriaTree.ScenarioStep = scenarioStep; + criteriaTree.QuestObjective = questObjective; criteriaTree.Entry = tree; _criteriaTrees[criteriaTree.Entry.Id] = criteriaTree; @@ -1491,6 +1518,7 @@ namespace Game.Achievements uint criterias = 0; uint guildCriterias = 0; uint scenarioCriterias = 0; + uint questObjectiveCriterias = 0; foreach (CriteriaRecord criteriaEntry in CliDB.CriteriaStorage.Values) { Contract.Assert(criteriaEntry.Type < CriteriaTypes.TotalTypes, string.Format("CRITERIA_TYPE_TOTAL must be greater than or equal to {0} but is currently equal to {1}", criteriaEntry.Type + 1, CriteriaTypes.TotalTypes)); @@ -1522,6 +1550,8 @@ namespace Game.Achievements } else if (tree.ScenarioStep != null) criteria.FlagsCu |= CriteriaFlagsCu.Scenario; + else if (tree.QuestObjective != null) + criteria.FlagsCu |= CriteriaFlagsCu.QuestObjective; } if (criteria.FlagsCu.HasAnyFlag(CriteriaFlagsCu.Player | CriteriaFlagsCu.Account)) @@ -1542,6 +1572,12 @@ namespace Game.Achievements _scenarioCriteriasByType.Add(criteriaEntry.Type, criteria); } + if (criteria.FlagsCu.HasAnyFlag(CriteriaFlagsCu.QuestObjective)) + { + ++questObjectiveCriterias; + _questObjectiveCriteriasByType.Add(criteriaEntry.Type, criteria); + } + if (criteriaEntry.StartTimer != 0) _criteriasByTimedType.Add(criteriaEntry.StartEvent, criteria); } @@ -1549,7 +1585,7 @@ namespace Game.Achievements foreach (var p in _criteriaTrees) p.Value.Criteria = GetCriteria(p.Value.Entry.CriteriaID); - Log.outInfo(LogFilter.ServerLoading, "Loaded {0} criteria, {1} guild criteria and {2} scenario criteria in {3} ms.", criterias, guildCriterias, scenarioCriterias, Time.GetMSTimeDiffToNow(oldMSTime)); + Log.outInfo(LogFilter.ServerLoading, "Loaded {criterias} criteria, {guildCriterias} guild criteria, {scenarioCriterias} scenario criteria and {questObjectiveCriterias} quest objective criteria in {Time.GetMSTimeDiffToNow(oldMSTime)} ms."); } public void LoadCriteriaData() @@ -1640,6 +1676,11 @@ namespace Game.Achievements return _scenarioCriteriasByType.LookupByKey(type); } + public List GetQuestObjectiveCriteriaByType(CriteriaTypes type) + { + return _questObjectiveCriteriasByType[type]; + } + public List GetCriteriaTreesByCriteria(uint criteriaId) { return _criteriaTreeByCriteria.LookupByKey(criteriaId); @@ -1693,6 +1734,7 @@ namespace Game.Achievements MultiMap _criteriasByType = new MultiMap(); MultiMap _guildCriteriasByType = new MultiMap(); MultiMap _scenarioCriteriasByType = new MultiMap(); + MultiMap _questObjectiveCriteriasByType = new MultiMap(); MultiMap _criteriasByTimedType = new MultiMap(); } @@ -1717,6 +1759,7 @@ namespace Game.Achievements public CriteriaTreeRecord Entry; public AchievementRecord Achievement; public ScenarioStepRecord ScenarioStep; + public QuestObjective QuestObjective; public Criteria Criteria; public List Children = new List(); } diff --git a/Game/AuctionHouse/AuctionManager.cs b/Game/AuctionHouse/AuctionManager.cs index 46b614771..bec1455d5 100644 --- a/Game/AuctionHouse/AuctionManager.cs +++ b/Game/AuctionHouse/AuctionManager.cs @@ -51,7 +51,7 @@ namespace Game return mNeutralAuctions; } - public uint GetAuctionDeposit(AuctionHouseRecord entry, uint time, Item pItem, uint count) + public ulong GetAuctionDeposit(AuctionHouseRecord entry, uint time, Item pItem, uint count) { uint MSV = pItem.GetTemplate().GetSellPrice(); @@ -60,12 +60,12 @@ namespace Game float multiplier = MathFunctions.CalculatePct((float)entry.DepositRate, 3); uint timeHr = (((time / 60) / 60) / 12); - uint deposit = (uint)(((multiplier * MSV * count / 3) * timeHr * 3) * WorldConfig.GetFloatValue(WorldCfg.RateAuctionDeposit)); + ulong deposit = (ulong)(((multiplier * MSV * count / 3) * timeHr * 3) * WorldConfig.GetFloatValue(WorldCfg.RateAuctionDeposit)); - Log.outDebug(LogFilter.Auctionhouse, "MSV: {0}", MSV); - Log.outDebug(LogFilter.Auctionhouse, "Items: {0}", count); - Log.outDebug(LogFilter.Auctionhouse, "Multiplier: {0}", multiplier); - Log.outDebug(LogFilter.Auctionhouse, "Deposit: {0}", deposit); + Log.outDebug(LogFilter.Auctionhouse, $"MSV: {MSV}"); + Log.outDebug(LogFilter.Auctionhouse, $"Items: {count}"); + Log.outDebug(LogFilter.Auctionhouse, $"Multiplier: {multiplier}"); + Log.outDebug(LogFilter.Auctionhouse, $"Deposit: {deposit}"); if (deposit < AH_MINIMUM_DEPOSIT * WorldConfig.GetFloatValue(WorldCfg.RateAuctionDeposit)) return AH_MINIMUM_DEPOSIT * (uint)WorldConfig.GetFloatValue(WorldCfg.RateAuctionDeposit); @@ -110,8 +110,7 @@ namespace Game uint ownerAccId = ObjectManager.GetPlayerAccountIdByGUID(ownerGuid); - Log.outCommand(bidderAccId, "GM {0} (Account: {1}) won item in auction: {2} (Entry: {3} Count: {4}) and pay money: {5}. Original owner {6} (Account: {7})", - bidderName, bidderAccId, item.GetTemplate().GetName(), item.GetEntry(), item.GetCount(), auction.bid, ownerName, ownerAccId); + Log.outCommand(bidderAccId, $"GM {bidderName} (Account: {bidderAccId}) won item in auction: {item.GetTemplate().GetName()} (Entry: {item.GetEntry()} Count: {item.GetCount()}) and pay money: {auction.bid}. Original owner {ownerName} (Account: {ownerAccId})"); } // receiver exist @@ -164,7 +163,7 @@ namespace Game // owner exist if (owner || owner_accId != 0) { - uint profit = auction.bid + auction.deposit - auction.GetAuctionCut(); + ulong profit = auction.bid + auction.deposit - auction.GetAuctionCut(); //FIXME: what do if owner offline if (owner && item) @@ -662,19 +661,19 @@ namespace Game items.Add(auctionItem); } - public uint GetAuctionCut() + public ulong GetAuctionCut() { - int cut = (int)(MathFunctions.CalculatePct(bid, auctionHouseEntry.ConsignmentRate) * WorldConfig.GetFloatValue(WorldCfg.RateAuctionCut)); - return (uint)Math.Max(cut, 0); + long cut = (long)(MathFunctions.CalculatePct(bid, auctionHouseEntry.ConsignmentRate) * WorldConfig.GetFloatValue(WorldCfg.RateAuctionCut)); + return (ulong)Math.Max(cut, 0); } /// /// the sum of outbid is (1% from current bid)*5, if bid is very small, it is 1c /// /// - public uint GetAuctionOutBid() + public ulong GetAuctionOutBid() { - uint outbid = MathFunctions.CalculatePct(bid, 5); + ulong outbid = MathFunctions.CalculatePct(bid, 5); return outbid != 0 ? outbid : 1; } @@ -709,12 +708,12 @@ namespace Game itemEntry = fields.Read(3); itemCount = fields.Read(4); owner = fields.Read(5); - buyout = fields.Read(6); + buyout = fields.Read(6); expire_time = fields.Read(7); bidder = fields.Read(8); - bid = fields.Read(9); - startbid = fields.Read(10); - deposit = fields.Read(11); + bid = fields.Read(9); + startbid = fields.Read(10); + deposit = fields.Read(11); CreatureData auctioneerData = Global.ObjectMgr.GetCreatureData(auctioneer); if (auctioneerData == null) @@ -798,9 +797,9 @@ namespace Game return string.Format("{0}:0:{1}:{2}:{3}", itemEntry, response, Id, itemCount); } - public static string BuildAuctionMailBody(ulong lowGuid, uint bid, uint buyout, uint deposit, uint cut) + public static string BuildAuctionMailBody(ulong lowGuid, ulong bid, ulong buyout, ulong deposit, ulong cut) { - return string.Format("{0}:{1}:{2}:{3}:{4}", lowGuid, bid, buyout, deposit, cut); + return string.Format($"{lowGuid}:{bid}:{buyout}:{deposit}:{cut}"); } // helpers @@ -813,12 +812,12 @@ namespace Game public uint itemEntry; public uint itemCount; public ulong owner; - public uint startbid; //maybe useless - public uint bid; - public uint buyout; + public ulong startbid; //maybe useless + public ulong bid; + public ulong buyout; public long expire_time; public ulong bidder; - public uint deposit; //deposit can be calculated only when creating auction + public ulong deposit; //deposit can be calculated only when creating auction public uint etime; uint houseId; public AuctionHouseRecord auctionHouseEntry; // in AuctionHouse.dbc diff --git a/Game/BattleGrounds/BattleGround.cs b/Game/BattleGrounds/BattleGround.cs index 8a4c2f8de..6da9c4b27 100644 --- a/Game/BattleGrounds/BattleGround.cs +++ b/Game/BattleGrounds/BattleGround.cs @@ -63,11 +63,11 @@ namespace Game.BattleGrounds { // remove objects and creatures // (this is done automatically in mapmanager update, when the instance is reset after the reset time) - foreach (var type in BgCreatures.Keys) - DelCreature(type); + for (var i = 0; i < BgCreatures.Length; ++i) + DelCreature(i); - foreach (var type in BgObjects.Keys) - DelObject(type); + for (var i = 0; i < BgObjects.Length; ++i) + DelObject(i); Global.BattlegroundMgr.RemoveBattleground(GetTypeID(), GetInstanceID()); // unload map @@ -550,7 +550,7 @@ namespace Game.BattleGrounds } } - void SendChatMessage(Creature source, byte textId, WorldObject target = null) + public void SendChatMessage(Creature source, byte textId, WorldObject target = null) { Global.CreatureTextMgr.SendChat(source, textId, target); } @@ -628,6 +628,15 @@ namespace Game.BattleGrounds SendPacketToAll(worldstate); } + public void UpdateWorldState(uint variable, bool value, bool hidden = false) + { + UpdateWorldState worldstate = new UpdateWorldState(); + worldstate.VariableID = variable; + worldstate.Value = value ? 1 : 0; + worldstate.Hidden = hidden; + SendPacketToAll(worldstate); + } + public virtual void EndBattleground(Team winner) { RemoveFromBGFreeSlotQueue(); @@ -1369,7 +1378,7 @@ namespace Game.BattleGrounds public GameObject GetBGObject(int type) { - if (!BgObjects.ContainsKey(type)) + if (BgObjects[type].IsEmpty()) return null; GameObject obj = GetBgMap().GetGameObject(BgObjects[type]); @@ -1381,7 +1390,7 @@ namespace Game.BattleGrounds public Creature GetBGCreature(int type) { - if (!BgCreatures.ContainsKey(type)) + if (BgCreatures[type].IsEmpty()) return null; Creature creature = GetBgMap().GetCreature(BgCreatures[type]); @@ -1470,7 +1479,7 @@ namespace Game.BattleGrounds public bool DelCreature(int type) { - if (!BgCreatures.ContainsKey(type) || BgCreatures[type].IsEmpty()) + if (BgCreatures[type].IsEmpty()) return true; Creature creature = GetBgMap().GetCreature(BgCreatures[type]); @@ -1487,7 +1496,7 @@ namespace Game.BattleGrounds return false; } - bool DelObject(int type) + public bool DelObject(int type) { if (BgObjects[type].IsEmpty()) return true; @@ -1500,8 +1509,7 @@ namespace Game.BattleGrounds BgObjects[type].Clear(); return true; } - Log.outError(LogFilter.Battleground, "Battleground.DelObject: gameobject (type: {0}, {1}) not found for BG (map: {2}, instance id: {3})!", - type, BgObjects[type].ToString(), m_MapId, m_InstanceID); + Log.outError(LogFilter.Battleground, "Battleground.DelObject: gameobject (type: {0}, {1}) not found for BG (map: {2}, instance id: {3})!", type, BgObjects[type].ToString(), m_MapId, m_InstanceID); BgObjects[type].Clear(); return false; } @@ -1581,7 +1589,7 @@ namespace Game.BattleGrounds return; // Change buff type, when buff is used: - int index = BgObjects.Count - 1; + int index = BgObjects.Length - 1; while (index >= 0 && BgObjects[index] != goGuid) index--; if (index < 0) @@ -1716,7 +1724,7 @@ namespace Game.BattleGrounds int GetObjectType(ObjectGuid guid) { - for (int i = 0; i < BgObjects.Count; ++i) + for (int i = 0; i < BgObjects.Length; ++i) if (BgObjects[i] == guid) return i; Log.outError(LogFilter.Battleground, "Battleground.GetObjectType: player used gameobject ({0}) which is not in internal data for BG (map: {1}, instance id: {2}), cheating?", @@ -1980,8 +1988,8 @@ namespace Game.BattleGrounds public BGHonorMode m_HonorMode; public uint[] m_TeamScores = new uint[SharedConst.BGTeamsCount]; - protected Dictionary BgObjects = new Dictionary(); - protected Dictionary BgCreatures = new Dictionary(); + protected ObjectGuid[] BgObjects;// = new Dictionary(); + protected ObjectGuid[] BgCreatures;// = new Dictionary(); public uint[] Buff_Entries = { BattlegroundConst.SpeedBuff, BattlegroundConst.RegenBuff, BattlegroundConst.BerserkerBuff }; diff --git a/Game/BattleGrounds/BattleGroundManager.cs b/Game/BattleGrounds/BattleGroundManager.cs index 56158c7ce..51f261984 100644 --- a/Game/BattleGrounds/BattleGroundManager.cs +++ b/Game/BattleGrounds/BattleGroundManager.cs @@ -24,6 +24,7 @@ using Game.Network.Packets; using System; using System.Collections.Generic; using System.Linq; +using Game.BattleGrounds.Zones; namespace Game.BattleGrounds { @@ -334,9 +335,9 @@ namespace Game.BattleGrounds case BattlegroundTypeId.RL: bg = new RuinsofLordaeronArena(); break; - //case BattlegroundTypeId.SA: - //bg = new BattlegroundSA(); - //break; + case BattlegroundTypeId.SA: + bg = new BgStrandOfAncients(); + break; case BattlegroundTypeId.DS: bg = new DalaranSewersArena(); break; diff --git a/Game/BattleGrounds/Zones/ArathiBasin.cs b/Game/BattleGrounds/Zones/ArathiBasin.cs index 6f980421e..86f463b98 100644 --- a/Game/BattleGrounds/Zones/ArathiBasin.cs +++ b/Game/BattleGrounds/Zones/ArathiBasin.cs @@ -23,7 +23,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; -namespace Game.BattleGrounds +namespace Game.BattleGrounds.Zones { class BgArathiBasin : Battleground { @@ -75,7 +75,7 @@ namespace Game.BattleGrounds else { m_BannerTimers[node].timer = 0; - _CreateBanner(node, (NodeStatus)m_BannerTimers[node].type, m_BannerTimers[node].teamIndex, false); + _CreateBanner(node, (ABNodeStatus)m_BannerTimers[node].type, m_BannerTimers[node].teamIndex, false); } } @@ -92,9 +92,9 @@ namespace Game.BattleGrounds m_prevNodes[node] = m_Nodes[node]; m_Nodes[node] += 2; // burn current contested banner - _DelBanner(node, NodeStatus.Contested, (byte)teamIndex); + _DelBanner(node, ABNodeStatus.Contested, (byte)teamIndex); // create new occupied banner - _CreateBanner(node, NodeStatus.Occupied, teamIndex, true); + _CreateBanner(node, ABNodeStatus.Occupied, teamIndex, true); _SendNodeUpdate(node); _NodeOccupied(node, (teamIndex == 0) ? Team.Alliance : Team.Horde); // Message to chatlog @@ -115,7 +115,7 @@ namespace Game.BattleGrounds } for (int team = 0; team < SharedConst.BGTeamsCount; ++team) - if (m_Nodes[node] == team + NodeStatus.Occupied) + if (m_Nodes[node] == team + ABNodeStatus.Occupied) ++team_points[team]; } @@ -165,9 +165,9 @@ namespace Game.BattleGrounds m_TeamScores[team] = MaxTeamScore; if (team == TeamId.Alliance) - UpdateWorldState(WorldStates.ResourcesAlly, m_TeamScores[team]); + UpdateWorldState(ABWorldStates.ResourcesAlly, m_TeamScores[team]); else if (team == TeamId.Horde) - UpdateWorldState(WorldStates.ResourcesHorde, m_TeamScores[team]); + UpdateWorldState(ABWorldStates.ResourcesHorde, m_TeamScores[team]); // update achievement flags // we increased m_TeamScores[team] so we just need to check if it is 500 more than other teams resources int otherTeam = (team + 1) % SharedConst.BGTeamsCount; @@ -187,16 +187,16 @@ namespace Game.BattleGrounds public override void StartingEventCloseDoors() { // despawn banners, auras and buffs - for (int obj = ObjectType.BannerNeutral; obj < BattlegroundNodes.DynamicNodesCount * 8; ++obj) + for (int obj = ABObjectTypes.BannerNeutral; obj < BattlegroundNodes.DynamicNodesCount * 8; ++obj) SpawnBGObject(obj, BattlegroundConst.RespawnOneDay); for (int i = 0; i < BattlegroundNodes.DynamicNodesCount * 3; ++i) - SpawnBGObject(ObjectType.SpeedbuffStables + i, BattlegroundConst.RespawnOneDay); + SpawnBGObject(ABObjectTypes.SpeedbuffStables + i, BattlegroundConst.RespawnOneDay); // Starting doors - DoorClose(ObjectType.GateA); - DoorClose(ObjectType.GateH); - SpawnBGObject(ObjectType.GateA, BattlegroundConst.RespawnImmediately); - SpawnBGObject(ObjectType.GateH, BattlegroundConst.RespawnImmediately); + DoorClose(ABObjectTypes.GateA); + DoorClose(ABObjectTypes.GateH); + SpawnBGObject(ABObjectTypes.GateA, BattlegroundConst.RespawnImmediately); + SpawnBGObject(ABObjectTypes.GateH, BattlegroundConst.RespawnImmediately); // Starting base spirit guides _NodeOccupied(BattlegroundNodes.SpiritAliance, Team.Alliance); @@ -206,16 +206,16 @@ namespace Game.BattleGrounds public override void StartingEventOpenDoors() { // spawn neutral banners - for (int banner = ObjectType.BannerNeutral, i = 0; i < 5; banner += 8, ++i) + for (int banner = ABObjectTypes.BannerNeutral, i = 0; i < 5; banner += 8, ++i) SpawnBGObject(banner, BattlegroundConst.RespawnImmediately); for (int i = 0; i < BattlegroundNodes.DynamicNodesCount; ++i) { //randomly select buff to spawn int buff = RandomHelper.IRand(0, 2); - SpawnBGObject(ObjectType.SpeedbuffStables + buff + i * 3, BattlegroundConst.RespawnImmediately); + SpawnBGObject(ABObjectTypes.SpeedbuffStables + buff + i * 3, BattlegroundConst.RespawnImmediately); } - DoorOpen(ObjectType.GateA); - DoorOpen(ObjectType.GateH); + DoorOpen(ABObjectTypes.GateA); + DoorOpen(ABObjectTypes.GateH); // Achievement: Let's Get This Done StartCriteriaTimer(CriteriaTimedTypes.Event, EventStartBattle); @@ -267,7 +267,7 @@ namespace Game.BattleGrounds } } - void _CreateBanner(byte node, NodeStatus type, int teamIndex, bool delay) + void _CreateBanner(byte node, ABNodeStatus type, int teamIndex, bool delay) { // Just put it into the queue if (delay) @@ -285,11 +285,11 @@ namespace Game.BattleGrounds // handle aura with banner if (type == 0) return; - obj = node * 8 + ((type == NodeStatus.Occupied) ? (5 + teamIndex) : 7); + obj = node * 8 + ((type == ABNodeStatus.Occupied) ? (5 + teamIndex) : 7); SpawnBGObject(obj, BattlegroundConst.RespawnImmediately); } - void _DelBanner(byte node, NodeStatus type, byte teamIndex) + void _DelBanner(byte node, ABNodeStatus type, byte teamIndex) { int obj = node * 8 + (byte)type + teamIndex; SpawnBGObject(obj, BattlegroundConst.RespawnOneDay); @@ -297,7 +297,7 @@ namespace Game.BattleGrounds // handle aura with banner if (type == 0) return; - obj = node * 8 + ((type == NodeStatus.Occupied) ? (5 + teamIndex) : 7); + obj = node * 8 + ((type == ABNodeStatus.Occupied) ? (5 + teamIndex) : 7); SpawnBGObject(obj, BattlegroundConst.RespawnOneDay); } @@ -338,19 +338,19 @@ namespace Game.BattleGrounds // How many bases each team owns byte ally = 0, horde = 0; for (byte node = 0; node < BattlegroundNodes.DynamicNodesCount; ++node) - if (m_Nodes[node] == NodeStatus.AllyOccupied) + if (m_Nodes[node] == ABNodeStatus.AllyOccupied) ++ally; - else if (m_Nodes[node] == NodeStatus.HordeOccupied) + else if (m_Nodes[node] == ABNodeStatus.HordeOccupied) ++horde; - packet.AddState(WorldStates.OccupiedBasesAlly, ally); - packet.AddState(WorldStates.OccupiedBasesHorde, horde); + packet.AddState(ABWorldStates.OccupiedBasesAlly, ally); + packet.AddState(ABWorldStates.OccupiedBasesHorde, horde); // Team scores - packet.AddState(WorldStates.ResourcesMax, MaxTeamScore); - packet.AddState(WorldStates.ResourcesWarning, WarningNearVictoryScore); - packet.AddState(WorldStates.ResourcesAlly, (int)m_TeamScores[TeamId.Alliance]); - packet.AddState(WorldStates.ResourcesHorde, (int)m_TeamScores[TeamId.Horde]); + packet.AddState(ABWorldStates.ResourcesMax, MaxTeamScore); + packet.AddState(ABWorldStates.ResourcesWarning, WarningNearVictoryScore); + packet.AddState(ABWorldStates.ResourcesAlly, (int)m_TeamScores[TeamId.Alliance]); + packet.AddState(ABWorldStates.ResourcesHorde, (int)m_TeamScores[TeamId.Horde]); // other unknown packet.AddState(0x745, 0x2); @@ -371,13 +371,13 @@ namespace Game.BattleGrounds // How many bases each team owns byte ally = 0, horde = 0; for (byte i = 0; i < BattlegroundNodes.DynamicNodesCount; ++i) - if (m_Nodes[i] == NodeStatus.AllyOccupied) + if (m_Nodes[i] == ABNodeStatus.AllyOccupied) ++ally; - else if (m_Nodes[i] == NodeStatus.HordeOccupied) + else if (m_Nodes[i] == ABNodeStatus.HordeOccupied) ++horde; - UpdateWorldState(WorldStates.OccupiedBasesAlly, ally); - UpdateWorldState(WorldStates.OccupiedBasesHorde, horde); + UpdateWorldState(ABWorldStates.OccupiedBasesAlly, ally); + UpdateWorldState(ABWorldStates.OccupiedBasesHorde, horde); } void _NodeOccupied(byte node, Team team) @@ -390,7 +390,7 @@ namespace Game.BattleGrounds byte capturedNodes = 0; for (byte i = 0; i < BattlegroundNodes.DynamicNodesCount; ++i) - if (m_Nodes[i] == NodeStatus.Occupied + GetTeamIndexByTeamId(team) && m_NodeTimers[i] == 0) + if (m_Nodes[i] == ABNodeStatus.Occupied + GetTeamIndexByTeamId(team) && m_NodeTimers[i] == 0) ++capturedNodes; if (capturedNodes >= 5) @@ -398,7 +398,7 @@ namespace Game.BattleGrounds if (capturedNodes >= 4) CastSpellOnTeam(BattlegroundConst.AbQuestReward4Bases, team); - Creature trigger = BgCreatures.ContainsKey(node + 7) ? GetBGCreature(node + 7) : null; // 0-6 spirit guides + Creature trigger = !BgCreatures[node + 7].IsEmpty() ? GetBGCreature(node + 7) : null; // 0-6 spirit guides if (!trigger) trigger = AddCreature(SharedConst.WorldTrigger, node + 7, NodePositions[node], GetTeamIndexByTeamId(team)); @@ -439,7 +439,7 @@ namespace Game.BattleGrounds while ((node < BattlegroundNodes.DynamicNodesCount) && ((!obj) || (!source.IsWithinDistInMap(obj, 10)))) { ++node; - obj = GetBgMap().GetGameObject(BgObjects[node * 8 + ObjectType.AuraContested]); + obj = GetBgMap().GetGameObject(BgObjects[node * 8 + ABObjectTypes.AuraContested]); } if (node == BattlegroundNodes.DynamicNodesCount) @@ -457,15 +457,15 @@ namespace Game.BattleGrounds source.RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.EnterPvpCombat); uint sound = 0; // If node is neutral, change to contested - if (m_Nodes[node] == NodeStatus.Neutral) + if (m_Nodes[node] == ABNodeStatus.Neutral) { UpdatePlayerScore(source, ScoreType.BasesAssaulted, 1); m_prevNodes[node] = m_Nodes[node]; - m_Nodes[node] = (NodeStatus)(teamIndex + 1); + m_Nodes[node] = (ABNodeStatus)(teamIndex + 1); // burn current neutral banner - _DelBanner(node, NodeStatus.Neutral, 0); + _DelBanner(node, ABNodeStatus.Neutral, 0); // create new contested banner - _CreateBanner(node, NodeStatus.Contested, (byte)teamIndex, true); + _CreateBanner(node, ABNodeStatus.Contested, (byte)teamIndex, true); _SendNodeUpdate(node); m_NodeTimers[node] = FlagCapturingTime; @@ -478,18 +478,18 @@ namespace Game.BattleGrounds sound = SoundClaimed; } // If node is contested - else if ((m_Nodes[node] == NodeStatus.AllyContested) || (m_Nodes[node] == NodeStatus.HordeContested)) + else if ((m_Nodes[node] == ABNodeStatus.AllyContested) || (m_Nodes[node] == ABNodeStatus.HordeContested)) { // If last state is NOT occupied, change node to enemy-contested - if (m_prevNodes[node] < NodeStatus.Occupied) + if (m_prevNodes[node] < ABNodeStatus.Occupied) { UpdatePlayerScore(source, ScoreType.BasesAssaulted, 1); m_prevNodes[node] = m_Nodes[node]; - m_Nodes[node] = (NodeStatus.Contested + (int)teamIndex); + m_Nodes[node] = (ABNodeStatus.Contested + (int)teamIndex); // burn current contested banner - _DelBanner(node, NodeStatus.Contested, (byte)teamIndex); + _DelBanner(node, ABNodeStatus.Contested, (byte)teamIndex); // create new contested banner - _CreateBanner(node, NodeStatus.Contested, (byte)teamIndex, true); + _CreateBanner(node, ABNodeStatus.Contested, (byte)teamIndex, true); _SendNodeUpdate(node); m_NodeTimers[node] = FlagCapturingTime; @@ -504,11 +504,11 @@ namespace Game.BattleGrounds { UpdatePlayerScore(source, ScoreType.BasesDefended, 1); m_prevNodes[node] = m_Nodes[node]; - m_Nodes[node] = (NodeStatus.Occupied + (int)teamIndex); + m_Nodes[node] = (ABNodeStatus.Occupied + (int)teamIndex); // burn current contested banner - _DelBanner(node, NodeStatus.Contested, (byte)teamIndex); + _DelBanner(node, ABNodeStatus.Contested, (byte)teamIndex); // create new occupied banner - _CreateBanner(node, NodeStatus.Occupied, (byte)teamIndex, true); + _CreateBanner(node, ABNodeStatus.Occupied, (byte)teamIndex, true); _SendNodeUpdate(node); m_NodeTimers[node] = 0; _NodeOccupied(node, (teamIndex == TeamId.Alliance) ? Team.Alliance : Team.Horde); @@ -526,11 +526,11 @@ namespace Game.BattleGrounds { UpdatePlayerScore(source, ScoreType.BasesAssaulted, 1); m_prevNodes[node] = m_Nodes[node]; - m_Nodes[node] = (NodeStatus.Contested + (int)teamIndex); + m_Nodes[node] = (ABNodeStatus.Contested + (int)teamIndex); // burn current occupied banner - _DelBanner(node, NodeStatus.Occupied, (byte)teamIndex); + _DelBanner(node, ABNodeStatus.Occupied, (byte)teamIndex); // create new contested banner - _CreateBanner(node, NodeStatus.Contested, (byte)teamIndex, true); + _CreateBanner(node, ABNodeStatus.Contested, (byte)teamIndex, true); _SendNodeUpdate(node); _NodeDeOccupied(node); m_NodeTimers[node] = FlagCapturingTime; @@ -545,7 +545,7 @@ namespace Game.BattleGrounds } // If node is occupied again, send "X has taken the Y" msg. - if (m_Nodes[node] >= NodeStatus.Occupied) + if (m_Nodes[node] >= ABNodeStatus.Occupied) { // FIXME: team and node names not localized if (teamIndex == TeamId.Alliance) @@ -561,9 +561,9 @@ namespace Game.BattleGrounds // How many bases each team owns byte ally = 0, horde = 0; for (byte i = 0; i < BattlegroundNodes.DynamicNodesCount; ++i) - if (m_Nodes[i] == NodeStatus.AllyOccupied) + if (m_Nodes[i] == ABNodeStatus.AllyOccupied) ++ally; - else if (m_Nodes[i] == NodeStatus.HordeOccupied) + else if (m_Nodes[i] == ABNodeStatus.HordeOccupied) ++horde; if (ally > horde) @@ -580,14 +580,14 @@ namespace Game.BattleGrounds bool result = true; for (int i = 0; i < BattlegroundNodes.DynamicNodesCount; ++i) { - result &= AddObject(ObjectType.BannerNeutral + 8 * i, (uint)(NodeObjectId.Banner0 + i), NodePositions[i], 0, 0, (float)Math.Sin(NodePositions[i].GetOrientation() / 2), (float)Math.Cos(NodePositions[i].GetOrientation() / 2), BattlegroundConst.RespawnOneDay); - result &= AddObject(ObjectType.BannerContA + 8 * i, ObjectIds.BannerContA, NodePositions[i], 0, 0, (float)Math.Sin(NodePositions[i].GetOrientation() / 2), (float)Math.Cos(NodePositions[i].GetOrientation() / 2), BattlegroundConst.RespawnOneDay); - result &= AddObject(ObjectType.BannerContH + 8 * i, ObjectIds.BannerContH, NodePositions[i], 0, 0, (float)Math.Sin(NodePositions[i].GetOrientation() / 2), (float)Math.Cos(NodePositions[i].GetOrientation() / 2), BattlegroundConst.RespawnOneDay); - result &= AddObject(ObjectType.BannerAlly + 8 * i, ObjectIds.BannerA, NodePositions[i], 0, 0, (float)Math.Sin(NodePositions[i].GetOrientation() / 2), (float)Math.Cos(NodePositions[i].GetOrientation() / 2), BattlegroundConst.RespawnOneDay); - result &= AddObject(ObjectType.BannerHorde + 8 * i, ObjectIds.BannerH, NodePositions[i], 0, 0, (float)Math.Sin(NodePositions[i].GetOrientation() / 2), (float)Math.Cos(NodePositions[i].GetOrientation() / 2), BattlegroundConst.RespawnOneDay); - result &= AddObject(ObjectType.AuraAlly + 8 * i, ObjectIds.AuraA, NodePositions[i], 0, 0, (float)Math.Sin(NodePositions[i].GetOrientation() / 2), (float)Math.Cos(NodePositions[i].GetOrientation() / 2), BattlegroundConst.RespawnOneDay); - result &= AddObject(ObjectType.AuraHorde + 8 * i, ObjectIds.AuraH, NodePositions[i], 0, 0, (float)Math.Sin(NodePositions[i].GetOrientation() / 2), (float)Math.Cos(NodePositions[i].GetOrientation() / 2), BattlegroundConst.RespawnOneDay); - result &= AddObject(ObjectType.AuraContested + 8 * i, ObjectIds.AuraC, NodePositions[i], 0, 0, (float)Math.Sin(NodePositions[i].GetOrientation() / 2), (float)Math.Cos(NodePositions[i].GetOrientation() / 2), BattlegroundConst.RespawnOneDay); + result &= AddObject(ABObjectTypes.BannerNeutral + 8 * i, (uint)(NodeObjectId.Banner0 + i), NodePositions[i], 0, 0, (float)Math.Sin(NodePositions[i].GetOrientation() / 2), (float)Math.Cos(NodePositions[i].GetOrientation() / 2), BattlegroundConst.RespawnOneDay); + result &= AddObject(ABObjectTypes.BannerContA + 8 * i, ABObjectIds.BannerContA, NodePositions[i], 0, 0, (float)Math.Sin(NodePositions[i].GetOrientation() / 2), (float)Math.Cos(NodePositions[i].GetOrientation() / 2), BattlegroundConst.RespawnOneDay); + result &= AddObject(ABObjectTypes.BannerContH + 8 * i, ABObjectIds.BannerContH, NodePositions[i], 0, 0, (float)Math.Sin(NodePositions[i].GetOrientation() / 2), (float)Math.Cos(NodePositions[i].GetOrientation() / 2), BattlegroundConst.RespawnOneDay); + result &= AddObject(ABObjectTypes.BannerAlly + 8 * i, ABObjectIds.BannerA, NodePositions[i], 0, 0, (float)Math.Sin(NodePositions[i].GetOrientation() / 2), (float)Math.Cos(NodePositions[i].GetOrientation() / 2), BattlegroundConst.RespawnOneDay); + result &= AddObject(ABObjectTypes.BannerHorde + 8 * i, ABObjectIds.BannerH, NodePositions[i], 0, 0, (float)Math.Sin(NodePositions[i].GetOrientation() / 2), (float)Math.Cos(NodePositions[i].GetOrientation() / 2), BattlegroundConst.RespawnOneDay); + result &= AddObject(ABObjectTypes.AuraAlly + 8 * i, ABObjectIds.AuraA, NodePositions[i], 0, 0, (float)Math.Sin(NodePositions[i].GetOrientation() / 2), (float)Math.Cos(NodePositions[i].GetOrientation() / 2), BattlegroundConst.RespawnOneDay); + result &= AddObject(ABObjectTypes.AuraHorde + 8 * i, ABObjectIds.AuraH, NodePositions[i], 0, 0, (float)Math.Sin(NodePositions[i].GetOrientation() / 2), (float)Math.Cos(NodePositions[i].GetOrientation() / 2), BattlegroundConst.RespawnOneDay); + result &= AddObject(ABObjectTypes.AuraContested + 8 * i, ABObjectIds.AuraC, NodePositions[i], 0, 0, (float)Math.Sin(NodePositions[i].GetOrientation() / 2), (float)Math.Cos(NodePositions[i].GetOrientation() / 2), BattlegroundConst.RespawnOneDay); if (!result) { Log.outError(LogFilter.Sql, "BatteGroundAB: Failed to spawn some object Battleground not created!"); @@ -595,8 +595,8 @@ namespace Game.BattleGrounds } } - result &= AddObject(ObjectType.GateA, ObjectIds.GateA, DoorPositions[0][0], DoorPositions[0][1], DoorPositions[0][2], DoorPositions[0][3], DoorPositions[0][4], DoorPositions[0][5], DoorPositions[0][6], DoorPositions[0][7], BattlegroundConst.RespawnImmediately); - result &= AddObject(ObjectType.GateH, ObjectIds.GateH, DoorPositions[1][0], DoorPositions[1][1], DoorPositions[1][2], DoorPositions[1][3], DoorPositions[1][4], DoorPositions[1][5], DoorPositions[1][6], DoorPositions[1][7], BattlegroundConst.RespawnImmediately); + result &= AddObject(ABObjectTypes.GateA, ABObjectIds.GateA, DoorPositions[0][0], DoorPositions[0][1], DoorPositions[0][2], DoorPositions[0][3], DoorPositions[0][4], DoorPositions[0][5], DoorPositions[0][6], DoorPositions[0][7], BattlegroundConst.RespawnImmediately); + result &= AddObject(ABObjectTypes.GateH, ABObjectIds.GateH, DoorPositions[1][0], DoorPositions[1][1], DoorPositions[1][2], DoorPositions[1][3], DoorPositions[1][4], DoorPositions[1][5], DoorPositions[1][6], DoorPositions[1][7], BattlegroundConst.RespawnImmediately); if (!result) { Log.outError(LogFilter.Sql, "BatteGroundAB: Failed to spawn door object Battleground not created!"); @@ -606,9 +606,9 @@ namespace Game.BattleGrounds //buffs for (int i = 0; i < BattlegroundNodes.DynamicNodesCount; ++i) { - result &= AddObject(ObjectType.SpeedbuffStables + 3 * i, Buff_Entries[0], BuffPositions[i][0], BuffPositions[i][1], BuffPositions[i][2], BuffPositions[i][3], 0, 0, (float)Math.Sin(BuffPositions[i][3] / 2), (float)Math.Cos(BuffPositions[i][3] / 2), BattlegroundConst.RespawnOneDay); - result &= AddObject(ObjectType.SpeedbuffStables + 3 * i + 1, Buff_Entries[1], BuffPositions[i][0], BuffPositions[i][1], BuffPositions[i][2], BuffPositions[i][3], 0, 0, (float)Math.Sin(BuffPositions[i][3] / 2), (float)Math.Cos(BuffPositions[i][3] / 2), BattlegroundConst.RespawnOneDay); - result &= AddObject(ObjectType.SpeedbuffStables + 3 * i + 2, Buff_Entries[2], BuffPositions[i][0], BuffPositions[i][1], BuffPositions[i][2], BuffPositions[i][3], 0, 0, (float)Math.Sin(BuffPositions[i][3] / 2), (float)Math.Cos(BuffPositions[i][3] / 2), BattlegroundConst.RespawnOneDay); + result &= AddObject(ABObjectTypes.SpeedbuffStables + 3 * i, Buff_Entries[0], BuffPositions[i][0], BuffPositions[i][1], BuffPositions[i][2], BuffPositions[i][3], 0, 0, (float)Math.Sin(BuffPositions[i][3] / 2), (float)Math.Cos(BuffPositions[i][3] / 2), BattlegroundConst.RespawnOneDay); + result &= AddObject(ABObjectTypes.SpeedbuffStables + 3 * i + 1, Buff_Entries[1], BuffPositions[i][0], BuffPositions[i][1], BuffPositions[i][2], BuffPositions[i][3], 0, 0, (float)Math.Sin(BuffPositions[i][3] / 2), (float)Math.Cos(BuffPositions[i][3] / 2), BattlegroundConst.RespawnOneDay); + result &= AddObject(ABObjectTypes.SpeedbuffStables + 3 * i + 2, Buff_Entries[2], BuffPositions[i][0], BuffPositions[i][1], BuffPositions[i][2], BuffPositions[i][3], 0, 0, (float)Math.Sin(BuffPositions[i][3] / 2), (float)Math.Cos(BuffPositions[i][3] / 2), BattlegroundConst.RespawnOneDay); if (!result) { Log.outError(LogFilter.Sql, "BatteGroundAB: Failed to spawn buff object!"); @@ -668,7 +668,7 @@ namespace Game.BattleGrounds // Is there any occupied node for this team? List nodes = new List(); for (byte i = 0; i < BattlegroundNodes.DynamicNodesCount; ++i) - if (m_Nodes[i] == NodeStatus.Occupied + (int)teamIndex) + if (m_Nodes[i] == ABNodeStatus.Occupied + (int)teamIndex) nodes.Add(i); WorldSafeLocsRecord good_entry = null; @@ -713,10 +713,10 @@ namespace Game.BattleGrounds switch (type) { case ScoreType.BasesAssaulted: - player.UpdateCriteria(CriteriaTypes.BgObjectiveCapture, (uint)Objectives.AssaultBase); + player.UpdateCriteria(CriteriaTypes.BgObjectiveCapture, (uint)ABObjectives.AssaultBase); break; case ScoreType.BasesDefended: - player.UpdateCriteria(CriteriaTypes.BgObjectiveCapture, (uint)Objectives.DefendBase); + player.UpdateCriteria(CriteriaTypes.BgObjectiveCapture, (uint)ABObjectives.DefendBase); break; default: break; @@ -728,8 +728,8 @@ namespace Game.BattleGrounds { uint count = 0; for (int i = 0; i < BattlegroundNodes.DynamicNodesCount; ++i) - if ((team == Team.Alliance && m_Nodes[i] == NodeStatus.AllyOccupied) || - (team == Team.Horde && m_Nodes[i] == NodeStatus.HordeOccupied)) + if ((team == Team.Alliance && m_Nodes[i] == ABNodeStatus.AllyOccupied) || + (team == Team.Horde && m_Nodes[i] == ABNodeStatus.HordeOccupied)) ++count; return count == BattlegroundNodes.DynamicNodesCount; @@ -754,8 +754,8 @@ namespace Game.BattleGrounds /// 3: ally occupied /// 4: horde occupied /// - NodeStatus[] m_Nodes = new NodeStatus[BattlegroundNodes.DynamicNodesCount]; - NodeStatus[] m_prevNodes = new NodeStatus[BattlegroundNodes.DynamicNodesCount]; + ABNodeStatus[] m_Nodes = new ABNodeStatus[BattlegroundNodes.DynamicNodesCount]; + ABNodeStatus[] m_prevNodes = new ABNodeStatus[BattlegroundNodes.DynamicNodesCount]; BannerTimer[] m_BannerTimers = new BannerTimer[BattlegroundNodes.DynamicNodesCount]; uint[] m_NodeTimers = new uint[BattlegroundNodes.DynamicNodesCount]; uint[] m_lastTick = new uint[SharedConst.BGTeamsCount]; @@ -886,7 +886,7 @@ namespace Game.BattleGrounds } #region Consts - struct WorldStates + struct ABWorldStates { public const uint OccupiedBasesHorde = 1778; public const uint OccupiedBasesAlly = 1779; @@ -934,7 +934,7 @@ namespace Game.BattleGrounds public const uint Banner4 = 180091; // Gold Mine Banner } - struct ObjectType + struct ABObjectTypes { // for all 5 node points 8*5=40 objects public const int BannerNeutral = 0; @@ -968,7 +968,7 @@ namespace Game.BattleGrounds } // Object id templates from DB - struct ObjectIds + struct ABObjectIds { public const uint BannerA = 180058; public const uint BannerContA = 180059; @@ -999,7 +999,7 @@ namespace Game.BattleGrounds public const int AllNodesCount = 7; // All Nodes (Dynamic And Static) } - enum NodeStatus + enum ABNodeStatus { Neutral = 0, Contested = 1, @@ -1010,7 +1010,7 @@ namespace Game.BattleGrounds HordeOccupied = 4 } - enum Objectives + enum ABObjectives { AssaultBase = 122, DefendBase = 123 diff --git a/Game/BattleGrounds/Zones/EyeofStorm.cs b/Game/BattleGrounds/Zones/EyeofStorm.cs index 173c368da..19ca52f53 100644 --- a/Game/BattleGrounds/Zones/EyeofStorm.cs +++ b/Game/BattleGrounds/Zones/EyeofStorm.cs @@ -21,7 +21,7 @@ using System.Collections.Generic; using Game.Network.Packets; using Game.DataStorage; -namespace Game.BattleGrounds +namespace Game.BattleGrounds.Zones { class BgEyeofStorm : Battleground { @@ -763,7 +763,7 @@ namespace Game.BattleGrounds else SendMessageToAll(EotSMisc.m_CapturingPointTypes[Point].MessageIdHorde, ChatMsg.BgSystemHorde, player); - if (BgCreatures.ContainsKey(Point) && !BgCreatures[Point].IsEmpty()) + if (!BgCreatures[Point].IsEmpty()) DelCreature(Point); WorldSafeLocsRecord sg = CliDB.WorldSafeLocsStorage.LookupByKey(EotSMisc.m_CapturingPointTypes[Point].GraveYardId); diff --git a/Game/BattleGrounds/Zones/StrandofAncients.cs b/Game/BattleGrounds/Zones/StrandofAncients.cs index 91498f5c5..acc1dca62 100644 --- a/Game/BattleGrounds/Zones/StrandofAncients.cs +++ b/Game/BattleGrounds/Zones/StrandofAncients.cs @@ -15,13 +15,1461 @@ * along with this program. If not, see . */ +using Framework.Constants; +using Game.DataStorage; +using Game.Entities; +using Game.Network.Packets; +using System.Collections.Generic; +using Framework.GameMath; + namespace Game.BattleGrounds.Zones { - class StrandofAncients + public class BgStrandOfAncients : Battleground { + public BgStrandOfAncients() + { + StartMessageIds[BattlegroundConst.EventIdFirst] = CypherStrings.BgSaStartTwoMinutes; + StartMessageIds[BattlegroundConst.EventIdSecond] = CypherStrings.BgSaStartOneMinute; + StartMessageIds[BattlegroundConst.EventIdThird] = CypherStrings.BgSaStartHalfMinute; + StartMessageIds[BattlegroundConst.EventIdFourth] = 0; + + BgObjects = new ObjectGuid[SAObjectTypes.MaxObj]; + BgCreatures = new ObjectGuid[SACreatureTypes.Max + SAGraveyards.Max]; + TimerEnabled = false; + UpdateWaitTimer = 0; + SignaledRoundTwo = false; + SignaledRoundTwoHalfMin = false; + InitSecondRound = false; + _gateDestroyed = false; + Attackers = TeamId.Alliance; + TotalTime = 0; + EndRoundTimer = 0; + ShipsStarted = false; + Status = SAStatus.NotStarted; + + for (byte i = 0; i < GateStatus.Length; ++i) + GateStatus[i] = SAGateState.Ok; + + for (byte i = 0; i < 2; i++) + { + RoundScores[i].winner = TeamId.Alliance; + RoundScores[i].time = 0; + _allVehiclesAlive[i] = true; + } + } + + public override void Reset() + { + TotalTime = 0; + Attackers = (RandomHelper.URand(0, 1) != 0 ? TeamId.Alliance : TeamId.Horde); + for (byte i = 0; i <= 5; i++) + GateStatus[i] = SAGateState.Ok; + ShipsStarted = false; + _gateDestroyed = false; + _allVehiclesAlive[TeamId.Alliance] = true; + _allVehiclesAlive[TeamId.Horde] = true; + Status = SAStatus.Warmup; + } + + public override bool SetupBattleground() + { + return ResetObjs(); + } + + bool ResetObjs() + { + foreach (var pair in GetPlayers()) + { + Player player = Global.ObjAccessor.FindPlayer(pair.Key); + if (player) + SendTransportsRemove(player); + } + + uint atF = SAMiscConst.Factions[Attackers]; + uint defF = SAMiscConst.Factions[Attackers != 0 ? TeamId.Alliance : TeamId.Horde]; + + for (byte i = 0; i < SAObjectTypes.MaxObj; i++) + DelObject(i); + + for (byte i = 0; i < SACreatureTypes.Max; i++) + DelCreature(i); + + for (byte i = SACreatureTypes.Max; i < SACreatureTypes.Max + SAGraveyards.Max; i++) + DelCreature(i); + + for (byte i = 0; i < GateStatus.Length; ++i) + GateStatus[i] = SAGateState.Ok; + + if (!AddCreature(SAMiscConst.NpcEntries[SACreatureTypes.Kanrethad], SACreatureTypes.Kanrethad, SAMiscConst.NpcSpawnlocs[SACreatureTypes.Kanrethad])) + { + Log.outError(LogFilter.Battleground, $"SOTA: couldn't spawn Kanrethad, aborted. Entry: {SAMiscConst.NpcEntries[SACreatureTypes.Kanrethad]}"); + return false; + } + + for (byte i = 0; i <= SAObjectTypes.PortalDeffenderRed; i++) + { + if (!AddObject(i, SAMiscConst.ObjEntries[i], SAMiscConst.ObjSpawnlocs[i], 0, 0, 0, 0, BattlegroundConst.RespawnOneDay)) + { + Log.outError(LogFilter.Battleground, "SOTA: couldn't spawn BG_SA_PORTAL_DEFFENDER_RED, Entry: %u", SAMiscConst.ObjEntries[i]); + continue; + } + } + + for (int i = SAObjectTypes.BoatOne; i <= SAObjectTypes.BoatTwo; i++) + { + uint boatid = 0; + switch (i) + { + case SAObjectTypes.BoatOne: + boatid = Attackers != 0 ? SAGameObjectIds.BoatOneH : SAGameObjectIds.BoatOneA; + break; + case SAObjectTypes.BoatTwo: + boatid = Attackers != 0 ? SAGameObjectIds.BoatTwoH : SAGameObjectIds.BoatTwoA; + break; + default: + break; + } + if (!AddObject(i, boatid, SAMiscConst.ObjSpawnlocs[i].GetPositionX(), + SAMiscConst.ObjSpawnlocs[i].GetPositionY(), + SAMiscConst.ObjSpawnlocs[i].GetPositionZ() + (Attackers != 0 ? -3.750f : 0), + SAMiscConst.ObjSpawnlocs[i].GetOrientation(), 0, 0, 0, 0, BattlegroundConst.RespawnOneDay)) + { + Log.outError(LogFilter.Battleground, "SOTA: couldn't spawn one of the BG_SA_BOAT, Entry: %u", boatid); + continue; + } + } + + for (byte i = SAObjectTypes.Sigil1; i <= SAObjectTypes.LeftFlagpole; i++) + { + if (!AddObject(i, SAMiscConst.ObjEntries[i], SAMiscConst.ObjSpawnlocs[i], 0, 0, 0, 0, BattlegroundConst.RespawnOneDay)) + { + Log.outError(LogFilter.Battleground, "SOTA: couldn't spawn Sigil, Entry: %u", SAMiscConst.ObjEntries[i]); + continue; + } + } + + // MAD props for Kiper for discovering those values - 4 hours of his work. + GetBGObject(SAObjectTypes.BoatOne).SetParentRotation(new Quaternion(0.0f, 0.0f, 1.0f, 0.0002f)); + GetBGObject(SAObjectTypes.BoatTwo).SetParentRotation(new Quaternion(0.0f, 0.0f, 1.0f, 0.00001f)); + SpawnBGObject(SAObjectTypes.BoatOne, BattlegroundConst.RespawnImmediately); + SpawnBGObject(SAObjectTypes.BoatTwo, BattlegroundConst.RespawnImmediately); + + //Cannons and demolishers - NPCs are spawned + //By capturing GYs. + for (byte i = 0; i < SACreatureTypes.Demolisher5; i++) + { + if (!AddCreature(SAMiscConst.NpcEntries[i], i, SAMiscConst.NpcSpawnlocs[i], Attackers == TeamId.Alliance ? TeamId.Horde : TeamId.Alliance, 600)) + { + Log.outError(LogFilter.Battleground, "SOTA: couldn't spawn Cannon or demolisher, Entry: %u, Attackers: %s", SAMiscConst.NpcEntries[i], Attackers == TeamId.Alliance ? "Horde(1)" : "Alliance(0)"); + continue; + } + } + + OverrideGunFaction(); + DemolisherStartState(true); + + for (byte i = 0; i <= SAObjectTypes.PortalDeffenderRed; i++) + { + SpawnBGObject(i, BattlegroundConst.RespawnImmediately); + GetBGObject(i).SetFaction(defF); + } + + GetBGObject(SAObjectTypes.TitanRelic).SetFaction(atF); + GetBGObject(SAObjectTypes.TitanRelic).Refresh(); + + TotalTime = 0; + ShipsStarted = false; + + //Graveyards + for (byte i = 0; i < SAGraveyards.Max; i++) + { + WorldSafeLocsRecord sg = CliDB.WorldSafeLocsStorage.LookupByKey(SAMiscConst.GYEntries[i]); + if (sg == null) + { + Log.outError(LogFilter.Battleground, "SOTA: Can't find GY entry %u", SAMiscConst.GYEntries[i]); + return false; + } + + if (i == SAGraveyards.BeachGy) + { + GraveyardStatus[i] = Attackers; + AddSpiritGuide(i + SACreatureTypes.Max, sg.Loc.X, sg.Loc.Y, sg.Loc.Z, SAMiscConst.GYOrientation[i], Attackers); + } + else + { + GraveyardStatus[i] = ((Attackers == TeamId.Horde) ? TeamId.Alliance : TeamId.Horde); + if (!AddSpiritGuide(i + SACreatureTypes.Max, sg.Loc.X, sg.Loc.Y, sg.Loc.Z, SAMiscConst.GYOrientation[i], Attackers == TeamId.Horde ? TeamId.Alliance : TeamId.Horde)) + Log.outError(LogFilter.Battleground, "SOTA: couldn't spawn GY: %u", i); + } + } + + //GY capture points + for (byte i = SAObjectTypes.CentralFlag; i <= SAObjectTypes.LeftFlag; i++) + { + if (!AddObject(i, (SAMiscConst.ObjEntries[i] - (Attackers == TeamId.Alliance ? 1u : 0)), SAMiscConst.ObjSpawnlocs[i], 0, 0, 0, 0, BattlegroundConst.RespawnOneDay)) + { + Log.outError(LogFilter.Battleground, "SOTA: couldn't spawn Central Flag Entry: %u", SAMiscConst.ObjEntries[i] - (Attackers == TeamId.Alliance ? 1 : 0)); + continue; + } + GetBGObject(i).SetFaction(atF); + } + + UpdateObjectInteractionFlags(); + + for (byte i = SAObjectTypes.Bomb; i < SAObjectTypes.MaxObj; i++) + { + if (!AddObject(i, SAMiscConst.ObjEntries[SAObjectTypes.Bomb], SAMiscConst.ObjSpawnlocs[i], 0, 0, 0, 0, BattlegroundConst.RespawnOneDay)) + { + Log.outError(LogFilter.Battleground, "SOTA: couldn't spawn SA Bomb Entry: %u", SAMiscConst.ObjEntries[SAObjectTypes.Bomb] + i); + continue; + } + GetBGObject(i).SetFaction(atF); + } + + //Player may enter BEFORE we set up BG - lets update his worldstates anyway... + UpdateWorldState(SAWorldStateIds.RightGyHorde, GraveyardStatus[SAGraveyards.RightCapturableGy] == TeamId.Horde ? 1 : 0u); + UpdateWorldState(SAWorldStateIds.LeftGyHorde, GraveyardStatus[SAGraveyards.LeftCapturableGy] == TeamId.Horde ? 1 : 0u); + UpdateWorldState(SAWorldStateIds.CenterGyHorde, GraveyardStatus[SAGraveyards.CentralCapturableGy] == TeamId.Horde ? 1 : 0u); + + UpdateWorldState(SAWorldStateIds.RightGyAlliance, GraveyardStatus[SAGraveyards.RightCapturableGy] == TeamId.Alliance ? 1 : 0u); + UpdateWorldState(SAWorldStateIds.LeftGyAlliance, GraveyardStatus[SAGraveyards.LeftCapturableGy] == TeamId.Alliance ? 1 : 0u); + UpdateWorldState(SAWorldStateIds.CenterGyAlliance, GraveyardStatus[SAGraveyards.CentralCapturableGy] == TeamId.Alliance ? 1 : 0u); + + if (Attackers == TeamId.Alliance) + { + UpdateWorldState(SAWorldStateIds.AllyAttacks, 1); + UpdateWorldState(SAWorldStateIds.HordeAttacks, 0); + + UpdateWorldState(SAWorldStateIds.RightAttTokenAll, 1); + UpdateWorldState(SAWorldStateIds.LeftAttTokenAll, 1); + UpdateWorldState(SAWorldStateIds.RightAttTokenHrd, 0); + UpdateWorldState(SAWorldStateIds.LeftAttTokenHrd, 0); + + UpdateWorldState(SAWorldStateIds.HordeDefenceToken, 1); + UpdateWorldState(SAWorldStateIds.AllianceDefenceToken, 0); + } + else + { + UpdateWorldState(SAWorldStateIds.HordeAttacks, 1); + UpdateWorldState(SAWorldStateIds.AllyAttacks, 0); + + UpdateWorldState(SAWorldStateIds.RightAttTokenAll, 0); + UpdateWorldState(SAWorldStateIds.LeftAttTokenAll, 0); + UpdateWorldState(SAWorldStateIds.RightAttTokenHrd, 1); + UpdateWorldState(SAWorldStateIds.LeftAttTokenHrd, 1); + + UpdateWorldState(SAWorldStateIds.HordeDefenceToken, 0); + UpdateWorldState(SAWorldStateIds.AllianceDefenceToken, 1); + } + + UpdateWorldState(SAWorldStateIds.PurpleGate, 1); + UpdateWorldState(SAWorldStateIds.RedGate, 1); + UpdateWorldState(SAWorldStateIds.BlueGate, 1); + UpdateWorldState(SAWorldStateIds.GreenGate, 1); + UpdateWorldState(SAWorldStateIds.YellowGate, 1); + UpdateWorldState(SAWorldStateIds.AncientGate, 1); + + for (int i = SAObjectTypes.BoatOne; i <= SAObjectTypes.BoatTwo; i++) + { + foreach (var pair in GetPlayers()) + { + Player player = Global.ObjAccessor.FindPlayer(pair.Key); + if (player) + SendTransportInit(player); + } + } + + // set status manually so preparation is cast correctly in 2nd round too + SetStatus(BattlegroundStatus.WaitJoin); + + TeleportPlayers(); + return true; + } + + void StartShips() + { + if (ShipsStarted) + return; + + DoorOpen(SAObjectTypes.BoatOne); + DoorOpen(SAObjectTypes.BoatTwo); + + for (int i = SAObjectTypes.BoatOne; i <= SAObjectTypes.BoatTwo; i++) + { + foreach (var pair in GetPlayers()) + { + Player p = Global.ObjAccessor.FindPlayer(pair.Key); + if (p) + { + UpdateData data = new UpdateData(p.GetMapId()); + GetBGObject(i).BuildValuesUpdateBlockForPlayer(data, p); + + UpdateObject pkt; + data.BuildPacket(out pkt); + p.SendPacket(pkt); + } + } + } + ShipsStarted = true; + } + + public override void PostUpdateImpl(uint diff) + { + if (InitSecondRound) + { + if (UpdateWaitTimer < diff) + { + if (!SignaledRoundTwo) + { + SignaledRoundTwo = true; + InitSecondRound = false; + SendMessageToAll(CypherStrings.BgSaRoundTwoOneMinute, ChatMsg.BgSystemNeutral); + } + } + else + { + UpdateWaitTimer -= diff; + return; + } + } + TotalTime += diff; + + if (Status == SAStatus.Warmup) + { + EndRoundTimer = SATimers.RoundLength; + if (TotalTime >= SATimers.WarmupLength) + { + Creature c = GetBGCreature(SACreatureTypes.Kanrethad); + if (c) + SendChatMessage(c, SATextIds.RoundStarted); + + TotalTime = 0; + ToggleTimer(); + DemolisherStartState(false); + Status = SAStatus.RoundOne; + StartCriteriaTimer(CriteriaTimedTypes.Event, (Attackers == TeamId.Alliance) ? 23748 : 21702u); + } + if (TotalTime >= SATimers.BoatStart) + StartShips(); + return; + } + else if (Status == SAStatus.SecondWarmup) + { + if (RoundScores[0].time < SATimers.RoundLength) + EndRoundTimer = RoundScores[0].time; + else + EndRoundTimer = SATimers.RoundLength; + + if (TotalTime >= 60000) + { + Creature c = GetBGCreature(SACreatureTypes.Kanrethad); + if (c) + SendChatMessage(c, SATextIds.RoundStarted); + + TotalTime = 0; + ToggleTimer(); + DemolisherStartState(false); + Status = SAStatus.RoundTwo; + StartCriteriaTimer(CriteriaTimedTypes.Event, (Attackers == TeamId.Alliance) ? 23748 : 21702u); + // status was set to STATUS_WAIT_JOIN manually for Preparation, set it back now + SetStatus(BattlegroundStatus.InProgress); + foreach (var pair in GetPlayers()) + { + Player p = Global.ObjAccessor.FindPlayer(pair.Key); + if (p) + p.RemoveAurasDueToSpell(BattlegroundConst.SpellPreparation); + } + } + if (TotalTime >= 30000) + { + if (!SignaledRoundTwoHalfMin) + { + SignaledRoundTwoHalfMin = true; + SendMessageToAll(CypherStrings.BgSaRoundTwoStartHalfMinute, ChatMsg.BgSystemNeutral); + } + } + StartShips(); + return; + } + else if (GetStatus() == BattlegroundStatus.InProgress) + { + if (Status == SAStatus.RoundOne) + { + if (TotalTime >= SATimers.RoundLength) + { + CastSpellOnTeam(SASpellIds.EndOfRound, Team.Alliance); + CastSpellOnTeam(SASpellIds.EndOfRound, Team.Horde); + RoundScores[0].winner = (uint)Attackers; + RoundScores[0].time = SATimers.RoundLength; + TotalTime = 0; + Status = SAStatus.SecondWarmup; + Attackers = (Attackers == TeamId.Alliance) ? TeamId.Horde : TeamId.Alliance; + UpdateWaitTimer = 5000; + SignaledRoundTwo = false; + SignaledRoundTwoHalfMin = false; + InitSecondRound = true; + ToggleTimer(); + ResetObjs(); + GetBgMap().UpdateAreaDependentAuras(); + return; + } + } + else if (Status == SAStatus.RoundTwo) + { + if (TotalTime >= EndRoundTimer) + { + CastSpellOnTeam(SASpellIds.EndOfRound, Team.Alliance); + CastSpellOnTeam(SASpellIds.EndOfRound, Team.Horde); + RoundScores[1].time = SATimers.RoundLength; + RoundScores[1].winner = (uint)((Attackers == TeamId.Alliance) ? TeamId.Horde : TeamId.Alliance); + if (RoundScores[0].time == RoundScores[1].time) + EndBattleground(0); + else if (RoundScores[0].time < RoundScores[1].time) + EndBattleground(RoundScores[0].winner == TeamId.Alliance ? Team.Alliance : Team.Horde); + else + EndBattleground(RoundScores[1].winner == TeamId.Alliance ? Team.Alliance : Team.Horde); + return; + } + } + if (Status == SAStatus.RoundOne || Status == SAStatus.RoundTwo) + { + SendTime(); + UpdateDemolisherSpawns(); + } + } + } + + public override void StartingEventCloseDoors() { } + + public override void StartingEventOpenDoors() { } + + public override void FillInitialWorldStates(InitWorldStates packet) + { + bool allyAttacks = Attackers == TeamId.Alliance; + bool hordeAttacks = Attackers == TeamId.Horde; + + packet.AddState(SAWorldStateIds.AncientGate, (int)GateStatus[SAObjectTypes.AncientGate]); + packet.AddState(SAWorldStateIds.YellowGate, (int)GateStatus[SAObjectTypes.YellowGate]); + packet.AddState(SAWorldStateIds.GreenGate, (int)GateStatus[SAObjectTypes.GreenGate]); + packet.AddState(SAWorldStateIds.BlueGate, (int)GateStatus[SAObjectTypes.BlueGate]); + packet.AddState(SAWorldStateIds.RedGate, (int)GateStatus[SAObjectTypes.RedGate]); + packet.AddState(SAWorldStateIds.PurpleGate, (int)GateStatus[SAObjectTypes.PurpleGate]); + + packet.AddState(SAWorldStateIds.BonusTimer, 0); + + packet.AddState(SAWorldStateIds.HordeAttacks, hordeAttacks); + packet.AddState(SAWorldStateIds.AllyAttacks, allyAttacks); + + // Time will be sent on first update... + packet.AddState(SAWorldStateIds.EnableTimer, TimerEnabled); + packet.AddState(SAWorldStateIds.TimerMins, 0); + packet.AddState(SAWorldStateIds.TimerSecTens, 0); + packet.AddState(SAWorldStateIds.TimerSecDecs, 0); + + packet.AddState(SAWorldStateIds.RightGyHorde, GraveyardStatus[SAGraveyards.RightCapturableGy] == TeamId.Horde); + packet.AddState(SAWorldStateIds.LeftGyHorde, GraveyardStatus[SAGraveyards.LeftCapturableGy] == TeamId.Horde); + packet.AddState(SAWorldStateIds.CenterGyHorde, GraveyardStatus[SAGraveyards.CentralCapturableGy] == TeamId.Horde); + + packet.AddState(SAWorldStateIds.RightGyAlliance, GraveyardStatus[SAGraveyards.RightCapturableGy] == TeamId.Alliance); + packet.AddState(SAWorldStateIds.LeftGyAlliance, GraveyardStatus[SAGraveyards.LeftCapturableGy] == TeamId.Alliance); + packet.AddState(SAWorldStateIds.CenterGyAlliance, GraveyardStatus[SAGraveyards.CentralCapturableGy] == TeamId.Alliance); + + packet.AddState(SAWorldStateIds.HordeDefenceToken, allyAttacks); + packet.AddState(SAWorldStateIds.AllianceDefenceToken, hordeAttacks); + + packet.AddState(SAWorldStateIds.LeftAttTokenHrd, hordeAttacks); + packet.AddState(SAWorldStateIds.RightAttTokenHrd, hordeAttacks); + packet.AddState(SAWorldStateIds.RightAttTokenAll, allyAttacks); + packet.AddState(SAWorldStateIds.LeftAttTokenAll, allyAttacks); + } + + public override void AddPlayer(Player player) + { + base.AddPlayer(player); + PlayerScores[player.GetGUID()] = new BattlegroundSAScore(player.GetGUID(), player.GetBGTeam()); + + SendTransportInit(player); + + TeleportToEntrancePosition(player); + } + + public override void RemovePlayer(Player player, ObjectGuid guid, Team team) { } + + public override void HandleAreaTrigger(Player source, uint trigger, bool entered) + { + // this is wrong way to implement these things. On official it done by gameobject spell cast. + if (GetStatus() != BattlegroundStatus.InProgress) + return; + } + + void TeleportPlayers() + { + foreach (var pair in GetPlayers()) + { + Player player = Global.ObjAccessor.FindPlayer(pair.Key); + if (player) + { + // should remove spirit of redemption + if (player.HasAuraType(AuraType.SpiritOfRedemption)) + player.RemoveAurasByType(AuraType.ModShapeshift); + + if (!player.IsAlive()) + { + player.ResurrectPlayer(1.0f); + player.SpawnCorpseBones(); + } + + player.ResetAllPowers(); + player.CombatStopWithPets(true); + + player.CastSpell(player, BattlegroundConst.SpellPreparation, true); + + TeleportToEntrancePosition(player); + } + } + } + + void TeleportToEntrancePosition(Player player) + { + if (player.GetTeamId() == Attackers) + { + if (!ShipsStarted) + { + // player.AddUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT); + + if (RandomHelper.URand(0, 1) != 0) + player.TeleportTo(607, 2682.936f, -830.368f, 15.0f, 2.895f, 0); + else + player.TeleportTo(607, 2577.003f, 980.261f, 15.0f, 0.807f, 0); + } + else + player.TeleportTo(607, 1600.381f, -106.263f, 8.8745f, 3.78f, 0); + } + else + player.TeleportTo(607, 1209.7f, -65.16f, 70.1f, 0.0f, 0); + } + + public override void ProcessEvent(WorldObject obj, uint eventId, WorldObject invoker = null) + { + GameObject go = obj.ToGameObject(); + if (go) + { + switch (go.GetGoType()) + { + case GameObjectTypes.Goober: + if (invoker) + if (eventId == (uint)SAEventIds.BG_SA_EVENT_TITAN_RELIC_ACTIVATED) + TitanRelicActivated(invoker.ToPlayer()); + break; + case GameObjectTypes.DestructibleBuilding: + { + SAGateInfo gate = GetGate(obj.GetEntry()); + if (gate != null) + { + uint gateId = gate.GateId; + + // damaged + if (eventId == go.GetGoInfo().DestructibleBuilding.DamagedEvent) + { + GateStatus[gateId] = SAGateState.Damaged; + + Creature c = obj.FindNearestCreature(SharedConst.WorldTrigger, 500.0f); + if (c) + SendChatMessage(c, (byte)gate.DamagedText, invoker); + + PlaySoundToAll(Attackers == TeamId.Alliance ? SASoundIds.WallAttackedAlliance : SASoundIds.WallAttackedHorde); + } + // destroyed + else if (eventId == go.GetGoInfo().DestructibleBuilding.DestroyedEvent) + { + GateStatus[gate.GateId] = SAGateState.Destroyed; + _gateDestroyed = true; + + if (gateId < 5) + DelObject((int)gateId + 14); + + Creature c = obj.FindNearestCreature(SharedConst.WorldTrigger, 500.0f); + if (c) + SendChatMessage(c, (byte)gate.DestroyedText, invoker); + + PlaySoundToAll(Attackers == TeamId.Alliance ? SASoundIds.WallDestroyedAlliance : SASoundIds.WallDestroyedHorde); + + bool rewardHonor = true; + switch (gateId) + { + case SAObjectTypes.GreenGate: + if (GateStatus[SAObjectTypes.BlueGate] == SAGateState.Destroyed) + rewardHonor = false; + break; + case SAObjectTypes.BlueGate: + if (GateStatus[SAObjectTypes.GreenGate] == SAGateState.Destroyed) + rewardHonor = false; + break; + case SAObjectTypes.RedGate: + if (GateStatus[SAObjectTypes.PurpleGate] == SAGateState.Destroyed) + rewardHonor = false; + break; + case SAObjectTypes.PurpleGate: + if (GateStatus[SAObjectTypes.RedGate] == SAGateState.Destroyed) + rewardHonor = false; + break; + default: + break; + } + + if (invoker) + { + Unit unit = invoker.ToUnit(); + if (unit) + { + Player player = unit.GetCharmerOrOwnerPlayerOrPlayerItself(); + if (player) + { + UpdatePlayerScore(player, ScoreType.DestroyedWall, 1); + if (rewardHonor) + UpdatePlayerScore(player, ScoreType.BonusHonor, GetBonusHonorFromKill(1)); + } + } + } + + UpdateObjectInteractionFlags(); + } + else + break; + + UpdateWorldState(gate.WorldState, (uint)GateStatus[gateId]); + } + break; + } + default: + break; + } + } + } + + public override void HandleKillUnit(Creature creature, Player killer) + { + if (creature.GetEntry() == SACreatureIds.Demolisher) + { + UpdatePlayerScore(killer, ScoreType.DestroyedDemolisher, 1); + _allVehiclesAlive[Attackers] = false; + } + } + + /* + You may ask what the fuck does it do? + Prevents owner overwriting guns faction with own. + */ + void OverrideGunFaction() + { + if (BgCreatures[0].IsEmpty()) + return; + + for (byte i = SACreatureTypes.Gun1; i <= SACreatureTypes.Gun10; i++) + { + Creature gun = GetBGCreature(i); + if (gun) + gun.SetFaction(SAMiscConst.Factions[Attackers != 0 ? TeamId.Alliance : TeamId.Horde]); + } + + for (byte i = SACreatureTypes.Demolisher1; i <= SACreatureTypes.Demolisher4; i++) + { + Creature dem = GetBGCreature(i); + if (dem) + dem.SetFaction(SAMiscConst.Factions[Attackers]); + } + } + + void DemolisherStartState(bool start) + { + if (BgCreatures[0].IsEmpty()) + return; + + // set flags only for the demolishers on the beach, factory ones dont need it + for (byte i = SACreatureTypes.Demolisher1; i <= SACreatureTypes.Demolisher4; i++) + { + Creature dem = GetBGCreature(i); + if (dem) + { + if (start) + dem.SetFlag(UnitFields.Flags, UnitFlags.NonAttackable | UnitFlags.NotSelectable); + else + dem.RemoveFlag(UnitFields.Flags, UnitFlags.NonAttackable | UnitFlags.NotSelectable); + } + } + } + + public override void DestroyGate(Player player, GameObject go) + { + } + + public override WorldSafeLocsRecord GetClosestGraveYard(Player player) + { + uint safeloc = 0; + WorldSafeLocsRecord ret; + WorldSafeLocsRecord closest; + float dist, nearest; + float x, y, z; + + player.GetPosition(out x, out y, out z); + + if (player.GetTeamId() == Attackers) + safeloc = SAMiscConst.GYEntries[SAGraveyards.BeachGy]; + else + safeloc = SAMiscConst.GYEntries[SAGraveyards.DefenderLastGy]; + + closest = CliDB.WorldSafeLocsStorage.LookupByKey(safeloc); + nearest = player.GetExactDistSq(closest.Loc.X, closest.Loc.Y, closest.Loc.Z); + + for (byte i = SAGraveyards.RightCapturableGy; i < SAGraveyards.Max; i++) + { + if (GraveyardStatus[i] != player.GetTeamId()) + continue; + + ret = CliDB.WorldSafeLocsStorage.LookupByKey(SAMiscConst.GYEntries[i]); + dist = player.GetExactDistSq(ret.Loc.X, ret.Loc.Y, ret.Loc.Z); + if (dist < nearest) + { + closest = ret; + nearest = dist; + } + } + + return closest; + } + + void SendTime() + { + uint end_of_round = (EndRoundTimer - TotalTime); + UpdateWorldState(SAWorldStateIds.TimerMins, end_of_round / 60000); + UpdateWorldState(SAWorldStateIds.TimerSecTens, (end_of_round % 60000) / 10000); + UpdateWorldState(SAWorldStateIds.TimerSecDecs, ((end_of_round % 60000) % 10000) / 1000); + } + + bool CanInteractWithObject(uint objectId) + { + switch (objectId) + { + case SAObjectTypes.TitanRelic: + if (GateStatus[SAObjectTypes.AncientGate] != SAGateState.Destroyed || GateStatus[SAObjectTypes.YellowGate] != SAGateState.Destroyed) + return false; + goto case SAObjectTypes.CentralFlag; + case SAObjectTypes.CentralFlag: + if (GateStatus[SAObjectTypes.RedGate] != SAGateState.Destroyed && GateStatus[SAObjectTypes.PurpleGate] != SAGateState.Destroyed) + return false; + goto case SAObjectTypes.LeftFlag; + case SAObjectTypes.LeftFlag: + case SAObjectTypes.RightFlag: + if (GateStatus[SAObjectTypes.GreenGate] != SAGateState.Destroyed && GateStatus[SAObjectTypes.BlueGate] != SAGateState.Destroyed) + return false; + break; + default: + //ABORT(); + break; + } + + return true; + } + + void UpdateObjectInteractionFlags(uint objectId) + { + GameObject go = GetBGObject((int)objectId); + if (go) + { + if (CanInteractWithObject(objectId)) + go.RemoveFlag(GameObjectFields.Flags, GameObjectFlags.NotSelectable); + else + go.SetFlag(GameObjectFields.Flags, GameObjectFlags.NotSelectable); + } + } + + void UpdateObjectInteractionFlags() + { + for (byte i = SAObjectTypes.CentralFlag; i <= SAObjectTypes.LeftFlag; ++i) + UpdateObjectInteractionFlags(i); + UpdateObjectInteractionFlags(SAObjectTypes.TitanRelic); + } + + public override void EventPlayerClickedOnFlag(Player source, GameObject go) + { + switch (go.GetEntry()) + { + case 191307: + case 191308: + if (CanInteractWithObject(SAObjectTypes.LeftFlag)) + CaptureGraveyard(SAGraveyards.LeftCapturableGy, source); + break; + case 191305: + case 191306: + if (CanInteractWithObject(SAObjectTypes.RightFlag)) + CaptureGraveyard(SAGraveyards.RightCapturableGy, source); + break; + case 191310: + case 191309: + if (CanInteractWithObject(SAObjectTypes.CentralFlag)) + CaptureGraveyard(SAGraveyards.CentralCapturableGy, source); + break; + default: + return; + }; + } + + void CaptureGraveyard(int i, Player Source) + { + if (GraveyardStatus[i] == Attackers) + return; + + DelCreature(SACreatureTypes.Max + i); + GraveyardStatus[i] = Source.GetTeamId(); + WorldSafeLocsRecord sg = CliDB.WorldSafeLocsStorage.LookupByKey(SAMiscConst.GYEntries[i]); + if (sg == null) + { + Log.outError(LogFilter.Battleground, "CaptureGraveyard: non-existant GY entry: %u", SAMiscConst.GYEntries[i]); + return; + } + + AddSpiritGuide(i + SACreatureTypes.Max, sg.Loc.X, sg.Loc.Y, sg.Loc.Z, SAMiscConst.GYOrientation[i], GraveyardStatus[i]); + uint npc = 0; + int flag = 0; + + switch (i) + { + case SAGraveyards.LeftCapturableGy: + { + flag = SAObjectTypes.LeftFlag; + DelObject(flag); + AddObject(flag, (SAMiscConst.ObjEntries[flag] - (Source.GetTeamId() == TeamId.Alliance ? 0 : 1u)), + SAMiscConst.ObjSpawnlocs[flag], 0, 0, 0, 0, BattlegroundConst.RespawnOneDay); + + npc = SACreatureTypes.Rigspark; + Creature rigspark = AddCreature(SAMiscConst.NpcEntries[npc], (int)npc, SAMiscConst.NpcSpawnlocs[npc], Attackers); + if (rigspark) + rigspark.GetAI().Talk(SATextIds.SparklightRigsparkSpawn); + + for (byte j = SACreatureTypes.Demolisher7; j <= SACreatureTypes.Demolisher8; j++) + { + AddCreature(SAMiscConst.NpcEntries[j], j, SAMiscConst.NpcSpawnlocs[j], (Attackers == TeamId.Alliance ? TeamId.Horde : TeamId.Alliance), 600); + Creature dem = GetBGCreature(j); + if (dem) + dem.SetFaction(SAMiscConst.Factions[Attackers]); + } + + UpdateWorldState(SAWorldStateIds.LeftGyAlliance, GraveyardStatus[i] == TeamId.Alliance); + UpdateWorldState(SAWorldStateIds.LeftGyHorde, GraveyardStatus[i] == TeamId.Horde); + + Creature c = Source.FindNearestCreature(SharedConst.WorldTrigger, 500.0f); + if (c) + SendChatMessage(c, Source.GetTeamId() == TeamId.Alliance ? SATextIds.WestGraveyardCapturedA : SATextIds.WestGraveyardCapturedH, Source); + } + break; + case SAGraveyards.RightCapturableGy: + { + flag = SAObjectTypes.RightFlag; + DelObject(flag); + AddObject(flag, (SAMiscConst.ObjEntries[flag] - (Source.GetTeamId() == TeamId.Alliance ? 0 : 1u)), + SAMiscConst.ObjSpawnlocs[flag], 0, 0, 0, 0, BattlegroundConst.RespawnOneDay); + + npc = SACreatureTypes.Sparklight; + Creature sparklight = AddCreature(SAMiscConst.NpcEntries[npc], (int)npc, SAMiscConst.NpcSpawnlocs[npc], Attackers); + if (sparklight) + sparklight.GetAI().Talk(SATextIds.SparklightRigsparkSpawn); + + for (byte j = SACreatureTypes.Demolisher5; j <= SACreatureTypes.Demolisher6; j++) + { + AddCreature(SAMiscConst.NpcEntries[j], j, SAMiscConst.NpcSpawnlocs[j], Attackers == TeamId.Alliance ? TeamId.Horde : TeamId.Alliance, 600); + + Creature dem = GetBGCreature(j); + if (dem) + dem.SetFaction(SAMiscConst.Factions[Attackers]); + } + + UpdateWorldState(SAWorldStateIds.RightGyAlliance, GraveyardStatus[i] == TeamId.Alliance); + UpdateWorldState(SAWorldStateIds.RightGyHorde, GraveyardStatus[i] == TeamId.Horde); + + Creature c = Source.FindNearestCreature(SharedConst.WorldTrigger, 500.0f); + if (c) + SendChatMessage(c, Source.GetTeamId() == TeamId.Alliance ? SATextIds.EastGraveyardCapturedA : SATextIds.EastGraveyardCapturedH, Source); + } + break; + case SAGraveyards.CentralCapturableGy: + { + flag = SAObjectTypes.CentralFlag; + DelObject(flag); + AddObject(flag, (SAMiscConst.ObjEntries[flag] - (Source.GetTeamId() == TeamId.Alliance ? 0 : 1u)), + SAMiscConst.ObjSpawnlocs[flag], 0, 0, 0, 0, BattlegroundConst.RespawnOneDay); + + UpdateWorldState(SAWorldStateIds.CenterGyAlliance, GraveyardStatus[i] == TeamId.Alliance); + UpdateWorldState(SAWorldStateIds.CenterGyHorde, GraveyardStatus[i] == TeamId.Horde); + + Creature c = Source.FindNearestCreature(SharedConst.WorldTrigger, 500.0f); + if (c) + SendChatMessage(c, Source.GetTeamId() == TeamId.Alliance ? SATextIds.SouthGraveyardCapturedA : SATextIds.SouthGraveyardCapturedH, Source); + } + break; + default: + //ABORT(); + break; + }; + } + + void TitanRelicActivated(Player clicker) + { + if (!clicker) + return; + + if (CanInteractWithObject(SAObjectTypes.TitanRelic)) + { + if (clicker.GetTeamId() == Attackers) + { + if (clicker.GetTeamId() == TeamId.Alliance) + SendMessageToAll(CypherStrings.BgSaAllianceCapturedRelic, ChatMsg.BgSystemNeutral); + else + SendMessageToAll(CypherStrings.BgSaHordeCapturedRelic, ChatMsg.BgSystemNeutral); + + if (Status == SAStatus.RoundOne) + { + RoundScores[0].winner = (uint)Attackers; + RoundScores[0].time = TotalTime; + // Achievement Storm the Beach (1310) + foreach (var pair in GetPlayers()) + { + Player player = Global.ObjAccessor.FindPlayer(pair.Key); + if (player) + if (player.GetTeamId() == Attackers) + player.UpdateCriteria(CriteriaTypes.BeSpellTarget, 65246); + } + + Attackers = (Attackers == TeamId.Alliance) ? TeamId.Horde : TeamId.Alliance; + Status = SAStatus.SecondWarmup; + TotalTime = 0; + ToggleTimer(); + + Creature c = GetBGCreature(SACreatureTypes.Kanrethad); + if (c) + SendChatMessage(c, SATextIds.Round1Finished); + + UpdateWaitTimer = 5000; + SignaledRoundTwo = false; + SignaledRoundTwoHalfMin = false; + InitSecondRound = true; + ResetObjs(); + GetBgMap().UpdateAreaDependentAuras(); + CastSpellOnTeam(SASpellIds.EndOfRound, Team.Alliance); + CastSpellOnTeam(SASpellIds.EndOfRound, Team.Horde); + } + else if (Status == SAStatus.RoundTwo) + { + RoundScores[1].winner = (uint)Attackers; + RoundScores[1].time = TotalTime; + ToggleTimer(); + // Achievement Storm the Beach (1310) + foreach (var pair in GetPlayers()) + { + Player player = Global.ObjAccessor.FindPlayer(pair.Key); + if (player) + if (player.GetTeamId() == Attackers && RoundScores[1].winner == Attackers) + player.UpdateCriteria(CriteriaTypes.BeSpellTarget, 65246); + } + + if (RoundScores[0].time == RoundScores[1].time) + EndBattleground(0); + else if (RoundScores[0].time < RoundScores[1].time) + EndBattleground(RoundScores[0].winner == TeamId.Alliance ? Team.Alliance : Team.Horde); + else + EndBattleground(RoundScores[1].winner == TeamId.Alliance ? Team.Alliance : Team.Horde); + } + } + } + } + + void ToggleTimer() + { + TimerEnabled = !TimerEnabled; + UpdateWorldState(SAWorldStateIds.EnableTimer, TimerEnabled); + } + + public override void EndBattleground(Team winner) + { + // honor reward for winning + if (winner == Team.Alliance) + RewardHonorToTeam(GetBonusHonorFromKill(1), Team.Alliance); + else if (winner == Team.Horde) + RewardHonorToTeam(GetBonusHonorFromKill(1), Team.Horde); + + // complete map_end rewards (even if no team wins) + RewardHonorToTeam(GetBonusHonorFromKill(2), Team.Alliance); + RewardHonorToTeam(GetBonusHonorFromKill(2), Team.Horde); + + base.EndBattleground(winner); + } + + void UpdateDemolisherSpawns() + { + for (byte i = SACreatureTypes.Demolisher1; i <= SACreatureTypes.Demolisher8; i++) + { + if (!BgCreatures[i].IsEmpty()) + { + Creature Demolisher = GetBGCreature(i); + if (Demolisher) + { + if (Demolisher.IsDead()) + { + // Demolisher is not in list + if (!DemoliserRespawnList.ContainsKey(i)) + { + DemoliserRespawnList[i] = Time.GetMSTime() + 30000; + } + else + { + if (DemoliserRespawnList[i] < Time.GetMSTime()) + { + Demolisher.Relocate(SAMiscConst.NpcSpawnlocs[i]); + Demolisher.Respawn(); + DemoliserRespawnList.Remove(i); + } + } + } + } + } + } + } + + void SendTransportInit(Player player) + { + if (!BgObjects[SAObjectTypes.BoatOne].IsEmpty() || !BgObjects[SAObjectTypes.BoatTwo].IsEmpty()) + { + UpdateData transData = new UpdateData(player.GetMapId()); + if (!BgObjects[SAObjectTypes.BoatOne].IsEmpty()) + GetBGObject(SAObjectTypes.BoatOne).BuildCreateUpdateBlockForPlayer(transData, player); + + if (!BgObjects[SAObjectTypes.BoatTwo].IsEmpty()) + GetBGObject(SAObjectTypes.BoatTwo).BuildCreateUpdateBlockForPlayer(transData, player); + + UpdateObject packet; + transData.BuildPacket(out packet); + player.SendPacket(packet); + } + } + + void SendTransportsRemove(Player player) + { + if (!BgObjects[SAObjectTypes.BoatOne].IsEmpty() || !BgObjects[SAObjectTypes.BoatTwo].IsEmpty()) + { + UpdateData transData = new UpdateData(player.GetMapId()); + if (!BgObjects[SAObjectTypes.BoatOne].IsEmpty()) + GetBGObject(SAObjectTypes.BoatOne).BuildOutOfRangeUpdateBlock(transData); + if (!BgObjects[SAObjectTypes.BoatTwo].IsEmpty()) + GetBGObject(SAObjectTypes.BoatTwo).BuildOutOfRangeUpdateBlock(transData); + + UpdateObject packet; + transData.BuildPacket(out packet); + player.SendPacket(packet); + } + } + + public override bool CheckAchievementCriteriaMeet(uint criteriaId, Player source, Unit target, uint miscValue) + { + switch ((BattlegroundCriteriaId)criteriaId) + { + case BattlegroundCriteriaId.NotEvenAScratch: + return _allVehiclesAlive[GetTeamIndexByTeamId(source.GetTeam())]; + case BattlegroundCriteriaId.DefenseOfTheAncients: + return source.GetTeamId() != Attackers && !_gateDestroyed; + } + + return base.CheckAchievementCriteriaMeet(criteriaId, source, target, miscValue); + } + + public override bool IsSpellAllowed(uint spellId, Player player) + { + switch (spellId) + { + case SASpellIds.AllianceControlPhaseShift: + return Attackers == TeamId.Horde; + case SASpellIds.HordeControlPhaseShift: + return Attackers == TeamId.Alliance; + case BattlegroundConst.SpellPreparation: + return Status == SAStatus.Warmup || Status == SAStatus.SecondWarmup; + default: + break; + } + + return true; + } + + SAGateInfo GetGate(uint entry) + { + foreach (var gate in SAMiscConst.Gates) + if (gate.GameObjectId == entry) + return gate; + + return null; + } + + /// Id of attacker team + int Attackers; + + // Totale elapsed time of current round + uint TotalTime; + // Max time of round + uint EndRoundTimer; + // For know if boats has start moving or not yet + bool ShipsStarted; + // Status of each gate (Destroy/Damage/Intact) + SAGateState[] GateStatus = new SAGateState[SAMiscConst.Gates.Length]; + // Statu of battle (Start or not, and what round) + SAStatus Status; + // Team witch conntrol each graveyard + int[] GraveyardStatus = new int[SAGraveyards.Max]; + // Score of each round + SARoundScore[] RoundScores = new SARoundScore[2]; + // used for know we are in timer phase or not (used for worldstate update) + bool TimerEnabled; + // 5secs before starting the 1min countdown for second round + uint UpdateWaitTimer; + // for know if warning about second round start has been sent + bool SignaledRoundTwo; + // for know if warning about second round start has been sent + bool SignaledRoundTwoHalfMin; + // for know if second round has been init + bool InitSecondRound; + Dictionary DemoliserRespawnList = new Dictionary(); + + // Achievement: Defense of the Ancients + bool _gateDestroyed; + + // Achievement: Not Even a Scratch + bool[] _allVehiclesAlive = new bool[SharedConst.BGTeamsCount]; } - public struct SACreatureIds + class BattlegroundSAScore : BattlegroundScore + { + public BattlegroundSAScore(ObjectGuid playerGuid, Team team) : base(playerGuid, team) { } + + public override void UpdateScore(ScoreType type, uint value) + { + switch (type) + { + case ScoreType.DestroyedDemolisher: + DemolishersDestroyed += value; + break; + case ScoreType.DestroyedWall: + GatesDestroyed += value; + break; + default: + base.UpdateScore(type, value); + break; + } + } + + public override void BuildPvPLogPlayerDataPacket(out PVPLogData.PlayerData playerData) + { + base.BuildPvPLogPlayerDataPacket(out playerData); + + playerData.Stats.Add(DemolishersDestroyed); + playerData.Stats.Add(GatesDestroyed); + } + + public override uint GetAttr1() { return DemolishersDestroyed; } + public override uint GetAttr2() { return GatesDestroyed; } + + uint DemolishersDestroyed; + uint GatesDestroyed; + } + + struct SARoundScore + { + public uint winner; + public uint time; + } + + class SAGateInfo + { + public SAGateInfo(uint gateId, uint gameObjectId, uint worldState, uint damagedText, uint destroyedText) + { + GateId = gateId; + GameObjectId = gameObjectId; + WorldState = worldState; + DamagedText = damagedText; + DestroyedText = destroyedText; + } + + public uint GateId; + public uint GameObjectId; + public uint WorldState; + public uint DamagedText; + public uint DestroyedText; + } + + #region Consts + struct SAMiscConst + { + public static uint[] NpcEntries = + { + SACreatureIds.AntiPersonnalCannon, + SACreatureIds.AntiPersonnalCannon, + SACreatureIds.AntiPersonnalCannon, + SACreatureIds.AntiPersonnalCannon, + SACreatureIds.AntiPersonnalCannon, + SACreatureIds.AntiPersonnalCannon, + SACreatureIds.AntiPersonnalCannon, + SACreatureIds.AntiPersonnalCannon, + SACreatureIds.AntiPersonnalCannon, + SACreatureIds.AntiPersonnalCannon, + // 4 beach demolishers + SACreatureIds.Demolisher, + SACreatureIds.Demolisher, + SACreatureIds.Demolisher, + SACreatureIds.Demolisher, + // 4 factory demolishers + SACreatureIds.Demolisher, + SACreatureIds.Demolisher, + SACreatureIds.Demolisher, + SACreatureIds.Demolisher, + // Used Demolisher Salesman + SACreatureIds.RiggerSparklight, + SACreatureIds.GorgrilRigspark, + // Kanrethad + SACreatureIds.Kanrethad + }; + + public static Position[] NpcSpawnlocs = + { + // Cannons + new Position(1436.429f, 110.05f, 41.407f, 5.4f), + new Position(1404.9023f, 84.758f, 41.183f, 5.46f), + new Position(1068.693f, -86.951f, 93.81f, 0.02f), + new Position(1068.83f, -127.56f, 96.45f, 0.0912f), + new Position(1422.115f, -196.433f, 42.1825f, 1.0222f), + new Position(1454.887f, -220.454f, 41.956f, 0.9627f), + new Position(1232.345f, -187.517f, 66.945f, 0.45f), + new Position(1249.634f, -224.189f, 66.72f, 0.635f), + new Position(1236.213f, 92.287f, 64.965f, 5.751f), + new Position(1215.11f, 57.772f, 64.739f, 5.78f), + // Demolishers + new Position(1611.597656f, -117.270073f, 8.719355f, 2.513274f), + new Position(1575.562500f, -158.421875f, 5.024450f, 2.129302f), + new Position(1618.047729f, 61.424641f, 7.248210f, 3.979351f), + new Position(1575.103149f, 98.873344f, 2.830360f, 3.752458f), + // Demolishers 2 + new Position(1371.055786f, -317.071136f, 35.007359f, 1.947460f), + new Position(1424.034912f, -260.195190f, 31.084425f, 2.820013f), + new Position(1353.139893f, 223.745438f, 35.265411f, 4.343684f), + new Position(1404.809570f, 197.027237f, 32.046032f, 3.605401f), + // Npcs + new Position(1348.644165f, -298.786469f, 31.080130f, 1.710423f), + new Position(1358.191040f, 195.527786f, 31.018187f, 4.171337f), + new Position(841.921f, -134.194f, 196.838f, 6.23082f) + }; + + public static Position[] ObjSpawnlocs = + { + new Position(1411.57f, 108.163f, 28.692f, 5.441f), + new Position(1055.452f, -108.1f, 82.134f, 0.034f), + new Position(1431.3413f, -219.437f, 30.893f, 0.9736f), + new Position(1227.667f, -212.555f, 55.372f, 0.5023f), + new Position(1214.681f, 81.21f, 53.413f, 5.745f), + new Position(878.555f, -108.2f, 117.845f, 0.0f), + new Position(836.5f, -108.8f, 120.219f, 0.0f), + // Portal + new Position(1468.380005f, -225.798996f, 30.896200f, 0.0f), //blue + new Position(1394.270020f, 72.551399f, 31.054300f, 0.0f), //green + new Position(1065.260010f, -89.79501f, 81.073402f, 0.0f), //yellow + new Position(1216.069946f, 47.904301f, 54.278198f, 0.0f), //purple + new Position(1255.569946f, -233.548996f, 56.43699f, 0.0f), //red + // Ships + new Position(2679.696777f, -826.891235f, 3.712860f, 5.78367f), //rot2 1 rot3 0.0002f + new Position(2574.003662f, 981.261475f, 2.603424f, 0.807696f), + // Sigils + new Position(1414.054f, 106.72f, 41.442f, 5.441f), + new Position(1060.63f, -107.8f, 94.7f, 0.034f), + new Position(1433.383f, -216.4f, 43.642f, 0.9736f), + new Position(1230.75f, -210.724f, 67.611f, 0.5023f), + new Position(1217.8f, 79.532f, 66.58f, 5.745f), + // Flagpoles + new Position(1215.114258f, -65.711861f, 70.084267f, -3.124123f), + new Position(1338.863892f, -153.336533f, 30.895121f, -2.530723f), + new Position(1309.124268f, 9.410645f, 30.893402f, -1.623156f), + // Flags + new Position(1215.108032f, -65.715767f, 70.084267f, -3.124123f), + new Position(1338.859253f, -153.327316f, 30.895077f, -2.530723f), + new Position(1309.192017f, 9.416233f, 30.893402f, 1.518436f), + // Bombs + new Position(1333.45f, 211.354f, 31.0538f, 5.03666f), + new Position(1334.29f, 209.582f, 31.0532f, 1.28088f), + new Position(1332.72f, 210.049f, 31.0532f, 1.28088f), + new Position(1334.28f, 210.78f, 31.0538f, 3.85856f), + new Position(1332.64f, 211.39f, 31.0532f, 1.29266f), + new Position(1371.41f, 194.028f, 31.5107f, 0.753095f), + new Position(1372.39f, 194.951f, 31.4679f, 0.753095f), + new Position(1371.58f, 196.942f, 30.9349f, 1.01777f), + new Position(1370.43f, 196.614f, 30.9349f, 0.957299f), + new Position(1369.46f, 196.877f, 30.9351f, 2.45348f), + new Position(1370.35f, 197.361f, 30.9349f, 1.08689f), + new Position(1369.47f, 197.941f, 30.9349f, 0.984787f), + new Position(1592.49f, 47.5969f, 7.52271f, 4.63218f), + new Position(1593.91f, 47.8036f, 7.65856f, 4.63218f), + new Position(1593.13f, 46.8106f, 7.54073f, 4.63218f), + new Position(1589.22f, 36.3616f, 7.45975f, 4.64396f), + new Position(1588.24f, 35.5842f, 7.55613f, 4.79564f), + new Position(1588.14f, 36.7611f, 7.49675f, 4.79564f), + new Position(1595.74f, 35.5278f, 7.46602f, 4.90246f), + new Position(1596, 36.6475f, 7.47991f, 4.90246f), + new Position(1597.03f, 36.2356f, 7.48631f, 4.90246f), + new Position(1597.93f, 37.1214f, 7.51725f, 4.90246f), + new Position(1598.16f, 35.888f, 7.50018f, 4.90246f), + new Position(1579.6f, -98.0917f, 8.48478f, 1.37996f), + new Position(1581.2f, -98.401f, 8.47483f, 1.37996f), + new Position(1580.38f, -98.9556f, 8.4772f, 1.38781f), + new Position(1585.68f, -104.966f, 8.88551f, 0.493246f), + new Position(1586.15f, -106.033f, 9.10616f, 0.493246f), + new Position(1584.88f, -105.394f, 8.82985f, 0.493246f), + new Position(1581.87f, -100.899f, 8.46164f, 0.929142f), + new Position(1581.48f, -99.4657f, 8.46926f, 0.929142f), + new Position(1583.2f, -91.2291f, 8.49227f, 1.40038f), + new Position(1581.94f, -91.0119f, 8.49977f, 1.40038f), + new Position(1582.33f, -91.951f, 8.49353f, 1.1844f), + new Position(1342.06f, -304.049f, 30.9532f, 5.59507f), + new Position(1340.96f, -304.536f, 30.9458f, 1.28323f), + new Position(1341.22f, -303.316f, 30.9413f, 0.486051f), + new Position(1342.22f, -302.939f, 30.986f, 4.87643f), + new Position(1382.16f, -287.466f, 32.3063f, 4.80968f), + new Position(1381, -287.58f, 32.2805f, 4.80968f), + new Position(1381.55f, -286.536f, 32.3929f, 2.84225f), + new Position(1382.75f, -286.354f, 32.4099f, 1.00442f), + new Position(1379.92f, -287.34f, 32.2872f, 3.81615f), + new Position(1100.52f, -2.41391f, 70.2984f, 0.131054f), + new Position(1099.35f, -2.13851f, 70.3375f, 4.4586f), + new Position(1099.59f, -1.00329f, 70.238f, 2.49903f), + new Position(1097.79f, 0.571316f, 70.159f, 4.00307f), + new Position(1098.74f, -7.23252f, 70.7972f, 4.1523f), + new Position(1098.46f, -5.91443f, 70.6715f, 4.1523f), + new Position(1097.53f, -7.39704f, 70.7959f, 4.1523f), + new Position(1097.32f, -6.64233f, 70.7424f, 4.1523f), + new Position(1096.45f, -5.96664f, 70.7242f, 4.1523f), + new Position(971.725f, 0.496763f, 86.8467f, 2.09233f), + new Position(973.589f, 0.119518f, 86.7985f, 3.17225f), + new Position(972.524f, 1.25333f, 86.8351f, 5.28497f), + new Position(971.993f, 2.05668f, 86.8584f, 5.28497f), + new Position(973.635f, 2.11805f, 86.8197f, 2.36722f), + new Position(974.791f, 1.74679f, 86.7942f, 1.5936f), + new Position(974.771f, 3.0445f, 86.8125f, 0.647199f), + new Position(979.554f, 3.6037f, 86.7923f, 1.69178f), + new Position(979.758f, 2.57519f, 86.7748f, 1.76639f), + new Position(980.769f, 3.48904f, 86.7939f, 1.76639f), + new Position(979.122f, 2.87109f, 86.7794f, 1.76639f), + new Position(986.167f, 4.85363f, 86.8439f, 1.5779f), + new Position(986.176f, 3.50367f, 86.8217f, 1.5779f), + new Position(987.33f, 4.67389f, 86.8486f, 1.5779f), + new Position(985.23f, 4.65898f, 86.8368f, 1.5779f), + new Position(984.556f, 3.54097f, 86.8137f, 1.5779f), + }; + + public static uint[] ObjEntries = + { + 190722, + 190727, + 190724, + 190726, + 190723, + 192549, + 192834, + 192819, + 192819, + 192819, + 192819, + 192819, + 0, // Boat + 0, // Boat + 192687, + 192685, + 192689, + 192690, + 192691, + 191311, + 191311, + 191311, + 191310, + 191306, + 191308, + 190753 + }; + + public static uint[] Factions = { 1732, 1735, }; + + public static uint[] GYEntries = { 1350, 1349, 1347, 1346, 1348, }; + + public static float[] GYOrientation = + { + 6.202f, + 1.926f, // right capturable GY + 3.917f, // left capturable GY + 3.104f, // center, capturable + 6.148f, // defender last GY + }; + + public static SAGateInfo[] Gates = + { + new SAGateInfo(SAObjectTypes.GreenGate, SAGameObjectIds.GateOfTheGreenEmerald, SAWorldStateIds.GreenGate, SATextIds.GreenGateUnderAttack, SATextIds.GreenGateDestroyed), + new SAGateInfo(SAObjectTypes.YellowGate, SAGameObjectIds.GateOfTheYellowMoon, SAWorldStateIds.YellowGate, SATextIds.YellowGateUnderAttack, SATextIds.YellowGateDestroyed), + new SAGateInfo(SAObjectTypes.BlueGate, SAGameObjectIds.GateOfTheBlueSapphire, SAWorldStateIds.BlueGate, SATextIds.BlueGateUnderAttack, SATextIds.BlueGateDestroyed), + new SAGateInfo(SAObjectTypes.RedGate, SAGameObjectIds.GateOfTheRedSun, SAWorldStateIds.RedGate, SATextIds.RedGateUnderAttack, SATextIds.RedGateDestroyed), + new SAGateInfo(SAObjectTypes.PurpleGate, SAGameObjectIds.GateOfThePurpleAmethyst, SAWorldStateIds.PurpleGate, SATextIds.PurpleGateUnderAttack, SATextIds.PurpleGateDestroyed), + new SAGateInfo(SAObjectTypes.AncientGate, SAGameObjectIds.ChamberOfAncientRelics, SAWorldStateIds.AncientGate, SATextIds.AncientGateUnderAttack, SATextIds.AncientGateDestroyed) + }; + } + + enum SAStatus + { + NotStarted = 0, + Warmup, + RoundOne, + SecondWarmup, + RoundTwo, + BonusRound + } + + enum SAGateState + { + Ok = 1, + Damaged = 2, + Destroyed = 3 + } + + enum SAEventIds + { + BG_SA_EVENT_BLUE_GATE_DAMAGED = 19040, + BG_SA_EVENT_BLUE_GATE_DESTROYED = 19045, + + BG_SA_EVENT_GREEN_GATE_DAMAGED = 19041, + BG_SA_EVENT_GREEN_GATE_DESTROYED = 19046, + + BG_SA_EVENT_RED_GATE_DAMAGED = 19042, + BG_SA_EVENT_RED_GATE_DESTROYED = 19047, + + BG_SA_EVENT_PURPLE_GATE_DAMAGED = 19043, + BG_SA_EVENT_PURPLE_GATE_DESTROYED = 19048, + + BG_SA_EVENT_YELLOW_GATE_DAMAGED = 19044, + BG_SA_EVENT_YELLOW_GATE_DESTROYED = 19049, + + BG_SA_EVENT_ANCIENT_GATE_DAMAGED = 19836, + BG_SA_EVENT_ANCIENT_GATE_DESTROYED = 19837, + + BG_SA_EVENT_TITAN_RELIC_ACTIVATED = 22097 + } + + struct SASpellIds + { + public const uint TeleportDefender = 52364; + public const uint TeleportAttackers = 60178; + public const uint EndOfRound = 52459; + public const uint RemoveSeaforium = 59077; + public const uint AllianceControlPhaseShift = 60027; + public const uint HordeControlPhaseShift = 60028; + } + + struct SACreatureIds { public const uint Kanrethad = 29; public const uint InvisibleStalker = 15214; @@ -29,8 +1477,171 @@ namespace Game.BattleGrounds.Zones public const uint WorldTriggerLargeAoiNotImmunePcNpc = 23472; public const uint AntiPersonnalCannon = 27894; - public const uint DemolisherSa = 28781; + public const uint Demolisher = 28781; public const uint RiggerSparklight = 29260; public const uint GorgrilRigspark = 29262; } + + struct SAGameObjectIds + { + public const uint GateOfTheGreenEmerald = 190722; + public const uint GateOfThePurpleAmethyst = 190723; + public const uint GateOfTheBlueSapphire = 190724; + public const uint GateOfTheRedSun = 190726; + public const uint GateOfTheYellowMoon = 190727; + public const uint ChamberOfAncientRelics = 192549; + + public const uint BoatOneA = 208000; + public const uint BoatTwoA = 193185; + public const uint BoatOneH = 193184; + public const uint BoatTwoH = 208001; + } + + struct SATimers + { + public const uint BoatStart = 60 * Time.InMilliseconds; + public const uint WarmupLength = 120 * Time.InMilliseconds; + public const uint RoundLength = 600 * Time.InMilliseconds; + } + + struct SASoundIds + { + public const uint GraveyardTakenHorde = 8174; + public const uint GraveyardTakenAlliance = 8212; + public const uint DefeatHorde = 15905; + public const uint VictoryHorde = 15906; + public const uint VictoryAlliance = 15907; + public const uint DefeatAlliance = 15908; + public const uint WallDestroyedAlliance = 15909; + public const uint WallDestroyedHorde = 15910; + public const uint WallAttackedHorde = 15911; + public const uint WallAttackedAlliance = 15912; + } + + struct SATextIds + { + // Kanrethad + public const byte RoundStarted = 1; + public const byte Round1Finished = 2; + + // Rigger Sparklight / Gorgril Rigspark + public const byte SparklightRigsparkSpawn = 1; + + // World Trigger + public const byte BlueGateUnderAttack = 1; + public const byte GreenGateUnderAttack = 2; + public const byte RedGateUnderAttack = 3; + public const byte PurpleGateUnderAttack = 4; + public const byte YellowGateUnderAttack = 5; + public const byte YellowGateDestroyed = 6; + public const byte PurpleGateDestroyed = 7; + public const byte RedGateDestroyed = 8; + public const byte GreenGateDestroyed = 9; + public const byte BlueGateDestroyed = 10; + public const byte EastGraveyardCapturedA = 11; + public const byte WestGraveyardCapturedA = 12; + public const byte SouthGraveyardCapturedA = 13; + public const byte EastGraveyardCapturedH = 14; + public const byte WestGraveyardCapturedH = 15; + public const byte SouthGraveyardCapturedH = 16; + public const byte AncientGateUnderAttack = 17; + public const byte AncientGateDestroyed = 18; + } + + struct SAWorldStateIds + { + public const uint TimerMins = 3559; + public const uint TimerSecTens = 3560; + public const uint TimerSecDecs = 3561; + public const uint AllyAttacks = 4352; + public const uint HordeAttacks = 4353; + public const uint PurpleGate = 3614; + public const uint RedGate = 3617; + public const uint BlueGate = 3620; + public const uint GreenGate = 3623; + public const uint YellowGate = 3638; + public const uint AncientGate = 3849; + public const uint LeftGyAlliance = 3635; + public const uint RightGyAlliance = 3636; + public const uint CenterGyAlliance = 3637; + public const uint RightAttTokenAll = 3627; + public const uint LeftAttTokenAll = 3626; + public const uint LeftAttTokenHrd = 3629; + public const uint RightAttTokenHrd = 3628; + public const uint HordeDefenceToken = 3631; + public const uint AllianceDefenceToken = 3630; + public const uint RightGyHorde = 3632; + public const uint LeftGyHorde = 3633; + public const uint CenterGyHorde = 3634; + public const uint BonusTimer = 3571; + public const uint EnableTimer = 3564; + } + + struct SACreatureTypes + { + public const int Gun1 = 0; + public const int Gun2 = 1; + public const int Gun3 = 2; + public const int Gun4 = 3; + public const int Gun5 = 4; + public const int Gun6 = 5; + public const int Gun7 = 6; + public const int Gun8 = 7; + public const int Gun9 = 8; + public const int Gun10 = 9; + public const int Demolisher1 = 10; + public const int Demolisher2 = 11; + public const int Demolisher3 = 12; + public const int Demolisher4 = 13; + public const int Demolisher5 = 14; + public const int Demolisher6 = 15; + public const int Demolisher7 = 16; + public const int Demolisher8 = 17; + public const int Sparklight = 18; + public const int Rigspark = 19; + public const int Kanrethad = 20; + public const int Max = 21; + } + + struct SAObjectTypes + { + public const int GreenGate = 0; + public const int YellowGate = 1; + public const int BlueGate = 2; + public const int RedGate = 3; + public const int PurpleGate = 4; + public const int AncientGate = 5; + public const int TitanRelic = 6; + public const int PortalDeffenderBlue = 7; + public const int PortalDeffenderGreen = 8; + public const int PortalDeffenderYellow = 9; + public const int PortalDeffenderPurple = 10; + public const int PortalDeffenderRed = 11; + public const int BoatOne = 12; + public const int BoatTwo = 13; + public const int Sigil1 = 14; + public const int Sigil2 = 15; + public const int Sigil3 = 16; + public const int Sigil4 = 17; + public const int Sigil5 = 18; + public const int CentralFlagpole = 19; + public const int RightFlagpole = 20; + public const int LeftFlagpole = 21; + public const int CentralFlag = 22; + public const int RightFlag = 23; + public const int LeftFlag = 24; + public const int Bomb = 25; + public const int MaxObj = Bomb + 68; + } + + struct SAGraveyards + { + public const int BeachGy = 0; + public const int DefenderLastGy = 1; + public const int RightCapturableGy = 2; + public const int LeftCapturableGy = 3; + public const int CentralCapturableGy = 4; + public const int Max = 5; + } + #endregion } diff --git a/Game/BattleGrounds/Zones/WarsongGluch.cs b/Game/BattleGrounds/Zones/WarsongGluch.cs index 5f1c3ed85..0d90d2ac1 100644 --- a/Game/BattleGrounds/Zones/WarsongGluch.cs +++ b/Game/BattleGrounds/Zones/WarsongGluch.cs @@ -21,7 +21,7 @@ using Game.Entities; using Game.Network.Packets; using System.Collections.Generic; -namespace Game.BattleGrounds +namespace Game.BattleGrounds.Zones { class BgWarsongGluch : Battleground { diff --git a/Game/Chat/Commands/ListCommands.cs b/Game/Chat/Commands/ListCommands.cs index 9bb226402..6f42f307f 100644 --- a/Game/Chat/Commands/ListCommands.cs +++ b/Game/Chat/Commands/ListCommands.cs @@ -399,11 +399,11 @@ namespace Game.Chat.Commands string subject = result1.Read(5); long deliverTime = result1.Read(6); long expireTime = result1.Read(7); - uint money = result1.Read(8); + ulong money = result1.Read(8); byte hasItem = result1.Read(9); - uint gold = money / MoneyConstants.Gold; - uint silv = (money % MoneyConstants.Gold) / MoneyConstants.Silver; - uint copp = (money % MoneyConstants.Gold) % MoneyConstants.Silver; + uint gold = (uint)(money / MoneyConstants.Gold); + uint silv = (uint)(money % MoneyConstants.Gold) / MoneyConstants.Silver; + uint copp = (uint)(money % MoneyConstants.Gold) % MoneyConstants.Silver; string receiverStr = handler.playerLink(receiver); string senderStr = handler.playerLink(sender); handler.SendSysMessage(CypherStrings.ListMailInfo1, messageId, subject, gold, silv, copp); diff --git a/Game/Chat/Commands/MiscCommands.cs b/Game/Chat/Commands/MiscCommands.cs index 2979556ca..f6ecedeb6 100644 --- a/Game/Chat/Commands/MiscCommands.cs +++ b/Game/Chat/Commands/MiscCommands.cs @@ -1435,7 +1435,7 @@ namespace Game.Chat totalPlayerTime = result.Read(0); level = result.Read(1); - money = result.Read(2); + money = result.Read(2); accId = result.Read(3); raceid = (Race)result.Read(4); classid = (Class)result.Read(5); diff --git a/Game/Chat/Commands/ModifyCommands.cs b/Game/Chat/Commands/ModifyCommands.cs index e3bbdccf4..c198d7ad1 100644 --- a/Game/Chat/Commands/ModifyCommands.cs +++ b/Game/Chat/Commands/ModifyCommands.cs @@ -299,14 +299,14 @@ namespace Game.Chat if (handler.HasLowerSecurity(target, ObjectGuid.Empty)) return false; - long addmoney = args.NextInt64(); - long moneyuser = (long)target.GetMoney(); + long moneyToAdd = args.NextInt64(); + ulong targetMoney = target.GetMoney(); - if (addmoney < 0) + if (moneyToAdd < 0) { - ulong newmoney = (ulong)(moneyuser + addmoney); + ulong newmoney = (targetMoney + (ulong)moneyToAdd); - Log.outDebug(LogFilter.ChatSystem, Global.ObjectMgr.GetCypherString(CypherStrings.CurrentMoney), moneyuser, addmoney, newmoney); + Log.outDebug(LogFilter.ChatSystem, Global.ObjectMgr.GetCypherString(CypherStrings.CurrentMoney), targetMoney, moneyToAdd, newmoney); if (newmoney <= 0) { handler.SendSysMessage(CypherStrings.YouTakeAllMoney, handler.GetNameLink(target)); @@ -317,28 +317,31 @@ namespace Game.Chat } else { + ulong moneyToAddMsg = (ulong)(moneyToAdd * -1); if (newmoney > PlayerConst.MaxMoneyAmount) newmoney = PlayerConst.MaxMoneyAmount; - handler.SendSysMessage(CypherStrings.YouTakeMoney, Math.Abs(addmoney), handler.GetNameLink(target)); + handler.SendSysMessage(CypherStrings.YouTakeMoney, Math.Abs(moneyToAdd), handler.GetNameLink(target)); if (handler.needReportToTarget(target)) - target.SendSysMessage(CypherStrings.YoursMoneyTaken, handler.GetNameLink(), Math.Abs(addmoney)); + target.SendSysMessage(CypherStrings.YoursMoneyTaken, handler.GetNameLink(), Math.Abs(moneyToAdd)); target.SetMoney(newmoney); } } else { - handler.SendSysMessage(CypherStrings.YouGiveMoney, addmoney, handler.GetNameLink(target)); + handler.SendSysMessage(CypherStrings.YouGiveMoney, moneyToAdd, handler.GetNameLink(target)); if (handler.needReportToTarget(target)) - target.SendSysMessage(CypherStrings.YoursMoneyGiven, handler.GetNameLink(), addmoney); + target.SendSysMessage(CypherStrings.YoursMoneyGiven, handler.GetNameLink(), moneyToAdd); - if (addmoney >= PlayerConst.MaxMoneyAmount) - target.SetMoney(PlayerConst.MaxMoneyAmount); - else - target.ModifyMoney(addmoney); + if ((ulong)moneyToAdd >= PlayerConst.MaxMoneyAmount) + moneyToAdd = Convert.ToInt64(PlayerConst.MaxMoneyAmount); + + moneyToAdd = Math.Min(moneyToAdd, (long)(PlayerConst.MaxMoneyAmount - targetMoney)); + + target.ModifyMoney(moneyToAdd); } - Log.outDebug(LogFilter.ChatSystem, Global.ObjectMgr.GetCypherString(CypherStrings.NewMoney), moneyuser, addmoney, target.GetMoney()); + Log.outDebug(LogFilter.ChatSystem, Global.ObjectMgr.GetCypherString(CypherStrings.NewMoney), targetMoney, moneyToAdd, target.GetMoney()); return true; } diff --git a/Game/Chat/Commands/ReloadCommand.cs b/Game/Chat/Commands/ReloadCommand.cs index 7a35dc764..2a7074e68 100644 --- a/Game/Chat/Commands/ReloadCommand.cs +++ b/Game/Chat/Commands/ReloadCommand.cs @@ -872,9 +872,11 @@ namespace Game.Chat { Log.outInfo(LogFilter.Misc, "Re-Loading `trainer` Table!"); Global.ObjectMgr.LoadTrainers(); + Global.ObjectMgr.LoadCreatureDefaultTrainers(); handler.SendGlobalGMSysMessage("DB table `trainer` reloaded."); handler.SendGlobalGMSysMessage("DB table `trainer_locale` reloaded."); handler.SendGlobalGMSysMessage("DB table `trainer_spell` reloaded."); + handler.SendGlobalGMSysMessage("DB table `creature_default_trainer` reloaded."); return true; } diff --git a/Game/Chat/Commands/SendCommands.cs b/Game/Chat/Commands/SendCommands.cs index 37ac433a5..b0808417e 100644 --- a/Game/Chat/Commands/SendCommands.cs +++ b/Game/Chat/Commands/SendCommands.cs @@ -195,7 +195,7 @@ namespace Game.Chat.Commands return false; string moneyStr = args.NextString(""); - int money = !string.IsNullOrEmpty(moneyStr) ? int.Parse(moneyStr) : 0; + long money = !string.IsNullOrEmpty(moneyStr) ? long.Parse(moneyStr) : 0; if (money <= 0) return false; diff --git a/Game/Combat/Events.cs b/Game/Combat/Events.cs index b59bcbe5d..2a1866fb2 100644 --- a/Game/Combat/Events.cs +++ b/Game/Combat/Events.cs @@ -103,32 +103,6 @@ namespace Game.Combat ThreatManager iThreatManager; } - public class ThreatManagerEvent : ThreatRefStatusChangeEvent - { - ThreatManagerEvent(UnitEventTypes pType) - : base(pType) - { - iThreatContainer = null; - } - ThreatManagerEvent(UnitEventTypes pType, HostileReference pHostileReference) - : base(pType, pHostileReference) - { - iThreatContainer = null; - } - - void setThreatContainer(ThreatContainer pThreatContainer) - { - iThreatContainer = pThreatContainer; - } - - ThreatContainer getThreatContainer() - { - return iThreatContainer; - } - - ThreatContainer iThreatContainer; - } - public enum UnitEventTypes { // Player/Pet Changed On/Offline Status @@ -141,7 +115,7 @@ namespace Game.Combat ThreatRefRemoveFromList = 1 << 2, // Player/Pet Entered/Left Water Or Some Other Place Where It Is/Was Not Accessible For The Creature - ThreatRefAsseccibleStatus = 1 << 3, + ThreatRefAccessibleStatus = 1 << 3, // Threat List Is Going To Be Sorted (If Dirty Flag Is Set) ThreatSortList = 1 << 4, diff --git a/Game/Combat/HostileRegManager.cs b/Game/Combat/HostileRegManager.cs index 72d05b40a..197efa994 100644 --- a/Game/Combat/HostileRegManager.cs +++ b/Game/Combat/HostileRegManager.cs @@ -33,14 +33,15 @@ namespace Game.Combat Unit getOwner() { return Owner; } - // send threat to all my hateres for the victim - // The victim is hated than by them as well + // send threat to all my haters for the victim + // The victim is then hated by them as well // use for buffs and healing threat functionality public void threatAssist(Unit victim, float baseThreat, SpellInfo threatSpell = null) { - HostileReference refe = getFirst(); float threat = ThreatManager.calcThreat(victim, Owner, baseThreat, (threatSpell != null ? threatSpell.GetSchoolMask() : SpellSchoolMask.Normal), threatSpell); threat /= getSize(); + + HostileReference refe = getFirst(); while (refe != null) { if (ThreatManager.isValidProcess(victim, refe.GetSource().GetOwner(), threatSpell)) @@ -53,7 +54,6 @@ namespace Game.Combat public void addTempThreat(float threat, bool apply) { HostileReference refe = getFirst(); - while (refe != null) { if (apply) @@ -209,18 +209,20 @@ namespace Game.Combat public void addThreat(float modThreat) { + if (modThreat == 0.0f) + return; + iThreat += modThreat; + // the threat is changed. Source and target unit have to be available // if the link was cut before relink it again if (!isOnline()) updateOnlineStatus(); - if (modThreat != 0.0f) - { - ThreatRefStatusChangeEvent Event = new ThreatRefStatusChangeEvent(UnitEventTypes.ThreatRefThreatChange, this, modThreat); - fireStatusChanged(Event); - } - if (modThreat >= 0.0f) + ThreatRefStatusChangeEvent Event = new ThreatRefStatusChangeEvent(UnitEventTypes.ThreatRefThreatChange, this, modThreat); + fireStatusChanged(Event); + + if (isValid() && modThreat > 0.0f) { Unit victimOwner = getTarget().GetCharmerOrOwner(); if (victimOwner != null && victimOwner.IsAlive()) @@ -230,9 +232,7 @@ namespace Game.Combat public void addThreatPercent(int percent) { - float tmpThreat = iThreat; - MathFunctions.AddPct(ref tmpThreat, percent); - addThreat(tmpThreat - iThreat); + addThreat(MathFunctions.CalculatePct(iThreat, percent)); } // check, if source can reach target and set the status @@ -292,7 +292,7 @@ namespace Game.Combat { iAccessible = isAccessible; - ThreatRefStatusChangeEvent Event = new ThreatRefStatusChangeEvent(UnitEventTypes.ThreatRefAsseccibleStatus, this); + ThreatRefStatusChangeEvent Event = new ThreatRefStatusChangeEvent(UnitEventTypes.ThreatRefAccessibleStatus, this); fireStatusChanged(Event); } } @@ -313,12 +313,12 @@ namespace Game.Combat public void setThreat(float threat) { - addThreat(threat - getThreat()); + addThreat(threat - iThreat); } public float getThreat() { - return iThreat; + return iThreat + iTempThreatModifier; } public bool isOnline() @@ -333,27 +333,27 @@ namespace Game.Combat return iAccessible; } - // used for temporary setting a threat and reducting it later again. + // used for temporary setting a threat and reducing it later again. // the threat modification is stored public void setTempThreat(float threat) { - addTempThreat(threat - getThreat()); + addTempThreat(threat - iTempThreatModifier); } public void addTempThreat(float threat) { - iTempThreatModifier = threat; - if (iTempThreatModifier != 0.0f) - addThreat(iTempThreatModifier); + if (threat == 0.0f) + return; + + iTempThreatModifier += threat; + + ThreatRefStatusChangeEvent Event = new ThreatRefStatusChangeEvent(UnitEventTypes.ThreatRefThreatChange, this, threat); + fireStatusChanged(Event); } public void resetTempThreat() { - if (iTempThreatModifier != 0.0f) - { - addThreat(-iTempThreatModifier); - iTempThreatModifier = 0.0f; - } + addTempThreat(-iTempThreatModifier); } public float getTempThreatModifier() @@ -369,7 +369,7 @@ namespace Game.Combat public new HostileReference next() { return (HostileReference)base.next(); } float iThreat; - float iTempThreatModifier; // used for taunt + float iTempThreatModifier; // used for SPELL_AURA_MOD_TOTAL_THREAT ObjectGuid iUnitGuid; bool iOnline; bool iAccessible; diff --git a/Game/Conditions/ConditionManager.cs b/Game/Conditions/ConditionManager.cs index bf6d00491..ce3c810cc 100644 --- a/Game/Conditions/ConditionManager.cs +++ b/Game/Conditions/ConditionManager.cs @@ -1014,7 +1014,7 @@ namespace Game } break; } - case ConditionSourceType.QuestAccept: + case ConditionSourceType.QuestAvailable: if (Global.ObjectMgr.GetQuestTemplate((uint)cond.SourceEntry) == null) { Log.outError(LogFilter.Sql, "{0} SourceEntry specifies non-existing quest, skipped.", cond.ToString()); diff --git a/Game/DataStorage/CliDB.cs b/Game/DataStorage/CliDB.cs index aef84f867..01809cbb4 100644 --- a/Game/DataStorage/CliDB.cs +++ b/Game/DataStorage/CliDB.cs @@ -60,6 +60,8 @@ namespace Game.DataStorage BattlePetSpeciesStateStorage = DB6Reader.Read("BattlePetSpeciesState.db2", DB6Metas.BattlePetSpeciesStateMeta, HotfixStatements.SEL_BATTLE_PET_SPECIES_STATE); BattlemasterListStorage = DB6Reader.Read("BattlemasterList.db2", DB6Metas.BattlemasterListMeta, HotfixStatements.SEL_BATTLEMASTER_LIST, HotfixStatements.SEL_BATTLEMASTER_LIST_LOCALE); BroadcastTextStorage = DB6Reader.Read("BroadcastText.db2", DB6Metas.BroadcastTextMeta, HotfixStatements.SEL_BROADCAST_TEXT, HotfixStatements.SEL_BROADCAST_TEXT_LOCALE); + CharacterFacialHairStylesStorage = DB6Reader.Read("CharacterFacialHairStyles.db2", DB6Metas.CharacterFacialHairStylesMeta, HotfixStatements.SEL_CHARACTER_FACIAL_HAIR_STYLES); + CharBaseSectionStorage = DB6Reader.Read("CharBaseSection.db2", DB6Metas.CharBaseSectionMeta, HotfixStatements.SEL_CHAR_BASE_SECTION); CharSectionsStorage = DB6Reader.Read("CharSections.db2", DB6Metas.CharSectionsMeta, HotfixStatements.SEL_CHAR_SECTIONS); CharStartOutfitStorage = DB6Reader.Read("CharStartOutfit.db2", DB6Metas.CharStartOutfitMeta, HotfixStatements.SEL_CHAR_START_OUTFIT); CharTitlesStorage = DB6Reader.Read("CharTitles.db2", DB6Metas.CharTitlesMeta, HotfixStatements.SEL_CHAR_TITLES, HotfixStatements.SEL_CHAR_TITLES_LOCALE); @@ -239,6 +241,10 @@ namespace Game.DataStorage TaxiPathNodeStorage = DB6Reader.Read("TaxiPathNode.db2", DB6Metas.TaxiPathNodeMeta, HotfixStatements.SEL_TAXI_PATH_NODE); TotemCategoryStorage = DB6Reader.Read("TotemCategory.db2", DB6Metas.TotemCategoryMeta, HotfixStatements.SEL_TOTEM_CATEGORY, HotfixStatements.SEL_TOTEM_CATEGORY_LOCALE); ToyStorage = DB6Reader.Read("Toy.db2", DB6Metas.ToyMeta, HotfixStatements.SEL_TOY, HotfixStatements.SEL_TOY_LOCALE); + TransmogHolidayStorage = DB6Reader.Read("TransmogHoliday.db2", DB6Metas.TransmogHolidayMeta, HotfixStatements.SEL_TRANSMOG_HOLIDAY); + TransmogSetStorage = DB6Reader.Read("TransmogSet.db2", DB6Metas.TransmogSetMeta, HotfixStatements.SEL_TRANSMOG_SET, HotfixStatements.SEL_TRANSMOG_SET_LOCALE); + TransmogSetGroupStorage = DB6Reader.Read("TransmogSetGroup.db2", DB6Metas.TransmogSetGroupMeta, HotfixStatements.SEL_TRANSMOG_SET_GROUP, HotfixStatements.SEL_TRANSMOG_SET_GROUP_LOCALE); + TransmogSetItemStorage = DB6Reader.Read("TransmogSetItem.db2", DB6Metas.TransmogSetItemMeta, HotfixStatements.SEL_TRANSMOG_SET_ITEM); TransportAnimationStorage = DB6Reader.Read("TransportAnimation.db2", DB6Metas.TransportAnimationMeta, HotfixStatements.SEL_TRANSPORT_ANIMATION); TransportRotationStorage = DB6Reader.Read("TransportRotation.db2", DB6Metas.TransportRotationMeta, HotfixStatements.SEL_TRANSPORT_ROTATION); UnitPowerBarStorage = DB6Reader.Read("UnitPowerBar.db2", DB6Metas.UnitPowerBarMeta, HotfixStatements.SEL_UNIT_POWER_BAR, HotfixStatements.SEL_UNIT_POWER_BAR_LOCALE); @@ -378,6 +384,8 @@ namespace Game.DataStorage public static DB6Storage BattlePetSpeciesStateStorage; public static DB6Storage BattlemasterListStorage; public static DB6Storage BroadcastTextStorage; + public static DB6Storage CharacterFacialHairStylesStorage; + public static DB6Storage CharBaseSectionStorage; public static DB6Storage CharSectionsStorage; public static DB6Storage CharStartOutfitStorage; public static DB6Storage CharTitlesStorage; @@ -557,6 +565,10 @@ namespace Game.DataStorage public static DB6Storage TaxiPathNodeStorage; public static DB6Storage TotemCategoryStorage; public static DB6Storage ToyStorage; + public static DB6Storage TransmogHolidayStorage; + public static DB6Storage TransmogSetStorage; + public static DB6Storage TransmogSetGroupStorage; + public static DB6Storage TransmogSetItemStorage; public static DB6Storage TransportAnimationStorage; public static DB6Storage TransportRotationStorage; public static DB6Storage UnitPowerBarStorage; diff --git a/Game/DataStorage/DB2Manager.cs b/Game/DataStorage/DB2Manager.cs index bbff8519d..3e7ac43a3 100644 --- a/Game/DataStorage/DB2Manager.cs +++ b/Game/DataStorage/DB2Manager.cs @@ -59,44 +59,25 @@ namespace Game.DataStorage foreach (ArtifactPowerRankRecord artifactPowerRank in CliDB.ArtifactPowerRankStorage.Values) _artifactPowerRanks[Tuple.Create((uint)artifactPowerRank.ArtifactPowerID, artifactPowerRank.Rank)] = artifactPowerRank; - MultiMap> addedSections = new MultiMap>(); + foreach (CharacterFacialHairStylesRecord characterFacialStyle in CliDB.CharacterFacialHairStylesStorage.Values) + _characterFacialHairStyles.Add(Tuple.Create(characterFacialStyle.RaceID, characterFacialStyle.SexID, (uint)characterFacialStyle.VariationID)); + + CharBaseSectionVariation[] sectionToBase = new CharBaseSectionVariation[(int)CharSectionType.Max]; + foreach (CharBaseSectionRecord charBaseSection in CliDB.CharBaseSectionStorage.Values) + { + Contract.Assert(charBaseSection.ResolutionVariation < (byte)CharSectionType.Max, $"SECTION_TYPE_MAX ({(byte)CharSectionType.Max}) must be equal to or greater than {charBaseSection.ResolutionVariation + 1}"); + Contract.Assert(charBaseSection.Variation < CharBaseSectionVariation.Max, $"CharBaseSectionVariation.Max {(byte)CharBaseSectionVariation.Max} must be equal to or greater than {charBaseSection.Variation + 1}"); + + sectionToBase[charBaseSection.ResolutionVariation] = (CharBaseSectionVariation)charBaseSection.Variation; + } + + MultiMap, Tuple> addedSections = new MultiMap, Tuple>(); foreach (CharSectionsRecord charSection in CliDB.CharSectionsStorage.Values) { - if (charSection.Race == 0 || !Convert.ToBoolean((1 << (charSection.Race - 1)) & (int)Race.RaceMaskAllPlayable)) //ignore Nonplayable races - continue; + Contract.Assert(charSection.BaseSection < (byte)CharSectionType.Max, $"SECTION_TYPE_MAX ({(byte)CharSectionType.Max}) must be equal to or greater than {charSection.BaseSection + 1}"); - // Not all sections are used for low-res models but we need to get all sections for validation since its viewer dependent - byte baseSection = charSection.GenType; - switch ((CharSectionType)baseSection) - { - case CharSectionType.SkinLowRes: - case CharSectionType.FaceLowRes: - case CharSectionType.FacialHairLowRes: - case CharSectionType.HairLowRes: - case CharSectionType.UnderwearLowRes: - baseSection = (byte)(baseSection + (byte)CharSectionType.Skin); - break; - case CharSectionType.Skin: - case CharSectionType.Face: - case CharSectionType.FacialHair: - case CharSectionType.Hair: - case CharSectionType.Underwear: - break; - case CharSectionType.CustomDisplay1LowRes: - case CharSectionType.CustomDisplay2LowRes: - case CharSectionType.CustomDisplay3LowRes: - ++baseSection; - break; - case CharSectionType.CustomDisplay1: - case CharSectionType.CustomDisplay2: - case CharSectionType.CustomDisplay3: - break; - default: - break; - } - - uint sectionKey = (uint)(baseSection | (charSection.Gender << 8) | (charSection.Race << 16)); - Tuple sectionCombination = Tuple.Create(charSection.Type, charSection.Color); + Tuple sectionKey = Tuple.Create(charSection.RaceID, charSection.SexID, sectionToBase[charSection.BaseSection]); + Tuple sectionCombination = Tuple.Create(charSection.VariationIndex, charSection.ColorIndex); if (addedSections.Contains(sectionKey, sectionCombination)) continue; @@ -382,6 +363,16 @@ namespace Game.DataStorage foreach (ToyRecord toy in CliDB.ToyStorage.Values) _toys.Add(toy.ItemID); + foreach (TransmogSetItemRecord transmogSetItem in CliDB.TransmogSetItemStorage.Values) + { + TransmogSetRecord set = CliDB.TransmogSetStorage.LookupByKey(transmogSetItem.TransmogSetID); + if (set == null) + continue; + + _transmogSetsByItemModifiedAppearance.Add(transmogSetItem.ItemModifiedAppearanceID, set); + _transmogSetItemsByTransmogSet.Add(transmogSetItem.TransmogSetID, transmogSetItem); + } + foreach (WMOAreaTableRecord entry in CliDB.WMOAreaTableStorage.Values) _wmoAreaTableLookup[Tuple.Create(entry.WMOID, entry.NameSet, entry.WMOGroupID)] = entry; @@ -523,11 +514,21 @@ namespace Game.DataStorage return broadcastText.MaleText[SharedConst.DefaultLocale]; } - public CharSectionsRecord GetCharSectionEntry(Race race, CharSectionType genType, Gender gender, byte type, byte color) + public bool HasCharacterFacialHairStyle(Race race, Gender gender, uint variationId) { - var list = _charSections.LookupByKey((byte)genType | ((byte)gender << 8) | ((byte)race << 16)); + return _characterFacialHairStyles.Contains(Tuple.Create((byte)race, (byte)gender, variationId)); + } + + public bool HasCharSections(Race race, Gender gender, CharBaseSectionVariation variation) + { + return _charSections.ContainsKey(Tuple.Create(race, gender, variation)); + } + + public CharSectionsRecord GetCharSectionEntry(Race race, Gender gender, CharBaseSectionVariation variation, byte variationIndex, byte colorIndex) + { + var list = _charSections.LookupByKey(Tuple.Create(race, gender, variation)); foreach (var charSection in list) - if (charSection.Type == type && charSection.Color == color) + if (charSection.VariationIndex == variationIndex && charSection.ColorIndex == colorIndex) return charSection; return null; @@ -1200,6 +1201,16 @@ namespace Game.DataStorage return _toys.Contains(toy); } + public List GetTransmogSetsForItemModifiedAppearance(uint itemModifiedAppearanceId) + { + return _transmogSetsByItemModifiedAppearance.LookupByKey(itemModifiedAppearanceId); + } + + public List GetTransmogSetItems(uint transmogSetId) + { + return _transmogSetItemsByTransmogSet.LookupByKey(transmogSetId); + } + public WMOAreaTableRecord GetWMOAreaTable(int rootId, int adtId, int groupId) { var wmoAreaTable = _wmoAreaTableLookup.LookupByKey(Tuple.Create((short)rootId, (sbyte)adtId, groupId)); @@ -1307,7 +1318,8 @@ namespace Game.DataStorage MultiMap _artifactPowers = new MultiMap(); MultiMap _artifactPowerLinks = new MultiMap(); Dictionary, ArtifactPowerRankRecord> _artifactPowerRanks = new Dictionary, ArtifactPowerRankRecord>(); - MultiMap _charSections = new MultiMap(); + List> _characterFacialHairStyles = new List>(); + MultiMap, CharSectionsRecord> _charSections = new MultiMap, CharSectionsRecord>(); Dictionary _charStartOutfits = new Dictionary(); uint[][] _powersByClass = new uint[(int)Class.Max][]; ChrSpecializationRecord[][] _chrSpecializationsByIndex = new ChrSpecializationRecord[(int)Class.Max + 1][]; @@ -1348,6 +1360,8 @@ namespace Game.DataStorage MultiMap _spellProcsPerMinuteMods = new MultiMap(); List[][][] _talentsByPosition = new List[(int)Class.Max][][]; List _toys = new List(); + MultiMap _transmogSetsByItemModifiedAppearance = new MultiMap(); + MultiMap _transmogSetItemsByTransmogSet = new MultiMap(); Dictionary, WMOAreaTableRecord> _wmoAreaTableLookup = new Dictionary, WMOAreaTableRecord>(); Dictionary _worldMapAreaByAreaID = new Dictionary(); } diff --git a/Game/DataStorage/Structs/C_Records.cs b/Game/DataStorage/Structs/C_Records.cs index 9ba9cbe53..d08a111b1 100644 --- a/Game/DataStorage/Structs/C_Records.cs +++ b/Game/DataStorage/Structs/C_Records.cs @@ -20,16 +20,33 @@ using Framework.GameMath; namespace Game.DataStorage { + public sealed class CharacterFacialHairStylesRecord + { + public uint ID; + public uint[] Geoset = new uint[5]; + public byte RaceID; + public byte SexID; + public byte VariationID; + } + + public sealed class CharBaseSectionRecord + { + public uint ID; + public CharBaseSectionVariation Variation; + public byte ResolutionVariation; + public byte Resolution; + } + public sealed class CharSectionsRecord { public uint Id; public uint[] TextureFileDataID = new uint[3]; public ushort Flags; - public byte Race; - public byte Gender; - public byte GenType; - public byte Type; - public byte Color; + public byte RaceID; + public byte SexID; + public byte BaseSection; + public byte VariationIndex; + public byte ColorIndex; } public sealed class CharStartOutfitRecord diff --git a/Game/DataStorage/Structs/T_Records.cs b/Game/DataStorage/Structs/T_Records.cs index d11e79658..0c96909bf 100644 --- a/Game/DataStorage/Structs/T_Records.cs +++ b/Game/DataStorage/Structs/T_Records.cs @@ -91,6 +91,40 @@ namespace Game.DataStorage public uint Id; } + public sealed class TransmogHolidayRecord + { + public uint Id; + public int HolidayID; + } + + public sealed class TransmogSetRecord + { + public LocalizedString Name; + public ushort BaseSetID; + public ushort UIOrder; + public byte ExpansionID; + public uint Id; + public int Flags; + public int QuestID; + public int ClassMask; + public int ItemNameDescriptionID; + public uint TransmogSetGroupID; + } + + public sealed class TransmogSetGroupRecord + { + public LocalizedString Label; + public uint Id; + } + + public sealed class TransmogSetItemRecord + { + public uint Id; + public uint TransmogSetID; + public uint ItemModifiedAppearanceID; + public int Flags; + } + public sealed class TransportAnimationRecord { public uint Id; diff --git a/Game/Entities/Creature/Creature.cs b/Game/Entities/Creature/Creature.cs index 7e33ae546..d473e6453 100644 --- a/Game/Entities/Creature/Creature.cs +++ b/Game/Entities/Creature/Creature.cs @@ -2369,7 +2369,7 @@ namespace Game.Entities return stats.GenerateHealth(cInfo); } - float GetHealthMultiplierForTarget(WorldObject target) + public override float GetHealthMultiplierForTarget(WorldObject target) { if (!HasScalableLevels()) return 1.0f; @@ -2388,7 +2388,7 @@ namespace Game.Entities return stats.GenerateBaseDamage(cInfo); } - float GetDamageMultiplierForTarget(WorldObject target) + public override float GetDamageMultiplierForTarget(WorldObject target) { if (!HasScalableLevels()) return 1.0f; @@ -2405,7 +2405,7 @@ namespace Game.Entities return stats.GenerateArmor(cInfo); } - float GetArmorMultiplierForTarget(WorldObject target) + public override float GetArmorMultiplierForTarget(WorldObject target) { if (!HasScalableLevels()) return 1.0f; diff --git a/Game/Entities/Item/Item.cs b/Game/Entities/Item/Item.cs index e116151fa..4dc229821 100644 --- a/Game/Entities/Item/Item.cs +++ b/Game/Entities/Item/Item.cs @@ -1497,11 +1497,11 @@ namespace Game.Entities return ItemTransmogrificationWeaponCategory.Invalid; } - static int[] ItemTransmogrificationSlots = + public static int[] ItemTransmogrificationSlots = { -1, // INVTYPE_NON_EQUIP EquipmentSlot.Head, // INVTYPE_HEAD - EquipmentSlot.Neck, // INVTYPE_NECK + -1, // INVTYPE_NECK EquipmentSlot.Shoulders, // INVTYPE_SHOULDERS EquipmentSlot.Shirt, // INVTYPE_BODY EquipmentSlot.Chest, // INVTYPE_CHEST @@ -1516,15 +1516,15 @@ namespace Game.Entities EquipmentSlot.OffHand, // INVTYPE_SHIELD EquipmentSlot.MainHand, // INVTYPE_RANGED EquipmentSlot.Cloak, // INVTYPE_CLOAK - -1, // INVTYPE_2HWEAPON + EquipmentSlot.MainHand, // INVTYPE_2HWEAPON -1, // INVTYPE_BAG EquipmentSlot.Tabard, // INVTYPE_TABARD EquipmentSlot.Chest, // INVTYPE_ROBE EquipmentSlot.MainHand, // INVTYPE_WEAPONMAINHAND - EquipmentSlot.OffHand, // INVTYPE_WEAPONOFFHAND + EquipmentSlot.MainHand, // INVTYPE_WEAPONOFFHAND EquipmentSlot.OffHand, // INVTYPE_HOLDABLE -1, // INVTYPE_AMMO - EquipmentSlot.MainHand, // INVTYPE_THROWN + -1, // INVTYPE_THROWN EquipmentSlot.MainHand, // INVTYPE_RANGEDRIGHT -1, // INVTYPE_QUIVER -1 // INVTYPE_RELIC @@ -1566,22 +1566,15 @@ namespace Game.Entities case ItemClass.Armor: if ((ItemSubClassArmor)source.GetSubClass() != ItemSubClassArmor.Cosmetic) return false; + if (source.GetInventoryType() != target.GetInventoryType()) + if (ItemTransmogrificationSlots[(int)source.GetInventoryType()] != ItemTransmogrificationSlots[(int)target.GetInventoryType()]) + return false; break; default: return false; } } - if (source.GetInventoryType() != target.GetInventoryType()) - { - int sourceSlot = ItemTransmogrificationSlots[(int)source.GetInventoryType()]; - if (sourceSlot == -1 && source.GetInventoryType() == InventoryType.Weapon && (target.GetInventoryType() == InventoryType.WeaponMainhand || target.GetInventoryType() == InventoryType.WeaponOffhand)) - sourceSlot = ItemTransmogrificationSlots[(int)target.GetInventoryType()]; - - if (sourceSlot != ItemTransmogrificationSlots[(int)target.GetInventoryType()]) - return false; - } - return true; } @@ -2531,11 +2524,11 @@ namespace Game.Entities public uint GetScalingStatDistribution() { return _bonusData.ScalingStatDistribution; } public void SetRefundRecipient(ObjectGuid guid) { m_refundRecipient = guid; } - public void SetPaidMoney(uint money) { m_paidMoney = money; } + public void SetPaidMoney(ulong money) { m_paidMoney = money; } public void SetPaidExtendedCost(uint iece) { m_paidExtendedCost = iece; } public ObjectGuid GetRefundRecipient() { return m_refundRecipient; } - public uint GetPaidMoney() { return m_paidMoney; } + public ulong GetPaidMoney() { return m_paidMoney; } public uint GetPaidExtendedCost() { return m_paidExtendedCost; } public uint GetScriptId() { return GetTemplate().ScriptId; } @@ -2637,7 +2630,7 @@ namespace Game.Entities ItemUpdateState uState; uint m_paidExtendedCost; - uint m_paidMoney; + ulong m_paidMoney; ObjectGuid m_refundRecipient; byte m_slot; Bag m_container; diff --git a/Game/Entities/Object/WorldObject.cs b/Game/Entities/Object/WorldObject.cs index ff8f23661..27deb2aaf 100644 --- a/Game/Entities/Object/WorldObject.cs +++ b/Game/Entities/Object/WorldObject.cs @@ -2417,6 +2417,14 @@ namespace Game.Entities SendMessageToSet(sound, true); } + public void PlayDirectMusic(uint musicId, Player target = null) + { + if (target) + target.SendPacket(new PlayMusic(musicId)); + else + SendMessageToSet(new PlayMusic(musicId), true); + } + public void DestroyForNearbyPlayers() { if (!IsInWorld) diff --git a/Game/Entities/Player/CollectionManager.cs b/Game/Entities/Player/CollectionManager.cs index 54d2ce088..1b7362186 100644 --- a/Game/Entities/Player/CollectionManager.cs +++ b/Game/Entities/Player/CollectionManager.cs @@ -675,6 +675,19 @@ namespace Game.Entities _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) @@ -763,6 +776,54 @@ namespace Game.Entities _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 GetAccountToys() { return _toys; } public Dictionary GetAccountHeirlooms() { return _heirlooms; } diff --git a/Game/Entities/Player/Player.Achievement.cs b/Game/Entities/Player/Player.Achievement.cs index ba01431f4..a7501b768 100644 --- a/Game/Entities/Player/Player.Achievement.cs +++ b/Game/Entities/Player/Player.Achievement.cs @@ -56,11 +56,13 @@ namespace Game.Entities 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 diff --git a/Game/Entities/Player/Player.DB.cs b/Game/Entities/Player/Player.DB.cs index 5bc3f998e..47cd7ce81 100644 --- a/Game/Entities/Player/Player.DB.cs +++ b/Game/Entities/Player/Player.DB.cs @@ -261,7 +261,7 @@ namespace Game.Entities if (!result.IsEmpty()) { item.SetRefundRecipient(GetGUID()); - item.SetPaidMoney(result.Read(0)); + item.SetPaidMoney(result.Read(0)); item.SetPaidExtendedCost(result.Read(1)); AddRefundReference(item.GetGUID()); } @@ -2373,6 +2373,7 @@ namespace Game.Entities // load achievements before anything else to prevent multiple gains for the same achievement/criteria on every loading (as loading does call UpdateAchievementCriteria) m_achievementSys.LoadFromDB(holder.GetResult(PlayerLoginQueryLoad.Achievements), holder.GetResult(PlayerLoginQueryLoad.CriteriaProgress)); + m_questObjectiveCriteriaMgr.LoadFromDB(holder.GetResult(PlayerLoginQueryLoad.QuestStatusObjectivesCriteria), holder.GetResult(PlayerLoginQueryLoad.QuestStatusObjectivesCriteriaProgress)); ulong money = result.Read(8); if (money > PlayerConst.MaxMoneyAmount) @@ -3035,7 +3036,7 @@ namespace Game.Entities } m_achievementSys.CheckAllAchievementCriteria(this); - + m_questObjectiveCriteriaMgr.CheckAllQuestObjectiveCriteria(this); return true; } public void SaveToDB(bool create = false) @@ -3383,6 +3384,7 @@ namespace Game.Entities _SaveSkills(trans); m_achievementSys.SaveToDB(trans); reputationMgr.SaveToDB(trans); + m_questObjectiveCriteriaMgr.SaveToDB(trans); _SaveEquipmentSets(trans); GetSession().SaveTutorialsData(trans); // changed only while character in game _SaveInstanceTimeRestrictions(trans); diff --git a/Game/Entities/Player/Player.Fields.cs b/Game/Entities/Player/Player.Fields.cs index a3d9e0b1e..61dfc43f0 100644 --- a/Game/Entities/Player/Player.Fields.cs +++ b/Game/Entities/Player/Player.Fields.cs @@ -202,6 +202,7 @@ namespace Game.Entities public uint m_timeSyncClient; public uint m_timeSyncServer; ReputationMgr reputationMgr; + QuestObjectiveCriteriaManager m_questObjectiveCriteriaMgr; public AtLoginFlags atLoginFlags; public bool m_itemUpdateQueueBlocked; diff --git a/Game/Entities/Player/Player.Items.cs b/Game/Entities/Player/Player.Items.cs index bfa7b4536..1b1518339 100644 --- a/Game/Entities/Player/Player.Items.cs +++ b/Game/Entities/Player/Player.Items.cs @@ -101,7 +101,7 @@ namespace Game.Entities SendItemRefundResult(item, iece, 0); - uint moneyRefund = item.GetPaidMoney(); // item. will be invalidated in DestroyItem + ulong moneyRefund = item.GetPaidMoney(); // item. will be invalidated in DestroyItem // Save all relevant data to DB to prevent desynchronisation exploits SQLTransaction trans = new SQLTransaction(); @@ -142,7 +142,7 @@ namespace Game.Entities // Grant back money if (moneyRefund != 0) - ModifyMoney(moneyRefund); // Saved in SaveInventoryAndGoldToDB + ModifyMoney((long)moneyRefund); // Saved in SaveInventoryAndGoldToDB SaveInventoryAndGoldToDB(trans); @@ -2483,7 +2483,7 @@ namespace Game.Entities } AutoUnequipOffhandIfNeed(); } - bool _StoreOrEquipNewItem(uint vendorslot, uint item, byte count, byte bag, byte slot, int price, ItemTemplate pProto, Creature pVendor, VendorItem crItem, bool bStore) + bool _StoreOrEquipNewItem(uint vendorslot, uint item, byte count, byte bag, byte slot, long price, ItemTemplate pProto, Creature pVendor, VendorItem crItem, bool bStore) { uint stacks = count / pProto.GetBuyCount(); List vDest = new List(); @@ -3632,7 +3632,7 @@ namespace Game.Entities // check current item amount if it limited if (crItem.maxcount != 0) { - if (creature.GetVendorItemCurrentCount(crItem) < pProto.GetBuyCount() * count) + if (creature.GetVendorItemCurrentCount(crItem) < count) { SendBuyError(BuyResult.ItemAlreadySold, creature, item); return false; @@ -3722,19 +3722,20 @@ namespace Game.Entities } } - uint price = 0; + ulong price = 0; if (crItem.IsGoldRequired(pProto) && pProto.GetBuyPrice() > 0) //Assume price cannot be negative (do not know why it is int32) { - uint maxCount = (uint)(Int64.MaxValue / pProto.GetBuyPrice()); + float buyPricePerItem = pProto.GetBuyPrice() / pProto.GetBuyCount(); + ulong maxCount = (ulong)(PlayerConst.MaxMoneyAmount / buyPricePerItem); if (count > maxCount) { Log.outError(LogFilter.Player, "Player {0} tried to buy {1} item id {2}, causing overflow", GetName(), count, pProto.GetId()); count = (byte)maxCount; } - price = pProto.GetBuyPrice() * count; //it should not exceed MAX_MONEY_AMOUNT + price = (ulong)(buyPricePerItem * count); //it should not exceed MAX_MONEY_AMOUNT // reputation discount - price = (uint)Math.Floor(price * GetReputationPriceDiscount(creature)); + price = (ulong)Math.Floor(price * GetReputationPriceDiscount(creature)); int priceMod = GetTotalAuraModifier(AuraType.ModVendorItemsPrices); if (priceMod != 0) diff --git a/Game/Entities/Player/Player.Quest.cs b/Game/Entities/Player/Player.Quest.cs index 24c0ae009..3cbcb07ec 100644 --- a/Game/Entities/Player/Player.Quest.cs +++ b/Game/Entities/Player/Player.Quest.cs @@ -540,6 +540,11 @@ namespace Game.Entities { AddQuest(quest, questGiver); + foreach (QuestObjective obj in quest.Objectives) + if (obj.Type == QuestObjectiveType.CriteriaTree) + if (m_questObjectiveCriteriaMgr.HasCompletedObjective(obj)) + KillCreditCriteriaTreeObjective(obj); + if (CanCompleteQuest(quest.Id)) CompleteQuest(quest.Id); @@ -700,11 +705,20 @@ namespace Game.Entities foreach (QuestObjective obj in quest.Objectives) { - if (obj.Type == QuestObjectiveType.MinReputation || obj.Type == QuestObjectiveType.MaxReputation) + switch (obj.Type) { - FactionRecord factionEntry = CliDB.FactionStorage.LookupByKey(obj.ObjectID); - if (factionEntry != null) - GetReputationMgr().SetVisible(factionEntry); + case QuestObjectiveType.MinReputation: + case QuestObjectiveType.MaxReputation: + FactionRecord factionEntry = CliDB.FactionStorage.LookupByKey(obj.ObjectID); + if (factionEntry != null) + GetReputationMgr().SetVisible(factionEntry); + break; + case QuestObjectiveType.CriteriaTree: + if (quest.HasFlagEx(QuestFlagsEx.ClearProgressOfCriteriaTreeObjectivesOnAccept)) + m_questObjectiveCriteriaMgr.ResetCriteriaTree((uint)obj.ObjectID); + break; + default: + break; } } @@ -1423,7 +1437,7 @@ namespace Game.Entities public bool SatisfyQuestConditions(Quest qInfo, bool msg) { - if (!Global.ConditionMgr.IsObjectMeetingNotGroupedConditions(ConditionSourceType.QuestAccept, qInfo.Id, this)) + if (!Global.ConditionMgr.IsObjectMeetingNotGroupedConditions(ConditionSourceType.QuestAvailable, qInfo.Id, this)) { if (msg) { @@ -1818,7 +1832,7 @@ namespace Game.Entities if (quest == null) continue; - if (!Global.ConditionMgr.IsObjectMeetingNotGroupedConditions(ConditionSourceType.QuestAccept, quest.Id, this)) + if (!Global.ConditionMgr.IsObjectMeetingNotGroupedConditions(ConditionSourceType.QuestAvailable, quest.Id, this)) continue; QuestStatus status = GetQuestStatus(questId); @@ -1844,7 +1858,7 @@ namespace Game.Entities if (quest == null) continue; - if (!Global.ConditionMgr.IsObjectMeetingNotGroupedConditions(ConditionSourceType.QuestAccept, quest.Id, this)) + if (!Global.ConditionMgr.IsObjectMeetingNotGroupedConditions(ConditionSourceType.QuestAvailable, quest.Id, this)) continue; QuestStatus status = GetQuestStatus(questId); @@ -2273,6 +2287,21 @@ namespace Game.Entities } } + public void KillCreditCriteriaTreeObjective(QuestObjective questObjective) + { + if (questObjective.Type != QuestObjectiveType.CriteriaTree) + return; + + if (GetQuestStatus(questObjective.QuestID) == QuestStatus.Incomplete) + { + SetQuestObjectiveData(questObjective, 1); + SendQuestUpdateAddCreditSimple(questObjective); + + if (CanCompleteQuest(questObjective.QuestID)) + CompleteQuest(questObjective.QuestID); + } + } + public void TalkedToCreature(uint entry, ObjectGuid guid) { ushort addTalkCount = 1; @@ -2579,6 +2608,7 @@ namespace Game.Entities return false; break; case QuestObjectiveType.AreaTrigger: + case QuestObjectiveType.CriteriaTree: if (GetQuestObjectiveData(quest, objective.StorageIndex) == 0) return false; break; diff --git a/Game/Entities/Player/Player.Spells.cs b/Game/Entities/Player/Player.Spells.cs index 967ca9c52..59723d248 100644 --- a/Game/Entities/Player/Player.Spells.cs +++ b/Game/Entities/Player/Player.Spells.cs @@ -1844,7 +1844,7 @@ namespace Game.Entities { for (CurrentSpellTypes i = 0; i < CurrentSpellTypes.Max; ++i) { - Spell spell = m_currentSpells[i]; + Spell spell = GetCurrentSpell(i); if (spell != null) RestoreSpellMods(spell, ownerAuraId, aura); } diff --git a/Game/Entities/Player/Player.cs b/Game/Entities/Player/Player.cs index bf35ee7f5..7149bf29b 100644 --- a/Game/Entities/Player/Player.cs +++ b/Game/Entities/Player/Player.cs @@ -117,6 +117,7 @@ namespace Game.Entities m_achievementSys = new PlayerAchievementMgr(this); reputationMgr = new ReputationMgr(this); + m_questObjectiveCriteriaMgr = new QuestObjectiveCriteriaManager(this); m_sceneMgr = new SceneMgr(this); m_bgBattlegroundQueueID[0] = new BgBattlegroundQueueID_Rec(); @@ -283,7 +284,7 @@ namespace Game.Entities InitRunes(); - SetUInt32Value(PlayerFields.Coinage, WorldConfig.GetUIntValue(WorldCfg.StartPlayerMoney)); + SetUInt64Value(PlayerFields.Coinage, WorldConfig.GetUIntValue(WorldCfg.StartPlayerMoney)); SetCurrency(CurrencyTypes.ApexisCrystals, WorldConfig.GetUIntValue(WorldCfg.CurrencyStartApexisCrystals)); SetCurrency(CurrencyTypes.JusticePoints, WorldConfig.GetUIntValue(WorldCfg.CurrencyStartJusticePoints)); @@ -2551,7 +2552,7 @@ namespace Game.Entities GetSession().SendStablePet(guid); break; case GossipOption.Trainer: - GetSession().SendTrainerList(guid, menuItemData.TrainerId); + GetSession().SendTrainerList(source.ToCreature(), menuItemData.TrainerId); break; case GossipOption.Learndualspec: break; @@ -4086,45 +4087,42 @@ namespace Game.Entities return ComponentFlagsMatch(entry, GetSelectionFromContext(2, class_)); } + static bool IsSectionValid(Race race, Class class_, Gender gender, CharBaseSectionVariation variation, byte variationIndex, byte colorIndex, bool create) + { + CharSectionsRecord section = Global.DB2Mgr.GetCharSectionEntry(race, gender, variation, variationIndex, colorIndex); + if (section != null) + return IsSectionFlagValid(section, class_, create); + return false; + } public static bool IsValidGender(Gender _gender) { return _gender <= Gender.Female; } public static bool IsValidClass(Class _class) { return Convert.ToBoolean((1 << ((int)_class - 1)) & (int)Class.ClassMaskAllPlayable); } public static bool IsValidRace(Race _race) { return Convert.ToBoolean((1 << ((int)_race - 1)) & (int)Race.RaceMaskAllPlayable); } + public static bool ValidateAppearance(Race race, Class class_, Gender gender, byte hairID, byte hairColor, byte faceID, byte facialHairId, byte skinColor, Array customDisplay, bool create = false) { - CharSectionsRecord skin = Global.DB2Mgr.GetCharSectionEntry(race, CharSectionType.Skin, gender, 0, skinColor); - if (skin == null) + if (!IsSectionValid(race, class_, gender, CharBaseSectionVariation.Skin, 0, skinColor, create)) return false; - if (!IsSectionFlagValid(skin, class_, create)) + if (!IsSectionValid(race, class_, gender, CharBaseSectionVariation.Face, faceID, skinColor, create)) return false; - CharSectionsRecord face = Global.DB2Mgr.GetCharSectionEntry(race, CharSectionType.Face, gender, faceID, skinColor); - if (face == null) + if (!IsSectionValid(race, class_, gender, CharBaseSectionVariation.Hair, hairID, hairColor, create)) return false; - if (!IsSectionFlagValid(face, class_, create)) + if (!IsSectionValid(race, class_, gender, CharBaseSectionVariation.FacialHair, facialHairId, hairColor, create)) + if (Global.DB2Mgr.HasCharSections(race, gender, CharBaseSectionVariation.FacialHair) || !Global.DB2Mgr.HasCharacterFacialHairStyle(race, gender, facialHairId)) + return false; + + if (!IsSectionValid(race, class_, gender, CharBaseSectionVariation.CustomDisplay1, customDisplay[0], 0, create)) return false; - CharSectionsRecord hair = Global.DB2Mgr.GetCharSectionEntry(race, CharSectionType.Hair, gender, hairID, hairColor); - if (hair == null) + if (!IsSectionValid(race, class_, gender, CharBaseSectionVariation.CustomDisplay2, customDisplay[1], 0, create)) return false; - if (!IsSectionFlagValid(hair, class_, create)) + if (!IsSectionValid(race, class_, gender, CharBaseSectionVariation.CustomDisplay3, customDisplay[2], 0, create)) return false; - CharSectionsRecord facialHair = Global.DB2Mgr.GetCharSectionEntry(race, CharSectionType.Hair, gender, facialHairId, hairColor); - if (facialHair == null) - return false; - - for (int i = 0; i < PlayerConst.CustomDisplaySize; ++i) - { - CharSectionsRecord entry = Global.DB2Mgr.GetCharSectionEntry(race, (CharSectionType.CustomDisplay1 + i * 2), gender, customDisplay[i], 0); - if (entry != null) - if (!IsSectionFlagValid(entry, class_, create)) - return false; - } - return true; } @@ -5125,8 +5123,8 @@ namespace Game.Entities SetMoney((ulong)(GetMoney() > (ulong)-amount ? (long)GetMoney() + amount : 0)); else { - if (GetMoney() < (ulong)(PlayerConst.MaxMoneyAmount - amount)) - SetMoney((ulong)((long)GetMoney() + amount)); + if (GetMoney() <= (PlayerConst.MaxMoneyAmount - (ulong)amount)) + SetMoney((ulong)(GetMoney() + (ulong)amount)); else { if (sendError) @@ -5514,6 +5512,7 @@ namespace Game.Entities SendEquipmentSetList(); m_achievementSys.SendAllData(this); + m_questObjectiveCriteriaMgr.SendAllData(this); // SMSG_LOGIN_SETTIMESPEED float TimeSpeed = 0.01666667f; diff --git a/Game/Entities/StatSystem.cs b/Game/Entities/StatSystem.cs index a92de6606..899bd2d9e 100644 --- a/Game/Entities/StatSystem.cs +++ b/Game/Entities/StatSystem.cs @@ -656,38 +656,37 @@ namespace Game.Entities float GetUnitCriticalChance(WeaponAttackType attackType, Unit victim) { - float crit; - + float chance = 0.0f; if (IsTypeId(TypeId.Player)) { switch (attackType) { case WeaponAttackType.BaseAttack: - crit = GetFloatValue(PlayerFields.CritPercentage); + chance = GetFloatValue(PlayerFields.CritPercentage); break; case WeaponAttackType.OffAttack: - crit = GetFloatValue(PlayerFields.OffhandCritPercentage); + chance = GetFloatValue(PlayerFields.OffhandCritPercentage); break; case WeaponAttackType.RangedAttack: - crit = GetFloatValue(PlayerFields.RangedCritPercentage); - break; - default: - crit = 0.0f; + chance = GetFloatValue(PlayerFields.RangedCritPercentage); break; } } else { - crit = 5.0f; - crit += GetTotalAuraModifier(AuraType.ModWeaponCritPercent); - crit += GetTotalAuraModifier(AuraType.ModCritPct); + if (!ToCreature().GetCreatureTemplate().FlagsExtra.HasAnyFlag(CreatureFlagsExtra.NoCrit)) + { + chance = 5.0f; + chance += GetTotalAuraModifier(AuraType.ModWeaponCritPercent); + chance += GetTotalAuraModifier(AuraType.ModCritPct); + } } // flat aura mods if (attackType == WeaponAttackType.RangedAttack) - crit += victim.GetTotalAuraModifier(AuraType.ModAttackerRangedCritChance); + chance += victim.GetTotalAuraModifier(AuraType.ModAttackerRangedCritChance); else - crit += victim.GetTotalAuraModifier(AuraType.ModAttackerMeleeCritChance); + chance += victim.GetTotalAuraModifier(AuraType.ModAttackerMeleeCritChance); var critChanceForCaster = victim.GetAuraEffectsByType(AuraType.ModCritChanceForCaster); foreach (AuraEffect aurEff in critChanceForCaster) @@ -695,14 +694,12 @@ namespace Game.Entities if (aurEff.GetCasterGUID() != GetGUID()) continue; - crit += aurEff.GetAmount(); + chance += aurEff.GetAmount(); } - crit += victim.GetTotalAuraModifier(AuraType.ModAttackerSpellAndWeaponCritChance); + chance += victim.GetTotalAuraModifier(AuraType.ModAttackerSpellAndWeaponCritChance); - if (crit < 0.0f) - crit = 0.0f; - return crit; + return Math.Max(chance, 0.0f); } float GetUnitDodgeChance(WeaponAttackType attType, Unit victim) { diff --git a/Game/Entities/Unit/Unit.Combat.cs b/Game/Entities/Unit/Unit.Combat.cs index 551c9c8a8..215f1faf4 100644 --- a/Game/Entities/Unit/Unit.Combat.cs +++ b/Game/Entities/Unit/Unit.Combat.cs @@ -688,19 +688,18 @@ namespace Game.Entities } public Unit GetMeleeHitRedirectTarget(Unit victim, SpellInfo spellInfo = null) { - var hitTriggerAuras = victim.GetAuraEffectsByType(AuraType.AddCasterHitTrigger); - foreach (var i in hitTriggerAuras) + var interceptAuras = victim.GetAuraEffectsByType(AuraType.InterceptMeleeRangedAttacks); + foreach (var i in interceptAuras) { Unit magnet = i.GetCaster(); if (magnet != null) if (_IsValidAttackTarget(magnet, spellInfo) && magnet.IsWithinLOSInMap(this) && (spellInfo == null || (spellInfo.CheckExplicitTarget(this, magnet) == SpellCastResult.SpellCastOk && spellInfo.CheckTarget(this, magnet, false) == SpellCastResult.SpellCastOk))) - if (RandomHelper.randChance(i.GetAmount())) - { - i.GetBase().DropCharge(AuraRemoveMode.Expire); - return magnet; - } + { + i.GetBase().DropCharge(AuraRemoveMode.Expire); + return magnet; + } } return victim; } @@ -2053,10 +2052,7 @@ namespace Game.Entities // 6.CRIT tmp = crit_chance; if (tmp > 0 && roll < (sum += tmp)) - { - if (GetTypeId() != TypeId.Unit || !(ToCreature().GetCreatureTemplate().FlagsExtra.HasAnyFlag(CreatureFlagsExtra.NoCrit))) - return MeleeHitOutcome.Crit; - } + return MeleeHitOutcome.Crit; // 7. CRUSHING // mobs can score crushing blows if they're 4 or more levels above victim diff --git a/Game/Entities/Unit/Unit.Fields.cs b/Game/Entities/Unit/Unit.Fields.cs index f3abc5ab1..eaab6d833 100644 --- a/Game/Entities/Unit/Unit.Fields.cs +++ b/Game/Entities/Unit/Unit.Fields.cs @@ -182,7 +182,6 @@ namespace Game.Entities public SpellInfo GetSpellInfo() { - /// WORKAROUND: unfinished new proc system if (_spell) return _spell.GetSpellInfo(); if (_damageInfo != null) @@ -194,7 +193,6 @@ namespace Game.Entities } public SpellSchoolMask GetSchoolMask() { - /// WORKAROUND: unfinished new proc system if (_spell) return _spell.GetSpellInfo().GetSchoolMask(); if (_damageInfo != null) diff --git a/Game/Entities/Unit/Unit.Spells.cs b/Game/Entities/Unit/Unit.Spells.cs index 54d3e05a3..f182bf8c0 100644 --- a/Game/Entities/Unit/Unit.Spells.cs +++ b/Game/Entities/Unit/Unit.Spells.cs @@ -752,6 +752,13 @@ namespace Game.Entities } break; } + + // Spell crit suppression + if (victim.GetTypeId() == TypeId.Unit) + { + int levelDiff = (int)(victim.GetLevelForTarget(this) - getLevel()); + crit_chance -= levelDiff * 1.0f; + } } break; } @@ -776,7 +783,7 @@ namespace Game.Entities if (aurEff.GetCasterGUID() == GetGUID() && aurEff.IsAffectingSpell(spellProto)) crit_chance += aurEff.GetAmount(); - return crit_chance > 0.0f ? crit_chance : 0.0f; + return Math.Max(crit_chance, 0.0f); } // Calculate spell hit result can be: @@ -1813,7 +1820,7 @@ namespace Game.Entities DateTime now = DateTime.Now; // use provided list of auras which can proc - if (!procAuras.Empty()) + if (procAuras != null) { foreach (AuraApplication aurApp in procAuras) { diff --git a/Game/Globals/ObjectManager.cs b/Game/Globals/ObjectManager.cs index 167ea456a..91cd9029f 100644 --- a/Game/Globals/ObjectManager.cs +++ b/Game/Globals/ObjectManager.cs @@ -3053,6 +3053,38 @@ namespace Game Log.outInfo(LogFilter.ServerLoading, $"Loaded {_trainers.Count} Trainers in {Time.GetMSTimeDiffToNow(oldMSTime)} ms"); } + public void LoadCreatureDefaultTrainers() + { + uint oldMSTime = Time.GetMSTime(); + + _creatureDefaultTrainers.Clear(); + + SQLResult result = DB.World.Query("SELECT CreatureId, TrainerId FROM creature_default_trainer"); + if (!result.IsEmpty()) + { + do + { + uint creatureId = result.Read(0); + uint trainerId = result.Read(1); + + if (GetCreatureTemplate(creatureId) == null) + { + Log.outError(LogFilter.Sql, $"Table `creature_default_trainer` references non-existing creature template (CreatureId: {creatureId}), ignoring"); + continue; + } + + if (GetTrainer(trainerId) == null) + { + Log.outError(LogFilter.Sql, $"Table `creature_default_trainer` references non-existing trainer (TrainerId: {trainerId}) for CreatureId {creatureId}, ignoring"); + continue; + } + + _creatureDefaultTrainers[creatureId] = trainerId; + } while (result.NextRow()); + } + + Log.outInfo(LogFilter.ServerLoading, $"Loaded {_creatureDefaultTrainers.Count} default trainers in {Time.GetMSTimeDiffToNow(oldMSTime)} ms"); + } public void LoadVendors() { var time = Time.GetMSTime(); @@ -3324,6 +3356,7 @@ namespace Game Log.outInfo(LogFilter.ServerLoading, "Loaded {0} creatures in {1} ms", count, Time.GetMSTimeDiffToNow(time)); } + public void AddCreatureToGrid(ulong guid, CreatureData data) { uint mask = data.spawnMask; @@ -3419,7 +3452,10 @@ namespace Game { return creatureTemplateAddonStorage.LookupByKey(entry); } - + public uint GetCreatureDefaultTrainer(uint creatureId) + { + return _creatureDefaultTrainers.LookupByKey(creatureId); + } public Dictionary GetCreatureTemplates() { return creatureTemplateStorage; @@ -9138,6 +9174,7 @@ namespace Game Dictionary creatureBaseStatsStorage = new Dictionary(); Dictionary cacheVendorItemStorage = new Dictionary(); Dictionary _trainers = new Dictionary(); + Dictionary _creatureDefaultTrainers = new Dictionary(); List[] _difficultyEntries = new List[SharedConst.MaxCreatureDifficulties]; // already loaded difficulty 1 value in creatures, used in CheckCreatureTemplate List[] _hasDifficultyEntries = new List[SharedConst.MaxCreatureDifficulties]; // already loaded creatures with difficulty 1 values, used in CheckCreatureTemplate Dictionary _npcTextStorage = new Dictionary(); diff --git a/Game/Guilds/Guild.cs b/Game/Guilds/Guild.cs index c7c1dde44..6499f1460 100644 --- a/Game/Guilds/Guild.cs +++ b/Game/Guilds/Guild.cs @@ -294,7 +294,7 @@ namespace Game.Guilds rankData.RankID = rankInfo.GetId(); rankData.RankOrder = i; rankData.Flags = (uint)rankInfo.GetRights(); - rankData.WithdrawGoldLimit = (uint)rankInfo.GetBankMoneyPerDay(); + rankData.WithdrawGoldLimit = rankInfo.GetBankMoneyPerDay(); rankData.RankName = rankInfo.GetName(); for (byte j = 0; j < GuildConst.MaxBankTabs; ++j) @@ -800,6 +800,11 @@ namespace Game.Guilds public void HandleMemberDepositMoney(WorldSession session, ulong amount, bool cashFlow = false) { + // guild bank cannot have more than MAX_MONEY_AMOUNT + amount = Math.Min(amount, PlayerConst.MaxMoneyAmount - m_bankMoney); + if (amount == 0) + return; + Player player = session.GetPlayer(); // Call script after validation and before money transfer. @@ -839,7 +844,10 @@ namespace Game.Guilds if (member == null) return false; - if ((ulong)_GetMemberRemainingMoney(member) < amount) // Check if we have enough slot/money today + if (!_HasRankRight(player, repair ? GuildRankRights.WithdrawRepair : GuildRankRights.WithdrawGold)) + return false; + + if (_GetMemberRemainingMoney(member) < (long)amount) // Check if we have enough slot/money today return false; // Call script after validation and before money transfer. @@ -856,7 +864,7 @@ namespace Game.Guilds } // Update remaining money amount - member.UpdateBankWithdrawValue(trans, GuildConst.MaxBankTabs, (uint)amount); + member.UpdateBankMoneyWithdrawValue(trans, amount); // Remove money from bank _ModifyBankMoney(trans, amount, false); @@ -1006,7 +1014,7 @@ namespace Game.Guilds GuildPermissionsQueryResults queryResult = new GuildPermissionsQueryResults(); queryResult.RankID = rankId; - queryResult.WithdrawGoldLimit = _GetMemberRemainingMoney(member); + queryResult.WithdrawGoldLimit = (int)_GetMemberRemainingMoney(member); queryResult.Flags = (int)_GetRankRights(rankId); queryResult.NumTabs = _GetPurchasedTabsSize(); @@ -1027,7 +1035,7 @@ namespace Game.Guilds if (member == null) return; - int amount = _GetMemberRemainingMoney(member); + long amount = _GetMemberRemainingMoney(member); GuildBankRemainingWithdrawMoney packet = new GuildBankRemainingWithdrawMoney(); packet.RemainingWithdrawMoney = amount; @@ -1829,7 +1837,7 @@ namespace Game.Guilds return 0; } - int _GetRankBankMoneyPerDay(byte rankId) + uint _GetRankBankMoneyPerDay(byte rankId) { RankInfo rankInfo = GetRankInfo(rankId); if (rankInfo != null) @@ -1862,10 +1870,10 @@ namespace Game.Guilds { byte rankId = member.GetRankId(); if (rankId == GuildDefaultRanks.Master) - return GuildConst.WithdrawSlotUnlimited; + return Convert.ToInt32(GuildConst.WithdrawSlotUnlimited); if ((_GetRankBankTabRights(rankId, tabId) & GuildBankRights.ViewTab) != 0) { - int remaining = _GetRankBankTabSlotsPerDay(rankId, tabId) - member.GetBankWithdrawValue(tabId); + int remaining = _GetRankBankTabSlotsPerDay(rankId, tabId) - (int)member.GetBankTabWithdrawValue(tabId); if (remaining > 0) return remaining; } @@ -1873,17 +1881,17 @@ namespace Game.Guilds return 0; } - int _GetMemberRemainingMoney(Member member) + long _GetMemberRemainingMoney(Member member) { if (member != null) { byte rankId = member.GetRankId(); if (rankId == GuildDefaultRanks.Master) - return GuildConst.WithdrawMoneyUnlimited; + return long.MaxValue; if ((_GetRankRights(rankId) & (GuildRankRights.WithdrawRepair | GuildRankRights.WithdrawGold)) != 0) { - int remaining = _GetRankBankMoneyPerDay(rankId) - member.GetBankWithdrawValue(GuildConst.MaxBankTabs); + long remaining = (long)((_GetRankBankMoneyPerDay(rankId) * MoneyConstants.Gold) - member.GetBankMoneyWithdrawValue()); if (remaining > 0) return remaining; } @@ -1895,12 +1903,7 @@ namespace Game.Guilds { Member member = GetMember(guid); if (member != null) - { - byte rankId = member.GetRankId(); - if (rankId != GuildDefaultRanks.Master - && member.GetBankWithdrawValue(tabId) < _GetRankBankTabSlotsPerDay(rankId, tabId)) - member.UpdateBankWithdrawValue(trans, tabId, 1); - } + member.UpdateBankTabWithdrawValue(trans, tabId, 1); } bool _MemberHasTabRights(ObjectGuid guid, byte tabId, GuildBankRights rights) @@ -2523,8 +2526,10 @@ namespace Game.Guilds m_publicNote = field.Read(3); m_officerNote = field.Read(4); - for (byte i = 0; i <= GuildConst.MaxBankTabs; ++i) - m_bankWithdraw[i] = field.Read(5 + i); + for (byte i = 0; i < GuildConst.MaxBankTabs; ++i) + m_bankWithdraw[i] = field.Read(5 + i); + + m_bankWithdrawMoney = field.Read(13); SetStats(field.Read(14), field.Read(15), // characters.level @@ -2566,26 +2571,40 @@ namespace Game.Guilds return true; } - public void UpdateBankWithdrawValue(SQLTransaction trans, byte tabId, uint amount) + // Decreases amount of slots left for today. + public void UpdateBankTabWithdrawValue(SQLTransaction trans, byte tabId, uint amount) { - m_bankWithdraw[tabId] += (int)amount; + m_bankWithdraw[tabId] += amount; - PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_GUILD_MEMBER_WITHDRAW); + PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_GUILD_MEMBER_WITHDRAW_TABS); stmt.AddValue(0, m_guid.GetCounter()); - for (byte i = 0; i <= GuildConst.MaxBankTabs; ) + for (byte i = 0; i < GuildConst.MaxBankTabs; ) { - int withdraw = m_bankWithdraw[i++]; + uint withdraw = m_bankWithdraw[i++]; stmt.AddValue(i, withdraw); } DB.Characters.ExecuteOrAppend(trans, stmt); } + // Decreases amount of money left for today. + public void UpdateBankMoneyWithdrawValue(SQLTransaction trans, ulong amount) + { + m_bankWithdrawMoney += amount; + + PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_GUILD_MEMBER_WITHDRAW_MONEY); + stmt.AddValue(0, m_guid.GetCounter()); + stmt.AddValue(1, m_bankWithdrawMoney); + DB.Characters.ExecuteOrAppend(trans, stmt); + } + public void ResetValues(bool weekly = false) { - for (byte tabId = 0; tabId <= GuildConst.MaxBankTabs; ++tabId) + for (byte tabId = 0; tabId < GuildConst.MaxBankTabs; ++tabId) m_bankWithdraw[tabId] = 0; + m_bankWithdrawMoney = 0; + if (weekly) { m_weekActivity = 0; @@ -2593,15 +2612,6 @@ namespace Game.Guilds } } - public int GetBankWithdrawValue(byte tabId) - { - // Guild master has unlimited amount. - if (IsRank(GuildDefaultRanks.Master)) - return tabId == GuildConst.MaxBankTabs ? GuildConst.WithdrawMoneyUnlimited : GuildConst.WithdrawSlotUnlimited; - - return m_bankWithdraw[tabId]; - } - public void SetZoneId(uint id) { m_zoneId = id; } public void SetAchievementPoints(uint val) { m_achievementPoints = val; } @@ -2642,6 +2652,9 @@ namespace Game.Guilds public bool IsRankNotLower(uint rankId) { return m_rankId <= rankId; } public bool IsSamePlayer(ObjectGuid guid) { return m_guid == guid; } + public uint GetBankTabWithdrawValue(byte tabId) { return m_bankWithdraw[tabId]; } + public ulong GetBankMoneyWithdrawValue() { return m_bankWithdrawMoney; } + public Player FindPlayer() { return Global.ObjAccessor.FindPlayer(m_guid); } #region Fields @@ -2661,7 +2674,8 @@ namespace Game.Guilds List m_trackedCriteriaIds = new List(); - int[] m_bankWithdraw = new int[GuildConst.MaxBankTabs + 1]; + uint[] m_bankWithdraw = new uint[GuildConst.MaxBankTabs]; + ulong m_bankWithdrawMoney; uint m_achievementPoints; ulong m_totalActivity; ulong m_weekActivity; @@ -3088,9 +3102,6 @@ namespace Game.Guilds public void SetBankMoneyPerDay(uint money) { - if (m_rankId == GuildDefaultRanks.Master) // Prevent loss of leader rights - money = Convert.ToUInt32(GuildConst.WithdrawMoneyUnlimited); - if (m_bankMoneyPerDay == money) return; @@ -3129,7 +3140,10 @@ namespace Game.Guilds public GuildRankRights GetRights() { return m_rights; } - public int GetBankMoneyPerDay() { return (int)m_bankMoneyPerDay; } + public uint GetBankMoneyPerDay() + { + return m_rankId != GuildDefaultRanks.Master ? m_bankMoneyPerDay : GuildConst.WithdrawMoneyUnlimited; + } public GuildBankRights GetBankTabRights(byte tabId) { @@ -3323,7 +3337,7 @@ namespace Game.Guilds public void SetGuildMasterValues() { rights = GuildBankRights.Full; - slots = GuildConst.WithdrawSlotUnlimited; + slots = Convert.ToInt32(GuildConst.WithdrawSlotUnlimited); } public void SetTabId(byte _tabId) { tabId = _tabId; } diff --git a/Game/Handlers/AuctionHandler.cs b/Game/Handlers/AuctionHandler.cs index 724a98fc7..bda2c91e9 100644 --- a/Game/Handlers/AuctionHandler.cs +++ b/Game/Handlers/AuctionHandler.cs @@ -216,7 +216,7 @@ namespace Game uint auctionTime = (uint)(packet.RunTime * WorldConfig.GetFloatValue(WorldCfg.RateAuctionTime)); AuctionHouseObject auctionHouse = Global.AuctionMgr.GetAuctionsMap(creature.getFaction()); - uint deposit = Global.AuctionMgr.GetAuctionDeposit(auctionHouseEntry, packet.RunTime, item, finalCount); + ulong deposit = Global.AuctionMgr.GetAuctionDeposit(auctionHouseEntry, packet.RunTime, item, finalCount); if (!GetPlayer().HasEnoughMoney(deposit)) { SendAuctionCommandResult(null, AuctionAction.SellItem, AuctionError.NotEnoughtMoney); @@ -345,7 +345,7 @@ namespace Game GetPlayer().UpdateCriteria(CriteriaTypes.CreateAuction, 1); } - GetPlayer().ModifyMoney(-deposit); + GetPlayer().ModifyMoney(-(long)deposit); } [WorldPacketHandler(ClientOpcodes.AuctionPlaceBid)] @@ -445,10 +445,10 @@ namespace Game { //buyout: if (player.GetGUID().GetCounter() == auction.bidder) - player.ModifyMoney(-auction.buyout - auction.bid); + player.ModifyMoney(-(long)(auction.buyout - auction.bid)); else { - player.ModifyMoney(-auction.buyout); + player.ModifyMoney(-(long)auction.buyout); if (auction.bidder != 0) //buyout for bidded auction .. Global.AuctionMgr.SendAuctionOutbiddedMail(auction, auction.buyout, GetPlayer(), trans); } @@ -500,11 +500,11 @@ namespace Game { if (auction.bidder > 0) // If we have a bidder, we have to send him the money he paid { - uint auctionCut = auction.GetAuctionCut(); + ulong auctionCut = auction.GetAuctionCut(); if (!player.HasEnoughMoney(auctionCut)) //player doesn't have enough money, maybe message needed return; Global.AuctionMgr.SendAuctionCancelledToBidderMail(auction, trans); - player.ModifyMoney(-auctionCut); + player.ModifyMoney(-(long)auctionCut); } // item will deleted or added to received mail list diff --git a/Game/Handlers/CharacterHandler.cs b/Game/Handlers/CharacterHandler.cs index 84923057f..7452c5c20 100644 --- a/Game/Handlers/CharacterHandler.cs +++ b/Game/Handlers/CharacterHandler.cs @@ -2451,6 +2451,14 @@ namespace Game stmt.AddValue(0, lowGuid); SetQuery(PlayerLoginQueryLoad.QuestStatusObjectives, stmt); + stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_QUESTSTATUS_OBJECTIVES_CRITERIA); + stmt.AddValue(0, lowGuid); + SetQuery(PlayerLoginQueryLoad.QuestStatusObjectivesCriteria, stmt); + + stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_QUESTSTATUS_OBJECTIVES_CRITERIA_PROGRESS); + stmt.AddValue(0, lowGuid); + SetQuery(PlayerLoginQueryLoad.QuestStatusObjectivesCriteriaProgress, stmt); + stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_QUESTSTATUS_DAILY); stmt.AddValue(0, lowGuid); SetQuery(PlayerLoginQueryLoad.DailyQuestStatus, stmt); @@ -2631,6 +2639,8 @@ namespace Game Spells, QuestStatus, QuestStatusObjectives, + QuestStatusObjectivesCriteria, + QuestStatusObjectivesCriteriaProgress, DailyQuestStatus, Reputation, Inventory, diff --git a/Game/Handlers/ItemHandler.cs b/Game/Handlers/ItemHandler.cs index 5aede5121..da5ea2b52 100644 --- a/Game/Handlers/ItemHandler.cs +++ b/Game/Handlers/ItemHandler.cs @@ -437,6 +437,16 @@ namespace Game { if (pProto.GetSellPrice() > 0) { + ulong money = pProto.GetSellPrice() * packet.Amount; + + if (!_player.ModifyMoney((long)money)) // ensure player doesn't exceed gold limit + { + _player.SendSellError(SellResult.CantSellItem, creature, packet.ItemGUID); + return; + } + + _player.UpdateCriteria(CriteriaTypes.MoneyFromVendors, money); + if (packet.Amount < pItem.GetCount()) // need split items { Item pNewItem = pItem.CloneItem(packet.Amount, pl); @@ -464,10 +474,6 @@ namespace Game Item.RemoveItemFromUpdateQueueOf(pItem, pl); pl.AddItemToBuyBackSlot(pItem); } - - uint money = pProto.GetSellPrice() * packet.Amount; - pl.ModifyMoney(money); - pl.UpdateCriteria(CriteriaTypes.MoneyFromVendors, money); } else pl.SendSellError(SellResult.CantSellItem, creature, packet.ItemGUID); diff --git a/Game/Handlers/MailHandler.cs b/Game/Handlers/MailHandler.cs index 35357f6be..adafccc7d 100644 --- a/Game/Handlers/MailHandler.cs +++ b/Game/Handlers/MailHandler.cs @@ -288,6 +288,7 @@ namespace Game item.DeleteFromInventoryDB(trans); // deletes item from character's inventory item.SetOwnerGUID(receiverGuid); + item.SetState(ItemUpdateState.Changed); item.SaveToDB(trans); // recursive and not have transaction guard into self, item not in inventory and can be save standalone draft.AddItem(item); @@ -494,7 +495,7 @@ namespace Game .SendMailTo(trans, new MailReceiver(receiver, m.sender), new MailSender( MailMessageType.Normal, m.receiver), MailCheckMask.CodPayment); } - player.ModifyMoney(-(int)m.COD); + player.ModifyMoney(-(long)m.COD); } m.COD = 0; m.state = MailState.Changed; diff --git a/Game/Handlers/NPCHandler.cs b/Game/Handlers/NPCHandler.cs index 452f88008..8ac3de6ff 100644 --- a/Game/Handlers/NPCHandler.cs +++ b/Game/Handlers/NPCHandler.cs @@ -56,18 +56,22 @@ namespace Game [WorldPacketHandler(ClientOpcodes.TrainerList)] void HandleTrainerList(Hello packet) { - Log.outInfo(LogFilter.Network, $"{GetPlayerInfo()} sent legacy gossipless trainer hello for unit {packet.Unit.ToString()}, no trainer list available"); - } - - public void SendTrainerList(ObjectGuid guid, uint trainerId) - { - Creature unit = GetPlayer().GetNPCIfCanInteractWith(guid, NPCFlags.Trainer); - if (unit == null) + Creature npc = GetPlayer().GetNPCIfCanInteractWith(packet.Unit, NPCFlags.Trainer); + if (!npc) { - Log.outDebug(LogFilter.Network, $"WORLD: SendTrainerList - {guid.ToString()} not found or you can not interact with him."); + Log.outDebug(LogFilter.Network, $"WorldSession.SendTrainerList - {packet.Unit.ToString()} not found or you can not interact with him."); return; } + uint trainerId = Global.ObjectMgr.GetCreatureDefaultTrainer(npc.GetEntry()); + if (trainerId != 0) + SendTrainerList(npc, trainerId); + else + Log.outDebug(LogFilter.Network, $"WorldSession.SendTrainerList - Creature id {npc.GetEntry()} has no trainer data."); + } + + public void SendTrainerList(Creature npc, uint trainerId) + { // remove fake death if (GetPlayer().HasUnitState(UnitState.Died)) GetPlayer().RemoveAurasByType(AuraType.FeignDeath); @@ -75,14 +79,14 @@ namespace Game Trainer trainer = Global.ObjectMgr.GetTrainer(trainerId); if (trainer == null) { - Log.outDebug(LogFilter.Network, $"WORLD: SendTrainerList - trainer spells not found for trainer {guid.ToString()} id {trainerId}"); + Log.outDebug(LogFilter.Network, $"WORLD: SendTrainerList - trainer spells not found for trainer {npc.GetGUID().ToString()} id {trainerId}"); return; } _player.PlayerTalkClass.GetInteractionData().Reset(); - _player.PlayerTalkClass.GetInteractionData().SourceGuid = guid; + _player.PlayerTalkClass.GetInteractionData().SourceGuid = npc.GetGUID(); _player.PlayerTalkClass.GetInteractionData().TrainerId = trainerId; - trainer.SendSpells(unit, _player, GetSessionDbLocaleIndex()); + trainer.SendSpells(npc, _player, GetSessionDbLocaleIndex()); } [WorldPacketHandler(ClientOpcodes.TrainerBuySpell)] diff --git a/Game/Maps/Instances/InstanceScript.cs b/Game/Maps/Instances/InstanceScript.cs index 52c6cde4e..0f20105f2 100644 --- a/Game/Maps/Instances/InstanceScript.cs +++ b/Game/Maps/Instances/InstanceScript.cs @@ -343,7 +343,7 @@ namespace Game.Maps } } - foreach (var guid in bossInfo.minion) + foreach (var guid in bossInfo.minion.ToArray()) { Creature minion = instance.GetCreature(guid); if (minion) @@ -608,7 +608,7 @@ namespace Game.Maps return false; } - void SetEntranceLocation(uint worldSafeLocationId) + public void SetEntranceLocation(uint worldSafeLocationId) { _entranceId = worldSafeLocationId; if (_temporaryEntranceId != 0) diff --git a/Game/Maps/Map.cs b/Game/Maps/Map.cs index 8733cec0a..98c83b3be 100644 --- a/Game/Maps/Map.cs +++ b/Game/Maps/Map.cs @@ -2701,6 +2701,22 @@ namespace Game.Maps } } + public void UpdateAreaDependentAuras() + { + var players = GetPlayers(); + foreach (var player in players) + { + if (player) + { + if (player.IsInWorld) + { + player.UpdateAreaDependentAuras(player.GetAreaId()); + player.UpdateZoneDependentAuras(player.GetZoneId()); + } + } + } + } + public MapRecord GetEntry() { return i_mapRecord; @@ -4391,6 +4407,7 @@ namespace Game.Maps { var data = result.Read(0); i_data.SetCompletedEncountersMask(result.Read(1)); + i_data.SetEntranceLocation(result.Read(2)); if (data != "") { Log.outDebug(LogFilter.Maps, "Loading instance data for `{0}` with id {1}", diff --git a/Game/OutdoorPVP/Zones/HellfirePeninsulaPvP.cs b/Game/OutdoorPVP/Zones/HellfirePeninsulaPvP.cs index 829b3a716..9eb0fa197 100644 --- a/Game/OutdoorPVP/Zones/HellfirePeninsulaPvP.cs +++ b/Game/OutdoorPVP/Zones/HellfirePeninsulaPvP.cs @@ -23,83 +23,6 @@ using Game.Scripting; namespace Game.PvP { - struct HPConst - { - public static uint[] LangCapture_A = { DefenseMessages.BrokenHillTakenAlliance, DefenseMessages.OverlookTakenAlliance, DefenseMessages.StadiumTakenAlliance }; - - public static uint[] LangCapture_H = { DefenseMessages.BrokenHillTakenHorde, DefenseMessages.OverlookTakenHorde, DefenseMessages.StadiumTakenHorde }; - - public static uint[] Map_N = { 0x9b5, 0x9b2, 0x9a8 }; - - public static uint[] Map_A = { 0x9b3, 0x9b0, 0x9a7 }; - - public static uint[] Map_H = { 0x9b4, 0x9b1, 0x9a6 }; - - public static uint[] TowerArtKit_A = { 65, 62, 67 }; - - public static uint[] TowerArtKit_H = { 64, 61, 68 }; - - public static uint[] TowerArtKit_N = { 66, 63, 69 }; - - // HP, citadel, ramparts, blood furnace, shattered halls, mag's lair - public static uint[] BuffZones = { 3483, 3563, 3562, 3713, 3714, 3836 }; - - public static uint[] CreditMarker = { 19032, 19028, 19029 }; - - public static uint[] CapturePointEventEnter = { 11404, 11396, 11388 }; - - public static uint[] CapturePointEventLeave = { 11403, 11395, 11387 }; - - public static go_type[] CapturePoints = - { - new go_type(182175, 530, -471.462f, 3451.09f, 34.6432f, 0.174533f, 0.0f, 0.0f, 0.087156f, 0.996195f), // 0 - Broken Hill - new go_type(182174, 530, -184.889f, 3476.93f, 38.205f, -0.017453f, 0.0f, 0.0f, 0.008727f, -0.999962f), // 1 - Overlook - new go_type(182173, 530, -290.016f, 3702.42f, 56.6729f, 0.034907f, 0.0f, 0.0f, 0.017452f, 0.999848f) // 2 - Stadium - }; - - public static go_type[] TowerFlags = - { - new go_type(183514, 530, -467.078f, 3528.17f, 64.7121f, 3.14159f, 0.0f, 0.0f, 1.0f, 0.0f), // 0 broken hill - new go_type(182525, 530, -187.887f, 3459.38f, 60.0403f, -3.12414f, 0.0f, 0.0f, 0.999962f, -0.008727f), // 1 overlook - new go_type(183515, 530, -289.610f, 3696.83f, 75.9447f, 3.12414f, 0.0f, 0.0f, 0.999962f, 0.008727f) // 2 stadium - }; - } - - struct DefenseMessages - { - public const uint OverlookTakenAlliance = 14841; // '|cffffff00The Overlook has been taken by the Alliance!|r' - public const uint OverlookTakenHorde = 14842; // '|cffffff00The Overlook has been taken by the Horde!|r' - public const uint StadiumTakenAlliance = 14843; // '|cffffff00The Stadium has been taken by the Alliance!|r' - public const uint StadiumTakenHorde = 14844; // '|cffffff00The Stadium has been taken by the Horde!|r' - public const uint BrokenHillTakenAlliance = 14845; // '|cffffff00Broken Hill has been taken by the Alliance!|r' - public const uint BrokenHillTakenHorde = 14846; // '|cffffff00Broken Hill has been taken by the Horde!|r' - } - - struct OutdoorPvPHPSpells - { - public const uint AlliancePlayerKillReward = 32155; - public const uint HordePlayerKillReward = 32158; - public const uint AllianceBuff = 32071; - public const uint HordeBuff = 32049; - } - - enum OutdoorPvPHPTowerType - { - BrokenHill = 0, - Overlook = 1, - Stadium = 2, - Num = 3 - } - - struct OutdoorPvPHPWorldStates - { - public const uint Display_A = 0x9ba; - public const uint Display_H = 0x9b9; - - public const uint Count_H = 0x9ae; - public const uint Count_A = 0x9ac; - } - class HellfirePeninsulaPvP : OutdoorPvP { public HellfirePeninsulaPvP() @@ -397,4 +320,81 @@ namespace Game.PvP return new HellfirePeninsulaPvP(); } } + + struct HPConst + { + public static uint[] LangCapture_A = { DefenseMessages.BrokenHillTakenAlliance, DefenseMessages.OverlookTakenAlliance, DefenseMessages.StadiumTakenAlliance }; + + public static uint[] LangCapture_H = { DefenseMessages.BrokenHillTakenHorde, DefenseMessages.OverlookTakenHorde, DefenseMessages.StadiumTakenHorde }; + + public static uint[] Map_N = { 0x9b5, 0x9b2, 0x9a8 }; + + public static uint[] Map_A = { 0x9b3, 0x9b0, 0x9a7 }; + + public static uint[] Map_H = { 0x9b4, 0x9b1, 0x9a6 }; + + public static uint[] TowerArtKit_A = { 65, 62, 67 }; + + public static uint[] TowerArtKit_H = { 64, 61, 68 }; + + public static uint[] TowerArtKit_N = { 66, 63, 69 }; + + // HP, citadel, ramparts, blood furnace, shattered halls, mag's lair + public static uint[] BuffZones = { 3483, 3563, 3562, 3713, 3714, 3836 }; + + public static uint[] CreditMarker = { 19032, 19028, 19029 }; + + public static uint[] CapturePointEventEnter = { 11404, 11396, 11388 }; + + public static uint[] CapturePointEventLeave = { 11403, 11395, 11387 }; + + public static go_type[] CapturePoints = + { + new go_type(182175, 530, -471.462f, 3451.09f, 34.6432f, 0.174533f, 0.0f, 0.0f, 0.087156f, 0.996195f), // 0 - Broken Hill + new go_type(182174, 530, -184.889f, 3476.93f, 38.205f, -0.017453f, 0.0f, 0.0f, 0.008727f, -0.999962f), // 1 - Overlook + new go_type(182173, 530, -290.016f, 3702.42f, 56.6729f, 0.034907f, 0.0f, 0.0f, 0.017452f, 0.999848f) // 2 - Stadium + }; + + public static go_type[] TowerFlags = + { + new go_type(183514, 530, -467.078f, 3528.17f, 64.7121f, 3.14159f, 0.0f, 0.0f, 1.0f, 0.0f), // 0 broken hill + new go_type(182525, 530, -187.887f, 3459.38f, 60.0403f, -3.12414f, 0.0f, 0.0f, 0.999962f, -0.008727f), // 1 overlook + new go_type(183515, 530, -289.610f, 3696.83f, 75.9447f, 3.12414f, 0.0f, 0.0f, 0.999962f, 0.008727f) // 2 stadium + }; + } + + struct DefenseMessages + { + public const uint OverlookTakenAlliance = 14841; // '|cffffff00The Overlook has been taken by the Alliance!|r' + public const uint OverlookTakenHorde = 14842; // '|cffffff00The Overlook has been taken by the Horde!|r' + public const uint StadiumTakenAlliance = 14843; // '|cffffff00The Stadium has been taken by the Alliance!|r' + public const uint StadiumTakenHorde = 14844; // '|cffffff00The Stadium has been taken by the Horde!|r' + public const uint BrokenHillTakenAlliance = 14845; // '|cffffff00Broken Hill has been taken by the Alliance!|r' + public const uint BrokenHillTakenHorde = 14846; // '|cffffff00Broken Hill has been taken by the Horde!|r' + } + + struct OutdoorPvPHPSpells + { + public const uint AlliancePlayerKillReward = 32155; + public const uint HordePlayerKillReward = 32158; + public const uint AllianceBuff = 32071; + public const uint HordeBuff = 32049; + } + + enum OutdoorPvPHPTowerType + { + BrokenHill = 0, + Overlook = 1, + Stadium = 2, + Num = 3 + } + + struct OutdoorPvPHPWorldStates + { + public const uint Display_A = 0x9ba; + public const uint Display_H = 0x9b9; + + public const uint Count_H = 0x9ae; + public const uint Count_A = 0x9ac; + } } diff --git a/Game/Quest/Quest.cs b/Game/Quest/Quest.cs index 03f27997f..ef7402a47 100644 --- a/Game/Quest/Quest.cs +++ b/Game/Quest/Quest.cs @@ -397,13 +397,13 @@ namespace Game } public bool HasFlag(QuestFlags flag) { return (Flags & flag) != 0; } - public void SetFlag(QuestFlags flag) { Flags |= flag; } public bool HasSpecialFlag(QuestSpecialFlags flag) { return (SpecialFlags & flag) != 0; } - public void SetSpecialFlag(QuestSpecialFlags flag) { SpecialFlags |= flag; } + public bool HasFlagEx(QuestFlagsEx flag) { return (FlagsEx & flag) != 0; } + // table data accessors: public bool IsRepeatable() { return SpecialFlags.HasAnyFlag(QuestSpecialFlags.Repeatable); } public bool IsDaily() { return Flags.HasAnyFlag(QuestFlags.Daily); } diff --git a/Game/Quest/QuestObjectiveCriteriaManager.cs b/Game/Quest/QuestObjectiveCriteriaManager.cs new file mode 100644 index 000000000..3a30c2100 --- /dev/null +++ b/Game/Quest/QuestObjectiveCriteriaManager.cs @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2012-2017 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 . + */ + +using Framework.Constants; +using Framework.Database; +using Game.Achievements; +using Game.Entities; +using Game.Network; +using Game.Network.Packets; +using System.Collections.Generic; + +namespace Game +{ + class QuestObjectiveCriteriaManager : CriteriaHandler + { + public QuestObjectiveCriteriaManager(Player owner) + { + _owner = owner; + } + + public void CheckAllQuestObjectiveCriteria(Player referencePlayer) + { + // suppress sending packets + for (CriteriaTypes i = 0; i < CriteriaTypes.TotalTypes; ++i) + UpdateCriteria(i, 0, 0, 0, null, referencePlayer); + } + + public override void Reset() + { + foreach (var pair in _criteriaProgress) + SendCriteriaProgressRemoved(pair.Key); + + _criteriaProgress.Clear(); + + DeleteFromDB(_owner.GetGUID()); + + // re-fill data + CheckAllQuestObjectiveCriteria(_owner); + } + + public static void DeleteFromDB(ObjectGuid guid) + { + SQLTransaction trans = new SQLTransaction(); + + PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_QUESTSTATUS_OBJECTIVES_CRITERIA); + stmt.AddValue(0, guid.GetCounter()); + trans.Append(stmt); + + stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_QUESTSTATUS_OBJECTIVES_CRITERIA_PROGRESS); + stmt.AddValue(0, guid.GetCounter()); + trans.Append(stmt); + + DB.Characters.CommitTransaction(trans); + } + + public void LoadFromDB(SQLResult objectiveResult, SQLResult criteriaResult) + { + if (!objectiveResult.IsEmpty()) + { + do + { + uint objectiveId = objectiveResult.Read(0); + + QuestObjective objective = Global.ObjectMgr.GetQuestObjective(objectiveId); + if (objective == null) + continue; + + _completedObjectives.Add(objectiveId); + + } while (objectiveResult.NextRow()); + } + + if (!criteriaResult.IsEmpty()) + { + long now = Time.UnixTime; + do + { + uint criteriaId = criteriaResult.Read(0); + ulong counter = criteriaResult.Read(1); + long date = criteriaResult.Read(2); + + Criteria criteria = Global.CriteriaMgr.GetCriteria(criteriaId); + if (criteria == null) + { + // Removing non-existing criteria data for all characters + Log.outError(LogFilter.Player, "Non-existing quest objective criteria {criteriaId} data has been removed from the table `character_queststatus_objectives_criteria_progress`."); + + PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_INVALID_QUEST_PROGRESS_CRITERIA); + stmt.AddValue(0, criteriaId); + DB.Characters.Execute(stmt); + + continue; + } + + if (criteria.Entry.StartTimer != 0 && date + criteria.Entry.StartTimer < now) + continue; + + CriteriaProgress progress = new CriteriaProgress(); + progress.Counter = counter; + progress.Date = date; + progress.Changed = false; + + _criteriaProgress[criteriaId] = progress; + } while (criteriaResult.NextRow()); + } + } + + public void SaveToDB(SQLTransaction trans) + { + PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_QUESTSTATUS_OBJECTIVES_CRITERIA); + stmt.AddValue(0, _owner.GetGUID().GetCounter()); + trans.Append(stmt); + + if (!_completedObjectives.Empty()) + { + foreach (uint completedObjectiveId in _completedObjectives) + { + stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_CHAR_QUESTSTATUS_OBJECTIVES_CRITERIA); + stmt.AddValue(0, _owner.GetGUID().GetCounter()); + stmt.AddValue(1, completedObjectiveId); + trans.Append(stmt); + } + } + + if (!_criteriaProgress.Empty()) + { + foreach (var pair in _criteriaProgress) + { + if (!pair.Value.Changed) + continue; + + stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_QUESTSTATUS_OBJECTIVES_CRITERIA_PROGRESS_BY_CRITERIA); + stmt.AddValue(0, _owner.GetGUID().GetCounter()); + stmt.AddValue(1, pair.Key); + trans.Append(stmt); + + if (pair.Value.Counter != 0) + { + stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_CHAR_QUESTSTATUS_OBJECTIVES_CRITERIA_PROGRESS); + stmt.AddValue(0, _owner.GetGUID().GetCounter()); + stmt.AddValue(1, pair.Key); + stmt.AddValue(2, pair.Value.Counter); + stmt.AddValue(3, (uint)pair.Value.Date); + trans.Append(stmt); + } + + pair.Value.Changed = false; + } + } + } + + public void ResetCriteria(CriteriaTypes type, ulong miscValue1, ulong miscValue2, bool evenIfCriteriaComplete) + { + Log.outDebug(LogFilter.Player, "QuestObjectiveCriteriaMgr.ResetCriteria({type}, {miscValue1}, {miscValue2})"); + + // disable for gamemasters with GM-mode enabled + if (_owner.IsGameMaster()) + return; + + var playerCriteriaList = GetCriteriaByType(type); + foreach (Criteria playerCriteria in playerCriteriaList) + { + if (playerCriteria.Entry.FailEvent != miscValue1 || (playerCriteria.Entry.FailAsset != 0 && playerCriteria.Entry.FailAsset != miscValue2)) + continue; + + var trees = Global.CriteriaMgr.GetCriteriaTreesByCriteria(playerCriteria.ID); + bool allComplete = true; + foreach (CriteriaTree tree in trees) + { + // don't update already completed criteria if not forced + if (!(IsCompletedCriteriaTree(tree) && !evenIfCriteriaComplete)) + { + allComplete = false; + break; + } + } + + if (allComplete) + continue; + + RemoveCriteriaProgress(playerCriteria); + } + } + + public void ResetCriteriaTree(uint criteriaTreeId) + { + CriteriaTree tree = Global.CriteriaMgr.GetCriteriaTree(criteriaTreeId); + if (tree == null) + return; + + CriteriaManager.WalkCriteriaTree(tree, criteriaTree => + { + RemoveCriteriaProgress(criteriaTree.Criteria); + }); + } + + public override void SendAllData(Player receiver) + { + foreach (var pair in _criteriaProgress) + { + CriteriaUpdate criteriaUpdate = new CriteriaUpdate(); + + criteriaUpdate.CriteriaID = pair.Key; + criteriaUpdate.Quantity = pair.Value.Counter; + criteriaUpdate.PlayerGUID = _owner.GetGUID(); + criteriaUpdate.Flags = 0; + + criteriaUpdate.CurrentTime = pair.Value.Date; + criteriaUpdate.CreationTime = 0; + + SendPacket(criteriaUpdate); + } + } + + void CompletedObjective(QuestObjective questObjective, Player referencePlayer) + { + // disable for gamemasters with GM-mode enabled + if (_owner.IsGameMaster()) + return; + + if (HasCompletedObjective(questObjective)) + return; + + referencePlayer.KillCreditCriteriaTreeObjective(questObjective); + + Log.outInfo(LogFilter.Player, "QuestObjectiveCriteriaMgr.CompletedObjective({questObjective.ID}). {GetOwnerInfo()}"); + + _completedObjectives.Add(questObjective.ID); + } + + public bool HasCompletedObjective(QuestObjective questObjective) + { + return _completedObjectives.Contains(questObjective.ID); + } + + public override void SendCriteriaUpdate(Criteria criteria, CriteriaProgress progress, uint timeElapsed, bool timedCompleted) + { + CriteriaUpdate criteriaUpdate = new CriteriaUpdate(); + + criteriaUpdate.CriteriaID = criteria.ID; + criteriaUpdate.Quantity = progress.Counter; + criteriaUpdate.PlayerGUID = _owner.GetGUID(); + criteriaUpdate.Flags = 0; + if (criteria.Entry.StartTimer != 0) + criteriaUpdate.Flags = timedCompleted ? 1 : 0u; // 1 is for keeping the counter at 0 in client + + criteriaUpdate.CurrentTime = progress.Date; + criteriaUpdate.ElapsedTime = timeElapsed; + criteriaUpdate.CreationTime = 0; + + SendPacket(criteriaUpdate); + } + + public override void SendCriteriaProgressRemoved(uint criteriaId) + { + CriteriaDeleted criteriaDeleted = new CriteriaDeleted(); + criteriaDeleted.CriteriaID = criteriaId; + SendPacket(criteriaDeleted); + } + + public override bool CanUpdateCriteriaTree(Criteria criteria, CriteriaTree tree, Player referencePlayer) + { + QuestObjective objective = tree.QuestObjective; + if (objective == null) + return false; + + if (HasCompletedObjective(objective)) + { + Log.outTrace(LogFilter.Player, "QuestObjectiveCriteriaMgr.CanUpdateCriteriaTree: (Id: {criteria.ID} Type {criteria.Entry.Type} Quest Objective {objective.ID}) Objective already completed"); + return false; + } + + return base.CanUpdateCriteriaTree(criteria, tree, referencePlayer); + } + + public override bool CanCompleteCriteriaTree(CriteriaTree tree) + { + QuestObjective objective = tree.QuestObjective; + if (objective == null) + return false; + + return base.CanCompleteCriteriaTree(tree); + } + + public override void CompletedCriteriaTree(CriteriaTree tree, Player referencePlayer) + { + QuestObjective objective = tree.QuestObjective; + if (objective == null) + return; + + CompletedObjective(objective, referencePlayer); + } + + public override void SendPacket(ServerPacket data) + { + _owner.SendPacket(data); + } + + public override string GetOwnerInfo() + { + return $"{_owner.GetGUID().ToString()} {_owner.GetName()}"; + } + + public override List GetCriteriaByType(CriteriaTypes type) + { + return Global.CriteriaMgr.GetQuestObjectiveCriteriaByType(type); + } + + + Player _owner; + List _completedObjectives = new List(); + } +} diff --git a/Game/Server/WorldConfig.cs b/Game/Server/WorldConfig.cs index 2a357ad03..3a3c965c8 100644 --- a/Game/Server/WorldConfig.cs +++ b/Game/Server/WorldConfig.cs @@ -288,8 +288,6 @@ namespace Game Values[WorldCfg.GroupXpDistance] = GetDefaultValue("MaxGroupXPDistance", 74.0f); Values[WorldCfg.MaxRecruitAFriendDistance] = GetDefaultValue("MaxRecruitAFriendBonusDistance", 100.0f); - - /// @todo Add MonsterSight (with meaning) in worldserver.conf or put them as define Values[WorldCfg.SightMonster] = GetDefaultValue("MonsterSight", 50.0f); if (reload) diff --git a/Game/Server/WorldManager.cs b/Game/Server/WorldManager.cs index cf6c83ff1..e2c885c1f 100644 --- a/Game/Server/WorldManager.cs +++ b/Game/Server/WorldManager.cs @@ -705,6 +705,9 @@ namespace Game Log.outInfo(LogFilter.ServerLoading, "Loading Trainers..."); Global.ObjectMgr.LoadTrainers(); // must be after load CreatureTemplate + Log.outInfo(LogFilter.ServerLoading, "Loading Creature default trainers..."); + Global.ObjectMgr.LoadCreatureDefaultTrainers(); + Log.outInfo(LogFilter.ServerLoading, "Loading Gossip menu..."); Global.ObjectMgr.LoadGossipMenu(); diff --git a/Game/Spells/Spell.cs b/Game/Spells/Spell.cs index ab0730644..bbe7bec57 100644 --- a/Game/Spells/Spell.cs +++ b/Game/Spells/Spell.cs @@ -5575,9 +5575,7 @@ namespace Game.Spells if (!strict && m_casttime == 0) return SpellCastResult.SpellCastOk; - var pair = GetMinMaxRange(strict); - float minRange = pair.Item1; - float maxRange = pair.Item2; + (float minRange, float maxRange) = GetMinMaxRange(strict); // get square values for sqr distance checks minRange *= minRange; diff --git a/Game/Spells/SpellEffects.cs b/Game/Spells/SpellEffects.cs index 2ff6352ae..fe550e658 100644 --- a/Game/Spells/SpellEffects.cs +++ b/Game/Spells/SpellEffects.cs @@ -5846,6 +5846,18 @@ namespace Game.Spells playerTarget.AddHonorXP((uint)damage); playerTarget.SendPacket(packet); } + + [SpellEffectHandler(SpellEffectName.LearnTransmogSet)] + void EffectLearnTransmogSet(uint effIndex) + { + if (effectHandleMode != SpellEffectHandleMode.HitTarget) + return; + + if (!unitTarget || !unitTarget.IsPlayer()) + return; + + unitTarget.ToPlayer().GetSession().GetCollectionMgr().AddTransmogSet((uint)effectInfo.MiscValue); + } } public class DispelCharges diff --git a/Game/Spells/SpellInfo.cs b/Game/Spells/SpellInfo.cs index d9b0d0c30..f92f24718 100644 --- a/Game/Spells/SpellInfo.cs +++ b/Game/Spells/SpellInfo.cs @@ -3313,7 +3313,7 @@ namespace Game.Spells new StaticData(SpellEffectImplicitTargetTypes.None, SpellTargetObjectTypes.None), // 252 SPELL_EFFECT_252 new StaticData(SpellEffectImplicitTargetTypes.Explicit, SpellTargetObjectTypes.Unit), // 253 SPELL_EFFECT_GIVE_HONOR new StaticData(SpellEffectImplicitTargetTypes.None, SpellTargetObjectTypes.None), // 254 SPELL_EFFECT_254 - new StaticData(SpellEffectImplicitTargetTypes.None, SpellTargetObjectTypes.None) // 255 SPELL_EFFECT_255 + new StaticData(SpellEffectImplicitTargetTypes.Explicit, SpellTargetObjectTypes.Unit) // 255 SPELL_EFFECT_LEARN_TRANSMOG_SET }; #region Fields diff --git a/Game/Spells/SpellManager.cs b/Game/Spells/SpellManager.cs index 61324af7a..e62489dc4 100644 --- a/Game/Spells/SpellManager.cs +++ b/Game/Spells/SpellManager.cs @@ -509,7 +509,7 @@ namespace Game.Entities if (procEntry.SpellFamilyName != 0 && eventSpellInfo != null && (procEntry.SpellFamilyName != eventSpellInfo.SpellFamilyName)) return false; - if (procEntry.SpellFamilyMask != null && eventSpellInfo != null && !(procEntry.SpellFamilyMask & eventSpellInfo.SpellFamilyFlags)) + if (eventSpellInfo != null && !(procEntry.SpellFamilyMask & eventSpellInfo.SpellFamilyFlags)) return false; } @@ -3158,7 +3158,7 @@ namespace Game.Entities case AuraType.SpellMagnet: case AuraType.ModAttackPower: case AuraType.ModPowerRegenPercent: - case AuraType.AddCasterHitTrigger: + case AuraType.InterceptMeleeRangedAttacks: case AuraType.OverrideClassScripts: case AuraType.ModMechanicResistance: case AuraType.MeleeAttackPowerAttackerBonus: @@ -3385,7 +3385,7 @@ namespace Game.Entities { public SpellSchoolMask SchoolMask { get; set; } // if nonzero - bitmask for matching proc condition based on spell's school public SpellFamilyNames SpellFamilyName { get; set; } // if nonzero - for matching proc condition based on candidate spell's SpellFamilyName - public FlagArray128 SpellFamilyMask { get; set; } // if nonzero - bitmask for matching proc condition based on candidate spell's SpellFamilyFlags + public FlagArray128 SpellFamilyMask { get; set; } = new FlagArray128(); // if nonzero - bitmask for matching proc condition based on candidate spell's SpellFamilyFlags public ProcFlags ProcFlags { get; set; } // if nonzero - owerwrite procFlags field for given Spell.dbc entry, bitmask for matching proc condition, see enum ProcFlags public ProcFlagsSpellType SpellTypeMask { get; set; } // if nonzero - bitmask for matching proc condition based on candidate spell's damage/heal effects, see enum ProcFlagsSpellType public ProcFlagsSpellPhase SpellPhaseMask { get; set; } // if nonzero - bitmask for matching phase of a spellcast on which proc occurs, see enum ProcFlagsSpellPhase diff --git a/Scripts/Northrend/DraktharonKeep/BossTrollgore.cs b/Scripts/Northrend/DraktharonKeep/BossTrollgore.cs index 737d8ef36..4e8429d49 100644 --- a/Scripts/Northrend/DraktharonKeep/BossTrollgore.cs +++ b/Scripts/Northrend/DraktharonKeep/BossTrollgore.cs @@ -269,7 +269,7 @@ namespace Scripts.Northrend.DraktharonKeep.Trollgore { } - public override bool OnCheck(Player player, Unit target) + public override bool OnCheck(Player source, Unit target) { if (!target) return false; diff --git a/Scripts/World/Achievements.cs b/Scripts/World/Achievements.cs index db101509c..a22db8de5 100644 --- a/Scripts/World/Achievements.cs +++ b/Scripts/World/Achievements.cs @@ -37,6 +37,9 @@ namespace Scripts.World public const uint AuraPerfumeForever = 70235; public const uint AuraPerfumeEnchantress = 70234; public const uint AuraPerfumeVictory = 70233; + + //BgSA Artillery + public const uint AntiPersonnalCannon = 27894; } [Script] @@ -144,7 +147,7 @@ namespace Scripts.World Creature vehicle = source.GetVehicleCreatureBase(); if (vehicle) { - if (vehicle.GetEntry() == SACreatureIds.AntiPersonnalCannon) + if (vehicle.GetEntry() == AchievementConst.AntiPersonnalCannon) return true; } diff --git a/Scripts/World/GameObjects.cs b/Scripts/World/GameObjects.cs index 2685c2321..b1cd026ff 100644 --- a/Scripts/World/GameObjects.cs +++ b/Scripts/World/GameObjects.cs @@ -189,6 +189,40 @@ namespace Scripts.World //Toy Train Set public const uint SpellToyTrainPulse = 61551; + + //BrewfestMusic + public const uint EventBrewfestdwarf01 = 11810; // 1.35 Min + public const uint EventBrewfestdwarf02 = 11812; // 1.55 Min + public const uint EventBrewfestdwarf03 = 11813; // 0.23 Min + public const uint EventBrewfestgoblin01 = 11811; // 1.08 Min + public const uint EventBrewfestgoblin02 = 11814; // 1.33 Min + public const uint EventBrewfestgoblin03 = 11815; // 0.28 Min + + // These Are In Seconds + //Brewfestmusictime + public const uint EventBrewfestdwarf01Time = 95000; + public const uint EventBrewfestdwarf02Time = 155000; + public const uint EventBrewfestdwarf03Time = 23000; + public const uint EventBrewfestgoblin01Time = 68000; + public const uint EventBrewfestgoblin02Time = 93000; + public const uint EventBrewfestgoblin03Time = 28000; + + //Brewfestmusicareas + public const uint Silvermoon = 3430; // Horde + public const uint Undercity = 1497; + public const uint Orgrimmar1 = 1296; + public const uint Orgrimmar2 = 14; + public const uint Thunderbluff = 1638; + public const uint Ironforge1 = 809; // Alliance + public const uint Ironforge2 = 1; + public const uint Stormwind = 12; + public const uint Exodar = 3557; + public const uint Darnassus = 1657; + public const uint Shattrath = 3703; // General + + //Brewfestmusicevents + public const uint EventBmSelectMusic = 1; + public const uint EventBmStartMusic = 2; } [Script] @@ -971,4 +1005,89 @@ namespace Scripts.World return new go_toy_train_setAI(go); } } + + [Script] + class go_brewfest_music : GameObjectScript + { + public go_brewfest_music() : base("go_brewfest_music") { } + + class go_brewfest_musicAI : GameObjectAI + { + public go_brewfest_musicAI(GameObject go) : base(go) + { + _events.ScheduleEvent(GameobjectConst.EventBmSelectMusic, 1000); + _events.ScheduleEvent(GameobjectConst.EventBmStartMusic, 2000); + } + + public override void UpdateAI(uint diff) + { + _events.Update(diff); + + _events.ExecuteEvents(eventId => + { + switch (eventId) + { + case GameobjectConst.EventBmSelectMusic: + if (!Global.GameEventMgr.IsHolidayActive(HolidayIds.Brewfest)) // Check if Brewfest is active + break; + rnd = RandomHelper.URand(0, 2); // Select random music sample + _events.ScheduleEvent(GameobjectConst.EventBmSelectMusic, musicTime); // Select new song music after play time is over + break; + case GameobjectConst.EventBmStartMusic: + if (!Global.GameEventMgr.IsHolidayActive(HolidayIds.Brewfest)) // Check if Brewfest is active + break; + // Check if gob is correct area, play music, set time of music + if (go.GetAreaId() == GameobjectConst.Silvermoon || go.GetAreaId() == GameobjectConst.Undercity || go.GetAreaId() == GameobjectConst.Orgrimmar1 || go.GetAreaId() == GameobjectConst.Orgrimmar2 || go.GetAreaId() == GameobjectConst.Thunderbluff || go.GetAreaId() == GameobjectConst.Shattrath) + { + if (rnd == 0) + { + go.PlayDirectMusic(GameobjectConst.EventBrewfestgoblin01); + musicTime = GameobjectConst.EventBrewfestgoblin01Time; + } + else if (rnd == 1) + { + go.PlayDirectMusic(GameobjectConst.EventBrewfestgoblin02); + musicTime = GameobjectConst.EventBrewfestgoblin02Time; + } + else + { + go.PlayDirectMusic(GameobjectConst.EventBrewfestgoblin03); + musicTime = GameobjectConst.EventBrewfestgoblin03Time; + } + } + if (go.GetAreaId() == GameobjectConst.Ironforge1 || go.GetAreaId() == GameobjectConst.Ironforge2 || go.GetAreaId() == GameobjectConst.Stormwind || go.GetAreaId() == GameobjectConst.Exodar || go.GetAreaId() == GameobjectConst.Darnassus || go.GetAreaId() == GameobjectConst.Shattrath) + { + if (rnd == 0) + { + go.PlayDirectMusic(GameobjectConst.EventBrewfestdwarf01); + musicTime = GameobjectConst.EventBrewfestdwarf01Time; + } + else if (rnd == 1) + { + go.PlayDirectMusic(GameobjectConst.EventBrewfestdwarf02); + musicTime = GameobjectConst.EventBrewfestdwarf02Time; + } + else + { + go.PlayDirectMusic(GameobjectConst.EventBrewfestdwarf03); + musicTime = GameobjectConst.EventBrewfestdwarf03Time; + } + } + _events.ScheduleEvent(GameobjectConst.EventBmStartMusic, 5000); // Every 5 second's SMSG_PLAY_MUSIC packet (PlayDirectMusic) is pushed to the client + break; + default: + break; + } + }); + } + + uint rnd = 0; + uint musicTime = 1000; + } + + public override GameObjectAI GetAI(GameObject go) + { + return new go_brewfest_musicAI(go); + } + } } diff --git a/Scripts/World/NpcProfessions.cs b/Scripts/World/NpcProfessions.cs index 1992a9dee..a1bcc97f2 100644 --- a/Scripts/World/NpcProfessions.cs +++ b/Scripts/World/NpcProfessions.cs @@ -21,109 +21,5 @@ using Game.Scripting; namespace Scripts.World { - enum GossipOptionIds - { - Alchemy = 0, - Blacksmithing = 1, - Enchanting = 2, - Engineering = 3, - Herbalism = 4, - Inscription = 5, - Jewelcrafting = 6, - Leatherworking = 7, - Mining = 8, - Skinning = 9, - Tailoring = 10, - Multi = 11, - } - enum GossipMenuIds - { - Herbalism = 12188, - Mining = 12189, - Skinning = 12190, - Alchemy = 12191, - Blacksmithing = 12192, - Enchanting = 12193, - Engineering = 12195, - Inscription = 12196, - Jewelcrafting = 12197, - Leatherworking = 12198, - Tailoring = 12199, - } - - [Script] //start menu multi profession trainer - class npc_multi_profession_trainer : ScriptedAI - { - public npc_multi_profession_trainer(Creature creature) : base(creature) { } - - public override void sGossipSelect(Player player, uint menuId, uint gossipListId) - { - switch ((GossipOptionIds)gossipListId) - { - case GossipOptionIds.Alchemy: - case GossipOptionIds.Blacksmithing: - case GossipOptionIds.Enchanting: - case GossipOptionIds.Engineering: - case GossipOptionIds.Herbalism: - case GossipOptionIds.Inscription: - case GossipOptionIds.Jewelcrafting: - case GossipOptionIds.Leatherworking: - case GossipOptionIds.Mining: - case GossipOptionIds.Skinning: - case GossipOptionIds.Tailoring: - SendTrainerList(player, (GossipOptionIds)gossipListId); - break; - case GossipOptionIds.Multi: - { - switch ((GossipMenuIds)menuId) - { - case GossipMenuIds.Herbalism: - SendTrainerList(player, GossipOptionIds.Herbalism); - break; - case GossipMenuIds.Mining: - SendTrainerList(player, GossipOptionIds.Mining); - break; - case GossipMenuIds.Skinning: - SendTrainerList(player, GossipOptionIds.Skinning); - break; - case GossipMenuIds.Alchemy: - SendTrainerList(player, GossipOptionIds.Alchemy); - break; - case GossipMenuIds.Blacksmithing: - SendTrainerList(player, GossipOptionIds.Blacksmithing); - break; - case GossipMenuIds.Enchanting: - SendTrainerList(player, GossipOptionIds.Enchanting); - break; - case GossipMenuIds.Engineering: - SendTrainerList(player, GossipOptionIds.Engineering); - break; - case GossipMenuIds.Inscription: - SendTrainerList(player, GossipOptionIds.Inscription); - break; - case GossipMenuIds.Jewelcrafting: - SendTrainerList(player, GossipOptionIds.Jewelcrafting); - break; - case GossipMenuIds.Leatherworking: - SendTrainerList(player, GossipOptionIds.Leatherworking); - break; - case GossipMenuIds.Tailoring: - SendTrainerList(player, GossipOptionIds.Tailoring); - break; - default: - break; - } - } - break; - default: - break; - } - } - - void SendTrainerList(Player player, GossipOptionIds Index) - { - player.GetSession().SendTrainerList(me.GetGUID(), (uint)Index + 1); - } - } } \ No newline at end of file diff --git a/WorldServer/WorldServer.conf.dist b/WorldServer/WorldServer.conf.dist index a8974465c..6d36048be 100644 --- a/WorldServer/WorldServer.conf.dist +++ b/WorldServer/WorldServer.conf.dist @@ -1635,6 +1635,14 @@ ListenRange.Yell = 300 Creature.MovingStopTimeForPlayer = 180000 +# MonsterSight +# Description: The maximum distance in yards that a "monster" creature can see +# regardless of level difference (through CreatureAI::IsVisible). +# Increases CONFIG_SIGHT_MONSTER to 50 yards. Used to be 20 yards. +# Default: 50.000000 + +MonsterSight = 50.000000 + # ###################################################################################################