/* * 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 Game.DataStorage; using Game.Entities; using Game.Network.Packets; using System; using System.Collections.Generic; using System.Diagnostics.Contracts; namespace Game.BattleGrounds.Zones { class BgArathiBasin : Battleground { public BgArathiBasin() { m_IsInformedNearVictory = false; m_BuffChange = true; for (byte i = 0; i < BattlegroundNodes.DynamicNodesCount; ++i) { m_Nodes[i] = 0; m_prevNodes[i] = 0; m_NodeTimers[i] = 0; m_BannerTimers[i].timer = 0; m_BannerTimers[i].type = 0; m_BannerTimers[i].teamIndex = 0; } for (byte i = 0; i < SharedConst.BGTeamsCount; ++i) { m_lastTick[i] = 0; m_HonorScoreTics[i] = 0; m_ReputationScoreTics[i] = 0; m_TeamScores500Disadvantage[i] = false; } m_HonorTics = 0; m_ReputationTics = 0; StartMessageIds[BattlegroundConst.EventIdFirst] = CypherStrings.BgAbStartTwoMinutes; StartMessageIds[BattlegroundConst.EventIdSecond] = CypherStrings.BgAbStartOneMinute; StartMessageIds[BattlegroundConst.EventIdThird] = CypherStrings.BgAbStartHalfMinute; StartMessageIds[BattlegroundConst.EventIdFourth] = CypherStrings.BgAbHasBegun; } public override void PostUpdateImpl(uint diff) { if (GetStatus() == BattlegroundStatus.InProgress) { int[] team_points = { 0, 0 }; for (byte node = 0; node < BattlegroundNodes.DynamicNodesCount; ++node) { // 3 sec delay to spawn new banner instead previous despawned one if (m_BannerTimers[node].timer != 0) { if (m_BannerTimers[node].timer > diff) m_BannerTimers[node].timer -= diff; else { m_BannerTimers[node].timer = 0; _CreateBanner(node, (ABNodeStatus)m_BannerTimers[node].type, m_BannerTimers[node].teamIndex, false); } } // 1-minute to occupy a node from contested state if (m_NodeTimers[node] != 0) { if (m_NodeTimers[node] > diff) m_NodeTimers[node] -= diff; else { m_NodeTimers[node] = 0; // Change from contested to occupied ! int teamIndex = (int)m_Nodes[node] - 1; m_prevNodes[node] = m_Nodes[node]; m_Nodes[node] += 2; // burn current contested banner _DelBanner(node, ABNodeStatus.Contested, (byte)teamIndex); // create new occupied banner _CreateBanner(node, ABNodeStatus.Occupied, teamIndex, true); _SendNodeUpdate(node); _NodeOccupied(node, (teamIndex == 0) ? Team.Alliance : Team.Horde); // Message to chatlog if (teamIndex == 0) { // FIXME: team and node names not localized SendMessage2ToAll(CypherStrings.BgAbNodeTaken, ChatMsg.BgSystemAlliance, null, CypherStrings.BgAbAlly, _GetNodeNameId(node)); PlaySoundToAll(SoundCapturedAlliance); } else { // FIXME: team and node names not localized SendMessage2ToAll(CypherStrings.BgAbNodeTaken, ChatMsg.BgSystemHorde, null, CypherStrings.BgAbHorde, _GetNodeNameId(node)); PlaySoundToAll(SoundCapturedHorde); } } } for (int team = 0; team < SharedConst.BGTeamsCount; ++team) if (m_Nodes[node] == team + ABNodeStatus.Occupied) ++team_points[team]; } // Accumulate points for (int team = 0; team < SharedConst.BGTeamsCount; ++team) { int points = team_points[team]; if (points == 0) continue; m_lastTick[team] += diff; if (m_lastTick[team] > TickIntervals[points]) { m_lastTick[team] -= TickIntervals[points]; m_TeamScores[team] += TickPoints[points]; m_HonorScoreTics[team] += TickPoints[points]; m_ReputationScoreTics[team] += TickPoints[points]; if (m_ReputationScoreTics[team] >= m_ReputationTics) { if (team == TeamId.Alliance) RewardReputationToTeam(509, 10, Team.Alliance); else RewardReputationToTeam(510, 10, Team.Horde); m_ReputationScoreTics[team] -= m_ReputationTics; } if (m_HonorScoreTics[team] >= m_HonorTics) { RewardHonorToTeam(GetBonusHonorFromKill(1), (team == TeamId.Alliance) ? Team.Alliance : Team.Horde); m_HonorScoreTics[team] -= m_HonorTics; } if (!m_IsInformedNearVictory && m_TeamScores[team] > WarningNearVictoryScore) { if (team == TeamId.Alliance) SendMessageToAll(CypherStrings.BgAbANearVictory, ChatMsg.BgSystemNeutral); else SendMessageToAll(CypherStrings.BgAbHNearVictory, ChatMsg.BgSystemNeutral); PlaySoundToAll(SoundNearVictory); m_IsInformedNearVictory = true; } if (m_TeamScores[team] > MaxTeamScore) m_TeamScores[team] = MaxTeamScore; if (team == TeamId.Alliance) UpdateWorldState(ABWorldStates.ResourcesAlly, m_TeamScores[team]); else if (team == TeamId.Horde) 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; if (m_TeamScores[team] > m_TeamScores[otherTeam] + 500) m_TeamScores500Disadvantage[otherTeam] = true; } } // Test win condition if (m_TeamScores[TeamId.Alliance] >= MaxTeamScore) EndBattleground(Team.Alliance); else if (m_TeamScores[TeamId.Horde] >= MaxTeamScore) EndBattleground(Team.Horde); } } public override void StartingEventCloseDoors() { // despawn banners, auras and buffs for (int obj = ABObjectTypes.BannerNeutral; obj < BattlegroundNodes.DynamicNodesCount * 8; ++obj) SpawnBGObject(obj, BattlegroundConst.RespawnOneDay); for (int i = 0; i < BattlegroundNodes.DynamicNodesCount * 3; ++i) SpawnBGObject(ABObjectTypes.SpeedbuffStables + i, BattlegroundConst.RespawnOneDay); // Starting doors 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); _NodeOccupied(BattlegroundNodes.SpiritHorde, Team.Horde); } public override void StartingEventOpenDoors() { // spawn neutral banners 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(ABObjectTypes.SpeedbuffStables + buff + i * 3, BattlegroundConst.RespawnImmediately); } DoorOpen(ABObjectTypes.GateA); DoorOpen(ABObjectTypes.GateH); // Achievement: Let's Get This Done StartCriteriaTimer(CriteriaTimedTypes.Event, EventStartBattle); } public override void AddPlayer(Player player) { base.AddPlayer(player); PlayerScores[player.GetGUID()] = new BattlegroundABScore(player.GetGUID(), player.GetBGTeam()); } public override void RemovePlayer(Player Player, ObjectGuid guid, Team team) { } public override void HandleAreaTrigger(Player player, uint trigger, bool entered) { switch (trigger) { case 6635: // Horde Start case 6634: // Alliance Start if (GetStatus() == BattlegroundStatus.WaitJoin && !entered) TeleportPlayerToExploitLocation(player); break; case 3948: // Arathi Basin Alliance Exit. if (player.GetTeam() != Team.Alliance) player.GetSession().SendNotification("Only The Alliance can use that portal"); else player.LeaveBattleground(); break; case 3949: // Arathi Basin Horde Exit. if (player.GetTeam() != Team.Horde) player.GetSession().SendNotification("Only The Horde can use that portal"); else player.LeaveBattleground(); break; case 3866: // Stables case 3869: // Gold Mine case 3867: // Farm case 3868: // Lumber Mill case 3870: // Black Smith case 4020: // Unk1 case 4021: // Unk2 case 4674: // Unk3 //break; default: base.HandleAreaTrigger(player, trigger, entered); break; } } void _CreateBanner(byte node, ABNodeStatus type, int teamIndex, bool delay) { // Just put it into the queue if (delay) { m_BannerTimers[node].timer = 2000; m_BannerTimers[node].type = (byte)type; m_BannerTimers[node].teamIndex = (byte)teamIndex; return; } int obj = node * 8 + (byte)type + teamIndex; SpawnBGObject(obj, BattlegroundConst.RespawnImmediately); // handle aura with banner if (type == 0) return; obj = node * 8 + ((type == ABNodeStatus.Occupied) ? (5 + teamIndex) : 7); SpawnBGObject(obj, BattlegroundConst.RespawnImmediately); } void _DelBanner(byte node, ABNodeStatus type, byte teamIndex) { int obj = node * 8 + (byte)type + teamIndex; SpawnBGObject(obj, BattlegroundConst.RespawnOneDay); // handle aura with banner if (type == 0) return; obj = node * 8 + ((type == ABNodeStatus.Occupied) ? (5 + teamIndex) : 7); SpawnBGObject(obj, BattlegroundConst.RespawnOneDay); } CypherStrings _GetNodeNameId(byte node) { switch (node) { case BattlegroundNodes.NodeStables: return CypherStrings.BgAbNodeStables; case BattlegroundNodes.NodeBlacksmith: return CypherStrings.BgAbNodeBlacksmith; case BattlegroundNodes.NodeFarm: return CypherStrings.BgAbNodeFarm; case BattlegroundNodes.NodeLumberMill: return CypherStrings.BgAbNodeLumberMill; case BattlegroundNodes.NodeGoldMine: return CypherStrings.BgAbNodeGoldMine; default: Contract.Assert(false); break; } return 0; } public override void FillInitialWorldStates(InitWorldStates packet) { byte[] plusArray = { 0, 2, 3, 0, 1 }; // Node icons for (byte node = 0; node < BattlegroundNodes.DynamicNodesCount; ++node) packet.AddState(NodeIcons[node], (m_Nodes[node] == 0)); // Node occupied states for (byte node = 0; node < BattlegroundNodes.DynamicNodesCount; ++node) for (byte i = 1; i < BattlegroundNodes.DynamicNodesCount; ++i) packet.AddState(NodeStates[node] + plusArray[i], ((int)m_Nodes[node] == i)); // How many bases each team owns byte ally = 0, horde = 0; for (byte node = 0; node < BattlegroundNodes.DynamicNodesCount; ++node) if (m_Nodes[node] == ABNodeStatus.AllyOccupied) ++ally; else if (m_Nodes[node] == ABNodeStatus.HordeOccupied) ++horde; packet.AddState(ABWorldStates.OccupiedBasesAlly, ally); packet.AddState(ABWorldStates.OccupiedBasesHorde, horde); // Team scores 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); } void _SendNodeUpdate(byte node) { // Send node owner state update to refresh map icons on client byte[] plusArray = { 0, 2, 3, 0, 1 }; if (m_prevNodes[node] != 0) UpdateWorldState(NodeStates[node] + plusArray[(int)m_prevNodes[node]], 0); else UpdateWorldState(NodeIcons[node], 0); UpdateWorldState(NodeStates[node] + plusArray[(byte)m_Nodes[node]], 1); // How many bases each team owns byte ally = 0, horde = 0; for (byte i = 0; i < BattlegroundNodes.DynamicNodesCount; ++i) if (m_Nodes[i] == ABNodeStatus.AllyOccupied) ++ally; else if (m_Nodes[i] == ABNodeStatus.HordeOccupied) ++horde; UpdateWorldState(ABWorldStates.OccupiedBasesAlly, ally); UpdateWorldState(ABWorldStates.OccupiedBasesHorde, horde); } void _NodeOccupied(byte node, Team team) { if (!AddSpiritGuide(node, SpiritGuidePos[node], GetTeamIndexByTeamId(team))) Log.outError(LogFilter.Battleground, "Failed to spawn spirit guide! point: {0}, team: {1}, ", node, team); if (node >= BattlegroundNodes.DynamicNodesCount)//only dynamic nodes, no start points return; byte capturedNodes = 0; for (byte i = 0; i < BattlegroundNodes.DynamicNodesCount; ++i) if (m_Nodes[i] == ABNodeStatus.Occupied + GetTeamIndexByTeamId(team) && m_NodeTimers[i] == 0) ++capturedNodes; if (capturedNodes >= 5) CastSpellOnTeam(BattlegroundConst.AbQuestReward5Bases, team); if (capturedNodes >= 4) CastSpellOnTeam(BattlegroundConst.AbQuestReward4Bases, team); 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)); //add bonus honor aura trigger creature when node is accupied //cast bonus aura (+50% honor in 25yards) //aura should only apply to players who have accupied the node, set correct faction for trigger if (trigger) { trigger.SetFaction(team == Team.Alliance ? 84u : 83u); trigger.CastSpell(trigger, BattlegroundConst.SpellHonorableDefender25y, false); } } void _NodeDeOccupied(byte node) { if (node >= BattlegroundNodes.DynamicNodesCount) return; //remove bonus honor aura trigger creature when node is lost if (node < BattlegroundNodes.DynamicNodesCount)//only dynamic nodes, no start points DelCreature(node + 7);//null checks are in DelCreature! 0-6 spirit guides RelocateDeadPlayers(BgCreatures[node]); DelCreature(node); // buff object isn't despawned } //Invoked if a player used a banner as a gameobject public override void EventPlayerClickedOnFlag(Player source, GameObject target_obj) { if (GetStatus() != BattlegroundStatus.InProgress) return; byte node = BattlegroundNodes.NodeStables; GameObject obj = GetBgMap().GetGameObject(BgObjects[node * 8 + 7]); while ((node < BattlegroundNodes.DynamicNodesCount) && ((!obj) || (!source.IsWithinDistInMap(obj, 10)))) { ++node; obj = GetBgMap().GetGameObject(BgObjects[node * 8 + ABObjectTypes.AuraContested]); } if (node == BattlegroundNodes.DynamicNodesCount) { // this means our player isn't close to any of banners - maybe cheater ?? return; } int teamIndex = GetTeamIndexByTeamId(source.GetTeam()); // Check if player really could use this banner, not cheated if (!(m_Nodes[node] == 0 || teamIndex == (int)m_Nodes[node] % 2)) return; source.RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.EnterPvpCombat); uint sound = 0; // If node is neutral, change to contested if (m_Nodes[node] == ABNodeStatus.Neutral) { UpdatePlayerScore(source, ScoreType.BasesAssaulted, 1); m_prevNodes[node] = m_Nodes[node]; m_Nodes[node] = (ABNodeStatus)(teamIndex + 1); // burn current neutral banner _DelBanner(node, ABNodeStatus.Neutral, 0); // create new contested banner _CreateBanner(node, ABNodeStatus.Contested, (byte)teamIndex, true); _SendNodeUpdate(node); m_NodeTimers[node] = FlagCapturingTime; // FIXME: team and node names not localized if (teamIndex == 0) SendMessage2ToAll(CypherStrings.BgAbNodeClaimed, ChatMsg.BgSystemAlliance, source, _GetNodeNameId(node), CypherStrings.BgAbAlly); else SendMessage2ToAll(CypherStrings.BgAbNodeClaimed, ChatMsg.BgSystemHorde, source, _GetNodeNameId(node), CypherStrings.BgAbHorde); sound = SoundClaimed; } // If node is contested 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] < ABNodeStatus.Occupied) { UpdatePlayerScore(source, ScoreType.BasesAssaulted, 1); m_prevNodes[node] = m_Nodes[node]; m_Nodes[node] = (ABNodeStatus.Contested + (int)teamIndex); // burn current contested banner _DelBanner(node, ABNodeStatus.Contested, (byte)teamIndex); // create new contested banner _CreateBanner(node, ABNodeStatus.Contested, (byte)teamIndex, true); _SendNodeUpdate(node); m_NodeTimers[node] = FlagCapturingTime; // FIXME: node names not localized if (teamIndex == TeamId.Alliance) SendMessage2ToAll(CypherStrings.BgAbNodeAssaulted, ChatMsg.BgSystemAlliance, source, _GetNodeNameId(node)); else SendMessage2ToAll(CypherStrings.BgAbNodeAssaulted, ChatMsg.BgSystemHorde, source, _GetNodeNameId(node)); } // If contested, change back to occupied else { UpdatePlayerScore(source, ScoreType.BasesDefended, 1); m_prevNodes[node] = m_Nodes[node]; m_Nodes[node] = (ABNodeStatus.Occupied + (int)teamIndex); // burn current contested banner _DelBanner(node, ABNodeStatus.Contested, (byte)teamIndex); // create new occupied banner _CreateBanner(node, ABNodeStatus.Occupied, (byte)teamIndex, true); _SendNodeUpdate(node); m_NodeTimers[node] = 0; _NodeOccupied(node, (teamIndex == TeamId.Alliance) ? Team.Alliance : Team.Horde); // FIXME: node names not localized if (teamIndex == TeamId.Alliance) SendMessage2ToAll(CypherStrings.BgAbNodeDefended, ChatMsg.BgSystemAlliance, source, _GetNodeNameId(node)); else SendMessage2ToAll(CypherStrings.BgAbNodeDefended, ChatMsg.BgSystemHorde, source, _GetNodeNameId(node)); } sound = (teamIndex == TeamId.Alliance) ? SoundAssaultedAlliance : SoundAssaultedHorde; } // If node is occupied, change to enemy-contested else { UpdatePlayerScore(source, ScoreType.BasesAssaulted, 1); m_prevNodes[node] = m_Nodes[node]; m_Nodes[node] = (ABNodeStatus.Contested + (int)teamIndex); // burn current occupied banner _DelBanner(node, ABNodeStatus.Occupied, (byte)teamIndex); // create new contested banner _CreateBanner(node, ABNodeStatus.Contested, (byte)teamIndex, true); _SendNodeUpdate(node); _NodeDeOccupied(node); m_NodeTimers[node] = FlagCapturingTime; // FIXME: node names not localized if (teamIndex == TeamId.Alliance) SendMessage2ToAll(CypherStrings.BgAbNodeAssaulted, ChatMsg.BgSystemAlliance, source, _GetNodeNameId(node)); else SendMessage2ToAll(CypherStrings.BgAbNodeAssaulted, ChatMsg.BgSystemHorde, source, _GetNodeNameId(node)); sound = (teamIndex == TeamId.Alliance) ? SoundAssaultedAlliance : SoundAssaultedHorde; } // If node is occupied again, send "X has taken the Y" msg. if (m_Nodes[node] >= ABNodeStatus.Occupied) { // FIXME: team and node names not localized if (teamIndex == TeamId.Alliance) SendMessage2ToAll(CypherStrings.BgAbNodeTaken, ChatMsg.BgSystemAlliance, null, CypherStrings.BgAbAlly, _GetNodeNameId(node)); else SendMessage2ToAll(CypherStrings.BgAbNodeTaken, ChatMsg.BgSystemHorde, null, CypherStrings.BgAbHorde, _GetNodeNameId(node)); } PlaySoundToAll(sound); } public override Team GetPrematureWinner() { // How many bases each team owns byte ally = 0, horde = 0; for (byte i = 0; i < BattlegroundNodes.DynamicNodesCount; ++i) if (m_Nodes[i] == ABNodeStatus.AllyOccupied) ++ally; else if (m_Nodes[i] == ABNodeStatus.HordeOccupied) ++horde; if (ally > horde) return Team.Alliance; else if (horde > ally) return Team.Horde; // If the values are equal, fall back to the original result (based on number of players on each team) return base.GetPrematureWinner(); } public override bool SetupBattleground() { bool result = true; for (int i = 0; i < BattlegroundNodes.DynamicNodesCount; ++i) { 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!"); return false; } } 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!"); return false; } //buffs for (int i = 0; i < BattlegroundNodes.DynamicNodesCount; ++i) { 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!"); return false; } } return true; } public override void Reset() { //call parent's class reset base.Reset(); for (var i = 0; i < SharedConst.BGTeamsCount; ++i) { m_TeamScores[i] = 0; m_lastTick[i] = 0; m_HonorScoreTics[i] = 0; m_ReputationScoreTics[i] = 0; m_TeamScores500Disadvantage[i] = false; } m_IsInformedNearVictory = false; bool isBGWeekend = Global.BattlegroundMgr.IsBGWeekend(GetTypeID()); m_HonorTics = (isBGWeekend) ? ABBGWeekendHonorTicks : NotABBGWeekendHonorTicks; m_ReputationTics = (isBGWeekend) ? ABBGWeekendReputationTicks : NotABBGWeekendReputationTicks; for (byte i = 0; i < BattlegroundNodes.DynamicNodesCount; ++i) { m_Nodes[i] = 0; m_prevNodes[i] = 0; m_NodeTimers[i] = 0; m_BannerTimers[i].timer = 0; } } public override void EndBattleground(Team winner) { // Win reward if (winner == Team.Alliance) RewardHonorToTeam(GetBonusHonorFromKill(1), Team.Alliance); if (winner == Team.Horde) RewardHonorToTeam(GetBonusHonorFromKill(1), Team.Horde); // Complete map_end rewards (even if no team wins) RewardHonorToTeam(GetBonusHonorFromKill(1), Team.Horde); RewardHonorToTeam(GetBonusHonorFromKill(1), Team.Alliance); base.EndBattleground(winner); } public override WorldSafeLocsRecord GetClosestGraveYard(Player player) { int teamIndex = GetTeamIndexByTeamId(player.GetTeam()); // Is there any occupied node for this team? List nodes = new List(); for (byte i = 0; i < BattlegroundNodes.DynamicNodesCount; ++i) if (m_Nodes[i] == ABNodeStatus.Occupied + (int)teamIndex) nodes.Add(i); WorldSafeLocsRecord good_entry = null; // If so, select the closest node to place ghost on if (!nodes.Empty()) { float plr_x = player.GetPositionX(); float plr_y = player.GetPositionY(); float mindist = 999999.0f; for (byte i = 0; i < nodes.Count; ++i) { WorldSafeLocsRecord entry = CliDB.WorldSafeLocsStorage.LookupByKey(GraveyardIds[nodes[i]]); if (entry == null) continue; float dist = (entry.Loc.X - plr_x) * (entry.Loc.X - plr_x) + (entry.Loc.Y - plr_y) * (entry.Loc.Y - plr_y); if (mindist > dist) { mindist = dist; good_entry = entry; } } nodes.Clear(); } // If not, place ghost on starting location if (good_entry == null) good_entry = CliDB.WorldSafeLocsStorage.LookupByKey(GraveyardIds[teamIndex + 5]); return good_entry; } public override WorldSafeLocsRecord GetExploitTeleportLocation(Team team) { return CliDB.WorldSafeLocsStorage.LookupByKey(team == Team.Alliance ? ExploitTeleportLocationAlliance : ExploitTeleportLocationHorde); } public override bool UpdatePlayerScore(Player player, ScoreType type, uint value, bool doAddHonor = true) { if (!base.UpdatePlayerScore(player, type, value, doAddHonor)) return false; switch (type) { case ScoreType.BasesAssaulted: player.UpdateCriteria(CriteriaTypes.BgObjectiveCapture, (uint)ABObjectives.AssaultBase); break; case ScoreType.BasesDefended: player.UpdateCriteria(CriteriaTypes.BgObjectiveCapture, (uint)ABObjectives.DefendBase); break; default: break; } return true; } public override bool IsAllNodesControlledByTeam(Team team) { uint count = 0; for (int i = 0; i < BattlegroundNodes.DynamicNodesCount; ++i) if ((team == Team.Alliance && m_Nodes[i] == ABNodeStatus.AllyOccupied) || (team == Team.Horde && m_Nodes[i] == ABNodeStatus.HordeOccupied)) ++count; return count == BattlegroundNodes.DynamicNodesCount; } public override bool CheckAchievementCriteriaMeet(uint criteriaId, Player player, Unit target, uint miscvalue) { switch ((BattlegroundCriteriaId)criteriaId) { case BattlegroundCriteriaId.ResilientVictory: return m_TeamScores500Disadvantage[GetTeamIndexByTeamId(player.GetTeam())]; } return base.CheckAchievementCriteriaMeet(criteriaId, player, target, miscvalue); } /// /// Nodes info: /// 0: neutral /// 1: ally contested /// 2: horde contested /// 3: ally occupied /// 4: horde occupied /// 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]; uint[] m_HonorScoreTics = new uint[SharedConst.BGTeamsCount]; uint[] m_ReputationScoreTics = new uint[SharedConst.BGTeamsCount]; bool m_IsInformedNearVictory; uint m_HonorTics; uint m_ReputationTics; // need for achievements bool[] m_TeamScores500Disadvantage = new bool[SharedConst.BGTeamsCount]; //Const public const uint NotABBGWeekendHonorTicks = 260; public const uint ABBGWeekendHonorTicks = 160; public const uint NotABBGWeekendReputationTicks = 160; public const uint ABBGWeekendReputationTicks = 120; public const int EventStartBattle = 9158;// Achievement: Let's Get This Done public const int SoundClaimed = 8192; public const int SoundCapturedAlliance = 8173; public const int SoundCapturedHorde = 8213; public const uint SoundAssaultedAlliance = 8212; public const uint SoundAssaultedHorde = 8174; public const int SoundNearVictory = 8456; public const int FlagCapturingTime = 60000; public const int WarningNearVictoryScore = 1400; public const int MaxTeamScore = 1600; public const int ExploitTeleportLocationAlliance = 3705; public const int ExploitTeleportLocationHorde = 3706; public static Position[] NodePositions = { new Position(1166.785f, 1200.132f, -56.70859f, 0.9075713f), // stables new Position(977.0156f, 1046.616f, -44.80923f, -2.600541f), // blacksmith new Position(806.1821f, 874.2723f, -55.99371f, -2.303835f), // farm new Position(856.1419f, 1148.902f, 11.18469f, -2.303835f), // lumber mill new Position(1146.923f, 848.1782f, -110.917f, -0.7330382f) // gold mine }; // x, y, z, o, rot0, rot1, rot2, rot3 public static float[][] DoorPositions = { new float[] {1284.597f, 1281.167f, -15.97792f, 0.7068594f, 0.012957f, -0.060288f, 0.344959f, 0.93659f }, new float[] {708.0903f, 708.4479f, -17.8342f, -2.391099f, 0.050291f, 0.015127f, 0.929217f, -0.365784f} }; // Tick intervals and given points: case 0, 1, 2, 3, 4, 5 captured nodes public static uint[] TickIntervals = { 0, 12000, 9000, 6000, 3000, 1000 }; public static uint[] TickPoints = { 0, 10, 10, 10, 10, 30 }; // WorldSafeLocs ids for 5 nodes, and for ally, and horde starting location public static uint[] GraveyardIds = { 895, 894, 893, 897, 896, 898, 899 }; // x, y, z, o public static float[][] BuffPositions = { new float[] {1185.71f, 1185.24f, -56.36f, 2.56f }, // stables new float[] {990.75f, 1008.18f, -42.60f, 2.43f }, // blacksmith new float[] {817.66f, 843.34f, -56.54f, 3.01f }, // farm new float[] {807.46f, 1189.16f, 11.92f, 5.44f }, // lumber mill new float[] {1146.62f, 816.94f, -98.49f, 6.14f } // gold mine }; public static Position[] SpiritGuidePos = { new Position(1200.03f, 1171.09f, -56.47f, 5.15f), // stables new Position(1017.43f, 960.61f, -42.95f, 4.88f), // blacksmith new Position(833.00f, 793.00f, -57.25f, 5.27f), // farm new Position(775.17f, 1206.40f, 15.79f, 1.90f), // lumber mill new Position(1207.48f, 787.00f, -83.36f, 5.51f), // gold mine new Position(1354.05f, 1275.48f, -11.30f, 4.77f), // alliance starting base new Position(714.61f, 646.15f, -10.87f, 4.34f) // horde starting base }; public static uint[] NodeStates = { 1767, 1782, 1772, 1792, 1787 }; public static uint[] NodeIcons = { 1842, 1846, 1845, 1844, 1843 }; } class BattlegroundABScore : BattlegroundScore { public BattlegroundABScore(ObjectGuid playerGuid, Team team) : base(playerGuid, team) { BasesAssaulted = 0; BasesDefended = 0; } public override void UpdateScore(ScoreType type, uint value) { switch (type) { case ScoreType.BasesAssaulted: BasesAssaulted += value; break; case ScoreType.BasesDefended: BasesDefended += value; break; default: base.UpdateScore(type, value); break; } } public override void BuildPvPLogPlayerDataPacket(out PVPLogData.PlayerData playerData) { base.BuildPvPLogPlayerDataPacket(out playerData); playerData.Stats.Add(BasesAssaulted); playerData.Stats.Add(BasesDefended); } public override uint GetAttr1() { return BasesAssaulted; } public override uint GetAttr2() { return BasesDefended; } uint BasesAssaulted; uint BasesDefended; } struct BannerTimer { public uint timer; public byte type; public byte teamIndex; } #region Consts struct ABWorldStates { public const uint OccupiedBasesHorde = 1778; public const uint OccupiedBasesAlly = 1779; public const uint ResourcesAlly = 1776; public const uint ResourcesHorde = 1777; public const uint ResourcesMax = 1780; public const uint ResourcesWarning = 1955; /* public const uint StableIcon = 1842; //Stable Map Icon (None) public const uint StableStateAlience = 1767; //Stable Map State (Alience) public const uint StableStateHorde = 1768; //Stable Map State (Horde) public const uint StableStateConAli = 1769; //Stable Map State (Con Alience) public const uint StableStateConHor = 1770; //Stable Map State (Con Horde) public const uint FarmIcon = 1845; //Farm Map Icon (None) public const uint FarmStateAlience = 1772; //Farm State (Alience) public const uint FarmStateHorde = 1773; //Farm State (Horde) public const uint FarmStateConAli = 1774; //Farm State (Con Alience) public const uint FarmStateConHor = 1775; //Farm State (Con Horde) public const uint BlacksmithIcon = 1846; //Blacksmith Map Icon (None) public const uint BlacksmithStateAlience = 1782; //Blacksmith Map State (Alience) public const uint BlacksmithStateHorde = 1783; //Blacksmith Map State (Horde) public const uint BlacksmithStateConAli = 1784; //Blacksmith Map State (Con Alience) public const uint BlacksmithStateConHor = 1785; //Blacksmith Map State (Con Horde) public const uint LumbermillIcon = 1844; //Lumber Mill Map Icon (None) public const uint LumbermillStateAlience = 1792; //Lumber Mill Map State (Alience) public const uint LumbermillStateHorde = 1793; //Lumber Mill Map State (Horde) public const uint LumbermillStateConAli = 1794; //Lumber Mill Map State (Con Alience) public const uint LumbermillStateConHor = 1795; //Lumber Mill Map State (Con Horde) public const uint GoldmineIcon = 1843; //Gold Mine Map Icon (None) public const uint GoldmineStateAlience = 1787; //Gold Mine Map State (Alience) public const uint GoldmineStateHorde = 1788; //Gold Mine Map State (Horde) public const uint GoldmineStateConAli = 1789; //Gold Mine Map State (Con Alience public const uint GoldmineStateConHor = 1790; //Gold Mine Map State (Con Horde) */ } // Note: code uses that these IDs follow each other struct NodeObjectId { public const uint Banner0 = 180087; // Stables Banner public const uint Banner1 = 180088; // Blacksmith Banner public const uint Banner2 = 180089; // Farm Banner public const uint Banner3 = 180090; // Lumber Mill Banner public const uint Banner4 = 180091; // Gold Mine Banner } struct ABObjectTypes { // for all 5 node points 8*5=40 objects public const int BannerNeutral = 0; public const int BannerContA = 1; public const int BannerContH = 2; public const int BannerAlly = 3; public const int BannerHorde = 4; public const int AuraAlly = 5; public const int AuraHorde = 6; public const int AuraContested = 7; //Gates public const int GateA = 40; public const int GateH = 41; //Buffs public const int SpeedbuffStables = 42; public const int RegenbuffStables = 43; public const int BerserkbuffStables = 44; public const int SpeedbuffBlacksmith = 45; public const int RegenbuffBlacksmith = 46; public const int BerserkbuffBlacksmith = 47; public const int SpeedbuffFarm = 48; public const int RegenbuffFarm = 49; public const int BerserkbuffFarm = 50; public const int SpeedbuffLumberMill = 51; public const int RegenbuffLumberMill = 52; public const int BerserkbuffLumberMill = 53; public const int SpeedbuffGoldMine = 54; public const int RegenbuffGoldMine = 55; public const int BerserkbuffGoldMine = 56; public const int Max = 57; } // Object id templates from DB struct ABObjectIds { public const uint BannerA = 180058; public const uint BannerContA = 180059; public const uint BannerH = 180060; public const uint BannerContH = 180061; public const uint AuraA = 180100; public const uint AuraH = 180101; public const uint AuraC = 180102; public const uint GateA = 180255; public const uint GateH = 180256; } struct BattlegroundNodes { public const int NodeStables = 0; public const int NodeBlacksmith = 1; public const int NodeFarm = 2; public const int NodeLumberMill = 3; public const int NodeGoldMine = 4; public const int DynamicNodesCount = 5; // Dynamic Nodes That Can Be Captured public const int SpiritAliance = 5; public const int SpiritHorde = 6; public const int AllNodesCount = 7; // All Nodes (Dynamic And Static) } enum ABNodeStatus { Neutral = 0, Contested = 1, AllyContested = 1, HordeContested = 2, Occupied = 3, AllyOccupied = 3, HordeOccupied = 4 } enum ABObjectives { AssaultBase = 122, DefendBase = 123 } #endregion }