2012-02-25 06:08:05 -07:00
|
|
|
#include "Core.h"
|
|
|
|
#include "Console.h"
|
|
|
|
#include "Export.h"
|
|
|
|
#include "PluginManager.h"
|
|
|
|
#include "MiscUtils.h"
|
|
|
|
#include "modules/World.h"
|
|
|
|
#include "modules/Translation.h"
|
2012-02-28 06:58:08 -07:00
|
|
|
#include "modules/Materials.h"
|
|
|
|
#include "modules/Items.h"
|
2012-02-25 06:08:05 -07:00
|
|
|
|
|
|
|
#include "DataDefs.h"
|
|
|
|
#include "df/world.h"
|
|
|
|
#include "df/ui_advmode.h"
|
|
|
|
#include "df/unit.h"
|
2012-02-28 06:58:08 -07:00
|
|
|
#include "df/unit_inventory_item.h"
|
2012-02-25 06:08:05 -07:00
|
|
|
#include "df/nemesis_record.h"
|
2012-02-28 06:58:08 -07:00
|
|
|
#include "df/historical_figure.h"
|
2012-02-25 06:08:05 -07:00
|
|
|
#include "df/general_ref_is_nemesisst.h"
|
2012-02-28 06:58:08 -07:00
|
|
|
#include "df/general_ref_contains_itemst.h"
|
|
|
|
#include "df/material.h"
|
|
|
|
#include "df/craft_material_class.h"
|
2012-02-25 06:08:05 -07:00
|
|
|
#include "df/viewscreen_optionst.h"
|
|
|
|
#include "df/viewscreen_dungeonmodest.h"
|
|
|
|
#include "df/viewscreen_dungeon_monsterstatusst.h"
|
|
|
|
|
|
|
|
|
|
|
|
using namespace DFHack;
|
|
|
|
using namespace df::enums;
|
|
|
|
|
|
|
|
using df::global::world;
|
|
|
|
using df::global::ui_advmode;
|
|
|
|
|
2012-02-28 06:58:08 -07:00
|
|
|
using df::nemesis_record;
|
|
|
|
using df::historical_figure;
|
|
|
|
|
2012-02-25 06:08:05 -07:00
|
|
|
using namespace DFHack::Simple::Translation;
|
|
|
|
|
2012-02-28 06:58:08 -07:00
|
|
|
/*********************
|
|
|
|
* PLUGIN INTERFACE *
|
|
|
|
*********************/
|
|
|
|
|
2012-02-25 06:08:05 -07:00
|
|
|
static bool bodyswap_hotkey(Core *c, df::viewscreen *top);
|
|
|
|
|
|
|
|
command_result adv_bodyswap (Core * c, std::vector <std::string> & parameters);
|
2012-02-28 06:58:08 -07:00
|
|
|
command_result adv_tools (Core * c, std::vector <std::string> & parameters);
|
2012-02-25 06:08:05 -07:00
|
|
|
|
|
|
|
DFHACK_PLUGIN("advtools");
|
|
|
|
|
|
|
|
DFhackCExport command_result plugin_init ( Core * c, std::vector <PluginCommand> &commands)
|
|
|
|
{
|
|
|
|
commands.clear();
|
|
|
|
|
|
|
|
if (!ui_advmode)
|
|
|
|
return CR_OK;
|
|
|
|
|
2012-02-28 06:58:08 -07:00
|
|
|
commands.push_back(PluginCommand(
|
|
|
|
"advtools", "Adventure mode tools.",
|
|
|
|
adv_tools, false,
|
|
|
|
" list-equipped [all]\n"
|
|
|
|
" List armor and weapons equipped by your companions.\n"
|
|
|
|
" If all is specified, also lists non-metal clothing.\n"
|
|
|
|
));
|
|
|
|
|
2012-02-25 06:08:05 -07:00
|
|
|
commands.push_back(PluginCommand(
|
|
|
|
"adv-bodyswap", "Change the adventurer unit.",
|
|
|
|
adv_bodyswap, bodyswap_hotkey,
|
|
|
|
" - When viewing unit details, body-swaps into that unit.\n"
|
|
|
|
" - In the main adventure mode screen, reverts transient swap.\n"
|
|
|
|
"Options:\n"
|
|
|
|
" force\n"
|
|
|
|
" Allow swapping into non-companion units.\n"
|
|
|
|
" permanent\n"
|
|
|
|
" Permanently change the unit to be the adventurer.\n"
|
|
|
|
" Otherwise it will revert if adv-bodyswap is called\n"
|
|
|
|
" in the main screen, or if the main menu, Fast Travel\n"
|
|
|
|
" or Sleep/Wait screen is opened.\n"
|
2012-02-28 06:58:08 -07:00
|
|
|
" noinherit\n"
|
|
|
|
" In permanent mode, don't reassign companions to the new unit.\n"
|
2012-02-25 06:08:05 -07:00
|
|
|
));
|
|
|
|
|
|
|
|
return CR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
DFhackCExport command_result plugin_shutdown ( Core * c )
|
|
|
|
{
|
|
|
|
return CR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
df::nemesis_record *getPlayerNemesis(Core *c, bool restore_swap);
|
|
|
|
|
|
|
|
static bool in_transient_swap = false;
|
|
|
|
|
|
|
|
DFhackCExport command_result plugin_onstatechange(Core* c, state_change_event event)
|
|
|
|
{
|
|
|
|
switch (event) {
|
|
|
|
case SC_GAME_LOADED:
|
|
|
|
case SC_GAME_UNLOADED:
|
|
|
|
in_transient_swap = false;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return CR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
DFhackCExport command_result plugin_onupdate ( Core * c )
|
|
|
|
{
|
|
|
|
// Revert transient swaps before trouble happens
|
|
|
|
if (in_transient_swap)
|
|
|
|
{
|
|
|
|
auto screen = c->getTopViewscreen();
|
|
|
|
bool revert = false;
|
|
|
|
|
|
|
|
if (strict_virtual_cast<df::viewscreen_dungeonmodest>(screen))
|
|
|
|
{
|
|
|
|
using namespace df::enums::ui_advmode_menu;
|
|
|
|
|
|
|
|
switch (ui_advmode->menu)
|
|
|
|
{
|
|
|
|
case Travel:
|
|
|
|
case Sleep:
|
|
|
|
revert = true;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strict_virtual_cast<df::viewscreen_optionst>(screen))
|
|
|
|
{
|
|
|
|
// Options may mean save game
|
|
|
|
revert = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (revert)
|
|
|
|
{
|
|
|
|
getPlayerNemesis(c, true);
|
|
|
|
in_transient_swap = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return CR_OK;
|
|
|
|
}
|
|
|
|
|
2012-02-28 06:58:08 -07:00
|
|
|
/*********************
|
|
|
|
* UTILITY FUNCTIONS *
|
|
|
|
*********************/
|
|
|
|
|
2012-02-25 06:08:05 -07:00
|
|
|
static bool bodyswap_hotkey(Core *c, df::viewscreen *top)
|
|
|
|
{
|
|
|
|
return !!virtual_cast<df::viewscreen_dungeonmodest>(top) ||
|
|
|
|
!!virtual_cast<df::viewscreen_dungeon_monsterstatusst>(top);
|
|
|
|
}
|
|
|
|
|
|
|
|
df::unit *getCurUnit(Core *c)
|
|
|
|
{
|
|
|
|
auto top = c->getTopViewscreen();
|
|
|
|
|
|
|
|
if (VIRTUAL_CAST_VAR(ms, df::viewscreen_dungeon_monsterstatusst, top))
|
|
|
|
return ms->unit;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
df::nemesis_record *getNemesis(df::unit *unit)
|
|
|
|
{
|
|
|
|
if (!unit)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < unit->refs.size(); i++)
|
|
|
|
{
|
|
|
|
df::nemesis_record *rv = unit->refs[i]->getNemesis();
|
|
|
|
if (rv && rv->unit == unit)
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool bodySwap(Core *c, df::unit *player)
|
|
|
|
{
|
|
|
|
if (!player)
|
|
|
|
{
|
|
|
|
c->con.printerr("Unit to swap is NULL\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto &vec = world->units.other[0];
|
|
|
|
|
|
|
|
int idx = linear_index(vec, player);
|
|
|
|
if (idx < 0)
|
|
|
|
{
|
|
|
|
c->con.printerr("Unit to swap not found: %d\n", player->id);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (idx != 0)
|
|
|
|
std::swap(vec[0], vec[idx]);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
df::nemesis_record *getPlayerNemesis(Core *c, bool restore_swap)
|
|
|
|
{
|
|
|
|
auto real_nemesis = df::nemesis_record::find(ui_advmode->player_id);
|
|
|
|
if (!real_nemesis || !real_nemesis->unit)
|
|
|
|
{
|
|
|
|
c->con.printerr("Invalid player nemesis id: %d\n", ui_advmode->player_id);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (restore_swap)
|
|
|
|
{
|
|
|
|
df::unit *ctl = world->units.other[0][0];
|
|
|
|
auto ctl_nemesis = getNemesis(ctl);
|
|
|
|
|
|
|
|
if (ctl_nemesis != real_nemesis)
|
|
|
|
{
|
|
|
|
if (!bodySwap(c, real_nemesis->unit))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
auto name = TranslateName(&real_nemesis->unit->name, false);
|
|
|
|
c->con.print("Returned into the body of %s.\n", name.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
real_nemesis->unit->relations.group_leader_id = -1;
|
|
|
|
in_transient_swap = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return real_nemesis;
|
|
|
|
}
|
|
|
|
|
|
|
|
void changeGroupLeader(df::nemesis_record *new_nemesis, df::nemesis_record *old_nemesis)
|
|
|
|
{
|
|
|
|
auto &cvec = new_nemesis->companions;
|
|
|
|
|
|
|
|
// Swap companions
|
|
|
|
cvec.swap(old_nemesis->companions);
|
|
|
|
|
|
|
|
vector_erase_at(cvec, linear_index(cvec, new_nemesis->id));
|
|
|
|
insert_into_vector(cvec, old_nemesis->id);
|
|
|
|
|
|
|
|
// Update follow
|
|
|
|
new_nemesis->group_leader_id = -1;
|
|
|
|
new_nemesis->unit->relations.group_leader_id = -1;
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < cvec.size(); i++)
|
|
|
|
{
|
|
|
|
auto nm = df::nemesis_record::find(cvec[i]);
|
|
|
|
if (!nm)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
nm->group_leader_id = new_nemesis->id;
|
|
|
|
if (nm->unit)
|
|
|
|
nm->unit->relations.group_leader_id = new_nemesis->unit_id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void copyAcquaintances(df::nemesis_record *new_nemesis, df::nemesis_record *old_nemesis)
|
|
|
|
{
|
|
|
|
auto &svec = old_nemesis->unit->adventurer_knows;
|
|
|
|
auto &tvec = new_nemesis->unit->adventurer_knows;
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < svec.size(); i++)
|
|
|
|
insert_into_vector(tvec, svec[i]);
|
|
|
|
|
|
|
|
insert_into_vector(tvec, old_nemesis->unit_id);
|
|
|
|
}
|
|
|
|
|
2012-02-28 06:58:08 -07:00
|
|
|
void sortCompanionNemesis(std::vector<nemesis_record*> *list, int player_id = -1)
|
|
|
|
{
|
|
|
|
std::map<int, nemesis_record*> table;
|
|
|
|
std::vector<nemesis_record*> output;
|
|
|
|
|
|
|
|
output.reserve(list->size());
|
|
|
|
|
|
|
|
if (player_id < 0)
|
|
|
|
player_id = ui_advmode->player_id;
|
|
|
|
|
|
|
|
// Index records; find the player
|
|
|
|
for (size_t i = 0; i < list->size(); i++)
|
|
|
|
{
|
|
|
|
auto item = (*list)[i];
|
|
|
|
if (item->id == player_id)
|
|
|
|
output.push_back(item);
|
|
|
|
else
|
|
|
|
table[item->figure->id] = item;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pull out the items by the persistent sort order
|
|
|
|
auto &order_vec = ui_advmode->companions.all_histfigs;
|
|
|
|
for (size_t i = 0; i < order_vec.size(); i++)
|
|
|
|
{
|
|
|
|
auto it = table.find(order_vec[i]);
|
|
|
|
if (it == table.end())
|
|
|
|
continue;
|
|
|
|
output.push_back(it->second);
|
|
|
|
table.erase(it);
|
|
|
|
}
|
|
|
|
|
|
|
|
// The remaining ones in reverse id order
|
|
|
|
for (auto it = table.rbegin(); it != table.rend(); ++it)
|
|
|
|
output.push_back(it->second);
|
|
|
|
|
|
|
|
list->swap(output);
|
|
|
|
}
|
|
|
|
|
|
|
|
void listCompanions(Core *c, std::vector<nemesis_record*> *list, bool units = true)
|
|
|
|
{
|
|
|
|
nemesis_record *player = getPlayerNemesis(c, false);
|
|
|
|
if (!player)
|
|
|
|
return;
|
|
|
|
|
|
|
|
list->push_back(player);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < player->companions.size(); i++)
|
|
|
|
{
|
|
|
|
auto item = nemesis_record::find(player->companions[i]);
|
|
|
|
if (item && (item->unit || !units))
|
|
|
|
list->push_back(item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string getUnitNameProfession(df::unit *unit)
|
|
|
|
{
|
|
|
|
std::string name = TranslateName(&unit->name, false) + ", ";
|
|
|
|
if (unit->custom_profession.empty())
|
|
|
|
name += ENUM_ATTR_STR(profession, caption, unit->profession);
|
|
|
|
else
|
|
|
|
name += unit->custom_profession;
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum InventoryMode {
|
|
|
|
INV_CARRIED,
|
|
|
|
INV_WEAPON,
|
|
|
|
INV_WORN,
|
|
|
|
INV_IN_CONTAINER
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef std::pair<df::item*,InventoryMode> inv_item;
|
|
|
|
|
|
|
|
static void listContainerInventory(std::vector<inv_item> *list, df::item *container)
|
|
|
|
{
|
|
|
|
auto &refs = container->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;
|
|
|
|
|
|
|
|
list->push_back(inv_item(child, INV_IN_CONTAINER));
|
|
|
|
listContainerInventory(list, child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void listUnitInventory(std::vector<inv_item> *list, df::unit *unit)
|
|
|
|
{
|
|
|
|
auto &items = unit->inventory;
|
|
|
|
for (size_t i = 0; i < items.size(); i++)
|
|
|
|
{
|
|
|
|
auto item = items[i];
|
|
|
|
InventoryMode mode;
|
|
|
|
|
|
|
|
switch (item->mode) {
|
|
|
|
case df::unit_inventory_item::Carried:
|
|
|
|
mode = INV_CARRIED;
|
|
|
|
break;
|
|
|
|
case df::unit_inventory_item::Weapon:
|
|
|
|
mode = INV_WEAPON;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
mode = INV_WORN;
|
|
|
|
}
|
|
|
|
|
|
|
|
list->push_back(inv_item(item->item, mode));
|
|
|
|
listContainerInventory(list, item->item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************
|
|
|
|
* FORMATTING *
|
|
|
|
*********************/
|
|
|
|
|
|
|
|
static void printCompanionHeader(Core *c, size_t i, df::unit *unit)
|
|
|
|
{
|
|
|
|
c->con.color(Console::COLOR_GREY);
|
|
|
|
|
|
|
|
if (i < 28)
|
|
|
|
c->con << char('a'+i);
|
|
|
|
else
|
|
|
|
c->con << i;
|
|
|
|
|
|
|
|
c->con << ": " << getUnitNameProfession(unit);
|
|
|
|
if (unit->flags1.bits.dead)
|
|
|
|
c->con << " (DEAD)";
|
|
|
|
if (unit->flags3.bits.ghostly)
|
|
|
|
c->con << " (GHOST)";
|
|
|
|
c->con << endl;
|
|
|
|
|
|
|
|
c->con.reset_color();
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t formatSize(std::vector<std::string> *out, const std::map<std::string, int> in, size_t *cnt)
|
|
|
|
{
|
|
|
|
size_t len = 0;
|
|
|
|
|
|
|
|
for (auto it = in.begin(); it != in.end(); ++it)
|
|
|
|
{
|
|
|
|
std::string line = it->first;
|
|
|
|
if (it->second != 1)
|
|
|
|
line += stl_sprintf(" [%d]", it->second);
|
|
|
|
len = std::max(len, line.size());
|
|
|
|
out->push_back(line);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (out->empty())
|
|
|
|
{
|
|
|
|
out->push_back("(none)");
|
|
|
|
len = 6;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cnt)
|
|
|
|
*cnt = std::max(*cnt, out->size());
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void printEquipped(Core *c, df::unit *unit, bool all)
|
|
|
|
{
|
|
|
|
std::vector<inv_item> items;
|
|
|
|
listUnitInventory(&items, unit);
|
|
|
|
|
|
|
|
std::map<std::string, int> head, body, legs, weapons;
|
|
|
|
|
|
|
|
for (auto it = items.begin(); it != items.end(); ++it)
|
|
|
|
{
|
|
|
|
df::item *item = it->first;
|
|
|
|
|
|
|
|
// Skip non-worn non-weapons
|
|
|
|
ItemTypeInfo iinfo(item);
|
|
|
|
|
|
|
|
bool is_weapon = (it->second == INV_WEAPON || iinfo.type == item_type::AMMO);
|
|
|
|
if (!(is_weapon || it->second == INV_WORN))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Skip non-metal, unless all
|
|
|
|
MaterialInfo minfo(item);
|
|
|
|
df::craft_material_class mclass = minfo.getCraftClass();
|
|
|
|
|
|
|
|
bool is_metal = (mclass == craft_material_class::Metal);
|
|
|
|
if (!(is_weapon || all || is_metal))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Format the name
|
|
|
|
std::string name;
|
|
|
|
if (is_metal)
|
|
|
|
name = minfo.toString() + " ";
|
|
|
|
else if (mclass != craft_material_class::None)
|
|
|
|
name = toLower(ENUM_KEY_STR(craft_material_class,mclass)) + " ";
|
|
|
|
name += iinfo.toString();
|
|
|
|
|
|
|
|
// Add to the right table
|
|
|
|
int count = item->getStackSize();
|
|
|
|
|
|
|
|
switch (iinfo.type) {
|
|
|
|
case item_type::HELM:
|
|
|
|
head[name] += count;
|
|
|
|
break;
|
|
|
|
case item_type::ARMOR:
|
|
|
|
case item_type::GLOVES:
|
|
|
|
case item_type::BACKPACK:
|
|
|
|
case item_type::QUIVER:
|
|
|
|
body[name] += count;
|
|
|
|
break;
|
|
|
|
case item_type::PANTS:
|
|
|
|
case item_type::SHOES:
|
|
|
|
legs[name] += count;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
weapons[name] += count;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::string> cols[4];
|
|
|
|
size_t sizes[4];
|
|
|
|
size_t lines = 0;
|
|
|
|
|
|
|
|
sizes[0] = formatSize(&cols[0], head, &lines);
|
|
|
|
sizes[1] = formatSize(&cols[1], body, &lines);
|
|
|
|
sizes[2] = formatSize(&cols[2], legs, &lines);
|
|
|
|
sizes[3] = formatSize(&cols[3], weapons, &lines);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < lines; i++)
|
|
|
|
{
|
|
|
|
for (int j = 0; j < 4; j++)
|
|
|
|
{
|
|
|
|
size_t sz = std::max(sizes[j], size_t(18));
|
|
|
|
c->con << "| " << std::left << std::setw(sz) << vector_get(cols[j],i) << " ";
|
|
|
|
}
|
|
|
|
|
|
|
|
c->con << "|" << std::endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************
|
|
|
|
* COMMANDS *
|
|
|
|
*********************/
|
|
|
|
|
2012-02-25 06:08:05 -07:00
|
|
|
command_result adv_bodyswap (Core * c, std::vector <std::string> & parameters)
|
|
|
|
{
|
|
|
|
// HOTKEY COMMAND; CORE IS SUSPENDED
|
|
|
|
bool force = false;
|
|
|
|
bool permanent = false;
|
|
|
|
bool no_make_leader = false;
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < parameters.size(); i++)
|
|
|
|
{
|
|
|
|
auto &item = parameters[i];
|
|
|
|
|
|
|
|
if (item == "force")
|
|
|
|
force = true;
|
|
|
|
else if (item == "permanent")
|
|
|
|
permanent = true;
|
2012-02-28 06:58:08 -07:00
|
|
|
else if (item == "noinherit")
|
2012-02-25 06:08:05 -07:00
|
|
|
no_make_leader = true;
|
|
|
|
else
|
|
|
|
return CR_WRONG_USAGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the real player; undo previous transient swap
|
|
|
|
auto real_nemesis = getPlayerNemesis(c, true);
|
|
|
|
if (!real_nemesis)
|
|
|
|
return CR_FAILURE;
|
|
|
|
|
|
|
|
// Get the unit to swap to
|
|
|
|
auto new_unit = getCurUnit(c);
|
|
|
|
auto new_nemesis = getNemesis(new_unit);
|
|
|
|
|
|
|
|
if (!new_nemesis)
|
|
|
|
{
|
|
|
|
if (new_unit)
|
|
|
|
{
|
|
|
|
c->con.printerr("Cannot swap into a non-historical unit.\n");
|
|
|
|
return CR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return CR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (new_nemesis == real_nemesis)
|
|
|
|
return CR_OK;
|
|
|
|
|
|
|
|
// Verify it's a companion
|
|
|
|
if (!force && linear_index(real_nemesis->companions, new_nemesis->id) < 0)
|
|
|
|
{
|
|
|
|
c->con.printerr("This is not your companion - use force to bodyswap.\n");
|
|
|
|
return CR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Swap
|
|
|
|
if (!bodySwap(c, new_nemesis->unit))
|
|
|
|
return CR_FAILURE;
|
|
|
|
|
|
|
|
auto name = TranslateName(&new_nemesis->unit->name, false);
|
|
|
|
c->con.print("Swapped into the body of %s.\n", name.c_str());
|
|
|
|
|
|
|
|
// Permanently re-link everything
|
|
|
|
if (permanent)
|
|
|
|
{
|
|
|
|
ui_advmode->player_id = new_nemesis->id;
|
|
|
|
|
|
|
|
// Flag 0 appears to be the 'active adventurer' flag, and
|
|
|
|
// the player_id field above seems to be computed using it
|
|
|
|
// when a savegame is loaded.
|
|
|
|
// Also, unless this is set, it is impossible to retire.
|
|
|
|
real_nemesis->flags.set(0, false);
|
|
|
|
new_nemesis->flags.set(0, true);
|
|
|
|
|
|
|
|
real_nemesis->flags.set(1, true); // former retired adventurer
|
|
|
|
new_nemesis->flags.set(2, true); // blue color in legends
|
|
|
|
|
|
|
|
// Reassign companions and acquaintances
|
|
|
|
if (!no_make_leader)
|
|
|
|
{
|
|
|
|
changeGroupLeader(new_nemesis, real_nemesis);
|
|
|
|
copyAcquaintances(new_nemesis, real_nemesis);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
in_transient_swap = true;
|
|
|
|
|
|
|
|
// Make the player unit follow around to avoid bad consequences
|
|
|
|
// if it is unloaded before the transient swap is reverted.
|
|
|
|
real_nemesis->unit->relations.group_leader_id = new_nemesis->unit_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
return CR_OK;
|
|
|
|
}
|
2012-02-28 06:58:08 -07:00
|
|
|
|
|
|
|
command_result adv_tools (Core * c, std::vector <std::string> & parameters)
|
|
|
|
{
|
|
|
|
if (parameters.empty())
|
|
|
|
return CR_WRONG_USAGE;
|
|
|
|
|
|
|
|
CoreSuspender suspend(c);
|
|
|
|
|
|
|
|
const auto &command = parameters[0];
|
|
|
|
if (command == "list-equipped")
|
|
|
|
{
|
|
|
|
bool all = false;
|
|
|
|
for (size_t i = 1; i < parameters.size(); i++)
|
|
|
|
{
|
|
|
|
if (parameters[i] == "all")
|
|
|
|
all = true;
|
|
|
|
else
|
|
|
|
return CR_WRONG_USAGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<nemesis_record*> list;
|
|
|
|
|
|
|
|
listCompanions(c, &list);
|
|
|
|
sortCompanionNemesis(&list);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < list.size(); i++)
|
|
|
|
{
|
|
|
|
auto item = list[i];
|
|
|
|
auto unit = item->unit;
|
|
|
|
|
|
|
|
printCompanionHeader(c, i, unit);
|
|
|
|
printEquipped(c, unit, all);
|
|
|
|
}
|
|
|
|
|
|
|
|
return CR_OK;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return CR_WRONG_USAGE;
|
|
|
|
}
|