From 047a6babfce363e46962fed7d9810661e71f1e69 Mon Sep 17 00:00:00 2001 From: hondacrx Date: Mon, 29 Mar 2021 16:36:05 -0400 Subject: [PATCH] Core/Spells: Implement SPELL_EFFECT_TELEPORT_TO_RETURN_POINT Port From (https://github.com/TrinityCore/TrinityCore/commit/1c852af7f2c820e429eaf8389822e8c910f961a1) --- .../Constants/Spells/SpellAuraConst.cs | 2 +- Source/Framework/Constants/UnitConst.cs | 1 + .../Database/Databases/CharacterDatabase.cs | 11 +++ Source/Game/Entities/Player/Player.DB.cs | 72 +++++++++++++++++++ Source/Game/Entities/Player/Player.Fields.cs | 14 ++++ Source/Game/Entities/Player/Player.Spells.cs | 25 +++++++ Source/Game/Handlers/CharacterHandler.cs | 5 ++ Source/Game/Spells/Auras/AuraEffect.cs | 26 +++++-- Source/Game/Spells/SpellEffects.cs | 14 ++++ sql/base/characters_database.sql | 31 +++++++- ..._27_00_characters_aura_stored_location.sql | 14 ++++ 11 files changed, 208 insertions(+), 7 deletions(-) create mode 100644 sql/updates/characters/master/2021_03_27_00_characters_aura_stored_location.sql diff --git a/Source/Framework/Constants/Spells/SpellAuraConst.cs b/Source/Framework/Constants/Spells/SpellAuraConst.cs index 2fbbd2f0f..6b8afc7f4 100644 --- a/Source/Framework/Constants/Spells/SpellAuraConst.cs +++ b/Source/Framework/Constants/Spells/SpellAuraConst.cs @@ -92,7 +92,7 @@ namespace Framework.Constants ModStalked = 68, SchoolAbsorb = 69, PeriodicWeaponPercentDamage = 70, - StoreTeleportReturnPoint = 71, // NYI + StoreTeleportReturnPoint = 71, ModPowerCostSchoolPct = 72, ModPowerCostSchool = 73, ReflectSpellsSchool = 74, diff --git a/Source/Framework/Constants/UnitConst.cs b/Source/Framework/Constants/UnitConst.cs index 258a69054..ca0250af8 100644 --- a/Source/Framework/Constants/UnitConst.cs +++ b/Source/Framework/Constants/UnitConst.cs @@ -62,6 +62,7 @@ namespace Framework.Constants Ejectable = 0x20, // Ejectable UsableForced2 = 0x40, UsableForced3 = 0x100, + PassengerMirrorsAnims = 0x10000, // Passenger forced to repeat all vehicle animations KeepPet = 0x20000, UsableForced4 = 0x02000000, CanSwitch = 0x4000000, diff --git a/Source/Framework/Database/Databases/CharacterDatabase.cs b/Source/Framework/Database/Databases/CharacterDatabase.cs index 08b4d59df..7b129d9f3 100644 --- a/Source/Framework/Database/Databases/CharacterDatabase.cs +++ b/Source/Framework/Database/Databases/CharacterDatabase.cs @@ -793,6 +793,12 @@ namespace Framework.Database PrepareStatement(CharStatements.DEL_SCENARIO_INSTANCE_CRITERIA, "DELETE FROM instance_scenario_progress WHERE id = ? AND criteria = ?"); PrepareStatement(CharStatements.INS_SCENARIO_INSTANCE_CRITERIA, "INSERT INTO instance_scenario_progress (id, criteria, counter, date) VALUES (?, ?, ?, ?)"); PrepareStatement(CharStatements.DEL_SCENARIO_INSTANCE_CRITERIA_FOR_INSTANCE, "DELETE FROM instance_scenario_progress WHERE id = ?"); + + // Spell Location + PrepareStatement(CharStatements.SEL_CHARACTER_AURA_STORED_LOCATIONS, "SELECT Spell, MapId, PositionX, PositionY, PositionZ, Orientation FROM character_aura_stored_location WHERE Guid = ?"); + PrepareStatement(CharStatements.DEL_CHARACTER_AURA_STORED_LOCATIONS_BY_GUID, "DELETE FROM character_aura_stored_location WHERE Guid = ?"); + PrepareStatement(CharStatements.DEL_CHARACTER_AURA_STORED_LOCATION, "DELETE FROM character_aura_stored_location WHERE Guid = ? AND Spell = ?"); + PrepareStatement(CharStatements.INS_CHARACTER_AURA_STORED_LOCATION, "INSERT INTO character_aura_stored_location (Guid, Spell, MapId, PositionX, PositionY, PositionZ, Orientation) VALUES (?, ?, ?, ?, ?, ?, ?)"); } } @@ -1442,6 +1448,11 @@ namespace Framework.Database INS_SCENARIO_INSTANCE_CRITERIA, DEL_SCENARIO_INSTANCE_CRITERIA_FOR_INSTANCE, + SEL_CHARACTER_AURA_STORED_LOCATIONS, + DEL_CHARACTER_AURA_STORED_LOCATIONS_BY_GUID, + DEL_CHARACTER_AURA_STORED_LOCATION, + INS_CHARACTER_AURA_STORED_LOCATION, + MAX_CHARACTERDATABASE_STATEMENTS } } diff --git a/Source/Game/Entities/Player/Player.DB.cs b/Source/Game/Entities/Player/Player.DB.cs index 235b0c1f4..3f74d3ebc 100644 --- a/Source/Game/Entities/Player/Player.DB.cs +++ b/Source/Game/Entities/Player/Player.DB.cs @@ -1353,6 +1353,38 @@ namespace Game.Entities SetArenaTeamInfoField(slot, ArenaTeamInfoType.PersonalRating, personalRatingCache[slot]); } } + void _LoadStoredAuraTeleportLocations(SQLResult result) + { + // 0 1 2 3 4 5 + //QueryResult* result = CharacterDatabase.PQuery("SELECT Spell, MapId, PositionX, PositionY, PositionZ, Orientation FROM character_spell_location WHERE Guid = ?", GetGUIDLow()); + + m_storedAuraTeleportLocations.Clear(); + if (!result.IsEmpty()) + { + do + { + uint spellId = result.Read(0); + + if (!Global.SpellMgr.HasSpellInfo(spellId, Difficulty.None)) + { + Log.outError(LogFilter.Spells, $"Player._LoadStoredAuraTeleportLocations: Player {GetName()} ({GetGUID()}) spell (ID: {spellId}) does not exist"); + continue; + } + + WorldLocation location = new WorldLocation(result.Read(1), result.Read(2), result.Read(3), result.Read(4), result.Read(5)); + if (!GridDefines.IsValidMapCoord(location)) + { + Log.outError(LogFilter.Spells, $"Player._LoadStoredAuraTeleportLocations: Player {GetName()} ({GetGUID()}) spell (ID: {spellId}) has invalid position on map {location.GetMapId()}, {location}."); + continue; + } + + StoredAuraTeleportLocation storedLocation = m_storedAuraTeleportLocations[spellId]; + storedLocation.Loc = location; + storedLocation.CurrentState = StoredAuraTeleportLocation.State.Unchanged; + } + while (result.NextRow()); + } + } void _LoadGroup(SQLResult result) { if (!result.IsEmpty()) @@ -2188,6 +2220,38 @@ namespace Game.Entities m_mailsUpdated = false; } + void _SaveStoredAuraTeleportLocations(SQLTransaction trans) + { + foreach (var pair in m_storedAuraTeleportLocations.ToList()) + { + var storedLocation = pair.Value; + if (storedLocation.CurrentState == StoredAuraTeleportLocation.State.Deleted) + { + PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHARACTER_AURA_STORED_LOCATION); + stmt.AddValue(0, GetGUID().GetCounter()); + trans.Append(stmt); + m_storedAuraTeleportLocations.Remove(pair.Key); + continue; + } + + if (storedLocation.CurrentState == StoredAuraTeleportLocation.State.Changed) + { + PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHARACTER_AURA_STORED_LOCATION); + stmt.AddValue(0, GetGUID().GetCounter()); + trans.Append(stmt); + + stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_CHARACTER_AURA_STORED_LOCATION); + stmt.AddValue(0, GetGUID().GetCounter()); + stmt.AddValue(1, pair.Key); + stmt.AddValue(2, storedLocation.Loc.GetMapId()); + stmt.AddValue(3, storedLocation.Loc.GetPositionX()); + stmt.AddValue(4, storedLocation.Loc.GetPositionY()); + stmt.AddValue(5, storedLocation.Loc.GetPositionZ()); + stmt.AddValue(6, storedLocation.Loc.GetOrientation()); + trans.Append(stmt); + } + } + } void _SaveStats(SQLTransaction trans) { // check if stat saving is enabled and if char level is high enough @@ -3036,6 +3100,9 @@ namespace Game.Entities if (HasPlayerFlag(PlayerFlags.Ghost)) m_deathState = DeathState.Dead; + // Load spell locations - must be after loading auras + _LoadStoredAuraTeleportLocations(holder.GetResult(PlayerLoginQueryLoad.AuraStoredLocations)); + // after spell load, learn rewarded spell if need also _LoadQuestStatus(holder.GetResult(PlayerLoginQueryLoad.QuestStatus)); _LoadQuestStatusObjectives(holder.GetResult(PlayerLoginQueryLoad.QuestStatusObjectives)); @@ -3572,6 +3639,7 @@ namespace Game.Entities _SaveActions(characterTransaction); _SaveAuras(characterTransaction); _SaveSkills(characterTransaction); + _SaveStoredAuraTeleportLocations(characterTransaction); m_achievementSys.SaveToDB(characterTransaction); reputationMgr.SaveToDB(characterTransaction); m_questObjectiveCriteriaMgr.SaveToDB(characterTransaction); @@ -4096,6 +4164,10 @@ namespace Game.Entities stmt.AddValue(0, guid); trans.Append(stmt); + stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHARACTER_AURA_STORED_LOCATIONS_BY_GUID); + stmt.AddValue(0, guid); + trans.Append(stmt); + Corpse.DeleteFromDB(playerGuid, trans); Garrison.DeleteFromDB(guid, trans); diff --git a/Source/Game/Entities/Player/Player.Fields.cs b/Source/Game/Entities/Player/Player.Fields.cs index b78f32d38..e4309fe9a 100644 --- a/Source/Game/Entities/Player/Player.Fields.cs +++ b/Source/Game/Entities/Player/Player.Fields.cs @@ -120,6 +120,7 @@ namespace Game.Entities MultiMap m_overrideSpells = new(); public Spell m_spellModTakingSpell; uint m_oldpetspell; + Dictionary m_storedAuraTeleportLocations = new(); //Mail List m_mail = new(); @@ -611,4 +612,17 @@ namespace Game.Entities public ObjectGuid GroupGuid; public int UpdateSequenceNumber; } + + class StoredAuraTeleportLocation + { + public WorldLocation Loc; + public State CurrentState; + + public enum State + { + Unchanged, + Changed, + Deleted + } + } } diff --git a/Source/Game/Entities/Player/Player.Spells.cs b/Source/Game/Entities/Player/Player.Spells.cs index abfc88f25..38f30af1a 100644 --- a/Source/Game/Entities/Player/Player.Spells.cs +++ b/Source/Game/Entities/Player/Player.Spells.cs @@ -2288,6 +2288,31 @@ namespace Game.Entities return false; } + public void AddStoredAuraTeleportLocation(uint spellId) + { + StoredAuraTeleportLocation storedLocation = new(); + storedLocation.Loc = new WorldLocation(this); + storedLocation.CurrentState = StoredAuraTeleportLocation.State.Changed; + + m_storedAuraTeleportLocations[spellId] = storedLocation; + } + + public void RemoveStoredAuraTeleportLocation(uint spellId) + { + StoredAuraTeleportLocation storedLocation = m_storedAuraTeleportLocations.LookupByKey(spellId); + if (storedLocation != null) + storedLocation.CurrentState = StoredAuraTeleportLocation.State.Deleted; + } + + public WorldLocation GetStoredAuraTeleportLocation(uint spellId) + { + StoredAuraTeleportLocation auraLocation = m_storedAuraTeleportLocations.LookupByKey(spellId); + if (auraLocation != null) + return auraLocation.Loc; + + return null; + } + bool AddSpell(uint spellId, bool active, bool learning, bool dependent, bool disabled, bool loading = false, uint fromSkill = 0) { SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spellId, Difficulty.None); diff --git a/Source/Game/Handlers/CharacterHandler.cs b/Source/Game/Handlers/CharacterHandler.cs index 5eccc4bb7..4e17c0810 100644 --- a/Source/Game/Handlers/CharacterHandler.cs +++ b/Source/Game/Handlers/CharacterHandler.cs @@ -2570,6 +2570,10 @@ namespace Game stmt.AddValue(0, lowGuid); SetQuery(PlayerLoginQueryLoad.AuraEffects, stmt); + stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_AURA_STORED_LOCATIONS); + stmt.AddValue(0, lowGuid); + SetQuery(PlayerLoginQueryLoad.AuraStoredLocations, stmt); + stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_SPELL); stmt.AddValue(0, lowGuid); SetQuery(PlayerLoginQueryLoad.Spells, stmt); @@ -2818,6 +2822,7 @@ namespace Game BoundInstances, Auras, AuraEffects, + AuraStoredLocations, Spells, QuestStatus, QuestStatusObjectives, diff --git a/Source/Game/Spells/Auras/AuraEffect.cs b/Source/Game/Spells/Auras/AuraEffect.cs index 1a4c0aa42..e4c162d4a 100644 --- a/Source/Game/Spells/Auras/AuraEffect.cs +++ b/Source/Game/Spells/Auras/AuraEffect.cs @@ -139,7 +139,7 @@ namespace Game.Spells return totalTicks; } - + void ResetPeriodic(bool resetPeriodicTimer = false) { _ticksDone = 0; @@ -774,9 +774,9 @@ namespace Game.Spells public void SetPeriodicTimer(int periodicTimer) { _periodicTimer = periodicTimer; } void RecalculateAmount(AuraEffect triggeredBy = null) - { + { if (!CanBeRecalculated()) - return; + return; ChangeAmount(CalculateAmount(GetCaster()), false, false, triggeredBy); } @@ -3099,7 +3099,7 @@ namespace Game.Spells player.UpdateArmor(); } - + [AuraEffectHandler(AuraType.ModStatBonusPct)] void HandleModStatBonusPercent(AuraApplication aurApp, AuraEffectHandleModes mode, bool apply) { @@ -4640,7 +4640,7 @@ namespace Game.Spells target.CastSpell(target, triggerSpell, true); } - + [AuraEffectHandler(AuraType.OpenStable)] void HandleAuraOpenStable(AuraApplication aurApp, AuraEffectHandleModes mode, bool apply) { @@ -5698,6 +5698,22 @@ namespace Game.Spells else bg.RemovePlayerPosition(target.GetGUID()); } + + [AuraEffectHandler(AuraType.StoreTeleportReturnPoint)] + void HandleStoreTeleportReturnPoint(AuraApplication aurApp, AuraEffectHandleModes mode, bool apply) + { + if (!mode.HasAnyFlag(AuraEffectHandleModes.Real)) + return; + + Player playerTarget = aurApp.GetTarget().ToPlayer(); + if (playerTarget == null) + return; + + if (apply) + playerTarget.AddStoredAuraTeleportLocation(GetSpellInfo().Id); + else if (!playerTarget.GetSession().IsLogingOut()) + playerTarget.RemoveStoredAuraTeleportLocation(GetSpellInfo().Id); + } #endregion } diff --git a/Source/Game/Spells/SpellEffects.cs b/Source/Game/Spells/SpellEffects.cs index b593d1a93..4bcc51b26 100644 --- a/Source/Game/Spells/SpellEffects.cs +++ b/Source/Game/Spells/SpellEffects.cs @@ -5139,6 +5139,20 @@ namespace Game.Spells player.SendPacket(packet); } + void EffectTeleportToReturnPoint(uint effIndex) + { + if (effectHandleMode != SpellEffectHandleMode.HitTarget) + return; + + Player player = unitTarget.ToPlayer(); + if (player != null) + { + WorldLocation dest = player.GetStoredAuraTeleportLocation((uint)effectInfo.MiscValue); + if (dest != null) + player.TeleportTo(dest, unitTarget == m_caster ? TeleportToOptions.Spell | TeleportToOptions.NotLeaveCombat : 0); + } + } + [SpellEffectHandler(SpellEffectName.SummonRafFriend)] void EffectSummonRaFFriend(uint effIndex) { diff --git a/sql/base/characters_database.sql b/sql/base/characters_database.sql index b2fe7bb0f..98bee12f4 100644 --- a/sql/base/characters_database.sql +++ b/sql/base/characters_database.sql @@ -560,6 +560,34 @@ LOCK TABLES `character_aura_effect` WRITE; /*!40000 ALTER TABLE `character_aura_effect` ENABLE KEYS */; UNLOCK TABLES; +-- +-- Table structure for table `character_aura_stored_location` +-- + +DROP TABLE IF EXISTS `character_aura_stored_location`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `character_aura_stored_location` ( + `Guid` bigint(20) unsigned NOT NULL COMMENT 'Global Unique Identifier of Player', + `Spell` int(10) unsigned NOT NULL COMMENT 'Spell Identifier', + `MapId` int(10) unsigned NOT NULL COMMENT 'Map Id', + `PositionX` float NOT NULL COMMENT 'position x', + `PositionY` float NOT NULL COMMENT 'position y', + `PositionZ` float NOT NULL COMMENT 'position z', + `Orientation` float NOT NULL COMMENT 'Orientation', + PRIMARY KEY (`Guid`,`Spell`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `character_aura_stored_location` +-- + +LOCK TABLES `character_aura_stored_location` WRITE; +/*!40000 ALTER TABLE `character_aura_stored_location` DISABLE KEYS */; +/*!40000 ALTER TABLE `character_aura_stored_location` ENABLE KEYS */; +UNLOCK TABLES; + -- -- Table structure for table `character_banned` -- @@ -3822,7 +3850,8 @@ INSERT INTO `updates` VALUES ('2020_08_14_00_characters.sql','355685FF86EE64E2ED9D4B7D1311D53A9C2E0FA5','ARCHIVED','2020-08-14 21:41:24',0), ('2020_10_20_00_characters.sql','744F2A36865761920CE98A6DDE3A3BADF44D1E77','ARCHIVED','2020-10-20 21:36:49',0), ('2020_11_16_00_characters.sql','33D5C7539E239132923D01F4B6EAD5F3EF3EEB69','RELEASED','2020-11-16 19:16:31',0), -('2020_12_13_00_characters.sql','6AC743240033DED2C402ECB894A59D79EF607920','RELEASED','2020-12-13 18:36:58',0); +('2020_12_13_00_characters.sql','6AC743240033DED2C402ECB894A59D79EF607920','RELEASED','2020-12-13 18:36:58',0), +('2021_03_27_00_characters_aura_stored_location.sql','BF772ABC2DF186AF0A5DC56D5E824A2F4813BA69','RELEASED','2021-03-27 15:53:04',0); /*!40000 ALTER TABLE `updates` ENABLE KEYS */; UNLOCK TABLES; diff --git a/sql/updates/characters/master/2021_03_27_00_characters_aura_stored_location.sql b/sql/updates/characters/master/2021_03_27_00_characters_aura_stored_location.sql new file mode 100644 index 000000000..2a0f6e936 --- /dev/null +++ b/sql/updates/characters/master/2021_03_27_00_characters_aura_stored_location.sql @@ -0,0 +1,14 @@ +-- +-- Table structure for table `character_spell_location` +-- +DROP TABLE IF EXISTS `character_aura_stored_location`; +CREATE TABLE `character_aura_stored_location` ( + `Guid` bigint(20) unsigned NOT NULL COMMENT 'Global Unique Identifier of Player', + `Spell` int(10) unsigned NOT NULL COMMENT 'Spell Identifier', + `MapId` int(10) unsigned NOT NULL COMMENT 'Map Id', + `PositionX` float NOT NULL COMMENT 'position x', + `PositionY` float NOT NULL COMMENT 'position y', + `PositionZ` float NOT NULL COMMENT 'position z', + `Orientation` float NOT NULL COMMENT 'Orientation', + PRIMARY KEY (`Guid`,`Spell`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8;