2020-08-29 11:43:33 +00:00
# include "CNShardServer.hpp"
# include "CNStructs.hpp"
# include "PlayerManager.hpp"
2020-10-04 23:54:08 +00:00
# include "NanoManager.hpp"
2020-08-29 11:43:33 +00:00
# include "TransportManager.hpp"
2020-10-06 19:53:21 +00:00
# include "TableData.hpp"
2020-10-12 19:03:18 +00:00
# include "MobManager.hpp"
2020-08-29 11:43:33 +00:00
2020-09-17 02:27:21 +00:00
# include <unordered_map>
2020-09-23 14:53:06 +00:00
# include <cmath>
2020-09-13 20:26:16 +00:00
std : : map < int32_t , TransportRoute > TransportManager : : Routes ;
std : : map < int32_t , TransportLocation > TransportManager : : Locations ;
2020-09-17 09:04:00 +00:00
std : : map < int32_t , std : : queue < WarpLocation > > TransportManager : : SkywayPaths ;
2020-09-22 01:03:48 +00:00
std : : unordered_map < CNSocket * , std : : queue < WarpLocation > > TransportManager : : SkywayQueues ;
2020-09-23 14:29:29 +00:00
std : : unordered_map < int32_t , std : : queue < WarpLocation > > TransportManager : : NPCQueues ;
2020-09-13 20:26:16 +00:00
2020-08-29 11:43:33 +00:00
void TransportManager : : init ( ) {
2020-09-23 14:29:29 +00:00
REGISTER_SHARD_TIMER ( tickTransportationSystem , 1000 ) ;
2020-09-17 02:27:21 +00:00
2020-08-29 11:43:33 +00:00
REGISTER_SHARD_PACKET ( P_CL2FE_REQ_REGIST_TRANSPORTATION_LOCATION , transportRegisterLocationHandler ) ;
2020-09-13 20:26:16 +00:00
REGISTER_SHARD_PACKET ( P_CL2FE_REQ_PC_WARP_USE_TRANSPORTATION , transportWarpHandler ) ;
2020-08-29 11:43:33 +00:00
}
void TransportManager : : transportRegisterLocationHandler ( CNSocket * sock , CNPacketData * data ) {
2020-09-13 20:26:16 +00:00
if ( data - > size ! = sizeof ( sP_CL2FE_REQ_REGIST_TRANSPORTATION_LOCATION ) )
return ; // malformed packet
2020-09-14 13:53:48 +00:00
2020-08-29 11:43:33 +00:00
sP_CL2FE_REQ_REGIST_TRANSPORTATION_LOCATION * transport = ( sP_CL2FE_REQ_REGIST_TRANSPORTATION_LOCATION * ) data - > buf ;
2020-09-13 20:26:16 +00:00
Player * plr = PlayerManager : : getPlayer ( sock ) ;
2020-09-28 18:11:13 +00:00
if ( plr = = nullptr )
return ;
2020-09-22 01:03:48 +00:00
bool newReg = false ; // this is a new registration
2020-09-17 13:22:06 +00:00
//std::cout << "request to register transport, eTT " << transport->eTT << ", locID " << transport->iLocationID << ", npc " << transport->iNPC_ID << std::endl;
2020-09-13 20:26:16 +00:00
if ( transport - > eTT = = 1 ) { // S.C.A.M.P.E.R.
if ( transport - > iLocationID < 1 | | transport - > iLocationID > 31 ) { // sanity check
std : : cout < < " [WARN] S.C.A.M.P.E.R. location ID " < < transport - > iLocationID < < " is out of bounds " < < std : : endl ;
INITSTRUCT ( sP_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_FAIL , failResp ) ;
failResp . eTT = transport - > eTT ;
failResp . iErrorCode = 0 ; // TODO: review what error code to use here
failResp . iLocationID = transport - > iLocationID ;
sock - > sendPacket ( ( void * ) & failResp , P_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_FAIL , sizeof ( sP_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_FAIL ) ) ;
return ;
}
2020-09-22 01:03:48 +00:00
// update registration bitfield using bitmask
2020-09-22 03:54:07 +00:00
uint32_t newScamperFlag = plr - > iWarpLocationFlag | ( plr - > accountLevel < = 40 ? INT32_MAX : ( 1UL < < ( transport - > iLocationID - 1 ) ) ) ;
2020-09-22 01:03:48 +00:00
if ( newScamperFlag ! = plr - > iWarpLocationFlag ) {
plr - > iWarpLocationFlag = newScamperFlag ;
newReg = true ;
}
2020-09-13 20:26:16 +00:00
} else if ( transport - > eTT = = 2 ) { // Monkey Skyway System
if ( transport - > iLocationID < 1 | | transport - > iLocationID > 127 ) { // sanity check
std : : cout < < " [WARN] Skyway location ID " < < transport - > iLocationID < < " is out of bounds " < < std : : endl ;
INITSTRUCT ( sP_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_FAIL , failResp ) ;
failResp . eTT = transport - > eTT ;
failResp . iErrorCode = 0 ; // TODO: review what error code to use here
failResp . iLocationID = transport - > iLocationID ;
sock - > sendPacket ( ( void * ) & failResp , P_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_FAIL , sizeof ( sP_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_FAIL ) ) ;
return ;
}
2020-09-14 13:53:48 +00:00
/*
2020-09-22 01:03:48 +00:00
* assuming the two bitfields are just stuck together to make a longer one , do a similar operation
2020-09-13 20:26:16 +00:00
*/
2020-09-22 02:26:12 +00:00
if ( plr - > accountLevel < = 40 ) {
2020-09-22 03:54:07 +00:00
plr - > aSkywayLocationFlag [ 0 ] = INT64_MAX ;
plr - > aSkywayLocationFlag [ 1 ] = INT64_MAX ;
2020-09-22 01:03:48 +00:00
newReg = true ;
} else {
int index = transport - > iLocationID > 64 ? 1 : 0 ;
uint64_t newMonkeyFlag = plr - > aSkywayLocationFlag [ index ] | ( 1ULL < < ( index ? transport - > iLocationID - 65 : transport - > iLocationID - 1 ) ) ;
if ( newMonkeyFlag ! = plr - > aSkywayLocationFlag [ index ] ) {
plr - > aSkywayLocationFlag [ index ] = newMonkeyFlag ;
newReg = true ;
}
2020-09-17 02:27:21 +00:00
}
2020-09-13 20:26:16 +00:00
} else {
std : : cout < < " [WARN] Unknown mode of transport; eTT = " < < transport - > eTT < < std : : endl ;
INITSTRUCT ( sP_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_FAIL , failResp ) ;
failResp . eTT = transport - > eTT ;
failResp . iErrorCode = 0 ; // TODO: review what error code to use here
failResp . iLocationID = transport - > iLocationID ;
sock - > sendPacket ( ( void * ) & failResp , P_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_FAIL , sizeof ( sP_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_FAIL ) ) ;
return ;
}
2020-08-29 11:43:33 +00:00
2020-09-22 01:03:48 +00:00
if ( ! newReg )
return ; // don't send new registration message
2020-09-13 20:26:16 +00:00
2020-09-22 01:03:48 +00:00
INITSTRUCT ( sP_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_SUCC , resp ) ;
2020-09-13 20:26:16 +00:00
// response parameters
2020-08-29 11:43:33 +00:00
resp . eTT = transport - > eTT ;
resp . iLocationID = transport - > iLocationID ;
2020-09-13 20:26:16 +00:00
resp . iWarpLocationFlag = plr - > iWarpLocationFlag ;
resp . aWyvernLocationFlag [ 0 ] = plr - > aSkywayLocationFlag [ 0 ] ;
resp . aWyvernLocationFlag [ 1 ] = plr - > aSkywayLocationFlag [ 1 ] ;
2020-08-29 11:43:33 +00:00
sock - > sendPacket ( ( void * ) & resp , P_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_SUCC , sizeof ( sP_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_SUCC ) ) ;
}
2020-09-13 20:26:16 +00:00
void TransportManager : : transportWarpHandler ( CNSocket * sock , CNPacketData * data ) {
if ( data - > size ! = sizeof ( sP_CL2FE_REQ_PC_WARP_USE_TRANSPORTATION ) )
return ; // malformed packet
sP_CL2FE_REQ_PC_WARP_USE_TRANSPORTATION * req = ( sP_CL2FE_REQ_PC_WARP_USE_TRANSPORTATION * ) data - > buf ;
Player * plr = PlayerManager : : getPlayer ( sock ) ;
2020-09-28 18:11:13 +00:00
if ( plr = = nullptr )
return ;
2020-09-13 20:26:16 +00:00
/*
* req :
* eIL - - inventory type
* iNPC_ID - - the ID of the NPC who is warping you
* iTransporationID - - iVehicleID
* iSlotNum - - inventory slot number
*/
2020-09-17 02:27:21 +00:00
if ( Routes . find ( req - > iTransporationID ) = = Routes . end ( ) | | Routes [ req - > iTransporationID ] . cost > plr - > money ) { // sanity check
2020-09-13 20:26:16 +00:00
INITSTRUCT ( sP_FE2CL_REP_PC_WARP_USE_TRANSPORTATION_FAIL , failResp ) ;
failResp . iErrorCode = 0 ; // TODO: error code
failResp . iTransportationID = req - > iTransporationID ;
sock - > sendPacket ( ( void * ) & failResp , P_FE2CL_REP_PC_WARP_USE_TRANSPORTATION_FAIL , sizeof ( sP_FE2CL_REP_PC_WARP_USE_TRANSPORTATION_FAIL ) ) ;
return ;
}
TransportRoute route = Routes [ req - > iTransporationID ] ;
plr - > money - = route . cost ;
2020-09-14 13:53:48 +00:00
2020-09-17 02:27:21 +00:00
TransportLocation target ;
PlayerView & plrv = PlayerManager : : players [ sock ] ;
2020-09-22 01:03:48 +00:00
switch ( route . type ) {
2020-09-13 20:26:16 +00:00
case 1 : // S.C.A.M.P.E.R.
2020-09-17 02:27:21 +00:00
target = Locations [ route . end ] ;
2020-09-13 20:26:16 +00:00
plr - > x = target . x ;
plr - > y = target . y ;
plr - > z = target . z ;
2020-09-17 02:27:21 +00:00
/*
* Not strictly necessary since there isn ' t a valid SCAMPER that puts you in the
* same map tile you were already in , but we might as well force an NPC reload .
*/
2020-09-17 13:22:06 +00:00
PlayerManager : : removePlayerFromChunks ( plrv . currentChunks , sock ) ;
plrv . currentChunks . clear ( ) ;
2020-10-01 01:44:37 +00:00
plrv . chunkPos = std : : make_tuple ( 0 , 0 , plrv . plr - > instanceID ) ;
2020-09-13 20:26:16 +00:00
break ;
case 2 : // Monkey Skyway
2020-09-22 01:03:48 +00:00
if ( SkywayPaths . find ( route . mssRouteNum ) ! = SkywayPaths . end ( ) ) { // check if route exists
2020-10-04 23:54:08 +00:00
NanoManager : : summonNano ( sock , - 1 ) ; // make sure that no nano is active during the ride
2020-09-22 01:03:48 +00:00
SkywayQueues [ sock ] = SkywayPaths [ route . mssRouteNum ] ; // set socket point queue to route
break ;
2020-10-06 19:53:21 +00:00
} else if ( TableData : : RunningSkywayRoutes . find ( route . mssRouteNum ) ! = TableData : : RunningSkywayRoutes . end ( ) ) {
std : : vector < WarpLocation > * _route = & TableData : : RunningSkywayRoutes [ route . mssRouteNum ] ;
NanoManager : : summonNano ( sock , - 1 ) ;
testMssRoute ( sock , _route ) ;
break ;
2020-09-22 01:03:48 +00:00
}
// refund and send alert packet
plr - > money + = route . cost ;
INITSTRUCT ( sP_FE2CL_ANNOUNCE_MSG , alert ) ;
alert . iAnnounceType = 0 ; // don't think this lets us make a confirm dialog
alert . iDuringTime = 3 ;
2020-10-04 17:50:58 +00:00
U8toU16 ( " Skyway route " + std : : to_string ( route . mssRouteNum ) + " isn't pathed yet. You will not be charged any taros. " , ( char16_t * ) alert . szAnnounceMsg , sizeof ( alert . szAnnounceMsg ) ) ;
2020-09-22 01:03:48 +00:00
sock - > sendPacket ( ( void * ) & alert , P_FE2CL_ANNOUNCE_MSG , sizeof ( sP_FE2CL_ANNOUNCE_MSG ) ) ;
std : : cout < < " [WARN] MSS route " < < route . mssRouteNum < < " not pathed " < < std : : endl ;
2020-09-13 20:26:16 +00:00
break ;
default :
std : : cout < < " [WARN] Unknown tranportation type " < < route . type < < std : : endl ;
break ;
}
INITSTRUCT ( sP_FE2CL_REP_PC_WARP_USE_TRANSPORTATION_SUCC , resp ) ;
// response parameters
resp . eTT = route . type ;
resp . iCandy = plr - > money ;
resp . iX = plr - > x ;
resp . iY = plr - > y ;
resp . iZ = plr - > z ;
2020-09-17 02:27:21 +00:00
sock - > sendPacket ( ( void * ) & resp , P_FE2CL_REP_PC_WARP_USE_TRANSPORTATION_SUCC , sizeof ( sP_FE2CL_REP_PC_WARP_USE_TRANSPORTATION_SUCC ) ) ;
}
2020-10-06 19:53:21 +00:00
void TransportManager : : testMssRoute ( CNSocket * sock , std : : vector < WarpLocation > * route ) {
int speed = 1500 ; // TODO: make this adjustable
std : : queue < WarpLocation > path ;
WarpLocation last = route - > front ( ) ; // start pos
for ( int i = 1 ; i < route - > size ( ) ; i + + ) {
WarpLocation coords = route - > at ( i ) ;
TransportManager : : lerp ( & path , last , coords , speed ) ;
path . push ( coords ) ; // add keyframe to the queue
last = coords ; // update start pos
}
SkywayQueues [ sock ] = path ;
}
2020-09-23 14:29:29 +00:00
void TransportManager : : tickTransportationSystem ( CNServer * serv , time_t currTime ) {
stepNPCPathing ( ) ;
stepSkywaySystem ( ) ;
}
2020-09-22 01:03:48 +00:00
/*
* Go through every socket that has broomstick points queued up , and advance to the next point .
* If the player has disconnected or finished the route , clean up and remove them from the queue .
*/
2020-09-23 14:29:29 +00:00
void TransportManager : : stepSkywaySystem ( ) {
2020-10-05 00:03:13 +00:00
2020-09-22 01:03:48 +00:00
// using an unordered map so we can remove finished players in one iteration
std : : unordered_map < CNSocket * , std : : queue < WarpLocation > > : : iterator it = SkywayQueues . begin ( ) ;
while ( it ! = SkywayQueues . end ( ) ) {
2020-09-17 02:27:21 +00:00
std : : queue < WarpLocation > * queue = & it - > second ;
2020-10-05 00:03:13 +00:00
2020-09-28 18:11:13 +00:00
Player * plr = PlayerManager : : getPlayer ( it - > first ) ;
2020-10-05 00:03:13 +00:00
2020-09-22 01:03:48 +00:00
if ( plr = = nullptr ) {
// pluck out dead socket + update iterator
it = SkywayQueues . erase ( it ) ;
2020-09-17 13:22:06 +00:00
continue ;
2020-09-17 02:27:21 +00:00
}
2020-09-17 13:22:06 +00:00
if ( queue - > empty ( ) ) {
2020-09-17 02:27:21 +00:00
// send dismount packet
INITSTRUCT ( sP_FE2CL_REP_PC_RIDING_SUCC , rideSucc ) ;
2020-09-17 13:22:06 +00:00
INITSTRUCT ( sP_FE2CL_PC_RIDING , rideBroadcast ) ;
2020-09-22 01:03:48 +00:00
rideSucc . iPC_ID = plr - > iID ;
2020-09-17 02:27:21 +00:00
rideSucc . eRT = 0 ;
2020-09-22 01:03:48 +00:00
rideBroadcast . iPC_ID = plr - > iID ;
2020-09-17 13:22:06 +00:00
rideBroadcast . eRT = 0 ;
2020-09-17 02:27:21 +00:00
it - > first - > sendPacket ( ( void * ) & rideSucc , P_FE2CL_REP_PC_RIDING_SUCC , sizeof ( sP_FE2CL_REP_PC_RIDING_SUCC ) ) ;
2020-09-22 01:03:48 +00:00
// send packet to players in view
PlayerManager : : sendToViewable ( it - > first , ( void * ) & rideBroadcast , P_FE2CL_PC_RIDING , sizeof ( sP_FE2CL_PC_RIDING ) ) ;
it = SkywayQueues . erase ( it ) ; // remove player from tracking map + update iterator
} else {
2020-09-17 02:27:21 +00:00
WarpLocation point = queue - > front ( ) ; // get point
queue - > pop ( ) ; // remove point from front of queue
INITSTRUCT ( sP_FE2CL_PC_BROOMSTICK_MOVE , bmstk ) ;
2020-09-22 01:03:48 +00:00
bmstk . iPC_ID = plr - > iID ;
2020-09-17 02:27:21 +00:00
bmstk . iToX = point . x ;
bmstk . iToY = point . y ;
bmstk . iToZ = point . z ;
it - > first - > sendPacket ( ( void * ) & bmstk , P_FE2CL_PC_BROOMSTICK_MOVE , sizeof ( sP_FE2CL_PC_BROOMSTICK_MOVE ) ) ;
2020-09-22 01:03:48 +00:00
// set player location to point to update viewables
2020-09-24 22:43:58 +00:00
PlayerManager : : updatePlayerChunk ( it - > first , point . x , point . y ) ;
2020-09-17 13:22:06 +00:00
// send packet to players in view
2020-09-22 01:03:48 +00:00
PlayerManager : : sendToViewable ( it - > first , ( void * ) & bmstk , P_FE2CL_PC_BROOMSTICK_MOVE , sizeof ( sP_FE2CL_PC_BROOMSTICK_MOVE ) ) ;
2020-09-17 02:27:21 +00:00
it + + ; // go to next entry in map
}
}
2020-09-13 20:26:16 +00:00
}
2020-09-23 14:29:29 +00:00
void TransportManager : : stepNPCPathing ( ) {
// all NPC pathing queues
std : : unordered_map < int32_t , std : : queue < WarpLocation > > : : iterator it = NPCQueues . begin ( ) ;
while ( it ! = NPCQueues . end ( ) ) {
std : : queue < WarpLocation > * queue = & it - > second ;
BaseNPC * npc = nullptr ;
if ( NPCManager : : NPCs . find ( it - > first ) ! = NPCManager : : NPCs . end ( ) )
npc = NPCManager : : NPCs [ it - > first ] ;
2020-10-12 19:03:18 +00:00
if ( npc = = nullptr | | queue - > empty ( ) ) {
2020-09-23 14:29:29 +00:00
// pluck out dead path + update iterator
it = NPCQueues . erase ( it ) ;
continue ;
}
2020-10-13 19:44:43 +00:00
// skip if not simulating mobs
if ( npc - > npcClass = = NPC_MOB & & ! MobManager : : simulateMobs ) {
it + + ;
continue ;
}
2020-10-12 19:03:18 +00:00
// do not roam if not roaming
if ( npc - > npcClass = = NPC_MOB & & ( ( Mob * ) npc ) - > state ! = MobState : : ROAMING ) {
it + + ;
continue ;
}
2020-09-23 14:29:29 +00:00
WarpLocation point = queue - > front ( ) ; // get point
queue - > pop ( ) ; // remove point from front of queue
// calculate displacement
int dXY = hypot ( point . x - npc - > appearanceData . iX , point . y - npc - > appearanceData . iY ) ; // XY plane distance
int distanceBetween = hypot ( dXY , point . z - npc - > appearanceData . iZ ) ; // total distance
// update NPC location to update viewables
2020-09-23 14:53:06 +00:00
NPCManager : : updateNPCPosition ( npc - > appearanceData . iNPC_ID , point . x , point . y , point . z ) ;
2020-09-23 14:29:29 +00:00
switch ( npc - > npcClass ) {
case NPC_BUS :
INITSTRUCT ( sP_FE2CL_TRANSPORTATION_MOVE , busMove ) ;
busMove . eTT = 3 ;
busMove . iT_ID = npc - > appearanceData . iNPC_ID ;
busMove . iMoveStyle = 0 ; // ???
busMove . iToX = point . x ;
busMove . iToY = point . y ;
busMove . iToZ = point . z ;
busMove . iSpeed = distanceBetween ; // set to distance to match how monkeys work
2020-10-12 19:03:18 +00:00
NPCManager : : sendToViewable ( npc , & busMove , P_FE2CL_TRANSPORTATION_MOVE , sizeof ( sP_FE2CL_TRANSPORTATION_MOVE ) ) ;
2020-09-23 14:29:29 +00:00
break ;
2020-10-12 19:03:18 +00:00
case NPC_MOB :
MobManager : : incNextMovement ( ( Mob * ) npc ) ;
/* fallthrough */
2020-09-23 14:29:29 +00:00
default :
INITSTRUCT ( sP_FE2CL_NPC_MOVE , move ) ;
move . iNPC_ID = npc - > appearanceData . iNPC_ID ;
move . iMoveStyle = 0 ; // ???
move . iToX = point . x ;
move . iToY = point . y ;
move . iToZ = point . z ;
2020-09-24 14:14:37 +00:00
move . iSpeed = distanceBetween ;
2020-09-23 14:29:29 +00:00
2020-10-12 19:03:18 +00:00
NPCManager : : sendToViewable ( npc , & move , P_FE2CL_NPC_MOVE , sizeof ( sP_FE2CL_NPC_MOVE ) ) ;
2020-09-23 14:29:29 +00:00
break ;
}
2020-10-12 19:03:18 +00:00
/*
* Move processed point to the back to maintain cycle , unless this is a
* dynamically calculated mob route .
*/
if ( ! ( npc - > npcClass = = NPC_MOB & & ! ( ( Mob * ) npc ) - > staticPath ) )
queue - > push ( point ) ;
2020-09-23 14:29:29 +00:00
it + + ; // go to next entry in map
}
}
/*
* Linearly interpolate between two points and insert the results into a queue .
*/
2020-09-26 22:45:19 +00:00
void TransportManager : : lerp ( std : : queue < WarpLocation > * queue , WarpLocation start , WarpLocation end , int gapSize , float curve ) {
2020-09-23 14:29:29 +00:00
int dXY = hypot ( end . x - start . x , end . y - start . y ) ; // XY plane distance
int distanceBetween = hypot ( dXY , end . z - start . z ) ; // total distance
2020-09-26 22:45:19 +00:00
int lerps = distanceBetween / gapSize ; // number of intermediate points to add
for ( int i = 1 ; i < = lerps ; i + + ) {
2020-09-23 14:29:29 +00:00
WarpLocation lerp ;
// lerp math
2020-09-26 22:45:19 +00:00
//float frac = i / (lerps + 1);
float frac = powf ( i , curve ) / powf ( lerps + 1 , curve ) ;
2020-09-23 14:29:29 +00:00
lerp . x = ( start . x * ( 1.0f - frac ) ) + ( end . x * frac ) ;
lerp . y = ( start . y * ( 1.0f - frac ) ) + ( end . y * frac ) ;
lerp . z = ( start . z * ( 1.0f - frac ) ) + ( end . z * frac ) ;
queue - > push ( lerp ) ; // add lerp'd point
}
}
2020-09-26 22:45:19 +00:00
void TransportManager : : lerp ( std : : queue < WarpLocation > * queue , WarpLocation start , WarpLocation end , int gapSize ) {
lerp ( queue , start , end , gapSize , 1 ) ;
}