added better drop handling, parsing, rng

This commit is contained in:
FinnHornhoover 2021-03-28 13:57:43 -07:00 committed by Gent Semaj
parent aa028392f0
commit 78b17aea72
12 changed files with 486 additions and 240 deletions

View File

@ -73,6 +73,7 @@ CXXSRC=\
src/Racing.cpp\ src/Racing.cpp\
src/Vendors.cpp\ src/Vendors.cpp\
src/Trading.cpp\ src/Trading.cpp\
src/Rand.cpp\
# headers (for timestamp purposes) # headers (for timestamp purposes)
CXXHDR=\ CXXHDR=\
@ -117,6 +118,7 @@ CXXHDR=\
src/Racing.hpp\ src/Racing.hpp\
src/Vendors.hpp\ src/Vendors.hpp\
src/Trading.hpp\ src/Trading.hpp\
src/Rand.hpp\
COBJ=$(CSRC:.c=.o) COBJ=$(CSRC:.c=.o)
CXXOBJ=$(CXXSRC:.cpp=.o) CXXOBJ=$(CXXSRC:.cpp=.o)

View File

@ -4,6 +4,7 @@
#include "Items.hpp" #include "Items.hpp"
#include "Missions.hpp" #include "Missions.hpp"
#include "Nanos.hpp" #include "Nanos.hpp"
#include "Rand.hpp"
// helper function, not a packet handler // helper function, not a packet handler
void BuiltinCommands::setSpecialState(CNSocket* sock, CNPacketData* data) { void BuiltinCommands::setSpecialState(CNSocket* sock, CNPacketData* data) {
@ -271,8 +272,8 @@ static void teleportPlayer(CNSocket *sock, CNPacketData *data) {
case eCN_GM_TeleportMapType__Unstick: case eCN_GM_TeleportMapType__Unstick:
targetPlr = PlayerManager::getPlayer(targetSock); targetPlr = PlayerManager::getPlayer(targetSock);
PlayerManager::sendPlayerTo(targetSock, targetPlr->x - unstickRange/2 + rand() % unstickRange, PlayerManager::sendPlayerTo(targetSock, targetPlr->x - unstickRange/2 + Rand::rand(unstickRange),
targetPlr->y - unstickRange/2 + rand() % unstickRange, targetPlr->z + 80); targetPlr->y - unstickRange/2 + Rand::rand(unstickRange), targetPlr->z + 80);
break; break;
} }
} }

View File

