eluna-scripts-ornfelt/lua/WowEmulationScriptPack/C++/TowerDefenseInstance.cpp

1097 lines
43 KiB
C++
Raw Permalink Normal View History

2023-08-16 00:50:59 +02:00
/*
* 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"
const char *TowerDefensePlayerStatFields[TD_PLAYER_STAT_MAX] =
{
"playerGUID",
"eventsLost",
"eventsWon",
"eventsUnfinished",
"currentResources",
};
void TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::LoadConfigData()
{
QueryResult queryResult = CharacterDatabase.Query("SELECT * FROM custom_td_config");
if(!queryResult){
RecordLog("TowerDefense: Unable to load tables required, please check your character database for the necessary tables.");
return;
}else{
Field *Fields = queryResult->Fetch();
/*
0 1 2 3 4 5 6 7 8 9 10 11 12
(`pSpawnX`, `pSpawnY`, `pSpawnZ`, `pSpawnO`, `minLvl`, `awardFled`, `disableGM`, `startResources`, `disableEvent`, `buildItemEntry`, `quitAfterWave`, `disableFileLog`, `gobPlatformEntry`)
*/
pSpawnX = Fields[0].GetFloat();
pSpawnY = Fields[1].GetFloat();
pSpawnZ = Fields[2].GetFloat();
pSpawnO = Fields[3].GetFloat();
minLvl = Fields[4].GetUInt32();
awardFled = Fields[5].GetBool();
disableGM = Fields[6].GetBool();
startResources = Fields[7].GetUInt32();
disableEvent = Fields[8].GetBool();
buildItemEntry = Fields[9].GetUInt32();
quitAfterWave = Fields[10].GetUInt32();
disableFileLog = Fields[11].GetBool();
gobPlatformEntry = Fields[12].GetUInt32();
}
if(QueryResult IdResult = CharacterDatabase.Query("SELECT MAX(Id) FROM custom_td_events"))
_highEventId = IdResult->Fetch()[0].GetUInt32();
else
_highEventId = 0;
CountDown = 0;
RecordLog("TowerDefense: Configuration table was loaded.");
return;
}
void TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::LoadPastEvents()
{
if(QueryResult queryResult = CharacterDatabase.Query("SELECT * FROM custom_td_events"))
{
do
{
// If there is a result, then create new event data, and set it.
if(queryResult)
{
Field* pFields = queryResult->Fetch();
uint32 playerGUID = pFields[1].GetUInt32();
uint32 currentResources = pFields[3].GetUInt32();
bool finished = pFields[6].GetBool();
if(!finished)
{
if(QueryResult queryResult = CharacterDatabase.PQuery("SELECT * FROM custom_td_players WHERE playerGUID = '%u'", playerGUID)){
UpdatePlayerStats(playerGUID, TD_PLAYER_STAT_EVENTS_UNFINISHED, 1);
UpdatePlayerStats(playerGUID, TD_PLAYER_STAT_CURRENT_RESOURCES, currentResources);
}
else{
CharacterDatabase.PExecute("REPLACE INTO custom_td_players (playerGUID, eventsUnfinished, currentResources) values('%u','1','%u');",playerGUID,currentResources);
RecordLog("TowerDefense: Inserted stats for Player GUID: [%u], added [%u] resources and 1 unfinished events.", playerGUID,currentResources);
}
SendMailToPlayer(NULL, playerGUID, TD_SYSTEM_MSG_MAIL_BODY_EVENT_UNFINISHED, currentResources);
continue;
}
}
}while(queryResult->NextRow());
CharacterDatabase.PExecute("DELETE FROM custom_td_events WHERE eventFinished = '0'");
RecordLog("TowerDefense: All past unfinished events were deleted, and their information was sent to the players.");
}
else if(!queryResult)
RecordLog("TowerDefense: No past events exist!");
}
/*
* This is used to spawn creatures
*/
void TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::OnUpdate(uint32 const diff)
{
if(GetEventStatus() < TD_EVENT_STATUS_RUNNING) // if wave is not running
return;
Events.Update(diff);
Player* player = GetPlayer();
if(!player)
return;
while (uint32 eventId = Events.ExecuteEvent())
{
switch (eventId)
{
case TD_EVENT_DATA_START_MUSIC_TIMER: // this is just some event music to make it feel alive.
{
Events.RescheduleEvent(TD_EVENT_DATA_START_MUSIC_TIMER, 240000); // repeat every 4 min, length of track
}
break;
case TD_EVENT_DATA_START_WAVE_TIMER:
{
switch(CountDown)
{
case 0:
player->PlayDirectSound(TD_STARTWAVE_MUSIC,player);
SendMessageToPlayer(TD_SYSTEM_MSG_WAVE_STARTING_IN,3);
Events.RescheduleEvent(TD_EVENT_DATA_START_WAVE_TIMER, 1000);
++CountDown;
break;
case 1:
SendMessageToPlayer(TD_SYSTEM_MSG_WAVE_STARTING,2);
Events.RescheduleEvent(TD_EVENT_DATA_START_WAVE_TIMER, 1000);
++CountDown;
break;
case 2:
player->PlayDirectSound(TD_STARTWAVE_MUSIC,player);
SendMessageToPlayer(TD_SYSTEM_MSG_WAVE_STARTING,1);
Events.CancelEvent(TD_EVENT_DATA_START_WAVE_TIMER);
Events.ScheduleEvent(TD_EVENT_DATA_START_WAVE_MODE, 1000);
++CountDown;
break;
}
}
break;
case TD_EVENT_DATA_START_WAVE_MODE:
{
CountDown = 0;
SetEventStatus(TD_EVENT_STATUS_LOADING); // set wave status to running so the boss npc can start spawning
SendMessageToPlayer(TD_SYSTEM_MSG_WAVE_STARTING_GO);
Events.CancelEvent(TD_EVENT_DATA_START_WAVE_MODE);
RecordLog("TowerDefense: Wave Id: [%u] is now running.", GetCurrentWaveId());
}
break;
}
}
}
/*
* Here, you can arrange mob spawns, add more mobs to the wave, remove mobs, add more waves, remove waves.
*/
void TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::SetupWaves()
{
CountDown = 0;
if(QueryResult waveResult = CharacterDatabase.PQuery("SELECT waveId, isBossWave FROM custom_td_waves"))
{
do
{
Field *Fields = waveResult->Fetch();
uint32 waveId = Fields[0].GetUInt32();
uint32 isBossWave = Fields[1].GetBool();
WaveInfo* Wave = new WaveInfo(waveId, isBossWave);
if(QueryResult creatureResult = CharacterDatabase.PQuery("SELECT creatureEntry, creatureAmount, pathId FROM custom_td_waves_data WHERE waveId = '%u' ORDER BY creatureOrder ASC", waveId))
{
do
{
Field *Fields = creatureResult->Fetch();
uint32 creatureEntry = Fields[0].GetUInt32();
uint32 creatureAmount = Fields[1].GetUInt32();
uint32 creaturePath = Fields[2].GetUInt32();
for (uint32 i = 0; i < creatureAmount ; ++i)
{
MonsterInfo* monster = Wave->AddMonster(creatureEntry);
if (Monsters.find(creatureEntry) == Monsters.end())
Monsters[creatureEntry] = monster;
monster->LoadBaseStats();
}
}while(creatureResult->NextRow());
}
Waves[waveId] = Wave;
}while(waveResult->NextRow());
}
}
/*
* This occurs when the player clicks on TD_ITEM(); TD_ITEM_MENU_START_WAVE OnGossipSelect, it starts counting.
* wave starting in 3... 2... 1... Wave Started.
* Timer that sends a message to the player every second, decreasing the time till wave is spawned, once it reaches zero. then it sets wave running to true.
*/
void TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::StartNextWave(uint32 timer)
{
uint32 waveId = GetCurrentWaveId();
SetCurrentWaveId(++waveId);
SetEventStatus(TD_EVENT_STATUS_RUNNING);
Events.ScheduleEvent(TD_EVENT_DATA_START_MUSIC_TIMER, 200000); // some music
Events.ScheduleEvent(TD_EVENT_DATA_START_WAVE_TIMER, timer); // wave countdown
RecordLog("TowerDefense: Wave Id: [%u] has been scheduled to run in %u milliseconds.", GetCurrentWaveId(), timer);
}
void TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::StartEvent(Player* player, uint32 Action)
{
if (!player || player->isInCombat() || player->GetSession()->isLogingOut() || player->GetGroup()){
ChatHandler(player).SendSysMessage(TD_SYSTEM_MSG_CANNOT_START_DUE);
return;
}
if(GetPlayer() && player->GetGUIDLow() != GetPlayer()->GetGUIDLow()){
ChatHandler(player).SendSysMessage(TD_SYSTEM_MSG_CANNOT_START_ALREADY);
return;
}else{
SetPlayer(player);
}
if(!player)
return;
player->PlayDirectSound(TD_STARTEVENT_MUSIC,player);
SetupEventData();
player->AddItem(34131, 1); // add the spawning item to the player
player->SaveToDB(); // save the player to the db in case of crash
player->TeleportTo(GetMapId(), GetPSpawnX(), GetPSpawnY(), GetPSpawnZ(), GetPSpawnO()); // teleport to starting location
SetEventStatus(TD_EVENT_STATUS_TELEPORT);
switch(Action)
{
case 1051: // Easy
SetEventMode(TD_EVENT_MODE_EASY);
SendMessageToPlayer(TD_SYSTEM_MSG_STARTED_EVENT_EASY);
// wait for user input to start wave
break;
case 1052: // Hard
SetEventMode(TD_EVENT_MODE_HARD);
SendMessageToPlayer(TD_SYSTEM_MSG_STARTED_EVENT_HARD);
StartNextWave(30000); // start wave from the start within 30 seconds
break;
case 1053: // Extreme
SetEventMode(TD_EVENT_MODE_EXTREME);
SendMessageToPlayer(TD_SYSTEM_MSG_STARTED_EVENT_EXTREME);
StartNextWave(1000); // start waves right away
break;
}
RecordLog("TowerDefense: Player Name: [%s] has started the tower defense event. With Event Id: [%u]",player->GetName(), GetEventId());
}
void TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::SpawnNextCreature(Creature* spawner)
{
WaveInfo* wave = GetWaveById(GetCurrentWaveId());
if(wave)
wave->Spawn(GetUnits(), spawner);
else
sLog->outBasic("TowerDefense: Wave Id [%u] does not have correct loading data in the db. the core could not load the wave.", GetCurrentWaveId());
}
void TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::SpawnGuard(uint32 entry)
{
if (!entry)
return;
Player *player = GetPlayer();
if(!player)
return;
GuardInfo* guard = new GuardInfo();
bool found = false;
float objectX, objectY, objectZ, objectO;
uint32 objectLowGUID, objectId;
uint16 objectMap, objectPhase;
uint32 objectPool;
GameEventMgr::ActiveEvents const& activeEventsList = sGameEventMgr->GetActiveEventList();
std::ostringstream eventFilter;
eventFilter << " AND (eventEntry IS NULL ";
bool initString = true;
for (GameEventMgr::ActiveEvents::const_iterator itr = activeEventsList.begin(); itr != activeEventsList.end(); ++itr)
{
if (initString)
{
eventFilter << "OR eventEntry IN (" << *itr;
initString = false;
}
else
eventFilter << ',' << *itr;
}
if (!initString)
eventFilter << "))";
else
eventFilter << ')';
QueryResult queryResult = WorldDatabase.PQuery("SELECT gameobject.guid, id, position_x, position_y, position_z, orientation, map, phaseMask, "
"(POW(position_x - %f, 2) + POW(position_y - %f, 2) + POW(position_z - %f, 2)) AS order_ FROM gameobject "
"LEFT OUTER JOIN game_event_gameobject on gameobject.guid = game_event_gameobject.guid WHERE map = '%i' %s ORDER BY order_ ASC LIMIT 10",
player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(),player->GetMapId(), eventFilter.str().c_str());
if (!queryResult)
{
SendMessageToPlayer(TD_SYSTEM_MSG_NO_NEARBY_PLATFORM); // if there were no nearby platforms to place guards.
return;
}
do
{
Field* fields = queryResult->Fetch();
objectLowGUID = fields[0].GetUInt32();
objectId = fields[1].GetUInt32();
objectX = fields[2].GetFloat();
objectY = fields[3].GetFloat();
objectZ = fields[4].GetFloat();
objectO = fields[5].GetFloat();
objectMap = fields[6].GetUInt16();
objectPhase = fields[7].GetUInt16();
objectPool = sPoolMgr->IsPartOfAPool<GameObject>(objectLowGUID);
if (!objectPool || sPoolMgr->IsSpawnedObject<GameObject>(objectLowGUID))
found = true;
} while (queryResult->NextRow() && !found);
if (!found)
{
SendMessageToPlayer(TD_SYSTEM_MSG_NEARBY_PLATFORM_NOT_EXIST); // no nearby platform found
return;
}
GameObjectTemplate const* objectInfo = sObjectMgr->GetGameObjectTemplate(objectId);
if (!objectInfo)
{
SendMessageToPlayer(TD_SYSTEM_MSG_NEARBY_PLATFORM_NOT_EXIST); // nearby platform is not an actual object
return;
}
guard->Entry = entry;
guard->X = objectX;
guard->Y = objectY;
guard->Z = objectZ + 0.5; // depends on size of platform object
guard->O = objectO;
if(player->GetDistance(objectX,objectY,objectZ) > 3)
{
SendMessageToPlayer(TD_SYSTEM_MSG_NEARBY_PLATFORM_TOO_FAR);
return;
}else
{
if(QueryResult queryResult = CharacterDatabase.PQuery("SELECT creatureEntry FROM custom_td_base_stats WHERE creatureType = 'Guard' ORDER BY Id ASC LIMIT 1"))
{
do
{
Field *Fields = queryResult->Fetch();
uint32 creatureEntry = Fields[0].GetUInt32();
Creature* creature = player->FindNearestCreature(creatureEntry,2,true);
if(creature){
SendMessageToPlayer(TD_SYSTEM_MSG_NEARBY_GUARD_TOO_CLOSE);
return;
}
}while(queryResult->NextRow());
}
if(QueryResult queryResult = CharacterDatabase.PQuery("SELECT creatureName, creatureEntry, creatureCost FROM custom_td_base_stats WHERE creatureEntry = '%u'", entry))
{
Field *Fields = queryResult->Fetch();
std::string creatureName = Fields[0].GetString();
uint32 creatureEntry = Fields[1].GetUInt32();
uint32 creatureCost = Fields[2].GetUInt32();
if(GetResources() < creatureCost)
{
SendMessageToPlayer(TD_SYSTEM_MSG_MORE_RESOURCES, creatureCost - GetResources());
return;
}else
{
guard->Spawn(player);
Guards[guard->Guid] = guard;
guard->SetDefSpell(guard->GetSpellIdByCastType(TD_CAST_DEFAULT_CAST_TARGET)); // set default spell
UpdateResources(TD_EVENT_DEC,creatureCost);
SendMessageToPlayer(TD_SYSTEM_MSG_BOUGHT_GUARD, creatureName.c_str(), creatureCost);
RecordLog("TowerDefense: Event ID: [%u] has spawned Guard Entry: [%u] for [%u] resources.", GetEventId(), guard->Entry,creatureCost);
}
}
}
}
void TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::UpgradeGuardMenu(uint64 guid)
{
if(!guid)
return;
Player *player = GetPlayer();
if(!player)
return;
uint32 currentLevel = 0;
uint32 entry = GUID_ENPART(guid);
std::string upgradeText = "";
if (Guards.find(guid) != Guards.end())
{
GuardInfo* guard = Guards[guid];
currentLevel = guard->Level;
uint32 newLevel = currentLevel +1;
if(QueryResult queryResult = CharacterDatabase.PQuery("SELECT creatureName, creatureEntry, creatureCost FROM custom_td_base_stats WHERE creatureEntry = '%u'", entry))
{
uint32 UpgradeCost = guard->GetUpgradeCost();
switch(GetEventMode())
{
case TD_EVENT_MODE_HARD:
UpgradeCost = UpgradeCost + (UpgradeCost/4);
break;
case TD_EVENT_MODE_EXTREME:
UpgradeCost = UpgradeCost + (UpgradeCost/2);
break;
}
if(QueryResult queryResult = CharacterDatabase.PQuery("SELECT * FROM custom_td_base_levels WHERE creatureEntry = '%u' AND creatureLevel = '%u'", entry, newLevel))
{
Field* Fields = queryResult->Fetch();
upgradeText = Fields[3].GetString();
AddSimpleMenuItemToPlayer(player,TD_ICON_UPGRADE_GUARD,TD_GUARD_MENU_UPGRADE, GOSSIP_ACTION_INFO_DEF + 1,upgradeText.c_str(),newLevel,UpgradeCost);
}else
AddSimpleMenuItemToPlayer(player,TD_ICON_DOT_EVENT,TD_GUARD_MENU_UPGRADE, GOSSIP_ACTION_INFO_DEF + 1,TD_GUARD_TEXT_UPGRADE_LEVEL_MAX);
}
}
}
void TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::UpgradeGuard(uint64 guid)
{
if(!guid)
return;
Player* player = GetPlayer();
if(!player)
return;
Creature* creature = player->GetMap()->GetCreature(guid);
if(!creature)
return;
uint32 currentLevel = 0;
uint32 entry = GUID_ENPART(guid);
if (Guards.find(guid) != Guards.end())
{
GuardInfo* guard = Guards[guid];
currentLevel = guard->Level;
uint32 newLevel = currentLevel +1;
if(QueryResult queryResult = CharacterDatabase.PQuery("SELECT creatureName, creatureEntry, creatureCost FROM custom_td_base_stats WHERE creatureEntry = '%u'", entry))
{
uint32 UpgradeCost = guard->GetUpgradeCost();
switch(GetEventMode())
{
case TD_EVENT_MODE_HARD:
UpgradeCost = UpgradeCost + (UpgradeCost/4);
break;
case TD_EVENT_MODE_EXTREME:
UpgradeCost = UpgradeCost + (UpgradeCost/2);
break;
}
if(GetResources() < UpgradeCost){
SendMessageToPlayer(TD_SYSTEM_MSG_MORE_RESOURCES_UPG, UpgradeCost - GetResources());
return;
}
if(QueryResult queryResult = CharacterDatabase.PQuery("SELECT * FROM custom_td_base_levels WHERE creatureEntry = '%u' AND creatureLevel = '%u'", entry, newLevel))
{
creature->CastSpell(creature, GetSpellIdByUniqueId(4),true); // upgrade level visual
guard->SetLevel(newLevel);
UpdateResources(TD_EVENT_DEC,UpgradeCost); // remove resource cost from player
creature->RemoveAllAuras(); // remove all auras to apply new ones.
SendMessageToPlayer(TD_SYSTEM_MSG_UPGRADED_TOWER_FOR, creature->GetName(),UpgradeCost);
Field* Fields = queryResult->Fetch();
uint32 newDefaultSpell = Fields[4].GetUInt32();
if(newDefaultSpell)
guard->SetDefSpell(newDefaultSpell);
uint32 newAttackSpeed = Fields[9].GetUInt32();
if (newAttackSpeed)
guard->SetAttSpeed(newAttackSpeed);
float newAttackDistance = Fields[8].GetFloat();
if(newAttackDistance)
guard->SetAttackDistance(newAttackDistance);
uint32 newAura = Fields[7].GetUInt32();
if(newAura)
creature->CastSpell(creature,newAura,true);
uint32 newDisplay = Fields[6].GetUInt32();
if(newDisplay)
creature->SetDisplayId(newDisplay);
float newScale = Fields[5].GetFloat();
if(newScale)
creature->SetObjectScale(newScale);
uint32 newDamage = Fields[10].GetUInt32();
if (newDamage)
guard->SetDamage(newDamage);
uint32 newIsAntiAir = Fields[11].GetBool();
if (newIsAntiAir)
guard->SetIsAntiAir(newIsAntiAir);
uint32 newIsAntiGround = Fields[12].GetBool();
if (newIsAntiGround)
guard->SetIsAntiGround(newIsAntiGround);
}
}
}
}
void TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::SellGuard(uint64 guid)
{
if(!guid)
return;
Player* player = GetPlayer();
if(!player)
return;
Creature* creature = player->GetMap()->GetCreature(guid);
if(!creature)
return;
uint32 entry = GUID_ENPART(guid);
if (Guards.find(guid) != Guards.end())
{
GuardInfo* guard = Guards[guid];
if(QueryResult queryResult = CharacterDatabase.PQuery("SELECT creatureName, creatureEntry, creatureCost FROM custom_td_base_stats WHERE creatureEntry = '%u'", entry))
{
creature->CastSpell(creature, GetSpellIdByUniqueId(5),true);
uint32 SellPrice = guard->GetSellPrice(guard->GetLevel());
switch(GetEventMode())
{
case TD_EVENT_MODE_HARD:
SellPrice = SellPrice - (SellPrice/4);
break;
case TD_EVENT_MODE_EXTREME:
SellPrice = SellPrice - (SellPrice/2);
break;
}
UpdateResources(TD_EVENT_INC,SellPrice);
SendMessageToPlayer(TD_SYSTEM_MSG_SOLD_TOWER_FOR, creature->GetName(),SellPrice);
creature->DisappearAndDie();
Guards.erase(guid);
}
}
}
void TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::GetGuardList()
{
Player* player = GetPlayer();
if(!player)
return;
if(QueryResult queryResult = CharacterDatabase.PQuery("SELECT creatureName, creatureEntry, creatureCost FROM custom_td_base_stats WHERE creatureType = 'Guard' ORDER BY Id ASC"))
{
do
{
Field *Fields = queryResult->Fetch();
std::string creatureName = Fields[0].GetString();
uint32 creatureEntry = Fields[1].GetUInt32();
uint32 creatureCost = Fields[2].GetUInt32();
AddSimpleMenuItemToPlayer(player,TD_ICON_BUY_GUARD,TD_ITEM_MENU_SPAWN_TOWER_GUARD, GOSSIP_ACTION_INFO_DEF + creatureEntry, TD_ITEM_TEXT_SPAWN_TOWER_GUARD, creatureName.c_str(), creatureCost);
}while(queryResult->NextRow());
}
}
void TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::SpawnFromGuardList(uint32 Action)
{
Player* player = GetPlayer();
if(!player || !Action)
return;
uint32 Entry = Action - GOSSIP_ACTION_INFO_DEF;
SpawnGuard(Entry);
}
uint32 TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::GetSpellIdByUniqueId(uint32 uniqueId)
{
if(QueryResult queryResult = CharacterDatabase.PQuery("SELECT spellDefaultCastTarget FROM custom_td_base_spells WHERE uniqueId = '%u'", uniqueId))
{
Field* Fields = queryResult->Fetch();
return Fields[0].GetUInt32();
}else
RecordLog("TowerDefense: unable to get system spell by unique Id: [%u]", uniqueId);
return 0;
}
MonsterInfo* TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::GetMonsterByEntry(uint32 Entry)
{
if(!Entry)
return NULL;
if (Monsters.find(Entry) != Monsters.end())
{
MonsterInfo* monster = Monsters[Entry];
return monster;
}else
RecordLog("No monster data found for entry [%u]", Entry);
return NULL;
}
GuardInfo* TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::GetGuardByGuid(uint64 guid)
{
if(guid){
if (Guards.find(guid) != Guards.end())
{
GuardInfo* guard = Guards[guid];
return guard;
}else
RecordLog("No creature data found for guid [%u]", guid);
}
return NULL;
}
uint32 TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::GetGuardResourceCost(uint32 entry)
{
if(!entry)
return 0;
if(QueryResult queryResult = CharacterDatabase.PQuery("SELECT creatureCost FROM custom_td_base_stats WHERE creatureEntry = '%u'", entry))
{
Field* Fields = queryResult->Fetch();
if(uint32 defCost = Fields[0].GetUInt32()){
switch(GetEventMode())
{
case TD_EVENT_MODE_HARD:
defCost = defCost + (defCost/4);
break;
case TD_EVENT_MODE_EXTREME:
defCost = defCost + (defCost/2);
break;
}
return defCost;
}
}
return 0;
}
bool TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::GetGuardIsAntiAir(uint64 guid)
{
if(guid){
GuardInfo* guard = GetGuardByGuid(guid);
if(guard)
return guard->GetIsAntiAir();
else
RecordLog("Could not find GetGuardIsAntiAir by Guard Guid [%u].", guid);
}
return 0;
}
bool TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::GetGuardIsAntiGround(uint64 guid)
{
if(guid){
GuardInfo* guard = GetGuardByGuid(guid);
if(guard)
return guard->GetIsAntiGround();
else
RecordLog("Could not find GetGuardIsAntiGround by Guard Guid [%u].", guid);
}
return 0;
}
uint32 TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::GetGuardSpellByCastType(uint64 guid, TDEventSpellCastType type)
{
if(guid){
GuardInfo* guard = GetGuardByGuid(guid);
if(guard)
return guard->GetSpellIdByCastType(type);
else
RecordLog("Could not find GetGuardSpellByCastType by Guard Guid [%u].", guid);
}
return 0;
}
uint32 TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::GetGuardAttSpeed(uint64 guid)
{
if(guid){
GuardInfo* guard = GetGuardByGuid(guid);
if(guard)
return guard->GetAttSpeed();
else
RecordLog("Could not find GetGuardAttSpeed by Guard Guid [%u].", guid);
}
return 0;
}
uint32 TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::GetGuardDamage(uint64 guid)
{
if(guid){
GuardInfo* guard = GetGuardByGuid(guid);
if(guard){
uint32 defDmg = guard->GetDamage();
switch(GetEventMode())
{
case TD_EVENT_MODE_HARD:
defDmg = defDmg - (defDmg/4);
break;
case TD_EVENT_MODE_EXTREME:
defDmg = defDmg - (defDmg/2);
break;
}
return defDmg;
}
else
RecordLog("Could not find GetGuardDamage by Guard Guid [%u].", guid);
}
return 0;
}
float TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::GetGuardAttackDistance(uint64 guid)
{
if(guid){
GuardInfo* guard = GetGuardByGuid(guid);
if(guard)
return guard->GetAttackDistance();
else
RecordLog("Could not find GetGuardAttackDistance by Guard Guid [%u].", guid);
}
return 0;
}
uint32 TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::GetGuardUpgradeCost(uint64 guid)
{
if(guid){
GuardInfo* guard = GetGuardByGuid(guid);
if(guard){
uint32 UpgradeCost = guard->GetUpgradeCost();
switch(GetEventMode())
{
case TD_EVENT_MODE_HARD:
UpgradeCost = UpgradeCost + (UpgradeCost/4);
break;
case TD_EVENT_MODE_EXTREME:
UpgradeCost = UpgradeCost + (UpgradeCost/2);
break;
}
return UpgradeCost;
}
else
RecordLog("Could not find GetGuardUpgradeCost by Guard Guid [%u].", guid);
}
return 0;
}
uint32 TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::GetGuardSellPrice(uint64 guid)
{
if(guid){
GuardInfo* guard = GetGuardByGuid(guid);
if(guard){
uint32 SellPrice = guard->GetSellPrice(guard->GetLevel());
switch(GetEventMode())
{
case TD_EVENT_MODE_HARD:
SellPrice = SellPrice - (SellPrice/4);
break;
case TD_EVENT_MODE_EXTREME:
SellPrice = SellPrice - (SellPrice/2);
break;
}
return SellPrice;
}
else
RecordLog("Could not find GetGuardUpgradeCost by Guard Guid [%u].", guid);
}
return 0;
}
uint32 TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::GetLastPointInPath(uint32 pathId)
{
uint32 lastPoint = 0;
if (QueryResult queryResult = WorldDatabase.PQuery("SELECT MAX(point) FROM waypoint_data WHERE id = '%u'", pathId)){
lastPoint = queryResult->Fetch()[0].GetUInt32();
return --lastPoint;
}else
RecordLog("TowerDefense: There is no waypoint data for GetLastPointInPath() for path [%u].", pathId);
return 0;
}
void TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::HandleEventComplete(TDEventCompleteType completeType)
{
Player *player = GetPlayer();
if(!player)
return;
if(!completeType)
return;
switch(completeType)
{
case TD_EVENT_COMPLETE_UNFINISHED:
{
if(IsAwardingFledPlayers()){
UpdatePlayerStats(GetPlayerGUID(), TD_PLAYER_STAT_CURRENT_RESOURCES, GetResources());
SendMailToPlayer(NULL, GetPlayerGUID(), TD_SYSTEM_MSG_MAIL_BODY_EVENT_UNFINISHED, GetResources());
RecordLog("TowerDefense: Player: [%s] has received: [%u] resources due to an unfinished Event Id: [%u].", player->GetName(), GetResources(), GetEventId());
}else{
SendMailToPlayer(NULL, GetPlayerGUID(), TD_SYSTEM_MSG_MAIL_BODY_EVENT_UNFINISHED_FLED, GetResources(), GetCurrentWaveId());
RecordLog("TowerDefense: Player: [%s] was informed that he lost all his unfinished Event Id: [%u] rewards.", player->GetName(), GetResources(), GetEventId());
}
UpdatePlayerStats(player->GetGUIDLow(), TD_PLAYER_STAT_EVENTS_UNFINISHED, 1);
}break;
case TD_EVENT_COMPLETE_QUIT:
{
if(player->GetSession()->isLogingOut()){
SendMessageToPlayer(TD_SYSTEM_MSG_LOGGING_OUT);
return;
}
if(GetCurrentWaveId() < GetQuitAfterWave()){
uint32 remaining = GetQuitAfterWave() - GetCurrentWaveId();
SendMessageToPlayer(TD_SYSTEM_MSG_MORE_WAVES, remaining);
return;
}
SendMessageToPlayer(TD_SYSTEM_MSG_QUIT_EVENT, GetResources(), GetCurrentWaveId());
UpdatePlayerStats(GetPlayerGUID(), TD_PLAYER_STAT_CURRENT_RESOURCES, GetResources());
UpdatePlayerStats(player->GetGUIDLow(), TD_PLAYER_STAT_EVENTS_LOST, 1);
RecordLog("TowerDefense: Player: [%s] has received: [%u] resources after leaving Event Id: [%u].", player->GetName(), GetResources(), GetEventId());
}break;
case TD_EVENT_COMPLETE_LOST:
{
player->PlayDirectSound(TD_ENDEVENT_MUSIC,_player);
SendMessageToPlayer(TD_SYSTEM_MSG_LOST_EVENT, GetResources(), GetCurrentWaveId());
UpdatePlayerStats(player->GetGUIDLow(), TD_PLAYER_STAT_EVENTS_LOST, 1);
RecordLog("TowerDefense: Player: [%s] was informed that he lost all his Event Id: [%u] rewards.", player->GetName(), GetResources(), GetEventId());
}break;
case TD_EVENT_COMPLETE_WON:
{
SendMessageToPlayer(TD_SYSTEM_MSG_WON_EVENT, GetResources());
UpdatePlayerStats(GetPlayerGUID(), TD_PLAYER_STAT_CURRENT_RESOURCES, GetResources());
UpdatePlayerStats(player->GetGUIDLow(), TD_PLAYER_STAT_EVENTS_WON, 1);
RecordLog("TowerDefense: Player: [%s] has won [%u] resources after completing Event Id: [%u].", player->GetName(), GetResources(), GetEventId());
}break;
}
player->DestroyItemCount(GetItemEntry(),1,true); // destroy the item from the player
player->TeleportTo(1, -3673.392090, -4384.723145, 10.026433, 3.879712); // Teleport to a Neutral Mall
player->RemoveAllAuras(); // remove all auras set by event
player->RemoveAllAttackers(); // remove all attackers
SetFinished(true); // finish the event
SaveEventData(); // save event information
RecordLog("TowerDefense: Player: [%s] has completed Event Id: [%u] and his event data was saved and he was teleported.", player->GetName(), GetEventId());
DeleteEventData();
}
void TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::UpdateHealth(uint32 remove)
{
if(!remove)
return;
Player *player = GetPlayer();
if(!player)
return;
uint32 currentHealth = GetBaseHealth();
if(remove > currentHealth){
SendMessageToPlayer(TD_SYSTEM_MSG_HEALTH_DEST); // the removal from the base health is larger than the base health. the player's event ends.
player->PlayDirectSound(TD_ENDEVENT_MUSIC,player); // play ending music
player->GetSession()->SendNotification("The Tower defense game has finished. You lost!");
HandleEventComplete(TD_EVENT_COMPLETE_LOST); // Complete the event data using the lost type
RecordLog("TowerDefense: Player Name: [%s] has lost the tower defense event. With Event Id: [%u]",player->GetName(), GetEventId()); // record a log to the server log if enabled.
return;
}else {
currentHealth -= remove; // decrement current health
SetBaseHealth(currentHealth);
RecordLog("TowerDefense: Updated health for Event Id: [%u] to [%u].", GetEventId(), currentHealth);
}
switch(currentHealth)
{
case 90:
SendMessageToPlayer(TD_SYSTEM_MSG_HEALTH_REACHED, 90);
break;
case 80:
SendMessageToPlayer(TD_SYSTEM_MSG_HEALTH_LOWERING, 80);
break;
case 70:
SendMessageToPlayer(TD_SYSTEM_MSG_HEALTH_REACHED, 70);
break;
case 60:
SendMessageToPlayer(TD_SYSTEM_MSG_HEALTH_LOWERING, 60);
break;
case 50:
SendMessageToPlayer(TD_SYSTEM_MSG_HEALTH_REACHED, 50);
break;
case 40:
SendMessageToPlayer(TD_SYSTEM_MSG_HEALTH_LOW);
player->CastSpell(player,GetSpellIdByUniqueId(3),true);
player->PlayDirectSound(TD_ENDEVENT_MUSIC,player);
SendMessageToPlayer(TD_SYSTEM_MSG_HEALTH_REACHED, 40);
break;
case 25:
SendMessageToPlayer(TD_SYSTEM_MSG_HEALTH_VERYLOW);
player->CastSpell(player,GetSpellIdByUniqueId(2),true);
player->PlayDirectSound(TD_BASE_LOSING_HEALTH,_player);
SendMessageToPlayer(TD_SYSTEM_MSG_HEALTH_LOWERING, 25);
break;
case 10:
SendMessageToPlayer(TD_SYSTEM_MSG_HEALTH_REACHED, 10);
SendMessageToPlayer(TD_SYSTEM_MSG_HEALTH_ALMOSTDEST);
break;
}
return;
}
void TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::UpdateResources(TDEventUpdateType type, uint32 add)
{
if(!type || !add)
return;
Player *player = GetPlayer();
if(!player)
return;
uint32 currentRes = GetResources();
uint32 newRes = 0;
if(type == TD_EVENT_INC){
newRes = currentRes + add;
}else{
newRes = currentRes - add;
}
SetResources(newRes);
RecordLog("TowerDefense: New resources were set: it is now %u.", newRes);
}
void TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::AddSimpleMenuItemToPlayer(Player* player, uint32 icon, uint32 sender,uint32 action, const char *Message, ...)
{
if(!player)
return;
char Result[1024];
va_list List;
va_start(List, Message);
vsnprintf(Result, 1024, Message, List);
va_end(List);
player->PlayerTalkClass->GetGossipMenu().AddMenuItem(-1, icon, Result, sender, action, "", false);
}
void TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::SendMessageToPlayer(const char *msgToSend, ...)
{
Player *player = GetPlayer();
if(!player)
return;
char Result[1024];
va_list List;
va_start(List, msgToSend);
vsnprintf(Result, 1024, msgToSend, List);
va_end(List);
player->GetSession()->SendAreaTriggerMessage(Result);
ChatHandler(player).PSendSysMessage(Result);
RecordLog("TowerDefense: Player: [%s] received the message: [%s] from the tower defense system.", player->GetName(), Result);
}
uint32 TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::GenerateEventId()
{
_highEventId++;
return _highEventId;
}
void TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::UpdatePlayerStats(uint32 playerGUID, TDEventPlayerIndex statIndex, uint32 statIncreaseValue)
{
if(statIndex > TD_PLAYER_STAT_MAX)
{
RecordLog("TowerDefense: UpdatePlayerStats statIndex out of range");
return;
}
if(QueryResult queryResult = CharacterDatabase.PQuery("SELECT %s FROM custom_td_players WHERE %s = '%u'", TowerDefensePlayerStatFields[statIndex], TowerDefensePlayerStatFields[TD_PLAYER_STAT_GUID], playerGUID)){
CharacterDatabase.PExecute("UPDATE custom_td_players SET %s = '%u' WHERE %s = '%u'", TowerDefensePlayerStatFields[statIndex], queryResult->Fetch()[0].GetUInt32()+statIncreaseValue, TowerDefensePlayerStatFields[TD_PLAYER_STAT_GUID], playerGUID);
RecordLog("Updated stats for Player GUID: [%u], added [%u] to [%s].", playerGUID, statIncreaseValue, TowerDefensePlayerStatFields[statIndex]);
}
else{
CharacterDatabase.PExecute("REPLACE INTO custom_td_players (%s,%s) VALUES('%u', '%u')", TowerDefensePlayerStatFields[TD_PLAYER_STAT_GUID], TowerDefensePlayerStatFields[statIndex], playerGUID, statIncreaseValue);
RecordLog("Inserted stats for Player GUID: [%u], added [%u] to [%s].", playerGUID, statIncreaseValue, TowerDefensePlayerStatFields[statIndex]);
}
}
void TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::SendMailToPlayer(Player *player, uint32 playerGUID, const char *mailMessage, ...)
{
MailSender toSend(MAIL_NORMAL, playerGUID, TD_SYSTEM_MAIL_TYPE);
SQLTransaction trans = CharacterDatabase.BeginTransaction();
char Body[1024];
va_list List;
va_start(List, mailMessage);
vsnprintf(Body, 1024, mailMessage, List);
va_end(List);
MailDraft mailDraft(TD_SYSTEM_MSG_MAIL_SUBJECT, Body);
if(player){
mailDraft.SendMailTo(trans, MailReceiver(player), toSend);
RecordLog("TowerDefense: Player: [%s] received the mail: [%s] from the tower defense system.", player->GetName(), Body);
}
else{
mailDraft.SendMailTo(trans, MailReceiver(playerGUID), toSend);
RecordLog("TowerDefense: Player GUID: [%u] received the mail: [%s] from the tower defense system.", playerGUID, Body);
}
CharacterDatabase.CommitTransaction(trans);
}
void TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::SetupEventData()
{
Player *player = GetPlayer();
if(!player)
return;
SetEventId(GenerateEventId()); // set the event id
SetPlayerGUID(player->GetGUIDLow()); // set the player guid
SetCurrentWaveId(0); // set the current wave id to zero
SetBaseHealth(100); // set the base health to 100
SetResources(GetStartResources()); // set the resources that the player has to starter resources
SetUnits(0); // set the number of spawned attackers to zero
SetFinished(false); // set the event to unfinished
SetEventStatus(TD_EVENT_STATUS_TELEPORT); // Set the event status to started and not running a wave
Events.Reset();
}
void TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::SaveEventData()
{
if(QueryResult queryResult = CharacterDatabase.PQuery("SELECT * FROM custom_td_events WHERE Id = '%u'", GetEventId())){
CharacterDatabase.PExecute("UPDATE custom_td_events SET eventFinished = '%u'", GetFinished());
RecordLog("TowerDefense: Updated Event Id: [%u], it is now set to finished!",GetEventId());
}
else{
CharacterDatabase.PExecute("INSERT INTO custom_td_events VALUES ('%u', '%u', '%u', '%u', '%u', NOW(), '%u')", GetEventId(), GetPlayerGUID(), GetCurrentWaveId(), GetResources(),GetBaseHealth(), GetFinished());
RecordLog("TowerDefense: Inserted Event Id: [%u] to the database.", GetEventId());
}
}
void TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::DeleteEventData()
{
Player *player = GetPlayer();
if(!player)
return;
SetEventId(0);
SetPlayerGUID(0);
SetCurrentWaveId(0);
SetBaseHealth(0);
SetResources(0);
SetUnits(0);
SetFinished(false);
SetEventStatus(TD_EVENT_STATUS_NONE);
CountDown = 0;
Events.Reset();
RecordLog("TowerDefense: the tower defense event for player %s was deleted", player->GetName());
_player = NULL;
}
void TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::GetRecords(Player *player)
{
if(!player)
return;
if(QueryResult queryResult = CharacterDatabase.PQuery("SELECT * FROM custom_td_players WHERE playerGUID = '%u'", player->GetGUIDLow()))
{
Field *Fields = queryResult->Fetch();
uint32 playerGUID = Fields[0].GetUInt32();
uint32 eventsLost = Fields[1].GetUInt32();
uint32 eventsWon = Fields[2].GetUInt32();
uint32 eventsUnfinished = Fields[3].GetUInt32();
uint32 currentResources = Fields[4].GetUInt32();
std::ostringstream Lost;
std::ostringstream Won;
std::ostringstream UnFinished;
std::ostringstream Resources;
Lost << MSG_COLOR_SUBWHITE << "You have lost: " << MSG_COLOR_LIGHTBLUE << eventsLost << " Event(s)." ;
Won << MSG_COLOR_SUBWHITE << "You have won: " << MSG_COLOR_LIGHTBLUE << eventsWon << " Event(s)." ;
UnFinished << MSG_COLOR_SUBWHITE << "You have resigned: " << MSG_COLOR_LIGHTBLUE << eventsUnfinished << " Event(s)." ;
Resources << MSG_COLOR_SUBWHITE << "You have: " << MSG_COLOR_LIGHTBLUE << currentResources << " Resource(s)." ;
ChatHandler(player).SendSysMessage(Lost.str().c_str());
ChatHandler(player).SendSysMessage(Won.str().c_str());
ChatHandler(player).SendSysMessage(UnFinished.str().c_str());
ChatHandler(player).SendSysMessage(Resources.str().c_str());
}
else
ChatHandler(player).SendSysMessage(TD_SYSTEM_MSG_NO_EVENT_DATA);
}
void TowerDefenseInstanceScript::TowerDefenseMapInstanceScript::RecordLog(const char * str, ...)
{
if (!str)
return;
// If file logging is enabled!
if(IsLoggingEnabled())
{
char Result[1024];
va_list List;
va_start(List, str);
vsnprintf(Result, 1024, str, List);
va_end(List);
sLog->outBasic(Result);
}
}
void TowerDefensePlayerScript::OnLogout(Player *player)
{
if(!player){
sLog->outBasic("TowerDefense: OnLogout player is NULL!");
return;
}
TowerDefenseInstanceScript::TowerDefenseMapInstanceScript* instance = static_cast<TowerDefenseInstanceScript::TowerDefenseMapInstanceScript*>(player->GetInstanceScript());
if (instance && instance->GetEventStatus() >= TD_EVENT_STATUS_TELEPORT)
if(player == instance->GetPlayer())
instance->HandleEventComplete(TD_EVENT_COMPLETE_UNFINISHED);
}
void AddSC_TowerDefenseInstanceScript()
{
new TowerDefenseInstanceScript();
new TowerDefensePlayerScript();
}