/* * 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 Framework.GameMath; using Game.AI; using Game.BattleGrounds; using Game.Chat; using Game.Conditions; using Game.DataStorage; using Game.Entities; using Game.Groups; using Game.Guilds; using Game.Maps; using Game.Movement; using Game.PvP; using Game.Spells; using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.IO; using System.Linq; using System.Reflection; namespace Game.Scripting { // Manages registration, loading, and execution of Scripts. public class ScriptManager : Singleton { ScriptManager() { } //Initialization public void Initialize() { uint oldMSTime = Time.GetMSTime(); LoadDatabase(); Log.outInfo(LogFilter.ServerLoading, "Loading C# scripts"); FillSpellSummary(); //Load Core Scripts LoadScripts(Assembly.GetExecutingAssembly()); //Load Scripts.dll if (!File.Exists("Scripts.dll")) Log.outError(LogFilter.ServerLoading, "Cant find file {0}, Only Core Scripts are loaded."); else { Assembly asm = Assembly.LoadFile(Path.GetFullPath("Scripts.dll")); if (asm == null) Log.outError(LogFilter.ServerLoading, "Error Loading Scripts.dll, Only Core Scripts are loaded."); else LoadScripts(asm); } Log.outInfo(LogFilter.ServerLoading, $"Loaded {GetScriptCount()} C# scripts in {Time.GetMSTimeDiffToNow(oldMSTime)} ms"); } public void LoadScripts(Assembly assembly) { foreach (var type in assembly.GetTypes()) { var attributes = (ScriptAttribute[])type.GetCustomAttributes(); if (attributes.Empty()) { var baseType = type.BaseType; while (baseType != null) { if (baseType == typeof(ScriptObject)) { Log.outWarn(LogFilter.Server, "Script {0} does not have ScriptAttribute", type.Name); continue; } baseType = baseType.BaseType; } } else { var constructors = type.GetConstructors(); if (constructors.Length == 0) { Log.outError(LogFilter.Scripts, "Type: {0} contains no Public Constructors. Can't load script.", type.Name); continue; } foreach (var attribute in attributes) { var genericType = type; string name = type.Name; switch (type.BaseType.Name) { case "SpellScript": genericType = typeof(GenericSpellScriptLoader<>).MakeGenericType(type); name = name.Replace("_SpellScript", ""); break; case "AuraScript": genericType = typeof(GenericAuraScriptLoader<>).MakeGenericType(type); name = name.Replace("_AuraScript", ""); break; case "SpellScriptLoader": case "AuraScriptLoader": case "WorldScript": case "FormulaScript": case "WorldMapScript": case "InstanceMapScript": case "BattlegroundMapScript": case "ItemScript": case "UnitScript": case "CreatureScript": case "GameObjectScript": case "AreaTriggerScript": case "OutdoorPvPScript": case "WeatherScript": case "AuctionHouseScript": case "ConditionScript": case "VehicleScript": case "DynamicObjectScript": case "TransportScript": case "AchievementCriteriaScript": case "PlayerScript": case "GuildScript": case "GroupScript": case "AreaTriggerEntityScript": case "SceneScript": if (!attribute.Name.IsEmpty()) name = attribute.Name; if (attribute.Args.Empty()) Activator.CreateInstance(genericType); else Activator.CreateInstance(genericType, new object[] { name }.Combine(attribute.Args)); continue; default: genericType = typeof(GenericCreatureScript<>).MakeGenericType(type); break; } if (!attribute.Name.IsEmpty()) name = attribute.Name; Activator.CreateInstance(genericType, name, attribute.Args); } } } } public void LoadDatabase() { LoadScriptWaypoints(); LoadScriptSplineChains(); } void LoadScriptWaypoints() { uint oldMSTime = Time.GetMSTime(); // Drop Existing Waypoint list m_mPointMoveMap.Clear(); ulong uiCreatureCount = 0; // Load Waypoints SQLResult result = DB.World.Query("SELECT COUNT(entry) FROM script_waypoint GROUP BY entry"); if (!result.IsEmpty()) uiCreatureCount = result.Read(0); Log.outInfo(LogFilter.ServerLoading, "Loading Script Waypoints for {0} creature(s)...", uiCreatureCount); // 0 1 2 3 4 5 result = DB.World.Query("SELECT entry, pointid, location_x, location_y, location_z, waittime FROM script_waypoint ORDER BY pointid"); if (result.IsEmpty()) { Log.outInfo(LogFilter.ServerLoading, "Loaded 0 Script Waypoints. DB table `script_waypoint` is empty."); return; } uint count = 0; do { ScriptPointMove temp = new ScriptPointMove(); temp.uiCreatureEntry = result.Read(0); uint uiEntry = temp.uiCreatureEntry; temp.uiPointId = result.Read(1); temp.fX = result.Read(2); temp.fY = result.Read(3); temp.fZ = result.Read(4); temp.uiWaitTime = result.Read(5); CreatureTemplate pCInfo = Global.ObjectMgr.GetCreatureTemplate(temp.uiCreatureEntry); if (pCInfo == null) { Log.outError(LogFilter.Sql, "TSCR: DB table script_waypoint has waypoint for non-existant creature entry {0}", temp.uiCreatureEntry); continue; } if (pCInfo.ScriptID == 0) Log.outError(LogFilter.Sql, "TSCR: DB table script_waypoint has waypoint for creature entry {0}, but creature does not have ScriptName defined and then useless.", temp.uiCreatureEntry); m_mPointMoveMap.Add(uiEntry, temp); ++count; } while (result.NextRow()); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} Script Waypoint nodes in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime)); } void LoadScriptSplineChains() { uint oldMSTime = Time.GetMSTime(); m_mSplineChainsMap.Clear(); // 0 1 2 3 4 SQLResult resultMeta = DB.World.Query("SELECT entry, chainId, splineId, expectedDuration, msUntilNext FROM script_spline_chain_meta ORDER BY entry asc, chainId asc, splineId asc"); // 0 1 2 3 4 5 6 SQLResult resultWP = DB.World.Query("SELECT entry, chainId, splineId, wpId, x, y, z FROM script_spline_chain_waypoints ORDER BY entry asc, chainId asc, splineId asc, wpId asc"); if (resultMeta.IsEmpty() || resultWP.IsEmpty()) { Log.outInfo(LogFilter.ServerLoading, "Loaded spline chain data for 0 chains, consisting of 0 splines with 0 waypoints. DB tables `script_spline_chain_meta` and `script_spline_chain_waypoints` are empty."); } else { uint chainCount = 0, splineCount = 0, wpCount = 0; do { uint entry = resultMeta.Read(0); ushort chainId = resultMeta.Read(1); byte splineId = resultMeta.Read(2); var key = Tuple.Create(entry, chainId); if (!m_mSplineChainsMap.ContainsKey(key)) m_mSplineChainsMap[key] = new List(); var chain = m_mSplineChainsMap[Tuple.Create(entry,chainId)]; if (splineId != chain.Count) { Log.outWarn(LogFilter.ServerLoading, "Creature #{0}: Chain {1} has orphaned spline {2}, skipped.", entry, chainId, splineId); continue; } uint expectedDuration = resultMeta.Read(3), msUntilNext = resultMeta.Read(4); chain.Add(new SplineChainLink(expectedDuration, msUntilNext)); if (splineId == 0) ++chainCount; ++splineCount; } while (resultMeta.NextRow()); do { uint entry = resultWP.Read(0); ushort chainId = resultWP.Read(1); byte splineId = resultWP.Read(2); byte wpId = resultWP.Read(3); float posX = resultWP.Read(4); float posY = resultWP.Read(5); float posZ = resultWP.Read(6); var chain = m_mSplineChainsMap.LookupByKey(Tuple.Create(entry,chainId)); if (chain == null) { Log.outWarn(LogFilter.ServerLoading, "Creature #{0} has waypoint data for spline chain {1}. No such chain exists - entry skipped.", entry, chainId); continue; } if (splineId >= chain.Count) { Log.outWarn(LogFilter.ServerLoading, "Creature #{0} has waypoint data for spline ({1},{2}). The specified chain does not have a spline with this index - entry skipped.", entry, chainId, splineId); continue; } SplineChainLink spline = chain[splineId]; if (wpId != spline.Points.Count) { Log.outWarn(LogFilter.ServerLoading, "Creature #{0} has orphaned waypoint data in spline ({1},{2}) at index {3}. Skipped.", entry, chainId, splineId, wpId); continue; } spline.Points.Add(new Vector3(posX, posY, posZ)); ++wpCount; } while (resultWP.NextRow()); Log.outInfo(LogFilter.ServerLoading, "Loaded spline chain data for {0} chains, consisting of {1} splines with {2} waypoints in {3} ms", chainCount, splineCount, wpCount, Time.GetMSTimeDiffToNow(oldMSTime)); } } public void FillSpellSummary() { UnitAI.FillAISpellInfo(); SpellInfo pTempSpell; var spellStorage = Global.SpellMgr.GetSpellInfoStorage(); foreach (var i in spellStorage.Keys) { spellSummaryStorage[i] = new SpellSummary(); pTempSpell = spellStorage.LookupByKey(i); // This spell doesn't exist. if (pTempSpell == null) continue; foreach (SpellEffectInfo effect in pTempSpell.GetEffectsForDifficulty(Difficulty.None)) { if (effect == null) continue; // Spell targets self. if (effect.TargetA.GetTarget() == Targets.UnitCaster) spellSummaryStorage[i].Targets |= 1 << ((int)SelectTargetType.Self - 1); // Spell targets a single enemy. if (effect.TargetA.GetTarget() == Targets.UnitEnemy || effect.TargetA.GetTarget() == Targets.DestEnemy) spellSummaryStorage[i].Targets |= 1 << ((int)SelectTargetType.SingleEnemy - 1); // Spell targets AoE at enemy. if (effect.TargetA.GetTarget() == Targets.UnitSrcAreaEnemy || effect.TargetA.GetTarget() == Targets.UnitDestAreaEnemy || effect.TargetA.GetTarget() == Targets.SrcCaster || effect.TargetA.GetTarget() == Targets.DestDynobjEnemy) spellSummaryStorage[i].Targets |= 1 << ((int)SelectTargetType.AoeEnemy - 1); // Spell targets an enemy. if (effect.TargetA.GetTarget() == Targets.UnitEnemy || effect.TargetA.GetTarget() == Targets.DestEnemy || effect.TargetA.GetTarget() == Targets.UnitSrcAreaEnemy || effect.TargetA.GetTarget() == Targets.UnitDestAreaEnemy || effect.TargetA.GetTarget() == Targets.SrcCaster || effect.TargetA.GetTarget() == Targets.DestDynobjEnemy) spellSummaryStorage[i].Targets |= 1 << ((int)SelectTargetType.AnyEnemy - 1); // Spell targets a single friend (or self). if (effect.TargetA.GetTarget() == Targets.UnitCaster || effect.TargetA.GetTarget() == Targets.UnitAlly || effect.TargetA.GetTarget() == Targets.UnitParty) spellSummaryStorage[i].Targets |= 1 << ((int)SelectTargetType.SingleFriend - 1); // Spell targets AoE friends. if (effect.TargetA.GetTarget() == Targets.UnitCasterAreaParty || effect.TargetA.GetTarget() == Targets.UnitLastareaParty || effect.TargetA.GetTarget() == Targets.SrcCaster) spellSummaryStorage[i].Targets |= 1 << ((int)SelectTargetType.AoeFriend - 1); // Spell targets any friend (or self). if (effect.TargetA.GetTarget() == Targets.UnitCaster || effect.TargetA.GetTarget() == Targets.UnitAlly || effect.TargetA.GetTarget() == Targets.UnitParty || effect.TargetA.GetTarget() == Targets.UnitCasterAreaParty || effect.TargetA.GetTarget() == Targets.UnitLastareaParty || effect.TargetA.GetTarget() == Targets.SrcCaster) spellSummaryStorage[i].Targets |= 1 << ((int)SelectTargetType.AnyFriend - 1); // Make sure that this spell includes a damage effect. if (effect.Effect == SpellEffectName.SchoolDamage || effect.Effect == SpellEffectName.Instakill || effect.Effect == SpellEffectName.EnvironmentalDamage || effect.Effect == SpellEffectName.HealthLeech) spellSummaryStorage[i].Effects |= 1 << ((int)SelectEffect.Damage - 1); // Make sure that this spell includes a healing effect (or an apply aura with a periodic heal). if (effect.Effect == SpellEffectName.Heal || effect.Effect == SpellEffectName.HealMaxHealth || effect.Effect == SpellEffectName.HealMechanical || (effect.Effect == SpellEffectName.ApplyAura && effect.ApplyAuraName == AuraType.PeriodicHeal)) spellSummaryStorage[i].Effects |= 1 << ((int)SelectEffect.Healing - 1); // Make sure that this spell applies an aura. if (effect.Effect == SpellEffectName.ApplyAura) spellSummaryStorage[i].Effects |= 1 << ((int)SelectEffect.Aura - 1); } } } public List GetSplineChain(Creature who, ushort chainId) { return GetSplineChain(who.GetEntry(), chainId); } List GetSplineChain(uint entry, ushort chainId) { return m_mSplineChainsMap.LookupByKey(Tuple.Create(entry, chainId)); } public string ScriptsVersion() { return "Integrated Cypher Scripts"; } public void IncrementScriptCount() { ++_ScriptCount; } public uint GetScriptCount() { return _ScriptCount; } //Reloading public void Reload() { } //Unloading public void Unload() { foreach (ScriptRegistry scr in ScriptStorage) scr.Unload(); } //SpellScriptLoader public List CreateSpellScripts(uint spellId, Spell invoker) { var scriptList = new List(); var bounds = Global.ObjectMgr.GetSpellScriptsBounds(spellId); var reg = GetScriptRegistry(); if (reg == null) return scriptList; foreach (var id in bounds) { var tmpscript = reg.GetScriptById(id); if (tmpscript == null) continue; SpellScript script = tmpscript.GetSpellScript(); if (script == null) continue; script._Init(tmpscript.GetName(), spellId); if (!script._Load(invoker)) continue; scriptList.Add(script); } return scriptList; } public List CreateAuraScripts(uint spellId, Aura invoker) { var scriptList = new List(); var bounds = Global.ObjectMgr.GetSpellScriptsBounds(spellId); var reg = GetScriptRegistry(); if (reg == null) return scriptList; foreach (var id in bounds) { var tmpscript = reg.GetScriptById(id); if (tmpscript == null) continue; AuraScript script = tmpscript.GetAuraScript(); if (script == null) continue; script._Init(tmpscript.GetName(), spellId); if (!script._Load(invoker)) continue; scriptList.Add(script); } return scriptList; } public Dictionary CreateSpellScriptLoaders(uint spellId) { var scriptDic = new Dictionary(); var bounds = Global.ObjectMgr.GetSpellScriptsBounds(spellId); var reg = GetScriptRegistry(); if (reg == null) return scriptDic; foreach (var id in bounds) { var tmpscript = reg.GetScriptById(id); if (tmpscript == null) continue; scriptDic.Add(tmpscript, id); } return scriptDic; } public Dictionary CreateAuraScriptLoaders(uint spellId) { var scriptDic = new Dictionary(); var bounds = Global.ObjectMgr.GetSpellScriptsBounds(spellId); var reg = GetScriptRegistry(); if (reg == null) return scriptDic; foreach (var id in bounds) { var tmpscript = reg.GetScriptById(id); if (tmpscript == null) continue; scriptDic.Add(tmpscript, id); } return scriptDic; } //WorldScript public void OnOpenStateChange(bool open) { ForEach(p => p.OnOpenStateChange(open)); } public void OnConfigLoad(bool reload) { ForEach(p => p.OnConfigLoad(reload)); } public void OnMotdChange(string newMotd) { ForEach(p => p.OnMotdChange(newMotd)); } public void OnShutdownInitiate(ShutdownExitCode code, ShutdownMask mask) { ForEach(p => p.OnShutdownInitiate(code, mask)); } public void OnShutdownCancel() { ForEach(p => p.OnShutdownCancel()); } public void OnWorldUpdate(uint diff) { ForEach(p => p.OnUpdate(diff)); } //FormulaScript public void OnHonorCalculation(float honor, uint level, float multiplier) { ForEach(p => p.OnHonorCalculation(honor, level, multiplier)); } public void OnGrayLevelCalculation(uint grayLevel, uint playerLevel) { ForEach(p => p.OnGrayLevelCalculation(grayLevel, playerLevel)); } public void OnColorCodeCalculation(XPColorChar color, uint playerLevel, uint mobLevel) { ForEach(p => p.OnColorCodeCalculation(color, playerLevel, mobLevel)); } public void OnZeroDifferenceCalculation(uint diff, uint playerLevel) { ForEach(p => p.OnZeroDifferenceCalculation(diff, playerLevel)); } public void OnBaseGainCalculation(uint gain, uint playerLevel, uint mobLevel) { ForEach(p => p.OnBaseGainCalculation(gain, playerLevel, mobLevel)); } public void OnGainCalculation(uint gain, Player player, Unit unit) { Contract.Assert(player != null); Contract.Assert(unit != null); ForEach(p => p.OnGainCalculation(gain, player, unit)); } public void OnGroupRateCalculation(float rate, uint count, bool isRaid) { ForEach(p => p.OnGroupRateCalculation(rate, count, isRaid)); } //MapScript public void OnCreateMap(Map map) { Contract.Assert(map != null); var record = map.GetEntry(); if (record != null && record.IsWorldMap()) ForEach(p => p.OnCreate(map)); if (record != null && record.IsDungeon()) ForEach(p => p.OnCreate(map.ToInstanceMap())); if (record != null && record.IsBattleground()) ForEach(p => p.OnCreate(map.ToBattlegroundMap())); } public void OnDestroyMap(Map map) { Contract.Assert(map != null); var record = map.GetEntry(); if (record != null && record.IsWorldMap()) ForEach(p => p.OnDestroy(map)); if (record != null && record.IsDungeon()) ForEach(p => p.OnDestroy(map.ToInstanceMap())); if (record != null && record.IsBattleground()) ForEach(p => p.OnDestroy(map.ToBattlegroundMap())); } public void OnLoadGridMap(Map map, GridMap gmap, uint gx, uint gy) { Contract.Assert(map != null); Contract.Assert(gmap != null); var record = map.GetEntry(); if (record != null && record.IsWorldMap()) ForEach(p => p.OnLoadGridMap(map, gmap, gx, gy)); if (record != null && record.IsDungeon()) ForEach(p => p.OnLoadGridMap(map.ToInstanceMap(), gmap, gx, gy)); if (record != null && record.IsBattleground()) ForEach(p => p.OnLoadGridMap(map.ToBattlegroundMap(), gmap, gx, gy)); } public void OnUnloadGridMap(Map map, GridMap gmap, uint gx, uint gy) { Contract.Assert(map != null); Contract.Assert(gmap != null); var record = map.GetEntry(); if (record != null && record.IsWorldMap()) ForEach(p => p.OnUnloadGridMap(map, gmap, gx, gy)); if (record != null && record.IsDungeon()) ForEach(p => p.OnUnloadGridMap(map.ToInstanceMap(), gmap, gx, gy)); if (record != null && record.IsBattleground()) ForEach(p => p.OnUnloadGridMap(map.ToBattlegroundMap(), gmap, gx, gy)); } public void OnPlayerEnterMap(Map map, Player player) { Contract.Assert(map != null); Contract.Assert(player != null); ForEach(p => p.OnMapChanged(player)); var record = map.GetEntry(); if (record != null && record.IsWorldMap()) ForEach(p => p.OnPlayerEnter(map, player)); if (record != null && record.IsDungeon()) ForEach(p => p.OnPlayerEnter(map.ToInstanceMap(), player)); if (record != null && record.IsBattleground()) ForEach(p => p.OnPlayerEnter(map.ToBattlegroundMap(), player)); } public void OnPlayerLeaveMap(Map map, Player player) { Contract.Assert(map != null); var record = map.GetEntry(); if (record != null && record.IsWorldMap()) ForEach(p => p.OnPlayerLeave(map, player)); if (record != null && record.IsDungeon()) ForEach(p => p.OnPlayerLeave(map.ToInstanceMap(), player)); if (record != null && record.IsBattleground()) ForEach(p => p.OnPlayerLeave(map.ToBattlegroundMap(), player)); } public void OnMapUpdate(Map map, uint diff) { Contract.Assert(map != null); var record = map.GetEntry(); if (record != null && record.IsWorldMap()) ForEach(p => p.OnUpdate(map, diff)); if (record != null && record.IsDungeon()) ForEach(p => p.OnUpdate(map.ToInstanceMap(), diff)); if (record != null && record.IsBattleground()) ForEach(p => p.OnUpdate(map.ToBattlegroundMap(), diff)); } //InstanceMapScript public InstanceScript CreateInstanceData(InstanceMap map) { Contract.Assert(map != null); return RunScriptRet(p => p.GetInstanceScript(map), map.GetScriptId(), null); } //ItemScript public bool OnDummyEffect(Unit caster, uint spellId, uint effIndex, Item target) { Contract.Assert(caster != null); Contract.Assert(target != null); return RunScriptRet(p => p.OnDummyEffect(caster, spellId, effIndex, target), target.GetScriptId()); } public bool OnQuestAccept(Player player, Item item, Quest quest) { Contract.Assert(player != null); Contract.Assert(item != null); Contract.Assert(quest != null); return RunScriptRet(p => p.OnQuestAccept(player, item, quest), item.GetScriptId()); } public bool OnItemUse(Player player, Item item, SpellCastTargets targets, ObjectGuid castId) { Contract.Assert(player); Contract.Assert(item); return RunScriptRet(p => p.OnUse(player, item, targets, castId), item.GetScriptId()); } public bool OnItemExpire(Player player, ItemTemplate proto) { Contract.Assert(player); Contract.Assert(proto != null); return RunScriptRet(p => p.OnExpire(player, proto), proto.ScriptId); } //CreatureScript public bool OnDummyEffect(Unit caster, uint spellId, uint effIndex, Creature target) { Contract.Assert(caster); Contract.Assert(target); return RunScriptRet(p => p.OnDummyEffect(caster, spellId, effIndex, target), target.GetScriptId()); } public bool OnGossipHello(Player player, Creature creature) { Contract.Assert(player); Contract.Assert(creature); return RunScriptRet(p => p.OnGossipHello(player, creature), creature.GetScriptId()); } public bool OnGossipSelect(Player player, Creature creature, uint sender, uint action) { Contract.Assert(player != null); Contract.Assert(creature != null); return RunScriptRet(p => p.OnGossipSelect(player, creature, sender, action), creature.GetScriptId()); } public bool OnGossipSelectCode(Player player, Creature creature, uint sender, uint action, string code) { Contract.Assert(player != null); Contract.Assert(creature != null); Contract.Assert(code != null); return RunScriptRet(p => p.OnGossipSelectCode(player, creature, sender, action, code), creature.GetScriptId()); } public bool OnQuestAccept(Player player, Creature creature, Quest quest) { Contract.Assert(player != null); Contract.Assert(creature != null); Contract.Assert(quest != null); return RunScriptRet(p => p.OnQuestAccept(player, creature, quest), creature.GetScriptId()); } public bool OnQuestSelect(Player player, Creature creature, Quest quest) { Contract.Assert(player != null); Contract.Assert(creature != null); Contract.Assert(quest != null); return RunScriptRet(p => p.OnQuestSelect(player, creature, quest), creature.GetScriptId()); } public bool OnQuestComplete(Player player, Creature creature, Quest quest) { Contract.Assert(player != null); Contract.Assert(creature != null); Contract.Assert(quest != null); return RunScriptRet(p => p.OnQuestComplete(player, creature, quest), creature.GetScriptId()); } public bool OnQuestReward(Player player, Creature creature, Quest quest, uint opt) { Contract.Assert(player != null); Contract.Assert(creature != null); Contract.Assert(quest != null); return RunScriptRet(p => p.OnQuestReward(player, creature, quest, opt), creature.GetScriptId()); } public uint GetDialogStatus(Player player, Creature creature) { Contract.Assert(player != null); Contract.Assert(creature != null); player.PlayerTalkClass.ClearMenus(); return RunScriptRet(p => p.GetDialogStatus(player, creature), creature.GetScriptId(), (uint)QuestGiverStatus.ScriptedNoStatus); } public bool CanSpawn(ulong spawnId, uint entry, CreatureTemplate actTemplate, CreatureData cData, Map map) { Contract.Assert(actTemplate != null); CreatureTemplate baseTemplate = Global.ObjectMgr.GetCreatureTemplate(entry); if (baseTemplate == null) baseTemplate = actTemplate; return RunScriptRet(p => p.CanSpawn(spawnId, entry, baseTemplate, actTemplate, cData, map), cData != null ? cData.ScriptId : baseTemplate.ScriptID, true); } public CreatureAI GetCreatureAI(Creature creature) { Contract.Assert(creature != null); return RunScriptRet(p => p.GetAI(creature), creature.GetScriptId()); } public void OnCreatureUpdate(Creature creature, uint diff) { Contract.Assert(creature != null); RunScript(p => p.OnUpdate(creature, diff), creature.GetScriptId()); } //GameObjectScript public bool OnDummyEffect(Unit caster, uint spellId, uint effIndex, GameObject target) { Contract.Assert(caster != null); Contract.Assert(target != null); return RunScriptRet(p => p.OnDummyEffect(caster, spellId, effIndex, target), target.GetScriptId()); } public bool OnGossipHello(Player player, GameObject go) { Contract.Assert(player != null); Contract.Assert(go != null); player.PlayerTalkClass.ClearMenus(); return RunScriptRet(p => p.OnGossipHello(player, go), go.GetScriptId()); } public bool OnGossipSelect(Player player, GameObject go, uint sender, uint action) { Contract.Assert(player != null); Contract.Assert(go != null); return RunScriptRet(p => p.OnGossipSelect(player, go, sender, action), go.GetScriptId()); } public bool OnGossipSelectCode(Player player, GameObject go, uint sender, uint action, string code) { Contract.Assert(player != null); Contract.Assert(go != null); Contract.Assert(code != null); return RunScriptRet(p => p.OnGossipSelectCode(player, go, sender, action, code), go.GetScriptId()); } public bool OnQuestAccept(Player player, GameObject go, Quest quest) { Contract.Assert(player != null); Contract.Assert(go != null); Contract.Assert(quest != null); return RunScriptRet(p => p.OnQuestAccept(player, go, quest), go.GetScriptId()); } public bool OnQuestReward(Player player, GameObject go, Quest quest, uint opt) { Contract.Assert(player != null); Contract.Assert(go != null); Contract.Assert(quest != null); return RunScriptRet(p => p.OnQuestReward(player, go, quest, opt), go.GetScriptId()); } public uint GetDialogStatus(Player player, GameObject go) { Contract.Assert(player != null); Contract.Assert(go != null); return RunScriptRet(p => p.GetDialogStatus(player, go), go.GetScriptId(), (uint)QuestGiverStatus.ScriptedNoStatus); } public void OnGameObjectDestroyed(GameObject go, Player player) { Contract.Assert(go != null); RunScript(p => p.OnDestroyed(go, player), go.GetScriptId()); } public void OnGameObjectDamaged(GameObject go, Player player) { Contract.Assert(go != null); RunScript(p => p.OnDamaged(go, player), go.GetScriptId()); } public void OnGameObjectLootStateChanged(GameObject go, uint state, Unit unit) { Contract.Assert(go != null); RunScript(p => p.OnLootStateChanged(go, state, unit), go.GetScriptId()); } public void OnGameObjectStateChanged(GameObject go, GameObjectState state) { Contract.Assert(go != null); RunScript(p => p.OnGameObjectStateChanged(go, state), go.GetScriptId()); } public void OnGameObjectUpdate(GameObject go, uint diff) { Contract.Assert(go != null); RunScript(p => p.OnUpdate(go, diff), go.GetScriptId()); } public GameObjectAI GetGameObjectAI(GameObject go) { Contract.Assert(go != null); return RunScriptRet(p => p.GetAI(go), go.GetScriptId()); } //AreaTriggerScript public bool OnAreaTrigger(Player player, AreaTriggerRecord trigger, bool entered) { Contract.Assert(player != null); Contract.Assert(trigger != null); return RunScriptRet(p => p.OnTrigger(player, trigger, entered), Global.ObjectMgr.GetAreaTriggerScriptId(trigger.Id)); } //BattlegroundScript public Battleground CreateBattleground(BattlegroundTypeId typeId) { // @todo Implement script-side Battlegrounds. Contract.Assert(false); return null; } // OutdoorPvPScript public OutdoorPvP CreateOutdoorPvP(OutdoorPvPData data) { Contract.Assert(data != null); return RunScriptRet(p => p.GetOutdoorPvP(), data.ScriptId, null); } // WeatherScript public void OnWeatherChange(Weather weather, WeatherState state, float grade) { Contract.Assert(weather != null); RunScript(p => p.OnChange(weather, state, grade), weather.GetScriptId()); } public void OnWeatherUpdate(Weather weather, uint diff) { Contract.Assert(weather != null); RunScript(p => p.OnUpdate(weather, diff), weather.GetScriptId()); } // AuctionHouseScript public void OnAuctionAdd(AuctionHouseObject ah, AuctionEntry entry) { Contract.Assert(ah != null); Contract.Assert(entry != null); ForEach(p => p.OnAuctionAdd(ah, entry)); } public void OnAuctionRemove(AuctionHouseObject ah, AuctionEntry entry) { Contract.Assert(ah != null); Contract.Assert(entry != null); ForEach(p => p.OnAuctionRemove(ah, entry)); } public void OnAuctionSuccessful(AuctionHouseObject ah, AuctionEntry entry) { Contract.Assert(ah != null); Contract.Assert(entry != null); ForEach(p => p.OnAuctionSuccessful(ah, entry)); } public void OnAuctionExpire(AuctionHouseObject ah, AuctionEntry entry) { Contract.Assert(ah != null); Contract.Assert(entry != null); ForEach(p => p.OnAuctionExpire(ah, entry)); } // ConditionScript public bool OnConditionCheck(Condition condition, ConditionSourceInfo sourceInfo) { Contract.Assert(condition != null); return RunScriptRet(p => p.OnConditionCheck(condition, sourceInfo), condition.ScriptId, true); } // VehicleScript public void OnInstall(Vehicle veh) { Contract.Assert(veh != null); Contract.Assert(veh.GetBase().IsTypeId(TypeId.Unit)); RunScript(p => p.OnInstall(veh), veh.GetBase().ToCreature().GetScriptId()); } public void OnUninstall(Vehicle veh) { Contract.Assert(veh != null); Contract.Assert(veh.GetBase().IsTypeId(TypeId.Unit)); RunScript(p => p.OnUninstall(veh), veh.GetBase().ToCreature().GetScriptId()); } public void OnReset(Vehicle veh) { Contract.Assert(veh != null); Contract.Assert(veh.GetBase().IsTypeId(TypeId.Unit)); RunScript(p => p.OnReset(veh), veh.GetBase().ToCreature().GetScriptId()); } public void OnInstallAccessory(Vehicle veh, Creature accessory) { Contract.Assert(veh != null); Contract.Assert(veh.GetBase().IsTypeId(TypeId.Unit)); Contract.Assert(accessory != null); RunScript(p => p.OnInstallAccessory(veh, accessory), veh.GetBase().ToCreature().GetScriptId()); } public void OnAddPassenger(Vehicle veh, Unit passenger, sbyte seatId) { Contract.Assert(veh != null); Contract.Assert(veh.GetBase().IsTypeId(TypeId.Unit)); Contract.Assert(passenger != null); RunScript(p => p.OnAddPassenger(veh, passenger, seatId), veh.GetBase().ToCreature().GetScriptId()); } public void OnRemovePassenger(Vehicle veh, Unit passenger) { Contract.Assert(veh != null); Contract.Assert(veh.GetBase().IsTypeId(TypeId.Unit)); Contract.Assert(passenger != null); RunScript(p => p.OnRemovePassenger(veh, passenger), veh.GetBase().ToCreature().GetScriptId()); } // DynamicObjectScript public void OnDynamicObjectUpdate(DynamicObject dynobj, uint diff) { Contract.Assert(dynobj != null); ForEach(p => p.OnUpdate(dynobj, diff)); } // TransportScript public void OnAddPassenger(Transport transport, Player player) { Contract.Assert(transport != null); Contract.Assert(player != null); RunScript(p => p.OnAddPassenger(transport, player), transport.GetScriptId()); } public void OnAddCreaturePassenger(Transport transport, Creature creature) { Contract.Assert(transport != null); Contract.Assert(creature != null); RunScript(p => p.OnAddCreaturePassenger(transport, creature), transport.GetScriptId()); } public void OnRemovePassenger(Transport transport, Player player) { Contract.Assert(transport != null); Contract.Assert(player != null); RunScript(p => p.OnRemovePassenger(transport, player), transport.GetScriptId()); } public void OnTransportUpdate(Transport transport, uint diff) { Contract.Assert(transport != null); RunScript(p => p.OnUpdate(transport, diff), transport.GetScriptId()); } public void OnRelocate(Transport transport, uint waypointId, uint mapId, float x, float y, float z) { RunScript(p => p.OnRelocate(transport, waypointId, mapId, x, y, z), transport.GetScriptId()); } // AchievementCriteriaScript public bool OnCriteriaCheck(uint ScriptId, Player source, Unit target) { Contract.Assert(source != null); // target can be NULL. return RunScriptRet(p => p.OnCheck(source, target), ScriptId); } // PlayerScript public void OnPVPKill(Player killer, Player killed) { ForEach(p => p.OnPVPKill(killer, killed)); } public void OnCreatureKill(Player killer, Creature killed) { ForEach(p => p.OnCreatureKill(killer, killed)); } public void OnPlayerKilledByCreature(Creature killer, Player killed) { ForEach(p => p.OnPlayerKilledByCreature(killer, killed)); } public void OnPlayerLevelChanged(Player player, byte oldLevel) { ForEach(p => p.OnLevelChanged(player, oldLevel)); } public void OnPlayerFreeTalentPointsChanged(Player player, uint newPoints) { ForEach(p => p.OnFreeTalentPointsChanged(player, newPoints)); } public void OnPlayerTalentsReset(Player player, bool noCost) { ForEach(p => p.OnTalentsReset(player, noCost)); } public void OnPlayerMoneyChanged(Player player, long amount) { ForEach(p => p.OnMoneyChanged(player, amount)); } public void OnGivePlayerXP(Player player, uint amount, Unit victim) { ForEach(p => p.OnGiveXP(player, amount, victim)); } public void OnPlayerReputationChange(Player player, uint factionID, int standing, bool incremental) { ForEach(p => p.OnReputationChange(player, factionID, standing, incremental)); } public void OnPlayerDuelRequest(Player target, Player challenger) { ForEach(p => p.OnDuelRequest(target, challenger)); } public void OnPlayerDuelStart(Player player1, Player player2) { ForEach(p => p.OnDuelStart(player1, player2)); } public void OnPlayerDuelEnd(Player winner, Player loser, DuelCompleteType type) { ForEach(p => p.OnDuelEnd(winner, loser, type)); } public void OnPlayerChat(Player player, ChatMsg type, Language lang, string msg) { ForEach(p => p.OnChat(player, type, lang, msg)); } public void OnPlayerChat(Player player, ChatMsg type, Language lang, string msg, Player receiver) { ForEach(p => p.OnChat(player, type, lang, msg, receiver)); } public void OnPlayerChat(Player player, ChatMsg type, Language lang, string msg, Group group) { ForEach(p => p.OnChat(player, type, lang, msg, group)); } public void OnPlayerChat(Player player, ChatMsg type, Language lang, string msg, Guild guild) { ForEach(p => p.OnChat(player, type, lang, msg, guild)); } public void OnPlayerChat(Player player, ChatMsg type, Language lang, string msg, Channel channel) { ForEach(p => p.OnChat(player, type, lang, msg, channel)); } public void OnPlayerClearEmote(Player player) { ForEach(p => p.OnClearEmote(player)); } public void OnPlayerTextEmote(Player player, uint textEmote, uint emoteNum, ObjectGuid guid) { ForEach(p => p.OnTextEmote(player, textEmote, emoteNum, guid)); } public void OnPlayerSpellCast(Player player, Spell spell, bool skipCheck) { ForEach(p => p.OnSpellCast(player, spell, skipCheck)); } public void OnPlayerLogin(Player player) { ForEach(p => p.OnLogin(player)); } public void OnPlayerLogout(Player player) { ForEach(p => p.OnLogout(player)); } public void OnPlayerCreate(Player player) { ForEach(p => p.OnCreate(player)); } public void OnPlayerDelete(ObjectGuid guid) { ForEach(p => p.OnDelete(guid)); } public void OnPlayerSave(Player player) { ForEach(p => p.OnSave(player)); } public void OnPlayerBindToInstance(Player player, Difficulty difficulty, uint mapid, bool permanent, BindExtensionState extendState) { ForEach(p => p.OnBindToInstance(player, difficulty, mapid, permanent, extendState)); } public void OnPlayerUpdateZone(Player player, uint newZone, uint newArea) { ForEach(p => p.OnUpdateZone(player, newZone, newArea)); } public void OnQuestStatusChange(Player player, uint questId) { ForEach(p => p.OnQuestStatusChange(player, questId)); } public void OnMovieComplete(Player player, uint movieId) { ForEach(p => p.OnMovieComplete(player, movieId)); } // GuildScript public void OnGuildAddMember(Guild guild, Player player, byte plRank) { ForEach(p => p.OnAddMember(guild, player, plRank)); } public void OnGuildRemoveMember(Guild guild, Player player, bool isDisbanding, bool isKicked) { ForEach(p => p.OnRemoveMember(guild, player, isDisbanding, isKicked)); } public void OnGuildMOTDChanged(Guild guild, string newMotd) { ForEach(p => p.OnMOTDChanged(guild, newMotd)); } public void OnGuildInfoChanged(Guild guild, string newInfo) { ForEach(p => p.OnInfoChanged(guild, newInfo)); } public void OnGuildCreate(Guild guild, Player leader, string name) { ForEach(p => p.OnCreate(guild, leader, name)); } public void OnGuildDisband(Guild guild) { ForEach(p => p.OnDisband(guild)); } public void OnGuildMemberWitdrawMoney(Guild guild, Player player, ulong amount, bool isRepair) { ForEach(p => p.OnMemberWitdrawMoney(guild, player, amount, isRepair)); } public void OnGuildMemberDepositMoney(Guild guild, Player player, ulong amount) { ForEach(p => p.OnMemberDepositMoney(guild, player, amount)); } public void OnGuildItemMove(Guild guild, Player player, Item pItem, bool isSrcBank, byte srcContainer, byte srcSlotId, bool isDestBank, byte destContainer, byte destSlotId) { ForEach(p => p.OnItemMove(guild, player, pItem, isSrcBank, srcContainer, srcSlotId, isDestBank, destContainer, destSlotId)); } public void OnGuildEvent(Guild guild, byte eventType, ulong playerGuid1, ulong playerGuid2, byte newRank) { ForEach(p => p.OnEvent(guild, eventType, playerGuid1, playerGuid2, newRank)); } public void OnGuildBankEvent(Guild guild, byte eventType, byte tabId, ulong playerGuid, uint itemOrMoney, ushort itemStackCount, byte destTabId) { ForEach(p => p.OnBankEvent(guild, eventType, tabId, playerGuid, itemOrMoney, itemStackCount, destTabId)); } // GroupScript public void OnGroupAddMember(Group group, ObjectGuid guid) { Contract.Assert(group); ForEach(p => p.OnAddMember(group, guid)); } public void OnGroupInviteMember(Group group, ObjectGuid guid) { Contract.Assert(group); ForEach(p => p.OnInviteMember(group, guid)); } public void OnGroupRemoveMember(Group group, ObjectGuid guid, RemoveMethod method, ObjectGuid kicker, string reason) { Contract.Assert(group); ForEach(p => p.OnRemoveMember(group, guid, method, kicker, reason)); } public void OnGroupChangeLeader(Group group, ObjectGuid newLeaderGuid, ObjectGuid oldLeaderGuid) { Contract.Assert(group); ForEach(p => p.OnChangeLeader(group, newLeaderGuid, oldLeaderGuid)); } public void OnGroupDisband(Group group) { Contract.Assert(group); ForEach(p => p.OnDisband(group)); } // UnitScript public void OnHeal(Unit healer, Unit reciever, ref uint gain) { uint dmg = gain; ForEach(p => p.OnHeal(healer, reciever, ref dmg)); gain = dmg; } public void OnDamage(Unit attacker, Unit victim, ref uint damage) { uint dmg = damage; ForEach(p => p.OnDamage(attacker, victim, ref dmg)); damage = dmg; } public void ModifyPeriodicDamageAurasTick(Unit target, Unit attacker, ref uint damage) { uint dmg = damage; ForEach(p => p.ModifyPeriodicDamageAurasTick(target, attacker, ref dmg)); damage = dmg; } public void ModifyMeleeDamage(Unit target, Unit attacker, ref uint damage) { uint dmg = damage; ForEach(p => p.ModifyMeleeDamage(target, attacker, ref dmg)); damage = dmg; } public void ModifySpellDamageTaken(Unit target, Unit attacker, ref int damage) { int dmg = damage; ForEach(p => p.ModifySpellDamageTaken(target, attacker, ref dmg)); damage = dmg; } // AreaTriggerEntityScript public AreaTriggerAI GetAreaTriggerAI(AreaTrigger areaTrigger) { Contract.Assert(areaTrigger); return RunScriptRet(p => p.GetAI(areaTrigger), areaTrigger.GetScriptId(), null); } //SceneScript public void OnSceneStart(Player player, uint sceneInstanceID, SceneTemplate sceneTemplate) { Contract.Assert(player); Contract.Assert(sceneTemplate != null); RunScript(script => script.OnSceneStart(player, sceneInstanceID, sceneTemplate), sceneTemplate.ScriptId); } public void OnSceneTrigger(Player player, uint sceneInstanceID, SceneTemplate sceneTemplate, string triggerName) { Contract.Assert(player); Contract.Assert(sceneTemplate != null); RunScript(script => script.OnSceneTriggerEvent(player, sceneInstanceID, sceneTemplate, triggerName), sceneTemplate.ScriptId); } public void OnSceneCancel(Player player, uint sceneInstanceID, SceneTemplate sceneTemplate) { Contract.Assert(player); Contract.Assert(sceneTemplate != null); RunScript(script => script.OnSceneCancel(player, sceneInstanceID, sceneTemplate), sceneTemplate.ScriptId); } public void OnSceneComplete(Player player, uint sceneInstanceID, SceneTemplate sceneTemplate) { Contract.Assert(player); Contract.Assert(sceneTemplate != null); RunScript(script => script.OnSceneComplete(player, sceneInstanceID, sceneTemplate), sceneTemplate.ScriptId); } public void ForEach(Action a) where T : ScriptObject { var reg = GetScriptRegistry(); if (reg == null || reg.Empty()) return; foreach (var script in reg.GetStorage()) a.Invoke(script); } public bool RunScriptRet(Func func, uint id, bool ret = false) where T : ScriptObject { return RunScriptRet(func, id, ret); } public U RunScriptRet(Func func, uint id, U ret = default(U)) where T : ScriptObject { var reg = GetScriptRegistry(); if (reg == null || reg.Empty()) return ret; var script = reg.GetScriptById(id); if (script == null) return ret; return func.Invoke(script); } public void RunScript(Action a, uint id) where T : ScriptObject { var reg = GetScriptRegistry(); if (reg == null || reg.Empty()) return; var script = reg.GetScriptById(id); if (script != null) a.Invoke(script); } public void AddScript(T script) where T : ScriptObject { Contract.Assert(script != null); if (!ScriptStorage.ContainsKey(typeof(T))) ScriptStorage[typeof(T)] = new ScriptRegistry(); GetScriptRegistry().AddScript(script); } public List GetPointMoveList(uint creatureEntry) { return m_mPointMoveMap.LookupByKey(creatureEntry); } ScriptRegistry GetScriptRegistry() where T : ScriptObject { if (ScriptStorage.ContainsKey(typeof(T))) return (ScriptRegistry)ScriptStorage[typeof(T)]; return null; } uint _ScriptCount; public Dictionary spellSummaryStorage = new Dictionary(); Hashtable ScriptStorage = new Hashtable(); MultiMap m_mPointMoveMap = new MultiMap(); // creature entry + chain ID MultiMap, SplineChainLink> m_mSplineChainsMap = new MultiMap, SplineChainLink>(); // spline chains } public class ScriptRegistry where TValue : ScriptObject { public void AddScript(TValue script) { Contract.Assert(script != null); if (!script.IsDatabaseBound()) { // We're dealing with a code-only script; just add it. ScriptMap[_scriptIdCounter++] = script; Global.ScriptMgr.IncrementScriptCount(); return; } // Get an ID for the script. An ID only exists if it's a script that is assigned in the database // through a script name (or similar). uint id = Global.ObjectMgr.GetScriptId(script.GetName()); if (id != 0) { // Try to find an existing script. bool existing = false; foreach (var it in ScriptMap) { if (it.Value.GetName() == script.GetName()) { existing = true; break; } } // If the script isn't assigned . assign it! if (!existing) { ScriptMap[id] = script; Global.ScriptMgr.IncrementScriptCount(); } else { // If the script is already assigned . delete it! Log.outError(LogFilter.Scripts, "Script '{0}' already assigned with the same script name, so the script can't work.", script.GetName()); Contract.Assert(false); // Error that should be fixed ASAP. } } else { // The script uses a script name from database, but isn't assigned to anything. Log.outError(LogFilter.Sql, "Script named '{0}' does not have a script name assigned in database.", script.GetName()); return; } } // Gets a script by its ID (assigned by ObjectMgr). public TValue GetScriptById(uint id) { return ScriptMap.LookupByKey(id); } public bool Empty() { return ScriptMap.Empty(); } public List GetStorage() { return ScriptMap.Values.ToList(); } public void Unload() { foreach (var key in ScriptMap.Keys) ScriptMap.Remove(key); } // Counter used for code-only scripts. uint _scriptIdCounter; Dictionary ScriptMap = new Dictionary(); } public class ScriptPointMove { public uint uiCreatureEntry; public uint uiPointId; public float fX; public float fY; public float fZ; public uint uiWaitTime; } public class SpellSummary { public byte Targets; // set of enum SelectTarget public byte Effects; // set of enum SelectEffect } [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] public class ScriptAttribute : Attribute { public ScriptAttribute(string name="", params object[] args) { Name = name; Args = args; } public string Name { get; private set; } public object[] Args { get; private set; } } }