/*
* Ascent MMORPG Server
* Copyright (C) 2005-2007 Ascent Team
*
* 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
* 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 .
*
*/
#include "StdAfx.h"
bool Transporter::CreateAsTransporter(uint32 EntryID, const char* Name, uint32 Time)
{
// Lookup GameobjectInfo
if(!CreateFromProto(EntryID,0,0,0,0,0))
return false;
SetUInt32Value(GAMEOBJECT_FLAGS,40);
SetUInt32Value(GAMEOBJECT_ANIMPROGRESS, 100);
// Set period
m_period = Time;
// Generate waypoints
if(!GenerateWaypoints())
return false;
// Set position
SetMapId(m_WayPoints[0].mapid);
SetPosition(m_WayPoints[0].x, m_WayPoints[0].y, m_WayPoints[0].z, 0);
// Add to world
AddToWorld();
return true;
}
bool FillPathVector(uint32 PathID, TransportPath & Path)
{
Path.Resize(sTaxiPathNodeStore.GetNumRows());
uint32 i = 0;
for(uint32 j = 0; j < sTaxiPathNodeStore.GetNumRows(); j++)
{
DBCTaxiPathNode *pathnode = sTaxiPathNodeStore.LookupEntry(j);
if(pathnode->path == PathID)
{
Path[i].mapid = pathnode->mapid;
Path[i].x = pathnode->x;
Path[i].y = pathnode->y;
Path[i].z = pathnode->z;
Path[i].actionFlag = pathnode->unk1;
Path[i].delay = pathnode->waittime;
++i;
}
}
Path.Resize(i);
return (i > 0 ? true : false);
}
bool Transporter::GenerateWaypoints()
{
TransportPath path;
FillPathVector(GetInfo()->SpellFocus, path);
if(path.Size() == 0) return false;
vector keyFrames;
int mapChange = 0;
for (size_t i = 1; i < path.Size() - 1; i++)
{
if (mapChange == 0)
{
if ((path[i].mapid == path[i+1].mapid))
{
keyFrame k(path[i].x, path[i].y, path[i].z, path[i].mapid, path[i].actionFlag, path[i].delay);
keyFrames.push_back(k);
}
else
{
mapChange = 1;
}
}
else
{
mapChange--;
}
}
int lastStop = -1;
// first cell is arrived at by teleportation :S
keyFrames[0].distFromPrev = 0;
if (keyFrames[0].actionflag == 2)
{
lastStop = 0;
}
// find the rest of the distances between key points
for (size_t i = 1; i < keyFrames.size(); i++)
{
if ((keyFrames[i].actionflag == 1) || (keyFrames[i].mapid != keyFrames[i-1].mapid))
{
keyFrames[i].distFromPrev = 0;
}
else
{
keyFrames[i].distFromPrev =
sqrt(pow(keyFrames[i].x - keyFrames[i - 1].x, 2) +
pow(keyFrames[i].y - keyFrames[i - 1].y, 2) +
pow(keyFrames[i].z - keyFrames[i - 1].z, 2));
}
if (keyFrames[i].actionflag == 2)
lastStop = i;
}
float tmpDist = 0;
for (size_t i = 0; i < keyFrames.size(); i++)
{
int j = (i + lastStop) % keyFrames.size();
if (keyFrames[j].actionflag == 2)
tmpDist = 0;
else
tmpDist += keyFrames[j].distFromPrev;
keyFrames[j].distSinceStop = tmpDist;
}
for (int i = int(keyFrames.size()) - 1; i >= 0; i--)
{
int j = (i + (keyFrames.size() - lastStop)) % keyFrames.size();
tmpDist += keyFrames[(j + 1) % keyFrames.size()].distFromPrev;
keyFrames[j].distUntilStop = tmpDist;
if (keyFrames[j].actionflag == 2)
tmpDist = 0;
}
for (size_t i = 0; i < keyFrames.size(); i++)
{
if (keyFrames[i].distSinceStop < (30 * 30 * 0.5))
keyFrames[i].tFrom = sqrt(2 * keyFrames[i].distSinceStop);
else
keyFrames[i].tFrom = ((keyFrames[i].distSinceStop - (30 * 30 * 0.5)) / 30) + 30;
if (keyFrames[i].distUntilStop < (30 * 30 * 0.5))
keyFrames[i].tTo = sqrt(2 * keyFrames[i].distUntilStop);
else
keyFrames[i].tTo = ((keyFrames[i].distUntilStop - (30 * 30 * 0.5)) / 30) + 30;
keyFrames[i].tFrom *= 1000;
keyFrames[i].tTo *= 1000;
}
// for (int i = 0; i < keyFrames.size(); i++) {
// sLog.outString("%f, %f, %f, %f, %f, %f, %f", keyFrames[i].x, keyFrames[i].y, keyFrames[i].distUntilStop, keyFrames[i].distSinceStop, keyFrames[i].distFromPrev, keyFrames[i].tFrom, keyFrames[i].tTo);
// }
// Now we're completely set up; we can move along the length of each waypoint at 100 ms intervals
// speed = max(30, t) (remember x = 0.5s^2, and when accelerating, a = 1 unit/s^2
int t = 0;
bool teleport = false;
if (keyFrames[keyFrames.size() - 1].mapid != keyFrames[0].mapid)
teleport = true;
TWayPoint pos(keyFrames[0].mapid, keyFrames[0].x, keyFrames[0].y, keyFrames[0].z, teleport);
m_WayPoints[0] = pos;
t += keyFrames[0].delay * 1000;
int cM = keyFrames[0].mapid;
for (size_t i = 0; i < keyFrames.size() - 1; i++) //
{
float d = 0;
float tFrom = keyFrames[i].tFrom;
float tTo = keyFrames[i].tTo;
// keep the generation of all these points; we use only a few now, but may need the others later
if (((d < keyFrames[i + 1].distFromPrev) && (tTo > 0)))
{
while ((d < keyFrames[i + 1].distFromPrev) && (tTo > 0))
{
tFrom += 100;
tTo -= 100;
if (d > 0)
{
float newX, newY, newZ;
newX = keyFrames[i].x + (keyFrames[i + 1].x - keyFrames[i].x) * d / keyFrames[i + 1].distFromPrev;
newY = keyFrames[i].y + (keyFrames[i + 1].y - keyFrames[i].y) * d / keyFrames[i + 1].distFromPrev;
newZ = keyFrames[i].z + (keyFrames[i + 1].z - keyFrames[i].z) * d / keyFrames[i + 1].distFromPrev;
bool teleport = false;
if ((int)keyFrames[i].mapid != cM)
{
teleport = true;
cM = keyFrames[i].mapid;
}
// sLog.outString("T: %d, D: %f, x: %f, y: %f, z: %f", t, d, newX, newY, newZ);
TWayPoint pos(keyFrames[i].mapid, newX, newY, newZ, teleport);
if (teleport)
m_WayPoints[t] = pos;
}
if (tFrom < tTo) // caught in tFrom dock's "gravitational pull"
{
if (tFrom <= 30000)
{
d = 0.5 * (tFrom / 1000) * (tFrom / 1000);
}
else
{
d = 0.5 * 30 * 30 + 30 * ((tFrom - 30000) / 1000);
}
d = d - keyFrames[i].distSinceStop;
}
else
{
if (tTo <= 30000)
{
d = 0.5 * (tTo / 1000) * (tTo / 1000);
}
else
{
d = 0.5 * 30 * 30 + 30 * ((tTo - 30000) / 1000);
}
d = keyFrames[i].distUntilStop - d;
}
t += 100;
}
t -= 100;
}
if (keyFrames[i + 1].tFrom > keyFrames[i + 1].tTo)
t += 100 - ((long)keyFrames[i + 1].tTo % 100);
else
t += (long)keyFrames[i + 1].tTo % 100;
bool teleport = false;
if ((keyFrames[i + 1].actionflag == 1) || (keyFrames[i + 1].mapid != keyFrames[i].mapid))
{
teleport = true;
cM = keyFrames[i + 1].mapid;
}
TWayPoint pos(keyFrames[i + 1].mapid, keyFrames[i + 1].x, keyFrames[i + 1].y, keyFrames[i + 1].z, teleport);
// sLog.outString("T: %d, x: %f, y: %f, z: %f, t:%d", t, pos.x, pos.y, pos.z, teleport);
//if (teleport)
//m_WayPoints[t] = pos;
m_WayPoints.insert(WaypointMap::value_type(t, pos));
t += keyFrames[i + 1].delay * 1000;
// sLog.outString("------");
}
uint32 timer = t;
mCurrentWaypoint = m_WayPoints.begin();
mCurrentWaypoint = GetNextWaypoint();
mNextWaypoint = GetNextWaypoint();
m_pathTime = timer;
m_timer = 0;
return true;
}
WaypointIterator Transporter::GetNextWaypoint()
{
WaypointIterator iter = mCurrentWaypoint;
iter++;
if (iter == m_WayPoints.end())
iter = m_WayPoints.begin();
return iter;
}
void Transporter::UpdatePosition()
{
if (m_WayPoints.size() <= 1)
return;
m_timer = getMSTime() % m_period;
while (((m_timer - mCurrentWaypoint->first) % m_pathTime) > ((mNextWaypoint->first - mCurrentWaypoint->first) % m_pathTime))
{
mCurrentWaypoint = GetNextWaypoint();
mNextWaypoint = GetNextWaypoint();
if (mCurrentWaypoint->second.teleport == true) {
WaypointIterator iterPrev = mCurrentWaypoint;
TransportPassengers(mCurrentWaypoint->second.mapid, GetMapId(),
mCurrentWaypoint->second.x, mCurrentWaypoint->second.y, mCurrentWaypoint->second.z);
break;
} else {
SetPosition(mCurrentWaypoint->second.x, mCurrentWaypoint->second.y,
mCurrentWaypoint->second.z, m_position.o, false);
}
}
}
void Transporter::TransportPassengers(uint32 mapid, uint32 oldmap, float x, float y, float z)
{
sEventMgr.RemoveEvents(this, EVENT_TRANSPORTER_NEXT_WAYPOINT);
if(mPassengers.size() > 0)
{
PassengerIterator itr = mPassengers.begin();
PassengerIterator it2;
WorldPacket Pending(SMSG_TRANSFER_PENDING, 12);
Pending << mapid << GetEntry() << oldmap;
WorldPacket NewWorld;
LocationVector v;
for(; itr != mPassengers.end();)
{
it2 = itr;
++itr;
Player *plr = objmgr.GetPlayer(it2->first);
if(!plr)
{
// remove from map
mPassengers.erase(it2);
continue;
}
if(!plr->GetSession() || !plr->IsInWorld())
continue;
plr->m_lockTransportVariables = true;
v.x = x + plr->m_TransporterX;
v.y = y + plr->m_TransporterY;
v.z = z + plr->m_TransporterZ;
v.o = plr->GetOrientation();
if(mapid == 530 && !plr->GetSession()->HasFlag(ACCOUNT_FLAG_XPACK_01))
{
// player is not flagged to access bc content, repop at graveyard
plr->RepopAtGraveyard(plr->GetPositionX(), plr->GetPositionY(), plr->GetPositionZ(), plr->GetMapId());
continue;
}
plr->GetSession()->SendPacket(&Pending);
plr->_Relocate(mapid, v, false, true);
// Lucky bitch. Do it like on official.
if(plr->isDead())
plr->ResurrectPlayer();
}
}
// Set our position
RemoveFromWorld();
SetMapId(mapid);
SetPosition(x,y,z,m_position.o,false);
AddToWorld();
}
Transporter::Transporter(uint32 guidlow, uint32 guidhigh) : GameObject(guidlow, guidhigh)
{
}
Transporter::~Transporter()
{
sEventMgr.RemoveEvents(this);
}
void ObjectMgr::LoadTransporters()
{
#ifdef CLUSTERING
return;
#endif
Log.Notice("ObjectMgr", "Loading Transports...");
QueryResult * QR = WorldDatabase.Query("SELECT * FROM transport_data");
if(!QR) return;
uint32 guid = 0;
int64 total = QR->GetRowCount();
TransportersCount=total;
do
{
uint32 entry = QR->Fetch()[0].GetUInt32();
uint32 period = QR->Fetch()[2].GetUInt32();
Transporter * pTransporter = new Transporter(HIGHGUID_TRANSPORTER,++guid);
if(!pTransporter->CreateAsTransporter(entry, "", period))
{
sLog.outError("Transporter %s failed creation for some reason.", QR->Fetch()[1].GetString());
delete pTransporter;
}else
{
AddTransport(pTransporter);
}
} while(QR->NextRow());
delete QR;
}
void Transporter::OnPushToWorld()
{
// Create waypoint event
sEventMgr.AddEvent(this, &Transporter::UpdatePosition, EVENT_TRANSPORTER_NEXT_WAYPOINT, 100, 0,EVENT_FLAG_DO_NOT_EXECUTE_IN_WORLD_CONTEXT);
}