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
This commit is contained in:
kamilprzyb 2020-10-10 19:18:47 +02:00 committed by dongresource
parent ab5857e7e2
commit dd54668697
9 changed files with 435 additions and 33 deletions

View File

@ -11,6 +11,13 @@
std::map<std::pair<int32_t, int32_t>, Item> ItemManager::ItemData; std::map<std::pair<int32_t, int32_t>, Item> ItemManager::ItemData;
std::map<int32_t, std::vector<VendorListing>> ItemManager::VendorTables; std::map<int32_t, std::vector<VendorListing>> ItemManager::VendorTables;
std::map<int32_t, CrocPotEntry> ItemManager::CrocPotTable; std::map<int32_t, CrocPotEntry> ItemManager::CrocPotTable;
std::map<int32_t, std::vector<int>> ItemManager::RarityRatios;
std::map<int32_t, Crate> ItemManager::Crates;
/// pair Itemset, Rarity -> vector of pointers (map iterators) to records in ItemData
std::map<std::pair<int32_t, int32_t>, std::vector<std::map<std::pair<int32_t, int32_t>, Item>::iterator>> ItemManager::CrateItems;
// buffer for error messages
char buffer[255];
void ItemManager::init() { void ItemManager::init() {
REGISTER_SHARD_PACKET(P_CL2FE_REQ_ITEM_MOVE, itemMoveHandler); REGISTER_SHARD_PACKET(P_CL2FE_REQ_ITEM_MOVE, itemMoveHandler);
@ -801,10 +808,10 @@ void ItemManager::chestOpenHandler(CNSocket *sock, CNPacketData *data) {
if (data->size != sizeof(sP_CL2FE_REQ_ITEM_CHEST_OPEN)) if (data->size != sizeof(sP_CL2FE_REQ_ITEM_CHEST_OPEN))
return; // ignore the malformed packet return; // ignore the malformed packet
sP_CL2FE_REQ_ITEM_CHEST_OPEN *pkt = (sP_CL2FE_REQ_ITEM_CHEST_OPEN *)data->buf; sP_CL2FE_REQ_ITEM_CHEST_OPEN *chest = (sP_CL2FE_REQ_ITEM_CHEST_OPEN *)data->buf;
Player *plr = PlayerManager::getPlayer(sock); Player *player = PlayerManager::getPlayer(sock);
if (plr == nullptr) if (player == nullptr)
return; return;
// item giving packet // item giving packet
@ -820,21 +827,23 @@ void ItemManager::chestOpenHandler(CNSocket *sock, CNPacketData *data) {
memset(respbuf, 0, resplen); memset(respbuf, 0, resplen);
// maintain stats // maintain stats
reward->m_iCandy = plr->money; reward->m_iCandy = player->money;
reward->m_iFusionMatter = plr->fusionmatter; reward->m_iFusionMatter = player->fusionmatter;
reward->iFatigue = 100; // prevents warning message reward->iFatigue = 100; // prevents warning message
reward->iFatigue_Level = 1; reward->iFatigue_Level = 1;
reward->iItemCnt = 1; // remember to update resplen if you change this reward->iItemCnt = 1; // remember to update resplen if you change this
// item reward // item reward
item->sItem.iType = 0; if (chest->ChestItem.iType != 9)
item->sItem.iID = 96; std::cout << "[WARN] Player tried to open a crate with incorrect iType ?!" << std::endl;
item->sItem.iOpt = 1; else
item->iSlotNum = pkt->iSlotNum; item->sItem = openCrate(chest->ChestItem.iID, player->PCStyle.iGender);
item->eIL = pkt->eIL;
item->iSlotNum = chest->iSlotNum;
item->eIL = chest->eIL;
// update player // update player
plr->Inven[pkt->iSlotNum] = item->sItem; player->Inven[chest->iSlotNum] = item->sItem;
// transmit item // transmit item
sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, resplen); sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, resplen);
@ -842,12 +851,119 @@ void ItemManager::chestOpenHandler(CNSocket *sock, CNPacketData *data) {
// chest opening acknowledgement packet // chest opening acknowledgement packet
INITSTRUCT(sP_FE2CL_REP_ITEM_CHEST_OPEN_SUCC, resp); INITSTRUCT(sP_FE2CL_REP_ITEM_CHEST_OPEN_SUCC, resp);
resp.iSlotNum = pkt->iSlotNum; resp.iSlotNum = chest->iSlotNum;
std::cout << "opening chest..." << std::endl; std::cout << "opening chest..." << std::endl;
sock->sendPacket((void*)&resp, P_FE2CL_REP_ITEM_CHEST_OPEN_SUCC, sizeof(sP_FE2CL_REP_ITEM_CHEST_OPEN_SUCC)); sock->sendPacket((void*)&resp, P_FE2CL_REP_ITEM_CHEST_OPEN_SUCC, sizeof(sP_FE2CL_REP_ITEM_CHEST_OPEN_SUCC));
} }
sItemBase ItemManager::openCrate(int crateId, int playerGender) {
sItemBase reward = {};
try {
Crate crate = getCrate(crateId);
int itemSetId = getItemSetId(crate, crateId);
int rarity = getRarity(crate, itemSetId);
reward = getCrateItem(itemSetId, rarity, playerGender);
}
catch (const std::exception& err) {
std::cerr << "[WARN] An error has occured while trying to open a crate. Error description: \n" << err.what() << std::endl;
// if we failed to open a crate, at least give the player a gumball (suggested by Jade)
reward.iType = 7;
reward.iID = 119 + (rand() % 3);
reward.iOpt = 1;
}
return reward;
}
Crate ItemManager::getCrate(int crateId) {
if (Crates.find(crateId) == Crates.end())
throwError(sprintf(buffer, "Crate %d was not found!", crateId));
return Crates[crateId];
}
int ItemManager::getItemSetId(Crate crate, int crateId) {
int itemSetsCount = crate.itemSets.size();
if (itemSetsCount == 0)
throwError(sprintf(buffer, "Crate %d has no item sets assigned?!", crateId));
// if crate points to multiple itemSets, choose a random one
int itemSetIndex = rand() % itemSetsCount;
return crate.itemSets[itemSetIndex];
}
int ItemManager::getRarity(Crate crate , int itemSetId) {
// find rarity ratio
if (RarityRatios.find(crate.rarityRatioId) == RarityRatios.end())
throwError(sprintf(buffer, "Rarity Ratio %d not found!", crate.rarityRatioId));
std::vector<int> rarityRatio = RarityRatios[crate.rarityRatioId];
/*
* First we have to check if specified item set contains items with all specified rarities,
* and if not eliminate them from the draw
* it is simpler to do here than to fix individually in the file
*/
// remember that rarities start from 1 !
for (int i = 0; i < rarityRatio.size(); i++){
if (CrateItems.find(std::make_pair(itemSetId, i+1)) == CrateItems.end())
rarityRatio[i] = 0;
}
int total = 0;
for (int value : rarityRatio)
total += value;
// if we didn't find any items, throw exception
if (total == 0)
throwError(sprintf(buffer, "Item Set %d has no items assigned?!", itemSetId));
// now return a random rarity number
int randomNum = rand() % total;
int rarity = 0;
int sum = 0;
do {
sum += rarityRatio[rarity];
rarity++;
} while (sum <= randomNum);
return rarity;
}
sItemBase ItemManager::getCrateItem(int itemSetId, int rarity, int playerGender) {
std::pair key = std::make_pair(itemSetId, rarity);
if (CrateItems.find(key) == CrateItems.end())
throwError(sprintf(buffer, "Item Set ID %d Rarity %d items have not been found", itemSetId, rarity));
// only take into account items that have correct gender
std::vector<std::map<std::pair<int32_t, int32_t>, Item>::iterator> items;
for (auto crateitem : CrateItems[key])
{
int gender = crateitem->second.gender;
// if gender is incorrect, exclude item
if (gender != 0 && gender != playerGender)
continue;
items.push_back(crateitem);
}
if (items.size() == 0)
throwError(sprintf(buffer, "Gender inequality! Set ID %d Rarity %d contains only %s items?!", itemSetId, rarity, playerGender==2 ? "boys" : "girls"));
auto item = items[rand() % items.size()];
sItemBase result = {};
result.iID = item->first.first;
result.iType = item->first.second;
result.iOpt = 1;
return result;
}
// argument is here only so we can call sprintf in brackets
void ItemManager::throwError(int ignore) {
throw buffer;
}
// TODO: use this in cleaned up ItemManager // TODO: use this in cleaned up ItemManager
int ItemManager::findFreeSlot(Player *plr) { int ItemManager::findFreeSlot(Player *plr) {
int i; int i;

View File

@ -5,7 +5,7 @@
struct Item { struct Item {
bool tradeable, sellable; bool tradeable, sellable;
int buyPrice, sellPrice, stackSize, level, rarity, pointDamage, groupDamage, defense; // TODO: implement more as needed int buyPrice, sellPrice, stackSize, level, rarity, pointDamage, groupDamage, defense, gender; // TODO: implement more as needed
}; };
struct VendorListing { struct VendorListing {
int sort, type, iID; int sort, type, iID;
@ -14,6 +14,10 @@ struct CrocPotEntry {
int multStats, multLooks; int multStats, multLooks;
float base, rd0, rd1, rd2, rd3; float base, rd0, rd1, rd2, rd3;
}; };
struct Crate {
int rarityRatioId;
std::vector<int> itemSets;
};
namespace ItemManager { namespace ItemManager {
enum class SlotType { enum class SlotType {
@ -25,6 +29,11 @@ namespace ItemManager {
extern std::map<std::pair<int32_t, int32_t>, Item> ItemData; // <id, type> -> data extern std::map<std::pair<int32_t, int32_t>, Item> ItemData; // <id, type> -> data
extern std::map<int32_t, std::vector<VendorListing>> VendorTables; extern std::map<int32_t, std::vector<VendorListing>> VendorTables;
extern std::map<int32_t, CrocPotEntry> CrocPotTable; // level gap -> entry extern std::map<int32_t, CrocPotEntry> CrocPotTable; // level gap -> entry
extern std::map<int32_t, std::vector<int>> RarityRatios;
extern std::map<int32_t, Crate> Crates;
// pair <Itemset, Rarity> -> vector of pointers (map iterators) to records in ItemData (it looks a lot scarier than it is)
extern std::map<std::pair<int32_t, int32_t>,
std::vector<std::map<std::pair<int32_t, int32_t>, Item>::iterator>> CrateItems;
void init(); void init();
@ -46,6 +55,14 @@ namespace ItemManager {
void itemTradeChatHandler(CNSocket* sock, CNPacketData* data); void itemTradeChatHandler(CNSocket* sock, CNPacketData* data);
void chestOpenHandler(CNSocket* sock, CNPacketData* data); void chestOpenHandler(CNSocket* sock, CNPacketData* data);
// crate opening logic with all helper functions
sItemBase openCrate(int crateId, int playerGender);
Crate getCrate(int crateId);
int getItemSetId(Crate crate, int crateId);
int getRarity(Crate crate, int itemSetId);
sItemBase getCrateItem(int itemSetId, int rarity, int playerGender);
void throwError(int ignore);
int findFreeSlot(Player *plr); int findFreeSlot(Player *plr);
Item* getItemData(int32_t id, int32_t type); Item* getItemData(int32_t id, int32_t type);
void checkItemExpire(CNSocket* sock, Player* player); void checkItemExpire(CNSocket* sock, Player* player);

View File

@ -11,6 +11,8 @@
#include <assert.h> #include <assert.h>
std::map<int32_t, Mob*> MobManager::Mobs; std::map<int32_t, Mob*> MobManager::Mobs;
std::map<int32_t, MobDropChance> MobManager::MobDropChances;
std::map<int32_t, MobDrop> MobManager::MobDrops;
std::queue<int32_t> MobManager::RemovalQueue; std::queue<int32_t> MobManager::RemovalQueue;
bool MobManager::simulateMobs; bool MobManager::simulateMobs;
@ -143,7 +145,7 @@ void MobManager::npcAttackPc(Mob *mob, time_t currTime) {
} }
} }
void MobManager::giveReward(CNSocket *sock) { void MobManager::giveReward(CNSocket *sock, Mob* mob) {
Player *plr = PlayerManager::getPlayer(sock); Player *plr = PlayerManager::getPlayer(sock);
if (plr == nullptr) if (plr == nullptr)
@ -160,9 +162,31 @@ void MobManager::giveReward(CNSocket *sock) {
// don't forget to zero the buffer! // don't forget to zero the buffer!
memset(respbuf, 0, resplen); memset(respbuf, 0, resplen);
// NOTE: these will need to be scaled according to the player/mob level difference // sanity check
plr->money += (int)MissionManager::AvatarGrowth[plr->level]["m_iMobFM"]; // this one's innacurate, but close enough for now if (MobDrops.find(mob->dropType) == MobDrops.end()) {
MissionManager::updateFusionMatter(sock, MissionManager::AvatarGrowth[plr->level]["m_iMobFM"]); std::cout << "[WARN] Drop Type " << mob->dropType << " was not found" << std::endl;
return;
}
// find correct mob drop
MobDrop drop = MobDrops[mob->dropType];
plr->money += drop.taros;
// formula for scaling FM with player/mob level difference
// TODO: adjust this better
int levelDifference = plr->level - mob->level;
int fm = drop.fm;
if (levelDifference > 0)
fm = levelDifference < 10 ? fm - (levelDifference * fm / 10) : 0;
MissionManager::updateFusionMatter(sock, fm);
// give boosts 1 in 3 times
if (drop.boosts > 0) {
if (rand() % 3 == 0)
plr->batteryN += drop.boosts;
if (rand() % 3 == 0)
plr->batteryW += drop.boosts;
}
// simple rewards // simple rewards
reward->m_iCandy = plr->money; reward->m_iCandy = plr->money;
@ -173,20 +197,29 @@ void MobManager::giveReward(CNSocket *sock) {
reward->iFatigue_Level = 1; reward->iFatigue_Level = 1;
reward->iItemCnt = 1; // remember to update resplen if you change this reward->iItemCnt = 1; // remember to update resplen if you change this
#if 0
int slot = ItemManager::findFreeSlot(plr); int slot = ItemManager::findFreeSlot(plr);
if (slot == -1) {
#else bool awardDrop = false;
int slot = -1; MobDropChance chance;
if (true) { // sanity check
#endif if (MobDropChances.find(drop.dropChanceType) == MobDropChances.end())
std::cout << "[WARN] Unknown Drop Chance Type: " << drop.dropChanceType << std::endl;
else {
chance = MobDropChances[drop.dropChanceType];
awardDrop = (rand() % 1000 < chance.dropChance);
}
// no drop
if (slot == -1 || !awardDrop) {
// no room for an item, but you still get FM and taros // no room for an item, but you still get FM and taros
reward->iItemCnt = 0; reward->iItemCnt = 0;
sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, sizeof(sP_FE2CL_REP_REWARD_ITEM)); sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, sizeof(sP_FE2CL_REP_REWARD_ITEM));
} else { } else {
// item reward // item reward
item->sItem.iType = 9; item->sItem = getReward(&drop, &chance);
item->sItem.iID = 1;
item->iSlotNum = slot; item->iSlotNum = slot;
item->eIL = 1; // Inventory Location. 1 means player inventory. item->eIL = 1; // Inventory Location. 1 means player inventory.
@ -194,8 +227,91 @@ void MobManager::giveReward(CNSocket *sock) {
plr->Inven[slot] = item->sItem; plr->Inven[slot] = item->sItem;
sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, resplen); sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, resplen);
} }
// event crates
if (settings::EVENTMODE != 0)
giveEventReward(sock, plr);
}
sItemBase MobManager::getReward(MobDrop* drop, MobDropChance* chance) {
sItemBase reward = {};
reward.iType = 9;
reward.iOpt = 1;
int total = 0;
for (int ratio : chance->cratesRatio)
total += ratio;
// randomizing a crate
int randomNum = rand() % total;
int i = 0;
int sum = 0;
do {
reward.iID = drop->crateIDs[i];
sum += chance->cratesRatio[i];
i++;
}
while (sum<=randomNum);
return reward;
}
void MobManager::giveEventReward(CNSocket* sock, Player* player) {
// random drop chance
if (rand() % 100 > settings::EVENTCRATECHANCE)
return;
// no slot = no award
int slot = ItemManager::findFreeSlot(player);
if (slot == -1)
return;
const size_t resplen = sizeof(sP_FE2CL_REP_REWARD_ITEM) + sizeof(sItemReward);
assert(resplen < CN_PACKET_BUFFER_SIZE - 8);
// we know it's only one trailing struct, so we can skip full validation
uint8_t respbuf[resplen]; // not a variable length array, don't worry
sP_FE2CL_REP_REWARD_ITEM* reward = (sP_FE2CL_REP_REWARD_ITEM*)respbuf;
sItemReward* item = (sItemReward*)(respbuf + sizeof(sP_FE2CL_REP_REWARD_ITEM));
// don't forget to zero the buffer!
memset(respbuf, 0, resplen);
// leave everything here as it is
reward->m_iCandy = player->money;
reward->m_iFusionMatter = player->fusionmatter;
reward->m_iBatteryN = player->batteryN;
reward->m_iBatteryW = player->batteryW;
reward->iFatigue = 100; // prevents warning message
reward->iFatigue_Level = 1;
reward->iItemCnt = 1; // remember to update resplen if you change this
// which crate to drop
int crateId;
switch (settings::EVENTMODE)
{
// knishmas
case 1: crateId = 1187; break;
// halloween
case 2: crateId = 1181; break;
// spring
case 3: crateId = 1126; break;
// what
default:
std::cout << "[WARN] Unknown event Id " << settings::EVENTMODE << std::endl;
return;
}
item->sItem.iType = 9;
item->sItem.iID = crateId;
item->sItem.iOpt = 1;
item->iSlotNum = slot;
item->eIL = 1; // Inventory Location. 1 means player inventory.
// update player
player->Inven[slot] = item->sItem;
sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, resplen);
} }
int MobManager::hitMob(CNSocket *sock, Mob *mob, int damage) { int MobManager::hitMob(CNSocket *sock, Mob *mob, int damage) {
@ -242,7 +358,7 @@ void MobManager::killMob(CNSocket *sock, Mob *mob) {
return; return;
if (plr->groupCnt == 1 && plr->iIDGroup == plr->iID) { if (plr->groupCnt == 1 && plr->iIDGroup == plr->iID) {
giveReward(sock); giveReward(sock, mob);
MissionManager::mobKilled(sock, mob->appearanceData.iNPCType); MissionManager::mobKilled(sock, mob->appearanceData.iNPCType);
} else { } else {
plr = PlayerManager::getPlayerFromID(plr->iIDGroup); plr = PlayerManager::getPlayerFromID(plr->iIDGroup);
@ -252,7 +368,7 @@ void MobManager::killMob(CNSocket *sock, Mob *mob) {
for (int i = 0; i < plr->groupCnt; i++) { for (int i = 0; i < plr->groupCnt; i++) {
CNSocket* sockTo = PlayerManager::getSockFromID(plr->groupIDs[i]); CNSocket* sockTo = PlayerManager::getSockFromID(plr->groupIDs[i]);
giveReward(sockTo); giveReward(sockTo, mob);
MissionManager::mobKilled(sockTo, mob->appearanceData.iNPCType); MissionManager::mobKilled(sockTo, mob->appearanceData.iNPCType);
} }
} }

View File

@ -25,6 +25,7 @@ struct Mob : public BaseNPC {
int spawnX; int spawnX;
int spawnY; int spawnY;
int spawnZ; int spawnZ;
int level;
// dead // dead
time_t killedTime = 0; time_t killedTime = 0;
@ -42,6 +43,9 @@ struct Mob : public BaseNPC {
time_t nextAttack = 0; time_t nextAttack = 0;
int roamX, roamY, roamZ; int roamX, roamY, roamZ;
//drop
int dropType;
// temporary; until we're sure what's what // temporary; until we're sure what's what
nlohmann::json data; nlohmann::json data;
@ -53,6 +57,8 @@ struct Mob : public BaseNPC {
regenTime = data["m_iRegenTime"]; regenTime = data["m_iRegenTime"];
idleRange = (int)data["m_iIdleRange"] * 2; // TODO: tuning? idleRange = (int)data["m_iIdleRange"] * 2; // TODO: tuning?
dropType = data["m_iDropType"];
level = data["m_iNpcLevel"];
// XXX: temporarily force respawns for Fusions until we implement instancing // XXX: temporarily force respawns for Fusions until we implement instancing
//if (regenTime >= 300000000) //if (regenTime >= 300000000)
@ -84,9 +90,24 @@ struct Mob : public BaseNPC {
} }
}; };
struct MobDropChance {
int dropChance;
std::vector<int> cratesRatio;
};
struct MobDrop {
std::vector<int> crateIDs;
int dropChanceType;
int taros;
int fm;
int boosts;
};
namespace MobManager { namespace MobManager {
extern std::map<int32_t, Mob*> Mobs; extern std::map<int32_t, Mob*> Mobs;
extern std::queue<int32_t> RemovalQueue; extern std::queue<int32_t> RemovalQueue;
extern std::map<int32_t, MobDropChance> MobDropChances;
extern std::map<int32_t, MobDrop> MobDrops;
extern bool simulateMobs; extern bool simulateMobs;
void init(); void init();
@ -107,7 +128,10 @@ namespace MobManager {
void npcAttackPc(Mob *mob, time_t currTime); void npcAttackPc(Mob *mob, time_t currTime);
int hitMob(CNSocket *sock, Mob *mob, int damage); int hitMob(CNSocket *sock, Mob *mob, int damage);
void killMob(CNSocket *sock, Mob *mob); void killMob(CNSocket *sock, Mob *mob);
void giveReward(CNSocket *sock); void giveReward(CNSocket *sock, Mob *mob);
sItemBase getReward(MobDrop *drop, MobDropChance *chance);
void giveEventReward(CNSocket* sock, Player* player);
std::pair<int,int> lerp(int, int, int, int, int); std::pair<int,int> lerp(int, int, int, int, int);
std::pair<int,int> getDamage(int, int, bool, bool, int, int, int); std::pair<int,int> getDamage(int, int, bool, bool, int, int, int);

View File

@ -119,7 +119,7 @@ void TableData::init() {
auto item = _item.value(); auto item = _item.value();
int typeOverride = getItemType(i); // used for special cases where iEquipLoc doesn't indicate item type int typeOverride = getItemType(i); // used for special cases where iEquipLoc doesn't indicate item type
ItemManager::ItemData[std::pair<int32_t, int32_t>(item["m_iItemNumber"], typeOverride != -1 ? typeOverride : (int)item["m_iEquipLoc"])] ItemManager::ItemData[std::pair<int32_t, int32_t>(item["m_iItemNumber"], typeOverride != -1 ? typeOverride : (int)item["m_iEquipLoc"])]
= { item["m_iTradeAble"] == 1, item["m_iSellAble"] == 1, item["m_iItemPrice"], item["m_iItemSellPrice"], item["m_iStackNumber"], i > 9 ? 0 : (int)item["m_iMinReqLev"], i > 9 ? 1 : (int)item["m_iRarity"], i > 9 ? 0 : (int)item["m_iPointRat"], i > 9 ? 0 : (int)item["m_iGroupRat"], i > 9 ? 0 : (int)item["m_iDefenseRat"] }; = { item["m_iTradeAble"] == 1, item["m_iSellAble"] == 1, item["m_iItemPrice"], item["m_iItemSellPrice"], item["m_iStackNumber"], i > 9 ? 0 : (int)item["m_iMinReqLev"], i > 9 ? 1 : (int)item["m_iRarity"], i > 9 ? 0 : (int)item["m_iPointRat"], i > 9 ? 0 : (int)item["m_iGroupRat"], i > 9 ? 0 : (int)item["m_iDefenseRat"], i > 9 ? 0 : (int)item["m_iReqSex"] };
} }
} }
@ -197,6 +197,8 @@ void TableData::init() {
std::cerr << "[WARN] Malformed mobs.json file! Reason:" << err.what() << std::endl; std::cerr << "[WARN] Malformed mobs.json file! Reason:" << err.what() << std::endl;
} }
loadDrops();
loadPaths(&nextId); // load paths loadPaths(&nextId); // load paths
loadGruntwork(&nextId); loadGruntwork(&nextId);
@ -293,6 +295,115 @@ void TableData::loadPaths(int* nextId) {
} }
} }
/*
* Load drops data from JSON.
* This has to be called after reading xdt because it reffers to ItemData!!!
*/
void TableData::loadDrops() {
try {
std::ifstream inFile(settings::DROPSJSON);
nlohmann::json dropData;
// read file into json
inFile >> dropData;
// MobDropChances
nlohmann::json mobDropChances = dropData["MobDropChances"];
for (nlohmann::json::iterator _dropChance = mobDropChances.begin(); _dropChance != mobDropChances.end(); _dropChance++) {
auto dropChance = _dropChance.value();
MobDropChance toAdd = {};
toAdd.dropChance = (int)dropChance["DropChance"];
for (nlohmann::json::iterator _cratesRatio = dropChance["CratesRatio"].begin(); _cratesRatio != dropChance["CratesRatio"].end(); _cratesRatio++) {
toAdd.cratesRatio.push_back((int)_cratesRatio.value());
}
MobManager::MobDropChances[(int)dropChance["Type"]] = toAdd;
}
// MobDrops
nlohmann::json mobDrops = dropData["MobDrops"];
for (nlohmann::json::iterator _drop = mobDrops.begin(); _drop != mobDrops.end(); _drop++) {
auto drop = _drop.value();
MobDrop toAdd = {};
for (nlohmann::json::iterator _crates = drop["CrateIDs"].begin(); _crates != drop["CrateIDs"].end(); _crates++) {
toAdd.crateIDs.push_back((int)_crates.value());
}
toAdd.dropChanceType = (int)drop["DropChance"];
// Check if DropChance exists
if (MobManager::MobDropChances.find(toAdd.dropChanceType) == MobManager::MobDropChances.end())
{
std::string errorMessage = " MobDropChance not found: " + std::to_string((toAdd.dropChanceType));
throw (std::exception((errorMessage).c_str()));
}
// Check if number of crates is correct
if (!(MobManager::MobDropChances[(int)drop["DropChance"]].cratesRatio.size() == toAdd.crateIDs.size()))
{
std::string errorMessage = " DropType " + std::to_string((int)drop["DropType"]) + " contains invalid number of crates";
throw (std::exception((errorMessage).c_str()));
}
toAdd.taros = (int)drop["Taros"];
toAdd.fm = (int)drop["FM"];
toAdd.boosts = (int)drop["Boosts"];
MobManager::MobDrops[(int)drop["DropType"]] = toAdd;
}
std::cout << "[INFO] Loaded " << MobManager::MobDrops.size() << " Mob Drop Types"<< std::endl;
// Rarity Ratios
nlohmann::json rarities = dropData["RarityRatios"];
for (nlohmann::json::iterator _rarity = rarities.begin(); _rarity != rarities.end(); _rarity++) {
auto rarity = _rarity.value();
std::vector<int> toAdd;
for (nlohmann::json::iterator _ratio = rarity["Ratio"].begin(); _ratio != rarity["Ratio"].end(); _ratio++){
toAdd.push_back((int)_ratio.value());
}
ItemManager::RarityRatios[(int)rarity["Type"]] = toAdd;
}
// Crates
nlohmann::json crates = dropData["Crates"];
for (nlohmann::json::iterator _crate = crates.begin(); _crate != crates.end(); _crate++) {
auto crate = _crate.value();
Crate toAdd;
toAdd.rarityRatioId = (int)crate["RarityRatio"];
for (nlohmann::json::iterator _itemSet = crate["ItemSets"].begin(); _itemSet != crate["ItemSets"].end(); _itemSet++) {
toAdd.itemSets.push_back((int)_itemSet.value());
}
ItemManager::Crates[(int)crate["Id"]] = toAdd;
}
// Crate Items
nlohmann::json items = dropData["Items"];
int itemCount = 0;
for (nlohmann::json::iterator _item = items.begin(); _item != items.end(); _item++) {
auto item = _item.value();
std::pair<int32_t, int32_t> itemSetkey = std::make_pair((int)item["ItemSet"], (int)item["Rarity"]);
std::pair<int32_t, int32_t> itemDataKey = std::make_pair((int)item["Id"], (int)item["Type"]);
if (ItemManager::ItemData.find(itemDataKey) == ItemManager::ItemData.end())
{
char buff[255];
sprintf(buff, "Unknown item with Id %d and Type %d", (int)item["Id"], (int)item["Type"]);
throw (std::exception(buff));
}
std::map<std::pair<int32_t, int32_t>, Item>::iterator toAdd = ItemManager::ItemData.find(itemDataKey);
// if item collection doesn't exist, start a new one
if (ItemManager::CrateItems.find(itemSetkey) == ItemManager::CrateItems.end()) {
std::vector<std::map<std::pair<int32_t, int32_t>, Item>::iterator> vector;
vector.push_back(toAdd);
ItemManager::CrateItems[itemSetkey] = vector;
}
// else add a new element to existing collection
else
ItemManager::CrateItems[itemSetkey].push_back(toAdd);
itemCount++;
}
std::cout << "[INFO] Loaded " << ItemManager::Crates.size() << " Crates containing "
<<itemCount<<" items" << std::endl;
}
catch (const std::exception& err) {
std::cerr << "[WARN] Malformed drops.json file! Reason:" << err.what() << std::endl;
}
}
/* /*
* Create a full and properly-paced path by interpolating between keyframes. * Create a full and properly-paced path by interpolating between keyframes.
*/ */

View File

@ -17,6 +17,7 @@ namespace TableData {
int getItemType(int); int getItemType(int);
void loadPaths(int*); void loadPaths(int*);
void loadDrops();
void constructPathSkyway(nlohmann::json::iterator); void constructPathSkyway(nlohmann::json::iterator);
void constructPathSlider(nlohmann::json, int, int); void constructPathSlider(nlohmann::json, int, int);
void constructPathNPC(nlohmann::json::iterator, int id=0); void constructPathNPC(nlohmann::json::iterator, int id=0);

View File

@ -101,9 +101,15 @@ int main() {
TransportManager::init(); TransportManager::init();
// BuddyManager::init(); // stubbed until we have database integration + lots of bug fixes // BuddyManager::init(); // stubbed until we have database integration + lots of bug fixes
GroupManager::init(); GroupManager::init();
Database::open(); Database::open();
switch (settings::EVENTMODE)
{
case 1: std::cout << "[INFO] Event active. Hey, Hey It's Knishmas!" << std::endl; break;
case 2: std::cout << "[INFO] Event active. Wishing you a spook-tacular Halloween!" << std::endl; break;
case 3: std::cout << "[INFO] Event active. Have a very hoppy Easter!" << std::endl; break;
}
std::cout << "[INFO] Starting Server Threads..." << std::endl; std::cout << "[INFO] Starting Server Threads..." << std::endl;
CNLoginServer loginServer(settings::LOGINPORT); CNLoginServer loginServer(settings::LOGINPORT);
shardServer = new CNShardServer(settings::SHARDPORT); shardServer = new CNShardServer(settings::SHARDPORT);

View File

@ -24,10 +24,15 @@ std::string settings::NPCJSON = "tdata/NPCs.json";
std::string settings::XDTJSON = "tdata/xdt.json"; std::string settings::XDTJSON = "tdata/xdt.json";
std::string settings::MOBJSON = "tdata/mobs.json"; std::string settings::MOBJSON = "tdata/mobs.json";
std::string settings::PATHJSON = "tdata/paths.json"; std::string settings::PATHJSON = "tdata/paths.json";
std::string settings::DROPSJSON = "tdata/drops.json";
std::string settings::GRUNTWORKJSON = "tdata/gruntwork.json"; std::string settings::GRUNTWORKJSON = "tdata/gruntwork.json";
std::string settings::MOTDSTRING = "Welcome to OpenFusion!"; std::string settings::MOTDSTRING = "Welcome to OpenFusion!";
int settings::ACCLEVEL = 1; int settings::ACCLEVEL = 1;
// event mode settings
int settings::EVENTMODE = 0;
int settings::EVENTCRATECHANCE = 10;
void settings::init() { void settings::init() {
INIReader reader("config.ini"); INIReader reader("config.ini");
@ -56,8 +61,11 @@ void settings::init() {
NPCJSON = reader.Get("shard", "npcdata", NPCJSON); NPCJSON = reader.Get("shard", "npcdata", NPCJSON);
XDTJSON = reader.Get("shard", "xdtdata", XDTJSON); XDTJSON = reader.Get("shard", "xdtdata", XDTJSON);
MOBJSON = reader.Get("shard", "mobdata", MOBJSON); MOBJSON = reader.Get("shard", "mobdata", MOBJSON);
DROPSJSON = reader.Get("shard", "dropdata", DROPSJSON);
PATHJSON = reader.Get("shard", "pathdata", PATHJSON); PATHJSON = reader.Get("shard", "pathdata", PATHJSON);
GRUNTWORKJSON = reader.Get("shard", "gruntwork", GRUNTWORKJSON); GRUNTWORKJSON = reader.Get("shard", "gruntwork", GRUNTWORKJSON);
MOTDSTRING = reader.Get("shard", "motd", MOTDSTRING); MOTDSTRING = reader.Get("shard", "motd", MOTDSTRING);
ACCLEVEL = reader.GetInteger("shard", "accountlevel", ACCLEVEL); ACCLEVEL = reader.GetInteger("shard", "accountlevel", ACCLEVEL);
EVENTMODE = reader.GetInteger("shard", "eventmode", EVENTMODE);
EVENTCRATECHANCE = reader.GetInteger("shard", "eventcratechance", EVENTCRATECHANCE);
} }

View File

@ -20,7 +20,10 @@ namespace settings {
extern std::string XDTJSON; extern std::string XDTJSON;
extern std::string MOBJSON; extern std::string MOBJSON;
extern std::string PATHJSON; extern std::string PATHJSON;
extern std::string DROPSJSON;
extern std::string GRUNTWORKJSON; extern std::string GRUNTWORKJSON;
extern int EVENTMODE;
extern int EVENTCRATECHANCE;
void init(); void init();
} }