@ -8,6 +8,7 @@
#include "Transport.hpp" #include "Transport.hpp"
#include "Racing.hpp" #include "Racing.hpp"
#include "Abilities.hpp" #include "Abilities.hpp"
#include "Rand.hpp"
#include <assert.h> #include <assert.h>
@ -26,7 +27,7 @@ static std::pair<int,int> getDamage(int attackPower, int defensePower, bool shou
// base calculation // base calculation
int damage = attackPower * attackPower / (attackPower + defensePower); int damage = attackPower * attackPower / (attackPower + defensePower);
damage = std::max(10 + attackPower / 10, damage - (defensePower - attackPower / 6) * difficulty / 100); damage = std::max(10 + attackPower / 10, damage - (defensePower - attackPower / 6) * difficulty / 100);
damage = damage * (rand() % 40 + 80) / 100; damage = damage * (Rand::rand(40) + 80) / 100;
// Adaptium/Blastons/Cosmix // Adaptium/Blastons/Cosmix
if (attackerStyle != -1 && defenderStyle != -1 && attackerStyle != defenderStyle) { if (attackerStyle != -1 && defenderStyle != -1 && attackerStyle != defenderStyle) {
@ -47,7 +48,7 @@ static std::pair<int,int> getDamage(int attackPower, int defensePower, bool shou
ret.first = damage; ret.first = damage;
ret.second = 1; ret.second = 1;
if (shouldCrit && rand() % 20 == 0) { if (shouldCrit && Rand::rand(20) == 0) {
ret.first *= 2; // critical hit ret.first *= 2; // critical hit
ret.second = 2; ret.second = 2;
} }
@ -224,12 +225,12 @@ void Combat::killMob(CNSocket *sock, Mob *mob) {
if (sock != nullptr) { if (sock != nullptr) {
Player* plr = PlayerManager::getPlayer(sock); Player* plr = PlayerManager::getPlayer(sock);
int rolledBoosts = rand(); int rolledBoosts = Rand::rand();
int rolledPotions = rand(); int rolledPotions = Rand::rand();
int rolledCrate = rand(); int rolledCrate = Rand::rand();
int rolledCrateType = rand(); int rolledCrateType = Rand::rand();
int rolledEvent = rand(); int rolledEvent = Rand::rand();
int rolledQItem = rand(); int rolledQItem = Rand::rand();
if (plr->groupCnt == 1 && plr->iIDGroup == plr->iID) { if (plr->groupCnt == 1 && plr->iIDGroup == plr->iID) {
Items::giveMobDrop(sock, mob, rolledBoosts, rolledPotions, rolledCrate, rolledCrateType, rolledEvent); Items::giveMobDrop(sock, mob, rolledBoosts, rolledPotions, rolledCrate, rolledCrateType, rolledEvent);
@ -522,7 +523,7 @@ static int8_t addBullet(Player* plr, bool isGrenade) {
// temp solution Jade fix plz // temp solution Jade fix plz
toAdd.weaponBoost = plr->batteryW > 0; toAdd.weaponBoost = plr->batteryW > 0;
if (toAdd.weaponBoost) { if (toAdd.weaponBoost) {
int boostCost = rand() % 11 + 20; int boostCost = Rand::rand(11) + 20;
plr->batteryW = boostCost > plr->batteryW ? 0 : plr->batteryW - boostCost; plr->batteryW = boostCost > plr->batteryW ? 0 : plr->batteryW - boostCost;
} }

View File

@ -7,6 +7,7 @@
#include "Abilities.hpp" #include "Abilities.hpp"
#include "Missions.hpp" #include "Missions.hpp"
#include "Eggs.hpp" #include "Eggs.hpp"
#include "Rand.hpp"
#include <string.h> // for memset() #include <string.h> // for memset()
#include <assert.h> #include <assert.h>
@ -15,14 +16,18 @@ using namespace Items;
std::map<std::pair<int32_t, int32_t>, Items::Item> Items::ItemData; std::map<std::pair<int32_t, int32_t>, Items::Item> Items::ItemData;
std::map<int32_t, CrocPotEntry> Items::CrocPotTable; std::map<int32_t, CrocPotEntry> Items::CrocPotTable;
std::map<int32_t, std::vector<int>> Items::RarityRatios; std::map<int32_t, std::vector<int32_t>> Items::RarityWeights;
std::map<int32_t, Crate> Items::Crates; std::map<int32_t, Crate> Items::Crates;
// pair Itemset, Rarity -> vector of pointers (map iterators) to records in ItemData std::map<int32_t, DroppableItem> Items::DroppableItems;
std::map<std::pair<int32_t, int32_t>, std::vector<std::map<std::pair<int32_t, int32_t>, Items::Item>::iterator>> Items::CrateItems;
std::map<std::string, std::vector<std::pair<int32_t, int32_t>>> Items::CodeItems; std::map<std::string, std::vector<std::pair<int32_t, int32_t>>> Items::CodeItems;
std::map<int32_t, MobDropChance> Items::MobDropChances; std::map<int32_t, CrateDropChance> Items::CrateDropChances;
std::map<int32_t, std::vector<int32_t>> Items::CrateDropTypes;
std::map<int32_t, MiscDropChance> Items::MiscDropChances;
std::map<int32_t, MiscDropType> Items::MiscDropTypes;
std::map<int32_t, MobDrop> Items::MobDrops; std::map<int32_t, MobDrop> Items::MobDrops;
std::map<int32_t, ItemSetType> Items::ItemSetTypes;
std::map<int32_t, ItemSetChance> Items::ItemSetChances;
#ifdef ACADEMY #ifdef ACADEMY
std::map<int32_t, int32_t> Items::NanoCapsules; // crate id -> nano id std::map<int32_t, int32_t> Items::NanoCapsules; // crate id -> nano id
@ -81,14 +86,29 @@ static void nanoCapsuleHandler(CNSocket* sock, int slot, sItemBase *chest) {
} }
#endif #endif
static int getRarity(Crate& crate, int itemSetId) { static int choice(const std::vector<int>& weights, int rolled) {
int total = std::accumulate(weights.begin(), weights.end(), 0);
int randValue = rolled % total;
int currentIndex = -1;
do {
currentIndex++;
randValue -= weights[currentIndex];
} while (randValue >= 0);
return currentIndex;
}
static int getRarity(int crateId, int itemSetTypeId) {
Crate& crate = Items::Crates[crateId];
// find rarity ratio // find rarity ratio
if (RarityRatios.find(crate.rarityRatioId) == RarityRatios.end()) { if (Items::RarityWeights.find(crate.rarityWeightId) == Items::RarityWeights.end()) {
std::cout << "[WARN] Rarity Ratio " << crate.rarityRatioId << " not found!" << std::endl; std::cout << "[WARN] Rarity Weight " << crate.rarityWeightId << " not found!" << std::endl;
return -1; return -1;
} }
std::vector<int> rarityRatio = RarityRatios[crate.rarityRatioId]; std::vector<int>& rarityWeights = Items::RarityWeights[crate.rarityWeightId];
/* /*
* First we have to check if specified item set contains items with all specified rarities, * First we have to check if specified item set contains items with all specified rarities,
@ -97,74 +117,137 @@ static int getRarity(Crate& crate, int itemSetId) {
*/ */
// remember that rarities start from 1! // remember that rarities start from 1!
for (int i = 0; i < rarityRatio.size(); i++){ std::set<int> rarityIndices;
if (CrateItems.find(std::make_pair(itemSetId, i+1)) == CrateItems.end()) for (int droppableItemId : Items::ItemSetTypes[itemSetTypeId].droppableItemIds) {
rarityRatio[i] = 0; if (Items::DroppableItems.find(droppableItemId) == Items::DroppableItems.end())
continue;
rarityIndices.insert(Items::DroppableItems[droppableItemId].rarity - 1);
// shortcut
if (rarityIndices.size() == rarityWeights.size())
break;
} }
int total = 0; if (rarityIndices.empty()) {
for (int value : rarityRatio) std::cout << "[WARN] Item Set " << crate.itemSetTypeId << " has no valid items assigned?!" << std::endl;
total += value;
if (total == 0) {
std::cout << "Item Set " << itemSetId << " has no items assigned?!" << std::endl;
return -1; return -1;
} }
// now return a random rarity number std::vector<int> relevantWeights;
int randomNum = rand() % total; for (int index : rarityIndices)
int rarity = 0; relevantWeights.push_back(rarityWeights[index]);
int sum = 0;
do {
sum += rarityRatio[rarity];
rarity++;
} while (sum <= randomNum);
return rarity; // now return a random rarity number (starting from 1)
return Rand::randWeighted(relevantWeights) + 1;
} }
static int getCrateItem(sItemBase& result, int itemSetId, int rarity, int playerGender) { static int getCrateItem(sItemBase* result, int itemSetTypeId, int itemSetChanceId, int rarity, int playerGender) {
auto key = std::make_pair(itemSetId, rarity); // (int, vector<int>)
auto& [ignoreGender, droppableItemIds] = Items::ItemSetTypes[itemSetTypeId];
if (CrateItems.find(key) == CrateItems.end()) { // collect valid items that match the rarity and (if not ignored) gender
std::cout << "[WARN] Item Set ID " << itemSetId << " Rarity " << rarity << " does not exist" << std::endl; std::vector<std::pair<int, DroppableItem*>> validItems;
return -1; for (int i = 0; i < droppableItemIds.size(); i++) {
} int droppableItemId = droppableItemIds[i];
// only take into account items that have correct gender if (Items::DroppableItems.find(droppableItemId) == Items::DroppableItems.end()) {
std::vector<std::map<std::pair<int32_t, int32_t>, Item>::iterator> items; std::cout << "[WARN] Droppable item " << droppableItemId << " was not found, skipping..." << std::endl;
for (auto crateitem : CrateItems[key]) {
int gender = crateitem->second.gender;
// if gender is incorrect, exclude item
if (gender != 0 && gender != playerGender)
continue; continue;
items.push_back(crateitem);
} }
if (items.size() == 0) { DroppableItem* droppableItem = &Items::DroppableItems[droppableItemId];
std::cout << "[WARN] Set ID " << itemSetId << " Rarity " << rarity << " contains no valid items" << std::endl;
if (droppableItem->rarity != rarity)
continue;
auto key = std::make_pair(droppableItem->itemId, droppableItem->type);
if (Items::ItemData.find(key) == Items::ItemData.end()) {
std::cout << "[WARN] Item-Type pair (" << key.first << ", " << key.second << ") specified by droppable item "
<< droppableItemId << " was not found, skipping..." << std::endl;
continue;
}
// if gender is incorrect, exclude item
int itemGender = Items::ItemData[key].gender;
if (!ignoreGender && itemGender != 0 && itemGender != playerGender)
continue;
validItems.push_back(std::make_pair(i, droppableItem));
}
if (validItems.empty()) {
std::cout << "[WARN] Set ID " << itemSetTypeId << " Chance ID " << itemSetChanceId
<< " Rarity " << rarity << " contains no valid items" << std::endl;
return -1; return -1;
} }
auto item = items[rand() % items.size()]; // (int, map<int, int>)
auto& [defaultWeight, specialWeights] = Items::ItemSetChances[itemSetChanceId];
result.iID = item->first.first; // initialize all weights as the default weight for all item slots
result.iType = item->first.second; std::vector<int> itemWeights(validItems.size(), defaultWeight);
result.iOpt = 1;
if (!specialWeights.empty()) {
for (int i = 0; i < validItems.size(); i++) {
// (int, DroppableItem*)
auto& [dropIndex, droppableItem] = validItems[i];
if (specialWeights.find(dropIndex) == specialWeights.end())
continue;
int weight = specialWeights[dropIndex];
// allow 0 weights for convenience
if (weight > -1)
itemWeights[i] = weight;
}
}
int chosenIndex = Rand::randWeighted(itemWeights);
DroppableItem* item = validItems[chosenIndex].second;
result->iID = item->itemId;
result->iType = item->type;
result->iOpt = 1;
return 0; return 0;
} }
static int getItemSetId(Crate& crate, int crateId) { static int getValidCrateId(int crateId) {
int itemSetsCount = crate.itemSets.size(); // find the crate
if (itemSetsCount == 0) { if (Items::Crates.find(crateId) == Items::Crates.end()) {
std::cout << "[WARN] Crate " << crateId << " has no item sets assigned?!" << std::endl; std::cout << "[WARN] Crate " << crateId << " not found!" << std::endl;
return -1; return -1;
} }
// if crate points to multiple itemSets, choose a random one return crateId;
int itemSetIndex = rand() % itemSetsCount; }
return crate.itemSets[itemSetIndex];
static int getValidItemSetTypeId(int crateId) {
Crate& crate = Items::Crates[crateId];
// find item set type
if (Items::ItemSetTypes.find(crate.itemSetTypeId) == Items::ItemSetTypes.end()) {
std::cout << "[WARN] Crate " << crateId << " was assigned item set "
<< crate.itemSetTypeId << " which is invalid!" << std::endl;
return -1;
}
return crate.itemSetTypeId;
}
static int getValidItemSetChanceId(int crateId) {
Crate& crate = Items::Crates[crateId];
// find item set chances
if (Items::ItemSetChances.find(crate.itemSetChanceId) == Items::ItemSetChances.end()) {
std::cout << "[WARN] Crate " << crateId << " was assigned item set chance object "
<< crate.itemSetChanceId << " which is invalid!" << std::endl;
return -1;
}
return crate.itemSetChanceId;
} }
static void itemMoveHandler(CNSocket* sock, CNPacketData* data) { static void itemMoveHandler(CNSocket* sock, CNPacketData* data) {
@ -486,36 +569,34 @@ static void chestOpenHandler(CNSocket *sock, CNPacketData *data) {
item->iSlotNum = pkt->iSlotNum; item->iSlotNum = pkt->iSlotNum;
item->eIL = 1; item->eIL = 1;
int itemSetId = -1, rarity = -1, ret = -1; int validItemSetTypeId = -1, validItemSetChanceId = -1, rarity = -1, ret = -1;
bool failing = false;
// find the crate int validCrateId = getValidCrateId(chest->iID);
if (Crates.find(chest->iID) == Crates.end()) { bool failing = (validCrateId == -1);
std::cout << "[WARN] Crate " << chest->iID << " not found!" << std::endl;
failing = true;
}
Crate& crate = Crates[chest->iID];
if (!failing) if (!failing)
itemSetId = getItemSetId(crate, chest->iID); validItemSetTypeId = getValidItemSetTypeId(validCrateId);
if (itemSetId == -1) failing = (validItemSetTypeId == -1);
failing = true;
if (!failing) if (!failing)
rarity = getRarity(crate, itemSetId); validItemSetChanceId = getValidItemSetChanceId(validCrateId);
if (rarity == -1) failing = (validItemSetChanceId == -1);
failing = true;
if (!failing) if (!failing)
ret = getCrateItem(item->sItem, itemSetId, rarity, plr->PCStyle.iGender); rarity = getRarity(validCrateId, validItemSetTypeId);
if (ret == -1) failing = (rarity == -1);
failing = true;
if (!failing)
ret = getCrateItem(&item->sItem, validItemSetTypeId, validItemSetChanceId, rarity, plr->PCStyle.iGender);
failing = (ret == -1);
// if we failed to open a crate, at least give the player a gumball (suggested by Jade) // if we failed to open a crate, at least give the player a gumball (suggested by Jade)
if (failing) { if (failing) {
item->sItem.iType = 7; item->sItem.iType = 7;
item->sItem.iID = 119 + (rand() % 3); item->sItem.iID = 119 + Rand::rand(3);
item->sItem.iOpt = 1; item->sItem.iOpt = 1;
std::cout << "[WARN] Crate open failed, giving a Gumball..." << std::endl;
} }
// update player // update player
plr->Inven[pkt->iSlotNum] = item->sItem; plr->Inven[pkt->iSlotNum] = item->sItem;
@ -617,29 +698,17 @@ void Items::updateEquips(CNSocket* sock, Player* plr) {
} }
} }
static void getMobDrop(sItemBase *reward, MobDrop* drop, MobDropChance* chance, int rolled) { static void getMobDrop(sItemBase* reward, const std::vector<int>& weights, const std::vector<int>& crateIds, int rolled) {
int chosenIndex = choice(weights, rolled);
reward->iType = 9; reward->iType = 9;
reward->iOpt = 1; reward->iOpt = 1;
reward->iID = crateIds[chosenIndex];
int total = 0;
for (int ratio : chance->cratesRatio)
total += ratio;
// randomizing a crate
int randomNum = rolled % total;
int i = 0;
int sum = 0;
do {
reward->iID = drop->crateIDs[i];
sum += chance->cratesRatio[i];
i++;
}
while (sum<=randomNum);
} }
static void giveEventDrop(CNSocket* sock, Player* player, int rolled) { static void giveEventDrop(CNSocket* sock, Player* player, int rolled) {
// random drop chance // random drop chance
if (rand() % 100 > settings::EVENTCRATECHANCE) if (Rand::rand(100) > settings::EVENTCRATECHANCE)
return; return;
// no slot = no reward // no slot = no reward
@ -715,18 +784,47 @@ void Items::giveMobDrop(CNSocket *sock, Mob* mob, int rolledBoosts, int rolledPo
// find correct mob drop // find correct mob drop
MobDrop& drop = MobDrops[mob->dropType]; MobDrop& drop = MobDrops[mob->dropType];
plr->money += drop.taros; // use the keys to fetch data from other maps
// sanity check
if (Items::CrateDropChances.find(drop.crateDropChanceId) == Items::CrateDropChances.end()) {
std::cout << "[WARN] Crate Drop Chance Object " << drop.crateDropChanceId << " was not found" << std::endl;
return;
}
CrateDropChance crateDropChance = Items::CrateDropChances[drop.crateDropChanceId];
// sanity check
if (Items::CrateDropTypes.find(drop.crateDropTypeId) == Items::CrateDropTypes.end()) {
std::cout << "[WARN] Crate Drop Type Object " << drop.crateDropTypeId << " was not found" << std::endl;
return;
}
std::vector<int> crateDropType = Items::CrateDropTypes[drop.crateDropTypeId];
// sanity check
if (Items::MiscDropChances.find(drop.miscDropChanceId) == Items::MiscDropChances.end()) {
std::cout << "[WARN] Misc Drop Chance Object " << drop.miscDropChanceId << " was not found" << std::endl;
return;
}
MiscDropChance miscDropChance = Items::MiscDropChances[drop.miscDropChanceId];
// sanity check
if (Items::MiscDropTypes.find(drop.miscDropTypeId) == Items::MiscDropTypes.end()) {
std::cout << "[WARN] Misc Drop Type Object " << drop.miscDropTypeId << " was not found" << std::endl;
return;
}
MiscDropType miscDropType = Items::MiscDropTypes[drop.miscDropTypeId];
plr->money += miscDropType.taroAmount;
// money nano boost // money nano boost
if (plr->iConditionBitFlag & CSB_BIT_REWARD_CASH) { if (plr->iConditionBitFlag & CSB_BIT_REWARD_CASH) {
int boost = 0; int boost = 0;
if (Nanos::getNanoBoost(plr)) // for gumballs if (Nanos::getNanoBoost(plr)) // for gumballs
boost = 1; boost = 1;
plr->money += drop.taros * (5 + boost) / 25; plr->money += miscDropType.taroAmount * (5 + boost) / 25;
} }
// formula for scaling FM with player/mob level difference // formula for scaling FM with player/mob level difference
// TODO: adjust this better // TODO: adjust this better
int levelDifference = plr->level - mob->level; int levelDifference = plr->level - mob->level;
int fm = drop.fm; int fm = miscDropType.fmAmount;
if (levelDifference > 0) if (levelDifference > 0)
fm = levelDifference < 10 ? fm - (levelDifference * fm / 10) : 0; fm = levelDifference < 10 ? fm - (levelDifference * fm / 10) : 0;
// scavenger nano boost // scavenger nano boost
@ -739,13 +837,11 @@ void Items::giveMobDrop(CNSocket *sock, Mob* mob, int rolledBoosts, int rolledPo
Missions::updateFusionMatter(sock, fm); Missions::updateFusionMatter(sock, fm);
// give boosts 1 in 3 times if (rolledPotions % miscDropChance.potionDropChanceTotal < miscDropChance.potionDropChance)
if (drop.boosts > 0) { plr->batteryN += miscDropType.potionAmount;
if (rolledPotions % 3 == 0) if (rolledBoosts % miscDropChance.boostDropChanceTotal < miscDropChance.boostDropChance)
plr->batteryN += drop.boosts; plr->batteryW += miscDropType.boostAmount;
if (rolledBoosts % 3 == 0)
plr->batteryW += drop.boosts;
}
// caps // caps
if (plr->batteryW > 9999) if (plr->batteryW > 9999)
plr->batteryW = 9999; plr->batteryW = 9999;
@ -763,25 +859,14 @@ void Items::giveMobDrop(CNSocket *sock, Mob* mob, int rolledBoosts, int rolledPo
int slot = findFreeSlot(plr); int slot = findFreeSlot(plr);
bool awardDrop = false;
MobDropChance *chance = nullptr;
// sanity check
if (MobDropChances.find(drop.dropChanceType) == MobDropChances.end()) {
std::cout << "[WARN] Unknown Drop Chance Type: " << drop.dropChanceType << std::endl;
return; // this also prevents holiday crate drops, but oh well
} else {
chance = &MobDropChances[drop.dropChanceType];
awardDrop = (rolledCrate % 1000 < chance->dropChance);
}
// no drop // no drop
if (slot == -1 || !awardDrop) { if (slot == -1 || rolledCrate % crateDropChance.dropChanceTotal >= crateDropChance.dropChance) {
// 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
getMobDrop(&item->sItem, &drop, chance, rolledCrateType); getMobDrop(&item->sItem, crateDropChance.crateWeights, crateDropType, rolledCrateType);
item->iSlotNum = slot; item->iSlotNum = slot;
item->eIL = 1; // Inventory Location. 1 means player inventory. item->eIL = 1; // Inventory Location. 1 means player inventory.

View File

@ -10,21 +10,51 @@ struct CrocPotEntry {
}; };
struct Crate { struct Crate {
int rarityRatioId; int itemSetChanceId;
std::vector<int> itemSets; int itemSetTypeId;
int rarityWeightId;
}; };
struct MobDropChance { struct CrateDropChance {
int dropChance; int dropChance, dropChanceTotal;
std::vector<int> cratesRatio; std::vector<int> crateWeights;
};
struct MiscDropChance {
int potionDropChance, potionDropChanceTotal;
int boostDropChance, boostDropChanceTotal;
int taroDropChance, taroDropChanceTotal;
int fmDropChance, fmDropChanceTotal;
};
struct MiscDropType {
int potionAmount;
int boostAmount;
int taroAmount;
int fmAmount;
}; };
struct MobDrop { struct MobDrop {
std::vector<int> crateIDs; int crateDropChanceId;
int dropChanceType; int crateDropTypeId;
int taros; int miscDropChanceId;
int fm; int miscDropTypeId;
int boosts; };
struct ItemSetType {
bool ignoreGender;
std::vector<int> droppableItemIds;
};
struct ItemSetChance {
int defaultItemWeight;
std::map<int, int> specialItemWeights;
};
struct DroppableItem {
int itemId;
int rarity;
int type;
}; };
namespace Items { namespace Items {
@ -44,16 +74,19 @@ namespace Items {
// hopefully this is fine since it's never modified after load // hopefully this is fine since it's never modified after load
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, 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, std::vector<int32_t>> RarityWeights;
extern std::map<int32_t, Crate> Crates; 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<int32_t, DroppableItem> DroppableItems;
extern std::map<std::pair<int32_t, int32_t>,
std::vector<std::map<std::pair<int32_t, int32_t>, Item>::iterator>> CrateItems;
extern std::map<std::string, std::vector<std::pair<int32_t, int32_t>>> CodeItems; // code -> vector of <id, type> extern std::map<std::string, std::vector<std::pair<int32_t, int32_t>>> CodeItems; // code -> vector of <id, type>
// mob drops // mob drops
extern std::map<int32_t, MobDropChance> MobDropChances; extern std::map<int32_t, CrateDropChance> CrateDropChances;
extern std::map<int32_t, std::vector<int32_t>> CrateDropTypes;
extern std::map<int32_t, MiscDropChance> MiscDropChances;
extern std::map<int32_t, MiscDropType> MiscDropTypes;
extern std::map<int32_t, MobDrop> MobDrops; extern std::map<int32_t, MobDrop> MobDrops;
extern std::map<int32_t, ItemSetType> ItemSetTypes;
extern std::map<int32_t, ItemSetChance> ItemSetChances;
void init(); void init();

View File

@ -5,6 +5,7 @@
#include "Nanos.hpp" #include "Nanos.hpp"
#include "Combat.hpp" #include "Combat.hpp"
#include "Abilities.hpp" #include "Abilities.hpp"
#include "Rand.hpp"
#include <cmath> #include <cmath>
#include <limits.h> #include <limits.h>
@ -334,7 +335,7 @@ static void useAbilities(Mob *mob, time_t currTime) {
return; return;
} }
int random = rand() % 2000 * 1000; int random = Rand::rand(2000) * 1000;
int prob1 = (int)mob->data["m_iActiveSkill1Prob"]; // active skill probability int prob1 = (int)mob->data["m_iActiveSkill1Prob"]; // active skill probability
int prob2 = (int)mob->data["m_iCorruptionTypeProb"]; // corruption probability int prob2 = (int)mob->data["m_iCorruptionTypeProb"]; // corruption probability
int prob3 = (int)mob->data["m_iMegaTypeProb"]; // eruption probability int prob3 = (int)mob->data["m_iMegaTypeProb"]; // eruption probability
@ -364,7 +365,7 @@ static void useAbilities(Mob *mob, time_t currTime) {
if (mob->skillStyle == -1) if (mob->skillStyle == -1)
mob->skillStyle = 2; mob->skillStyle = 2;
if (mob->skillStyle == -2) if (mob->skillStyle == -2)
mob->skillStyle = rand() % 3; mob->skillStyle = Rand::rand(3);
pkt.iStyle = mob->skillStyle; pkt.iStyle = mob->skillStyle;
NPCManager::sendToViewable(mob, &pkt, P_FE2CL_NPC_SKILL_CORRUPTION_READY, sizeof(sP_FE2CL_NPC_SKILL_CORRUPTION_READY)); NPCManager::sendToViewable(mob, &pkt, P_FE2CL_NPC_SKILL_CORRUPTION_READY, sizeof(sP_FE2CL_NPC_SKILL_CORRUPTION_READY));
mob->nextAttack = currTime + 1800; mob->nextAttack = currTime + 1800;
@ -563,7 +564,7 @@ static void combatStep(Mob *mob, time_t currTime) {
int mobRange = (int)mob->data["m_iAtkRange"] + (int)mob->data["m_iRadius"]; int mobRange = (int)mob->data["m_iAtkRange"] + (int)mob->data["m_iRadius"];
if (currTime >= mob->nextAttack) { if (currTime >= mob->nextAttack) {
if (mob->skillStyle != -1 || distance <= mobRange || rand() % 20 == 0) // while not in attack range, 1 / 20 chance. if (mob->skillStyle != -1 || distance <= mobRange || Rand::rand(20) == 0) // while not in attack range, 1 / 20 chance.
useAbilities(mob, currTime); useAbilities(mob, currTime);
if (mob->target == nullptr) if (mob->target == nullptr)
return; return;
@ -638,7 +639,7 @@ void MobAI::incNextMovement(Mob *mob, time_t currTime) {
currTime = getTime(); currTime = getTime();
int delay = (int)mob->data["m_iDelayTime"] * 1000; int delay = (int)mob->data["m_iDelayTime"] * 1000;
mob->nextMovement = currTime + delay/2 + rand() % (delay/2); mob->nextMovement = currTime + delay/2 + Rand::rand(delay/2);
} }
static void roamingStep(Mob *mob, time_t currTime) { static void roamingStep(Mob *mob, time_t currTime) {
@ -681,8 +682,8 @@ static void roamingStep(Mob *mob, time_t currTime) {
int minDistance = mob->idleRange / 2; int minDistance = mob->idleRange / 2;
// pick a random destination // pick a random destination
farX = xStart + rand() % mob->idleRange; farX = xStart + Rand::rand(mob->idleRange);
farY = yStart + rand() % mob->idleRange; farY = yStart + Rand::rand(mob->idleRange);
distance = std::abs(std::max(farX - mob->x, farY - mob->y)); distance = std::abs(std::max(farX - mob->x, farY - mob->y));
if (distance == 0) if (distance == 0)

View File

@ -10,6 +10,7 @@
#include "Racing.hpp" #include "Racing.hpp"
#include "Vendors.hpp" #include "Vendors.hpp"
#include "Abilities.hpp" #include "Abilities.hpp"
#include "Rand.hpp"
#include <cmath> #include <cmath>
#include <algorithm> #include <algorithm>
@ -104,7 +105,7 @@ static void npcBarkHandler(CNSocket* sock, CNPacketData* data) {
INITSTRUCT(sP_FE2CL_REP_BARKER, resp); INITSTRUCT(sP_FE2CL_REP_BARKER, resp);
resp.iNPC_ID = req->iNPC_ID; resp.iNPC_ID = req->iNPC_ID;
resp.iMissionStringID = barks[rand() % barks.size()]; resp.iMissionStringID = barks[Rand::rand(barks.size())];
sock->sendPacket(resp, P_FE2CL_REP_BARKER); sock->sendPacket(resp, P_FE2CL_REP_BARKER);
} }

44
src/Rand.cpp Normal file
View File

@ -0,0 +1,44 @@
#include "Rand.hpp"
#include <chrono>
std::unique_ptr<std::mt19937> Rand::generator;
int32_t Rand::rand(int32_t startInclusive, int32_t endExclusive) {
std::uniform_int_distribution<int32_t> dist(startInclusive, endExclusive - 1);
return dist(*Rand::generator);
}
int32_t Rand::rand(int32_t endExclusive) {
return Rand::rand(0, endExclusive);
}
int32_t Rand::rand() {
return Rand::rand(0, INT32_MAX);
}
int32_t Rand::randWeighted(const std::vector<int32_t>& weights) {
std::discrete_distribution<int32_t> dist(weights.begin(), weights.end());
return dist(*Rand::generator);
}
float Rand::randFloat(float startInclusive, float endExclusive) {
std::uniform_real_distribution<float> dist(startInclusive, endExclusive);
return dist(*Rand::generator);
}
float Rand::randFloat(float endExclusive) {
std::uniform_real_distribution<float> dist(0.0f, endExclusive);
return dist(*Rand::generator);
}
float Rand::randFloat() {
std::uniform_real_distribution<float> dist(0.0f, 1.0f);
return dist(*Rand::generator);
}
void Rand::init() {
// modern equivalent of srand(time(0))
uint64_t seed = std::chrono::high_resolution_clock::now().time_since_epoch().count();
Rand::generator = std::make_unique<std::mt19937>(std::mt19937(seed));
}

20
src/Rand.hpp Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include <random>
#include <memory>
namespace Rand {
extern std::unique_ptr<std::mt19937> generator;
void init();
int32_t rand(int32_t startInclusive, int32_t endExclusive);
int32_t rand(int32_t endExclusive);
int32_t rand();
int32_t randWeighted(const std::vector<int32_t>& weights);
float randFloat(float startInclusive, float endExclusive);
float randFloat(float endExclusive);
float randFloat();
};

View File

@ -202,117 +202,156 @@ static void loadDrops() {
// read file into json // read file into json
inFile >> dropData; inFile >> dropData;
// MobDropChances // CrateDropChances
nlohmann::json mobDropChances = dropData["MobDropChances"]; nlohmann::json crateDropChances = dropData["CrateDropChances"];
for (nlohmann::json::iterator _dropChance = mobDropChances.begin(); _dropChance != mobDropChances.end(); _dropChance++) { for (nlohmann::json::iterator _crateDropChance = crateDropChances.begin(); _crateDropChance != crateDropChances.end(); _crateDropChance++) {
auto dropChance = _dropChance.value(); auto crateDropChance = _crateDropChance.value();
MobDropChance toAdd = {}; CrateDropChance toAdd = {};
toAdd.dropChance = (int)dropChance["DropChance"];
for (nlohmann::json::iterator _cratesRatio = dropChance["CratesRatio"].begin(); _cratesRatio != dropChance["CratesRatio"].end(); _cratesRatio++) { toAdd.dropChance = (int)crateDropChance["DropChance"];
toAdd.cratesRatio.push_back((int)_cratesRatio.value()); toAdd.dropChanceTotal = (int)crateDropChance["DropChanceTotal"];
nlohmann::json crateWeights = crateDropChance["CrateTypeDropWeights"];
for (nlohmann::json::iterator _crateWeight = crateWeights.begin(); _crateWeight != crateWeights.end(); _crateWeight++)
toAdd.crateWeights.push_back((int)_crateWeight.value());
Items::CrateDropChances[(int)crateDropChance["CrateDropChanceID"]] = toAdd;
} }
Items::MobDropChances[(int)dropChance["Type"]] = 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"]
};
} }
// MobDrops // MobDrops
nlohmann::json mobDrops = dropData["MobDrops"]; nlohmann::json mobDrops = dropData["MobDrops"];
for (nlohmann::json::iterator _drop = mobDrops.begin(); _drop != mobDrops.end(); _drop++) { for (nlohmann::json::iterator _mobDrop = mobDrops.begin(); _mobDrop != mobDrops.end(); _mobDrop++) {
auto drop = _drop.value(); auto mobDrop = _mobDrop.value();
MobDrop toAdd = {};
for (nlohmann::json::iterator _crates = drop["CrateIDs"].begin(); _crates != drop["CrateIDs"].end(); _crates++) { Items::MobDrops[(int)mobDrop["MobDropID"]] = {
toAdd.crateIDs.push_back((int)_crates.value()); (int)mobDrop["CrateDropChanceID"],
(int)mobDrop["CrateDropTypeID"],
(int)mobDrop["MiscDropChanceID"],
(int)mobDrop["MiscDropTypeID"],
};
} }
toAdd.dropChanceType = (int)drop["DropChance"]; // RarityWeights
// Check if DropChance exists nlohmann::json rarityWeights = dropData["RarityWeights"];
if (Items::MobDropChances.find(toAdd.dropChanceType) == Items::MobDropChances.end()) { for (nlohmann::json::iterator _rarityWeightsObject = rarityWeights.begin(); _rarityWeightsObject != rarityWeights.end(); _rarityWeightsObject++) {
throw TableException(" MobDropChance not found: " + std::to_string((toAdd.dropChanceType))); auto rarityWeightsObject = _rarityWeightsObject.value();
}
// Check if number of crates is correct
if (!(Items::MobDropChances[(int)drop["DropChance"]].cratesRatio.size() == toAdd.crateIDs.size())) {
throw TableException(" DropType " + std::to_string((int)drop["DropType"]) + " contains invalid number of crates");
}
toAdd.taros = (int)drop["Taros"];
toAdd.fm = (int)drop["FM"];
toAdd.boosts = (int)drop["Boosts"];
Items::MobDrops[(int)drop["DropType"]] = toAdd;
}
std::cout << "[INFO] Loaded " << Items::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; std::vector<int> toAdd;
for (nlohmann::json::iterator _ratio = rarity["Ratio"].begin(); _ratio != rarity["Ratio"].end(); _ratio++){
toAdd.push_back((int)_ratio.value()); 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;
} }
Items::RarityRatios[(int)rarity["Type"]] = toAdd;
// ItemSetTypes
nlohmann::json itemSetTypes = dropData["ItemSetTypes"];
for (nlohmann::json::iterator _itemSetType = itemSetTypes.begin(); _itemSetType != itemSetTypes.end(); _itemSetType++) {
auto itemSetType = _itemSetType.value();
ItemSetType toAdd = {};
toAdd.ignoreGender = (bool)itemSetType["IgnoreGender"];
nlohmann::json droppableItemIds = itemSetType["DroppableItemIDs"];
for (nlohmann::json::iterator _droppableItemId = droppableItemIds.begin(); _droppableItemId != droppableItemIds.end(); _droppableItemId++)
toAdd.droppableItemIds.push_back((int)_droppableItemId.value());
Items::ItemSetTypes[(int)itemSetType["ItemSetTypeID"]] = toAdd;
}
// ItemSetChances
nlohmann::json itemSetChances = dropData["ItemSetChances"];
for (nlohmann::json::iterator _itemSetChanceObject = itemSetChances.begin(); _itemSetChanceObject != itemSetChances.end(); _itemSetChanceObject++) {
auto itemSetChanceObject = _itemSetChanceObject.value();
ItemSetChance toAdd = {};
toAdd.defaultItemWeight = (int)itemSetChanceObject["DefaultItemWeight"];
nlohmann::json specialItemWeightIndices = itemSetChanceObject["SpecialItemWeightIndices"];
nlohmann::json specialItemWeightVector = itemSetChanceObject["SpecialItemWeights"];
for (nlohmann::json::iterator _specialItemWeightIndex = specialItemWeightIndices.begin(), _specialItemWeight = specialItemWeightVector.begin();
_specialItemWeightIndex != specialItemWeightIndices.end() && _specialItemWeight != specialItemWeightVector.end();
_specialItemWeightIndex++, _specialItemWeight++)
toAdd.specialItemWeights[(int)_specialItemWeightIndex.value()] = (int)_specialItemWeight.value();
Items::ItemSetChances[(int)itemSetChanceObject["ItemSetChanceID"]] = toAdd;
} }
// Crates // Crates
nlohmann::json crates = dropData["Crates"]; nlohmann::json crates = dropData["Crates"];
for (nlohmann::json::iterator _crate = crates.begin(); _crate != crates.end(); _crate++) { for (nlohmann::json::iterator _crate = crates.begin(); _crate != crates.end(); _crate++) {
auto crate = _crate.value(); auto crate = _crate.value();
Crate toAdd;
toAdd.rarityRatioId = (int)crate["RarityRatio"]; Items::Crates[(int)crate["CrateID"]] = {
for (nlohmann::json::iterator _itemSet = crate["ItemSets"].begin(); _itemSet != crate["ItemSets"].end(); _itemSet++) { (int)crate["ItemSetChanceID"],
toAdd.itemSets.push_back((int)_itemSet.value()); (int)crate["ItemSetTypeID"],
} (int)crate["RarityWeightID"]
Items::Crates[(int)crate["Id"]] = toAdd; };
} }
// Crate Items // DroppableItems
nlohmann::json items = dropData["Items"]; nlohmann::json droppableItems = dropData["DroppableItems"];
int itemCount = 0; for (nlohmann::json::iterator _droppableItem = droppableItems.begin(); _droppableItem != droppableItems.end(); _droppableItem++) {
for (nlohmann::json::iterator _item = items.begin(); _item != items.end(); _item++) { auto droppableItem = _droppableItem.value();
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 (Items::ItemData.find(itemDataKey) == Items::ItemData.end()) { Items::DroppableItems[(int)droppableItem["DroppableItemID"]] = {
char buff[255]; (int)droppableItem["ItemID"],
sprintf(buff, "Unknown item with Id %d and Type %d", (int)item["Id"], (int)item["Type"]); (int)droppableItem["Rarity"],
throw TableException(std::string(buff)); (int)droppableItem["Type"]
} };
std::map<std::pair<int32_t, int32_t>, Items::Item>::iterator toAdd = Items::ItemData.find(itemDataKey);
// if item collection doesn't exist, start a new one
if (Items::CrateItems.find(itemSetkey) == Items::CrateItems.end()) {
std::vector<std::map<std::pair<int32_t, int32_t>, Items::Item>::iterator> vector;
vector.push_back(toAdd);
Items::CrateItems[itemSetkey] = vector;
} else // else add a new element to existing collection
Items::CrateItems[itemSetkey].push_back(toAdd);
itemCount++;
} }
#ifdef ACADEMY #ifdef ACADEMY
// NanoCapsules
nlohmann::json capsules = dropData["NanoCapsules"]; nlohmann::json capsules = dropData["NanoCapsules"];
for (nlohmann::json::iterator _capsule = capsules.begin(); _capsule != capsules.end(); _capsule++) { for (nlohmann::json::iterator _capsule = capsules.begin(); _capsule != capsules.end(); _capsule++) {
auto capsule = _capsule.value(); auto capsule = _capsule.value();
Items::NanoCapsules[(int)capsule["Crate"]] = (int)capsule["Nano"]; Items::NanoCapsules[(int)capsule["CrateID"]] = (int)capsule["Nano"];
} }
#endif #endif
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::pair<int32_t, int32_t> item = std::make_pair((int)code["Id"], (int)code["Type"]);
if (Items::CodeItems.find(codeStr) == Items::CodeItems.end())
Items::CodeItems[codeStr] = std::vector<std::pair<int32_t, int32_t>>();
Items::CodeItems[codeStr].push_back(item);
}
std::cout << "[INFO] Loaded " << Items::Crates.size() << " Crates containing "
<< itemCount << " items" << std::endl;
// Racing rewards // Racing rewards
nlohmann::json racing = dropData["Racing"]; nlohmann::json racing = dropData["Racing"];
@ -359,6 +398,23 @@ static void loadDrops() {
std::cout << "[INFO] Loaded rewards for " << Racing::EPRewards.size() << " IZ races" << std::endl; std::cout << "[INFO] Loaded rewards for " << Racing::EPRewards.size() << " IZ races" << std::endl;
// 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;
nlohmann::json items = code["Items"];
for (nlohmann::json::iterator _item = items.begin(); _item != items.end(); _item++)
itemVector.push_back(std::make_pair((int)code["ItemID"], (int)code["Type"]));
Items::CodeItems[codeStr] = itemVector;
}
std::cout << "[INFO] Loaded " << Items::Crates.size() << " Crates containing "
<< Items::DroppableItems.size() << " unique items" << std::endl;
} }
catch (const std::exception& err) { catch (const std::exception& err) {
std::cerr << "[FATAL] Malformed drops.json file! Reason:" << err.what() << std::endl; std::cerr << "[FATAL] Malformed drops.json file! Reason:" << err.what() << std::endl;

View File

@ -1,4 +1,5 @@
#include "Vendors.hpp" #include "Vendors.hpp"
#include "Rand.hpp"
using namespace Vendors; using namespace Vendors;
@ -328,7 +329,7 @@ static void vendorCombineItems(CNSocket* sock, CNPacketData* data) {
break; break;
} }
float rolled = (rand() * 1.0f / RAND_MAX) * 100.0f; // success chance out of 100 float rolled = Rand::randFloat(100.0f); // success chance out of 100
//std::cout << rolled << " vs " << successChance << std::endl; //std::cout << rolled << " vs " << successChance << std::endl;
plr->money -= cost; plr->money -= cost;

View File

@ -22,6 +22,7 @@
#include "Vendors.hpp" #include "Vendors.hpp"
#include "Chat.hpp" #include "Chat.hpp"
#include "Eggs.hpp" #include "Eggs.hpp"
#include "Rand.hpp"
#include "settings.hpp" #include "settings.hpp"
@ -93,7 +94,7 @@ int main() {
#else #else
initsignals(); initsignals();
#endif #endif
srand(getTime()); Rand::init();
settings::init(); settings::init();
std::cout << "[INFO] OpenFusion v" GIT_VERSION << std::endl; std::cout << "[INFO] OpenFusion v" GIT_VERSION << std::endl;
std::cout << "[INFO] Protocol version: " << PROTOCOL_VERSION << std::endl; std::cout << "[INFO] Protocol version: " << PROTOCOL_VERSION << std::endl;