From c45dcdd7b0aa1d458d06e5ee2cc0dfd2a0866c0f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 4 Jul 2023 01:53:45 -0700 Subject: [PATCH] implement sell_prices adjustments --- library/modules/Items.cpp | 393 +++++++++++++++++++++++++++++++++++++- 1 file changed, 390 insertions(+), 3 deletions(-) diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index 63c2a1992..537be7c3a 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -50,11 +50,14 @@ using namespace std; #include "df/building.h" #include "df/building_actual.h" #include "df/building_tradedepotst.h" +#include "df/builtin_mats.h" #include "df/caravan_state.h" #include "df/caste_raw.h" #include "df/creature_raw.h" +#include "df/dfhack_material_category.h" #include "df/entity_buy_prices.h" #include "df/entity_buy_requests.h" +#include "df/entity_sell_category.h" #include "df/entity_sell_prices.h" #include "df/entity_raw.h" #include "df/general_ref.h" @@ -1500,6 +1503,9 @@ static int32_t get_war_multiplier(df::item *item, df::caravan_state *caravan) { static const int32_t DEFAULT_AGREEMENT_MULTIPLIER = 128; static int32_t get_buy_request_multiplier(df::item *item, const df::entity_buy_prices *buy_prices) { + if (!buy_prices) + return DEFAULT_AGREEMENT_MULTIPLIER; + int16_t item_type = item->getType(); int16_t item_subtype = item->getSubtype(); int16_t mat_type = item->getMaterial(); @@ -1519,15 +1525,396 @@ static int32_t get_buy_request_multiplier(df::item *item, const df::entity_buy_p return DEFAULT_AGREEMENT_MULTIPLIER; } -static int32_t get_sell_request_multiplier(df::item *item, const df::entity_sell_prices *sell_prices) { +template +static int get_price(const std::vector &res, int32_t val, const std::vector &pri) { + for (size_t idx = 0; idx < res.size(); ++idx) { + if (res[idx] == val && pri.size() > idx) + return pri[idx]; + } + return -1; +} + +template +static int get_price(const std::vector &mat_res, int32_t mat, const std::vector &gloss_res, int32_t gloss, const std::vector &pri) { + for (size_t idx = 0; idx < mat_res.size(); ++idx) { + if (mat_res[idx] == mat && (gloss_res[idx] == -1 || gloss_res[idx] == gloss) && pri.size() > idx) + return pri[idx]; + } + return -1; +} + +static const uint16_t PLANT_BASE = 419; +static const uint16_t NUM_PLANT_TYPES = 200; + +static int32_t get_sell_request_multiplier(df::item *item, const df::historical_entity::T_resources &resources, const std::vector *prices) { + static const df::dfhack_material_category silk_cat(df::dfhack_material_category::mask_silk); + static const df::dfhack_material_category yarn_cat(df::dfhack_material_category::mask_yarn); + static const df::dfhack_material_category leather_cat(df::dfhack_material_category::mask_leather); + + int16_t item_type = item->getType(); + int16_t item_subtype = item->getSubtype(); + int16_t mat_type = item->getMaterial(); + int32_t mat_subtype = item->getMaterialIndex(); + + bool inorganic = mat_type == df::builtin_mats::INORGANIC; + bool is_plant = (uint16_t)(mat_type - PLANT_BASE) < NUM_PLANT_TYPES; + + switch (item_type) { + case df::item_type::BAR: + if (inorganic) { + if (int32_t price = get_price(resources.metals, mat_subtype, prices[df::entity_sell_category::MetalBars]); price != -1) + return price; + } + break; + case df::item_type::SMALLGEM: + if (inorganic) { + if (int32_t price = get_price(resources.gems, mat_subtype, prices[df::entity_sell_category::SmallCutGems]); price != -1) + return price; + } + break; + case df::item_type::BLOCKS: + if (inorganic) { + if (int32_t price = get_price(resources.stones, mat_subtype, prices[df::entity_sell_category::StoneBlocks]); price != -1) + return price; + } + break; + case df::item_type::ROUGH: + if (int32_t price = get_price(resources.misc_mat.glass.mat_type, mat_type, resources.misc_mat.glass.mat_index, mat_subtype, + prices[df::entity_sell_category::Glass]); price != -1) + return price; + break; + case df::item_type::BOULDER: + if (int32_t price = get_price(resources.stones, mat_subtype, prices[df::entity_sell_category::Stone]); price != -1) + return price; + if (int32_t price = get_price(resources.misc_mat.clay.mat_type, mat_type, resources.misc_mat.clay.mat_index, mat_subtype, + prices[df::entity_sell_category::Clay]); price != -1) + return price; + break; + case df::item_type::WOOD: + if (int32_t price = get_price(resources.organic.wood.mat_type, mat_type, resources.organic.wood.mat_index, mat_subtype, + prices[df::entity_sell_category::Wood]); price != -1) + return price; + break; + case df::item_type::CHAIN: + if (is_plant) { + if (int32_t price = get_price(resources.organic.fiber.mat_type, mat_type, resources.organic.fiber.mat_index, mat_subtype, + prices[df::entity_sell_category::RopesPlant]); price != -1) + return price; + } + { + MaterialInfo mi; + mi.decode(mat_type, mat_subtype); + if (mi.isValid()) { + if (mi.matches(silk_cat)) { + if (int32_t price = get_price(resources.organic.silk.mat_type, mat_type, resources.organic.silk.mat_index, mat_subtype, + prices[df::entity_sell_category::RopesSilk]); price != -1) + return price; + } + if (mi.matches(yarn_cat)) { + if (int32_t price = get_price(resources.organic.wool.mat_type, mat_type, resources.organic.wool.mat_index, mat_subtype, + prices[df::entity_sell_category::RopesYarn]); price != -1) + return price; + } + } + } + break; + case df::item_type::FLASK: + if (int32_t price = get_price(resources.misc_mat.flasks.mat_type, mat_type, resources.misc_mat.flasks.mat_index, mat_subtype, + prices[df::entity_sell_category::FlasksWaterskins]); price != -1) + return price; + break; + case df::item_type::GOBLET: + if (int32_t price = get_price(resources.misc_mat.crafts.mat_type, mat_type, resources.misc_mat.crafts.mat_index, mat_subtype, + prices[df::entity_sell_category::CupsMugsGoblets]); price != -1) + return price; + break; + case df::item_type::INSTRUMENT: + if (int32_t price = get_price(resources.instrument_type, mat_subtype, prices[df::entity_sell_category::Instruments]); price != -1) + return price; + break; + case df::item_type::TOY: + if (int32_t price = get_price(resources.toy_type, mat_subtype, prices[df::entity_sell_category::Toys]); price != -1) + return price; + break; + case df::item_type::CAGE: + if (int32_t price = get_price(resources.misc_mat.cages.mat_type, mat_type, resources.misc_mat.cages.mat_index, mat_subtype, + prices[df::entity_sell_category::Cages]); price != -1) + return price; + break; + case df::item_type::BARREL: + if (int32_t price = get_price(resources.misc_mat.barrels.mat_type, mat_type, resources.misc_mat.barrels.mat_index, mat_subtype, + prices[df::entity_sell_category::Barrels]); price != -1) + return price; + break; + case df::item_type::BUCKET: + if (int32_t price = get_price(resources.misc_mat.barrels.mat_type, mat_type, resources.misc_mat.barrels.mat_index, mat_subtype, + prices[df::entity_sell_category::Buckets]); price != -1) + return price; + break; + case df::item_type::WEAPON: + if (int32_t price = get_price(resources.weapon_type, mat_subtype, prices[df::entity_sell_category::Weapons]); price != -1) + return price; + if (int32_t price = get_price(resources.digger_type, mat_subtype, prices[df::entity_sell_category::DiggingImplements]); price != -1) + return price; + if (int32_t price = get_price(resources.training_weapon_type, mat_subtype, prices[df::entity_sell_category::TrainingWeapons]); price != -1) + return price; + break; + case df::item_type::ARMOR: + if (int32_t price = get_price(resources.armor_type, mat_subtype, prices[df::entity_sell_category::Bodywear]); price != -1) + return price; + break; + case df::item_type::SHOES: + if (int32_t price = get_price(resources.shoes_type, mat_subtype, prices[df::entity_sell_category::Footwear]); price != -1) + return price; + break; + case df::item_type::SHIELD: + if (int32_t price = get_price(resources.shield_type, mat_subtype, prices[df::entity_sell_category::Shields]); price != -1) + return price; + break; + case df::item_type::HELM: + if (int32_t price = get_price(resources.helm_type, mat_subtype, prices[df::entity_sell_category::Headwear]); price != -1) + return price; + break; + case df::item_type::GLOVES: + if (int32_t price = get_price(resources.gloves_type, mat_subtype, prices[df::entity_sell_category::Handwear]); price != -1) + return price; + break; + case df::item_type::BAG: + { + MaterialInfo mi; + mi.decode(mat_type, mat_subtype); + if (mi.isValid() && mi.matches(leather_cat)) { + if (int32_t price = get_price(resources.organic.leather.mat_type, mat_type, resources.organic.leather.mat_index, mat_subtype, + prices[df::entity_sell_category::BagsLeather]); price != -1) + return price; + } + if (is_plant) { + if (int32_t price = get_price(resources.organic.fiber.mat_type, mat_type, resources.organic.fiber.mat_index, mat_subtype, + prices[df::entity_sell_category::BagsPlant]); price != -1) + return price; + } + if (mi.isValid() && mi.matches(silk_cat)) { + if (int32_t price = get_price(resources.organic.silk.mat_type, mat_type, resources.organic.silk.mat_index, mat_subtype, + prices[df::entity_sell_category::BagsSilk]); price != -1) + return price; + } + if (mi.isValid() && mi.matches(yarn_cat)) { + if (int32_t price = get_price(resources.organic.wool.mat_type, mat_type, resources.organic.wool.mat_index, mat_subtype, + prices[df::entity_sell_category::BagsYarn]); price != -1) + return price; + } + } + break; + case df::item_type::FIGURINE: + case df::item_type::AMULET: + case df::item_type::SCEPTER: + case df::item_type::CROWN: + case df::item_type::RING: + case df::item_type::EARRING: + case df::item_type::BRACELET: + case df::item_type::TOTEM: + case df::item_type::BOOK: + if (int32_t price = get_price(resources.misc_mat.crafts.mat_type, mat_type, resources.misc_mat.crafts.mat_index, mat_subtype, + prices[df::entity_sell_category::Crafts]); price != -1) + return price; + break; + case df::item_type::AMMO: + if (int32_t price = get_price(resources.ammo_type, mat_subtype, prices[df::entity_sell_category::Ammo]); price != -1) + return price; + break; + case df::item_type::GEM: + if (inorganic) { + if (int32_t price = get_price(resources.gems, mat_subtype, prices[df::entity_sell_category::LargeCutGems]); price != -1) + return price; + } + break; + case df::item_type::ANVIL: + if (int32_t price = get_price(resources.metal.anvil.mat_type, mat_type, resources.metal.anvil.mat_index, mat_subtype, + prices[df::entity_sell_category::Anvils]); price != -1) + return price; + break; + case df::item_type::MEAT: + if (int32_t price = get_price(resources.misc_mat.meat.mat_type, mat_type, resources.misc_mat.meat.mat_index, mat_subtype, + prices[df::entity_sell_category::Meat]); price != -1) + return price; + break; + case df::item_type::FISH: + case df::item_type::FISH_RAW: + if (int32_t price = get_price(resources.fish_races, mat_type, resources.fish_castes, mat_subtype, + prices[df::entity_sell_category::Fish]); price != -1) + return price; + break; + case df::item_type::VERMIN: + case df::item_type::PET: + if (int32_t price = get_price(resources.animals.pet_races, mat_type, resources.animals.pet_castes, mat_subtype, + prices[df::entity_sell_category::Pets]); price != -1) + return price; + break; + case df::item_type::SEEDS: + if (int32_t price = get_price(resources.seeds.mat_type, mat_type, resources.seeds.mat_index, mat_subtype, + prices[df::entity_sell_category::Seeds]); price != -1) + return price; + break; + case df::item_type::PLANT: + if (int32_t price = get_price(resources.plants.mat_type, mat_type, resources.plants.mat_index, mat_subtype, + prices[df::entity_sell_category::Plants]); price != -1) + return price; + break; + case df::item_type::SKIN_TANNED: + if (int32_t price = get_price(resources.organic.leather.mat_type, mat_type, resources.organic.leather.mat_index, mat_subtype, + prices[df::entity_sell_category::Leather]); price != -1) + return price; + break; + case df::item_type::PLANT_GROWTH: + if (is_plant) { + if (int32_t price = get_price(resources.tree_fruit_plants, mat_type, resources.tree_fruit_growths, mat_subtype, + prices[df::entity_sell_category::FruitsNuts]); price != -1) + return price; + if (int32_t price = get_price(resources.shrub_fruit_plants, mat_type, resources.shrub_fruit_growths, mat_subtype, + prices[df::entity_sell_category::GardenVegetables]); price != -1) + return price; + } + break; + case df::item_type::THREAD: + if (is_plant) { + if (int32_t price = get_price(resources.organic.fiber.mat_type, mat_type, resources.organic.fiber.mat_index, mat_subtype, + prices[df::entity_sell_category::ThreadPlant]); price != -1) + return price; + } + { + MaterialInfo mi; + mi.decode(mat_type, mat_subtype); + if (mi.isValid() && mi.matches(silk_cat)) { + if (int32_t price = get_price(resources.organic.silk.mat_type, mat_type, resources.organic.silk.mat_index, mat_subtype, + prices[df::entity_sell_category::ThreadSilk]); price != -1) + return price; + } + if (mi.isValid() && mi.matches(yarn_cat)) { + if (int32_t price = get_price(resources.organic.wool.mat_type, mat_type, resources.organic.wool.mat_index, mat_subtype, + prices[df::entity_sell_category::ThreadYarn]); price != -1) + return price; + } + } + break; + case df::item_type::CLOTH: + if (is_plant) { + if (int32_t price = get_price(resources.organic.fiber.mat_type, mat_type, resources.organic.fiber.mat_index, mat_subtype, + prices[df::entity_sell_category::ClothPlant]); price != -1) + return price; + } + { + MaterialInfo mi; + mi.decode(mat_type, mat_subtype); + if (mi.isValid() && mi.matches(silk_cat)) { + if (int32_t price = get_price(resources.organic.silk.mat_type, mat_type, resources.organic.silk.mat_index, mat_subtype, + prices[df::entity_sell_category::ClothSilk]); price != -1) + return price; + } + if (mi.isValid() && mi.matches(yarn_cat)) { + if (int32_t price = get_price(resources.organic.wool.mat_type, mat_type, resources.organic.wool.mat_index, mat_subtype, + prices[df::entity_sell_category::ClothYarn]); price != -1) + return price; + } + } + break; + case df::item_type::PANTS: + if (int32_t price = get_price(resources.pants_type, mat_subtype, prices[df::entity_sell_category::Legwear]); price != -1) + return price; + break; + case df::item_type::BACKPACK: + if (int32_t price = get_price(resources.misc_mat.backpacks.mat_type, mat_type, resources.misc_mat.backpacks.mat_index, mat_subtype, + prices[df::entity_sell_category::Backpacks]); price != -1) + return price; + break; + case df::item_type::QUIVER: + if (int32_t price = get_price(resources.misc_mat.quivers.mat_type, mat_type, resources.misc_mat.quivers.mat_index, mat_subtype, + prices[df::entity_sell_category::Quivers]); price != -1) + return price; + break; + case df::item_type::TRAPCOMP: + if (int32_t price = get_price(resources.trapcomp_type, mat_subtype, prices[df::entity_sell_category::TrapComponents]); price != -1) + return price; + break; + case df::item_type::DRINK: + if (int32_t price = get_price(resources.misc_mat.booze.mat_type, mat_type, resources.misc_mat.booze.mat_index, mat_subtype, + prices[df::entity_sell_category::Drinks]); price != -1) + return price; + break; + case df::item_type::POWDER_MISC: + if (int32_t price = get_price(resources.misc_mat.powders.mat_type, mat_type, resources.misc_mat.powders.mat_index, mat_subtype, + prices[df::entity_sell_category::Powders]); price != -1) + return price; + if (int32_t price = get_price(resources.misc_mat.sand.mat_type, mat_type, resources.misc_mat.sand.mat_index, mat_subtype, + prices[df::entity_sell_category::Sand]); price != -1) + return price; + break; + case df::item_type::CHEESE: + if (int32_t price = get_price(resources.misc_mat.cheese.mat_type, mat_type, resources.misc_mat.cheese.mat_index, mat_subtype, + prices[df::entity_sell_category::Cheese]); price != -1) + return price; + break; + case df::item_type::LIQUID_MISC: + if (int32_t price = get_price(resources.misc_mat.extracts.mat_type, mat_type, resources.misc_mat.extracts.mat_index, mat_subtype, + prices[df::entity_sell_category::Extracts]); price != -1) + return price; + break; + case df::item_type::SPLINT: + if (int32_t price = get_price(resources.misc_mat.barrels.mat_type, mat_type, resources.misc_mat.barrels.mat_index, mat_subtype, + prices[df::entity_sell_category::Splints]); price != -1) + return price; + break; + case df::item_type::CRUTCH: + if (int32_t price = get_price(resources.misc_mat.barrels.mat_type, mat_type, resources.misc_mat.barrels.mat_index, mat_subtype, + prices[df::entity_sell_category::Crutches]); price != -1) + return price; + break; + case df::item_type::TOOL: + if (int32_t price = get_price(resources.tool_type, mat_subtype, prices[df::entity_sell_category::Tools]); price != -1) + return price; + break; + case df::item_type::EGG: + if (int32_t price = get_price(resources.egg_races, mat_type, resources.egg_castes, mat_subtype, + prices[df::entity_sell_category::Eggs]); price != -1) + return price; + break; + case df::item_type::SHEET: + if (int32_t price = get_price(resources.organic.parchment.mat_type, mat_type, resources.organic.parchment.mat_index, mat_subtype, + prices[df::entity_sell_category::Parchment]); price != -1) + return price; + break; + default: + break; + } + + for (size_t idx = 0; idx < resources.wood_products.item_type.size(); ++idx) { + if (resources.wood_products.item_type[idx] == item_type && + (resources.wood_products.item_subtype[idx] == -1 || resources.wood_products.item_subtype[idx] == item_subtype) && + resources.wood_products.material.mat_type[idx] == mat_type && + (resources.wood_products.material.mat_index[idx] == -1 || resources.wood_products.material.mat_index[idx] == mat_subtype) && + prices[df::entity_sell_category::Miscellaneous].size() > idx) + return prices[df::entity_sell_category::Miscellaneous][idx]; + } + return DEFAULT_AGREEMENT_MULTIPLIER; } -static int32_t get_trade_agreement_multiplier(df::item *item, df::caravan_state *caravan, bool caravan_buying) { +static int32_t get_sell_request_multiplier(df::item *item, const df::caravan_state *caravan) { + const df::entity_sell_prices *sell_prices = caravan->sell_prices; + if (!sell_prices) + return DEFAULT_AGREEMENT_MULTIPLIER; + + auto caravan_he = df::historical_entity::find(caravan->entity); + if (!caravan_he) + return DEFAULT_AGREEMENT_MULTIPLIER; + + return get_sell_request_multiplier(item, caravan_he->resources, &sell_prices->price[0]); +} + +static int32_t get_trade_agreement_multiplier(df::item *item, const df::caravan_state *caravan, bool caravan_buying) { if (!caravan) return DEFAULT_AGREEMENT_MULTIPLIER; return caravan_buying ? get_buy_request_multiplier(item, caravan->buy_prices) - : get_sell_request_multiplier(item, caravan->sell_prices); + : get_sell_request_multiplier(item, caravan); } int Items::getValue(df::item *item, df::caravan_state *caravan, bool caravan_buying)