gdle/Source/TreasureFactory.cpp
Morosity 320e3b7967 Void casters in loot
Spirit Drinker/Thirst update
2020-04-29 04:05:02 +00:00

2804 lines
121 KiB
C++

#include <StdAfx.h>
#include "TreasureFactory.h"
#include "WeenieObject.h"
#include "WeenieFactory.h"
#include "InferredPortalData.h"
#include "RandomRange.h"
#include "World.h"
#include "Container.h"
#include "Config.h"
#include "Monster.h"
#include "Player.h"
#include "easylogging++.h"
#include "ClientCommands.h"
#include <chrono>
double round(double value, int decimalPlaces)
{
double multiplier = pow(10, decimalPlaces);
double converted = (value + (0.05 / multiplier)) * multiplier;
double returnValue = converted / multiplier;
return returnValue;
}
bool HasField(const nlohmann::json &reader, std::string name)
{
if (reader.find(name) != reader.end())
return true;
else
return false;
}
void from_json(const nlohmann::json &reader, CSpellProperties &output)
{
if (HasField(reader, "spellPower"))
output.spellPower = reader.at("spellPower").get<std::vector<int>>();
if (HasField(reader, "spellMana"))
output.spellMana = reader.at("spellMana").get<std::vector<int>>();
if (HasField(reader, "cantripPower"))
output.cantripPower = reader.at("cantripPower").get<std::vector<int>>();
if (HasField(reader, "cantripMana"))
output.cantripMana = reader.at("cantripMana").get<std::vector<int>>();
}
void from_json(const nlohmann::json &reader, CMaterialProperties &output)
{
output.valueMultiplier = reader.value("valueMultiplier", 0.0f);
if (HasField(reader, "palettes"))
output.palettes = reader.at("palettes").get<std::vector<int>>();
output.gemValue = reader.value("gemValue", 0);
if (HasField(reader, "gemName"))
output.gemName = reader.at("gemName").get<std::string>();
if (HasField(reader, "gemPluralName"))
output.gemPluralName = reader.at("gemPluralName").get<std::string>();
}
void from_json(const nlohmann::json &reader, CWorkmanshipProperties &output)
{
output.valueMultiplier = reader.value("valueMultiplier", 0.0f);
output.gemChance = reader.value("gemChance", 0.0f);
output.maxGemCount = reader.value("maxGemCount", 0);
}
void from_json(const nlohmann::json &reader, CItemTreasureProfileEntry &output)
{
if (HasField(reader, "name"))
output.name = reader.at("name").get<std::string>();
output.wcid = reader.value("wcid", 0);
output.maxAmount = reader.value("maxAmount", 0);
if (HasField(reader, "possibleMaterials"))
{
std::vector<std::string> tempMaterials;
tempMaterials = reader.at("possibleMaterials").get<std::vector<std::string>>();
for (auto iter = tempMaterials.begin(); iter != tempMaterials.end(); ++iter)
output.possibleMaterials.push_back(g_pTreasureFactory->TranslateMaterialStringToEnumValue(iter->data()));
}
if (HasField(reader, "elementalVariants"))
output.elementalVariants = reader.at("elementalVariants").get<std::vector<int>>();
if (HasField(reader, "weaponSkill"))
{
std::string tempWeaponSkill = reader.at("weaponSkill").get<std::string>();
output.weaponSkill = g_pTreasureFactory->TranslateSkillStringToEnumValue(tempWeaponSkill);
}
else
output.weaponSkill = UNDEF_SKILL;
}
void from_json(const nlohmann::json &reader, CPossibleSpells &output)
{
if (HasField(reader, "spellName"))
output.spellName = reader.at("spellName").get<std::string>();
if (HasField(reader, "spellCategory"))
{
std::string spellCategoryString = reader.at("spellCategory");
if (spellCategoryString == "magicSkillMastery")
output.spellCategory = magicSkillMastery;
else if (spellCategoryString == "weaponSkillMastery")
output.spellCategory = weaponSkillMastery;
else
output.spellCategory = noCategory;
}
else
output.spellCategory = noCategory;
if (HasField(reader, "spells"))
output.spells = reader.at("spells").get<std::vector<int>>();
if (HasField(reader, "cantrips"))
output.cantrips = reader.at("cantrips").get<std::vector<int>>();
output.id = 0;
}
void from_json(const nlohmann::json &reader, CWieldTier &output)
{
output.weaponSkillRequired = reader.value("weaponSkillRequired", 0);
output.minAttackSkillBonus = reader.value("minAttackSkillBonus", 0.0f);
output.maxAttackSkillBonus = reader.value("maxAttackSkillBonus", 0.0f);
output.minMeleeDefenseBonus = reader.value("minMeleeDefenseBonus", 0.0f);
output.maxMeleeDefenseBonus = reader.value("maxMeleeDefenseBonus", 0.0f);
output.maxMissileDefenseBonus = reader.value("maxMissileDefenseBonus", 0.0f);
output.maxMagicDefenseBonus = reader.value("maxMagicDefenseBonus", 0.0f);
output.attackSkillBonusChance = reader.value("attackSkillBonusChance", 0.0f);
output.meleeDefenseBonusChance = reader.value("meleeDefenseBonusChance", 0.0f);
output.missileDefenseBonusChance = reader.value("missileDefenseBonusChance", 0.0f);
output.magicDefenseBonusChance = reader.value("magicDefenseBonusChance", 0.0f);
output.elementalChance = reader.value("elementalChance", 0.0f);
output.slayer_Chance = reader.value("slayer_Chance", 0.0f);
output.slayer_MinDamageBonus = reader.value("slayer_MinDamageBonus", 0.0f);
output.slayer_MaxDamageBonus = reader.value("slayer_MaxDamageBonus", 0.0f);
if (HasField(reader, "slayer_Types"))
{
std::vector<std::string> tempSlayerTypes;
tempSlayerTypes = reader.at("slayer_Types").get<std::vector<std::string>>();
for (auto iter = tempSlayerTypes.begin(); iter != tempSlayerTypes.end(); ++iter)
output.slayer_Types.push_back(g_pTreasureFactory->TranslateCreatureStringToEnumValue(iter->data()));
}
output.maxPropertyAmount = reader.value("maxPropertyAmount", 0);
output.crushingBlow_Chance = reader.value("crushingBlow_Chance", 0.0f);
output.crushingBlow_MinCriticalMultiplier = reader.value("crushingBlow_MinCriticalMultiplier", 0.0f);
output.crushingBlow_MaxCriticalMultiplier = reader.value("crushingBlow_MaxCriticalMultiplier", 0.0f);
output.bitingStrike_Chance = reader.value("bitingStrike_Chance", 0.0f);
output.bitingStrike_MinCriticalFrequency = reader.value("bitingStrike_MinCriticalFrequency", 0.0f);
output.bitingStrike_MaxCriticalFrequency = reader.value("bitingStrike_MaxCriticalFrequency", 0.0f);
output.minDamageBonus = reader.value("minDamageBonus", 0);
output.maxDamageBonus = reader.value("maxDamageBonus", 0);
output.highVariance = reader.value("highVariance", 0.0f);
output.lowVariance = reader.value("lowVariance", 0.0f);
output.slowSpeedMod = reader.value("slowSpeedMod", 0.0f);
output.fastSpeedMod = reader.value("fastSpeedMod", 0.0f);
output.minDamageModBonus = reader.value("minDamageModBonus", 0.0f);
output.maxDamageModBonus = reader.value("maxDamageModBonus", 0.0f);
output.minElementalDamageBonus = reader.value("minElementalDamageBonus", 0);
output.maxElementalDamageBonus = reader.value("maxElementalDamageBonus", 0);
output.minManaConversionBonus = reader.value("minManaConversionBonus", 0.0f);
output.maxManaConversionBonus = reader.value("maxManaConversionBonus", 0.0f);
output.minElementalDamageMod = reader.value("minElementalDamageMod", 0.0f);
output.maxElementalDamageMod = reader.value("maxElementalDamageMod", 0.0f);
output.manaConversionBonusChance = reader.value("manaConversionBonusChance", 0.0f);
output.gemTier = reader.value("gemTier", 0.0f);
output.spellChance = reader.value("spellChance", 0.0f);
output.armorWieldTier = reader.value("armorWieldTier", 0);
output.meleeDefenseSkillRequired = reader.value("meleeDefenseSkillRequired", 0);
output.missileDefenseSkillRequired = reader.value("missileDefenseSkillRequired", 0);
output.magicDefenseSkillRequired = reader.value("magicDefenseSkillRequired", 0);
output.minArmorBonus = reader.value("minArmorBonus", 0);
output.maxArmorBonus = reader.value("maxArmorBonus", 0);
output.minBurdenMod = reader.value("minBurdenMod", 0.0f);
output.maxBurdenMod = reader.value("maxBurdenMod", 0.0f);
output.minShieldArmorBonus = reader.value("minShieldArmorBonus", 0);
output.maxShieldArmorBonus = reader.value("maxShieldArmorBonus", 0);
output.minLevel = reader.value("minLevel", 0);
}
void from_json(const nlohmann::json &reader, CTreasureProfileCategory &output)
{
if (HasField(reader, "category"))
output.category = reader.at("category").get<std::string>();
if (HasField(reader, "possibleMaterials"))
{
std::vector<std::string> tempMaterials;
tempMaterials = reader.at("possibleMaterials").get<std::vector<std::string>>();
for (auto iter = tempMaterials.begin(); iter != tempMaterials.end(); ++iter)
output.possibleMaterials.push_back(g_pTreasureFactory->TranslateMaterialStringToEnumValue(iter->data()));
}
if (HasField(reader, "wieldTiers"))
output.wieldTiers = reader.at("wieldTiers").get<std::vector<CWieldTier>>();
if (HasField(reader, "entries"))
output.entries = reader.at("entries").get<std::vector<CItemTreasureProfileEntry>>();
}
void from_json(const nlohmann::json &reader, CTreasureArmorProfile &output)
{
if (HasField(reader, "wieldTiers"))
output.wieldTiers = reader.at("wieldTiers").get<std::vector<CWieldTier>>();
if (HasField(reader, "categories"))
output.categories = reader.at("categories").get<std::vector<CTreasureProfileCategory>>();
}
void from_json(const nlohmann::json &reader, CTreasureTier &output)
{
output.tierId = reader.value("tierId", 0);
if (HasField(reader, "miscItemsCategories"))
output.miscItemsCategories = reader.at("miscItemsCategories").get<std::vector<std::string>>();
if (HasField(reader, "scrollCategories"))
output.scrollCategories = reader.at("scrollCategories").get<std::vector<std::string>>();
if (HasField(reader, "petdeviceCategories"))
output.petDeviceCategories = reader.at("petdeviceCategories").get<std::vector<std::string>>();
output.lootChance = reader.value("lootChance", 0.0f);
output.chestLootChance = reader.value("chestLootChance", 0.0f);
output.miscLootChance = reader.value("miscLootChance", 0.0f);
output.scrollLootChance = reader.value("scrollLootChance", 0.0f);
output.petdeviceLootChance = reader.value("petdeviceLootChance", 0.0f);
output.lootAmountMultiplier = reader.value("lootAmountMultiplier", 0.0f);
output.chestLootAmountMultiplier = reader.value("chestLootAmountMultiplier", 0.0f);
output.qualityLootLevelThreshold = reader.value("qualityLootLevelThreshold", 0);
output.qualityLootModifier = reader.value("qualityLootModifier", 0.0f);
output.minMeleeWeaponWieldTier = reader.value("minMeleeWeaponWieldTier", 0);
output.maxMeleeWeaponWieldTier = reader.value("maxMeleeWeaponWieldTier", 0);
output.minMissileWeaponWieldTier = reader.value("minMissileWeaponWieldTier", 0);
output.maxMissileWeaponWieldTier = reader.value("maxMissileWeaponWieldTier", 0);
output.minCasterWieldTier = reader.value("minCasterWieldTier", 0);
output.maxCasterWieldTier = reader.value("maxCasterWieldTier", 0);
output.minGemTier = reader.value("minGemTier", 0);
output.maxGemTier = reader.value("maxGemTier", 0);
output.minArmorWieldTier = reader.value("minArmorWieldTier", 0);
output.maxArmorWieldTier = reader.value("maxArmorWieldTier", 0);
output.minArmorMeleeWieldTier = reader.value("minArmorMeleeWieldTier", 0);
output.maxArmorMeleeWieldTier = reader.value("maxArmorMeleeWieldTier", 0);
output.minArmorMissileWieldTier = reader.value("minArmorMissileWieldTier", 0);
output.maxArmorMissileWieldTier = reader.value("maxArmorMissileWieldTier", 0);
output.minArmorMagicWieldTier = reader.value("minArmorMagicWieldTier", 0);
output.maxArmorMagicWieldTier = reader.value("maxArmorMagicWieldTier", 0);
output.meleeWeaponsLootProportion = reader.value("meleeWeaponsLootProportion", 0);
output.missileWeaponsLootProportion = reader.value("missileWeaponsLootProportion", 0);
output.castersLootProportion = reader.value("castersLootProportion", 0);
output.armorLootProportion = reader.value("armorLootProportion", 0);
output.clothingLootProportion = reader.value("clothingLootProportion", 0);
output.jewelryLootProportion = reader.value("jewelryLootProportion", 0);
output.gemLootProportion = reader.value("gemLootProportion", 0);
if (HasField(reader, "commonArmorCategories"))
output.commonArmorCategoryNames = reader.at("commonArmorCategories").get<std::vector<std::string>>();
if (HasField(reader, "rareArmorCategories"))
output.rareArmorCategoryNames = reader.at("rareArmorCategories").get<std::vector<std::string>>();
output.rareArmorChance = reader.value("rareArmorChance", 0.0f);
output.minWorkmanship = reader.value("minWorkmanship", 0);
output.maxWorkmanship = reader.value("maxWorkmanship", 0);
output.maxAmountOfSpells = reader.value("maxAmountOfSpells", 0);
output.chanceOfSpells = reader.value("chanceOfSpells", 0.0f);
output.minSpellLevel = reader.value("minSpellLevel", 0);
output.maxSpellLevel = reader.value("maxSpellLevel", 0);
output.preferredSpellLevel = reader.value("preferredSpellLevel", 0);
output.preferredSpellLevelStrength = reader.value("preferredSpellLevelStrength", 0.0f);
output.maxAmountOfCantrips = reader.value("maxAmountOfCantrips", 0);
output.chanceOfCantrips = reader.value("chanceOfCantrips", 0.0f);
output.minCantripLevel = reader.value("minCantripLevel", 0);
output.maxCantripLevel = reader.value("maxCantripLevel", 0);
output.preferredCantripLevel = reader.value("preferredCantripLevel", 0);
output.preferredCantripLevelStrength = reader.value("preferredCantripLevelStrength", 0.0f);
output.heritageRequirementChance = reader.value("heritageRequirementChance", 0.0f);
output.minSpellsForHeritageRequirement = reader.value("minSpellsForHeritageRequirement", 0);
output.allegianceRankRequirementChance = reader.value("allegianceRankRequirementChance", 0.0f);
output.minSpellsForAllegianceRankRequirement = reader.value("minSpellsForAllegianceRankRequirement", 0);
output.maxAllegianceRankRequired = reader.value("maxAllegianceRankRequired", 0);
if (HasField(reader, "materialsCeramic"))
{
std::vector<std::string> tempMaterials;
tempMaterials = reader.at("materialsCeramic").get<std::vector<std::string>>();
for (auto iter = tempMaterials.begin(); iter != tempMaterials.end(); ++iter)
output.materialsCeramic.push_back(g_pTreasureFactory->TranslateMaterialStringToEnumValue(iter->data()));
}
if (HasField(reader, "materialsCloth"))
{
std::vector<std::string> tempMaterials;
tempMaterials = reader.at("materialsCloth").get<std::vector<std::string>>();
for (auto iter = tempMaterials.begin(); iter != tempMaterials.end(); ++iter)
output.materialsCloth.push_back(g_pTreasureFactory->TranslateMaterialStringToEnumValue(iter->data()));
}
if (HasField(reader, "materialsGem"))
{
std::vector<std::string> tempMaterials;
tempMaterials = reader.at("materialsGem").get<std::vector<std::string>>();
for (auto iter = tempMaterials.begin(); iter != tempMaterials.end(); ++iter)
output.materialsGem.push_back(g_pTreasureFactory->TranslateMaterialStringToEnumValue(iter->data()));
}
if (HasField(reader, "materialsLeather"))
{
std::vector<std::string> tempMaterials;
tempMaterials = reader.at("materialsLeather").get<std::vector<std::string>>();
for (auto iter = tempMaterials.begin(); iter != tempMaterials.end(); ++iter)
output.materialsLeather.push_back(g_pTreasureFactory->TranslateMaterialStringToEnumValue(iter->data()));
}
if (HasField(reader, "materialsMetal"))
{
std::vector<std::string> tempMaterials;
tempMaterials = reader.at("materialsMetal").get<std::vector<std::string>>();
for (auto iter = tempMaterials.begin(); iter != tempMaterials.end(); ++iter)
output.materialsMetal.push_back(g_pTreasureFactory->TranslateMaterialStringToEnumValue(iter->data()));
}
if (HasField(reader, "materialsStone"))
{
std::vector<std::string> tempMaterials;
tempMaterials = reader.at("materialsStone").get<std::vector<std::string>>();
for (auto iter = tempMaterials.begin(); iter != tempMaterials.end(); ++iter)
output.materialsStone.push_back(g_pTreasureFactory->TranslateMaterialStringToEnumValue(iter->data()));
}
if (HasField(reader, "materialsWood"))
{
std::vector<std::string> tempMaterials;
tempMaterials = reader.at("materialsWood").get<std::vector<std::string>>();
for (auto iter = tempMaterials.begin(); iter != tempMaterials.end(); ++iter)
output.materialsWood.push_back(g_pTreasureFactory->TranslateMaterialStringToEnumValue(iter->data()));
}
}
void from_json(const nlohmann::json &reader, CTreasureType &output)
{
output.tier = reader.value("tier", 0);
output.maxTreasureAmount = reader.value("maxTreasureAmount", 0);
output.lootChance = reader.value("lootChance", 0.0f);
output.maxMundaneAmount = reader.value("maxMundaneAmount", 0);
output.mundaneLootChance = reader.value("mundaneLootChance", 0.0f);
output.qualityModifier = reader.value("qualityModifier", 0.0f);
}
void from_json(const nlohmann::json &reader, CRareTier &output)
{
if (HasField(reader, "rareTierWcids"))
output.rareTierWcids = reader.at("rareTierWcids").get<std::vector<int>>();
}
DEFINE_UNPACK_JSON(CTreasureProfile)
{
if (HasField(reader, "options"))
{
if (HasField(reader.at("options"), "extraMutations"))
extraMutations = reader.at("options").value("extraMutations", false);
if (HasField(reader.at("options"), "rareJunkChance"))
rareJunkChance = reader.at("options").value("rareJunkChance", 0.0f);
}
if (HasField(reader, "tiers"))
{
std::vector<CTreasureTier> tempTiers;
tempTiers = reader.at("tiers").get<std::vector<CTreasureTier>>();
for (auto iter = tempTiers.begin(); iter != tempTiers.end(); ++iter)
tiers.emplace(iter->tierId, (*iter));
}
if (HasField(reader, "meleeWeapons"))
meleeWeapons = reader.at("meleeWeapons").get<std::vector<CTreasureProfileCategory>>();
if (HasField(reader, "missileWeapons"))
missileWeapons = reader.at("missileWeapons").get<std::vector<CTreasureProfileCategory>>();
if (HasField(reader, "casters"))
casters = reader.at("casters").get<std::vector<CTreasureProfileCategory>>();
if (HasField(reader, "gemstones"))
gemstones = reader.at("gemstones");
if (HasField(reader, "armor"))
armor = reader.at("armor");
if (HasField(reader, "clothing"))
clothing = reader.at("clothing").get<std::vector<CTreasureProfileCategory>>();
if (HasField(reader, "jewelry"))
jewelry = reader.at("jewelry").get<std::vector<CTreasureProfileCategory>>();
if (HasField(reader, "miscItems"))
miscItems = reader.at("miscItems").get<std::vector<CTreasureProfileCategory>>();
if (HasField(reader, "petdevices"))
petdevices = reader.at("petdevices").get<std::vector<CTreasureProfileCategory>>();
if (HasField(reader, "scrolls"))
scrolls = reader.at("scrolls").get<std::vector<CTreasureProfileCategory>>();
if (HasField(reader, "spellProperties"))
spellProperties = reader.at("spellProperties");
if (HasField(reader, "meleeWeaponSpells"))
meleeWeaponSpells = reader.at("meleeWeaponSpells").get<std::vector<CPossibleSpells>>();
if (HasField(reader, "missileWeaponSpells"))
missileWeaponSpells = reader.at("missileWeaponSpells").get<std::vector<CPossibleSpells>>();
if (HasField(reader, "casterSpells"))
casterSpells = reader.at("casterSpells").get<std::vector<CPossibleSpells>>();
if (HasField(reader, "gemSpells"))
gemSpells = reader.at("gemSpells").get<std::vector<CPossibleSpells>>();
if (HasField(reader, "shieldSpells"))
shieldSpells = reader.at("shieldSpells").get<std::vector<CPossibleSpells>>();
if (HasField(reader, "jewelrySpells"))
jewelrySpells = reader.at("jewelrySpells").get<std::vector<CPossibleSpells>>();
if (HasField(reader, "clothingSpells"))
clothingSpells = reader.at("clothingSpells").get<std::vector<CPossibleSpells>>();
if (HasField(reader, "clothingHeadSpells"))
clothingHeadSpells = reader.at("clothingHeadSpells").get<std::vector<CPossibleSpells>>();
if (HasField(reader, "clothingHandsSpells"))
clothingHandsSpells = reader.at("clothingHandsSpells").get<std::vector<CPossibleSpells>>();
if (HasField(reader, "clothingFeetSpells"))
clothingFeetSpells = reader.at("clothingFeetSpells").get<std::vector<CPossibleSpells>>();
if (HasField(reader, "armorItemSpells"))
armorItemSpells = reader.at("armorItemSpells").get<std::vector<CPossibleSpells>>();
if (HasField(reader, "armorHeadSpells"))
armorHeadSpells = reader.at("armorHeadSpells").get<std::vector<CPossibleSpells>>();
if (HasField(reader, "armorUpperBodySpells"))
armorUpperBodySpells = reader.at("armorUpperBodySpells").get<std::vector<CPossibleSpells>>();
if (HasField(reader, "armorHandsSpells"))
armorHandsSpells = reader.at("armorHandsSpells").get<std::vector<CPossibleSpells>>();
if (HasField(reader, "armorLowerBodySpells"))
armorLowerBodySpells = reader.at("armorLowerBodySpells").get<std::vector<CPossibleSpells>>();
if (HasField(reader, "armorFeetSpells"))
armorFeetSpells = reader.at("armorFeetSpells").get<std::vector<CPossibleSpells>>();
if (HasField(reader, "materialProperties"))
{
std::map<std::string, CMaterialProperties> tempMaterialProperties;
tempMaterialProperties = reader.at("materialProperties").get<std::map<std::string, CMaterialProperties>>();
for (auto iter = tempMaterialProperties.begin(); iter != tempMaterialProperties.end(); ++iter)
materialProperties.emplace(g_pTreasureFactory->TranslateMaterialStringToEnumValue(iter->first), iter->second);
}
if (HasField(reader, "workmanshipProperties"))
{
// nlohmann::json doesn't like int keys in maps, so we read as string and convert to int.
std::map<std::string, CWorkmanshipProperties> tempWorkmanshipProperties;
tempWorkmanshipProperties = reader.at("workmanshipProperties").get<std::map<std::string, CWorkmanshipProperties>>();
for (auto iter = tempWorkmanshipProperties.begin(); iter != tempWorkmanshipProperties.end(); ++iter)
workmanshipProperties.emplace(atoi(iter->first.c_str()), iter->second);
}
if (HasField(reader, "chestTreasureTypeReplacementTable"))
{
// nlohmann::json doesn't like int keys in maps, so we read as string and convert to int.
std::map<std::string, int> tempChestTreasureTypeReplacementTable;
tempChestTreasureTypeReplacementTable = reader.at("chestTreasureTypeReplacementTable").get<std::map<std::string, int>>();
for (auto iter = tempChestTreasureTypeReplacementTable.begin(); iter != tempChestTreasureTypeReplacementTable.end(); ++iter)
chestTreasureTypeReplacementTable.emplace(atoi(iter->first.c_str()), iter->second);
}
if (HasField(reader, "treasureTypeOverrides"))
{
// nlohmann::json doesn't like int keys in maps, so we read as string and convert to int.
std::map<std::string, CTreasureType> tempTreasureTypeOverrides;
tempTreasureTypeOverrides = reader.at("treasureTypeOverrides").get<std::map<std::string, CTreasureType>>();
for (auto iter = tempTreasureTypeOverrides.begin(); iter != tempTreasureTypeOverrides.end(); ++iter)
treasureTypeOverrides.emplace(atoi(iter->first.c_str()), iter->second);
}
if (HasField(reader, "rareTiers"))
{
// nlohmann::json doesn't like int keys in maps, so we read as string and convert to int.
std::map<std::string, CRareTier> tempRareTiers;
tempRareTiers = reader.at("rareTiers").get<std::map<std::string, CRareTier>>();
for (auto iter = tempRareTiers.begin(); iter != tempRareTiers.end(); ++iter)
rareTiers.emplace(atoi(iter->first.c_str()), iter->second);
}
return true;
}
DEFINE_PACK_JSON(CTreasureProfile)
{
UNFINISHED();
}
std::vector<CPossibleSpells> CTreasureProfile::ComputeUniqueSpellIds(std::vector<CPossibleSpells> spellList)
{
try
{
for (int i = 0; i < spellList.size(); i++)
{
CPossibleSpells spell = spellList[i];
int id;
if (spellNameToIdMap.find(spell.spellName) != spellNameToIdMap.end())
{
id = spellNameToIdMap[spell.spellName];
spell.id = id;
}
else
{
id = nextSpellId++;
spell.id = id;
spellNameToIdMap.emplace(spell.spellName, id);
spells.emplace(id, spell);
}
spellList[i] = spell;
}
}
catch(...)
{
SERVER_ERROR << "ERror in CTreasureProfile::ComputeUniqueSpellIds";
}
return spellList;
}
void CTreasureProfile::Initialize()
{
if (isInitialized)
return;
isInitialized = true;
nextSpellId = 0;
meleeWeaponSpells = ComputeUniqueSpellIds(meleeWeaponSpells);
missileWeaponSpells = ComputeUniqueSpellIds(missileWeaponSpells);
casterSpells = ComputeUniqueSpellIds(casterSpells);
shieldSpells = ComputeUniqueSpellIds(shieldSpells);
clothingSpells = ComputeUniqueSpellIds(clothingSpells);
jewelrySpells = ComputeUniqueSpellIds(jewelrySpells);
gemSpells = ComputeUniqueSpellIds(gemSpells);
armorItemSpells = ComputeUniqueSpellIds(armorItemSpells);
armorHeadSpells = ComputeUniqueSpellIds(armorHeadSpells);
armorUpperBodySpells = ComputeUniqueSpellIds(armorUpperBodySpells);
armorHandsSpells = ComputeUniqueSpellIds(armorHandsSpells);
armorLowerBodySpells = ComputeUniqueSpellIds(armorLowerBodySpells);
armorFeetSpells = ComputeUniqueSpellIds(armorFeetSpells);
clothingHeadSpells = ComputeUniqueSpellIds(clothingHeadSpells);
clothingHandsSpells = ComputeUniqueSpellIds(clothingHandsSpells);
clothingFeetSpells = ComputeUniqueSpellIds(clothingFeetSpells);
for (auto iter = tiers.begin(); iter != tiers.end(); ++iter)
{
CTreasureTier *tier = &iter->second;
float totalProportion = tier->meleeWeaponsLootProportion +
tier->missileWeaponsLootProportion +
tier->castersLootProportion +
tier->armorLootProportion +
tier->clothingLootProportion +
tier->jewelryLootProportion +
tier->gemLootProportion;
float meleeWeaponChance = tier->meleeWeaponsLootProportion / totalProportion;
float missileWeaponChance = tier->missileWeaponsLootProportion / totalProportion;
float casterChance = tier->castersLootProportion / totalProportion;
float armorChance = tier->armorLootProportion / totalProportion;
float clothingChance = tier->clothingLootProportion / totalProportion;
float jewelryChance = tier->jewelryLootProportion / totalProportion;
float gemChance = tier->gemLootProportion / totalProportion;
float cumulativeMeleeWeaponChance = meleeWeaponChance;
float cumulativeMissileWeaponChance = cumulativeMeleeWeaponChance + missileWeaponChance;
float cumulativeCasterChance = cumulativeMissileWeaponChance + casterChance;
float cumulativeArmorChance = cumulativeCasterChance + armorChance;
float cumulativeClothingChance = cumulativeArmorChance + clothingChance;
float cumulativeJewelryChance = cumulativeClothingChance + jewelryChance;
float cumulativeGemChance = cumulativeJewelryChance + gemChance;
tier->categoryChances.emplace(cumulativeMeleeWeaponChance, eTreasureCategory::TreasureCategory_Melee_Weapon);
tier->categoryChances.emplace(cumulativeMissileWeaponChance, eTreasureCategory::TreasureCategory_Missile_Weapon);
tier->categoryChances.emplace(cumulativeCasterChance, eTreasureCategory::TreasureCategory_Caster);
tier->categoryChances.emplace(cumulativeArmorChance, eTreasureCategory::TreasureCategory_Armor);
tier->categoryChances.emplace(cumulativeClothingChance, eTreasureCategory::TreasureCategory_Clothing);
tier->categoryChances.emplace(cumulativeJewelryChance, eTreasureCategory::TreasureCategory_Jewelry);
tier->categoryChances.emplace(cumulativeGemChance, eTreasureCategory::TreasureCategory_Gem);
for (auto iter2 = iter->second.commonArmorCategoryNames.begin(); iter2 != iter->second.commonArmorCategoryNames.end(); ++iter2)
{
for (auto iter3 = armor.categories.begin(); iter3 != armor.categories.end(); ++iter3)
{
if (iter3->category == iter2->data())
{
iter->second.commonArmorCategories.push_back(&(*iter3));
break;
}
}
}
for (auto iter2 = iter->second.rareArmorCategoryNames.begin(); iter2 != iter->second.rareArmorCategoryNames.end(); ++iter2)
{
for (auto iter3 = armor.categories.begin(); iter3 != armor.categories.end(); ++iter3)
{
if (iter3->category == iter2->data())
{
iter->second.rareArmorCategories.push_back(&(*iter3));
break;
}
}
}
}
}
eTreasureCategory CTreasureTier::GetRandomTreasureCategory()
{
float roll = getRandomDouble(1.0);
for (auto entry : categoryChances)
{
if (entry.first >= roll)
return entry.second;
}
return categoryChances.rbegin()->second;
}
CTreasureFactory::CTreasureFactory()
{
_materialTypeTranslationTable.emplace("none", Undef_MaterialType);
_materialTypeTranslationTable.emplace("ceramic", Ceramic_MaterialType);
_materialTypeTranslationTable.emplace("porcelain", Porcelain_MaterialType);
_materialTypeTranslationTable.emplace("cloth", Cloth_MaterialType);
_materialTypeTranslationTable.emplace("linen", Linen_MaterialType);
_materialTypeTranslationTable.emplace("satin", Satin_MaterialType);
_materialTypeTranslationTable.emplace("silk", Silk_MaterialType);
_materialTypeTranslationTable.emplace("velvet", Velvet_MaterialType);
_materialTypeTranslationTable.emplace("wool", Wool_MaterialType);
_materialTypeTranslationTable.emplace("gem", Gem_MaterialType);
_materialTypeTranslationTable.emplace("agate", Agate_MaterialType);
_materialTypeTranslationTable.emplace("amber", Amber_MaterialType);
_materialTypeTranslationTable.emplace("amethyst", Amethyst_MaterialType);
_materialTypeTranslationTable.emplace("aquamarine", Aquamarine_MaterialType);
_materialTypeTranslationTable.emplace("azurite", Azurite_MaterialType);
_materialTypeTranslationTable.emplace("blackGarnet", Black_Garnet_MaterialType);
_materialTypeTranslationTable.emplace("blackOpal", Black_Opal_MaterialType);
_materialTypeTranslationTable.emplace("bloodstone", Bloodstone_MaterialType);
_materialTypeTranslationTable.emplace("carnelian", Carnelian_MaterialType);
_materialTypeTranslationTable.emplace("citrine", Citrine_MaterialType);
_materialTypeTranslationTable.emplace("diamond", Diamond_MaterialType);
_materialTypeTranslationTable.emplace("emerald", Emerald_MaterialType);
_materialTypeTranslationTable.emplace("fireOpal", Fire_Opal_MaterialType);
_materialTypeTranslationTable.emplace("greenGarnet", Green_Garnet_MaterialType);
_materialTypeTranslationTable.emplace("greenJade", Green_Jade_MaterialType);
_materialTypeTranslationTable.emplace("hematite", Hematite_MaterialType);
_materialTypeTranslationTable.emplace("imperialTopaz", Imperial_Topaz_MaterialType);
_materialTypeTranslationTable.emplace("jet", Jet_MaterialType);
_materialTypeTranslationTable.emplace("lapisLazuli", Lapis_Lazuli_MaterialType);
_materialTypeTranslationTable.emplace("lavenderJade", Lavender_Jade_MaterialType);
_materialTypeTranslationTable.emplace("malachite", Malachite_MaterialType);
_materialTypeTranslationTable.emplace("moonstone", Moonstone_MaterialType);
_materialTypeTranslationTable.emplace("onyx", Onyx_MaterialType);
_materialTypeTranslationTable.emplace("opal", Opal_MaterialType);
_materialTypeTranslationTable.emplace("peridot", Peridot_MaterialType);
_materialTypeTranslationTable.emplace("redGarnet", Red_Garnet_MaterialType);
_materialTypeTranslationTable.emplace("RedJade", Red_Jade_MaterialType);
_materialTypeTranslationTable.emplace("roseQuartz", Rose_Quartz_MaterialType);
_materialTypeTranslationTable.emplace("ruby", Ruby_MaterialType);
_materialTypeTranslationTable.emplace("sapphire", Sapphire_MaterialType);
_materialTypeTranslationTable.emplace("smokeyQuartz", Smoky_Quartz_MaterialType);
_materialTypeTranslationTable.emplace("sunstone", Sunstone_MaterialType);
_materialTypeTranslationTable.emplace("tigerEye", Tiger_Eye_MaterialType);
_materialTypeTranslationTable.emplace("tourmaline", Tourmaline_MaterialType);
_materialTypeTranslationTable.emplace("turquoise", Turquoise_MaterialType);
_materialTypeTranslationTable.emplace("whiteJade", White_Jade_MaterialType);
_materialTypeTranslationTable.emplace("whiteQuartz", White_Quartz_MaterialType);
_materialTypeTranslationTable.emplace("whiteSapphire", White_Sapphire_MaterialType);
_materialTypeTranslationTable.emplace("yellowGarnet", Yellow_Garnet_MaterialType);
_materialTypeTranslationTable.emplace("yellowTopaz", Yellow_Topaz_MaterialType);
_materialTypeTranslationTable.emplace("zircon", Zircon_MaterialType);
_materialTypeTranslationTable.emplace("ivory", Ivory_MaterialType);
_materialTypeTranslationTable.emplace("leather", Leather_MaterialType);
_materialTypeTranslationTable.emplace("armoredilloHide", Armoredillo_Hide_MaterialType);
_materialTypeTranslationTable.emplace("gromnieHide", Gromnie_Hide_MaterialType);
_materialTypeTranslationTable.emplace("reedSharkHide", Reed_Shark_Hide_MaterialType);
_materialTypeTranslationTable.emplace("metal", Metal_MaterialType);
_materialTypeTranslationTable.emplace("brass", Brass_MaterialType);
_materialTypeTranslationTable.emplace("bronze", Bronze_MaterialType);
_materialTypeTranslationTable.emplace("copper", Copper_MaterialType);
_materialTypeTranslationTable.emplace("gold", Gold_MaterialType);
_materialTypeTranslationTable.emplace("iron", Iron_MaterialType);
_materialTypeTranslationTable.emplace("pyreal", Pyreal_MaterialType);
_materialTypeTranslationTable.emplace("silver", Silver_MaterialType);
_materialTypeTranslationTable.emplace("steel", Steel_MaterialType);
_materialTypeTranslationTable.emplace("stone", Stone_MaterialType);
_materialTypeTranslationTable.emplace("alabaster", Alabaster_MaterialType);
_materialTypeTranslationTable.emplace("granite", Granite_MaterialType);
_materialTypeTranslationTable.emplace("marble", Marble_MaterialType);
_materialTypeTranslationTable.emplace("obsidian", Obsidian_MaterialType);
_materialTypeTranslationTable.emplace("sandstone", Sandstone_MaterialType);
_materialTypeTranslationTable.emplace("serpentine", Serpentine_MaterialType);
_materialTypeTranslationTable.emplace("wood", Wood_MaterialType);
_materialTypeTranslationTable.emplace("ebony", Ebony_MaterialType);
_materialTypeTranslationTable.emplace("mahogany", Mahogany_MaterialType);
_materialTypeTranslationTable.emplace("oak", Oak_MaterialType);
_materialTypeTranslationTable.emplace("pine", Pine_MaterialType);
_materialTypeTranslationTable.emplace("teak", Teak_MaterialType);
_skillTypeTranslationTable.emplace("None", UNDEF_SKILL);
_skillTypeTranslationTable.emplace("Axe", AXE_SKILL);
_skillTypeTranslationTable.emplace("Bow", BOW_SKILL);
_skillTypeTranslationTable.emplace("Crossbow", CROSSBOW_SKILL);
_skillTypeTranslationTable.emplace("Dagger", DAGGER_SKILL);
_skillTypeTranslationTable.emplace("Mace", MACE_SKILL);
_skillTypeTranslationTable.emplace("MeleeDefense", MELEE_DEFENSE_SKILL);
_skillTypeTranslationTable.emplace("MissileDefense", MISSILE_DEFENSE_SKILL);
_skillTypeTranslationTable.emplace("Sling", SLING_SKILL);
_skillTypeTranslationTable.emplace("Spear", SPEAR_SKILL);
_skillTypeTranslationTable.emplace("Staff", STAFF_SKILL);
_skillTypeTranslationTable.emplace("Sword", SWORD_SKILL);
_skillTypeTranslationTable.emplace("ThrownWeapon", THROWN_WEAPON_SKILL);
_skillTypeTranslationTable.emplace("UnarmedCombat", UNARMED_COMBAT_SKILL);
_skillTypeTranslationTable.emplace("ArcaneLore", ARCANE_LORE_SKILL);
_skillTypeTranslationTable.emplace("MagicDefense", MAGIC_DEFENSE_SKILL);
_skillTypeTranslationTable.emplace("ManaConversion", MANA_CONVERSION_SKILL);
_skillTypeTranslationTable.emplace("SpellCraft", SPELLCRAFT_SKILL);
_skillTypeTranslationTable.emplace("ItemAppraisal", ITEM_APPRAISAL_SKILL);
_skillTypeTranslationTable.emplace("PersonalAppraisal", PERSONAL_APPRAISAL_SKILL);
_skillTypeTranslationTable.emplace("Deception", DECEPTION_SKILL);
_skillTypeTranslationTable.emplace("Healing", HEALING_SKILL);
_skillTypeTranslationTable.emplace("Jump", JUMP_SKILL);
_skillTypeTranslationTable.emplace("Lockpick", LOCKPICK_SKILL);
_skillTypeTranslationTable.emplace("Run", RUN_SKILL);
_skillTypeTranslationTable.emplace("Awareness", AWARENESS_SKILL);
_skillTypeTranslationTable.emplace("ArmsAndArmorRepair", ARMS_AND_ARMOR_REPAIR_SKILL);
_skillTypeTranslationTable.emplace("CreatureAppraisal", CREATURE_APPRAISAL_SKILL);
_skillTypeTranslationTable.emplace("WeaponAppraisal", WEAPON_APPRAISAL_SKILL);
_skillTypeTranslationTable.emplace("ArmorAppraisal", ARMOR_APPRAISAL_SKILL);
_skillTypeTranslationTable.emplace("MagicItemAppraisal", MAGIC_ITEM_APPRAISAL_SKILL);
_skillTypeTranslationTable.emplace("CreatureEnchantment", CREATURE_ENCHANTMENT_SKILL);
_skillTypeTranslationTable.emplace("ItemEnchantment", ITEM_ENCHANTMENT_SKILL);
_skillTypeTranslationTable.emplace("LifeMagic", LIFE_MAGIC_SKILL);
_skillTypeTranslationTable.emplace("WarMagic", WAR_MAGIC_SKILL);
_skillTypeTranslationTable.emplace("Leadership", LEADERSHIP_SKILL);
_skillTypeTranslationTable.emplace("Loyalty", LOYALTY_SKILL);
_skillTypeTranslationTable.emplace("Fletching", FLETCHING_SKILL);
_skillTypeTranslationTable.emplace("Alchemy", ALCHEMY_SKILL);
_skillTypeTranslationTable.emplace("Cooking", COOKING_SKILL);
_skillTypeTranslationTable.emplace("Salvaging", SALVAGING_SKILL);
_skillTypeTranslationTable.emplace("TwoHandedCombat", TWO_HANDED_COMBAT_SKILL);
_skillTypeTranslationTable.emplace("GearCraft", GEARCRAFT_SKILL);
_skillTypeTranslationTable.emplace("VoidMagic", VOID_MAGIC_SKILL);
_skillTypeTranslationTable.emplace("HeavyWeapons", HEAVY_WEAPONS_SKILL);
_skillTypeTranslationTable.emplace("LightWeapons", LIGHT_WEAPONS_SKILL);
_skillTypeTranslationTable.emplace("FinesseWeapons", FINESSE_WEAPONS_SKILL);
_skillTypeTranslationTable.emplace("MissileWeapons", MISSILE_WEAPONS_SKILL);
_skillTypeTranslationTable.emplace("ShieldSkill", SHIELD_SKILL);
_skillTypeTranslationTable.emplace("DualWield", DUAL_WIELD_SKILL);
_skillTypeTranslationTable.emplace("Recklessness", RECKLESSNESS_SKILL);
_skillTypeTranslationTable.emplace("SneakAttack", SNEAK_ATTACK_SKILL);
_skillTypeTranslationTable.emplace("DirtyFighting", DIRTY_FIGHTING_SKILL);
_skillTypeTranslationTable.emplace("Challenge", CHALLENGE_SKILL);
_skillTypeTranslationTable.emplace("Summoning", SUMMONING_SKILL);
_creatureTypeTranslationTable.emplace("none", none);
_creatureTypeTranslationTable.emplace("olthoi", olthoi);
_creatureTypeTranslationTable.emplace("banderling", banderling);
_creatureTypeTranslationTable.emplace("drudge", drudge);
_creatureTypeTranslationTable.emplace("mosswart", mosswart);
_creatureTypeTranslationTable.emplace("lugian", lugian);
_creatureTypeTranslationTable.emplace("tumerok", tumerok);
_creatureTypeTranslationTable.emplace("mite", mite);
_creatureTypeTranslationTable.emplace("tusker", tusker);
_creatureTypeTranslationTable.emplace("phyntosWasp", phyntosWasp);
_creatureTypeTranslationTable.emplace("rat", rat);
_creatureTypeTranslationTable.emplace("auroch", auroch);
_creatureTypeTranslationTable.emplace("cow", cow);
_creatureTypeTranslationTable.emplace("golem", golem);
_creatureTypeTranslationTable.emplace("undead", undead);
_creatureTypeTranslationTable.emplace("gromnie", gromnie);
_creatureTypeTranslationTable.emplace("reedshark", reedshark);
_creatureTypeTranslationTable.emplace("armoredillo", armoredillo);
_creatureTypeTranslationTable.emplace("fae", fae);
_creatureTypeTranslationTable.emplace("virindi", virindi);
_creatureTypeTranslationTable.emplace("wisp", wisp);
_creatureTypeTranslationTable.emplace("knathtaed", knathtaed);
_creatureTypeTranslationTable.emplace("shadow", shadow);
_creatureTypeTranslationTable.emplace("mattekar", mattekar);
_creatureTypeTranslationTable.emplace("mumiyah", mumiyah);
_creatureTypeTranslationTable.emplace("rabbit", rabbit);
_creatureTypeTranslationTable.emplace("sclavus", sclavus);
_creatureTypeTranslationTable.emplace("shallowsShark", shallowsShark);
_creatureTypeTranslationTable.emplace("monouga", monouga);
_creatureTypeTranslationTable.emplace("zefir", zefir);
_creatureTypeTranslationTable.emplace("skeleton", skeleton);
_creatureTypeTranslationTable.emplace("human", human);
_creatureTypeTranslationTable.emplace("shreth", shreth);
_creatureTypeTranslationTable.emplace("chittick", chittick);
_creatureTypeTranslationTable.emplace("moarsman", moarsman);
_creatureTypeTranslationTable.emplace("slithis", slithis);
_creatureTypeTranslationTable.emplace("fireElemental", fireElemental);
_creatureTypeTranslationTable.emplace("snowman", snowman);
_creatureTypeTranslationTable.emplace("bunny", bunny);
_creatureTypeTranslationTable.emplace("lightningElemental", lightningElemental);
_creatureTypeTranslationTable.emplace("grievver", grievver);
_creatureTypeTranslationTable.emplace("niffis", niffis);
_creatureTypeTranslationTable.emplace("ursuin", ursuin);
_creatureTypeTranslationTable.emplace("crystal", crystal);
_creatureTypeTranslationTable.emplace("hollowMinion", hollowMinion);
_creatureTypeTranslationTable.emplace("scarecrow", scarecrow);
_creatureTypeTranslationTable.emplace("idol", idol);
_creatureTypeTranslationTable.emplace("empyrean", empyrean);
_creatureTypeTranslationTable.emplace("hopeslayer", hopeslayer);
_creatureTypeTranslationTable.emplace("doll", doll);
_creatureTypeTranslationTable.emplace("marionette", marionette);
_creatureTypeTranslationTable.emplace("carenzi", carenzi);
_creatureTypeTranslationTable.emplace("siraluun", siraluun);
_creatureTypeTranslationTable.emplace("aunTumerok", aunTumerok);
_creatureTypeTranslationTable.emplace("heaTumerok", heaTumerok);
_creatureTypeTranslationTable.emplace("simulacrum", simulacrum);
_creatureTypeTranslationTable.emplace("acidElemental", acidElemental);
_creatureTypeTranslationTable.emplace("frostElemental", frostElemental);
_creatureTypeTranslationTable.emplace("elemental", elemental);
_creatureTypeTranslationTable.emplace("statue", statue);
_creatureTypeTranslationTable.emplace("wall", wall);
_creatureTypeTranslationTable.emplace("alteredHuman", alteredHuman);
_creatureTypeTranslationTable.emplace("device", device);
_creatureTypeTranslationTable.emplace("harbinger", harbinger);
_creatureTypeTranslationTable.emplace("darkSarcophagus", darkSarcophagus);
_creatureTypeTranslationTable.emplace("chicken", chicken);
_creatureTypeTranslationTable.emplace("gotrokLugian", gotrokLugian);
_creatureTypeTranslationTable.emplace("margul", margul);
_creatureTypeTranslationTable.emplace("bleachedRabbit", bleachedRabbit);
_creatureTypeTranslationTable.emplace("nastyRabbit", nastyRabbit);
_creatureTypeTranslationTable.emplace("grimacingRabbit", grimacingRabbit);
_creatureTypeTranslationTable.emplace("burun", burun);
_creatureTypeTranslationTable.emplace("target", target);
_creatureTypeTranslationTable.emplace("ghost", ghost);
_creatureTypeTranslationTable.emplace("fiun", fiun);
_creatureTypeTranslationTable.emplace("eater", eater);
_creatureTypeTranslationTable.emplace("penguin", penguin);
_creatureTypeTranslationTable.emplace("ruschk", ruschk);
_creatureTypeTranslationTable.emplace("thrungus", thrungus);
_creatureTypeTranslationTable.emplace("viamontianKnight", viamontianKnight);
_creatureTypeTranslationTable.emplace("remoran", remoran);
_creatureTypeTranslationTable.emplace("swarm", swarm);
_creatureTypeTranslationTable.emplace("moar", moar);
_creatureTypeTranslationTable.emplace("enchantedArms", enchantedArms);
_creatureTypeTranslationTable.emplace("sleech", sleech);
_creatureTypeTranslationTable.emplace("mukkir", mukkir);
_creatureTypeTranslationTable.emplace("merwart", merwart);
_creatureTypeTranslationTable.emplace("food", food);
_creatureTypeTranslationTable.emplace("paradoxOlthoi", paradoxOlthoi);
_creatureTypeTranslationTable.emplace("energy", energy);
_creatureTypeTranslationTable.emplace("apparition", apparition);
_creatureTypeTranslationTable.emplace("aerbax", aerbax);
_creatureTypeTranslationTable.emplace("touched", touched);
_creatureTypeTranslationTable.emplace("blightedMoarsman", blightedMoarsman);
_creatureTypeTranslationTable.emplace("gearKnight", gearKnight);
_creatureTypeTranslationTable.emplace("gurog", gurog);
_creatureTypeTranslationTable.emplace("anekshay", anekshay);
}
CTreasureFactory::~CTreasureFactory()
{
SafeDelete(_TreasureProfile)
}
MaterialType CTreasureFactory::TranslateMaterialStringToEnumValue(std::string stringValue)
{
if (_materialTypeTranslationTable.find(stringValue) != _materialTypeTranslationTable.end())
return _materialTypeTranslationTable[stringValue];
return MaterialType::Undef_MaterialType;
}
STypeSkill CTreasureFactory::TranslateSkillStringToEnumValue(std::string stringValue)
{
if (_skillTypeTranslationTable.find(stringValue) != _skillTypeTranslationTable.end())
return _skillTypeTranslationTable[stringValue];
return STypeSkill::UNDEF_SKILL;
}
CreatureType CTreasureFactory::TranslateCreatureStringToEnumValue(std::string stringValue)
{
if (_creatureTypeTranslationTable.find(stringValue) != _creatureTypeTranslationTable.end())
return _creatureTypeTranslationTable[stringValue];
return CreatureType::none;
}
void CTreasureFactory::Initialize()
{
WINLOG(Data, Normal, "Loading treasure generation profile...\n");
SERVER_INFO << "Loading treasure generation profile...";
fs::path path = g_pGlobals->GetGameData("Data", "json");
std::ifstream fileStream(path / "treasureProfile.json");
if (fileStream.is_open())
{
json jsonData;
try
{
fileStream >> jsonData;
fileStream.close();
}
catch (const nlohmann::detail::parse_error &e)
{
WINLOG(Data, Error, "----------------------\nError parsing treasureProfile.json:\n%s\n----------------------\n", e.what());
SERVER_ERROR << "Error parsing treasureProfile.json";
return;
}
_TreasureProfile = new CTreasureProfile();
try
{
_TreasureProfile->UnPackJson(jsonData);
_TreasureProfile->Initialize();
}
catch (...)
{
WINLOG(Data, Error, "----------------------\nError loading treasure generation profile!\n----------------------\n");
SERVER_ERROR << "Error parsing treasure generation profile";
SafeDelete(_TreasureProfile);
return;
}
}
WINLOG(Data, Normal, "Finished loading treasure generation profile.\n");
SERVER_INFO << "Finished loading treasure generation profile";
}
CWeenieObject *CreateFromEntry(PackableList<TreasureEquipment>::iterator entry, unsigned int ptid, float shade)
{
if (CWeenieObject *newItem = g_pWeenieFactory->CreateWeenieByClassID(entry->_wcid))
{
if (entry->_amount > 1)
{
int maxStackSize = newItem->InqIntQuality(MAX_STACK_SIZE_INT, 1);
if (maxStackSize > 1)
{
double minAmount = (double)entry->_amount * entry->_amountVariance;
int modifiedAmount = round(Random::RollDice(minAmount, entry->_amount));
newItem->SetStackSize(min(modifiedAmount, maxStackSize));
}
}
if (ptid != 0)
newItem->m_Qualities.SetInt(PALETTE_TEMPLATE_INT, ptid);
else if (entry->_ptid)
newItem->m_Qualities.SetInt(PALETTE_TEMPLATE_INT, entry->_ptid);
if (shade > 0)
newItem->m_Qualities.SetFloat(SHADE_FLOAT, shade);
else if (entry->_shade > 0.0)
newItem->m_Qualities.SetFloat(SHADE_FLOAT, entry->_shade);
return newItem;
}
return NULL;
}
int SkipToNextSet(PackableList<TreasureEquipment> *treasureList, int index)
{
int i = index;
while (i < treasureList->size())
{
PackableList<TreasureEquipment>::iterator entry = treasureList->GetAt(i);
if (entry->_setStart)
return i;
else if (entry->_hasSubSet)
i = SkipToNextSet(treasureList, i + 2); //next entry is the sublist's first entry so skip 2.
else
i++;
}
return (int)treasureList->size();
}
int ProcessList(PackableList<TreasureEquipment> *treasureList, int index, SItemListCreationData creationData)
{
int i = index;
bool alreadySpawnedInThisSet = false;
while (i < treasureList->size())
{
PackableList<TreasureEquipment>::iterator entry = treasureList->GetAt(i);
if (entry->_continuesPreviousSet && alreadySpawnedInThisSet)
{
if (entry->_hasSubSet)
i = SkipToNextSet(treasureList, i + 2);
else
i = SkipToNextSet(treasureList, i + 1);
continue;
}
//we've already spawned an item for the set, and jumped to the next set. We've checked to see if the set is a continuation so we must be a new set.
if (alreadySpawnedInThisSet)
{
//prepare the data for the next list.
alreadySpawnedInThisSet = false;
creationData.accumulatedChance = 0.0;
creationData.diceRoll = Random::RollDice(0.0, 1.0);
}
double diceRoll = creationData.diceRoll;
double chance = entry->_chance + creationData.accumulatedChance;
creationData.accumulatedChance = chance;
if (diceRoll <= chance)
{
if (CWeenieObject *newItem = CreateFromEntry(entry, creationData.ptid, creationData.shade))
{
g_pWeenieFactory->AddWeenieToDestination(newItem, creationData.parent, creationData.destinationType, creationData.isRegenLocationType, creationData.profile);
creationData.amountCreated++;
alreadySpawnedInThisSet = true;
if (entry->_hasSubSet)
{
SItemListCreationData subListCreationData;
subListCreationData.amountCreated = 0;
subListCreationData.parent = creationData.parent;
subListCreationData.destinationType = creationData.destinationType;
subListCreationData.isRegenLocationType = creationData.isRegenLocationType;
subListCreationData.treasureTypeOrWcid = creationData.treasureTypeOrWcid;
subListCreationData.ptid = creationData.ptid;
subListCreationData.shade = creationData.shade;
subListCreationData.profile = creationData.profile;
subListCreationData.accumulatedChance = 0.0;
subListCreationData.diceRoll = Random::RollDice(0.0, 1.0);
subListCreationData.isSubSet = true;
i = ProcessList(treasureList, i + 1, subListCreationData); //set list index to the selected subset item.
creationData.amountCreated += subListCreationData.amountCreated;
}
if (creationData.isSubSet) //return from recursive call to select a subset item.
return i;
else
i = SkipToNextSet(treasureList, i + 1); //since this index won and if it had a subset we would have parsed it and returned the index of that selected item. There is no need to skip forward 2 indices.
continue;
}
}
else if (entry->_hasSubSet)
{
i = SkipToNextSet(treasureList, i + 2); //next entry is the sublist's first entry so skip 2.
continue;
}
i++;
}
return (int)treasureList->size();
}
int CTreasureFactory::GenerateFromTypeOrWcid(CWeenieObject *parent, int destinationType, bool isRegenLocationType, uint32_t treasureTypeOrWcid, unsigned int ptid, float shade, const GeneratorProfile *profile)
{
int amountCreated = 0;
bool isTreasureType = false;
if (isRegenLocationType && (destinationType & Treasure_RegenLocationType))
isTreasureType = true;
else if (!isRegenLocationType && destinationType & Treasure_DestinationType)
isTreasureType = true;
if (isTreasureType)
{
if (PackableList<TreasureEquipment> *entry = g_pPortalDataEx->_treasureTableData._treasureEquipment.lookup(treasureTypeOrWcid))
{
//we're a list of items
SItemListCreationData creationData;
creationData.amountCreated = 0;
creationData.parent = parent;
creationData.destinationType = destinationType;
creationData.isRegenLocationType = isRegenLocationType;
creationData.treasureTypeOrWcid = treasureTypeOrWcid;
creationData.ptid = ptid;
creationData.shade = shade;
creationData.profile = profile;
creationData.accumulatedChance = 0.0;
creationData.diceRoll = Random::RollDice(0.0, 1.0);
ProcessList(entry, 0, creationData);
}
else if (_TreasureProfile->treasureTypeOverrides.find(treasureTypeOrWcid) != _TreasureProfile->treasureTypeOverrides.end())
{
CTreasureType *entry = &_TreasureProfile->treasureTypeOverrides.at(treasureTypeOrWcid);
return GenerateFromType(entry, parent, destinationType, isRegenLocationType, treasureTypeOrWcid, ptid, shade, profile);
}
else if (TreasureLoot *entry = g_pPortalDataEx->_treasureTableData.GetTreasureGenerationProfile(treasureTypeOrWcid))
{
//we're instructions for random treasure generation
//setup some default values
int tierId = entry->_tier;
CTreasureTier *tier = &_TreasureProfile->tiers[tierId];
if (tier == NULL)
return amountCreated;
float qualityModifier = entry->_lootQualityMod;
int maxTreasureAmount = entry->_itemMaxAmount + entry->_magicItemMaxAmount;
int maxMundaneAmount = entry->_mundaneItemMaxAmount;
float lootChance = 0.5f;
if (maxTreasureAmount == 1)
lootChance = 1.0f;
float mundaneLootChance = 0.5f;
if (maxMundaneAmount == 1)
mundaneLootChance = 1.0f;
CTreasureType treasureType;
treasureType.tier = tierId;
treasureType.maxTreasureAmount = maxTreasureAmount;
treasureType.lootChance = lootChance;
treasureType.maxMundaneAmount = maxMundaneAmount;
treasureType.mundaneLootChance = mundaneLootChance;
treasureType.qualityModifier = qualityModifier;
return GenerateFromType(&treasureType, parent, destinationType, isRegenLocationType, treasureTypeOrWcid, ptid, shade, profile);
}
}
else
{
//we're a wcid
if (CWeenieObject *newItem = g_pWeenieFactory->CreateWeenieByClassID(treasureTypeOrWcid))
{
g_pWeenieFactory->AddWeenieToDestination(newItem, parent, destinationType, isRegenLocationType, profile);
amountCreated++;
}
}
return amountCreated;
}
int CTreasureFactory::GenerateRareItem(CWeenieObject *parent, CWeenieObject *killer)
{
int amountCreated = 0;
uint32_t rareWcid = 0;
bool rareDropped = false;
bool realTimeRare = false;
double roll = getRandomDouble(1.0);
int tier = 1;
int tierInt = 0;
time_t t = chrono::system_clock::to_time_t(std::chrono::system_clock::now());
// Standard rare drop rate is 1 in 2,500 kills. This is for a second Real Time Rare drop system. You would get a dramatically increased rare drop chance (90%?) after a hitting a random number of seconds (up to 2 months) since last rare drop.
// https://asheron.wikia.com/wiki/Announcements_-_2010/04_-_Shedding_Skin
// Q: Were there any changes to the real time rare system?
// A: Real Time Rares work the same as they always have. It rolls a number between 1 second and 2 months worth of seconds. When that number is up you get an additional chance of finding a rare on any valid rare kill. That additional chance is very high.
// You can still only find one rare on any given kill but it's possible to find a normal rare when your Real Time Rare timer is up but you haven't found one yet.
if (g_pConfig->RealTimeRares() && killer->m_Qualities.GetInt(RARES_LOGIN_TIMESTAMP_INT, 0) <= t)
{
if (roll < 0.9)
{
int seconds = getRandomNumber(5184000);
t += seconds;
realTimeRare = true;
rareDropped = true;
killer->m_Qualities.SetInt(RARES_LOGIN_TIMESTAMP_INT, t);
}
}
if (!realTimeRare && roll < (0.0004 * g_pConfig->RareDropMultiplier()))
rareDropped = true;
if (rareDropped)
{
int tierRoll = getRandomNumber(1, 100000);
if (tierRoll > 10000)
tier = 1;
else if (tierRoll > 1000)
tier = 2;
else if (tierRoll > 80)
tier = 3;
else if (tierRoll > 33)
tier = 4;
else if (tierRoll > 18)
tier = 5;
else if (tierRoll > 0)
tier = 6;
if (!realTimeRare)
{
switch (tier)
{
case 6:
tierInt = RARES_TIER_SIX_INT;
case 7:
tierInt = RARES_TIER_SEVEN_INT; // We currently only have 6 tiers but apparently Turbine had plans for a 7th
default:
tierInt = RARES_TIER_ONE_INT + (tier - 1);
}
}
else
{
switch (tier)
{
case 6:
tierInt = RARES_TIER_SIX_LOGIN_INT;
case 7:
tierInt = RARES_TIER_SEVEN_LOGIN_INT; // We currently only have 6 tiers but apparently Turbine had plans for a 7th
default:
tierInt = RARES_TIER_ONE_LOGIN_INT + (tier - 1);
}
}
int rareCount = killer->m_Qualities.GetInt((STypeInt)tierInt, 0);
killer->m_Qualities.SetInt((STypeInt)tierInt, rareCount + 1);
if (CWeenieObject *newItem = GenerateRandomRareByTier(tier))
{
g_pWeenieFactory->AddWeenieToDestination(newItem, parent, Treasure_DestinationType, false);
amountCreated++;
g_pWorld->BroadcastLocal(killer->GetLandcell(), csprintf("%s has discovered the %s!", killer->GetName().c_str(), newItem->GetName().c_str()), LTT_SYSTEM_EVENT);
parent->m_Qualities.SetBool(CORPSE_GENERATED_RARE_BOOL, 1);
parent->EmitSound(149, 1.0, false);
RARE_LOG << killer->InqStringQuality(NAME_STRING, "") << "found" << newItem->GetName().c_str() << "they have found" << rareCount + 1 << "tier" << tier << "rares.";
}
}
return amountCreated;
}
int CTreasureFactory::GenerateFromType(CTreasureType *type, CWeenieObject * parent, int destinationType, bool isRegenLocationType, uint32_t treasureTypeOrWcid, unsigned int ptid, float shade, const GeneratorProfile * profile)
{
int amountCreated = 0;
int tierId = type->tier;
CTreasureTier *tier = &_TreasureProfile->tiers[type->tier];
if (tier == NULL)
return amountCreated;
int maxTreasureAmount = type->maxTreasureAmount;
int maxMundaneAmount = type->maxMundaneAmount;
float lootChance = type->lootChance;
float mundaneLootChance = type->mundaneLootChance;
float qualityModifier = type->qualityModifier;
if (parent->AsCorpse())
{
//we're a corpse
maxTreasureAmount = round(maxTreasureAmount * tier->lootAmountMultiplier);
lootChance = tier->lootChance;
mundaneLootChance = tier->miscLootChance;
int level = parent->InqIntQuality(LEVEL_INT, 0);
if (level >= tier->qualityLootLevelThreshold && qualityModifier < tier->qualityLootModifier)
qualityModifier = tier->qualityLootModifier;
}
else if (parent->AsContainer() && !parent->AsMonster())
{
//we're a container so apply container related stuff.
//Turbine did a bad job of converting chests to the new treasure profiles resulting in chests with loot that
//doesnt match the level of the area they are located in. This is a quick fix for that. A better fix would be
//to edit the chest generator tables themselves. But even then it would require changing some chest wcids
//because there are chests wcids that are uses both in newbie and high level areas.
int newTreasureTypeOrWcid = 0;
for (auto entry : _TreasureProfile->chestTreasureTypeReplacementTable)
{
if (treasureTypeOrWcid == entry.first)
{
newTreasureTypeOrWcid = entry.second;
break;
}
}
if (newTreasureTypeOrWcid != 0 && treasureTypeOrWcid != newTreasureTypeOrWcid)
{
treasureTypeOrWcid = newTreasureTypeOrWcid;
if (_TreasureProfile->treasureTypeOverrides.find(treasureTypeOrWcid) != _TreasureProfile->treasureTypeOverrides.end())
{
type = &_TreasureProfile->treasureTypeOverrides.at(treasureTypeOrWcid);
tierId = type->tier;
tier = &_TreasureProfile->tiers[type->tier];
if (tier == NULL)
return amountCreated;
qualityModifier = type->qualityModifier;
maxTreasureAmount = type->maxTreasureAmount;
if (maxTreasureAmount == 1)
maxTreasureAmount = 3;
if (qualityModifier < tier->qualityLootModifier)
qualityModifier = tier->qualityLootModifier;
}
else if (TreasureLoot *entry = g_pPortalDataEx->_treasureTableData.GetTreasureGenerationProfile(treasureTypeOrWcid))
{
tierId = entry->_tier;
tier = &_TreasureProfile->tiers[tierId];
if (tier == NULL)
return amountCreated;
qualityModifier = entry->_lootQualityMod;
maxTreasureAmount = entry->_itemMaxAmount + entry->_magicItemMaxAmount;
if (maxTreasureAmount == 1)
maxTreasureAmount = 3;
if (qualityModifier < tier->qualityLootModifier)
qualityModifier = tier->qualityLootModifier;
}
else
return amountCreated;
}
maxTreasureAmount = round(maxTreasureAmount * tier->chestLootAmountMultiplier);
lootChance = tier->chestLootChance;
//chests do not drop mundane items.
//mundaneLootChance = 0;
//maxMundaneAmount = 0;
}
for (int i = 0; i < maxTreasureAmount; i++)
{
if (getRandomNumberExclusive(100) < lootChance * 100)
{
if (CWeenieObject *newItem = GenerateTreasure(tierId, tier->GetRandomTreasureCategory(), qualityModifier))
{
g_pWeenieFactory->AddWeenieToDestination(newItem, parent, destinationType, isRegenLocationType, profile);
amountCreated++;
}
}
}
for (int i = 0; i < maxMundaneAmount; i++)
{
if (getRandomNumberExclusive(100) < mundaneLootChance * 100)
{
if (CWeenieObject *newItem = GenerateMundaneItem(tier))
{
g_pWeenieFactory->AddWeenieToDestination(newItem, parent, destinationType, isRegenLocationType, profile);
amountCreated++;
}
}
}
if (getRandomNumberExclusive(100) < tier->scrollLootChance * 100)
{
if (CWeenieObject *newItem = GenerateScroll(tier))
{
g_pWeenieFactory->AddWeenieToDestination(newItem, parent, destinationType, isRegenLocationType, profile);
amountCreated++;
}
}
if (getRandomNumberExclusive(100) < tier->petdeviceLootChance * 100)
{
if (CWeenieObject *newItem = GeneratePetDevice(tier))
{
int itemWorkmanship = getRandomNumber(tier->minWorkmanship, tier->maxWorkmanship, eRandomFormula::favorMid, 2, qualityModifier);
newItem->m_Qualities.SetInt(ITEM_WORKMANSHIP_INT, itemWorkmanship);
g_pWeenieFactory->AddWeenieToDestination(newItem, parent, destinationType, isRegenLocationType, profile);
amountCreated++;
}
}
return amountCreated;
}
CWeenieObject *CTreasureFactory::GenerateRandomRareByTier(int rareTier)
{
int rareWcid = 0;
if (_TreasureProfile->rareTiers.empty())
return NULL;
for (auto entry : _TreasureProfile->rareTiers)
{
if (entry.first == rareTier)
{
rareWcid = entry.second.rareTierWcids[getRandomNumberExclusive((int)entry.second.rareTierWcids.size())];
break;
}
}
if (rareWcid)
return g_pWeenieFactory->CreateWeenieByClassID(rareWcid);
return NULL;
}
CWeenieObject *CTreasureFactory::GenerateMundaneItem(CTreasureTier *tier)
{
int miscItemWcid = 0;
if (tier->miscItemsCategories.size() == 0)
return NULL;
std::string miscCategory = tier->miscItemsCategories[getRandomNumberExclusive((int)tier->miscItemsCategories.size())];
for (CTreasureProfileCategory category : _TreasureProfile->miscItems)
{
if (category.category == miscCategory)
{
if (!category.entries.empty())
miscItemWcid = category.entries[getRandomNumberExclusive((int)category.entries.size())].wcid;
break;
}
}
if (miscItemWcid)
return g_pWeenieFactory->CreateWeenieByClassID(miscItemWcid);
return NULL;
}
CWeenieObject *CTreasureFactory::GenerateScroll(CTreasureTier *tier)
{
int scrollWcid = 0;
if (tier->scrollCategories.size() == 0)
return NULL;
std::string scrollCategory = tier->scrollCategories[getRandomNumberExclusive((int)tier->scrollCategories.size())];
for (CTreasureProfileCategory category : _TreasureProfile->scrolls)
{
if (category.category == scrollCategory)
{
if (!category.entries.empty())
scrollWcid = category.entries[getRandomNumberExclusive((int)category.entries.size())].wcid;
break;
}
}
if (scrollWcid)
return g_pWeenieFactory->CreateWeenieByClassID(scrollWcid);
return NULL;
}
CWeenieObject *CTreasureFactory::GeneratePetDevice(CTreasureTier *tier)
{
int petdevicewcid = 0;
if (tier->petDeviceCategories.size() == 0)
return NULL;
std::string petdeviceCategory = tier->petDeviceCategories[getRandomNumberExclusive((int)tier->petDeviceCategories.size())];
for (CTreasureProfileCategory category : _TreasureProfile->petdevices)
{
if (category.category == petdeviceCategory)
{
if (!category.entries.empty())
petdevicewcid = category.entries[getRandomNumberExclusive((int)category.entries.size())].wcid;
break;
}
}
if (petdevicewcid)
{
CWeenieObject* newPetDevice = g_pWeenieFactory->CreateWeenieByClassID(petdevicewcid);
// GEAR_DAMAGE_INT - 307
if ((Random::GenInt(0, 99) % 2) == 1)
newPetDevice->m_Qualities.SetInt(GEAR_DAMAGE_INT, GeneratePetDeviceRating((float)tier->tierId));
// GEAR_DAMAGE_RESIST_INT - 308
if ((Random::GenInt(0, 99) % 2) == 1)
newPetDevice->m_Qualities.SetInt(GEAR_DAMAGE_RESIST_INT, GeneratePetDeviceRating((float)tier->tierId));
// GEAR_CRIT_INT - 313
if ((Random::GenInt(0, 99) % 2) == 1)
newPetDevice->m_Qualities.SetInt(GEAR_CRIT_INT, GeneratePetDeviceRating((float)tier->tierId));
// GEAR_CRIT_RESIST_INT - 314
if ((Random::GenInt(0, 99) % 2) == 1)
newPetDevice->m_Qualities.SetInt(GEAR_CRIT_RESIST_INT, GeneratePetDeviceRating((float)tier->tierId));
// GEAR_CRIT_DAMAGE_INT - 315
if ((Random::GenInt(0, 99) % 2) == 1)
newPetDevice->m_Qualities.SetInt(GEAR_CRIT_DAMAGE_INT, GeneratePetDeviceRating((float)tier->tierId));
// GEAR_CRIT_DAMAGE_RESIST_INT - 316
if ((Random::GenInt(0, 99) % 2) == 1)
newPetDevice->m_Qualities.SetInt(GEAR_CRIT_DAMAGE_RESIST_INT, GeneratePetDeviceRating((float)tier->tierId));
return newPetDevice;
}
return NULL;
}
uint32_t CTreasureFactory::GeneratePetDeviceRating(float tier)
{
uint32_t baseRating = Random::GenInt(1, 10);
if ((Random::GenFloat(0.0, 1.0)) > (.40 + (.02 * tier)))
{
baseRating += Random::GenInt(1, 10);
}
return baseRating;
}
CWeenieObject *CTreasureFactory::GenerateJunk()
{
int miscItemWcid = 0;
std::string miscCategory;
if (getRandomNumberExclusive(100) < _TreasureProfile->rareJunkChance * 100)
miscCategory = "rareJunk";
else
miscCategory = "commonJunk";
for (CTreasureProfileCategory category : _TreasureProfile->miscItems)
{
if (category.category == miscCategory)
{
if (!category.entries.empty())
miscItemWcid = category.entries[getRandomNumberExclusive((int)category.entries.size())].wcid;
break;
}
}
if (miscItemWcid)
return g_pWeenieFactory->CreateWeenieByClassID(miscItemWcid);
return NULL;
}
CWeenieObject *CTreasureFactory::GenerateTreasure(int tierId, eTreasureCategory treasureCategory, double qualityModifier)
{
if (!_TreasureProfile)
return NULL;
if (_TreasureProfile->tiers.find(tierId) == _TreasureProfile->tiers.end())
return NULL;
CTreasureTier *tier = &_TreasureProfile->tiers[tierId];
CTreasureProfileCategory *category = NULL;
switch (treasureCategory)
{
default:
case TreasureCategory_Undef:
case TreasureCategory_Unknown:
return NULL; //not implemented.
case TreasureCategory_PetDevices:
return GeneratePetDevice(tier);
case TreasureCategory_Junk:
return GenerateJunk();
case TreasureCategory_Scroll:
return GenerateScroll(tier);
case TreasureCategory_Mana_Stone:
case TreasureCategory_Consumable:
case TreasureCategory_Healer:
case TreasureCategory_Lockpick:
case TreasureCategory_Pea:
return GenerateMundaneItem(tier);
//return GenerateMundaneItemInferred(tierId, treasureCategory);
case TreasureCategory_Jewelry:
if (!_TreasureProfile->jewelry.empty())
category = &_TreasureProfile->jewelry[getRandomNumberExclusive((int)_TreasureProfile->jewelry.size())];
break;
case TreasureCategory_Caster:
if (!_TreasureProfile->casters.empty())
category = &_TreasureProfile->casters[getRandomNumberExclusive((int)_TreasureProfile->casters.size())];
break;
case TreasureCategory_Gem:
category = &_TreasureProfile->gemstones;
break;
case TreasureCategory_Clothing:
if (!_TreasureProfile->clothing.empty())
category = &_TreasureProfile->clothing[getRandomNumberExclusive((int)_TreasureProfile->clothing.size())];
break;
case TreasureCategory_Melee_Weapon:
if (!_TreasureProfile->meleeWeapons.empty())
category = &_TreasureProfile->meleeWeapons[getRandomNumberExclusive((int)_TreasureProfile->meleeWeapons.size())];
break;
case TreasureCategory_Missile_Weapon:
if (!_TreasureProfile->missileWeapons.empty())
category = &_TreasureProfile->missileWeapons[getRandomNumberExclusive((int)_TreasureProfile->missileWeapons.size())];
break;
case TreasureCategory_Armor:
if (!tier->rareArmorCategories.empty() && getRandomNumberExclusive(100) < tier->rareArmorChance * 100)
category = tier->rareArmorCategories[getRandomNumberExclusive((int)tier->rareArmorCategories.size())];
else if (!tier->commonArmorCategories.empty())
category = tier->commonArmorCategories[getRandomNumberExclusive((int)tier->commonArmorCategories.size())];
break;
}
if (!category || category->entries.size() == 0)
return NULL;
for (int i = 0; i < 10; i++) //max amount of retries for failed mutations.
{
sItemCreationInfo creationInfo;
creationInfo.qualityModifier = qualityModifier;
CItemTreasureProfileEntry *entry = &category->entries[getRandomNumberExclusive((int)category->entries.size())];
CWeenieObject *weenie;
if (weenie = g_pWeenieFactory->CreateWeenieByClassID(entry->wcid))
{
if (MutateItem(weenie, creationInfo, tier, category, entry))
return weenie;
else
SafeDelete(weenie);
}
}
return NULL;
}
bool CTreasureFactory::MutateItem(CWeenieObject *newItem, sItemCreationInfo &creationInfo, CTreasureTier *tier, CTreasureProfileCategory *category, CItemTreasureProfileEntry *entry)
{
ITEM_TYPE itemType = (ITEM_TYPE)newItem->InqIntQuality(ITEM_TYPE_INT, TYPE_UNDEF, true);
if (itemType != TYPE_MELEE_WEAPON &&
itemType != TYPE_MISSILE_WEAPON &&
itemType != TYPE_CASTER &&
itemType != TYPE_ARMOR &&
itemType != TYPE_CLOTHING &&
itemType != TYPE_JEWELRY &&
itemType != TYPE_GEM)
{
return false; // we only know how to mutate the above types.
}
INVENTORY_LOC coveredAreas = (INVENTORY_LOC)newItem->InqIntQuality(LOCATIONS_INT, NONE_LOC, TRUE);
if (itemType == TYPE_MELEE_WEAPON ||
itemType == TYPE_MISSILE_WEAPON ||
itemType == TYPE_CASTER || itemType == TYPE_GEM)
{
if (!category->wieldTiers.empty())
{
CWieldTier zeroReqWieldTier;
std::vector<CWieldTier> possibleWieldTiers;
CWieldTier *wieldTier;
for (CWieldTier wieldTierEntry : category->wieldTiers)
{
if (itemType == TYPE_MELEE_WEAPON &&
wieldTierEntry.weaponSkillRequired >= tier->minMeleeWeaponWieldTier &&
wieldTierEntry.weaponSkillRequired <= tier->maxMeleeWeaponWieldTier)
{
possibleWieldTiers.push_back(wieldTierEntry);
}
else if (itemType == TYPE_MISSILE_WEAPON &&
wieldTierEntry.weaponSkillRequired >= tier->minMissileWeaponWieldTier &&
wieldTierEntry.weaponSkillRequired <= tier->maxMissileWeaponWieldTier)
{
possibleWieldTiers.push_back(wieldTierEntry);
}
else if (itemType == TYPE_GEM)
{
possibleWieldTiers.push_back(wieldTierEntry);
}
else if (itemType == TYPE_CASTER &&
wieldTierEntry.weaponSkillRequired >= tier->minCasterWieldTier &&
wieldTierEntry.weaponSkillRequired <= tier->maxCasterWieldTier)
{
if (wieldTierEntry.weaponSkillRequired == 0)
zeroReqWieldTier = wieldTierEntry;
possibleWieldTiers.push_back(wieldTierEntry);
}
}
/*if (itemType == TYPE_CASTER)
{
for (int i = 0; i < 2; i++)
{
//increase chance of no weapon skill casters as that is only used for war magic.
possibleWieldTiers.push_back(zeroReqWieldTier);
}
}*/
if (possibleWieldTiers.size() == 0)
return false;
wieldTier = &possibleWieldTiers[(int)getRandomNumberExclusive((int)possibleWieldTiers.size(), eRandomFormula::equalDistribution, 2, 0)];
MutateWeapon(newItem, wieldTier, creationInfo, tier, category, entry);
if (itemType == TYPE_MELEE_WEAPON)
MutateMeleeWeapon(newItem, wieldTier, creationInfo, tier, category, entry);
else if (itemType == TYPE_MISSILE_WEAPON)
MutateMissileWeapon(newItem, wieldTier, creationInfo, tier, category, entry);
else if (itemType == TYPE_CASTER)
MutateCaster(newItem, wieldTier, creationInfo, tier, category, entry);
else if (itemType == TYPE_GEM)
MutateGem(newItem, wieldTier, creationInfo, tier, category, entry);
}
}
else if (itemType == TYPE_ARMOR || (itemType == TYPE_CLOTHING && (coveredAreas & HEAD_WEAR_LOC || coveredAreas & FOOT_WEAR_LOC || coveredAreas & HAND_WEAR_LOC)))
{
std::vector<CWieldTier> *wieldTiers;
if (!category->wieldTiers.empty())
wieldTiers = &category->wieldTiers;
else
wieldTiers = &_TreasureProfile->armor.wieldTiers;
if (wieldTiers->size() > 0)
{
std::vector<CWieldTier> possibleWieldTiers;
CWieldTier *wieldTier;
for (CWieldTier wieldTierEntry : *wieldTiers)
{
if ((wieldTierEntry.armorWieldTier >= tier->minArmorWieldTier && wieldTierEntry.armorWieldTier <= tier->maxArmorWieldTier) ||
(wieldTierEntry.meleeDefenseSkillRequired != 0 && wieldTierEntry.meleeDefenseSkillRequired >= tier->minMeleeWeaponWieldTier && wieldTierEntry.meleeDefenseSkillRequired <= tier->maxMeleeWeaponWieldTier) &&
(wieldTierEntry.missileDefenseSkillRequired != 0 && wieldTierEntry.missileDefenseSkillRequired >= tier->minArmorMissileWieldTier && wieldTierEntry.missileDefenseSkillRequired <= tier->maxArmorMissileWieldTier) &&
(wieldTierEntry.magicDefenseSkillRequired != 0 && wieldTierEntry.magicDefenseSkillRequired >= tier->minArmorMagicWieldTier && wieldTierEntry.magicDefenseSkillRequired <= tier->maxArmorMagicWieldTier))
{
if (coveredAreas & SHIELD_LOC)
{
//if both shieldMinArmorBonus and shieldMaxArmorBonus are 0 it means there's no shield in this wieldTier.
if (wieldTierEntry.minShieldArmorBonus != 0 || wieldTierEntry.maxShieldArmorBonus != 0)
possibleWieldTiers.push_back(wieldTierEntry);
}
else
possibleWieldTiers.push_back(wieldTierEntry);
}
}
if (possibleWieldTiers.size() == 0)
return false;
wieldTier = &possibleWieldTiers[getRandomNumberExclusive((int)possibleWieldTiers.size(), eRandomFormula::equalDistribution, 2, 0)];
MutateArmor(newItem, wieldTier, creationInfo, tier, category, entry);
}
}
int itemWorkmanship = getRandomNumber(tier->minWorkmanship, tier->maxWorkmanship, eRandomFormula::favorMid, 2, creationInfo.qualityModifier);
newItem->m_Qualities.SetInt(ITEM_WORKMANSHIP_INT, itemWorkmanship);
int tsysMutationDataInt = 0;
float materialValueMultiplier = 1.0;
float gemValueMultiplier = 1.0;
MaterialType material = MaterialType::Undef_MaterialType;
int gemCount = 0;
MaterialType gemType = Undef_MaterialType;
if (newItem->m_Qualities.InqInt(TSYS_MUTATION_DATA_INT, tsysMutationDataInt, TRUE, TRUE) && tsysMutationDataInt && itemType != TYPE_GEM)
{
uint32_t tsysMutationData = (uint32_t)tsysMutationDataInt;
BYTE spellCode = (tsysMutationData >> 24) & 0xFF;
BYTE colorCode = (tsysMutationData >> 16) & 0xFF;
BYTE gemCode = (tsysMutationData >> 8) & 0xFF;
BYTE materialCode = (tsysMutationData >> 0) & 0xFF;
std::vector<MaterialType> possibleMaterialsList;
if (!entry->possibleMaterials.empty())
{
for (auto possibleMaterial : entry->possibleMaterials)
{
if (possibleMaterial == MaterialType::Ceramic_MaterialType)
possibleMaterialsList.insert(possibleMaterialsList.end(), tier->materialsCeramic.begin(), tier->materialsCeramic.end());
if (possibleMaterial == MaterialType::Cloth_MaterialType)
possibleMaterialsList.insert(possibleMaterialsList.end(), tier->materialsCloth.begin(), tier->materialsCloth.end());
if (possibleMaterial == MaterialType::Gem_MaterialType)
possibleMaterialsList.insert(possibleMaterialsList.end(), tier->materialsGem.begin(), tier->materialsGem.end());
if (possibleMaterial == MaterialType::Leather_MaterialType)
possibleMaterialsList.insert(possibleMaterialsList.end(), tier->materialsLeather.begin(), tier->materialsLeather.end());
if (possibleMaterial == MaterialType::Metal_MaterialType)
possibleMaterialsList.insert(possibleMaterialsList.end(), tier->materialsMetal.begin(), tier->materialsMetal.end());
if (possibleMaterial == MaterialType::Stone_MaterialType)
possibleMaterialsList.insert(possibleMaterialsList.end(), tier->materialsStone.begin(), tier->materialsStone.end());
if (possibleMaterial == MaterialType::Wood_MaterialType)
possibleMaterialsList.insert(possibleMaterialsList.end(), tier->materialsWood.begin(), tier->materialsWood.end());
}
}
else if (!category->possibleMaterials.empty())
{ //if we do not have our own materials entry try the category
for (auto possibleMaterial : category->possibleMaterials)
{
if (possibleMaterial == MaterialType::Ceramic_MaterialType)
possibleMaterialsList.insert(possibleMaterialsList.end(), tier->materialsCeramic.begin(), tier->materialsCeramic.end());
if (possibleMaterial == MaterialType::Cloth_MaterialType)
possibleMaterialsList.insert(possibleMaterialsList.end(), tier->materialsCloth.begin(), tier->materialsCloth.end());
if (possibleMaterial == MaterialType::Gem_MaterialType)
possibleMaterialsList.insert(possibleMaterialsList.end(), tier->materialsGem.begin(), tier->materialsGem.end());
if (possibleMaterial == MaterialType::Leather_MaterialType)
possibleMaterialsList.insert(possibleMaterialsList.end(), tier->materialsLeather.begin(), tier->materialsLeather.end());
if (possibleMaterial == MaterialType::Metal_MaterialType)
possibleMaterialsList.insert(possibleMaterialsList.end(), tier->materialsMetal.begin(), tier->materialsMetal.end());
if (possibleMaterial == MaterialType::Stone_MaterialType)
possibleMaterialsList.insert(possibleMaterialsList.end(), tier->materialsStone.begin(), tier->materialsStone.end());
if (possibleMaterial == MaterialType::Wood_MaterialType)
possibleMaterialsList.insert(possibleMaterialsList.end(), tier->materialsWood.begin(), tier->materialsWood.end());
}
}
material = possibleMaterialsList[getRandomNumberExclusive(possibleMaterialsList.size())];
if (material != MaterialType::Undef_MaterialType && itemType != TYPE_GEM)
{
newItem->m_Qualities.SetInt(MATERIAL_TYPE_INT, material);
int ptid = g_pPortalDataEx->_treasureTableData.RollPaletteTemplateIDFromMaterialAndColorCode(material, colorCode);
if (ptid != 0)
{
newItem->m_Qualities.SetInt(PALETTE_TEMPLATE_INT, ptid);
newItem->m_Qualities.SetFloat(SHADE_FLOAT, getRandomDouble(1.0));
/*newItem->m_Qualities.SetFloat(SHADE2_FLOAT, getRandomDouble(1.0));
newItem->m_Qualities.SetFloat(SHADE3_FLOAT, getRandomDouble(1.0));
newItem->m_Qualities.SetFloat(SHADE4_FLOAT, getRandomDouble(1.0));*/
}
}
//The following code generated materials from the inferred data
//MaterialType materialCategory = g_pPortalDataEx->_treasureTableData.RollBaseMaterialFromMaterialCode(materialCode, tier->tierId);
//if (materialCategory != MaterialType::Undef_MaterialType)
//{
// material = g_pPortalDataEx->_treasureTableData.RollMaterialFromBaseMaterial(materialCategory, tier->tierId);
// if (material != MaterialType::Undef_MaterialType)
// {
// newItem->m_Qualities.SetInt(MATERIAL_TYPE_INT, material);
// //materialValueMultiplier = *g_pPortalDataEx->_treasureTableData._materialValueAddedPossibly.lookup(material);
// int ptid = g_pPortalDataEx->_treasureTableData.RollPaletteTemplateIDFromMaterialAndColorCode(material, colorCode);
// if (ptid != 0)
// {
// newItem->m_Qualities.SetInt(PALETTE_TEMPLATE_INT, ptid);
// newItem->m_Qualities.SetFloat(SHADE_FLOAT, getRandomDouble(1.0));
// newItem->m_Qualities.SetFloat(SHADE2_FLOAT, getRandomDouble(1.0));
// newItem->m_Qualities.SetFloat(SHADE3_FLOAT, getRandomDouble(1.0));
// newItem->m_Qualities.SetFloat(SHADE4_FLOAT, getRandomDouble(1.0));
// }
// }
//}
}
int maxGemCount = _TreasureProfile->workmanshipProperties[itemWorkmanship].maxGemCount;
if (maxGemCount > 0 && itemType != TYPE_GEM)
{
if (getRandomNumberExclusive(100) < _TreasureProfile->workmanshipProperties[itemWorkmanship].gemChance * 100)
{
gemCount = getRandomNumber(1, maxGemCount, eRandomFormula::favorLow, 2, 0);
int gemTypeIndex = getRandomNumberExclusive((int)tier->materialsGem.size() - 1); //-1 because ivory is the last gem type in the profile and we don't want that
gemType = tier->materialsGem[gemTypeIndex];
//gemType = g_pPortalDataEx->_treasureTableData.RollMaterialFromBaseMaterial(Gem_MaterialType, tier->tierId);
//gemValueMultiplier = *g_pPortalDataEx->_treasureTableData._materialValueAddedPossibly.lookup(gemType);
newItem->m_Qualities.SetInt(GEM_COUNT_INT, gemCount);
newItem->m_Qualities.SetInt(GEM_TYPE_INT, gemType);
}
}
//make sure we clean the item of any existing spells
if (newItem->m_Qualities._spell_book != NULL)
newItem->m_Qualities._spell_book->ClearSpells();
AddSpells(newItem, creationInfo, tier, category, entry);
int baseValue = newItem->InqIntQuality(VALUE_INT, 0, TRUE);
float valueCalc = baseValue;
//valueCalc += baseValue * materialValueMultiplier;
//valueCalc += baseValue * (gemValueMultiplier * gemCount);
valueCalc *= _TreasureProfile->workmanshipProperties[itemWorkmanship].valueMultiplier;
valueCalc *= _TreasureProfile->materialProperties[material].valueMultiplier;
valueCalc += _TreasureProfile->materialProperties[gemType].gemValue * gemCount;
if (creationInfo.isMagical)
valueCalc += creationInfo.totalPower * 5;
if (itemType == TYPE_MELEE_WEAPON)
{
DAMAGE_TYPE damageType = (DAMAGE_TYPE)newItem->InqIntQuality(DAMAGE_TYPE_INT, DAMAGE_TYPE::UNDEF_DAMAGE_TYPE);
if (damageType == ACID_DAMAGE_TYPE || damageType == FIRE_DAMAGE_TYPE || damageType == COLD_DAMAGE_TYPE || damageType == ELECTRIC_DAMAGE_TYPE)
valueCalc += 0.25f * baseValue;
}
else if (itemType == TYPE_MISSILE_WEAPON)
{
int elementalDamageBonus = (DAMAGE_TYPE)newItem->InqIntQuality(ELEMENTAL_DAMAGE_BONUS_INT, 0, TRUE);
if (elementalDamageBonus > 0)
valueCalc += 0.25f * baseValue;
}
else if (itemType == TYPE_CASTER)
{
double elementalDamageMod = newItem->InqFloatQuality(ELEMENTAL_DAMAGE_MOD_FLOAT, 0.0, TRUE);
if (elementalDamageMod > 0)
valueCalc += 0.25f * baseValue;
}
int value = round(getRandomDouble(valueCalc * 0.9, valueCalc * 1.1));
newItem->m_Qualities.SetInt(VALUE_INT, value);
std::string longDesc = "";
if (_TreasureProfile->extraMutations)
{
if (itemType == TYPE_CASTER || itemType == TYPE_MELEE_WEAPON || itemType == TYPE_MISSILE_WEAPON)
{
float critChance = 0.1f;
float critMultiplier = 1.0f;
if (itemType == TYPE_CASTER)
{
critChance = 0.05f;
critMultiplier = 0.5f;
}
critChance += (critChance * newItem->InqFloatQuality(CRITICAL_FREQUENCY_FLOAT, 0.0));
critMultiplier += newItem->InqFloatQuality(CRITICAL_MULTIPLIER_FLOAT, 0.0);
if (!longDesc.empty())
longDesc += "\n";
longDesc += csprintf("Critical Hit Chance: %.1f%%", critChance * 100.0);
if (!longDesc.empty())
longDesc += "\n";
longDesc += csprintf("Critical Hit Extra Damage: %d%%", (int)round(critMultiplier * 100.0));
if (double slayerMod = newItem->InqFloatQuality(SLAYER_DAMAGE_BONUS_FLOAT, 0))
{
if (!longDesc.empty())
longDesc += "\n";
longDesc += csprintf("Slayer Extra Damage: %d%%", (int)round(slayerMod * 100.0));
}
if (gemCount > 0)
{
std::string gemName = _TreasureProfile->materialProperties[gemType].gemName;
if (gemCount > 1)
{
if (!_TreasureProfile->materialProperties[gemType].gemPluralName.empty())
gemName = _TreasureProfile->materialProperties[gemType].gemPluralName;
else
gemName += "s";
}
if (!longDesc.empty())
longDesc += "\n";
longDesc += csprintf("Gem: %d %s", gemCount, gemName.c_str());
}
}
if (!longDesc.empty())
longDesc += "\n";
longDesc += csprintf("Tier: %d", tier->tierId);
if (creationInfo.qualityModifier > 0)
longDesc += "(Quality)";
newItem->m_Qualities.SetString(LONG_DESC_STRING, longDesc);
}
else if (gemCount > 0)
{
longDesc = newItem->InqStringQuality(NAME_STRING, "");
newItem->m_Qualities.SetString(LONG_DESC_STRING, longDesc);
newItem->m_Qualities.SetInt(APPRAISAL_LONG_DESC_DECORATION_INT, (prependWorkmanship | prependMaterial | appendGemInfo));
}
return true;
}
void CTreasureFactory::MutateWeapon(CWeenieObject *newItem, CWieldTier *wieldTier, sItemCreationInfo &creationInfo, CTreasureTier *tier, CTreasureProfileCategory *category, CItemTreasureProfileEntry *entry)
{
if (entry->weaponSkill != UNDEF_SKILL)
{
//this allow overwriting a weapon's default weapon skill.
newItem->m_Qualities.SetInt(WEAPON_SKILL_INT, entry->weaponSkill);
}
if (wieldTier->weaponSkillRequired > 0)
{
newItem->m_Qualities.SetInt(WIELD_REQUIREMENTS_INT, eWieldRequirements::baseSkill);
newItem->m_Qualities.SetInt(WIELD_SKILLTYPE_INT, newItem->InqIntQuality(WEAPON_SKILL_INT, UNDEF_SKILL));
newItem->m_Qualities.SetInt(WIELD_DIFFICULTY_INT, wieldTier->weaponSkillRequired);
}
if (wieldTier->maxMagicDefenseBonus > 0 && getRandomNumberExclusive(100) < wieldTier->magicDefenseBonusChance * 100 * (1 + (creationInfo.qualityModifier * 2)))
newItem->m_Qualities.SetFloat(WEAPON_MAGIC_DEFENSE_FLOAT, round(getRandomDouble(1, 1 + (wieldTier->maxMagicDefenseBonus / 100), eRandomFormula::favorMid, 2, creationInfo.qualityModifier), 2));
if (wieldTier->maxMissileDefenseBonus > 0 && getRandomNumberExclusive(100) < wieldTier->missileDefenseBonusChance * 100 * (1 + (creationInfo.qualityModifier * 2)))
newItem->m_Qualities.SetFloat(WEAPON_MISSILE_DEFENSE_FLOAT, round(getRandomDouble(1, 1 + (wieldTier->maxMissileDefenseBonus / 100), eRandomFormula::favorMid, 2, creationInfo.qualityModifier), 2));
if (wieldTier->maxMeleeDefenseBonus > 0 && getRandomNumberExclusive(100) < wieldTier->meleeDefenseBonusChance * 100 * (1 + (creationInfo.qualityModifier * 2)))
newItem->m_Qualities.SetFloat(WEAPON_DEFENSE_FLOAT, round(getRandomDouble(1 + (wieldTier->minMeleeDefenseBonus / 100), 1 + (wieldTier->maxMeleeDefenseBonus / 100), eRandomFormula::favorMid, 2, creationInfo.qualityModifier), 2));
if (_TreasureProfile->extraMutations)
{
int propertyCount = 0;
if (propertyCount < wieldTier->maxPropertyAmount && !wieldTier->slayer_Types.empty() && wieldTier->slayer_MaxDamageBonus > 0.0 && getRandomNumberExclusive(100) < wieldTier->slayer_Chance * 100 * (1 + (creationInfo.qualityModifier * 2)))
{
propertyCount++;
CreatureType slayerType = wieldTier->slayer_Types[getRandomNumberExclusive((int)wieldTier->slayer_Types.size())];
newItem->m_Qualities.SetInt(SLAYER_CREATURE_TYPE_INT, slayerType);
newItem->m_Qualities.SetFloat(SLAYER_DAMAGE_BONUS_FLOAT, round(getRandomDouble(wieldTier->slayer_MinDamageBonus, wieldTier->slayer_MaxDamageBonus, eRandomFormula::favorLow, 1.5, creationInfo.qualityModifier), 2));
}
if (propertyCount < wieldTier->maxPropertyAmount && getRandomNumberExclusive(100) < wieldTier->crushingBlow_Chance * 100 * (1 + (creationInfo.qualityModifier * 2)))
{
propertyCount++;
newItem->m_Qualities.SetFloat(CRITICAL_MULTIPLIER_FLOAT, round(getRandomDouble(wieldTier->crushingBlow_MinCriticalMultiplier, wieldTier->crushingBlow_MaxCriticalMultiplier, eRandomFormula::favorLow, 1.5, creationInfo.qualityModifier), 2));
}
if (propertyCount < wieldTier->maxPropertyAmount && getRandomNumberExclusive(100) < wieldTier->bitingStrike_Chance * 100 * (1 + (creationInfo.qualityModifier * 2)))
{
propertyCount++;
newItem->m_Qualities.SetFloat(CRITICAL_FREQUENCY_FLOAT, round(getRandomDouble(wieldTier->bitingStrike_MinCriticalFrequency, wieldTier->bitingStrike_MaxCriticalFrequency, eRandomFormula::favorLow, 1.5, creationInfo.qualityModifier), 2));
}
}
}
void CTreasureFactory::MutateMeleeWeapon(CWeenieObject *newItem, CWieldTier *wieldTier, sItemCreationInfo &creationInfo, CTreasureTier *tier, CTreasureProfileCategory *category, CItemTreasureProfileEntry *entry)
{
newItem->m_Qualities.SetInt(DAMAGE_INT, newItem->InqIntQuality(DAMAGE_INT, 0, TRUE) + getRandomNumber(wieldTier->minDamageBonus, wieldTier->maxDamageBonus, eRandomFormula::favorMid, 2, creationInfo.qualityModifier));
newItem->m_Qualities.SetFloat(DAMAGE_VARIANCE_FLOAT, round(getRandomDouble(1.f - wieldTier->highVariance, 1.f - wieldTier->lowVariance, eRandomFormula::favorMid, 2, creationInfo.qualityModifier), 2));
if (wieldTier->slowSpeedMod > 0 && wieldTier->fastSpeedMod > 0)
{
int weaponTime = newItem->InqIntQuality(WEAPON_TIME_INT, 50, TRUE);
double weaponTimeMod = getRandomDouble(wieldTier->fastSpeedMod, wieldTier->slowSpeedMod, eRandomFormula::favorHigh, 2, creationInfo.qualityModifier);
newItem->m_Qualities.SetInt(WEAPON_TIME_INT, round(weaponTime * weaponTimeMod));
}
if (wieldTier->maxAttackSkillBonus > 0 && getRandomNumberExclusive(100) < wieldTier->attackSkillBonusChance * 100 * (1 + (creationInfo.qualityModifier * 2)))
newItem->m_Qualities.SetFloat(WEAPON_OFFENSE_FLOAT, round(getRandomDouble(1 + (wieldTier->minAttackSkillBonus / 100), 1 + (wieldTier->maxAttackSkillBonus / 100), eRandomFormula::favorMid, 2, creationInfo.qualityModifier), 2));
if (!entry->elementalVariants.empty())
{
if (getRandomNumberExclusive(100) < wieldTier->elementalChance * 100)
{
int variantId = entry->elementalVariants[getRandomNumberExclusive((int)entry->elementalVariants.size())];
CWeenieDefaults *weenieDefs = g_pWeenieFactory->GetWeenieDefaults(variantId);
// set wcid to that of the elemental variant.
newItem->m_Qualities.id = weenieDefs->m_Qualities.id;
if (weenieDefs == NULL)
return;
int elementalType;
weenieDefs->m_Qualities.InqInt(DAMAGE_TYPE_INT, elementalType, TRUE);
uint32_t setup;
if (weenieDefs->m_Qualities.InqDataID(SETUP_DID, setup))
newItem->m_Qualities.SetDataID(SETUP_DID, setup);
if (weenieDefs->m_WCID == 3750) //acid battleaxe has the wrong graphics
newItem->m_Qualities.SetDataID(SETUP_DID, 33555690);
switch ((DAMAGE_TYPE)elementalType)
{
case DAMAGE_TYPE::ACID_DAMAGE_TYPE:
newItem->m_Qualities.SetInt(DAMAGE_TYPE_INT, ACID_DAMAGE_TYPE);
newItem->m_Qualities.SetInt(UI_EFFECTS_INT, UI_EFFECT_TYPE::UI_EFFECT_ACID);
newItem->m_Qualities.SetString(NAME_STRING, "Acid " + newItem->m_Qualities.GetString(NAME_STRING, ""));
break;
case DAMAGE_TYPE::COLD_DAMAGE_TYPE:
newItem->m_Qualities.SetInt(DAMAGE_TYPE_INT, COLD_DAMAGE_TYPE);
newItem->m_Qualities.SetInt(UI_EFFECTS_INT, UI_EFFECT_TYPE::UI_EFFECT_FROST);
newItem->m_Qualities.SetString(NAME_STRING, "Frost " + newItem->m_Qualities.GetString(NAME_STRING, ""));
break;
case DAMAGE_TYPE::FIRE_DAMAGE_TYPE:
newItem->m_Qualities.SetInt(DAMAGE_TYPE_INT, FIRE_DAMAGE_TYPE);
newItem->m_Qualities.SetInt(UI_EFFECTS_INT, UI_EFFECT_TYPE::UI_EFFECT_FIRE);
newItem->m_Qualities.SetString(NAME_STRING, "Flaming " + newItem->m_Qualities.GetString(NAME_STRING, ""));
break;
case DAMAGE_TYPE::ELECTRIC_DAMAGE_TYPE:
newItem->m_Qualities.SetInt(DAMAGE_TYPE_INT, ELECTRIC_DAMAGE_TYPE);
newItem->m_Qualities.SetInt(UI_EFFECTS_INT, UI_EFFECT_TYPE::UI_EFFECT_LIGHTNING);
newItem->m_Qualities.SetString(NAME_STRING, "Lightning " + newItem->m_Qualities.GetString(NAME_STRING, ""));
break;
}
}
}
}
void CTreasureFactory::MutateMissileWeapon(CWeenieObject *newItem, CWieldTier *wieldTier, sItemCreationInfo &creationInfo, CTreasureTier *tier, CTreasureProfileCategory *category, CItemTreasureProfileEntry *entry)
{
newItem->m_Qualities.SetFloat(DAMAGE_MOD_FLOAT, round(newItem->InqFloatQuality(DAMAGE_MOD_FLOAT, 1.0, TRUE) + getRandomDouble(wieldTier->minDamageModBonus, wieldTier->maxDamageModBonus, eRandomFormula::favorMid, 2, creationInfo.qualityModifier), 2));
if (wieldTier->slowSpeedMod > 0 && wieldTier->fastSpeedMod > 0)
{
int weaponTime = newItem->InqIntQuality(WEAPON_TIME_INT, 50, TRUE);
double weaponTimeMod = getRandomDouble(wieldTier->fastSpeedMod, wieldTier->slowSpeedMod, eRandomFormula::favorHigh, 2, creationInfo.qualityModifier);
newItem->m_Qualities.SetInt(WEAPON_TIME_INT, round(weaponTime * weaponTimeMod));
}
if (wieldTier->maxElementalDamageBonus > 0)
{
int elementalDamage = getRandomNumber(wieldTier->minElementalDamageBonus, wieldTier->maxElementalDamageBonus, eRandomFormula::favorMid, 2, 0);
if (elementalDamage > 0)
{
eElements elementalType = (eElements)getRandomNumber(1, 3);
if (getRandomNumberExclusive(100) < wieldTier->elementalChance * 100)
elementalType = (eElements)getRandomNumber(4, 7);
newItem->m_Qualities.SetInt(ELEMENTAL_DAMAGE_BONUS_INT, elementalDamage);
}
}
}
void CTreasureFactory::MutateCaster(CWeenieObject *newItem, CWieldTier *wieldTier, sItemCreationInfo &creationInfo, CTreasureTier *tier, CTreasureProfileCategory *category, CItemTreasureProfileEntry *entry)
{
bool hasRequirement1 = false;
if (wieldTier->weaponSkillRequired > 0 && wieldTier->maxElementalDamageMod > 0)
{
std::vector<STypeSkill> possibleRequirements;
possibleRequirements.push_back(WAR_MAGIC_SKILL);
possibleRequirements.push_back(VOID_MAGIC_SKILL);
hasRequirement1 = true;
if (newItem->m_Qualities.GetInt(DAMAGE_TYPE_INT, 0) == 1024) // void
{
creationInfo.favoredMagicSchoolMasterySpell = _TreasureProfile->spells[_TreasureProfile->spellNameToIdMap["Void Magic Mastery"]];
creationInfo.otherMagicSchoolMasterySpells.push_back(_TreasureProfile->spells[_TreasureProfile->spellNameToIdMap["Creature Enchantment Mastery"]]);
creationInfo.otherMagicSchoolMasterySpells.push_back(_TreasureProfile->spells[_TreasureProfile->spellNameToIdMap["Item Enchantment Mastery"]]);
creationInfo.otherMagicSchoolMasterySpells.push_back(_TreasureProfile->spells[_TreasureProfile->spellNameToIdMap["Life Magic Mastery"]]);
creationInfo.otherMagicSchoolMasterySpells.push_back(_TreasureProfile->spells[_TreasureProfile->spellNameToIdMap["War Magic Mastery"]]);
newItem->m_Qualities.SetInt(WIELD_SKILLTYPE_INT, VOID_MAGIC_SKILL);
}
else // war
{
creationInfo.favoredMagicSchoolMasterySpell = _TreasureProfile->spells[_TreasureProfile->spellNameToIdMap["War Magic Mastery"]];
creationInfo.otherMagicSchoolMasterySpells.push_back(_TreasureProfile->spells[_TreasureProfile->spellNameToIdMap["Creature Enchantment Mastery"]]);
creationInfo.otherMagicSchoolMasterySpells.push_back(_TreasureProfile->spells[_TreasureProfile->spellNameToIdMap["Item Enchantment Mastery"]]);
creationInfo.otherMagicSchoolMasterySpells.push_back(_TreasureProfile->spells[_TreasureProfile->spellNameToIdMap["Life Magic Mastery"]]);
creationInfo.otherMagicSchoolMasterySpells.push_back(_TreasureProfile->spells[_TreasureProfile->spellNameToIdMap["Void Magic Mastery"]]);
newItem->m_Qualities.SetInt(WIELD_SKILLTYPE_INT, WAR_MAGIC_SKILL);
}
double elementalDamageMod = round(getRandomDouble(wieldTier->minElementalDamageMod, wieldTier->maxElementalDamageMod, eRandomFormula::favorMid, 2, 0), 2);
elementalDamageMod = 1.0 + (elementalDamageMod / 100.0);
eElements elementalType = (eElements)getRandomNumber(4, 8);
if (getRandomNumberExclusive(100) < wieldTier->elementalChance * 100)
elementalType = (eElements)getRandomNumber(1, 3);
newItem->m_Qualities.SetFloat(ELEMENTAL_DAMAGE_MOD_FLOAT, elementalDamageMod);
}
if (wieldTier->maxManaConversionBonus > 0 && getRandomNumberExclusive(100) < wieldTier->manaConversionBonusChance * 100 * (1 + (creationInfo.qualityModifier * 2)))
{
double manaConversionMod = round(getRandomDouble(wieldTier->minManaConversionBonus / 100, wieldTier->maxManaConversionBonus / 100, eRandomFormula::favorMid, 2, creationInfo.qualityModifier), 2);
if (manaConversionMod > 0)
newItem->m_Qualities.SetFloat(MANA_CONVERSION_MOD_FLOAT, manaConversionMod);
}
if (wieldTier->maxMeleeDefenseBonus > 0 && getRandomNumberExclusive(100) < wieldTier->meleeDefenseBonusChance * 100 * (1 + (creationInfo.qualityModifier * 2)))
{
double meleeMod = round(getRandomDouble(wieldTier->minMeleeDefenseBonus / 100, wieldTier->maxMeleeDefenseBonus / 100, eRandomFormula::favorMid, 2, creationInfo.qualityModifier), 2);
if (meleeMod > 0)
newItem->m_Qualities.SetFloat(WEAPON_DEFENSE_FLOAT, 1.0 + meleeMod);
}
if (wieldTier->minLevel > 0)
{
if (hasRequirement1)
{
newItem->m_Qualities.SetInt(WIELD_REQUIREMENTS_2_INT, eWieldRequirements::level);
newItem->m_Qualities.SetInt(WIELD_SKILLTYPE_2_INT, 1);
newItem->m_Qualities.SetInt(WIELD_DIFFICULTY_2_INT, wieldTier->minLevel);
}
else
{
newItem->m_Qualities.SetInt(WIELD_REQUIREMENTS_INT, eWieldRequirements::level);
newItem->m_Qualities.SetInt(WIELD_SKILLTYPE_INT, 1);
newItem->m_Qualities.SetInt(WIELD_DIFFICULTY_INT, wieldTier->minLevel);
}
}
}
void CTreasureFactory::MutateGem(CWeenieObject *newItem, CWieldTier *wieldTier, sItemCreationInfo &creationInfo, CTreasureTier *tier, CTreasureProfileCategory *category, CItemTreasureProfileEntry *entry)
{
if (!entry->elementalVariants.empty())
{
int variantId = entry->elementalVariants[getRandomNumberExclusive((int)entry->elementalVariants.size())];
CWeenieDefaults *weenieDefs = g_pWeenieFactory->GetWeenieDefaults(variantId);
if (weenieDefs == NULL)
return;
else
{
g_pWeenieFactory->ApplyWeenieDefaults(newItem, variantId);
newItem->m_Qualities.SetInt(ITEM_USEABLE_INT, 8);
newItem->m_Qualities.m_WeenieType = 38;
}
}
}
void CTreasureFactory::MutateArmor(CWeenieObject *newItem, CWieldTier *wieldTier, sItemCreationInfo &creationInfo, CTreasureTier *tier, CTreasureProfileCategory *category, CItemTreasureProfileEntry *entry)
{
int encumbValue = newItem->InqIntQuality(ENCUMB_VAL_INT, 0, TRUE);
if (encumbValue)
{
encumbValue = round(encumbValue * getRandomDouble(wieldTier->minBurdenMod, wieldTier->maxBurdenMod, eRandomFormula::favorMid, 1.4, -creationInfo.qualityModifier));
newItem->m_Qualities.SetInt(ENCUMB_VAL_INT, encumbValue);
}
double resistanceAcid = newItem->InqFloatQuality(ARMOR_MOD_VS_ACID_FLOAT, 0.0, true);
double resistanceElectric = newItem->InqFloatQuality(ARMOR_MOD_VS_ELECTRIC_FLOAT, 0.0, true);
double resistanceFire = newItem->InqFloatQuality(ARMOR_MOD_VS_FIRE_FLOAT, 0.0, true);
double resistanceCold = newItem->InqFloatQuality(ARMOR_MOD_VS_COLD_FLOAT, 0.0, true);
double resistanceSlash = newItem->InqFloatQuality(ARMOR_MOD_VS_SLASH_FLOAT, 0.0, true);
double resistancePierce = newItem->InqFloatQuality(ARMOR_MOD_VS_PIERCE_FLOAT, 0.0, true);
double resistanceBludgeon = newItem->InqFloatQuality(ARMOR_MOD_VS_BLUDGEON_FLOAT, 0.0, true);
resistanceAcid = min(resistanceAcid + (getRandomNumber(1, 2, eRandomFormula::favorLow, 1.4, creationInfo.qualityModifier) / 10), 2.0);
resistanceElectric = min(resistanceElectric + (getRandomNumber(1, 2, eRandomFormula::favorLow, 1.4, creationInfo.qualityModifier) / 10), 2.0);
resistanceFire = min(resistanceFire + (getRandomNumber(1, 2, eRandomFormula::favorLow, 1.4, creationInfo.qualityModifier) / 10), 2.0);
resistanceCold = min(resistanceCold + (getRandomNumber(1, 2, eRandomFormula::favorLow, 1.4, creationInfo.qualityModifier) / 10), 2.0);
resistanceSlash = min(resistanceSlash + (getRandomNumber(1, 2, eRandomFormula::favorLow, 1.4, creationInfo.qualityModifier) / 10), 2.0);
resistancePierce = min(resistancePierce + (getRandomNumber(1, 2, eRandomFormula::favorLow, 1.4, creationInfo.qualityModifier) / 10), 2.0);
resistanceBludgeon = min(resistanceBludgeon + (getRandomNumber(1, 2, eRandomFormula::favorLow, 1.4, creationInfo.qualityModifier) / 10), 2.0);
if (getRandomNumberExclusive(100) < 25 * (1 + (creationInfo.qualityModifier * 2)))
newItem->m_Qualities.SetFloat(ARMOR_MOD_VS_ACID_FLOAT, resistanceAcid);
if (getRandomNumberExclusive(100) < 25 * (1 + (creationInfo.qualityModifier * 2)))
newItem->m_Qualities.SetFloat(ARMOR_MOD_VS_ELECTRIC_FLOAT, resistanceElectric);
if (getRandomNumberExclusive(100) < 25 * (1 + (creationInfo.qualityModifier * 2)))
newItem->m_Qualities.SetFloat(ARMOR_MOD_VS_FIRE_FLOAT, resistanceFire);
if (getRandomNumberExclusive(100) < 25 * (1 + (creationInfo.qualityModifier * 2)))
newItem->m_Qualities.SetFloat(ARMOR_MOD_VS_COLD_FLOAT, resistanceCold);
if (getRandomNumberExclusive(100) < 25 * (1 + (creationInfo.qualityModifier * 2)))
newItem->m_Qualities.SetFloat(ARMOR_MOD_VS_SLASH_FLOAT, resistanceSlash);
if (getRandomNumberExclusive(100) < 25 * (1 + (creationInfo.qualityModifier * 2)))
newItem->m_Qualities.SetFloat(ARMOR_MOD_VS_PIERCE_FLOAT, resistancePierce);
if (getRandomNumberExclusive(100) < 25 * (1 + (creationInfo.qualityModifier * 2)))
newItem->m_Qualities.SetFloat(ARMOR_MOD_VS_BLUDGEON_FLOAT, resistanceBludgeon);
INVENTORY_LOC coveredAreas = (INVENTORY_LOC)newItem->InqIntQuality(LOCATIONS_INT, NONE_LOC, TRUE);
int armorLevel = newItem->m_Qualities.GetInt(ARMOR_LEVEL_INT, 0);
if (coveredAreas & SHIELD_LOC)
armorLevel += getRandomNumber(wieldTier->minShieldArmorBonus, wieldTier->maxShieldArmorBonus, eRandomFormula::favorMid, 1.4, creationInfo.qualityModifier);
else
armorLevel += getRandomNumber(wieldTier->minArmorBonus, wieldTier->maxArmorBonus, eRandomFormula::favorMid, 1.4, creationInfo.qualityModifier);
newItem->m_Qualities.SetInt(ARMOR_LEVEL_INT, armorLevel);
std::vector<STypeSkill> possibleRequirementTypes;
if (wieldTier->meleeDefenseSkillRequired != 0)
possibleRequirementTypes.push_back(STypeSkill::MELEE_DEFENSE_SKILL);
if (wieldTier->missileDefenseSkillRequired != 0)
possibleRequirementTypes.push_back(STypeSkill::MISSILE_DEFENSE_SKILL);
if (wieldTier->magicDefenseSkillRequired != 0)
possibleRequirementTypes.push_back(STypeSkill::MAGIC_DEFENSE_SKILL);
bool hasRequirement1 = false;
if (!possibleRequirementTypes.empty())
{
STypeSkill requirementType = possibleRequirementTypes[getRandomNumberExclusive((int)possibleRequirementTypes.size())];
newItem->m_Qualities.SetInt(WIELD_REQUIREMENTS_INT, eWieldRequirements::baseSkill);
newItem->m_Qualities.SetInt(WIELD_SKILLTYPE_INT, requirementType);
hasRequirement1 = true;
switch (requirementType)
{
case STypeSkill::MELEE_DEFENSE_SKILL:
newItem->m_Qualities.SetInt(WIELD_DIFFICULTY_INT, wieldTier->meleeDefenseSkillRequired);
newItem->m_Qualities.SetDataID(ITEM_SKILL_LIMIT_DID, MELEE_DEFENSE_SKILL); //used by tinkering to know if the item can have it's requirement changed.
break;
case STypeSkill::MISSILE_DEFENSE_SKILL:
newItem->m_Qualities.SetInt(WIELD_DIFFICULTY_INT, wieldTier->missileDefenseSkillRequired);
newItem->m_Qualities.SetDataID(ITEM_SKILL_LIMIT_DID, MISSILE_DEFENSE_SKILL); //used by tinkering to know if the item can have it's requirement changed.
break;
case STypeSkill::MAGIC_DEFENSE_SKILL:
newItem->m_Qualities.SetInt(WIELD_DIFFICULTY_INT, wieldTier->magicDefenseSkillRequired);
newItem->m_Qualities.SetDataID(ITEM_SKILL_LIMIT_DID, MAGIC_DEFENSE_SKILL); //used by tinkering to know if the item can have it's requirement changed.
break;
}
}
if (wieldTier->minLevel > 0)
{
if (hasRequirement1)
{
newItem->m_Qualities.SetInt(WIELD_REQUIREMENTS_2_INT, eWieldRequirements::level);
newItem->m_Qualities.SetInt(WIELD_SKILLTYPE_2_INT, 1);
newItem->m_Qualities.SetInt(WIELD_DIFFICULTY_2_INT, wieldTier->minLevel);
}
else
{
newItem->m_Qualities.SetInt(WIELD_REQUIREMENTS_INT, eWieldRequirements::level);
newItem->m_Qualities.SetInt(WIELD_SKILLTYPE_INT, 1);
newItem->m_Qualities.SetInt(WIELD_DIFFICULTY_INT, wieldTier->minLevel);
}
}
}
std::vector<CPossibleSpells> CTreasureFactory::MergeSpellLists(std::vector<CPossibleSpells> list1, std::vector<CPossibleSpells> list2)
{
std::vector<CPossibleSpells> mergedList = std::vector<CPossibleSpells>(list1);
for (CPossibleSpells spell2 : list2)
{
bool isRepeat = false;
for (CPossibleSpells spell1 : list1)
{
if (spell1.id == spell2.id)
{
isRepeat = true;
break;
}
}
if (!isRepeat)
mergedList.push_back(spell2);
}
return mergedList;
}
void CTreasureFactory::AddSpells(CWeenieObject *newItem, sItemCreationInfo &creationInfo, CTreasureTier *tier, CTreasureProfileCategory *category, CItemTreasureProfileEntry *entry)
{
ITEM_TYPE itemType = (ITEM_TYPE)newItem->InqIntQuality(ITEM_TYPE_INT, TYPE_UNDEF, true);
INVENTORY_LOC coveredAreas = (INVENTORY_LOC)newItem->InqIntQuality(LOCATIONS_INT, NONE_LOC, TRUE);
creationInfo.spellAmountMultiplier = 1;
std::vector<CPossibleSpells> armorSpells = std::vector<CPossibleSpells>(_TreasureProfile->armorItemSpells);
switch (itemType)
{
case TYPE_MELEE_WEAPON:
AddSpell(newItem, _TreasureProfile->meleeWeaponSpells, creationInfo, tier, category, entry);
break;
case TYPE_MISSILE_WEAPON:
AddSpell(newItem, _TreasureProfile->missileWeaponSpells, creationInfo, tier, category, entry);
break;
case TYPE_CASTER:
AddSpell(newItem, _TreasureProfile->casterSpells, creationInfo, tier, category, entry);
break;
case TYPE_CLOTHING:
if (coveredAreas & HEAD_WEAR_LOC)
armorSpells = MergeSpellLists(armorSpells, _TreasureProfile->clothingHeadSpells);
else if (coveredAreas & HAND_WEAR_LOC)
armorSpells = MergeSpellLists(armorSpells, _TreasureProfile->clothingHandsSpells);
else if (coveredAreas & FOOT_WEAR_LOC)
armorSpells = MergeSpellLists(armorSpells, _TreasureProfile->clothingFeetSpells);
else
armorSpells = _TreasureProfile->clothingSpells;
AddSpell(newItem, armorSpells, creationInfo, tier, category, entry);
break;
case TYPE_JEWELRY:
AddSpell(newItem, _TreasureProfile->jewelrySpells, creationInfo, tier, category, entry);
break;
case TYPE_GEM:
AddSpell(newItem, _TreasureProfile->gemSpells, creationInfo, tier, category, entry);
break;
case TYPE_ARMOR:
int spellAmountMultiplier = 0;
if (coveredAreas & SHIELD_LOC)
{
armorSpells = MergeSpellLists(armorSpells, _TreasureProfile->shieldSpells);
spellAmountMultiplier++;
}
if (coveredAreas & HEAD_WEAR_LOC)
{
armorSpells = MergeSpellLists(armorSpells, _TreasureProfile->armorHeadSpells);
spellAmountMultiplier++;
}
if (coveredAreas & CHEST_ARMOR_LOC)
{
armorSpells = MergeSpellLists(armorSpells, _TreasureProfile->armorUpperBodySpells);
spellAmountMultiplier++;
}
if (coveredAreas & UPPER_ARM_ARMOR_LOC)
{
armorSpells = MergeSpellLists(armorSpells, _TreasureProfile->armorUpperBodySpells);
spellAmountMultiplier++;
}
if (coveredAreas & LOWER_ARM_ARMOR_LOC)
{
armorSpells = MergeSpellLists(armorSpells, _TreasureProfile->armorUpperBodySpells);
spellAmountMultiplier++;
}
if (coveredAreas & ABDOMEN_ARMOR_LOC)
{
//leg armor that also cover the abdomen do not have abdomen specific spells
if (!(coveredAreas & UPPER_LEG_ARMOR_LOC) && !(coveredAreas & LOWER_LEG_ARMOR_LOC))
{
armorSpells = MergeSpellLists(armorSpells, _TreasureProfile->armorUpperBodySpells);
spellAmountMultiplier++;
}
}
if (coveredAreas & HAND_WEAR_LOC)
{
armorSpells = MergeSpellLists(armorSpells, _TreasureProfile->armorHandsSpells);
spellAmountMultiplier++;
}
if (coveredAreas & UPPER_LEG_ARMOR_LOC)
{
armorSpells = MergeSpellLists(armorSpells, _TreasureProfile->armorLowerBodySpells);
spellAmountMultiplier++;
}
if (coveredAreas & LOWER_LEG_ARMOR_LOC)
{
armorSpells = MergeSpellLists(armorSpells, _TreasureProfile->armorLowerBodySpells);
spellAmountMultiplier++;
}
if (coveredAreas & FOOT_WEAR_LOC)
{
armorSpells = MergeSpellLists(armorSpells, _TreasureProfile->armorFeetSpells);
spellAmountMultiplier++;
}
creationInfo.spellAmountMultiplier = spellAmountMultiplier;
AddSpell(newItem, armorSpells, creationInfo, tier, category, entry);
break;
}
if (creationInfo.isMagical)
{
newItem->m_Qualities.SetInt(UI_EFFECTS_INT, UI_EFFECT_MAGICAL);
newItem->m_Qualities.SetInt(ITEM_SPELLCRAFT_INT, getRandomNumber(creationInfo.highestPower, creationInfo.highestPower * 1.3, eRandomFormula::favorMid, 2, 0));
newItem->m_Qualities.SetInt(ITEM_MAX_MANA_INT, creationInfo.totalMana);
newItem->m_Qualities.SetInt(ITEM_CUR_MANA_INT, creationInfo.totalMana);
double averagePower = (double)creationInfo.totalPower / creationInfo.totalSpellsCount;
double manaRate = min(-1.0 * creationInfo.totalSpellsCount * averagePower / 10000.0, -0.0166);
newItem->m_Qualities.SetFloat(MANA_RATE_FLOAT, manaRate);
double arcaneLoreModifier = 1.0;
if (creationInfo.totalSpellsCount >= tier->minSpellsForHeritageRequirement)
{
if (getRandomNumberExclusive(100) < tier->heritageRequirementChance * 100)
{
HeritageGroup heritageRequirement = (HeritageGroup)getRandomNumber(1, 3);
newItem->m_Qualities.SetInt(HERITAGE_GROUP_INT, heritageRequirement);
switch (heritageRequirement)
{
case Aluvian_HeritageGroup:
newItem->m_Qualities.SetString(ITEM_HERITAGE_GROUP_RESTRICTION_STRING, "Aluvian");
break;
case Gharundim_HeritageGroup:
newItem->m_Qualities.SetString(ITEM_HERITAGE_GROUP_RESTRICTION_STRING, "Gharu'ndim");
break;
case Sho_HeritageGroup:
newItem->m_Qualities.SetString(ITEM_HERITAGE_GROUP_RESTRICTION_STRING, "Sho");
break;
}
arcaneLoreModifier -= 0.20;
}
}
if (creationInfo.totalSpellsCount >= tier->minSpellsForAllegianceRankRequirement && tier->maxAllegianceRankRequired > 0)
{
if (getRandomNumberExclusive(100) < tier->allegianceRankRequirementChance * 100)
{
int favoredRank = (int)ceil(tier->maxAllegianceRankRequired * 0.4);
favoredRank = max(favoredRank, 1);
int allegianceRankRequirement = getRandomNumber(1, tier->maxAllegianceRankRequired, eRandomFormula::favorSpecificValue, 3, 0, favoredRank);
newItem->m_Qualities.SetInt(ITEM_ALLEGIANCE_RANK_LIMIT_INT, allegianceRankRequirement);
arcaneLoreModifier -= (allegianceRankRequirement * 0.08);
}
}
int cappedTotalSpellsCount = creationInfo.totalSpellsCount;
if (creationInfo.totalSpellsCount > tier->maxAmountOfSpells)
cappedTotalSpellsCount = tier->maxAmountOfSpells; //cap amount of spells influence on arcane lore on items that cover more than one body part.
int arcaneLoreRequirement = (int)round((pow(averagePower, 2) + ((creationInfo.totalSpellsCount - 1) * averagePower * 15)) / 200);
arcaneLoreRequirement = (int)round(getRandomDouble(arcaneLoreRequirement * 0.9, arcaneLoreRequirement * 1.1, eRandomFormula::favorMid, 2, 0));
arcaneLoreRequirement = (int)round(arcaneLoreRequirement * arcaneLoreModifier);
arcaneLoreRequirement = max(arcaneLoreRequirement, 5);
newItem->m_Qualities.SetInt(ITEM_DIFFICULTY_INT, arcaneLoreRequirement);
}
}
void CTreasureFactory::AddSpell(CWeenieObject *newItem, std::vector<CPossibleSpells> possibleSpellsTemplate, sItemCreationInfo &creationInfo, CTreasureTier *tier, CTreasureProfileCategory *category, CItemTreasureProfileEntry *entry)
{
if (possibleSpellsTemplate.size() == 0)
return;
if (tier->maxAmountOfSpells > 0 && tier->minSpellLevel > 0 && tier->maxSpellLevel > 0)
{
//we have possible spells
ITEM_TYPE itemType = (ITEM_TYPE)newItem->InqIntQuality(ITEM_TYPE_INT, TYPE_UNDEF, true);
//make it so clothing and jewelry always have at least 1 spell
if (getRandomNumberExclusive(100) < tier->chanceOfSpells * 100 * (1 + (creationInfo.qualityModifier * 2)) || itemType == TYPE_JEWELRY || itemType == TYPE_CLOTHING)
{
std::vector<CPossibleSpells> possibleSpells = std::vector<CPossibleSpells>(possibleSpellsTemplate);
int spellCount = getRandomNumber(1, tier->maxAmountOfSpells, eRandomFormula::favorLow, 3, creationInfo.qualityModifier);
for (int i = 1; i < creationInfo.spellAmountMultiplier; i++)
spellCount += getRandomNumber(0, tier->maxAmountOfSpells, eRandomFormula::favorLow, 3, creationInfo.qualityModifier);
//force gems to only one spell
if (category->category == "gemstones")
spellCount = 1;
for (int i = 0; i < spellCount; i++)
{
if (possibleSpells.size() == 0)
break; //no more spells;
int spellRoll = getRandomNumberExclusive((int)possibleSpells.size());
CPossibleSpells possibleSpell = possibleSpells[spellRoll];
if (!creationInfo.hasAlreadyReplacedSpell && possibleSpell.spellCategory == eSpellCategory::magicSkillMastery && creationInfo.favoredMagicSchoolMasterySpell.id != 0 && !creationInfo.otherMagicSchoolMasterySpells.empty())
{
//replace magic school mastery spell for the one that the item has a requirement for.
for (CPossibleSpells spellToBeReplaced : creationInfo.otherMagicSchoolMasterySpells)
{
if (possibleSpell.id == spellToBeReplaced.id)
{
possibleSpell = creationInfo.favoredMagicSchoolMasterySpell;
creationInfo.hasAlreadyReplacedSpell = true;
break;
}
}
}
std::vector<int> spellLevelVariants = possibleSpell.spells;
int minSpellVariantId = tier->minSpellLevel - 1;
int maxSpellVariantId = min(tier->maxSpellLevel, (int)spellLevelVariants.size()) - 1;
int spellVariantIndex = getRandomNumber(minSpellVariantId, maxSpellVariantId, eRandomFormula::favorSpecificValue, tier->preferredSpellLevelStrength, creationInfo.qualityModifier, tier->preferredSpellLevel - 1);
int spell = spellLevelVariants[spellVariantIndex];
if (spell == 0)
{
//this spell doesnt exit at this level
continue;
}
if (possibleSpell.spellCategory == eSpellCategory::weaponSkillMastery)
{
int skillSpell = 0;
newItem->m_Qualities.InqInt(WIELD_SKILLTYPE_INT, skillSpell);
string possibleSpellName = possibleSpell.spellName;
if (newItem->m_Qualities.GetInt(WEAPON_TYPE_INT, 0) != 11) // Recklessness only appears on 2handed weapons
{
if (skillSpell == RECKLESSNESS_SKILL && possibleSpellName == "Recklessness")
{
i--;
possibleSpells.erase(possibleSpells.begin() + spellRoll);
continue;
}
}
}
possibleSpells.erase(possibleSpells.begin() + spellRoll);//remove this from the list of possible spells so we do not readd it
if (possibleSpell.spellCategory == eSpellCategory::weaponSkillMastery)
{
//remove other weapon masteries from the possible spells list.
std::vector<CPossibleSpells> updatedPossibleSpells;
for (CPossibleSpells otherSpell : possibleSpells)
{
if (otherSpell.spellCategory != eSpellCategory::weaponSkillMastery)
updatedPossibleSpells.push_back(otherSpell);
}
possibleSpells = updatedPossibleSpells;
}
CSpellTable *pSpellTable = MagicSystem::GetSpellTable();
if (!spell || !pSpellTable || !pSpellTable->GetSpellBase(spell))
continue; //invalid spell id
if (category->category == "gemstones")
{
newItem->m_Qualities.SetDataID(SPELL_DID, spell);
newItem->m_Qualities.SetInt(ITEM_USEABLE_INT, 8);
}
newItem->m_Qualities.AddSpell(spell);
creationInfo.spellIds.push_back(spell);
creationInfo.spells.push_back(possibleSpell);
creationInfo.isMagical = true;
creationInfo.totalSpellsCount++;
int spellPower = _TreasureProfile->spellProperties.spellPower[spellVariantIndex];
creationInfo.totalPower += spellPower;
creationInfo.totalMana += _TreasureProfile->spellProperties.spellMana[spellVariantIndex];
if (creationInfo.highestPower < spellPower)
creationInfo.highestPower = spellPower;
}
}
}
if (tier->maxAmountOfCantrips > 0 && tier->minCantripLevel > 0 && tier->maxCantripLevel > 0)
{
//we have possible cantrips
if (getRandomNumberExclusive(100) < tier->chanceOfCantrips * 100 * (1 + (creationInfo.qualityModifier * 2)))
{
std::vector<CPossibleSpells> possibleCantrips = std::vector<CPossibleSpells>(possibleSpellsTemplate);
if (!creationInfo.spells.empty())
{
//if we also have spells, coordinate cantrips with them
bool hasWeaponMastery = false;
std::vector<CPossibleSpells> updatedPossibleCantrips;
std::vector<CPossibleSpells > magicMasterySpells;
for (CPossibleSpells spell : creationInfo.spells)
{
if (spell.spellCategory == eSpellCategory::weaponSkillMastery)
{
hasWeaponMastery = true;
}
else if (spell.spellCategory == eSpellCategory::magicSkillMastery)
{
ITEM_TYPE itemType = (ITEM_TYPE)newItem->InqIntQuality(ITEM_TYPE_INT, TYPE_UNDEF, true);
STypeSkill wieldSkill = (STypeSkill)newItem->InqIntQuality(WIELD_SKILLTYPE_INT, UNDEF_SKILL, true);
if (itemType != TYPE_CASTER || wieldSkill == UNDEF_SKILL)
magicMasterySpells.push_back(spell);
}
}
for (CPossibleSpells cantrip : possibleCantrips)
{
if (hasWeaponMastery && cantrip.spellCategory == eSpellCategory::weaponSkillMastery)
{
for (CPossibleSpells spell : creationInfo.spells)
{
if (spell.id == cantrip.id)
{
updatedPossibleCantrips.push_back(cantrip);
break;
}
}
}
else
updatedPossibleCantrips.push_back(cantrip);
}
possibleCantrips = updatedPossibleCantrips;
if (!magicMasterySpells.empty())
{
creationInfo.favoredMagicSchoolMasterySpell = magicMasterySpells[getRandomNumberExclusive((int)magicMasterySpells.size())];
creationInfo.otherMagicSchoolMasterySpells;
if (creationInfo.favoredMagicSchoolMasterySpell.spellName != "Creature Enchantment Mastery")
creationInfo.otherMagicSchoolMasterySpells.push_back(_TreasureProfile->spells[_TreasureProfile->spellNameToIdMap["Creature Enchantment Mastery"]]);
if (creationInfo.favoredMagicSchoolMasterySpell.spellName != "Item Enchantment Mastery")
creationInfo.otherMagicSchoolMasterySpells.push_back(_TreasureProfile->spells[_TreasureProfile->spellNameToIdMap["Item Enchantment Mastery"]]);
if (creationInfo.favoredMagicSchoolMasterySpell.spellName != "Life Magic Mastery")
creationInfo.otherMagicSchoolMasterySpells.push_back(_TreasureProfile->spells[_TreasureProfile->spellNameToIdMap["Life Magic Mastery"]]);
if (creationInfo.favoredMagicSchoolMasterySpell.spellName != "War Magic Mastery")
creationInfo.otherMagicSchoolMasterySpells.push_back(_TreasureProfile->spells[_TreasureProfile->spellNameToIdMap["War Magic Mastery"]]);
}
}
int cantripCount = getRandomNumber(1, tier->maxAmountOfCantrips, eRandomFormula::favorLow, 10.0, 0);
for (int i = 1; i < creationInfo.spellAmountMultiplier; i++)
cantripCount += getRandomNumber(0, tier->maxAmountOfCantrips, eRandomFormula::favorLow, 10.0, 0);
for (int i = 0; i < cantripCount; i++)
{
if (possibleCantrips.size() == 0)
break; //no more cantrips;
int cantripRoll = getRandomNumberExclusive((int)possibleCantrips.size());
CPossibleSpells possibleCantrip = possibleCantrips[cantripRoll];
if (!creationInfo.hasAlreadyReplacedCantrip && possibleCantrip.spellCategory == eSpellCategory::magicSkillMastery && creationInfo.favoredMagicSchoolMasterySpell.id != 0 && !creationInfo.otherMagicSchoolMasterySpells.empty())
{
//replace magic school mastery cantrip for the one that the item has a requirement for.
for (CPossibleSpells spellToBeReplaced : creationInfo.otherMagicSchoolMasterySpells)
{
if (possibleCantrip.id == spellToBeReplaced.id)
{
possibleCantrip = creationInfo.favoredMagicSchoolMasterySpell;
creationInfo.hasAlreadyReplacedCantrip = true;
break;
}
}
}
std::vector<int> cantripLevelVariants = possibleCantrip.cantrips;
int minCantripVariantId = tier->minCantripLevel - 1;
int maxCantripVariantId = min(tier->maxCantripLevel, (int)cantripLevelVariants.size()) - 1;
int cantripVariantIndex = getRandomNumber(minCantripVariantId, maxCantripVariantId, eRandomFormula::favorSpecificValue, tier->preferredCantripLevelStrength, creationInfo.qualityModifier / 2, tier->preferredCantripLevel - 1);
int cantrip = cantripLevelVariants[cantripVariantIndex];
if (cantrip == 0)
{
//this cantrip doesnt exit at this level
continue;
}
if (possibleCantrip.spellCategory == eSpellCategory::weaponSkillMastery)
{
int skillCantrip = 0;
newItem->m_Qualities.InqInt(WIELD_SKILLTYPE_INT, skillCantrip);
string possibleCantripName = possibleCantrip.spellName;
if (newItem->m_Qualities.GetInt(WEAPON_TYPE_INT, 0) != 11) // Recklessness only appears on 2handed weapons
{
if (skillCantrip == RECKLESSNESS_SKILL && possibleCantripName == "Recklessness")
{
i--;
possibleCantrips.erase(possibleCantrips.begin() + cantripRoll);
continue;
}
}
}
possibleCantrips.erase(possibleCantrips.begin() + cantripRoll);//remove this from the list of possible cantrips so we do not readd it
if (possibleCantrip.spellCategory == eSpellCategory::weaponSkillMastery)
{
//remove other weapon masteries from the possible cantrips list.
std::vector<CPossibleSpells> updatedPossibleCantrips;
for (CPossibleSpells otherCantrip : possibleCantrips)
{
if (otherCantrip.spellCategory != eSpellCategory::weaponSkillMastery)
updatedPossibleCantrips.push_back(otherCantrip);
}
possibleCantrips = updatedPossibleCantrips;
}
CSpellTable *pSpellTable = MagicSystem::GetSpellTable();
if (!cantrip || !pSpellTable || !pSpellTable->GetSpellBase(cantrip))
continue; //invalid cantrip id
newItem->m_Qualities.AddSpell(cantrip);
creationInfo.cantripIds.push_back(cantrip);
creationInfo.cantrips.push_back(possibleCantrip);
creationInfo.isMagical = true;
creationInfo.totalSpellsCount++;
int cantripPower = _TreasureProfile->spellProperties.cantripPower[cantripVariantIndex];
creationInfo.totalPower += cantripPower;
creationInfo.totalMana += _TreasureProfile->spellProperties.cantripMana[cantripVariantIndex];
if (creationInfo.highestPower < cantripPower)
creationInfo.highestPower = cantripPower;
}
}
}
}
//bool CTreasureFactory::AddTreasureToContainerInferred(CContainerWeenie *container, uint32_t treasureType)
//{
// TreasureEntry2 *entry = g_pPortalDataEx->_treasureTableData.GetTreasureGenerationProfile(treasureType);
// if (!entry)
// return false;
//
// int tier = entry->_tier;
// double qualityModifier = entry->_lootQualityMod;
//
// // Even though they are called item and magicItem we're not doing anything to make them respect that.
// // We leave that to the generator to decide according to the settings specified in the treasureProfile.json.
// int itemCount = getRandomNumber(entry->_itemMinAmount, entry->_itemMaxAmount);
// for (int i = 0; i < itemCount; i++)
// {
// CWeenieObject *newItem = GenerateTreasure(tier, g_pPortalDataEx->_treasureTableData.RollTreasureCategory(0, entry->_itemTreasureTypeSelectionChances), qualityModifier);
// if (newItem)
// {
// g_pWorld->CreateEntity(newItem);
// newItem->SetWeenieContainer(container->GetID());
// container->Container_InsertInventoryItem(0, newItem, 0);
// }
// }
//
// int magicItemCount = getRandomNumber(entry->_magicItemMinAmount, entry->_magicItemMaxAmount);
// for (int i = 0; i < magicItemCount; i++)
// {
// CWeenieObject *newItem = GenerateTreasure(tier, g_pPortalDataEx->_treasureTableData.RollTreasureCategory(1, entry->_magicItemTreasureTypeSelectionChances), qualityModifier);
// if (newItem)
// {
// g_pWorld->CreateEntity(newItem);
// newItem->SetWeenieContainer(container->GetID());
// container->Container_InsertInventoryItem(0, newItem, 0);
// }
// }
//
// int mundaneItemCount = getRandomNumber(entry->_mundaneItemMinAmount, entry->_mundaneItemMaxAmount);
// for (int i = 0; i < mundaneItemCount; i++)
// {
// CWeenieObject *newItem = GenerateTreasure(tier, g_pPortalDataEx->_treasureTableData.RollTreasureCategory(2, entry->_mundaneItemTypeSelectionChances), qualityModifier);
// if (newItem)
// {
// g_pWorld->CreateEntity(newItem);
// newItem->SetWeenieContainer(container->GetID());
// container->Container_InsertInventoryItem(0, newItem, 0);
// }
// }
//
// return true;
//}
//
//CWeenieObject *CTreasureFactory::GenerateMundaneItemInferred(int tierId, eTreasureCategory treasureCategory)
//{
// uint32_t wcid = 0;
// switch (treasureCategory)
// {
// case TreasureCategory_Mana_Stone:
// wcid = g_pPortalDataEx->_treasureTableData.RollManaStone(tierId);
// break;
// case TreasureCategory_Consumable:
// wcid = g_pPortalDataEx->_treasureTableData.RollConsumable(tierId);
// break;
// case TreasureCategory_Healer:
// wcid = g_pPortalDataEx->_treasureTableData.RollHealer(tierId);
// break;
// case TreasureCategory_Lockpick:
// wcid = g_pPortalDataEx->_treasureTableData.RollLockpick(tierId);
// break;
// case TreasureCategory_Pea:
// wcid = g_pPortalDataEx->_treasureTableData.RollPea(tierId);
// break;
// }
//
// if (wcid)
// return g_pWeenieFactory->CreateWeenieByClassID(wcid);
// return NULL;
//}