2021-03-17 19:07:40 +00:00
|
|
|
#include "servers/CNShardServer.hpp"
|
2021-03-16 22:29:13 +00:00
|
|
|
#include "Racing.hpp"
|
2020-11-28 16:20:37 +00:00
|
|
|
#include "PlayerManager.hpp"
|
2021-03-16 22:29:13 +00:00
|
|
|
#include "Missions.hpp"
|
|
|
|
#include "Items.hpp"
|
2021-03-16 01:06:54 +00:00
|
|
|
#include "db/Database.hpp"
|
2020-12-14 20:09:41 +00:00
|
|
|
#include "NPCManager.hpp"
|
2020-11-28 13:29:06 +00:00
|
|
|
|
2021-03-16 22:29:13 +00:00
|
|
|
using namespace Racing;
|
2021-03-16 21:06:10 +00:00
|
|
|
|
2021-03-16 22:29:13 +00:00
|
|
|
std::map<int32_t, EPInfo> Racing::EPData;
|
|
|
|
std::map<CNSocket*, EPRace> Racing::EPRaces;
|
|
|
|
std::map<int32_t, std::pair<std::vector<int>, std::vector<int>>> Racing::EPRewards;
|
2020-11-28 13:29:06 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
static void racingStart(CNSocket* sock, CNPacketData* data) {
|
2021-03-20 23:50:57 +00:00
|
|
|
auto req = (sP_CL2FE_REQ_EP_RACE_START*)data->buf;
|
2020-11-28 16:20:37 +00:00
|
|
|
|
2020-12-22 13:46:09 +00:00
|
|
|
if (NPCManager::NPCs.find(req->iStartEcomID) == NPCManager::NPCs.end())
|
|
|
|
return; // starting line agent not found
|
2020-12-22 05:37:04 +00:00
|
|
|
|
2020-12-22 13:46:09 +00:00
|
|
|
int mapNum = MAPNUM(NPCManager::NPCs[req->iStartEcomID]->instanceID);
|
|
|
|
if (EPData.find(mapNum) == EPData.end() || EPData[mapNum].EPID == 0)
|
|
|
|
return; // IZ not found
|
2020-12-22 05:37:04 +00:00
|
|
|
|
2020-12-22 13:46:09 +00:00
|
|
|
// make ongoing race entry
|
2021-04-14 02:17:21 +00:00
|
|
|
EPRace race = { {}, req->iEPRaceMode, req->iEPTicketItemSlotNum, getTime() / 1000 };
|
2020-12-22 13:46:09 +00:00
|
|
|
EPRaces[sock] = race;
|
2020-11-28 16:20:37 +00:00
|
|
|
|
2020-12-22 13:46:09 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_REP_EP_RACE_START_SUCC, resp);
|
|
|
|
resp.iStartTick = 0; // ignored
|
|
|
|
resp.iLimitTime = EPData[mapNum].maxTime;
|
2020-11-28 16:20:37 +00:00
|
|
|
|
2021-03-20 23:50:57 +00:00
|
|
|
sock->sendPacket(resp, P_FE2CL_REP_EP_RACE_START_SUCC);
|
2020-11-28 16:20:37 +00:00
|
|
|
}
|
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
static void racingGetPod(CNSocket* sock, CNPacketData* data) {
|
2020-12-22 13:46:09 +00:00
|
|
|
if (EPRaces.find(sock) == EPRaces.end())
|
|
|
|
return; // race not found
|
2020-11-28 16:20:37 +00:00
|
|
|
|
2021-03-20 23:50:57 +00:00
|
|
|
auto req = (sP_CL2FE_REQ_EP_GET_RING*)data->buf;
|
2020-11-28 16:20:37 +00:00
|
|
|
|
2021-04-14 02:17:21 +00:00
|
|
|
if (EPRaces[sock].collectedRings.count(req->iRingLID))
|
|
|
|
return; // can't collect the same ring twice
|
|
|
|
|
2020-12-22 13:46:09 +00:00
|
|
|
// without an anticheat system, we really don't have a choice but to honor the request
|
2021-04-14 02:17:21 +00:00
|
|
|
// TODO: proximity check so players can't cheat the race by replaying packets
|
|
|
|
EPRaces[sock].collectedRings.insert(req->iRingLID);
|
2020-11-28 16:20:37 +00:00
|
|
|
|
2020-12-22 13:46:09 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_REP_EP_GET_RING_SUCC, resp);
|
2020-11-28 16:20:37 +00:00
|
|
|
|
2021-04-14 02:17:21 +00:00
|
|
|
resp.iRingLID = req->iRingLID;
|
|
|
|
resp.iRingCount_Get = EPRaces[sock].collectedRings.size();
|
2020-12-22 13:46:09 +00:00
|
|
|
|
2021-03-20 23:50:57 +00:00
|
|
|
sock->sendPacket(resp, P_FE2CL_REP_EP_GET_RING_SUCC);
|
2020-11-28 16:20:37 +00:00
|
|
|
}
|
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
static void racingCancel(CNSocket* sock, CNPacketData* data) {
|
2020-12-22 13:46:09 +00:00
|
|
|
if (EPRaces.find(sock) == EPRaces.end())
|
|
|
|
return; // race not found
|
2020-11-28 16:20:37 +00:00
|
|
|
|
2021-04-25 07:40:36 +00:00
|
|
|
Player* plr = PlayerManager::getPlayer(sock);
|
2020-12-22 13:46:09 +00:00
|
|
|
EPRaces.erase(sock);
|
2020-11-28 16:20:37 +00:00
|
|
|
|
2020-12-22 13:46:09 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_REP_EP_RACE_CANCEL_SUCC, resp);
|
2021-03-20 23:50:57 +00:00
|
|
|
sock->sendPacket(resp, P_FE2CL_REP_EP_RACE_CANCEL_SUCC);
|
2021-04-25 07:40:36 +00:00
|
|
|
|
|
|
|
// we have to teleport the player back after this, otherwise they are unable to move
|
|
|
|
WarpLocation* respawnLoc = PlayerManager::getRespawnPoint(plr);
|
|
|
|
PlayerManager::sendPlayerTo(sock, respawnLoc->x, respawnLoc->y, respawnLoc->z, respawnLoc->instanceID);
|
2020-11-28 16:20:37 +00:00
|
|
|
}
|
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
static void racingEnd(CNSocket* sock, CNPacketData* data) {
|
2020-12-22 13:46:09 +00:00
|
|
|
if (EPRaces.find(sock) == EPRaces.end())
|
|
|
|
return; // race not found
|
|
|
|
|
2021-03-20 23:50:57 +00:00
|
|
|
auto req = (sP_CL2FE_REQ_EP_RACE_END*)data->buf;
|
2020-12-22 13:46:09 +00:00
|
|
|
Player* plr = PlayerManager::getPlayer(sock);
|
|
|
|
|
|
|
|
if (NPCManager::NPCs.find(req->iEndEcomID) == NPCManager::NPCs.end())
|
|
|
|
return; // finish line agent not found
|
|
|
|
|
|
|
|
int mapNum = MAPNUM(NPCManager::NPCs[req->iEndEcomID]->instanceID);
|
|
|
|
if (EPData.find(mapNum) == EPData.end() || EPData[mapNum].EPID == 0)
|
|
|
|
return; // IZ not found
|
|
|
|
|
|
|
|
uint64_t now = getTime() / 1000;
|
|
|
|
|
|
|
|
int timeDiff = now - EPRaces[sock].startTime;
|
2021-04-14 02:17:21 +00:00
|
|
|
int score = 500 * EPRaces[sock].collectedRings.size() - 10 * timeDiff;
|
2020-12-22 13:46:09 +00:00
|
|
|
if (score < 0) score = 0; // lol
|
|
|
|
int fm = score * plr->level * (1.0f / 36) * 0.3f;
|
|
|
|
|
|
|
|
// we submit the ranking first...
|
|
|
|
Database::RaceRanking postRanking = {};
|
|
|
|
postRanking.EPID = EPData[mapNum].EPID;
|
|
|
|
postRanking.PlayerID = plr->iID;
|
2021-04-14 02:17:21 +00:00
|
|
|
postRanking.RingCount = EPRaces[sock].collectedRings.size();
|
2020-12-22 13:46:09 +00:00
|
|
|
postRanking.Score = score;
|
|
|
|
postRanking.Time = timeDiff;
|
|
|
|
postRanking.Timestamp = getTimestamp();
|
|
|
|
Database::postRaceRanking(postRanking);
|
|
|
|
|
|
|
|
// ...then we get the top ranking, which may or may not be what we just submitted
|
|
|
|
Database::RaceRanking topRankingPlayer = Database::getTopRaceRanking(EPData[mapNum].EPID, plr->iID);
|
|
|
|
|
|
|
|
INITSTRUCT(sP_FE2CL_REP_EP_RACE_END_SUCC, resp);
|
|
|
|
|
|
|
|
// get rank scores and rewards
|
|
|
|
std::vector<int>* rankScores = &EPRewards[EPData[mapNum].EPID].first;
|
|
|
|
std::vector<int>* rankRewards = &EPRewards[EPData[mapNum].EPID].second;
|
|
|
|
|
|
|
|
// top ranking
|
|
|
|
int topRank = 0;
|
|
|
|
while (rankScores->at(topRank) > topRankingPlayer.Score)
|
|
|
|
topRank++;
|
|
|
|
|
|
|
|
resp.iEPTopRank = topRank + 1;
|
|
|
|
resp.iEPTopRingCount = topRankingPlayer.RingCount;
|
|
|
|
resp.iEPTopScore = topRankingPlayer.Score;
|
|
|
|
resp.iEPTopTime = topRankingPlayer.Time;
|
|
|
|
|
|
|
|
// this ranking
|
|
|
|
int rank = 0;
|
|
|
|
while (rankScores->at(rank) > postRanking.Score)
|
|
|
|
rank++;
|
|
|
|
|
|
|
|
resp.iEPRank = rank + 1;
|
|
|
|
resp.iEPRingCnt = postRanking.RingCount;
|
|
|
|
resp.iEPScore = postRanking.Score;
|
|
|
|
resp.iEPRaceTime = postRanking.Time;
|
|
|
|
resp.iEPRaceMode = EPRaces[sock].mode;
|
|
|
|
resp.iEPRewardFM = fm;
|
|
|
|
|
2021-03-16 22:29:13 +00:00
|
|
|
Missions::updateFusionMatter(sock, resp.iEPRewardFM);
|
2020-12-22 13:46:09 +00:00
|
|
|
|
|
|
|
resp.iFusionMatter = plr->fusionmatter;
|
|
|
|
resp.iFatigue = 50;
|
|
|
|
resp.iFatigue_Level = 1;
|
|
|
|
|
|
|
|
sItemReward reward;
|
2021-03-16 22:29:13 +00:00
|
|
|
reward.iSlotNum = Items::findFreeSlot(plr);
|
2020-12-22 13:46:09 +00:00
|
|
|
reward.eIL = 1;
|
|
|
|
sItemBase item;
|
|
|
|
item.iID = rankRewards->at(rank); // rank scores and rewards line up
|
2020-12-23 23:18:33 +00:00
|
|
|
item.iType = 9;
|
2020-12-22 13:46:09 +00:00
|
|
|
item.iOpt = 1;
|
2020-12-23 23:18:33 +00:00
|
|
|
item.iTimeLimit = 0;
|
2020-12-22 13:46:09 +00:00
|
|
|
reward.sItem = item;
|
|
|
|
|
|
|
|
if (reward.iSlotNum > -1 && reward.sItem.iID != 0) {
|
|
|
|
resp.RewardItem = reward;
|
|
|
|
plr->Inven[reward.iSlotNum] = item;
|
|
|
|
}
|
|
|
|
|
|
|
|
EPRaces.erase(sock);
|
2021-03-20 23:50:57 +00:00
|
|
|
sock->sendPacket(resp, P_FE2CL_REP_EP_RACE_END_SUCC);
|
2020-11-28 16:20:37 +00:00
|
|
|
}
|
2020-11-28 14:16:14 +00:00
|
|
|
|
2021-03-16 22:29:13 +00:00
|
|
|
void Racing::init() {
|
2021-03-16 21:06:10 +00:00
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_EP_RACE_START, racingStart);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_EP_GET_RING, racingGetPod);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_EP_RACE_CANCEL, racingCancel);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_EP_RACE_END, racingEnd);
|
|
|
|
}
|