/* * Author: Xees * FileName: TowerDefense.h * Description: This file is used for definitions and requirements of the tower defense system * Version: 2.1 * Status: Needs an Overhaul! */ #include "ScriptPCH.h" #include "TowerDefense.h" class TD_NPC : public CreatureScript { public: TD_NPC() : CreatureScript(TD_NPC_SCRIPT_NAME) {} bool OnGossipHello(Player *player, Creature *creature) { player->PlayerTalkClass->ClearMenus(); // Load instance script. TowerDefenseInstanceScript::TowerDefenseMapInstanceScript * instance = static_cast(creature->GetInstanceScript()); if(!instance){ sLog->outBasic("TowerDefense: there is no TowerDefenseMapInstanceScript for this creature On TD_NPC OnGossipHello();!"); return false; } if(instance->GetEventStatus() > TD_EVENT_STATUS_NONE){ ChatHandler(player).SendSysMessage(TD_SYSTEM_MSG_ALREADYRUNNING); return false; } instance->AddSimpleMenuItemToPlayer(player,TD_ICON_START_EVENT,TD_NPC_MENU_MAIN, GOSSIP_ACTION_INFO_DEF + 1,TD_NPC_TEXT_START_EVENT); player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); return true; } bool OnGossipSelect(Player *player, Creature *creature, uint32 sender, uint32 action) { player->PlayerTalkClass->ClearMenus(); // Load instance script. TowerDefenseInstanceScript::TowerDefenseMapInstanceScript* instance = static_cast(creature->GetInstanceScript()); if(!instance){ sLog->outBasic("TowerDefense: there is no TowerDefenseMapInstanceScript for this creature On TD_NPC OnGossipSelect();!"); return false; } switch(sender) { case TD_NPC_MENU_MAIN: if(instance->GetEventStatus() < TD_EVENT_STATUS_TELEPORT){ instance->AddSimpleMenuItemToPlayer(player,TD_ICON_START_EVENT,TD_NPC_MENU_EVENT_MODE, GOSSIP_ACTION_INFO_DEF + 1, TD_NPC_TEXT_START_EVENT); instance->AddSimpleMenuItemToPlayer(player,TD_ICON_DOT_EVENT,TD_NPC_MENU_SHOW_RECORDS, GOSSIP_ACTION_INFO_DEF + 1, TD_NPC_TEXT_SHOW_RECORDS); instance->AddSimpleMenuItemToPlayer(player,TD_ICON_EXIT,TD_NPC_MENU_EXIT, GOSSIP_ACTION_INFO_DEF + 1, TD_TEXT_EXIT); player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); }else{ player->GetSession()->SendNotification(TD_SYSTEM_MSG_STARTED_ALREADY); player->CLOSE_GOSSIP_MENU(); } break; case TD_NPC_MENU_EVENT_MODE: instance->AddSimpleMenuItemToPlayer(player,TD_ICON_DOT_EVENT,TD_NPC_MENU_START_EVENT, GOSSIP_ACTION_INFO_DEF + 51, TD_NPC_TEXT_EVENT_EASY); instance->AddSimpleMenuItemToPlayer(player,TD_ICON_START_EVENT,TD_NPC_MENU_START_EVENT, GOSSIP_ACTION_INFO_DEF + 52, TD_NPC_TEXT_EVENT_HARD); instance->AddSimpleMenuItemToPlayer(player,TD_ICON_START_EVENT,TD_NPC_MENU_START_EVENT, GOSSIP_ACTION_INFO_DEF + 53, TD_NPC_TEXT_EVENT_EXTREME); instance->AddSimpleMenuItemToPlayer(player,TD_ICON_EXIT,TD_NPC_MENU_EXIT, GOSSIP_ACTION_INFO_DEF + 1, TD_TEXT_EXIT); player->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE,creature->GetGUID()); break; case TD_NPC_MENU_START_EVENT: instance->StartEvent(player, action); player->CLOSE_GOSSIP_MENU(); break; case TD_NPC_MENU_SHOW_RECORDS: instance->GetRecords(player); player->CLOSE_GOSSIP_MENU(); break; case TD_NPC_MENU_EXIT: player->CLOSE_GOSSIP_MENU(); break; } return true; } }; class TD_ITEM : public ItemScript { public: TD_ITEM() : ItemScript(TD_ITEM_SCRIPT_NAME) {} bool OnUse(Player* player, Item* item, SpellCastTargets const& targets) { player->PlayerTalkClass->ClearMenus(); if(player->GetMapId() != TD_INSTANCE_MAP_ID) return false; // Load instance script. TowerDefenseInstanceScript::TowerDefenseMapInstanceScript* instance = static_cast(player->GetInstanceScript()); if(!instance){ sLog->outBasic("TowerDefense: there is no TowerDefenseMapInstanceScript for this item On TD_ITEM OnUse();!"); return false; } if(instance->GetEventStatus() < TD_EVENT_STATUS_TELEPORT){ ChatHandler(player).SendSysMessage(TD_SYSTEM_MSG_CANNOT_USE_OUTSIDE); return false; } instance->AddSimpleMenuItemToPlayer(player,TD_ICON_DOT_EVENT,TD_ITEM_MENU_MAIN, GOSSIP_ACTION_INFO_DEF + 1, TD_ITEM_TEXT_START_EVENT); player->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE,item->GetGUID()); return true; } bool OnGossipSelect(Player* player, Item* item, uint32 Sender, uint32 Action) { player->PlayerTalkClass->ClearMenus(); // Load instance script. TowerDefenseInstanceScript::TowerDefenseMapInstanceScript* instance = static_cast(player->GetInstanceScript()); if(!instance){ sLog->outBasic("TowerDefense: there is no TowerDefenseMapInstanceScript for this item On TD_ITEM OnGossipSelect();!"); return false; } switch(Sender) { case TD_ITEM_MENU_MAIN: if(instance->GetEventStatus() < TD_EVENT_STATUS_TELEPORT) instance->AddSimpleMenuItemToPlayer(player,TD_ICON_START_EVENT,TD_ITEM_MENU_SELECT_EVENT_MODE, GOSSIP_ACTION_INFO_DEF + 1, TD_NPC_TEXT_START_EVENT); if(instance && instance->GetEventStatus() >= TD_EVENT_STATUS_TELEPORT) { instance->AddSimpleMenuItemToPlayer(player,TD_ICON_DOT_EVENT,TD_ITEM_MENU_BASE_HEALTH, GOSSIP_ACTION_INFO_DEF + 1, TD_ITEM_TEXT_BASE_HEALTH,instance->GetBaseHealth()); instance->AddSimpleMenuItemToPlayer(player,TD_ICON_DOT_EVENT,TD_ITEM_MENU_CURRENT_RESOURCES, GOSSIP_ACTION_INFO_DEF + 1, TD_ITEM_TEXT_CURRENT_RESOURCES,instance->GetResources()); if(instance->GetEventStatus() < TD_EVENT_STATUS_RUNNING && instance->GetEventMode() == TD_EVENT_MODE_EASY){ instance->AddSimpleMenuItemToPlayer(player,TD_ICON_START_EVENT,TD_ITEM_MENU_START_WAVE, GOSSIP_ACTION_INFO_DEF + 1, TD_ITEM_TEXT_START_WAVE,instance->GetCurrentWaveId()+1); } instance->AddSimpleMenuItemToPlayer(player,TD_ICON_BUY_GUARD,TD_ITEM_MENU_SPAWN_TOWER_MENU, GOSSIP_ACTION_INFO_DEF + 1, TD_ITEM_TEXT_SPAWN_TOWER_MENU); instance->AddSimpleMenuItemToPlayer(player,TD_ICON_DECLINE,TD_ITEM_MENU_QUIT_EVENT, GOSSIP_ACTION_INFO_DEF + 1, TD_ITEM_TEXT_QUIT_EVENT); } else{ instance->AddSimpleMenuItemToPlayer(player,TD_ICON_DOT_EVENT,TD_ITEM_MENU_MAIN, GOSSIP_ACTION_INFO_DEF + 1, TD_ITEM_TEXT_NO_EVENT_DATA); } instance->AddSimpleMenuItemToPlayer(player,TD_ICON_EXIT,TD_ITEM_MENU_EXIT, GOSSIP_ACTION_INFO_DEF + 1, TD_TEXT_EXIT); player->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE,item->GetGUID()); break; case TD_ITEM_MENU_SELECT_EVENT_MODE: instance->AddSimpleMenuItemToPlayer(player,TD_ICON_START_EVENT,TD_ITEM_MENU_START_EVENT, GOSSIP_ACTION_INFO_DEF + 51, TD_NPC_TEXT_EVENT_EASY); instance->AddSimpleMenuItemToPlayer(player,TD_ICON_START_EVENT,TD_ITEM_MENU_START_EVENT, GOSSIP_ACTION_INFO_DEF + 52, TD_NPC_TEXT_EVENT_HARD); instance->AddSimpleMenuItemToPlayer(player,TD_ICON_START_EVENT,TD_ITEM_MENU_START_EVENT, GOSSIP_ACTION_INFO_DEF + 53, TD_NPC_TEXT_EVENT_EXTREME); instance->AddSimpleMenuItemToPlayer(player,TD_ICON_EXIT,TD_ITEM_MENU_EXIT, GOSSIP_ACTION_INFO_DEF + 1, TD_TEXT_EXIT); player->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE,item->GetGUID()); break; case TD_ITEM_MENU_START_EVENT: player->CLOSE_GOSSIP_MENU(); instance->StartEvent(player, Action); break; case TD_ITEM_MENU_BASE_HEALTH: OnGossipSelect(player, item, TD_ITEM_MENU_MAIN, GOSSIP_ACTION_INFO_DEF + 1); break; case TD_ITEM_MENU_CURRENT_RESOURCES: OnGossipSelect(player, item, TD_ITEM_MENU_MAIN, GOSSIP_ACTION_INFO_DEF + 1); break; case TD_ITEM_MENU_SPAWN_TOWER_MENU: instance->AddSimpleMenuItemToPlayer(player,TD_ICON_DOT_EVENT,TD_ITEM_MENU_SPAWN_TOWER_MENU, GOSSIP_ACTION_INFO_DEF + 1, TD_ITEM_TEXT_CURRENT_RESOURCES,instance->GetResources()); instance->GetGuardList(); instance->AddSimpleMenuItemToPlayer(player,TD_ICON_DOT_EVENT,TD_ITEM_MENU_MAIN, GOSSIP_ACTION_INFO_DEF + 1, TD_TEXT_GOBACK); instance->AddSimpleMenuItemToPlayer(player,TD_ICON_EXIT,TD_ITEM_MENU_EXIT, GOSSIP_ACTION_INFO_DEF + 1, TD_TEXT_EXIT); player->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE,item->GetGUID()); break; case TD_ITEM_MENU_SPAWN_TOWER_GUARD: player->CLOSE_GOSSIP_MENU(); instance->SpawnFromGuardList(Action); break; case TD_ITEM_MENU_START_WAVE: instance->StartNextWave(1000); // start wave right away player->CLOSE_GOSSIP_MENU(); break; case TD_ITEM_MENU_QUIT_EVENT: instance->HandleEventComplete(TD_EVENT_COMPLETE_QUIT); player->CLOSE_GOSSIP_MENU(); break; case TD_ITEM_MENU_EXIT: player->CLOSE_GOSSIP_MENU(); break; } return true; } }; class TD_MOB : public CreatureScript { public: TD_MOB() : CreatureScript(TD_MOB_SCRIPT_NAME) {} CreatureAI* GetAI(Creature* creature) const { return new TD_MOBAI (creature); } struct TD_MOBAI : public ScriptedAI { TD_MOBAI(Creature* creature) : instance(static_cast(creature->GetInstanceScript())), ScriptedAI(creature) {} Player* player; uint32 MobSpawnTimer, MobEffectTimer, MobPathId, MobPower, MobBounty, MobHealth, MobDmg, MobAttackSpeed, MobSpellId; bool MobStartedPath, MobIsRunning, MobIsAirMob; float MobAttackDistance, MobSpeed; MonsterInfo* monster; TowerDefenseInstanceScript::TowerDefenseMapInstanceScript* instance; void Reset() { if(!instance) return; player = instance->GetPlayer(); if(!player) return; MobSpawnTimer = 500; MobEffectTimer = 1000; MobStartedPath = false; monster = instance->GetMonsterByEntry(me->GetEntry()); if(!monster){ me->DespawnOrUnsummon(); sLog->outBasic("TowerDefense: creature with entry: [%u] has Script Name that is used by TowerDefense, but is not part of the wave: [%u].", me->GetEntry(), instance->GetCurrentWaveId()); }else{ MobPathId = monster->GetPathId(); MobPower = monster->GetPower(); MobBounty = monster->GetBounty(); MobIsRunning = monster->GetIsRunning(); MobIsAirMob = monster->GetIsAirMob(); MobHealth = monster->GetHealth(); MobDmg = monster->GetDamage(); MobAttackDistance = monster->GetAttackDistance(); MobAttackSpeed = monster->GetAttSpeed(); MobSpeed = monster->GetSpeed(); MobSpellId = monster->GetDefSpell(); } } /* All attackers creature template needs to have SET RegenHealth = 0 */ void InitOnCreate() { me->SetMaxHealth(MobHealth); me->SetHealth(me->GetMaxHealth()); SetSpeeds(MobSpeed); } void SetSpeeds(float speed) { me->SetSpeed(MOVE_WALK, speed, true); me->SetWalk(true); if(MobIsRunning){ me->SetSpeed(MOVE_RUN, speed, true); me->SetWalk(false); } if(MobIsAirMob){ me->SetSpeed(MOVE_FLIGHT,speed, true); me->SetWalk(false); } } bool WaveIsRunning() { if(!instance) return false; if(instance->GetEventStatus() >= TD_EVENT_STATUS_RUNNING) return true; return false; } void StartWalkingToEndPoint() { (MobPathId) ? me->GetMotionMaster()->MovePath(MobPathId,false) : sLog->outBasic("TowerDefense: Creature entry: [%u] could not load path id, please check path id in the database.", me->GetEntry()); } uint32 GetMobSpellByCastType(TDEventSpellCastType type) { if(!monster) return 0; return monster->GetSpellIdByCastType(type); } void CastOnSpawnSpells() { if(!player) return; if(GetMobSpellByCastType(TD_CAST_ON_SPAWN_CAST_SELF)) me->CastSpell(me, GetMobSpellByCastType(TD_CAST_ON_SPAWN_CAST_SELF), true); if(GetMobSpellByCastType(TD_CAST_ON_SPAWN_CAST_TARGET)) me->CastSpell(player, GetMobSpellByCastType(TD_CAST_ON_SPAWN_CAST_TARGET), true); } void DoGuardAOEEffects(Unit* guard) { if(!instance) return; uint32 AOESpell = instance->GetGuardSpellByCastType(guard->GetGUID(), TD_CAST_AOE_CAST_TARGET); guard->CastSpell(me, AOESpell, true); switch(AOESpell) { case 15571: // This is the case where a creature enters the radius of an ice tower, it causes the creatures speed to reset to a lower speed SetSpeeds(MobSpeed * 0.7f); break; } } void StopGuardAOEEffects(Unit* guard){ SetSpeeds(MobSpeed); } Unit* GetClosestAOEGuard() { if(!instance || !WaveIsRunning()) return NULL; Unit* target = me->SelectNearestTarget(5.0f); // TEMP for Ice Tower AOE effects if(target){ GuardInfo* guardInfo = instance->GetGuardByGuid(target->GetGUID()); if(guardInfo && guardInfo->Entry == target->GetEntry()) if(IsAOEGuard(target)) return target; } return NULL; } Unit* GetClosestGuard() { if(!instance || !WaveIsRunning()) return NULL; Unit* target = me->SelectNearestTarget(5.0f); // TEMP for Ice Tower AOE effects if(target){ GuardInfo* guardInfo = instance->GetGuardByGuid(target->GetGUID()); if(guardInfo && guardInfo->Entry == target->GetEntry()) return target; } return NULL; } bool IsAOEGuard(Unit* guard) { if(!guard) return false; if (QueryResult queryResult = CharacterDatabase.PQuery("SELECT spellAOECastTarget FROM custom_td_base_spells WHERE creatureEntry = '%u'", guard->GetEntry())) return true; return false; } void JustDied(Unit* killer) { if(!instance || !killer) return; instance->UpdateUnits(TD_EVENT_DEC, 1); instance->UpdateResources(TD_EVENT_INC,MobBounty); } void MovementInform(uint32 type, uint32 id) { if(!instance || !MobPathId) {return;} if (id == instance->GetLastPointInPath(MobPathId)){ instance->UpdateHealth(MobPower); instance->UpdateUnits(TD_EVENT_DEC, 1); me->DespawnOrUnsummon(); } return; } void CastDefaultSpell(Unit* target) { if(!target) return; if(me->GetDistance(target->GetPositionX(),target->GetPositionY(),target->GetPositionZ()) > 8) me->SetFacingTo(target->GetOrientation()); if(MobIsAirMob && instance->GetGuardIsAntiAir(target->GetGUID()) && MobSpellId) // if mob is air mob, and attacker is anti air, they can attack him me->CastSpell(target, MobSpellId, true); else if(!MobIsAirMob && instance->GetGuardIsAntiGround(target->GetGUID()) && MobSpellId) // if mob is ground mob, and attacker is ground, then attack me->CastSpell(target, MobSpellId, true); } void DamageDealt(Unit* target, uint32& damage, DamageEffectType damageType) { if (damageType != SPELL_DIRECT_DAMAGE) // don't do any spell damage, only specific damage return; damage = 0; } void DamageTaken(Unit* attacker, uint32& damage) { me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); // not attackable by player } bool GuardIsInDistance(Unit* guard) { if(!guard) return false; if (WaveIsRunning()){ if(me->GetDistance(guard->GetPositionX(),guard->GetPositionY(),guard->GetPositionZ()) <= MobAttackDistance) return true; } return false; } bool IsCasting() { if(me->HasUnitState(UNIT_STATE_CASTING)) return true; return false; } void UpdateAI(const uint32 diff) { if(!instance) return; Player* player = instance->GetPlayer(); if(!player || instance->GetEventStatus() < TD_EVENT_STATUS_TELEPORT) me->DespawnOrUnsummon(); if(!WaveIsRunning()) return; if (MobSpawnTimer <= diff && !MobStartedPath) { me->CastSpell(me, instance->GetSpellIdByUniqueId(1), true); InitOnCreate(); StartWalkingToEndPoint(); CastOnSpawnSpells(); MobStartedPath = true; } else MobSpawnTimer -= diff; if (MobEffectTimer <= diff) { Unit* guard = GetClosestAOEGuard(); // will need to rewrite the entire guard script, because this is just stupid. if(guard && me->GetDistance(guard->GetPositionX(),guard->GetPositionY(),guard->GetPositionZ()) > 8) StopGuardAOEEffects(guard); if(guard && me->GetDistance(guard->GetPositionX(),guard->GetPositionY(),guard->GetPositionZ()) < 8) DoGuardAOEEffects(guard); if (!WaveIsRunning() || IsCasting()) return; if(GuardIsInDistance(GetClosestGuard())) CastDefaultSpell(GetClosestGuard()); MobEffectTimer = 1000; } else MobEffectTimer -= diff; } }; }; class TD_GUARD : public CreatureScript { public: TD_GUARD() : CreatureScript(TD_GUARD_SCRIPT_NAME) {} bool OnGossipHello(Player* player, Creature* creature) { player->PlayerTalkClass->ClearMenus(); TowerDefenseInstanceScript::TowerDefenseMapInstanceScript* instance = static_cast(creature->GetInstanceScript()); if(!instance) return false; if(instance->GetEventStatus() < TD_EVENT_STATUS_TELEPORT){ ChatHandler(player).SendSysMessage(TD_SYSTEM_MSG_CANNOT_USE_OUTSIDE); return false; } instance->AddSimpleMenuItemToPlayer(player,TD_ICON_DOT_EVENT,TD_GUARD_MENU_MAIN, GOSSIP_ACTION_INFO_DEF + 1,TD_GUARD_TEXT_TOWER_NAME,creature->GetName()); player->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE,creature->GetGUID()); return true; } bool OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action) { player->PlayerTalkClass->ClearMenus(); TowerDefenseInstanceScript::TowerDefenseMapInstanceScript* instance = static_cast(creature->GetInstanceScript()); if(!instance) return false; switch(sender) { case TD_GUARD_MENU_MAIN: if(instance->GetEventStatus() >= TD_EVENT_STATUS_TELEPORT) { instance->AddSimpleMenuItemToPlayer(player,TD_ICON_DOT_EVENT,TD_GUARD_MENU_NAME, GOSSIP_ACTION_INFO_DEF + 1,TD_GUARD_TEXT_TOWER_NAME,creature->GetName()); instance->AddSimpleMenuItemToPlayer(player,TD_ICON_DOT_EVENT,TD_GUARD_MENU_CURRENT_RESOURCES, GOSSIP_ACTION_INFO_DEF + 1,TD_GUARD_TEXT_CURRENT_RESOURCES,instance->GetResources()); instance->UpgradeGuardMenu(creature->GetGUID()); instance->AddSimpleMenuItemToPlayer(player,TD_ICON_BUY_GUARD,TD_GUARD_MENU_SELL, GOSSIP_ACTION_INFO_DEF + 1,TD_GUARD_TEXT_SELL_GUARD,instance->GetGuardSellPrice(creature->GetGUID())); } instance->AddSimpleMenuItemToPlayer(player,TD_ICON_EXIT,TD_GUARD_MENU_EXIT, GOSSIP_ACTION_INFO_DEF + 1,TD_TEXT_EXIT); player->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE,creature->GetGUID()); break; case TD_GUARD_MENU_NAME: OnGossipSelect(player, creature, TD_GUARD_MENU_MAIN, GOSSIP_ACTION_INFO_DEF + 1); break; case TD_GUARD_MENU_CURRENT_RESOURCES: OnGossipSelect(player, creature, TD_GUARD_MENU_MAIN, GOSSIP_ACTION_INFO_DEF + 1); break; case TD_GUARD_MENU_UPGRADE: instance->UpgradeGuard(creature->GetGUID()); player->CLOSE_GOSSIP_MENU(); break; case TD_GUARD_MENU_UPGRADE_MAX: OnGossipSelect(player, creature, TD_GUARD_MENU_MAIN, GOSSIP_ACTION_INFO_DEF + 1); break; case TD_GUARD_MENU_SELL: instance->SellGuard(creature->GetGUID()); player->CLOSE_GOSSIP_MENU(); break; case TD_GUARD_MENU_EXIT: player->CLOSE_GOSSIP_MENU(); break; } return true; } CreatureAI* GetAI(Creature* creature) const { return new TD_GUARDAI (creature); } struct TD_GUARDAI : public ScriptedAI { TD_GUARDAI(Creature* creature) : instance(static_cast(creature->GetInstanceScript())), ScriptedAI(creature) {} uint32 SpawnTimer; uint32 SpellTimer; Unit* Target; bool Spawned; MonsterInfo* monster; TowerDefenseInstanceScript::TowerDefenseMapInstanceScript* instance; void Reset() { if(!instance){ sLog->outBasic("TowerDefense: there is no TowerDefenseMapInstanceScript for this creature On TD_GUARDAI Reset();!"); return; } Spawned = false; SpawnTimer = 200; SpellTimer = 1000; // need to allow db attack speed to be loaded before trying to fetch it. InitOnCreate(); } void InitOnCreate() { me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); me->setFaction(TD_FACTION_GUARD); // Set creature faction to friendly to all players but hostile to monsters me->SetReactState(REACT_AGGRESSIVE); } bool WaveIsRunning() { if(instance){ if (instance->GetEventStatus() >= TD_EVENT_STATUS_RUNNING) return true; } return false; } bool AttackerIsInWave(Unit* attacker) { if(!attacker) return false; if(instance->IsCreatureInWave(instance->GetCurrentWaveId(),attacker->GetEntry())) return true; return false; } bool AttackerIsInDistance(Unit* attacker) { if(!attacker) return false; if (WaveIsRunning()){ if(me->GetDistance(attacker->GetPositionX(),attacker->GetPositionY(),attacker->GetPositionZ()) <= instance->GetGuardAttackDistance(me->GetGUID())) return true; } return false; } bool IsCasting() { if(me->HasUnitState(UNIT_STATE_CASTING)) return true; return false; } Unit* GetClosestAttacker() { if(!instance || !WaveIsRunning()) return NULL; Unit* target = NULL; target = SelectTarget(SELECT_TARGET_TOPAGGRO,0,instance->GetGuardAttackDistance(me->GetGUID()),false); if(!target) target = me->SelectNearestTarget(instance->GetGuardAttackDistance(me->GetGUID())); if(target && AttackerIsInWave(target)){ me->AddThreat(target,100); return target; } return NULL; } uint32 GetDefaultDmg() { if(!instance) return 0; uint32 defDmg = instance->GetGuardDamage(me->GetGUID()); if(defDmg) return defDmg; else instance->RecordLog("TowerDefense: there is no default damage for guard entry: [%u].", me->GetEntry()); return 0; } uint32 GetDefaultSpell() { if(!instance) return 0; GuardInfo* guard = instance->GetGuardByGuid(me->GetGUID()); if(guard) return guard->GetDefSpell(); else instance->RecordLog("TowerDefense: there is no default spell id for guard entry: [%u].", me->GetEntry()); return 0; } void CastDefaultSpell(Unit* target) { if(!target) return; if(me->GetDistance(target->GetPositionX(),target->GetPositionY(),target->GetPositionZ()) > 8) me->SetFacingTo(target->GetOrientation()); if(!GetDefaultSpell()) return; monster = instance->GetMonsterByEntry(target->GetEntry()); if(!monster){ sLog->outBasic("TowerDefense: creature with entry: [%u] has Script Name that is used by TowerDefense, but is not part of the wave: [%u].", me->GetEntry(), instance->GetCurrentWaveId()); }else{ if(instance->GetGuardIsAntiAir(me->GetGUID()) && monster->GetIsAirMob()) // if guard is anti air, and attacker is air mob then cast me->CastSpell(target, GetDefaultSpell(), true); else if(instance->GetGuardIsAntiGround(me->GetGUID()) && !monster->GetIsAirMob()) // if guard is anti ground and attacker is ground then cast me->CastSpell(target, GetDefaultSpell(), true); } } void DamageDealt(Unit* target, uint32& damage, DamageEffectType damageType) { if (damageType != SPELL_DIRECT_DAMAGE) // guards cannot use spells for damage, only default damage return; damage = 0; } void DamageTaken(Unit* attacker, uint32& damage) { if(!instance || !WaveIsRunning()) return; if (!AttackerIsInWave(attacker)) damage = 0; } void SpellHitTarget(Unit* target, SpellInfo const* spell) { if(!target || !WaveIsRunning() || !spell) return; if(target->GetTypeId() == TYPEID_UNIT && AttackerIsInWave(target)){ me->DealDamage(target,GetDefaultDmg()); me->CastSpell(target,instance->GetGuardSpellByCastType(me->GetGUID(), TD_CAST_ON_HIT_TARGET_CAST_TARGET),true); } } void CastOnSpawnSpells() { if(!instance) return; Player* player = instance->GetPlayer(); if(!player) return; me->CastSpell(me, instance->GetGuardSpellByCastType(me->GetGUID(), TD_CAST_ON_SPAWN_CAST_SELF), true); } void UpdateAI(const uint32 diff){ if(!instance) return; Player* player = instance->GetPlayer(); if(!player || instance->GetEventStatus() < TD_EVENT_STATUS_TELEPORT) me->DespawnOrUnsummon(); if (SpawnTimer <= diff && !Spawned) { me->CastSpell(me, instance->GetSpellIdByUniqueId(1), true); CastOnSpawnSpells(); Spawned = true; } else SpawnTimer -= diff; if (SpellTimer <= diff) { if (!WaveIsRunning() || IsCasting()) return; if(AttackerIsInDistance(GetClosestAttacker())) CastDefaultSpell(GetClosestAttacker()); SpellTimer = instance->GetGuardAttSpeed(me->GetGUID()); } else SpellTimer -= diff; } }; }; class TD_UPDATER_TRIGGER : public CreatureScript { public: TD_UPDATER_TRIGGER() : CreatureScript(TD_UPDATER_SCRIPT_NAME) { } CreatureAI* GetAI(Creature* creature) const { return new TD_UPDATER_TRIGGERAI(creature); } struct TD_UPDATER_TRIGGERAI : public ScriptedAI { TD_UPDATER_TRIGGERAI(Creature* creature) : instance(static_cast(creature->GetInstanceScript())), ScriptedAI(creature) {} TowerDefenseInstanceScript::TowerDefenseMapInstanceScript* instance; uint32 SpawnTimer; uint32 UnitsCounter; Player* player; void Reset() { SpawnTimer = 1000; UnitsCounter = 0; InitOnCreate(); } void InitOnCreate() { me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_IMMUNE_TO_PC); me->SetWalk(false); me->setFaction(35); me->SetReactState(REACT_PASSIVE); } bool WaveIsLoaded() { if (!instance) return false; if (instance->GetEventStatus() == TD_EVENT_STATUS_LOADED) return true; return false; } void SpawnWaveCreatures() { if (!instance) return; WaveInfo* wave = instance->GetWaveById(instance->GetCurrentWaveId()); if(UnitsCounter < wave->GetNumCreatures()) { instance->SpawnNextCreature(me); instance->UpdateUnits(TD_EVENT_INC,1); UnitsCounter++; }else{ instance->SetEventStatus(TD_EVENT_STATUS_LOADED); instance->RecordLog("TowerDefense: Wave [%u] is now loaded for Event Id: [%u].", instance->GetCurrentWaveId(), instance->GetEventId()); UnitsCounter = 0; } } void UpdateAI(uint32 const diff) { if (!instance) return; player = instance->GetPlayer(); if(!player) return; instance->OnUpdate(diff); if (instance->GetEventStatus() < TD_EVENT_STATUS_LOADING) return; if(instance->GetUnits() == 0 && WaveIsLoaded()){ instance->SendMessageToPlayer(TD_SYSTEM_MSG_WAVE_HAS_ENDED, instance->GetCurrentWaveId()); instance->SetEventStatus(TD_EVENT_STATUS_TELEPORT); instance->RecordLog("Event Id: [%u] there are no spawned units in wave [%u], wave is no longer running.",instance->GetEventId(),instance->GetCurrentWaveId()); WaveInfo* wave = instance->GetWaveById(instance->GetCurrentWaveId()+1); if(!wave) // if no wave, this means the last wave is done, you can finish the event now! instance->HandleEventComplete(TD_EVENT_COMPLETE_WON); switch(instance->GetEventMode()) { case TD_EVENT_MODE_HARD: instance->SendMessageToPlayer(TD_SYSTEM_MSG_NEXT_WAVE_STARTS_IN_HARD, 30); instance->StartNextWave(30000); // start waves after 30 seconds break; case TD_EVENT_MODE_EXTREME: instance->StartNextWave(1000); // start waves right away break; } } if (!WaveIsLoaded()){ if (SpawnTimer <= diff) { SpawnWaveCreatures(); SpawnTimer = 1000; } else SpawnTimer -= diff; } } }; }; class TD_VENDOR : public CreatureScript { public: TD_VENDOR() : CreatureScript(TD_VENDOR_SCRIPT_NAME) {} bool OnGossipHello(Player *player, Creature *creature) { player->PlayerTalkClass->ClearMenus(); player->PlayerTalkClass->GetGossipMenu().AddMenuItem(-1, TD_ICON_DOT_EVENT, TD_VENDOR_TEXT_EXCHANGE, TD_VENDOR_MENU_MAIN, GOSSIP_ACTION_INFO_DEF + 1, "", false); player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); return true; } bool OnGossipSelect(Player *player, Creature *creature, uint32 sender, uint32 action) { player->PlayerTalkClass->ClearMenus(); switch(sender) { case TD_VENDOR_MENU_MAIN: player->PlayerTalkClass->GetGossipMenu().AddMenuItem(-1, TD_ICON_DOT_EVENT, TD_VENDOR_TEXT_EXCHANGE, TD_VENDOR_EXCHANGE, GOSSIP_ACTION_INFO_DEF + 1, "", false); player->PlayerTalkClass->GetGossipMenu().AddMenuItem(-1, TD_ICON_BUY_GUARD, TD_VENDOR_TEXT_LIST, TD_VENDOR_LIST, GOSSIP_ACTION_INFO_DEF + 1, "", false); player->PlayerTalkClass->GetGossipMenu().AddMenuItem(-1, TD_ICON_EXIT, TD_TEXT_EXIT, TD_VENDOR_MENU_EXIT, GOSSIP_ACTION_INFO_DEF + 1, "", false); player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); break; case TD_VENDOR_EXCHANGE: player->CLOSE_GOSSIP_MENU(); if(uint32 ResAmount = GetAmountOfResourcesPlayerHas(player)) { UpdatePlayerResources(player, TD_EVENT_DEC, ResAmount); player->AddItem(40752, ResAmount); ChatHandler(player).SendSysMessage("All your resources were exchanged."); }else ChatHandler(player).SendSysMessage("You don't have any resources!"); break; case TD_VENDOR_LIST: player->GetSession()->SendListInventory(creature->GetGUID()); break; case TD_VENDOR_MENU_EXIT: player->CLOSE_GOSSIP_MENU(); break; } return true; } uint32 GetAmountOfResourcesPlayerHas(Player* player) { if(!player) return 0; if(QueryResult queryResult = CharacterDatabase.PQuery("SELECT currentResources FROM custom_td_players WHERE playerGUID = '%u'", player->GetGUIDLow())) { Field* Fields = queryResult->Fetch(); return Fields[0].GetUInt32(); } return 0; } void UpdatePlayerResources(Player* player, TDEventUpdateType type, uint32 resValue) { if(!player) return; if(!type) return; uint32 newRes = 0; if(QueryResult queryResult = CharacterDatabase.PQuery("SELECT currentResources FROM custom_td_players WHERE playerGUID = '%u'", player->GetGUIDLow())) { Field* Fields = queryResult->Fetch(); uint32 oldRes = Fields[0].GetUInt32(); switch(type) { case TD_EVENT_INC: newRes = oldRes + resValue; break; case TD_EVENT_DEC: newRes = oldRes - resValue; break; } CharacterDatabase.PExecute("UPDATE custom_td_players SET currentResources = '%u' WHERE playerGUID = '%u'", newRes, player->GetGUIDLow()); } return; } }; void AddSC_TowerDefenseMisc() { new TD_NPC(); new TD_ITEM(); new TD_MOB(); new TD_GUARD(); new TD_VENDOR(); new TD_UPDATER_TRIGGER(); }