diff --git a/Lua API.html b/Lua API.html index bd17cc644..d73384241 100644 --- a/Lua API.html +++ b/Lua API.html @@ -1252,6 +1252,9 @@ the flags in the job item.

  • dfhack.job.isSuitableMaterial(job_item, mat_type, mat_index)

    Likewise, if replacing material.

  • +
  • dfhack.job.getName(job)

    +

    Returns the job's description, as seen in the Units and Jobs screens.

    +
  • @@ -1420,6 +1423,12 @@ Returns false in case of error.

  • dfhack.items.getSubtypeDef(item_type, subtype)

    Returns the raw definition for the given item type and subtype, or nil if invalid.

  • +
  • dfhack.items.getItemBaseValue(item_type, subtype, material, mat_index)

    +

    Calculates the base value for an item of the specified type and material.

    +
  • +
  • dfhack.items.getValue(item)

    +

    Calculates the Basic Value of an item, as seen in the View Item screen.

    +
  • diff --git a/Lua API.rst b/Lua API.rst index 072d9265f..9afbb1e78 100644 --- a/Lua API.rst +++ b/Lua API.rst @@ -979,6 +979,10 @@ Job module Likewise, if replacing material. +* ``dfhack.job.getName(job)`` + + Returns the job's description, as seen in the Units and Jobs screens. + Units module ------------ @@ -1187,6 +1191,14 @@ Items module Returns the raw definition for the given item type and subtype, or *nil* if invalid. +* ``dfhack.items.getItemBaseValue(item_type, subtype, material, mat_index)`` + + Calculates the base value for an item of the specified type and material. + +* ``dfhack.items.getValue(item)`` + + Calculates the Basic Value of an item, as seen in the View Item screen. + Maps module ----------- diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 4288802ea..3a374445f 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1476,6 +1476,8 @@ static const LuaWrapper::FunctionReg dfhack_items_module[] = { WRAPM(Items, isCasteMaterial), WRAPM(Items, getSubtypeCount), WRAPM(Items, getSubtypeDef), + WRAPM(Items, getItemBaseValue), + WRAPM(Items, getValue), WRAPN(moveToGround, items_moveToGround), WRAPN(moveToContainer, items_moveToContainer), WRAPN(moveToBuilding, items_moveToBuilding), diff --git a/library/include/modules/Items.h b/library/include/modules/Items.h index 34ca98162..e5b6eb4df 100644 --- a/library/include/modules/Items.h +++ b/library/include/modules/Items.h @@ -173,5 +173,11 @@ DFHACK_EXPORT bool remove(MapExtras::MapCache &mc, df::item *item, bool no_uncat /// Detaches the items from its current location and turns it into a projectile DFHACK_EXPORT df::proj_itemst *makeProjectile(MapExtras::MapCache &mc, df::item *item); + +/// Gets value of base-quality item with specified type and material +DFHACK_EXPORT int getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat_type, int32_t mat_subtype); + +/// Gets the value of a specific item, ignoring civ values and trade agreements +DFHACK_EXPORT int getValue(df::item *item); } } diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index b243e5d85..d766b4166 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -1022,3 +1022,300 @@ df::proj_itemst *Items::makeProjectile(MapExtras::MapCache &mc, df::item *item) return proj; } + +int Items::getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat_type, int32_t mat_subtype) +{ + int value = 0; + switch (item_type) + { + case item_type::BAR: + case item_type::SMALLGEM: + case item_type::BLOCKS: + case item_type::SKIN_TANNED: + value = 5; + break; + + case item_type::ROUGH: + case item_type::BOULDER: + case item_type::WOOD: + value = 3; + break; + + case item_type::DOOR: + case item_type::FLOODGATE: + case item_type::BED: + case item_type::CHAIR: + case item_type::CHAIN: + case item_type::FLASK: + case item_type::GOBLET: + case item_type::INSTRUMENT: + case item_type::TOY: + case item_type::CAGE: + case item_type::BARREL: + case item_type::BUCKET: + case item_type::ANIMALTRAP: + case item_type::TABLE: + case item_type::COFFIN: + case item_type::BOX: + case item_type::BIN: + case item_type::ARMORSTAND: + case item_type::WEAPONRACK: + case item_type::CABINET: + case item_type::FIGURINE: + case item_type::AMULET: + case item_type::SCEPTER: + case item_type::CROWN: + case item_type::RING: + case item_type::EARRING: + case item_type::BRACELET: + case item_type::GEM: + case item_type::ANVIL: + case item_type::TOTEM: + case item_type::BACKPACK: + case item_type::QUIVER: + case item_type::BALLISTAARROWHEAD: + case item_type::PIPE_SECTION: + case item_type::HATCH_COVER: + case item_type::GRATE: + case item_type::QUERN: + case item_type::MILLSTONE: + case item_type::SPLINT: + case item_type::CRUTCH: + case item_type::SLAB: + case item_type::BOOK: + value = 10; + break; + + case item_type::WINDOW: + case item_type::STATUE: + value = 25; + break; + + case item_type::CORPSE: + case item_type::CORPSEPIECE: + case item_type::REMAINS: + return 0; + + case item_type::WEAPON: + if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.weapons.size()) + value = world->raws.itemdefs.weapons[item_subtype]->value; + else + value = 10; + break; + + case item_type::ARMOR: + if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.armor.size()) + value = world->raws.itemdefs.armor[item_subtype]->value; + else + value = 10; + break; + + case item_type::SHOES: + if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.shoes.size()) + value = world->raws.itemdefs.shoes[item_subtype]->value; + else + value = 5; + break; + + case item_type::SHIELD: + if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.shields.size()) + value = world->raws.itemdefs.shields[item_subtype]->value; + else + value = 10; + break; + + case item_type::HELM: + if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.helms.size()) + value = world->raws.itemdefs.helms[item_subtype]->value; + else + value = 10; + break; + + case item_type::GLOVES: + if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.gloves.size()) + value = world->raws.itemdefs.gloves[item_subtype]->value; + else + value = 5; + break; + + case item_type::AMMO: + if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.ammo.size()) + value = world->raws.itemdefs.ammo[item_subtype]->value; + else + value = 1; + break; + + case item_type::MEAT: + case item_type::PLANT: + case item_type::LEAVES: + case item_type::CHEESE: + value = 2; + break; + + case item_type::FISH: + case item_type::FISH_RAW: + case item_type::EGG: + value = 2; + if (mat_type >= 0 && mat_type < world->raws.creatures.all.size()) + { + auto creature = world->raws.creatures.all[mat_type]; + if (mat_subtype >= 0 && mat_type < creature->caste.size()) + { + auto caste = creature->caste[mat_subtype]; + mat_type = caste->misc.bone_mat; + mat_subtype = caste->misc.bone_matidx; + } + } + break; + + case item_type::VERMIN: + value = 0; + if (mat_type >= 0 && mat_type < world->raws.creatures.all.size()) + { + auto creature = world->raws.creatures.all[mat_type]; + if (mat_subtype >= 0 && mat_type < creature->caste.size()) + value = creature->caste[mat_subtype]->misc.petvalue; + } + value /= 2; + if (!value) + return 1; + return value; + + case item_type::PET: + if (mat_type >= 0 && mat_type < world->raws.creatures.all.size()) + { + auto creature = world->raws.creatures.all[mat_type]; + if (mat_subtype >= 0 && mat_type < creature->caste.size()) + return creature->caste[mat_subtype]->misc.petvalue; + } + return 0; + + case item_type::SEEDS: + case item_type::DRINK: + case item_type::POWDER_MISC: + case item_type::LIQUID_MISC: + case item_type::COIN: + case item_type::GLOB: + case item_type::ORTHOPEDIC_CAST: + value = 1; + break; + + case item_type::THREAD: + value = 6; + break; + + case item_type::CLOTH: + value = 7; + break; + + case item_type::PANTS: + if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.pants.size()) + value = world->raws.itemdefs.pants[item_subtype]->value; + else + value = 10; + break; + + case item_type::CATAPULTPARTS: + case item_type::BALLISTAPARTS: + case item_type::TRAPPARTS: + value = 30; + break; + + case item_type::SIEGEAMMO: + case item_type::TRACTION_BENCH: + value = 20; + break; + + case item_type::TRAPCOMP: + if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.trapcomps.size()) + value = world->raws.itemdefs.trapcomps[item_subtype]->value; + else + value = 10; + break; + + case item_type::FOOD: + return 10; + +// case item_type::ROCK: + default: + return 0; + + case item_type::TOOL: + if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.tools.size()) + value = world->raws.itemdefs.tools[item_subtype]->value; + else + value = 10; + break; + } + + MaterialInfo mat; + if (mat.decode(mat_type, mat_subtype)) + value *= mat.material->material_value; + return value; +} + +int Items::getValue(df::item *item) +{ + CHECK_NULL_POINTER(item); + + int16_t item_type = item->getType(); + int16_t item_subtype = item->getSubtype(); + int16_t mat_type = item->getMaterial(); + int32_t mat_subtype = item->getMaterialIndex(); + + // Get base value for item type, subtype, and material + int value = getItemBaseValue(item_type, item_subtype, mat_type, mat_subtype); + + // Ignore entity value modifications + + // Improve value based on quality + int quality = item->getQuality(); + value *= (quality + 1); + if (quality == 5) + value *= 2; + + // Add improvement values + int impValue = item->getThreadDyeValue(NULL) + item->getImprovementsValue(NULL); + if (item_type == item_type::AMMO) // Ammo improvements are worth less + impValue /= 30; + value += impValue; + + // Degrade value due to wear + switch (item->getWear()) + { + case 1: + value = value * 3 / 4; + break; + case 2: + value = value / 2; + break; + case 3: + value = value / 4; + break; + } + + // Ignore value bonuses from magic, since that never actually happens + + // Artifacts have 10x value + if (item->flags.bits.artifact_mood) + value *= 10; + + // Boost value from stack size + value *= item->getStackSize(); + // ...but not for coins + if (item_type == item_type::COIN) + { + value /= 500; + if (!value) + value = 1; + } + + // Handle vermin swarms + if (item_type == item_type::VERMIN || item_type == item_type::PET) + { + int divisor = world->raws.creatures.all[mat_type]->caste[mat_subtype]->misc.petvalue_divisor; + if (divisor > 1) + value /= divisor; + } + return value; +} \ No newline at end of file