Petr Mrázek 2012-03-04 11:58:37 +01:00
commit 99abd4102d
6 changed files with 360 additions and 54 deletions

@ -467,7 +467,7 @@ void MaterialInfo::getMatchBits(df::job_item_flags2 &ok, df::job_item_flags2 &ma
TEST(fire_safe, material->heat.melting_point > 11000);
TEST(magma_safe, material->heat.melting_point > 12000);
TEST(deep_material, FLAG(inorganic, inorganic_flags::DEEP_ANY));
TEST(deep_material, FLAG(inorganic, inorganic_flags::SPECIAL));
TEST(non_economic, inorganic && !(ui && ui->economic_stone[index]));
TEST(plant, plant);

@ -11,18 +11,22 @@
#include "DataDefs.h"
#include "df/world.h"
#include "df/ui_advmode.h"
#include "df/item.h"
#include "df/unit.h"
#include "df/unit_inventory_item.h"
#include "df/map_block.h"
#include "df/nemesis_record.h"
#include "df/historical_figure.h"
#include "df/general_ref_is_nemesisst.h"
#include "df/general_ref_contains_itemst.h"
#include "df/general_ref_building_civzone_assignedst.h"
#include "df/material.h"
#include "df/craft_material_class.h"
#include "df/viewscreen_optionst.h"
#include "df/viewscreen_dungeonmodest.h"
#include "df/viewscreen_dungeon_monsterstatusst.h"
#include <math.h>
using namespace DFHack;
using namespace df::enums;
@ -59,6 +63,9 @@ DFhackCExport command_result plugin_init ( Core * c, std::vector <PluginCommand>
" list-equipped [all]\n"
" List armor and weapons equipped by your companions.\n"
" If all is specified, also lists non-metal clothing.\n"
" metal-detector [all-types] [non-trader]\n"
" Reveal metal armor and weapons in shops. The options\n"
" disable the checks on item type and being in shop.\n"
));
commands.push_back(PluginCommand(
@ -201,7 +208,7 @@ bool bodySwap(Core *c, df::unit *player)
df::nemesis_record *getPlayerNemesis(Core *c, bool restore_swap)
{
auto real_nemesis = df::nemesis_record::find(ui_advmode->player_id);
auto real_nemesis = vector_get(world->nemesis.all, ui_advmode->player_id);
if (!real_nemesis || !real_nemesis->unit)
{
c->con.printerr("Invalid player nemesis id: %d\n", ui_advmode->player_id);
@ -274,7 +281,11 @@ void sortCompanionNemesis(std::vector<nemesis_record*> *list, int player_id = -1
output.reserve(list->size());
if (player_id < 0)
player_id = ui_advmode->player_id;
{
auto real_nemesis = vector_get(world->nemesis.all, ui_advmode->player_id);
if (real_nemesis)
player_id = real_nemesis->id;
}
// Index records; find the player
for (size_t i = 0; i < list->size(); i++)
@ -380,6 +391,87 @@ void listUnitInventory(std::vector<inv_item> *list, df::unit *unit)
}
}
bool isShopItem(df::item *item)
{
for (size_t k = 0; k < item->itemrefs.size(); k++)
{
auto ref = item->itemrefs[k];
if (virtual_cast<df::general_ref_building_civzone_assignedst>(ref))
return true;
}
return false;
}
bool isWeaponArmor(df::item *item)
{
using namespace df::enums::item_type;
switch (item->getType()) {
case HELM:
case ARMOR:
case WEAPON:
case AMMO:
case GLOVES:
case PANTS:
case SHOES:
return true;
default:
return false;
}
}
int containsMetalItems(df::item *item, bool all, bool non_trader)
{
int cnt = 0;
auto &refs = item->itemrefs;
for (size_t i = 0; i < refs.size(); i++)
{
auto ref = refs[i];
if (!strict_virtual_cast<df::general_ref_contains_itemst>(ref))
continue;
df::item *child = ref->getItem();
if (!child) continue;
cnt += containsMetalItems(child, all, non_trader);
}
if (!non_trader && !isShopItem(item))
return cnt;
if (!all && !isWeaponArmor(item))
return cnt;
MaterialInfo minfo(item);
if (minfo.getCraftClass() != craft_material_class::Metal)
return cnt;
return ++cnt;
}
void joinCounts(std::map<df::coord, int> &counts)
{
for (auto it = counts.begin(); it != counts.end(); it++)
{
df::coord pt = it->first;
while (pt.x > 0 && counts.count(pt - df::coord(1,0,0)))
pt.x--;
while (pt.y > 0 &&counts.count(pt - df::coord(0,1,0)))
pt.y--;
while (pt.x < 0 && counts.count(pt + df::coord(1,0,0)))
pt.x++;
while (pt.y < 0 &&counts.count(pt + df::coord(0,1,0)))
pt.y++;
if (pt == it->first)
continue;
counts[pt] += it->second;
it->second = 0;
}
}
/*********************
* FORMATTING *
*********************/
@ -428,6 +520,37 @@ static size_t formatSize(std::vector<std::string> *out, const std::map<std::stri
return len;
}
static std::string formatDirection(df::coord delta)
{
std::string ns, ew, dir;
if (delta.x > 0)
ew = "E";
else if (delta.x < 0)
ew = "W";
if (delta.y > 0)
ns = "S";
else if (delta.y < 0)
ns = "N";
if (abs(delta.x) > abs(delta.y)*5)
dir = ew;
else if (abs(delta.y) > abs(delta.x)*5)
dir = ns;
else if (abs(delta.x) > abs(delta.y)*2)
dir = ew + ns + ew;
else if (abs(delta.y) > abs(delta.x)*2)
dir = ns + ns + ew;
else if (delta.x || delta.y)
dir = ns + ew;
else
dir = "***";
int dist = (int)sqrt((double)(delta.x*delta.x + delta.y*delta.y));
return stl_sprintf("%d away %s %+d", dist, dir.c_str(), delta.z);
}
static void printEquipped(Core *c, df::unit *unit, bool all)
{
std::vector<inv_item> items;
@ -465,6 +588,12 @@ static void printEquipped(Core *c, df::unit *unit, bool all)
// Add to the right table
int count = item->getStackSize();
if (is_weapon)
{
weapons[name] += count;
continue;
}
switch (iinfo.type) {
case item_type::HELM:
head[name] += count;
@ -570,7 +699,7 @@ command_result adv_bodyswap (Core * c, std::vector <std::string> & parameters)
// Permanently re-link everything
if (permanent)
{
ui_advmode->player_id = new_nemesis->id;
ui_advmode->player_id = linear_index(world->nemesis.all, new_nemesis);
// Flag 0 appears to be the 'active adventurer' flag, and
// the player_id field above seems to be computed using it
@ -636,6 +765,67 @@ command_result adv_tools (Core * c, std::vector <std::string> & parameters)
return CR_OK;
}
else if (command == "metal-detector")
{
bool all = false, non_trader = false;
for (size_t i = 1; i < parameters.size(); i++)
{
if (parameters[i] == "all-types")
all = true;
else if (parameters[i] == "non-trader")
non_trader = true;
else
return CR_WRONG_USAGE;
}
auto *player = getPlayerNemesis(c, false);
if (!player)
return CR_FAILURE;
df::coord player_pos = player->unit->pos;
int total = 0;
std::map<df::coord,int> counts;
for (size_t i = 0; i < world->map.map_blocks.size(); i++)
{
df::map_block *block = world->map.map_blocks[i];
for (size_t j = 0; j < block->items.size(); j++)
{
df::item *item = df::item::find(block->items[j]);
if (!item)
continue;
int num = containsMetalItems(item, all, non_trader);
if (!num)
continue;
total += num;
counts[(item->pos - player_pos)/10] += num;
auto &designations = block->designation;
auto &dgn = designations[item->pos.x%16][item->pos.y%16];
dgn.bits.hidden = 0; // revealed
dgn.bits.pile = 1; // visible
}
}
joinCounts(counts);
c->con.print("%d items of metal merchandise found in the vicinity.\n", total);
for (auto it = counts.begin(); it != counts.end(); it++)
{
if (!it->second)
continue;
df::coord delta = it->first * 10;
c->con.print(" %s: %d\n", formatDirection(delta).c_str(), it->second);
}
return CR_OK;
}
else
return CR_WRONG_USAGE;
}

@ -19,19 +19,23 @@ using namespace std;
#include "modules/Gui.h"
#include "modules/Materials.h"
#include "modules/MapCache.h"
#include "modules/Buildings.h"
#include "MiscUtils.h"
#include "df/world.h"
#include "df/world_raws.h"
#include "df/building_def.h"
using std::vector;
using std::string;
using namespace DFHack;
using namespace df::enums;
using df::global::world;
using df::global::cursor;
command_result df_probe (Core * c, vector <string> & parameters);
command_result df_cprobe (Core * c, vector <string> & parameters);
command_result df_bprobe (Core * c, vector <string> & parameters);
DFHACK_PLUGIN("probe");
@ -44,6 +48,9 @@ DFhackCExport command_result plugin_init ( Core * c, std::vector <PluginCommand>
commands.push_back(PluginCommand("cprobe",
"A creature probe",
df_cprobe));
commands.push_back(PluginCommand("bprobe",
"A simple building probe",
df_bprobe));
return CR_OK;
}
@ -270,3 +277,61 @@ command_result df_probe (Core * c, vector <string> & parameters)
con << std::endl;
return CR_OK;
}
command_result df_bprobe (Core * c, vector <string> & parameters)
{
CoreSuspender suspend(c);
if(cursor->x == -30000)
{
c->con.printerr("No cursor; place cursor over tile to probe.\n");
return CR_FAILURE;
}
for (size_t i = 0; i < world->buildings.all.size(); i++)
{
Buildings::t_building building;
if (!Buildings::Read(i, building))
continue;
if (!(building.x1 <= cursor->x && cursor->x <= building.x2 &&
building.y1 <= cursor->y && cursor->y <= building.y2 &&
building.z == cursor->z))
continue;
string name;
building.origin->getName(&name);
c->con.print("Building %i - \"%s\" - type %s", building.origin->id, name.c_str(), ENUM_KEY_STR(building_type, building.type));
switch (building.type)
{
case building_type::Furnace:
c->con.print(", subtype %s", ENUM_KEY_STR(furnace_type, building.furnace_type));
if (building.furnace_type == furnace_type::Custom)
c->con.print(", custom type %i (%s)", building.custom_type, world->raws.buildings.all[building.custom_type]->code.c_str());
break;
case building_type::Workshop:
c->con.print(", subtype %s", ENUM_KEY_STR(workshop_type, building.workshop_type));
if (building.workshop_type == workshop_type::Custom)
c->con.print(", custom type %i (%s)", building.custom_type, world->raws.buildings.all[building.custom_type]->code.c_str());
break;
case building_type::Construction:
c->con.print(", subtype %s", ENUM_KEY_STR(construction_type, building.construction_type));
break;
case building_type::Shop:
c->con.print(", subtype %s", ENUM_KEY_STR(shop_type, building.shop_type));
break;
case building_type::SiegeEngine:
c->con.print(", subtype %s", ENUM_KEY_STR(siegeengine_type, building.siegeengine_type));
break;
case building_type::Trap:
c->con.print(", subtype %s", ENUM_KEY_STR(trap_type, building.trap_type));
break;
default:
if (building.subtype != -1)
c->con.print(", subtype %i", building.subtype);
break;
}
c->con.print("\n");
}
return CR_OK;
}

@ -120,6 +120,26 @@ command_result nopause (Core * c, std::vector <std::string> & parameters)
return CR_OK;
}
void revealAdventure(DFHack::Core * c)
{
for (size_t i = 0; i < world->map.map_blocks.size(); i++)
{
df::map_block *block = world->map.map_blocks[i];
// in 'no-hell'/'safe' mode, don't reveal blocks with hell and adamantine
if (!isSafe(block->map_pos))
continue;
DFHack::designations40d & designations = block->designation;
// for each tile in block
for (uint32_t x = 0; x < 16; x++) for (uint32_t y = 0; y < 16; y++)
{
// set to revealed
designations[x][y].bits.hidden = 0;
// and visible
designations[x][y].bits.pile = 1;
}
}
c->con.print("Local map revealed.\n");
}
command_result reveal(DFHack::Core * c, std::vector<std::string> & params)
{
@ -157,16 +177,21 @@ command_result reveal(DFHack::Core * c, std::vector<std::string> & params)
CoreSuspender suspend(c);
DFHack::World *World =c->getWorld();
if (!Maps::IsValid())
{
c->con.printerr("Map is not available!\n");
return CR_FAILURE;
}
t_gamemodes gm;
World->ReadGameMode(gm);
if(gm.g_mode != GAMEMODE_DWARF)
if(gm.g_mode == GAMEMODE_ADVENTURE)
{
con.printerr("Only in fortress mode.\n");
return CR_FAILURE;
revealAdventure(c);
return CR_OK;
}
if (!Maps::IsValid())
if(gm.g_mode != GAMEMODE_DWARF)
{
c->con.printerr("Map is not available!\n");
con.printerr("Only in fortress mode.\n");
return CR_FAILURE;
}
@ -231,6 +256,11 @@ command_result unreveal(DFHack::Core * c, std::vector<std::string> & params)
CoreSuspender suspend(c);
DFHack::World *World =c->getWorld();
if (!Maps::IsValid())
{
c->con.printerr("Map is not available!\n");
return CR_FAILURE;
}
t_gamemodes gm;
World->ReadGameMode(gm);
if(gm.g_mode != GAMEMODE_DWARF)
@ -238,11 +268,6 @@ command_result unreveal(DFHack::Core * c, std::vector<std::string> & params)
con.printerr("Only in fortress mode.\n");
return CR_FAILURE;
}
if (!Maps::IsValid())
{
c->con.printerr("Map is not available!\n");
return CR_FAILURE;
}
// Sanity check: map size
uint32_t x_max_b, y_max_b, z_max_b;

@ -71,50 +71,64 @@ static ParseCxxHandler(func, handler, fixFunc)
y = x;
z = x;
EHCookieOffset=0; GSCookieOffset=0;
if (matchBytes(x,"8B5424088D420C"))
// 8B 54 24 08 mov edx, [esp+8]
// 8D 42 0C lea eax, [edx+0Ch]
// 8B 54 24 08 mov edx, [esp+8]
if (matchBytes(x,"8B5424088D02"))
x = x+6;
// 8D 02 lea eax, [edx]
else if (matchBytes(x,"8B5424088D42"))
x = x+7;
// 8D 42 xx lea eax, [edx+XXh]
else if (matchBytes(x,"8B5424088D82"))
x = x+10;
// 8D 82 xx xx xx xx lea eax, [edx+XXh]
else {
Message("Function at %08X not recognized as exception handler!\n",x);
return;
}
//EH cookie check:
// 8B 4A xx mov ecx, [edx-XXh]
// OR
// 8B 8A xx xx xx xx mov ecx, [edx-XXh]
// 33 C8 xor ecx, eax
// E8 xx xx xx xx call __security_check_cookie
if (matchBytes(x,"8B4A??33C8E8"))
{
//byte argument
EHCookieOffset = (~Byte(x+2)+1)&0xFF;
EHCookieOffset = 12 + EHCookieOffset;
x = x+10;
}
else if (matchBytes(x,"8B8A????????33C8E8"))
{
//dword argument
EHCookieOffset = (~Dword(x+2)+1);
EHCookieOffset = 12 + EHCookieOffset;
x = x+13;
}
if (matchBytes(x,"83C0"))
x = x + 3;
// 8B 4A xx add eax, XXh
if (matchBytes(x,"8B4A??33C8E8"))
{
//EH cookie check:
// 8B 4A xx mov ecx, [edx-XXh]
// OR
// 8B 8A xx xx xx xx mov ecx, [edx-XXh]
// 33 C8 xor ecx, eax
// E8 xx xx xx xx call __security_check_cookie
x = x+7;
if (matchBytes(x,"8B4A??33C8E8"))
{
//byte argument
EHCookieOffset = (~Byte(x+2)+1)&0xFF;
EHCookieOffset = 12 + EHCookieOffset;
x = x+10;
}
else if (matchBytes(x,"8B8A????????33C8E8"))
{
//dword argument
EHCookieOffset = (~Dword(x+2)+1);
EHCookieOffset = 12 + EHCookieOffset;
x = x+13;
}
if (matchBytes(x,"8B4A??33C8E8"))
{
// 8B 4A xx mov ecx, [edx-XXh]
// 33 C8 xor ecx, eax
// E8 xx xx xx xx call __security_check_cookie
GSCookieOffset = (~Byte(x+2)+1)&0xFF;
GSCookieOffset = 12 + GSCookieOffset;
x = x+10;
}
else if (matchBytes(x,"8B8A????????33C8E8"))
{
//dword argument
GSCookieOffset = (~Dword(x+9)+1);
GSCookieOffset = 12 + GSCookieOffset;
x = x+13;
}
//Message("EH3: EH Cookie=%02X, GSCookie=%02X\n",EHCookieOffset, GSCookieOffset);
GSCookieOffset = (~Byte(x+2)+1)&0xFF;
GSCookieOffset = 12 + GSCookieOffset;
x = x+10;
}
else if (matchBytes(x,"8B8A????????33C8E8"))
{
//dword argument
GSCookieOffset = (~Dword(x+9)+1);
GSCookieOffset = 12 + GSCookieOffset;
x = x+13;
}
//Message("EH3: EH Cookie=%02X, GSCookie=%02X\n",EHCookieOffset, GSCookieOffset);
if (Byte(x)==0xB8) {
// 8B 4A xx xx xx mov eax, offset FuncInfo
x = Dword(x+1);
}
else {

@ -293,6 +293,7 @@ struct _s_RTTIBaseClassDescriptor
DWORD numContainedBases; //number of nested classes following in the array
struct PMD where; //some displacement info
DWORD attributes; //usually 0, sometimes 10h
struct _s_RTTIClassHierarchyDescriptor *pClassHierarchyDescriptor; //of this base class
};
struct PMD
@ -314,6 +315,15 @@ struct PMD
DwordCmt(x+4, "numContainedBases");
DwordArrayCmt(x+8, 3, "PMD where");
DwordCmt(x+20, "attributes");
OffCmt(x+24, "pClassHierarchyDescriptor");
if(substr(Name(Dword(x+24)),0,5) != "??_R3")
{
// assign dummy name to prevent infinite recursion
MakeName(Dword(x+24),"??_R3"+form("%06x",x)+"@@8");
// a proper name will be assigned shortly
Parse_CHD(Dword(x+24),indent-1);
}
s = Parse_TD(Dword(x), indent+1);
//??_R1A@?0A@A@B@@8 = B::`RTTI Base Class Descriptor at (0,-1,0,0)'
@ -414,9 +424,11 @@ static Parse_CHD(x, indent)
i=0;
DumpNestedClass(a, indent, n);
indent=indent+1;
while(i<n)
while(i<=n)
{
p = Dword(a);
if (i==n && p!=0)
break;
//Message(indent_str+" BaseClass[%02d]: %08.8Xh\n", i, p);
OffCmt(a, form("BaseClass[%02d]", i));
if (i==0)