mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-12-23 11:50:04 +00:00
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:
parent
ab5857e7e2
commit
dd54668697
@ -11,6 +11,13 @@
|
||||
std::map<std::pair<int32_t, int32_t>, Item> ItemManager::ItemData;
|
||||
std::map<int32_t, std::vector<VendorListing>> ItemManager::VendorTables;
|
||||
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() {
|
||||
REGISTER_SHARD_PACKET(P_CL2FE_REQ_ITEM_MOVE, itemMoveHandler);
|
||||
@ -801,12 +808,12 @@ void ItemManager::chestOpenHandler(CNSocket *sock, CNPacketData *data) {
|
||||
if (data->size != sizeof(sP_CL2FE_REQ_ITEM_CHEST_OPEN))
|
||||
return; // ignore the malformed packet
|
||||
|
||||
sP_CL2FE_REQ_ITEM_CHEST_OPEN *pkt = (sP_CL2FE_REQ_ITEM_CHEST_OPEN *)data->buf;
|
||||
Player *plr = PlayerManager::getPlayer(sock);
|
||||
sP_CL2FE_REQ_ITEM_CHEST_OPEN *chest = (sP_CL2FE_REQ_ITEM_CHEST_OPEN *)data->buf;
|
||||
Player *player = PlayerManager::getPlayer(sock);
|
||||
|
||||
if (plr == nullptr)
|
||||
if (player == nullptr)
|
||||
return;
|
||||
|
||||
|
||||
// item giving packet
|
||||
const size_t resplen = sizeof(sP_FE2CL_REP_REWARD_ITEM) + sizeof(sItemReward);
|
||||
assert(resplen < CN_PACKET_BUFFER_SIZE - 8);
|
||||
@ -820,21 +827,23 @@ void ItemManager::chestOpenHandler(CNSocket *sock, CNPacketData *data) {
|
||||
memset(respbuf, 0, resplen);
|
||||
|
||||
// maintain stats
|
||||
reward->m_iCandy = plr->money;
|
||||
reward->m_iFusionMatter = plr->fusionmatter;
|
||||
reward->m_iCandy = player->money;
|
||||
reward->m_iFusionMatter = player->fusionmatter;
|
||||
reward->iFatigue = 100; // prevents warning message
|
||||
reward->iFatigue_Level = 1;
|
||||
reward->iItemCnt = 1; // remember to update resplen if you change this
|
||||
|
||||
// item reward
|
||||
item->sItem.iType = 0;
|
||||
item->sItem.iID = 96;
|
||||
item->sItem.iOpt = 1;
|
||||
item->iSlotNum = pkt->iSlotNum;
|
||||
item->eIL = pkt->eIL;
|
||||
// item reward
|
||||
if (chest->ChestItem.iType != 9)
|
||||
std::cout << "[WARN] Player tried to open a crate with incorrect iType ?!" << std::endl;
|
||||
else
|
||||
item->sItem = openCrate(chest->ChestItem.iID, player->PCStyle.iGender);
|
||||
|
||||
item->iSlotNum = chest->iSlotNum;
|
||||
item->eIL = chest->eIL;
|
||||
|
||||
// update player
|
||||
plr->Inven[pkt->iSlotNum] = item->sItem;
|
||||
player->Inven[chest->iSlotNum] = item->sItem;
|
||||
|
||||
// transmit item
|
||||
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
|
||||
INITSTRUCT(sP_FE2CL_REP_ITEM_CHEST_OPEN_SUCC, resp);
|
||||
|
||||
resp.iSlotNum = pkt->iSlotNum;
|
||||
resp.iSlotNum = chest->iSlotNum;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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
|
||||
int ItemManager::findFreeSlot(Player *plr) {
|
||||
int i;
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
struct Item {
|
||||
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 {
|
||||
int sort, type, iID;
|
||||
@ -14,6 +14,10 @@ struct CrocPotEntry {
|
||||
int multStats, multLooks;
|
||||
float base, rd0, rd1, rd2, rd3;
|
||||
};
|
||||
struct Crate {
|
||||
int rarityRatioId;
|
||||
std::vector<int> itemSets;
|
||||
};
|
||||
|
||||
namespace ItemManager {
|
||||
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<int32_t, std::vector<VendorListing>> VendorTables;
|
||||
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();
|
||||
|
||||
@ -46,6 +55,14 @@ namespace ItemManager {
|
||||
void itemTradeChatHandler(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);
|
||||
Item* getItemData(int32_t id, int32_t type);
|
||||
void checkItemExpire(CNSocket* sock, Player* player);
|
||||
|
@ -11,6 +11,8 @@
|
||||
#include <assert.h>
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
if (plr == nullptr)
|
||||
@ -160,9 +162,31 @@ void MobManager::giveReward(CNSocket *sock) {
|
||||
// don't forget to zero the buffer!
|
||||
memset(respbuf, 0, resplen);
|
||||
|
||||
// NOTE: these will need to be scaled according to the player/mob level difference
|
||||
plr->money += (int)MissionManager::AvatarGrowth[plr->level]["m_iMobFM"]; // this one's innacurate, but close enough for now
|
||||
MissionManager::updateFusionMatter(sock, MissionManager::AvatarGrowth[plr->level]["m_iMobFM"]);
|
||||
// sanity check
|
||||
if (MobDrops.find(mob->dropType) == MobDrops.end()) {
|
||||
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
|
||||
reward->m_iCandy = plr->money;
|
||||
@ -173,20 +197,29 @@ void MobManager::giveReward(CNSocket *sock) {
|
||||
reward->iFatigue_Level = 1;
|
||||
reward->iItemCnt = 1; // remember to update resplen if you change this
|
||||
|
||||
#if 0
|
||||
|
||||
int slot = ItemManager::findFreeSlot(plr);
|
||||
if (slot == -1) {
|
||||
#else
|
||||
int slot = -1;
|
||||
if (true) {
|
||||
#endif
|
||||
|
||||
bool awardDrop = false;
|
||||
MobDropChance chance;
|
||||
// sanity check
|
||||
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
|
||||
reward->iItemCnt = 0;
|
||||
sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, sizeof(sP_FE2CL_REP_REWARD_ITEM));
|
||||
} else {
|
||||
// item reward
|
||||
item->sItem.iType = 9;
|
||||
item->sItem.iID = 1;
|
||||
item->sItem = getReward(&drop, &chance);
|
||||
item->iSlotNum = slot;
|
||||
item->eIL = 1; // Inventory Location. 1 means player inventory.
|
||||
|
||||
@ -194,8 +227,91 @@ void MobManager::giveReward(CNSocket *sock) {
|
||||
plr->Inven[slot] = item->sItem;
|
||||
|
||||
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) {
|
||||
@ -233,7 +349,7 @@ void MobManager::killMob(CNSocket *sock, Mob *mob) {
|
||||
mob->target = nullptr;
|
||||
mob->appearanceData.iConditionBitFlag = 0;
|
||||
mob->killedTime = getTime(); // XXX: maybe introduce a shard-global time for each step?
|
||||
|
||||
|
||||
// check for the edge case where hitting the mob did not aggro it
|
||||
if (sock != nullptr) {
|
||||
Player* plr = PlayerManager::getPlayer(sock);
|
||||
@ -242,7 +358,7 @@ void MobManager::killMob(CNSocket *sock, Mob *mob) {
|
||||
return;
|
||||
|
||||
if (plr->groupCnt == 1 && plr->iIDGroup == plr->iID) {
|
||||
giveReward(sock);
|
||||
giveReward(sock, mob);
|
||||
MissionManager::mobKilled(sock, mob->appearanceData.iNPCType);
|
||||
} else {
|
||||
plr = PlayerManager::getPlayerFromID(plr->iIDGroup);
|
||||
@ -252,7 +368,7 @@ void MobManager::killMob(CNSocket *sock, Mob *mob) {
|
||||
|
||||
for (int i = 0; i < plr->groupCnt; i++) {
|
||||
CNSocket* sockTo = PlayerManager::getSockFromID(plr->groupIDs[i]);
|
||||
giveReward(sockTo);
|
||||
giveReward(sockTo, mob);
|
||||
MissionManager::mobKilled(sockTo, mob->appearanceData.iNPCType);
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ struct Mob : public BaseNPC {
|
||||
int spawnX;
|
||||
int spawnY;
|
||||
int spawnZ;
|
||||
int level;
|
||||
|
||||
// dead
|
||||
time_t killedTime = 0;
|
||||
@ -42,6 +43,9 @@ struct Mob : public BaseNPC {
|
||||
time_t nextAttack = 0;
|
||||
int roamX, roamY, roamZ;
|
||||
|
||||
//drop
|
||||
int dropType;
|
||||
|
||||
// temporary; until we're sure what's what
|
||||
nlohmann::json data;
|
||||
|
||||
@ -53,6 +57,8 @@ struct Mob : public BaseNPC {
|
||||
|
||||
regenTime = data["m_iRegenTime"];
|
||||
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
|
||||
//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 {
|
||||
extern std::map<int32_t, Mob*> Mobs;
|
||||
extern std::queue<int32_t> RemovalQueue;
|
||||
extern std::map<int32_t, MobDropChance> MobDropChances;
|
||||
extern std::map<int32_t, MobDrop> MobDrops;
|
||||
extern bool simulateMobs;
|
||||
|
||||
void init();
|
||||
@ -107,7 +128,10 @@ namespace MobManager {
|
||||
void npcAttackPc(Mob *mob, time_t currTime);
|
||||
int hitMob(CNSocket *sock, Mob *mob, int damage);
|
||||
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> getDamage(int, int, bool, bool, int, int, int);
|
||||
|
||||
|
@ -119,7 +119,7 @@ void TableData::init() {
|
||||
auto item = _item.value();
|
||||
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"])]
|
||||
= { 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;
|
||||
}
|
||||
|
||||
loadDrops();
|
||||
|
||||
loadPaths(&nextId); // load paths
|
||||
|
||||
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.
|
||||
*/
|
||||
|
@ -17,6 +17,7 @@ namespace TableData {
|
||||
|
||||
int getItemType(int);
|
||||
void loadPaths(int*);
|
||||
void loadDrops();
|
||||
void constructPathSkyway(nlohmann::json::iterator);
|
||||
void constructPathSlider(nlohmann::json, int, int);
|
||||
void constructPathNPC(nlohmann::json::iterator, int id=0);
|
||||
|
@ -101,9 +101,15 @@ int main() {
|
||||
TransportManager::init();
|
||||
// BuddyManager::init(); // stubbed until we have database integration + lots of bug fixes
|
||||
GroupManager::init();
|
||||
|
||||
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;
|
||||
CNLoginServer loginServer(settings::LOGINPORT);
|
||||
shardServer = new CNShardServer(settings::SHARDPORT);
|
||||
|
@ -24,10 +24,15 @@ std::string settings::NPCJSON = "tdata/NPCs.json";
|
||||
std::string settings::XDTJSON = "tdata/xdt.json";
|
||||
std::string settings::MOBJSON = "tdata/mobs.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::MOTDSTRING = "Welcome to OpenFusion!";
|
||||
int settings::ACCLEVEL = 1;
|
||||
|
||||
// event mode settings
|
||||
int settings::EVENTMODE = 0;
|
||||
int settings::EVENTCRATECHANCE = 10;
|
||||
|
||||
void settings::init() {
|
||||
INIReader reader("config.ini");
|
||||
|
||||
@ -56,8 +61,11 @@ void settings::init() {
|
||||
NPCJSON = reader.Get("shard", "npcdata", NPCJSON);
|
||||
XDTJSON = reader.Get("shard", "xdtdata", XDTJSON);
|
||||
MOBJSON = reader.Get("shard", "mobdata", MOBJSON);
|
||||
DROPSJSON = reader.Get("shard", "dropdata", DROPSJSON);
|
||||
PATHJSON = reader.Get("shard", "pathdata", PATHJSON);
|
||||
GRUNTWORKJSON = reader.Get("shard", "gruntwork", GRUNTWORKJSON);
|
||||
MOTDSTRING = reader.Get("shard", "motd", MOTDSTRING);
|
||||
ACCLEVEL = reader.GetInteger("shard", "accountlevel", ACCLEVEL);
|
||||
EVENTMODE = reader.GetInteger("shard", "eventmode", EVENTMODE);
|
||||
EVENTCRATECHANCE = reader.GetInteger("shard", "eventcratechance", EVENTCRATECHANCE);
|
||||
}
|
||||
|
@ -20,7 +20,10 @@ namespace settings {
|
||||
extern std::string XDTJSON;
|
||||
extern std::string MOBJSON;
|
||||
extern std::string PATHJSON;
|
||||
extern std::string DROPSJSON;
|
||||
extern std::string GRUNTWORKJSON;
|
||||
extern int EVENTMODE;
|
||||
extern int EVENTCRATECHANCE;
|
||||
|
||||
void init();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user