mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-12-22 19:30:05 +00:00
added better drop handling, parsing, rng
This commit is contained in:
parent
aa028392f0
commit
78b17aea72
2
Makefile
2
Makefile
@ -73,6 +73,7 @@ CXXSRC=\
|
||||
src/Racing.cpp\
|
||||
src/Vendors.cpp\
|
||||
src/Trading.cpp\
|
||||
src/Rand.cpp\
|
||||
|
||||
# headers (for timestamp purposes)
|
||||
CXXHDR=\
|
||||
@ -117,6 +118,7 @@ CXXHDR=\
|
||||
src/Racing.hpp\
|
||||
src/Vendors.hpp\
|
||||
src/Trading.hpp\
|
||||
src/Rand.hpp\
|
||||
|
||||
COBJ=$(CSRC:.c=.o)
|
||||
CXXOBJ=$(CXXSRC:.cpp=.o)
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "Items.hpp"
|
||||
#include "Missions.hpp"
|
||||
#include "Nanos.hpp"
|
||||
#include "Rand.hpp"
|
||||
|
||||
// helper function, not a packet handler
|
||||
void BuiltinCommands::setSpecialState(CNSocket* sock, CNPacketData* data) {
|
||||
@ -271,8 +272,8 @@ static void teleportPlayer(CNSocket *sock, CNPacketData *data) {
|
||||
case eCN_GM_TeleportMapType__Unstick:
|
||||
targetPlr = PlayerManager::getPlayer(targetSock);
|
||||
|
||||
PlayerManager::sendPlayerTo(targetSock, targetPlr->x - unstickRange/2 + rand() % unstickRange,
|
||||
targetPlr->y - unstickRange/2 + rand() % unstickRange, targetPlr->z + 80);
|
||||
PlayerManager::sendPlayerTo(targetSock, targetPlr->x - unstickRange/2 + Rand::rand(unstickRange),
|
||||
targetPlr->y - unstickRange/2 + Rand::rand(unstickRange), targetPlr->z + 80);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "Transport.hpp"
|
||||
#include "Racing.hpp"
|
||||
#include "Abilities.hpp"
|
||||
#include "Rand.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
@ -26,7 +27,7 @@ static std::pair<int,int> getDamage(int attackPower, int defensePower, bool shou
|
||||
// base calculation
|
||||
int damage = attackPower * attackPower / (attackPower + defensePower);
|
||||
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
|
||||
if (attackerStyle != -1 && defenderStyle != -1 && attackerStyle != defenderStyle) {
|
||||
@ -34,7 +35,7 @@ static std::pair<int,int> getDamage(int attackPower, int defensePower, bool shou
|
||||
defenderStyle += 3;
|
||||
if (defenderStyle - attackerStyle == 2)
|
||||
defenderStyle -= 3;
|
||||
if (attackerStyle < defenderStyle)
|
||||
if (attackerStyle < defenderStyle)
|
||||
damage = damage * 5 / 4;
|
||||
else
|
||||
damage = damage * 4 / 5;
|
||||
@ -47,7 +48,7 @@ static std::pair<int,int> getDamage(int attackPower, int defensePower, bool shou
|
||||
ret.first = damage;
|
||||
ret.second = 1;
|
||||
|
||||
if (shouldCrit && rand() % 20 == 0) {
|
||||
if (shouldCrit && Rand::rand(20) == 0) {
|
||||
ret.first *= 2; // critical hit
|
||||
ret.second = 2;
|
||||
}
|
||||
@ -117,7 +118,7 @@ static void pcAttackNpcs(CNSocket *sock, CNPacketData *data) {
|
||||
int difficulty = (int)mob->data["m_iNpcLevel"];
|
||||
damage = getDamage(damage.first, (int)mob->data["m_iProtection"], true, (plr->batteryW > 6 + difficulty),
|
||||
Nanos::nanoStyle(plr->activeNano), (int)mob->data["m_iNpcStyle"], difficulty);
|
||||
|
||||
|
||||
if (plr->batteryW >= 6 + difficulty)
|
||||
plr->batteryW -= 6 + difficulty;
|
||||
else
|
||||
@ -224,12 +225,12 @@ void Combat::killMob(CNSocket *sock, Mob *mob) {
|
||||
if (sock != nullptr) {
|
||||
Player* plr = PlayerManager::getPlayer(sock);
|
||||
|
||||
int rolledBoosts = rand();
|
||||
int rolledPotions = rand();
|
||||
int rolledCrate = rand();
|
||||
int rolledCrateType = rand();
|
||||
int rolledEvent = rand();
|
||||
int rolledQItem = rand();
|
||||
int rolledBoosts = Rand::rand();
|
||||
int rolledPotions = Rand::rand();
|
||||
int rolledCrate = Rand::rand();
|
||||
int rolledCrateType = Rand::rand();
|
||||
int rolledEvent = Rand::rand();
|
||||
int rolledQItem = Rand::rand();
|
||||
|
||||
if (plr->groupCnt == 1 && plr->iIDGroup == plr->iID) {
|
||||
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
|
||||
toAdd.weaponBoost = plr->batteryW > 0;
|
||||
if (toAdd.weaponBoost) {
|
||||
int boostCost = rand() % 11 + 20;
|
||||
int boostCost = Rand::rand(11) + 20;
|
||||
plr->batteryW = boostCost > plr->batteryW ? 0 : plr->batteryW - boostCost;
|
||||
}
|
||||
|
||||
@ -634,7 +635,7 @@ static void projectileHit(CNSocket* sock, CNPacketData* data) {
|
||||
* initialize response struct
|
||||
* rocket style hit doesn't work properly, so we're always sending this one
|
||||
*/
|
||||
|
||||
|
||||
size_t resplen = sizeof(sP_FE2CL_PC_GRENADE_STYLE_HIT) + pkt->iTargetCnt * sizeof(sAttackResult);
|
||||
uint8_t respbuf[CN_PACKET_BUFFER_SIZE];
|
||||
|
||||
@ -655,7 +656,7 @@ static void projectileHit(CNSocket* sock, CNPacketData* data) {
|
||||
// not sure how to best handle this
|
||||
std::cout << "[WARN] projectileHit: NPC ID not found" << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
BaseNPC* npc = NPCManager::NPCs[pktdata[i]];
|
||||
if (npc->type != EntityType::MOB) {
|
||||
@ -684,7 +685,7 @@ static void projectileHit(CNSocket* sock, CNPacketData* data) {
|
||||
resp->Bullet.iID = bullet->bulletType;
|
||||
sock->sendPacket((void*)respbuf, P_FE2CL_PC_GRENADE_STYLE_HIT, resplen);
|
||||
PlayerManager::sendToViewable(sock, (void*)respbuf, P_FE2CL_PC_GRENADE_STYLE_HIT, resplen);
|
||||
|
||||
|
||||
Bullets[plr->iID].erase(resp->iBulletID);
|
||||
}
|
||||
|
||||
|
307
src/Items.cpp
307
src/Items.cpp
@ -7,6 +7,7 @@
|
||||
#include "Abilities.hpp"
|
||||
#include "Missions.hpp"
|
||||
#include "Eggs.hpp"
|
||||
#include "Rand.hpp"
|
||||
|
||||
#include <string.h> // for memset()
|
||||
#include <assert.h>
|
||||
@ -15,14 +16,18 @@ using namespace Items;
|
||||
|
||||
std::map<std::pair<int32_t, int32_t>, Items::Item> Items::ItemData;
|
||||
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;
|
||||
// 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>, Items::Item>::iterator>> Items::CrateItems;
|
||||
std::map<int32_t, DroppableItem> Items::DroppableItems;
|
||||
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, ItemSetType> Items::ItemSetTypes;
|
||||
std::map<int32_t, ItemSetChance> Items::ItemSetChances;
|
||||
|
||||
#ifdef ACADEMY
|
||||
std::map<int32_t, int32_t> Items::NanoCapsules; // crate id -> nano id
|
||||
@ -58,7 +63,7 @@ static void nanoCapsuleHandler(CNSocket* sock, int slot, sItemBase *chest) {
|
||||
|
||||
item->iSlotNum = slot;
|
||||
item->eIL = 1;
|
||||
|
||||
|
||||
// update player serverside
|
||||
plr->Inven[slot] = item->sItem;
|
||||
|
||||
@ -81,14 +86,29 @@ static void nanoCapsuleHandler(CNSocket* sock, int slot, sItemBase *chest) {
|
||||
}
|
||||
#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
|
||||
if (RarityRatios.find(crate.rarityRatioId) == RarityRatios.end()) {
|
||||
std::cout << "[WARN] Rarity Ratio " << crate.rarityRatioId << " not found!" << std::endl;
|
||||
if (Items::RarityWeights.find(crate.rarityWeightId) == Items::RarityWeights.end()) {
|
||||
std::cout << "[WARN] Rarity Weight " << crate.rarityWeightId << " not found!" << std::endl;
|
||||
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,
|
||||
@ -97,74 +117,137 @@ static int getRarity(Crate& crate, int itemSetId) {
|
||||
*/
|
||||
|
||||
// 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;
|
||||
std::set<int> rarityIndices;
|
||||
for (int droppableItemId : Items::ItemSetTypes[itemSetTypeId].droppableItemIds) {
|
||||
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;
|
||||
for (int value : rarityRatio)
|
||||
total += value;
|
||||
|
||||
if (total == 0) {
|
||||
std::cout << "Item Set " << itemSetId << " has no items assigned?!" << std::endl;
|
||||
if (rarityIndices.empty()) {
|
||||
std::cout << "[WARN] Item Set " << crate.itemSetTypeId << " has no valid items assigned?!" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// now return a random rarity number
|
||||
int randomNum = rand() % total;
|
||||
int rarity = 0;
|
||||
int sum = 0;
|
||||
do {
|
||||
sum += rarityRatio[rarity];
|
||||
rarity++;
|
||||
} while (sum <= randomNum);
|
||||
std::vector<int> relevantWeights;
|
||||
for (int index : rarityIndices)
|
||||
relevantWeights.push_back(rarityWeights[index]);
|
||||
|
||||
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) {
|
||||
auto key = std::make_pair(itemSetId, rarity);
|
||||
static int getCrateItem(sItemBase* result, int itemSetTypeId, int itemSetChanceId, int rarity, int playerGender) {
|
||||
// (int, vector<int>)
|
||||
auto& [ignoreGender, droppableItemIds] = Items::ItemSetTypes[itemSetTypeId];
|
||||
|
||||
if (CrateItems.find(key) == CrateItems.end()) {
|
||||
std::cout << "[WARN] Item Set ID " << itemSetId << " Rarity " << rarity << " does not exist" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
// collect valid items that match the rarity and (if not ignored) gender
|
||||
std::vector<std::pair<int, DroppableItem*>> validItems;
|
||||
for (int i = 0; i < droppableItemIds.size(); i++) {
|
||||
int droppableItemId = droppableItemIds[i];
|
||||
|
||||
// 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)
|
||||
if (Items::DroppableItems.find(droppableItemId) == Items::DroppableItems.end()) {
|
||||
std::cout << "[WARN] Droppable item " << droppableItemId << " was not found, skipping..." << std::endl;
|
||||
continue;
|
||||
items.push_back(crateitem);
|
||||
}
|
||||
|
||||
DroppableItem* droppableItem = &Items::DroppableItems[droppableItemId];
|
||||
|
||||
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 (items.size() == 0) {
|
||||
std::cout << "[WARN] Set ID " << itemSetId << " Rarity " << rarity << " contains no valid items" << std::endl;
|
||||
if (validItems.empty()) {
|
||||
std::cout << "[WARN] Set ID " << itemSetTypeId << " Chance ID " << itemSetChanceId
|
||||
<< " Rarity " << rarity << " contains no valid items" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto item = items[rand() % items.size()];
|
||||
// (int, map<int, int>)
|
||||
auto& [defaultWeight, specialWeights] = Items::ItemSetChances[itemSetChanceId];
|
||||
|
||||
result.iID = item->first.first;
|
||||
result.iType = item->first.second;
|
||||
result.iOpt = 1;
|
||||
// initialize all weights as the default weight for all item slots
|
||||
std::vector<int> itemWeights(validItems.size(), defaultWeight);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int getItemSetId(Crate& crate, int crateId) {
|
||||
int itemSetsCount = crate.itemSets.size();
|
||||
if (itemSetsCount == 0) {
|
||||
std::cout << "[WARN] Crate " << crateId << " has no item sets assigned?!" << std::endl;
|
||||
static int getValidCrateId(int crateId) {
|
||||
// find the crate
|
||||
if (Items::Crates.find(crateId) == Items::Crates.end()) {
|
||||
std::cout << "[WARN] Crate " << crateId << " not found!" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// if crate points to multiple itemSets, choose a random one
|
||||
int itemSetIndex = rand() % itemSetsCount;
|
||||
return crate.itemSets[itemSetIndex];
|
||||
return crateId;
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -486,36 +569,34 @@ static void chestOpenHandler(CNSocket *sock, CNPacketData *data) {
|
||||
item->iSlotNum = pkt->iSlotNum;
|
||||
item->eIL = 1;
|
||||
|
||||
int itemSetId = -1, rarity = -1, ret = -1;
|
||||
bool failing = false;
|
||||
int validItemSetTypeId = -1, validItemSetChanceId = -1, rarity = -1, ret = -1;
|
||||
|
||||
// find the crate
|
||||
if (Crates.find(chest->iID) == Crates.end()) {
|
||||
std::cout << "[WARN] Crate " << chest->iID << " not found!" << std::endl;
|
||||
failing = true;
|
||||
}
|
||||
Crate& crate = Crates[chest->iID];
|
||||
int validCrateId = getValidCrateId(chest->iID);
|
||||
bool failing = (validCrateId == -1);
|
||||
|
||||
if (!failing)
|
||||
itemSetId = getItemSetId(crate, chest->iID);
|
||||
if (itemSetId == -1)
|
||||
failing = true;
|
||||
validItemSetTypeId = getValidItemSetTypeId(validCrateId);
|
||||
failing = (validItemSetTypeId == -1);
|
||||
|
||||
if (!failing)
|
||||
rarity = getRarity(crate, itemSetId);
|
||||
if (rarity == -1)
|
||||
failing = true;
|
||||
validItemSetChanceId = getValidItemSetChanceId(validCrateId);
|
||||
failing = (validItemSetChanceId == -1);
|
||||
|
||||
if (!failing)
|
||||
ret = getCrateItem(item->sItem, itemSetId, rarity, plr->PCStyle.iGender);
|
||||
if (ret == -1)
|
||||
failing = true;
|
||||
rarity = getRarity(validCrateId, validItemSetTypeId);
|
||||
failing = (rarity == -1);
|
||||
|
||||
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 (failing) {
|
||||
item->sItem.iType = 7;
|
||||
item->sItem.iID = 119 + (rand() % 3);
|
||||
item->sItem.iID = 119 + Rand::rand(3);
|
||||
item->sItem.iOpt = 1;
|
||||
|
||||
std::cout << "[WARN] Crate open failed, giving a Gumball..." << std::endl;
|
||||
}
|
||||
// update player
|
||||
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->iOpt = 1;
|
||||
|
||||
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);
|
||||
reward->iID = crateIds[chosenIndex];
|
||||
}
|
||||
|
||||
static void giveEventDrop(CNSocket* sock, Player* player, int rolled) {
|
||||
// random drop chance
|
||||
if (rand() % 100 > settings::EVENTCRATECHANCE)
|
||||
if (Rand::rand(100) > settings::EVENTCRATECHANCE)
|
||||
return;
|
||||
|
||||
// no slot = no reward
|
||||
@ -715,18 +784,47 @@ void Items::giveMobDrop(CNSocket *sock, Mob* mob, int rolledBoosts, int rolledPo
|
||||
// find correct mob drop
|
||||
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
|
||||
if (plr->iConditionBitFlag & CSB_BIT_REWARD_CASH) {
|
||||
int boost = 0;
|
||||
if (Nanos::getNanoBoost(plr)) // for gumballs
|
||||
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
|
||||
// TODO: adjust this better
|
||||
int levelDifference = plr->level - mob->level;
|
||||
int fm = drop.fm;
|
||||
int fm = miscDropType.fmAmount;
|
||||
if (levelDifference > 0)
|
||||
fm = levelDifference < 10 ? fm - (levelDifference * fm / 10) : 0;
|
||||
// scavenger nano boost
|
||||
@ -739,13 +837,11 @@ void Items::giveMobDrop(CNSocket *sock, Mob* mob, int rolledBoosts, int rolledPo
|
||||
|
||||
Missions::updateFusionMatter(sock, fm);
|
||||
|
||||
// give boosts 1 in 3 times
|
||||
if (drop.boosts > 0) {
|
||||
if (rolledPotions % 3 == 0)
|
||||
plr->batteryN += drop.boosts;
|
||||
if (rolledBoosts % 3 == 0)
|
||||
plr->batteryW += drop.boosts;
|
||||
}
|
||||
if (rolledPotions % miscDropChance.potionDropChanceTotal < miscDropChance.potionDropChance)
|
||||
plr->batteryN += miscDropType.potionAmount;
|
||||
if (rolledBoosts % miscDropChance.boostDropChanceTotal < miscDropChance.boostDropChance)
|
||||
plr->batteryW += miscDropType.boostAmount;
|
||||
|
||||
// caps
|
||||
if (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);
|
||||
|
||||
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
|
||||
if (slot == -1 || !awardDrop) {
|
||||
if (slot == -1 || rolledCrate % crateDropChance.dropChanceTotal >= crateDropChance.dropChance) {
|
||||
// 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
|
||||
getMobDrop(&item->sItem, &drop, chance, rolledCrateType);
|
||||
getMobDrop(&item->sItem, crateDropChance.crateWeights, crateDropType, rolledCrateType);
|
||||
item->iSlotNum = slot;
|
||||
item->eIL = 1; // Inventory Location. 1 means player inventory.
|
||||
|
||||
|
@ -10,21 +10,51 @@ struct CrocPotEntry {
|
||||
};
|
||||
|
||||
struct Crate {
|
||||
int rarityRatioId;
|
||||
std::vector<int> itemSets;
|
||||
int itemSetChanceId;
|
||||
int itemSetTypeId;
|
||||
int rarityWeightId;
|
||||
};
|
||||
|
||||
struct MobDropChance {
|
||||
int dropChance;
|
||||
std::vector<int> cratesRatio;
|
||||
struct CrateDropChance {
|
||||
int dropChance, dropChanceTotal;
|
||||
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 {
|
||||
std::vector<int> crateIDs;
|
||||
int dropChanceType;
|
||||
int taros;
|
||||
int fm;
|
||||
int boosts;
|
||||
int crateDropChanceId;
|
||||
int crateDropTypeId;
|
||||
int miscDropChanceId;
|
||||
int miscDropTypeId;
|
||||
};
|
||||
|
||||
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 {
|
||||
@ -44,16 +74,19 @@ namespace Items {
|
||||
// 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<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;
|
||||
// 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;
|
||||
extern std::map<int32_t, DroppableItem> DroppableItems;
|
||||
extern std::map<std::string, std::vector<std::pair<int32_t, int32_t>>> CodeItems; // code -> vector of <id, type>
|
||||
|
||||
// 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, ItemSetType> ItemSetTypes;
|
||||
extern std::map<int32_t, ItemSetChance> ItemSetChances;
|
||||
|
||||
void init();
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "Nanos.hpp"
|
||||
#include "Combat.hpp"
|
||||
#include "Abilities.hpp"
|
||||
#include "Rand.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <limits.h>
|
||||
@ -334,7 +335,7 @@ static void useAbilities(Mob *mob, time_t currTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
int random = rand() % 2000 * 1000;
|
||||
int random = Rand::rand(2000) * 1000;
|
||||
int prob1 = (int)mob->data["m_iActiveSkill1Prob"]; // active skill probability
|
||||
int prob2 = (int)mob->data["m_iCorruptionTypeProb"]; // corruption 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)
|
||||
mob->skillStyle = 2;
|
||||
if (mob->skillStyle == -2)
|
||||
mob->skillStyle = rand() % 3;
|
||||
mob->skillStyle = Rand::rand(3);
|
||||
pkt.iStyle = mob->skillStyle;
|
||||
NPCManager::sendToViewable(mob, &pkt, P_FE2CL_NPC_SKILL_CORRUPTION_READY, sizeof(sP_FE2CL_NPC_SKILL_CORRUPTION_READY));
|
||||
mob->nextAttack = currTime + 1800;
|
||||
@ -546,7 +547,7 @@ static void combatStep(Mob *mob, time_t currTime) {
|
||||
pkt1.iID = mob->appearanceData.iNPC_ID;
|
||||
pkt1.iConditionBitFlag = mob->appearanceData.iConditionBitFlag;
|
||||
NPCManager::sendToViewable(mob, &pkt1, P_FE2CL_CHAR_TIME_BUFF_TIME_OUT, sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT));
|
||||
|
||||
|
||||
it = mob->unbuffTimes.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
@ -563,7 +564,7 @@ static void combatStep(Mob *mob, time_t currTime) {
|
||||
int mobRange = (int)mob->data["m_iAtkRange"] + (int)mob->data["m_iRadius"];
|
||||
|
||||
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);
|
||||
if (mob->target == nullptr)
|
||||
return;
|
||||
@ -610,7 +611,7 @@ static void combatStep(Mob *mob, time_t currTime) {
|
||||
NPCManager::sendToViewable(mob, &pkt, P_FE2CL_NPC_MOVE, sizeof(sP_FE2CL_NPC_MOVE));
|
||||
}
|
||||
|
||||
/* attack logic
|
||||
/* attack logic
|
||||
* 2/5 represents 400 ms which is the time interval mobs use per movement logic step
|
||||
* if the mob is one move interval away, we should just start attacking anyways.
|
||||
*/
|
||||
@ -638,7 +639,7 @@ void MobAI::incNextMovement(Mob *mob, time_t currTime) {
|
||||
currTime = getTime();
|
||||
|
||||
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) {
|
||||
@ -681,8 +682,8 @@ static void roamingStep(Mob *mob, time_t currTime) {
|
||||
int minDistance = mob->idleRange / 2;
|
||||
|
||||
// pick a random destination
|
||||
farX = xStart + rand() % mob->idleRange;
|
||||
farY = yStart + rand() % mob->idleRange;
|
||||
farX = xStart + Rand::rand(mob->idleRange);
|
||||
farY = yStart + Rand::rand(mob->idleRange);
|
||||
|
||||
distance = std::abs(std::max(farX - mob->x, farY - mob->y));
|
||||
if (distance == 0)
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "Racing.hpp"
|
||||
#include "Vendors.hpp"
|
||||
#include "Abilities.hpp"
|
||||
#include "Rand.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
@ -104,7 +105,7 @@ static void npcBarkHandler(CNSocket* sock, CNPacketData* data) {
|
||||
|
||||
INITSTRUCT(sP_FE2CL_REP_BARKER, resp);
|
||||
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);
|
||||
}
|
||||
|
||||
|
44
src/Rand.cpp
Normal file
44
src/Rand.cpp
Normal 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
20
src/Rand.hpp
Normal 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();
|
||||
};
|
@ -202,117 +202,156 @@ static void loadDrops() {
|
||||
// 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());
|
||||
}
|
||||
Items::MobDropChances[(int)dropChance["Type"]] = toAdd;
|
||||
// CrateDropChances
|
||||
nlohmann::json crateDropChances = dropData["CrateDropChances"];
|
||||
for (nlohmann::json::iterator _crateDropChance = crateDropChances.begin(); _crateDropChance != crateDropChances.end(); _crateDropChance++) {
|
||||
auto crateDropChance = _crateDropChance.value();
|
||||
CrateDropChance toAdd = {};
|
||||
|
||||
toAdd.dropChance = (int)crateDropChance["DropChance"];
|
||||
toAdd.dropChanceTotal = (int)crateDropChance["DropChanceTotal"];
|
||||
|
||||
nlohmann::json crateWeights = crateDropChance["CrateTypeDropWeights"];
|
||||
for (nlohmann::json::iterator _crateWeight = crateWeights.begin(); _crateWeight != crateWeights.end(); _crateWeight++)
|
||||
toAdd.crateWeights.push_back((int)_crateWeight.value());
|
||||
|
||||
Items::CrateDropChances[(int)crateDropChance["CrateDropChanceID"]] = toAdd;
|
||||
}
|
||||
|
||||
// CrateDropTypes
|
||||
nlohmann::json crateDropTypes = dropData["CrateDropTypes"];
|
||||
for (nlohmann::json::iterator _crateDropType = crateDropTypes.begin(); _crateDropType != crateDropTypes.end(); _crateDropType++) {
|
||||
auto crateDropType = _crateDropType.value();
|
||||
std::vector<int> toAdd;
|
||||
|
||||
nlohmann::json crateIds = crateDropType["CrateIDs"];
|
||||
for (nlohmann::json::iterator _crateId = crateIds.begin(); _crateId != crateIds.end(); _crateId++)
|
||||
toAdd.push_back((int)_crateId.value());
|
||||
|
||||
Items::CrateDropTypes[(int)crateDropType["CrateDropTypeID"]] = toAdd;
|
||||
}
|
||||
|
||||
// MiscDropChances
|
||||
nlohmann::json miscDropChances = dropData["MiscDropChances"];
|
||||
for (nlohmann::json::iterator _miscDropChance = miscDropChances.begin(); _miscDropChance != miscDropChances.end(); _miscDropChance++) {
|
||||
auto miscDropChance = _miscDropChance.value();
|
||||
|
||||
Items::MiscDropChances[(int)miscDropChance["MiscDropChanceID"]] = {
|
||||
(int)miscDropChance["PotionDropChance"],
|
||||
(int)miscDropChance["PotionDropChanceTotal"],
|
||||
(int)miscDropChance["BoostDropChance"],
|
||||
(int)miscDropChance["BoostDropChanceTotal"],
|
||||
(int)miscDropChance["TaroDropChance"],
|
||||
(int)miscDropChance["TaroDropChanceTotal"],
|
||||
(int)miscDropChance["FMDropChance"],
|
||||
(int)miscDropChance["FMDropChanceTotal"]
|
||||
};
|
||||
}
|
||||
|
||||
// MiscDropTypes
|
||||
nlohmann::json miscDropTypes = dropData["MiscDropTypes"];
|
||||
for (nlohmann::json::iterator _miscDropType = miscDropTypes.begin(); _miscDropType != miscDropTypes.end(); _miscDropType++) {
|
||||
auto miscDropType = _miscDropType.value();
|
||||
|
||||
Items::MiscDropTypes[(int)miscDropType["MiscDropTypeID"]] = {
|
||||
(int)miscDropType["PotionAmount"],
|
||||
(int)miscDropType["BoostAmount"],
|
||||
(int)miscDropType["TaroAmount"],
|
||||
(int)miscDropType["FMAmount"]
|
||||
};
|
||||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
for (nlohmann::json::iterator _mobDrop = mobDrops.begin(); _mobDrop != mobDrops.end(); _mobDrop++) {
|
||||
auto mobDrop = _mobDrop.value();
|
||||
|
||||
toAdd.dropChanceType = (int)drop["DropChance"];
|
||||
// Check if DropChance exists
|
||||
if (Items::MobDropChances.find(toAdd.dropChanceType) == Items::MobDropChances.end()) {
|
||||
throw TableException(" MobDropChance not found: " + std::to_string((toAdd.dropChanceType)));
|
||||
}
|
||||
// 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;
|
||||
Items::MobDrops[(int)mobDrop["MobDropID"]] = {
|
||||
(int)mobDrop["CrateDropChanceID"],
|
||||
(int)mobDrop["CrateDropTypeID"],
|
||||
(int)mobDrop["MiscDropChanceID"],
|
||||
(int)mobDrop["MiscDropTypeID"],
|
||||
};
|
||||
}
|
||||
|
||||
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();
|
||||
// RarityWeights
|
||||
nlohmann::json rarityWeights = dropData["RarityWeights"];
|
||||
for (nlohmann::json::iterator _rarityWeightsObject = rarityWeights.begin(); _rarityWeightsObject != rarityWeights.end(); _rarityWeightsObject++) {
|
||||
auto rarityWeightsObject = _rarityWeightsObject.value();
|
||||
std::vector<int> toAdd;
|
||||
for (nlohmann::json::iterator _ratio = rarity["Ratio"].begin(); _ratio != rarity["Ratio"].end(); _ratio++){
|
||||
toAdd.push_back((int)_ratio.value());
|
||||
}
|
||||
Items::RarityRatios[(int)rarity["Type"]] = toAdd;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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
|
||||
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());
|
||||
}
|
||||
Items::Crates[(int)crate["Id"]] = toAdd;
|
||||
|
||||
Items::Crates[(int)crate["CrateID"]] = {
|
||||
(int)crate["ItemSetChanceID"],
|
||||
(int)crate["ItemSetTypeID"],
|
||||
(int)crate["RarityWeightID"]
|
||||
};
|
||||
}
|
||||
|
||||
// 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"]);
|
||||
// DroppableItems
|
||||
nlohmann::json droppableItems = dropData["DroppableItems"];
|
||||
for (nlohmann::json::iterator _droppableItem = droppableItems.begin(); _droppableItem != droppableItems.end(); _droppableItem++) {
|
||||
auto droppableItem = _droppableItem.value();
|
||||
|
||||
if (Items::ItemData.find(itemDataKey) == Items::ItemData.end()) {
|
||||
char buff[255];
|
||||
sprintf(buff, "Unknown item with Id %d and Type %d", (int)item["Id"], (int)item["Type"]);
|
||||
throw TableException(std::string(buff));
|
||||
}
|
||||
|
||||
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++;
|
||||
Items::DroppableItems[(int)droppableItem["DroppableItemID"]] = {
|
||||
(int)droppableItem["ItemID"],
|
||||
(int)droppableItem["Rarity"],
|
||||
(int)droppableItem["Type"]
|
||||
};
|
||||
}
|
||||
|
||||
#ifdef ACADEMY
|
||||
// NanoCapsules
|
||||
nlohmann::json capsules = dropData["NanoCapsules"];
|
||||
|
||||
for (nlohmann::json::iterator _capsule = capsules.begin(); _capsule != capsules.end(); _capsule++) {
|
||||
auto capsule = _capsule.value();
|
||||
Items::NanoCapsules[(int)capsule["Crate"]] = (int)capsule["Nano"];
|
||||
Items::NanoCapsules[(int)capsule["CrateID"]] = (int)capsule["Nano"];
|
||||
}
|
||||
#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
|
||||
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;
|
||||
|
||||
// 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) {
|
||||
std::cerr << "[FATAL] Malformed drops.json file! Reason:" << err.what() << std::endl;
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "Vendors.hpp"
|
||||
#include "Rand.hpp"
|
||||
|
||||
using namespace Vendors;
|
||||
|
||||
@ -328,7 +329,7 @@ static void vendorCombineItems(CNSocket* sock, CNPacketData* data) {
|
||||
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;
|
||||
plr->money -= cost;
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "Vendors.hpp"
|
||||
#include "Chat.hpp"
|
||||
#include "Eggs.hpp"
|
||||
#include "Rand.hpp"
|
||||
|
||||
#include "settings.hpp"
|
||||
|
||||
@ -56,7 +57,7 @@ void terminate(int arg) {
|
||||
|
||||
if (shardServer != nullptr && shardThread != nullptr)
|
||||
shardServer->kill();
|
||||
|
||||
|
||||
Database::close();
|
||||
exit(0);
|
||||
}
|
||||
@ -93,7 +94,7 @@ int main() {
|
||||
#else
|
||||
initsignals();
|
||||
#endif
|
||||
srand(getTime());
|
||||
Rand::init();
|
||||
settings::init();
|
||||
std::cout << "[INFO] OpenFusion v" GIT_VERSION << std::endl;
|
||||
std::cout << "[INFO] Protocol version: " << PROTOCOL_VERSION << std::endl;
|
||||
|
Loading…
Reference in New Issue
Block a user