2020-09-09 17:06:22 +00:00
|
|
|
#include "TableData.hpp"
|
|
|
|
#include "NPCManager.hpp"
|
2021-03-16 22:29:13 +00:00
|
|
|
#include "Transport.hpp"
|
|
|
|
#include "Items.hpp"
|
2020-09-09 17:06:22 +00:00
|
|
|
#include "settings.hpp"
|
2021-03-16 22:29:13 +00:00
|
|
|
#include "Missions.hpp"
|
|
|
|
#include "Chunking.hpp"
|
|
|
|
#include "Nanos.hpp"
|
|
|
|
#include "Racing.hpp"
|
2021-04-16 17:28:59 +00:00
|
|
|
#include "Vendors.hpp"
|
2021-03-13 20:22:29 +00:00
|
|
|
#include "Abilities.hpp"
|
2021-03-17 21:28:24 +00:00
|
|
|
#include "Eggs.hpp"
|
2020-09-09 19:09:01 +00:00
|
|
|
|
2021-03-12 17:50:21 +00:00
|
|
|
#include "JSON.hpp"
|
2020-09-09 17:06:22 +00:00
|
|
|
|
|
|
|
#include <fstream>
|
2020-12-19 00:18:39 +00:00
|
|
|
#include <cmath>
|
2020-09-09 17:06:22 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
using namespace TableData;
|
|
|
|
|
2020-10-03 15:21:36 +00:00
|
|
|
std::map<int32_t, std::vector<WarpLocation>> TableData::RunningSkywayRoutes;
|
2020-10-06 21:59:12 +00:00
|
|
|
std::map<int32_t, int> TableData::RunningNPCRotations;
|
2020-10-12 01:53:01 +00:00
|
|
|
std::map<int32_t, int> TableData::RunningNPCMapNumbers;
|
2020-10-07 17:29:59 +00:00
|
|
|
std::map<int32_t, BaseNPC*> TableData::RunningMobs;
|
2020-11-25 15:41:10 +00:00
|
|
|
std::map<int32_t, BaseNPC*> TableData::RunningGroups;
|
2020-11-08 08:42:49 +00:00
|
|
|
std::map<int32_t, BaseNPC*> TableData::RunningEggs;
|
2020-10-03 02:05:20 +00:00
|
|
|
|
2020-10-16 19:47:43 +00:00
|
|
|
class TableException : public std::exception {
|
|
|
|
public:
|
|
|
|
std::string msg;
|
|
|
|
|
|
|
|
TableException(std::string m) : std::exception() { msg = m; }
|
|
|
|
|
|
|
|
const char *what() const throw() { return msg.c_str(); }
|
|
|
|
};
|
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
/*
|
|
|
|
* Create a full and properly-paced path by interpolating between keyframes.
|
|
|
|
*/
|
|
|
|
static void constructPathSkyway(nlohmann::json::iterator _pathData) {
|
|
|
|
auto pathData = _pathData.value();
|
|
|
|
// Interpolate
|
|
|
|
nlohmann::json pathPoints = pathData["points"];
|
|
|
|
std::queue<WarpLocation> points;
|
|
|
|
nlohmann::json::iterator _point = pathPoints.begin();
|
|
|
|
auto point = _point.value();
|
|
|
|
WarpLocation last = { point["iX"] , point["iY"] , point["iZ"] }; // start pos
|
|
|
|
// use some for loop trickery; start position should not be a point
|
|
|
|
for (_point++; _point != pathPoints.end(); _point++) {
|
|
|
|
point = _point.value();
|
|
|
|
WarpLocation coords = { point["iX"] , point["iY"] , point["iZ"] };
|
2021-03-16 22:29:13 +00:00
|
|
|
Transport::lerp(&points, last, coords, pathData["iMonkeySpeed"]);
|
2021-03-16 21:06:10 +00:00
|
|
|
points.push(coords); // add keyframe to the queue
|
|
|
|
last = coords; // update start pos
|
|
|
|
}
|
2021-03-16 22:29:13 +00:00
|
|
|
Transport::SkywayPaths[pathData["iRouteID"]] = points;
|
2021-03-16 21:06:10 +00:00
|
|
|
}
|
2020-09-09 17:06:22 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
static void constructPathNPC(nlohmann::json::iterator _pathData, int32_t id=0) {
|
|
|
|
auto pathData = _pathData.value();
|
|
|
|
// Interpolate
|
|
|
|
nlohmann::json pathPoints = pathData["points"];
|
|
|
|
std::queue<WarpLocation> points;
|
|
|
|
nlohmann::json::iterator _point = pathPoints.begin();
|
|
|
|
auto point = _point.value();
|
|
|
|
WarpLocation from = { point["iX"] , point["iY"] , point["iZ"] }; // point A coords
|
|
|
|
int stopTime = point["stop"];
|
|
|
|
for (_point++; _point != pathPoints.end(); _point++) { // loop through all point Bs
|
|
|
|
point = _point.value();
|
|
|
|
for(int i = 0; i < stopTime + 1; i++) // repeat point if it's a stop
|
|
|
|
points.push(from); // add point A to the queue
|
|
|
|
WarpLocation to = { point["iX"] , point["iY"] , point["iZ"] }; // point B coords
|
2021-03-16 22:29:13 +00:00
|
|
|
Transport::lerp(&points, from, to, pathData["iBaseSpeed"]); // lerp from A to B
|
2021-03-16 21:06:10 +00:00
|
|
|
from = to; // update point A
|
|
|
|
stopTime = point["stop"];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (id == 0)
|
|
|
|
id = pathData["iNPCID"];
|
|
|
|
|
2021-03-16 22:29:13 +00:00
|
|
|
Transport::NPCQueues[id] = points;
|
2021-03-16 21:06:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Load paths from paths JSON.
|
|
|
|
*/
|
|
|
|
static void loadPaths(int* nextId) {
|
2020-09-09 17:06:22 +00:00
|
|
|
try {
|
2021-03-16 21:06:10 +00:00
|
|
|
std::ifstream inFile(settings::PATHJSON);
|
|
|
|
nlohmann::json pathData;
|
2020-09-09 17:06:22 +00:00
|
|
|
|
|
|
|
// read file into json
|
2021-03-16 21:06:10 +00:00
|
|
|
inFile >> pathData;
|
|
|
|
|
|
|
|
// skyway paths
|
|
|
|
nlohmann::json pathDataSkyway = pathData["skyway"];
|
|
|
|
for (nlohmann::json::iterator skywayPath = pathDataSkyway.begin(); skywayPath != pathDataSkyway.end(); skywayPath++) {
|
|
|
|
constructPathSkyway(skywayPath);
|
|
|
|
}
|
2021-03-16 22:29:13 +00:00
|
|
|
std::cout << "[INFO] Loaded " << Transport::SkywayPaths.size() << " skyway paths" << std::endl;
|
2021-03-16 21:06:10 +00:00
|
|
|
|
|
|
|
// slider circuit
|
|
|
|
nlohmann::json pathDataSlider = pathData["slider"];
|
|
|
|
// lerp between keyframes
|
|
|
|
std::queue<WarpLocation> route;
|
|
|
|
// initial point
|
|
|
|
nlohmann::json::iterator _point = pathDataSlider.begin(); // iterator
|
|
|
|
auto point = _point.value();
|
|
|
|
WarpLocation from = { point["iX"] , point["iY"] , point["iZ"] }; // point A coords
|
|
|
|
int stopTime = point["stop"] ? SLIDER_STOP_TICKS : 0; // arbitrary stop length
|
|
|
|
// remaining points
|
|
|
|
for (_point++; _point != pathDataSlider.end(); _point++) { // loop through all point Bs
|
|
|
|
point = _point.value();
|
|
|
|
for (int i = 0; i < stopTime + 1; i++) { // repeat point if it's a stop
|
|
|
|
route.push(from); // add point A to the queue
|
2020-12-31 16:26:17 +00:00
|
|
|
}
|
2021-03-16 21:06:10 +00:00
|
|
|
WarpLocation to = { point["iX"] , point["iY"] , point["iZ"] }; // point B coords
|
|
|
|
// we may need to change this later; right now, the speed is cut before and after stops (no accel)
|
|
|
|
float curve = 1;
|
|
|
|
if (stopTime > 0) { // point A is a stop
|
|
|
|
curve = 0.375f;//2.0f;
|
|
|
|
} else if (point["stop"]) { // point B is a stop
|
|
|
|
curve = 0.375f;//0.35f;
|
|
|
|
}
|
2021-03-16 22:29:13 +00:00
|
|
|
Transport::lerp(&route, from, to, SLIDER_SPEED * curve, 1); // lerp from A to B (arbitrary speed)
|
2021-03-16 21:06:10 +00:00
|
|
|
from = to; // update point A
|
|
|
|
stopTime = point["stop"] ? SLIDER_STOP_TICKS : 0; // set stop ticks for next point A
|
|
|
|
}
|
|
|
|
// Uniform distance calculation
|
|
|
|
int passedDistance = 0;
|
|
|
|
// initial point
|
|
|
|
int pos = 0;
|
|
|
|
WarpLocation lastPoint = route.front();
|
|
|
|
route.pop();
|
|
|
|
route.push(lastPoint);
|
|
|
|
for (pos = 1; pos < route.size(); pos++) {
|
|
|
|
WarpLocation point = route.front();
|
|
|
|
passedDistance += hypot(point.x - lastPoint.x, point.y - lastPoint.y);
|
|
|
|
if (passedDistance >= SLIDER_GAP_SIZE) { // space them out uniformaly
|
|
|
|
passedDistance -= SLIDER_GAP_SIZE; // step down
|
|
|
|
// spawn a slider
|
2021-03-22 16:53:46 +00:00
|
|
|
Bus* slider = new Bus(point.x, point.y, point.z, 0, INSTANCE_OVERWORLD, 1, (*nextId)++);
|
2021-03-16 21:06:10 +00:00
|
|
|
NPCManager::NPCs[slider->appearanceData.iNPC_ID] = slider;
|
2021-04-14 00:57:24 +00:00
|
|
|
NPCManager::updateNPCPosition(slider->appearanceData.iNPC_ID, slider->x, slider->y, slider->z, INSTANCE_OVERWORLD, 0);
|
2021-03-16 22:29:13 +00:00
|
|
|
Transport::NPCQueues[slider->appearanceData.iNPC_ID] = route;
|
2021-03-16 21:06:10 +00:00
|
|
|
}
|
|
|
|
// rotate
|
|
|
|
route.pop();
|
|
|
|
route.push(point);
|
|
|
|
lastPoint = point;
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2021-04-07 00:43:43 +00:00
|
|
|
// npc paths (pending refactor)
|
2021-03-16 21:06:10 +00:00
|
|
|
nlohmann::json pathDataNPC = pathData["npc"];
|
|
|
|
/*
|
|
|
|
for (nlohmann::json::iterator npcPath = pathDataNPC.begin(); npcPath != pathDataNPC.end(); npcPath++) {
|
|
|
|
constructPathNPC(npcPath);
|
|
|
|
}
|
|
|
|
*/
|
2020-09-09 17:06:22 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
// mob paths
|
|
|
|
pathDataNPC = pathData["mob"];
|
|
|
|
for (nlohmann::json::iterator npcPath = pathDataNPC.begin(); npcPath != pathDataNPC.end(); npcPath++) {
|
2021-04-07 00:43:43 +00:00
|
|
|
for (auto& pair : NPCManager::NPCs) {
|
|
|
|
if (pair.second->type != EntityType::MOB)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
Mob* mob = (Mob*)pair.second;
|
|
|
|
if (mob->appearanceData.iNPCType == npcPath.value()["iNPCType"]) {
|
|
|
|
std::cout << "[INFO] Using static path for mob " << mob->appearanceData.iNPCType << " with ID " << pair.first << std::endl;
|
2021-03-16 21:06:10 +00:00
|
|
|
|
|
|
|
auto firstPoint = npcPath.value()["points"][0];
|
2021-04-07 00:43:43 +00:00
|
|
|
if (firstPoint["iX"] != mob->spawnX || firstPoint["iY"] != mob->spawnY) {
|
2021-03-16 21:06:10 +00:00
|
|
|
std::cout << "[FATAL] The first point of the route for mob " << pair.first <<
|
2021-04-07 00:43:43 +00:00
|
|
|
" (type " << mob->appearanceData.iNPCType << ") does not correspond with its spawn point." << std::endl;
|
2021-03-16 21:06:10 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
constructPathNPC(npcPath, pair.first);
|
2021-04-07 00:43:43 +00:00
|
|
|
mob->staticPath = true;
|
2021-03-16 21:06:10 +00:00
|
|
|
break; // only one NPC per path
|
|
|
|
}
|
|
|
|
}
|
2020-09-09 17:06:22 +00:00
|
|
|
}
|
2021-03-16 22:29:13 +00:00
|
|
|
std::cout << "[INFO] Loaded " << Transport::NPCQueues.size() << " NPC paths" << std::endl;
|
2020-09-09 17:06:22 +00:00
|
|
|
}
|
|
|
|
catch (const std::exception& err) {
|
2021-03-16 21:06:10 +00:00
|
|
|
std::cerr << "[FATAL] Malformed paths.json file! Reason:" << err.what() << std::endl;
|
2020-12-06 04:25:23 +00:00
|
|
|
exit(1);
|
2020-09-09 17:06:22 +00:00
|
|
|
}
|
2021-03-16 21:06:10 +00:00
|
|
|
}
|
2020-09-09 17:06:22 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
/*
|
|
|
|
* Load drops data from JSON.
|
|
|
|
* This has to be called after reading xdt because it reffers to ItemData!!!
|
|
|
|
*/
|
|
|
|
static void loadDrops() {
|
2020-09-09 19:09:01 +00:00
|
|
|
try {
|
2021-03-16 21:06:10 +00:00
|
|
|
std::ifstream inFile(settings::DROPSJSON);
|
|
|
|
nlohmann::json dropData;
|
2020-09-13 20:26:16 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
// read file into json
|
|
|
|
inFile >> dropData;
|
2020-09-13 20:26:16 +00:00
|
|
|
|
2021-03-28 20:57:43 +00:00
|
|
|
// CrateDropChances
|
|
|
|
nlohmann::json crateDropChances = dropData["CrateDropChances"];
|
|
|
|
for (nlohmann::json::iterator _crateDropChance = crateDropChances.begin(); _crateDropChance != crateDropChances.end(); _crateDropChance++) {
|
|
|
|
auto crateDropChance = _crateDropChance.value();
|
|
|
|
CrateDropChance toAdd = {};
|
|
|
|
|
|
|
|
toAdd.dropChance = (int)crateDropChance["DropChance"];
|
|
|
|
toAdd.dropChanceTotal = (int)crateDropChance["DropChanceTotal"];
|
|
|
|
|
|
|
|
nlohmann::json crateWeights = crateDropChance["CrateTypeDropWeights"];
|
|
|
|
for (nlohmann::json::iterator _crateWeight = crateWeights.begin(); _crateWeight != crateWeights.end(); _crateWeight++)
|
2021-03-29 06:22:23 +00:00
|
|
|
toAdd.crateTypeDropWeights.push_back((int)_crateWeight.value());
|
2021-03-28 20:57:43 +00:00
|
|
|
|
|
|
|
Items::CrateDropChances[(int)crateDropChance["CrateDropChanceID"]] = toAdd;
|
|
|
|
}
|
|
|
|
|
|
|
|
// CrateDropTypes
|
|
|
|
nlohmann::json crateDropTypes = dropData["CrateDropTypes"];
|
|
|
|
for (nlohmann::json::iterator _crateDropType = crateDropTypes.begin(); _crateDropType != crateDropTypes.end(); _crateDropType++) {
|
|
|
|
auto crateDropType = _crateDropType.value();
|
|
|
|
std::vector<int> toAdd;
|
|
|
|
|
|
|
|
nlohmann::json crateIds = crateDropType["CrateIDs"];
|
|
|
|
for (nlohmann::json::iterator _crateId = crateIds.begin(); _crateId != crateIds.end(); _crateId++)
|
|
|
|
toAdd.push_back((int)_crateId.value());
|
|
|
|
|
|
|
|
Items::CrateDropTypes[(int)crateDropType["CrateDropTypeID"]] = toAdd;
|
|
|
|
}
|
|
|
|
|
|
|
|
// MiscDropChances
|
|
|
|
nlohmann::json miscDropChances = dropData["MiscDropChances"];
|
|
|
|
for (nlohmann::json::iterator _miscDropChance = miscDropChances.begin(); _miscDropChance != miscDropChances.end(); _miscDropChance++) {
|
|
|
|
auto miscDropChance = _miscDropChance.value();
|
|
|
|
|
|
|
|
Items::MiscDropChances[(int)miscDropChance["MiscDropChanceID"]] = {
|
|
|
|
(int)miscDropChance["PotionDropChance"],
|
|
|
|
(int)miscDropChance["PotionDropChanceTotal"],
|
|
|
|
(int)miscDropChance["BoostDropChance"],
|
|
|
|
(int)miscDropChance["BoostDropChanceTotal"],
|
|
|
|
(int)miscDropChance["TaroDropChance"],
|
|
|
|
(int)miscDropChance["TaroDropChanceTotal"],
|
|
|
|
(int)miscDropChance["FMDropChance"],
|
|
|
|
(int)miscDropChance["FMDropChanceTotal"]
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// MiscDropTypes
|
|
|
|
nlohmann::json miscDropTypes = dropData["MiscDropTypes"];
|
|
|
|
for (nlohmann::json::iterator _miscDropType = miscDropTypes.begin(); _miscDropType != miscDropTypes.end(); _miscDropType++) {
|
|
|
|
auto miscDropType = _miscDropType.value();
|
|
|
|
|
|
|
|
Items::MiscDropTypes[(int)miscDropType["MiscDropTypeID"]] = {
|
|
|
|
(int)miscDropType["PotionAmount"],
|
|
|
|
(int)miscDropType["BoostAmount"],
|
|
|
|
(int)miscDropType["TaroAmount"],
|
|
|
|
(int)miscDropType["FMAmount"]
|
|
|
|
};
|
2020-09-13 20:26:16 +00:00
|
|
|
}
|
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
// MobDrops
|
|
|
|
nlohmann::json mobDrops = dropData["MobDrops"];
|
2021-03-28 20:57:43 +00:00
|
|
|
for (nlohmann::json::iterator _mobDrop = mobDrops.begin(); _mobDrop != mobDrops.end(); _mobDrop++) {
|
|
|
|
auto mobDrop = _mobDrop.value();
|
|
|
|
|
|
|
|
Items::MobDrops[(int)mobDrop["MobDropID"]] = {
|
|
|
|
(int)mobDrop["CrateDropChanceID"],
|
|
|
|
(int)mobDrop["CrateDropTypeID"],
|
|
|
|
(int)mobDrop["MiscDropChanceID"],
|
|
|
|
(int)mobDrop["MiscDropTypeID"],
|
|
|
|
};
|
|
|
|
}
|
2020-09-09 19:09:01 +00:00
|
|
|
|
2021-03-28 20:57:43 +00:00
|
|
|
// RarityWeights
|
|
|
|
nlohmann::json rarityWeights = dropData["RarityWeights"];
|
|
|
|
for (nlohmann::json::iterator _rarityWeightsObject = rarityWeights.begin(); _rarityWeightsObject != rarityWeights.end(); _rarityWeightsObject++) {
|
|
|
|
auto rarityWeightsObject = _rarityWeightsObject.value();
|
|
|
|
std::vector<int> toAdd;
|
2020-09-09 22:31:09 +00:00
|
|
|
|
2021-03-28 20:57:43 +00:00
|
|
|
nlohmann::json weights = rarityWeightsObject["Weights"];
|
|
|
|
for (nlohmann::json::iterator _weight = weights.begin(); _weight != weights.end(); _weight++)
|
|
|
|
toAdd.push_back((int)_weight.value());
|
|
|
|
|
|
|
|
Items::RarityWeights[(int)rarityWeightsObject["RarityWeightID"]] = toAdd;
|
2020-09-09 19:09:01 +00:00
|
|
|
}
|
|
|
|
|
2021-03-30 02:48:31 +00:00
|
|
|
// ItemSets
|
|
|
|
nlohmann::json itemSets = dropData["ItemSets"];
|
|
|
|
for (nlohmann::json::iterator _itemSet = itemSets.begin(); _itemSet != itemSets.end(); _itemSet++) {
|
|
|
|
auto itemSet = _itemSet.value();
|
|
|
|
ItemSet toAdd = {};
|
2020-09-13 22:54:47 +00:00
|
|
|
|
2021-03-30 02:48:31 +00:00
|
|
|
toAdd.ignoreRarity = (bool)itemSet["IgnoreRarity"];
|
|
|
|
toAdd.ignoreGender = (bool)itemSet["IgnoreGender"];
|
|
|
|
toAdd.defaultItemWeight = (int)itemSet["DefaultItemWeight"];
|
2021-03-28 20:57:43 +00:00
|
|
|
|
2021-03-30 02:48:31 +00:00
|
|
|
nlohmann::json alterRarityMap = itemSet["AlterRarityMap"];
|
|
|
|
for (nlohmann::json::iterator _alterRarityMapEntry = alterRarityMap.begin(); _alterRarityMapEntry != alterRarityMap.end(); _alterRarityMapEntry++)
|
|
|
|
toAdd.alterRarityMap[std::atoi(_alterRarityMapEntry.key().c_str())] = (int)_alterRarityMapEntry.value();
|
2021-03-28 20:57:43 +00:00
|
|
|
|
2021-03-30 02:48:31 +00:00
|
|
|
nlohmann::json alterGenderMap = itemSet["AlterGenderMap"];
|
|
|
|
for (nlohmann::json::iterator _alterGenderMapEntry = alterGenderMap.begin(); _alterGenderMapEntry != alterGenderMap.end(); _alterGenderMapEntry++)
|
|
|
|
toAdd.alterGenderMap[std::atoi(_alterGenderMapEntry.key().c_str())] = (int)_alterGenderMapEntry.value();
|
2021-03-28 20:57:43 +00:00
|
|
|
|
2021-03-30 02:48:31 +00:00
|
|
|
nlohmann::json alterItemWeightMap = itemSet["AlterItemWeightMap"];
|
|
|
|
for (nlohmann::json::iterator _alterItemWeightMapEntry = alterItemWeightMap.begin(); _alterItemWeightMapEntry != alterItemWeightMap.end(); _alterItemWeightMapEntry++)
|
|
|
|
toAdd.alterItemWeightMap[std::atoi(_alterItemWeightMapEntry.key().c_str())] = (int)_alterItemWeightMapEntry.value();
|
2021-03-28 20:57:43 +00:00
|
|
|
|
2021-03-30 02:48:31 +00:00
|
|
|
nlohmann::json itemReferenceIds = itemSet["ItemReferenceIDs"];
|
|
|
|
for (nlohmann::json::iterator itemReferenceId = itemReferenceIds.begin(); itemReferenceId != itemReferenceIds.end(); itemReferenceId++)
|
|
|
|
toAdd.itemReferenceIds.push_back((int)itemReferenceId.value());
|
2021-03-28 20:57:43 +00:00
|
|
|
|
2021-03-30 02:48:31 +00:00
|
|
|
Items::ItemSets[(int)itemSet["ItemSetID"]] = toAdd;
|
2020-09-14 04:25:14 +00:00
|
|
|
}
|
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
// Crates
|
|
|
|
nlohmann::json crates = dropData["Crates"];
|
|
|
|
for (nlohmann::json::iterator _crate = crates.begin(); _crate != crates.end(); _crate++) {
|
|
|
|
auto crate = _crate.value();
|
2020-09-23 19:44:27 +00:00
|
|
|
|
2021-03-28 20:57:43 +00:00
|
|
|
Items::Crates[(int)crate["CrateID"]] = {
|
2021-03-30 02:48:31 +00:00
|
|
|
(int)crate["ItemSetID"],
|
2021-03-28 20:57:43 +00:00
|
|
|
(int)crate["RarityWeightID"]
|
|
|
|
};
|
|
|
|
}
|
2020-09-23 19:44:27 +00:00
|
|
|
|
2021-03-29 06:22:23 +00:00
|
|
|
// ItemReferences
|
|
|
|
nlohmann::json itemReferences = dropData["ItemReferences"];
|
|
|
|
for (nlohmann::json::iterator _itemReference = itemReferences.begin(); _itemReference != itemReferences.end(); _itemReference++) {
|
|
|
|
auto itemReference = _itemReference.value();
|
2020-09-13 22:54:47 +00:00
|
|
|
|
2021-03-29 06:22:23 +00:00
|
|
|
int itemReferenceId = (int)itemReference["ItemReferenceID"];
|
|
|
|
int itemId = (int)itemReference["ItemID"];
|
|
|
|
int type = (int)itemReference["Type"];
|
|
|
|
|
|
|
|
// validate and fetch relevant fields as they're loaded
|
|
|
|
auto key = std::make_pair(itemId, type);
|
|
|
|
if (Items::ItemData.find(key) == Items::ItemData.end()) {
|
|
|
|
std::cout << "[WARN] Item-Type pair (" << key.first << ", " << key.second << ") specified by item reference "
|
|
|
|
<< itemReferenceId << " was not found, skipping..." << std::endl;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
Items::Item& item = Items::ItemData[key];
|
|
|
|
|
|
|
|
Items::ItemReferences[itemReferenceId] = {
|
|
|
|
itemId,
|
|
|
|
type,
|
|
|
|
item.rarity,
|
|
|
|
item.gender
|
2021-03-28 20:57:43 +00:00
|
|
|
};
|
2020-09-13 22:54:47 +00:00
|
|
|
}
|
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
#ifdef ACADEMY
|
2021-03-28 20:57:43 +00:00
|
|
|
// NanoCapsules
|
2021-03-16 21:06:10 +00:00
|
|
|
nlohmann::json capsules = dropData["NanoCapsules"];
|
|
|
|
for (nlohmann::json::iterator _capsule = capsules.begin(); _capsule != capsules.end(); _capsule++) {
|
|
|
|
auto capsule = _capsule.value();
|
2021-03-28 20:57:43 +00:00
|
|
|
Items::NanoCapsules[(int)capsule["CrateID"]] = (int)capsule["Nano"];
|
2020-09-16 00:30:01 +00:00
|
|
|
}
|
2021-03-16 21:06:10 +00:00
|
|
|
#endif
|
2020-10-24 20:28:35 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
// Racing rewards
|
|
|
|
nlohmann::json racing = dropData["Racing"];
|
|
|
|
for (nlohmann::json::iterator _race = racing.begin(); _race != racing.end(); _race++) {
|
|
|
|
auto race = _race.value();
|
|
|
|
int raceEPID = race["EPID"];
|
2020-10-24 20:28:35 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
// find the instance data corresponding to the EPID
|
|
|
|
int EPMap = -1;
|
2021-03-16 22:29:13 +00:00
|
|
|
for (auto it = Racing::EPData.begin(); it != Racing::EPData.end(); it++) {
|
2021-03-16 21:06:10 +00:00
|
|
|
if (it->second.EPID == raceEPID) {
|
|
|
|
EPMap = it->first;
|
|
|
|
}
|
|
|
|
}
|
2020-10-24 20:28:35 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
if (EPMap == -1) { // not found
|
|
|
|
std::cout << "[WARN] EP with ID " << raceEPID << " not found, skipping" << std::endl;
|
|
|
|
continue;
|
|
|
|
}
|
2020-11-07 23:22:48 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
// time limit isn't stored in the XDT, so we include it in the reward table instead
|
2021-03-16 22:29:13 +00:00
|
|
|
Racing::EPData[EPMap].maxTime = race["TimeLimit"];
|
2021-03-16 21:06:10 +00:00
|
|
|
|
|
|
|
// score cutoffs
|
|
|
|
std::vector<int> rankScores;
|
|
|
|
for (nlohmann::json::iterator _rankScore = race["RankScores"].begin(); _rankScore != race["RankScores"].end(); _rankScore++) {
|
|
|
|
rankScores.push_back((int)_rankScore.value());
|
2020-11-07 23:22:48 +00:00
|
|
|
}
|
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
// reward IDs for each rank
|
|
|
|
std::vector<int> rankRewards;
|
|
|
|
for (nlohmann::json::iterator _rankReward = race["Rewards"].begin(); _rankReward != race["Rewards"].end(); _rankReward++) {
|
|
|
|
rankRewards.push_back((int)_rankReward.value());
|
|
|
|
}
|
2020-11-07 23:22:48 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
if (rankScores.size() != 5 || rankScores.size() != rankRewards.size()) {
|
|
|
|
char buff[255];
|
|
|
|
sprintf(buff, "Race in EP %d doesn't have exactly 5 score/reward pairs", raceEPID);
|
|
|
|
throw TableException(std::string(buff));
|
|
|
|
}
|
2020-11-28 14:16:14 +00:00
|
|
|
|
2021-03-16 22:29:13 +00:00
|
|
|
Racing::EPRewards[raceEPID] = std::make_pair(rankScores, rankRewards);
|
2020-11-28 14:16:14 +00:00
|
|
|
}
|
|
|
|
|
2021-03-16 22:29:13 +00:00
|
|
|
std::cout << "[INFO] Loaded rewards for " << Racing::EPRewards.size() << " IZ races" << std::endl;
|
2020-11-28 14:16:14 +00:00
|
|
|
|
2021-03-28 20:57:43 +00:00
|
|
|
// CodeItems
|
|
|
|
nlohmann::json codes = dropData["CodeItems"];
|
|
|
|
for (nlohmann::json::iterator _code = codes.begin(); _code != codes.end(); _code++) {
|
|
|
|
auto code = _code.value();
|
|
|
|
std::string codeStr = code["Code"];
|
|
|
|
std::vector<std::pair<int32_t, int32_t>> itemVector;
|
|
|
|
|
2021-03-29 06:22:23 +00:00
|
|
|
nlohmann::json itemReferenceIds = code["ItemReferenceIDs"];
|
|
|
|
for (nlohmann::json::iterator _itemReferenceId = itemReferenceIds.begin(); _itemReferenceId != itemReferenceIds.end(); _itemReferenceId++) {
|
|
|
|
int itemReferenceId = (int)_itemReferenceId.value();
|
|
|
|
|
|
|
|
// validate and convert here
|
|
|
|
if (Items::ItemReferences.find(itemReferenceId) == Items::ItemReferences.end()) {
|
|
|
|
std::cout << "[WARN] Item reference " << itemReferenceId << " for code "
|
|
|
|
<< codeStr << " was not found, skipping..." << std::endl;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// no need to further check whether this is a real item or not, we already did this!
|
|
|
|
ItemReference& itemReference = Items::ItemReferences[itemReferenceId];
|
|
|
|
itemVector.push_back(std::make_pair(itemReference.itemId, itemReference.type));
|
|
|
|
}
|
2021-03-28 20:57:43 +00:00
|
|
|
|
|
|
|
Items::CodeItems[codeStr] = itemVector;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::cout << "[INFO] Loaded " << Items::Crates.size() << " Crates containing "
|
2021-03-29 06:22:23 +00:00
|
|
|
<< Items::ItemReferences.size() << " unique items" << std::endl;
|
2021-03-28 20:57:43 +00:00
|
|
|
|
2020-09-09 17:06:22 +00:00
|
|
|
}
|
|
|
|
catch (const std::exception& err) {
|
2021-03-16 21:06:10 +00:00
|
|
|
std::cerr << "[FATAL] Malformed drops.json file! Reason:" << err.what() << std::endl;
|
2020-12-06 04:25:23 +00:00
|
|
|
exit(1);
|
2020-09-09 17:06:22 +00:00
|
|
|
}
|
2021-03-16 21:06:10 +00:00
|
|
|
}
|
2020-09-16 23:43:48 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
static void loadEggs(int32_t* nextId) {
|
2020-09-16 23:43:48 +00:00
|
|
|
try {
|
2021-03-16 21:06:10 +00:00
|
|
|
std::ifstream inFile(settings::EGGSJSON);
|
|
|
|
nlohmann::json eggData;
|
2020-09-16 23:43:48 +00:00
|
|
|
|
|
|
|
// read file into json
|
2021-03-16 21:06:10 +00:00
|
|
|
inFile >> eggData;
|
2020-12-31 01:44:02 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
// EggTypes
|
|
|
|
nlohmann::json eggTypes = eggData["EggTypes"];
|
|
|
|
for (nlohmann::json::iterator _eggType = eggTypes.begin(); _eggType != eggTypes.end(); _eggType++) {
|
|
|
|
auto eggType = _eggType.value();
|
|
|
|
EggType toAdd = {};
|
|
|
|
toAdd.dropCrateId = (int)eggType["DropCrateId"];
|
|
|
|
toAdd.effectId = (int)eggType["EffectId"];
|
|
|
|
toAdd.duration = (int)eggType["Duration"];
|
|
|
|
toAdd.regen= (int)eggType["Regen"];
|
2021-03-17 21:28:24 +00:00
|
|
|
Eggs::EggTypes[(int)eggType["Id"]] = toAdd;
|
2021-03-16 21:06:10 +00:00
|
|
|
}
|
2020-09-16 23:43:48 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
// Egg instances
|
|
|
|
auto eggs = eggData["Eggs"];
|
2021-03-22 16:53:46 +00:00
|
|
|
int eggCount = 0;
|
2021-03-16 21:06:10 +00:00
|
|
|
for (auto _egg = eggs.begin(); _egg != eggs.end(); _egg++) {
|
|
|
|
auto egg = _egg.value();
|
|
|
|
int id = (*nextId)++;
|
|
|
|
uint64_t instanceID = egg.find("iMapNum") == egg.end() ? INSTANCE_OVERWORLD : (int)egg["iMapNum"];
|
2020-09-16 23:43:48 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
Egg* addEgg = new Egg((int)egg["iX"], (int)egg["iY"], (int)egg["iZ"], instanceID, (int)egg["iType"], id, false);
|
|
|
|
NPCManager::NPCs[id] = addEgg;
|
2021-03-22 16:53:46 +00:00
|
|
|
eggCount++;
|
2021-03-16 21:06:10 +00:00
|
|
|
NPCManager::updateNPCPosition(id, (int)egg["iX"], (int)egg["iY"], (int)egg["iZ"], instanceID, 0);
|
2020-11-24 00:28:22 +00:00
|
|
|
}
|
2020-11-15 07:06:29 +00:00
|
|
|
|
2021-03-22 16:53:46 +00:00
|
|
|
std::cout << "[INFO] Loaded " << eggCount << " eggs" <<std::endl;
|
2020-12-31 01:44:02 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
}
|
|
|
|
catch (const std::exception& err) {
|
|
|
|
std::cerr << "[FATAL] Malformed eggs.json file! Reason:" << err.what() << std::endl;
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
2020-11-24 00:28:22 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
// load gruntwork output; if it exists
|
|
|
|
static void loadGruntwork(int32_t *nextId) {
|
|
|
|
try {
|
|
|
|
std::ifstream inFile(settings::GRUNTWORKJSON);
|
|
|
|
nlohmann::json gruntwork;
|
2020-11-24 00:28:22 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
// skip if there's no gruntwork to load
|
|
|
|
if (inFile.fail())
|
|
|
|
return;
|
2020-11-15 07:06:29 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
inFile >> gruntwork;
|
|
|
|
|
|
|
|
// skyway paths
|
|
|
|
auto skyway = gruntwork["skyway"];
|
|
|
|
for (auto _route = skyway.begin(); _route != skyway.end(); _route++) {
|
|
|
|
auto route = _route.value();
|
|
|
|
std::vector<WarpLocation> points;
|
|
|
|
|
|
|
|
for (auto _point = route["points"].begin(); _point != route["points"].end(); _point++) {
|
|
|
|
auto point = _point.value();
|
|
|
|
points.push_back(WarpLocation{point["x"], point["y"], point["z"]});
|
|
|
|
}
|
|
|
|
|
|
|
|
RunningSkywayRoutes[(int)route["iRouteID"]] = points;
|
|
|
|
}
|
|
|
|
|
|
|
|
// npc rotations
|
|
|
|
auto npcRot = gruntwork["rotations"];
|
|
|
|
for (auto _rot = npcRot.begin(); _rot != npcRot.end(); _rot++) {
|
|
|
|
int32_t npcID = _rot.value()["iNPCID"];
|
|
|
|
int angle = _rot.value()["iAngle"];
|
|
|
|
if (NPCManager::NPCs.find(npcID) == NPCManager::NPCs.end())
|
|
|
|
continue; // NPC not found
|
|
|
|
BaseNPC* npc = NPCManager::NPCs[npcID];
|
|
|
|
npc->appearanceData.iAngle = angle;
|
|
|
|
|
|
|
|
RunningNPCRotations[npcID] = angle;
|
|
|
|
}
|
|
|
|
|
|
|
|
// npc map numbers
|
|
|
|
auto npcMap = gruntwork["instances"];
|
|
|
|
for (auto _map = npcMap.begin(); _map != npcMap.end(); _map++) {
|
|
|
|
int32_t npcID = _map.value()["iNPCID"];
|
|
|
|
uint64_t instanceID = _map.value()["iMapNum"];
|
|
|
|
if (NPCManager::NPCs.find(npcID) == NPCManager::NPCs.end())
|
|
|
|
continue; // NPC not found
|
|
|
|
BaseNPC* npc = NPCManager::NPCs[npcID];
|
2021-04-14 00:57:24 +00:00
|
|
|
NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, npc->x, npc->y,
|
|
|
|
npc->z, instanceID, npc->appearanceData.iAngle);
|
2021-03-16 21:06:10 +00:00
|
|
|
|
|
|
|
RunningNPCMapNumbers[npcID] = instanceID;
|
|
|
|
}
|
|
|
|
|
|
|
|
// mobs
|
|
|
|
auto mobs = gruntwork["mobs"];
|
|
|
|
for (auto _mob = mobs.begin(); _mob != mobs.end(); _mob++) {
|
|
|
|
auto mob = _mob.value();
|
|
|
|
BaseNPC *npc;
|
|
|
|
int id = (*nextId)++;
|
|
|
|
uint64_t instanceID = mob.find("iMapNum") == mob.end() ? INSTANCE_OVERWORLD : (int)mob["iMapNum"];
|
|
|
|
|
|
|
|
if (NPCManager::NPCData[(int)mob["iNPCType"]]["m_iTeam"] == 2) {
|
|
|
|
npc = new Mob(mob["iX"], mob["iY"], mob["iZ"], instanceID, mob["iNPCType"],
|
|
|
|
NPCManager::NPCData[(int)mob["iNPCType"]], id);
|
|
|
|
|
|
|
|
// re-enable respawning
|
|
|
|
((Mob*)npc)->summoned = false;
|
|
|
|
} else {
|
|
|
|
npc = new BaseNPC(mob["iX"], mob["iY"], mob["iZ"], mob["iAngle"], instanceID, mob["iNPCType"], id);
|
|
|
|
}
|
|
|
|
|
|
|
|
NPCManager::NPCs[npc->appearanceData.iNPC_ID] = npc;
|
|
|
|
RunningMobs[npc->appearanceData.iNPC_ID] = npc;
|
|
|
|
NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, mob["iX"], mob["iY"], mob["iZ"], instanceID, mob["iAngle"]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// mob groups
|
|
|
|
auto groups = gruntwork["groups"];
|
|
|
|
for (auto _group = groups.begin(); _group != groups.end(); _group++) {
|
|
|
|
auto leader = _group.value();
|
|
|
|
auto td = NPCManager::NPCData[(int)leader["iNPCType"]];
|
|
|
|
uint64_t instanceID = leader.find("iMapNum") == leader.end() ? INSTANCE_OVERWORLD : (int)leader["iMapNum"];
|
|
|
|
|
|
|
|
Mob* tmp = new Mob(leader["iX"], leader["iY"], leader["iZ"], leader["iAngle"], instanceID, leader["iNPCType"], td, *nextId);
|
|
|
|
|
|
|
|
// re-enable respawning
|
|
|
|
((Mob*)tmp)->summoned = false;
|
|
|
|
|
|
|
|
NPCManager::NPCs[*nextId] = tmp;
|
|
|
|
NPCManager::updateNPCPosition(*nextId, leader["iX"], leader["iY"], leader["iZ"], instanceID, leader["iAngle"]);
|
|
|
|
|
|
|
|
tmp->groupLeader = *nextId;
|
|
|
|
|
|
|
|
(*nextId)++;
|
2020-11-24 00:28:22 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
auto followers = leader["aFollowers"];
|
2020-11-24 00:28:22 +00:00
|
|
|
if (followers.size() < 5) {
|
|
|
|
int followerCount = 0;
|
|
|
|
for (nlohmann::json::iterator _fol = followers.begin(); _fol != followers.end(); _fol++) {
|
|
|
|
auto follower = _fol.value();
|
|
|
|
auto tdFol = NPCManager::NPCData[(int)follower["iNPCType"]];
|
2021-03-16 21:06:10 +00:00
|
|
|
Mob* tmpFol = new Mob((int)leader["iX"] + (int)follower["iOffsetX"], (int)leader["iY"] + (int)follower["iOffsetY"], leader["iZ"], leader["iAngle"], instanceID, follower["iNPCType"], tdFol, *nextId);
|
2020-11-24 00:28:22 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
// re-enable respawning
|
|
|
|
((Mob*)tmp)->summoned = false;
|
|
|
|
|
|
|
|
NPCManager::NPCs[*nextId] = tmpFol;
|
|
|
|
NPCManager::updateNPCPosition(*nextId, (int)leader["iX"] + (int)follower["iOffsetX"], (int)leader["iY"] + (int)follower["iOffsetY"], leader["iZ"], instanceID, leader["iAngle"]);
|
2020-11-24 00:28:22 +00:00
|
|
|
|
|
|
|
tmpFol->offsetX = follower.find("iOffsetX") == follower.end() ? 0 : (int)follower["iOffsetX"];
|
|
|
|
tmpFol->offsetY = follower.find("iOffsetY") == follower.end() ? 0 : (int)follower["iOffsetY"];
|
|
|
|
tmpFol->groupLeader = tmp->appearanceData.iNPC_ID;
|
2021-03-16 21:06:10 +00:00
|
|
|
tmp->groupMember[followerCount++] = *nextId;
|
2020-11-24 00:28:22 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
(*nextId)++;
|
2020-11-24 00:28:22 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-16 21:06:10 +00:00
|
|
|
else {
|
|
|
|
std::cout << "[WARN] Mob group leader with ID " << *nextId << " has too many followers (" << followers.size() << ")\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
RunningGroups[tmp->appearanceData.iNPC_ID] = tmp; // store as running
|
2020-09-16 23:43:48 +00:00
|
|
|
}
|
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
auto eggs = gruntwork["eggs"];
|
|
|
|
for (auto _egg = eggs.begin(); _egg != eggs.end(); _egg++) {
|
|
|
|
auto egg = _egg.value();
|
|
|
|
int id = (*nextId)++;
|
|
|
|
uint64_t instanceID = egg.find("iMapNum") == egg.end() ? INSTANCE_OVERWORLD : (int)egg["iMapNum"];
|
|
|
|
|
|
|
|
Egg* addEgg = new Egg((int)egg["iX"], (int)egg["iY"], (int)egg["iZ"], instanceID, (int)egg["iType"], id, false);
|
|
|
|
NPCManager::NPCs[id] = addEgg;
|
|
|
|
NPCManager::updateNPCPosition(id, (int)egg["iX"], (int)egg["iY"], (int)egg["iZ"], instanceID, 0);
|
|
|
|
RunningEggs[id] = addEgg;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "[INFO] Loaded gruntwork.json" << std::endl;
|
2020-09-16 23:43:48 +00:00
|
|
|
}
|
|
|
|
catch (const std::exception& err) {
|
2021-03-16 21:06:10 +00:00
|
|
|
std::cerr << "[FATAL] Malformed gruntwork.json file! Reason:" << err.what() << std::endl;
|
2020-12-06 04:25:23 +00:00
|
|
|
exit(1);
|
2020-09-16 23:43:48 +00:00
|
|
|
}
|
2021-03-16 21:06:10 +00:00
|
|
|
}
|
2020-09-24 22:36:25 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
void TableData::init() {
|
|
|
|
int32_t nextId = 0;
|
|
|
|
|
|
|
|
// load NPCs from NPC.json
|
2020-12-12 21:21:55 +00:00
|
|
|
try {
|
2021-03-16 21:06:10 +00:00
|
|
|
std::ifstream inFile(settings::NPCJSON);
|
2020-12-12 21:21:55 +00:00
|
|
|
nlohmann::json npcData;
|
|
|
|
|
|
|
|
// read file into json
|
|
|
|
inFile >> npcData;
|
|
|
|
npcData = npcData["NPCs"];
|
|
|
|
for (nlohmann::json::iterator _npc = npcData.begin(); _npc != npcData.end(); _npc++) {
|
|
|
|
auto npc = _npc.value();
|
2021-04-24 15:36:33 +00:00
|
|
|
int instanceID = npc.find("iMapNum") == npc.end() ? INSTANCE_OVERWORLD : (int)npc["iMapNum"];
|
2021-03-16 21:06:10 +00:00
|
|
|
#ifdef ACADEMY
|
|
|
|
// do not spawn NPCs in the future
|
2021-04-24 15:36:33 +00:00
|
|
|
if (npc["iX"] > 512000 && npc["iY"] < 256000) {
|
2021-03-16 21:06:10 +00:00
|
|
|
nextId++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#endif
|
2021-04-24 15:36:33 +00:00
|
|
|
BaseNPC *tmp = new BaseNPC(npc["iX"], npc["iY"], npc["iZ"], npc["iAngle"], instanceID, npc["iNPCType"], nextId);
|
2020-12-12 21:21:55 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
NPCManager::NPCs[nextId] = tmp;
|
2021-04-24 15:36:33 +00:00
|
|
|
NPCManager::updateNPCPosition(nextId, npc["iX"], npc["iY"], npc["iZ"], instanceID, npc["iAngle"]);
|
2020-12-12 21:21:55 +00:00
|
|
|
nextId++;
|
|
|
|
|
2021-04-24 15:36:33 +00:00
|
|
|
if (npc["iNPCType"] == 641 || npc["iNPCType"] == 642)
|
|
|
|
NPCManager::RespawnPoints.push_back({ npc["iX"], npc["iY"], ((int)npc["iZ"]) + RESURRECT_HEIGHT, instanceID });
|
2020-12-12 21:21:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (const std::exception& err) {
|
2021-03-16 21:06:10 +00:00
|
|
|
std::cerr << "[FATAL] Malformed NPCs.json file! Reason:" << err.what() << std::endl;
|
2020-12-12 21:21:55 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
// load everything else from xdttable
|
|
|
|
std::cout << "[INFO] Parsing xdt.json..." << std::endl;
|
|
|
|
std::ifstream infile(settings::XDTJSON);
|
|
|
|
nlohmann::json xdtData;
|
functional crates (no more plungers) (#133)
* FM, Taros and Boosts awards from killing mobs should be pretty
accurate now. A temporary formula for adjusting player/mob level gap is
implemented, but it will probably need to be adjusted in the future
* Mobs now drop correct crates
* Crates can be opened and give you correct items This includes
regular mob crates, world boss crates, mission crates, IZ race crates,
E.G.G.E.R.s, golden Eggs, and Event Crates. Keep in mind that neither
IZ races or golden Eggs are implemented, but if you spawn such a crate
it can be opened.
* All data is read from a json file, for which I'm going to release a
tool soon so it's easily adjustable
* There is a new setting for enabling events, which enables dropping
extra event crates These are Knishmas, Halloween and Easter
2020-10-10 17:18:47 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
// read file into json
|
|
|
|
infile >> xdtData;
|
2020-10-24 19:48:55 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
// data we'll need for summoned mobs
|
|
|
|
NPCManager::NPCData = xdtData["m_pNpcTable"]["m_pNpcData"];
|
2020-10-13 19:44:43 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
try {
|
|
|
|
// load warps
|
|
|
|
nlohmann::json warpData = xdtData["m_pInstanceTable"]["m_pWarpData"];
|
2020-10-06 19:53:21 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
for (nlohmann::json::iterator _warp = warpData.begin(); _warp != warpData.end(); _warp++) {
|
|
|
|
auto warp = _warp.value();
|
|
|
|
WarpLocation warpLoc = { warp["m_iToX"], warp["m_iToY"], warp["m_iToZ"], warp["m_iToMapNum"], warp["m_iIsInstance"], warp["m_iLimit_TaskID"], warp["m_iNpcNumber"] };
|
|
|
|
int warpID = warp["m_iWarpNumber"];
|
|
|
|
NPCManager::Warps[warpID] = warpLoc;
|
|
|
|
}
|
2020-09-09 19:09:01 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
std::cout << "[INFO] Populated " << NPCManager::Warps.size() << " Warps" << std::endl;
|
2020-09-22 01:03:48 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
// load transport routes and locations
|
|
|
|
nlohmann::json transRouteData = xdtData["m_pTransportationTable"]["m_pTransportationData"];
|
|
|
|
nlohmann::json transLocData = xdtData["m_pTransportationTable"]["m_pTransportationWarpLocation"];
|
2020-09-22 01:03:48 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
for (nlohmann::json::iterator _tLoc = transLocData.begin(); _tLoc != transLocData.end(); _tLoc++) {
|
|
|
|
auto tLoc = _tLoc.value();
|
|
|
|
TransportLocation transLoc = { tLoc["m_iNPCID"], tLoc["m_iXpos"], tLoc["m_iYpos"], tLoc["m_iZpos"] };
|
2021-03-16 22:29:13 +00:00
|
|
|
Transport::Locations[tLoc["m_iLocationID"]] = transLoc;
|
2020-09-22 01:03:48 +00:00
|
|
|
}
|
2021-03-16 22:29:13 +00:00
|
|
|
std::cout << "[INFO] Loaded " << Transport::Locations.size() << " S.C.A.M.P.E.R. locations" << std::endl;
|
2020-09-24 14:14:37 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
for (nlohmann::json::iterator _tRoute = transRouteData.begin(); _tRoute != transRouteData.end(); _tRoute++) {
|
|
|
|
auto tRoute = _tRoute.value();
|
|
|
|
TransportRoute transRoute = { tRoute["m_iMoveType"], tRoute["m_iStartLocation"], tRoute["m_iEndLocation"],
|
|
|
|
tRoute["m_iCost"] , tRoute["m_iSpeed"], tRoute["m_iRouteNum"] };
|
2021-03-16 22:29:13 +00:00
|
|
|
Transport::Routes[tRoute["m_iVehicleID"]] = transRoute;
|
2020-12-17 01:15:21 +00:00
|
|
|
}
|
2021-03-16 22:29:13 +00:00
|
|
|
std::cout << "[INFO] Loaded " << Transport::Routes.size() << " transportation routes" << std::endl;
|
2020-10-17 23:19:05 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
// load mission-related data
|
|
|
|
nlohmann::json tasks = xdtData["m_pMissionTable"]["m_pMissionData"];
|
2020-10-17 23:19:05 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
for (auto _task = tasks.begin(); _task != tasks.end(); _task++) {
|
|
|
|
auto task = _task.value();
|
2020-10-17 23:19:05 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
// rewards
|
|
|
|
if (task["m_iSUReward"] != 0) {
|
|
|
|
auto _rew = xdtData["m_pMissionTable"]["m_pRewardData"][(int)task["m_iSUReward"]];
|
|
|
|
Reward *rew = new Reward(_rew["m_iMissionRewardID"], _rew["m_iMissionRewarItemType"],
|
|
|
|
_rew["m_iMissionRewardItemID"], _rew["m_iCash"], _rew["m_iFusionMatter"]);
|
2020-10-17 23:19:05 +00:00
|
|
|
|
2021-03-16 22:29:13 +00:00
|
|
|
Missions::Rewards[task["m_iHTaskID"]] = rew;
|
functional crates (no more plungers) (#133)
* FM, Taros and Boosts awards from killing mobs should be pretty
accurate now. A temporary formula for adjusting player/mob level gap is
implemented, but it will probably need to be adjusted in the future
* Mobs now drop correct crates
* Crates can be opened and give you correct items This includes
regular mob crates, world boss crates, mission crates, IZ race crates,
E.G.G.E.R.s, golden Eggs, and Event Crates. Keep in mind that neither
IZ races or golden Eggs are implemented, but if you spawn such a crate
it can be opened.
* All data is read from a json file, for which I'm going to release a
tool soon so it's easily adjustable
* There is a new setting for enabling events, which enables dropping
extra event crates These are Knishmas, Halloween and Easter
2020-10-10 17:18:47 +00:00
|
|
|
}
|
2020-10-17 23:19:05 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
// everything else lol. see TaskData comment.
|
2021-03-16 22:29:13 +00:00
|
|
|
Missions::Tasks[task["m_iHTaskID"]] = new TaskData(task);
|
2020-12-17 00:03:38 +00:00
|
|
|
}
|
2020-12-16 15:27:18 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
std::cout << "[INFO] Loaded mission-related data" << std::endl;
|
2020-10-17 23:19:05 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
/*
|
|
|
|
* load all equipment data. i'm sorry. it has to be done
|
|
|
|
* NOTE: please don't change the ordering. it determines the types, since type and equipLoc are used inconsistently
|
|
|
|
*/
|
|
|
|
const char* setNames[11] = { "m_pWeaponItemTable", "m_pShirtsItemTable", "m_pPantsItemTable", "m_pShoesItemTable",
|
|
|
|
"m_pHatItemTable", "m_pGlassItemTable", "m_pBackItemTable", "m_pGeneralItemTable", "",
|
|
|
|
"m_pChestItemTable", "m_pVehicleItemTable" };
|
|
|
|
nlohmann::json itemSet;
|
|
|
|
for (int i = 0; i < 11; i++) {
|
|
|
|
if (i == 8)
|
|
|
|
continue; // there is no type 8, of course
|
2020-12-22 04:55:43 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
itemSet = xdtData[setNames[i]]["m_pItemData"];
|
|
|
|
for (nlohmann::json::iterator _item = itemSet.begin(); _item != itemSet.end(); _item++) {
|
|
|
|
auto item = _item.value();
|
|
|
|
int itemID = item["m_iItemNumber"];
|
2021-03-16 22:29:13 +00:00
|
|
|
INITSTRUCT(Items::Item, itemData);
|
2021-03-16 21:06:10 +00:00
|
|
|
itemData.tradeable = item["m_iTradeAble"] == 1;
|
|
|
|
itemData.sellable = item["m_iSellAble"] == 1;
|
|
|
|
itemData.buyPrice = item["m_iItemPrice"];
|
|
|
|
itemData.sellPrice = item["m_iItemSellPrice"];
|
|
|
|
itemData.stackSize = item["m_iStackNumber"];
|
|
|
|
if (i != 7 && i != 9) {
|
|
|
|
itemData.rarity = item["m_iRarity"];
|
|
|
|
itemData.level = item["m_iMinReqLev"];
|
|
|
|
itemData.pointDamage = item["m_iPointRat"];
|
|
|
|
itemData.groupDamage = item["m_iGroupRat"];
|
|
|
|
itemData.fireRate = item["m_iDelayTime"];
|
|
|
|
itemData.defense = item["m_iDefenseRat"];
|
|
|
|
itemData.gender = item["m_iReqSex"];
|
|
|
|
itemData.weaponType = item["m_iEquipType"];
|
|
|
|
} else {
|
|
|
|
itemData.rarity = 1;
|
2020-12-22 04:55:43 +00:00
|
|
|
}
|
2021-03-16 22:29:13 +00:00
|
|
|
Items::ItemData[std::make_pair(itemID, i)] = itemData;
|
2020-12-22 04:55:43 +00:00
|
|
|
}
|
2020-10-24 19:48:55 +00:00
|
|
|
}
|
|
|
|
|
2021-03-16 22:29:13 +00:00
|
|
|
std::cout << "[INFO] Loaded " << Items::ItemData.size() << " items" << std::endl;
|
functional crates (no more plungers) (#133)
* FM, Taros and Boosts awards from killing mobs should be pretty
accurate now. A temporary formula for adjusting player/mob level gap is
implemented, but it will probably need to be adjusted in the future
* Mobs now drop correct crates
* Crates can be opened and give you correct items This includes
regular mob crates, world boss crates, mission crates, IZ race crates,
E.G.G.E.R.s, golden Eggs, and Event Crates. Keep in mind that neither
IZ races or golden Eggs are implemented, but if you spawn such a crate
it can be opened.
* All data is read from a json file, for which I'm going to release a
tool soon so it's easily adjustable
* There is a new setting for enabling events, which enables dropping
extra event crates These are Knishmas, Halloween and Easter
2020-10-10 17:18:47 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
// load player limits from m_pAvatarTable.m_pAvatarGrowData
|
2020-09-24 14:14:37 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
nlohmann::json growth = xdtData["m_pAvatarTable"]["m_pAvatarGrowData"];
|
2020-10-13 19:44:43 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
for (int i = 0; i < 37; i++) {
|
2021-03-16 22:29:13 +00:00
|
|
|
Missions::AvatarGrowth[i] = growth[i];
|
2021-03-16 21:06:10 +00:00
|
|
|
}
|
2020-10-13 19:44:43 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
// load vendor listings
|
|
|
|
nlohmann::json listings = xdtData["m_pVendorTable"]["m_pItemData"];
|
2020-10-06 19:53:21 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
for (nlohmann::json::iterator _lst = listings.begin(); _lst != listings.end(); _lst++) {
|
|
|
|
auto lst = _lst.value();
|
|
|
|
VendorListing vListing = { lst["m_iSortNumber"], lst["m_iItemType"], lst["m_iitemID"] };
|
2021-04-16 17:28:59 +00:00
|
|
|
Vendors::VendorTables[lst["m_iNpcNumber"]].push_back(vListing);
|
2021-03-16 21:06:10 +00:00
|
|
|
}
|
2020-10-06 19:53:21 +00:00
|
|
|
|
2021-04-16 17:28:59 +00:00
|
|
|
std::cout << "[INFO] Loaded " << Vendors::VendorTables.size() << " vendor tables" << std::endl;
|
2020-10-06 19:53:21 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
// load crocpot entries
|
|
|
|
nlohmann::json crocs = xdtData["m_pCombiningTable"]["m_pCombiningData"];
|
2020-10-06 19:53:21 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
for (nlohmann::json::iterator croc = crocs.begin(); croc != crocs.end(); croc++) {
|
|
|
|
CrocPotEntry crocEntry = { croc.value()["m_iStatConstant"], croc.value()["m_iLookConstant"], croc.value()["m_fLevelGapStandard"],
|
|
|
|
croc.value()["m_fSameGrade"], croc.value()["m_fOneGrade"], croc.value()["m_fTwoGrade"], croc.value()["m_fThreeGrade"] };
|
2021-03-16 22:29:13 +00:00
|
|
|
Items::CrocPotTable[croc.value()["m_iLevelGap"]] = crocEntry;
|
2021-03-16 21:06:10 +00:00
|
|
|
}
|
2020-10-06 19:53:21 +00:00
|
|
|
|
2021-03-16 22:29:13 +00:00
|
|
|
std::cout << "[INFO] Loaded " << Items::CrocPotTable.size() << " croc pot value sets" << std::endl;
|
2020-10-06 19:53:21 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
// load nano info
|
|
|
|
nlohmann::json nanoInfo = xdtData["m_pNanoTable"]["m_pNanoData"];
|
|
|
|
for (nlohmann::json::iterator _nano = nanoInfo.begin(); _nano != nanoInfo.end(); _nano++) {
|
|
|
|
auto nano = _nano.value();
|
|
|
|
NanoData nanoData;
|
|
|
|
nanoData.style = nano["m_iStyle"];
|
2021-03-16 22:29:13 +00:00
|
|
|
Nanos::NanoTable[Nanos::NanoTable.size()] = nanoData;
|
2020-10-06 19:53:21 +00:00
|
|
|
}
|
|
|
|
|
2021-03-16 22:29:13 +00:00
|
|
|
std::cout << "[INFO] Loaded " << Nanos::NanoTable.size() << " nanos" << std::endl;
|
2020-10-07 19:13:26 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
nlohmann::json nanoTuneInfo = xdtData["m_pNanoTable"]["m_pNanoTuneData"];
|
|
|
|
for (nlohmann::json::iterator _nano = nanoTuneInfo.begin(); _nano != nanoTuneInfo.end(); _nano++) {
|
|
|
|
auto nano = _nano.value();
|
|
|
|
NanoTuning nanoData;
|
|
|
|
nanoData.reqItems = nano["m_iReqItemID"];
|
|
|
|
nanoData.reqItemCount = nano["m_iReqItemCount"];
|
2021-03-16 22:29:13 +00:00
|
|
|
Nanos::NanoTunings[nano["m_iSkillID"]] = nanoData;
|
2020-10-06 21:59:12 +00:00
|
|
|
}
|
|
|
|
|
2021-03-16 22:29:13 +00:00
|
|
|
std::cout << "[INFO] Loaded " << Nanos::NanoTable.size() << " nano tunings" << std::endl;
|
2020-10-12 01:53:01 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
// load nano powers
|
|
|
|
nlohmann::json skills = xdtData["m_pSkillTable"]["m_pSkillData"];
|
|
|
|
|
|
|
|
for (nlohmann::json::iterator _skills = skills.begin(); _skills != skills.end(); _skills++) {
|
|
|
|
auto skills = _skills.value();
|
|
|
|
SkillData skillData = {skills["m_iSkillType"], skills["m_iTargetType"], skills["m_iBatteryDrainType"], skills["m_iEffectArea"]};
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
skillData.batteryUse[i] = skills["m_iBatteryDrainUse"][i];
|
|
|
|
skillData.durationTime[i] = skills["m_iDurationTime"][i];
|
|
|
|
skillData.powerIntensity[i] = skills["m_iValueA"][i];
|
|
|
|
}
|
2021-03-16 22:29:13 +00:00
|
|
|
Nanos::SkillTable[skills["m_iSkillNumber"]] = skillData;
|
2020-10-12 01:53:01 +00:00
|
|
|
}
|
|
|
|
|
2021-03-16 22:29:13 +00:00
|
|
|
std::cout << "[INFO] Loaded " << Nanos::SkillTable.size() << " nano skills" << std::endl;
|
2020-10-19 01:45:58 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
// load EP data
|
|
|
|
nlohmann::json instances = xdtData["m_pInstanceTable"]["m_pInstanceData"];
|
2020-10-19 01:45:58 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
for (nlohmann::json::iterator _instance = instances.begin(); _instance != instances.end(); _instance++) {
|
|
|
|
auto instance = _instance.value();
|
|
|
|
EPInfo epInfo = {instance["m_iZoneX"], instance["m_iZoneY"], instance["m_iIsEP"], (int)instance["m_ScoreMax"]};
|
2021-03-16 22:29:13 +00:00
|
|
|
Racing::EPData[instance["m_iInstanceNameID"]] = epInfo;
|
2021-03-16 21:06:10 +00:00
|
|
|
}
|
2020-10-19 01:45:58 +00:00
|
|
|
|
2021-03-16 22:29:13 +00:00
|
|
|
std::cout << "[INFO] Loaded " << Racing::EPData.size() << " instances" << std::endl;
|
2021-03-16 21:06:10 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
catch (const std::exception& err) {
|
|
|
|
std::cerr << "[FATAL] Malformed xdt.json file! Reason:" << err.what() << std::endl;
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// load mobs
|
|
|
|
try {
|
|
|
|
std::ifstream inFile(settings::MOBJSON);
|
|
|
|
nlohmann::json npcData, groupData;
|
|
|
|
|
|
|
|
// read file into json
|
|
|
|
inFile >> npcData;
|
|
|
|
groupData = npcData["groups"];
|
|
|
|
npcData = npcData["mobs"];
|
|
|
|
|
|
|
|
// single mobs
|
|
|
|
for (nlohmann::json::iterator _npc = npcData.begin(); _npc != npcData.end(); _npc++) {
|
|
|
|
auto npc = _npc.value();
|
|
|
|
auto td = NPCManager::NPCData[(int)npc["iNPCType"]];
|
|
|
|
uint64_t instanceID = npc.find("iMapNum") == npc.end() ? INSTANCE_OVERWORLD : (int)npc["iMapNum"];
|
|
|
|
|
|
|
|
#ifdef ACADEMY
|
|
|
|
// do not spawn NPCs in the future
|
|
|
|
if (npc["iX"] > 512000 && npc["iY"] < 256000) {
|
|
|
|
nextId++;
|
|
|
|
continue;
|
2020-10-19 01:45:58 +00:00
|
|
|
}
|
2021-03-16 21:06:10 +00:00
|
|
|
#endif
|
2020-10-07 17:29:59 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
Mob *tmp = new Mob(npc["iX"], npc["iY"], npc["iZ"], npc["iAngle"], instanceID, npc["iNPCType"], td, nextId);
|
|
|
|
|
|
|
|
NPCManager::NPCs[nextId] = tmp;
|
|
|
|
NPCManager::updateNPCPosition(nextId, npc["iX"], npc["iY"], npc["iZ"], instanceID, npc["iAngle"]);
|
|
|
|
|
|
|
|
nextId++;
|
2020-10-07 17:29:59 +00:00
|
|
|
}
|
|
|
|
|
2020-11-25 16:09:05 +00:00
|
|
|
// mob groups
|
2021-03-16 21:06:10 +00:00
|
|
|
// single mobs
|
|
|
|
for (nlohmann::json::iterator _group = groupData.begin(); _group != groupData.end(); _group++) {
|
2020-11-25 16:09:05 +00:00
|
|
|
auto leader = _group.value();
|
|
|
|
auto td = NPCManager::NPCData[(int)leader["iNPCType"]];
|
|
|
|
uint64_t instanceID = leader.find("iMapNum") == leader.end() ? INSTANCE_OVERWORLD : (int)leader["iMapNum"];
|
2021-03-16 21:06:10 +00:00
|
|
|
auto followers = leader["aFollowers"];
|
2020-11-25 16:09:05 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
#ifdef ACADEMY
|
|
|
|
// do not spawn NPCs in the future
|
|
|
|
if (leader["iX"] > 512000 && leader["iY"] < 256000) {
|
|
|
|
nextId++;
|
|
|
|
nextId += followers.size();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#endif
|
2020-11-25 16:09:05 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
Mob* tmp = new Mob(leader["iX"], leader["iY"], leader["iZ"], leader["iAngle"], instanceID, leader["iNPCType"], td, nextId);
|
2020-11-25 16:09:05 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
NPCManager::NPCs[nextId] = tmp;
|
|
|
|
NPCManager::updateNPCPosition(nextId, leader["iX"], leader["iY"], leader["iZ"], instanceID, leader["iAngle"]);
|
2020-11-25 16:09:05 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
tmp->groupLeader = nextId;
|
2020-11-25 16:09:05 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
nextId++;
|
2020-11-25 16:09:05 +00:00
|
|
|
|
|
|
|
if (followers.size() < 5) {
|
|
|
|
int followerCount = 0;
|
|
|
|
for (nlohmann::json::iterator _fol = followers.begin(); _fol != followers.end(); _fol++) {
|
|
|
|
auto follower = _fol.value();
|
|
|
|
auto tdFol = NPCManager::NPCData[(int)follower["iNPCType"]];
|
2021-03-16 21:06:10 +00:00
|
|
|
Mob* tmpFol = new Mob((int)leader["iX"] + (int)follower["iOffsetX"], (int)leader["iY"] + (int)follower["iOffsetY"], leader["iZ"], leader["iAngle"], instanceID, follower["iNPCType"], tdFol, nextId);
|
2020-11-25 16:09:05 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
NPCManager::NPCs[nextId] = tmpFol;
|
|
|
|
NPCManager::updateNPCPosition(nextId, (int)leader["iX"] + (int)follower["iOffsetX"], (int)leader["iY"] + (int)follower["iOffsetY"], leader["iZ"], instanceID, leader["iAngle"]);
|
2020-11-25 16:09:05 +00:00
|
|
|
|
|
|
|
tmpFol->offsetX = follower.find("iOffsetX") == follower.end() ? 0 : (int)follower["iOffsetX"];
|
|
|
|
tmpFol->offsetY = follower.find("iOffsetY") == follower.end() ? 0 : (int)follower["iOffsetY"];
|
|
|
|
tmpFol->groupLeader = tmp->appearanceData.iNPC_ID;
|
2021-03-16 21:06:10 +00:00
|
|
|
tmp->groupMember[followerCount++] = nextId;
|
2020-11-25 16:09:05 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
nextId++;
|
2020-11-25 16:09:05 +00:00
|
|
|
}
|
2021-03-16 21:06:10 +00:00
|
|
|
} else {
|
|
|
|
std::cout << "[WARN] Mob group leader with ID " << nextId << " has too many followers (" << followers.size() << ")\n";
|
2020-11-25 16:09:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
std::cout << "[INFO] Populated " << NPCManager::NPCs.size() << " NPCs" << std::endl;
|
|
|
|
}
|
|
|
|
catch (const std::exception& err) {
|
|
|
|
std::cerr << "[FATAL] Malformed mobs.json file! Reason:" << err.what() << std::endl;
|
|
|
|
exit(1);
|
|
|
|
}
|
2020-11-09 09:34:11 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
#ifdef ACADEMY
|
|
|
|
// load Academy NPCs from academy.json
|
|
|
|
try {
|
|
|
|
std::ifstream inFile(settings::ACADEMYJSON);
|
|
|
|
nlohmann::json npcData;
|
|
|
|
|
|
|
|
// read file into json
|
|
|
|
inFile >> npcData;
|
|
|
|
npcData = npcData["NPCs"];
|
|
|
|
for (nlohmann::json::iterator _npc = npcData.begin(); _npc != npcData.end(); _npc++) {
|
|
|
|
auto npc = _npc.value();
|
|
|
|
int instanceID = npc.find("iMapNum") == npc.end() ? INSTANCE_OVERWORLD : (int)npc["iMapNum"];
|
2020-11-08 08:42:49 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
int team = NPCManager::NPCData[(int)npc["iNPCType"]]["m_iTeam"];
|
2020-11-08 08:42:49 +00:00
|
|
|
|
2021-04-07 00:43:43 +00:00
|
|
|
if (team == 2)
|
2021-03-16 21:06:10 +00:00
|
|
|
NPCManager::NPCs[nextId] = new Mob(npc["iX"], npc["iY"], npc["iZ"], npc["iAngle"], instanceID, npc["iNPCType"], NPCManager::NPCData[(int)npc["iNPCType"]], nextId);
|
2021-04-07 00:43:43 +00:00
|
|
|
else
|
2021-03-16 21:06:10 +00:00
|
|
|
NPCManager::NPCs[nextId] = new BaseNPC(npc["iX"], npc["iY"], npc["iZ"], npc["iAngle"], instanceID, npc["iNPCType"], nextId);
|
|
|
|
|
|
|
|
NPCManager::updateNPCPosition(nextId, npc["iX"], npc["iY"], npc["iZ"], instanceID, npc["iAngle"]);
|
|
|
|
nextId++;
|
|
|
|
|
|
|
|
if (npc["iNPCType"] == 641 || npc["iNPCType"] == 642)
|
|
|
|
NPCManager::RespawnPoints.push_back({ npc["iX"], npc["iY"], ((int)npc["iZ"]) + RESURRECT_HEIGHT, instanceID });
|
|
|
|
}
|
2020-10-06 19:53:21 +00:00
|
|
|
}
|
|
|
|
catch (const std::exception& err) {
|
2021-03-16 21:06:10 +00:00
|
|
|
std::cerr << "[FATAL] Malformed academy.json file! Reason:" << err.what() << std::endl;
|
2020-12-06 04:25:23 +00:00
|
|
|
exit(1);
|
2020-10-06 19:53:21 +00:00
|
|
|
}
|
2021-03-16 21:06:10 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
loadDrops();
|
|
|
|
|
|
|
|
loadEggs(&nextId);
|
|
|
|
|
|
|
|
loadPaths(&nextId); // load paths
|
|
|
|
|
|
|
|
loadGruntwork(&nextId);
|
|
|
|
|
|
|
|
NPCManager::nextId = nextId;
|
2020-10-06 19:53:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// write gruntwork output to file
|
|
|
|
void TableData::flush() {
|
|
|
|
std::ofstream file(settings::GRUNTWORKJSON);
|
|
|
|
nlohmann::json gruntwork;
|
|
|
|
|
|
|
|
for (auto& pair : RunningSkywayRoutes) {
|
|
|
|
nlohmann::json route;
|
|
|
|
|
|
|
|
route["iRouteID"] = (int)pair.first;
|
2020-10-12 01:53:01 +00:00
|
|
|
route["iMonkeySpeed"] = 1500;
|
2020-10-06 19:53:21 +00:00
|
|
|
|
|
|
|
std::cout << "serializing mss route " << (int)pair.first << std::endl;
|
|
|
|
for (WarpLocation& point : pair.second) {
|
|
|
|
nlohmann::json tmp;
|
|
|
|
|
|
|
|
tmp["x"] = point.x;
|
|
|
|
tmp["y"] = point.y;
|
|
|
|
tmp["z"] = point.z;
|
|
|
|
|
|
|
|
route["points"].push_back(tmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
gruntwork["skyway"].push_back(route);
|
|
|
|
}
|
|
|
|
|
2020-10-06 21:59:12 +00:00
|
|
|
for (auto& pair : RunningNPCRotations) {
|
|
|
|
nlohmann::json rotation;
|
|
|
|
|
|
|
|
rotation["iNPCID"] = (int)pair.first;
|
|
|
|
rotation["iAngle"] = pair.second;
|
|
|
|
|
|
|
|
gruntwork["rotations"].push_back(rotation);
|
|
|
|
}
|
|
|
|
|
2020-10-12 01:53:01 +00:00
|
|
|
for (auto& pair : RunningNPCMapNumbers) {
|
|
|
|
nlohmann::json mapNumber;
|
|
|
|
|
|
|
|
mapNumber["iNPCID"] = (int)pair.first;
|
|
|
|
mapNumber["iMapNum"] = pair.second;
|
|
|
|
|
|
|
|
gruntwork["instances"].push_back(mapNumber);
|
|
|
|
}
|
|
|
|
|
2020-10-07 17:29:59 +00:00
|
|
|
for (auto& pair : RunningMobs) {
|
|
|
|
nlohmann::json mob;
|
2020-10-19 01:45:58 +00:00
|
|
|
BaseNPC *npc = pair.second;
|
2020-10-07 17:29:59 +00:00
|
|
|
|
|
|
|
if (NPCManager::NPCs.find(pair.first) == NPCManager::NPCs.end())
|
|
|
|
continue;
|
|
|
|
|
2020-12-14 15:00:07 +00:00
|
|
|
int x, y, z;
|
2021-03-21 02:54:24 +00:00
|
|
|
if (npc->type == EntityType::MOB) {
|
2020-10-19 01:45:58 +00:00
|
|
|
Mob *m = (Mob*)npc;
|
|
|
|
x = m->spawnX;
|
|
|
|
y = m->spawnY;
|
|
|
|
z = m->spawnZ;
|
|
|
|
} else {
|
2021-04-14 00:57:24 +00:00
|
|
|
x = npc->x;
|
|
|
|
y = npc->y;
|
|
|
|
z = npc->z;
|
2020-10-19 01:45:58 +00:00
|
|
|
}
|
|
|
|
|
2020-10-07 17:29:59 +00:00
|
|
|
// NOTE: this format deviates slightly from the one in mobs.json
|
2020-10-19 01:45:58 +00:00
|
|
|
mob["iNPCType"] = (int)npc->appearanceData.iNPCType;
|
|
|
|
mob["iX"] = x;
|
|
|
|
mob["iY"] = y;
|
|
|
|
mob["iZ"] = z;
|
|
|
|
mob["iMapNum"] = MAPNUM(npc->instanceID);
|
2020-10-07 17:29:59 +00:00
|
|
|
// this is a bit imperfect, since this is a live angle, not a spawn angle so it'll change often, but eh
|
2020-10-19 01:45:58 +00:00
|
|
|
mob["iAngle"] = npc->appearanceData.iAngle;
|
2020-10-07 17:29:59 +00:00
|
|
|
|
2020-11-25 15:41:10 +00:00
|
|
|
// it's called mobs, but really it's everything
|
|
|
|
gruntwork["mobs"].push_back(mob);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto& pair : RunningGroups) {
|
|
|
|
nlohmann::json mob;
|
|
|
|
BaseNPC* npc = pair.second;
|
|
|
|
|
|
|
|
if (NPCManager::NPCs.find(pair.first) == NPCManager::NPCs.end())
|
|
|
|
continue;
|
|
|
|
|
2020-12-14 15:00:07 +00:00
|
|
|
int x, y, z;
|
2020-11-25 15:41:10 +00:00
|
|
|
std::vector<Mob*> followers;
|
2021-03-21 02:54:24 +00:00
|
|
|
if (npc->type == EntityType::MOB) {
|
2020-11-25 15:41:10 +00:00
|
|
|
Mob* m = (Mob*)npc;
|
|
|
|
x = m->spawnX;
|
|
|
|
y = m->spawnY;
|
|
|
|
z = m->spawnZ;
|
|
|
|
if (m->groupLeader != m->appearanceData.iNPC_ID) { // make sure this is a leader
|
|
|
|
std::cout << "[WARN] Non-leader mob found in running groups; ignoring\n";
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// add follower data to vector; go until OOB or until follower ID is 0
|
|
|
|
for (int i = 0; i < 4 && m->groupMember[i] > 0; i++) {
|
2021-04-07 00:43:43 +00:00
|
|
|
if (NPCManager::NPCs.find(m->groupMember[i]) == NPCManager::NPCs.end() || NPCManager::NPCs[m->groupMember[i]]->type != EntityType::MOB) {
|
2020-11-25 15:41:10 +00:00
|
|
|
std::cout << "[WARN] Follower with ID " << m->groupMember[i] << " not found; skipping\n";
|
|
|
|
continue;
|
|
|
|
}
|
2021-04-07 00:43:43 +00:00
|
|
|
followers.push_back((Mob*)NPCManager::NPCs[m->groupMember[i]]);
|
2020-11-25 15:41:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2021-04-14 00:57:24 +00:00
|
|
|
x = npc->x;
|
|
|
|
y = npc->y;
|
|
|
|
z = npc->z;
|
2020-11-25 15:41:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: this format deviates slightly from the one in mobs.json
|
|
|
|
mob["iNPCType"] = (int)npc->appearanceData.iNPCType;
|
|
|
|
mob["iX"] = x;
|
|
|
|
mob["iY"] = y;
|
|
|
|
mob["iZ"] = z;
|
|
|
|
mob["iMapNum"] = MAPNUM(npc->instanceID);
|
|
|
|
// this is a bit imperfect, since this is a live angle, not a spawn angle so it'll change often, but eh
|
|
|
|
mob["iAngle"] = npc->appearanceData.iAngle;
|
|
|
|
|
|
|
|
// followers
|
|
|
|
while (followers.size() > 0) {
|
|
|
|
Mob* follower = followers.back();
|
|
|
|
followers.pop_back(); // remove from vector
|
|
|
|
|
|
|
|
// populate JSON entry
|
|
|
|
nlohmann::json fol;
|
|
|
|
fol["iNPCType"] = follower->appearanceData.iNPCType;
|
|
|
|
fol["iOffsetX"] = follower->offsetX;
|
|
|
|
fol["iOffsetY"] = follower->offsetY;
|
|
|
|
|
|
|
|
mob["aFollowers"].push_back(fol); // add to follower array
|
2020-11-15 18:19:34 +00:00
|
|
|
}
|
|
|
|
|
2020-10-19 01:45:58 +00:00
|
|
|
// it's called mobs, but really it's everything
|
2020-11-25 15:41:10 +00:00
|
|
|
gruntwork["groups"].push_back(mob);
|
2020-10-07 17:29:59 +00:00
|
|
|
}
|
|
|
|
|
2020-11-08 08:42:49 +00:00
|
|
|
for (auto& pair : RunningEggs) {
|
|
|
|
nlohmann::json egg;
|
|
|
|
BaseNPC* npc = pair.second;
|
|
|
|
|
2021-03-22 16:53:46 +00:00
|
|
|
if (NPCManager::NPCs.find(pair.first) == NPCManager::NPCs.end())
|
2020-11-08 08:42:49 +00:00
|
|
|
continue;
|
2021-03-22 16:53:46 +00:00
|
|
|
// we can trust that if it exists, it probably is indeed an egg
|
|
|
|
|
2021-04-14 00:57:24 +00:00
|
|
|
egg["iX"] = npc->x;
|
|
|
|
egg["iY"] = npc->y;
|
|
|
|
egg["iZ"] = npc->z;
|
2020-11-09 09:34:11 +00:00
|
|
|
int mapnum = MAPNUM(npc->instanceID);
|
|
|
|
if (mapnum != 0)
|
|
|
|
egg["iMapNum"] = mapnum;
|
2020-11-08 08:42:49 +00:00
|
|
|
egg["iType"] = npc->appearanceData.iNPCType;
|
|
|
|
|
|
|
|
gruntwork["eggs"].push_back(egg);
|
|
|
|
}
|
|
|
|
|
2020-10-06 19:53:21 +00:00
|
|
|
file << gruntwork << std::endl;
|
|
|
|
}
|