add plugin vampcheck

develop
Robert Heinrich 2012-03-20 09:36:36 +01:00
parent 2bec9f6711
commit 6c6438267b
4 changed files with 258 additions and 0 deletions

@ -108,3 +108,9 @@ OPTION(BUILD_SKELETON "Build the skeleton plugin." OFF)
if(BUILD_SKELETON)
add_subdirectory(skeleton)
endif()
# this is a plugin which helps detect vampires
OPTION(BUILD_VAMPCHECK "Build the vampcheck plugin." ON)
if(BUILD_VAMPCHECK)
add_subdirectory(vampcheck)
endif()

@ -0,0 +1,33 @@
PROJECT (vampcheck)
# A list of source files
SET(PROJECT_SRCS
vampcheck.cpp
)
# A list of headers
SET(PROJECT_HDRS
vampcheck.h
)
SET_SOURCE_FILES_PROPERTIES( ${PROJECT_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE)
# mash them together (headers are marked as headers and nothing will try to compile them)
LIST(APPEND PROJECT_SRCS ${PROJECT_HDRS})
# option to use a thread for no particular reason
#OPTION(SKELETON_THREAD "Use threads in the skeleton plugin." ON)
#linux
IF(UNIX)
add_definitions(-DLINUX_BUILD)
SET(PROJECT_LIBS
# add any extra linux libs here
${PROJECT_LIBS}
)
# windows
ELSE(UNIX)
SET(PROJECT_LIBS
# add any extra linux libs here
${PROJECT_LIBS}
$(NOINHERIT)
)
ENDIF(UNIX)
# this makes sure all the stuff is put in proper places and linked to dfhack
DFHACK_PLUGIN(vampcheck ${PROJECT_SRCS} LINK_LIBRARIES ${PROJECT_LIBS})

@ -0,0 +1,218 @@
// check single tile or whole map/world for vampires
// only tested in fortress mode, maybe it should bail out if called in another mode
// TODO: add some check for deceased vampires, currently lists everybody who has a curse active
// curse probably persists after death to be evauluated for legends...
#include <iostream>
#include <iomanip>
#include <climits>
#include <vector>
#include <string>
#include <sstream>
#include <ctime>
#include <cstdio>
using namespace std;
// TODO: check if all these includes are really necessary
// (they were thrown together from the other plugins where the code was borrowed from)
#include "Core.h"
#include "Console.h"
#include "Export.h"
#include "PluginManager.h"
#include "modules/Units.h"
#include <modules/Translation.h>
#include "modules/Maps.h"
#include "modules/Gui.h"
#include "modules/Materials.h"
#include "modules/MapCache.h"
#include "modules/Buildings.h"
#include "MiscUtils.h"
#include "df/unit.h"
#include "df/unit_soul.h"
#include "df/historical_entity.h"
#include "df/historical_figure.h"
#include "df/historical_figure_info.h"
#include "df/assumed_identity.h"
#include "df/language_name.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 vampcheck (color_ostream &out, vector <string> & parameters);
DFHACK_PLUGIN("vampcheck");
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
commands.clear();
commands.push_back(PluginCommand("vampcheck",
"Checks for curses (vampires).",
vampcheck, false ));
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
return CR_OK;
}
// code for setting nicknames is borrowed from rename.cpp
// will not work in all cases, some vampires don't like to get nicks
static void set_nickname(df::language_name *name, std::string nick)
{
if (!name->has_name)
{
*name = df::language_name();
name->language = 0;
name->has_name = true;
}
name->nickname = nick;
}
void setUnitNickname(df::unit *unit, const std::string &nick)
{
// There are >=3 copies of the name, and the one
// in the unit is not the authoritative one.
// This is the reason why military units often
// lose nicknames set from Dwarf Therapist.
set_nickname(&unit->name, nick);
if (unit->status.current_soul)
set_nickname(&unit->status.current_soul->name, nick);
df::historical_figure *figure = df::historical_figure::find(unit->hist_figure_id);
if (figure)
{
set_nickname(&figure->name, nick);
// v0.34.01: added the vampire's assumed identity
if (figure->info && figure->info->reputation)
{
auto identity = df::assumed_identity::find(figure->info->reputation->cur_identity);
if (identity)
{
auto id_hfig = df::historical_figure::find(identity->histfig_id);
if (id_hfig)
{
// Even DF doesn't do this bit, because it's apparently
// only used for demons masquerading as gods, so you
// can't ever change their nickname in-game.
set_nickname(&id_hfig->name, nick);
}
else
set_nickname(&identity->name, nick);
}
}
}
}
command_result vampcheck (color_ostream &out, vector <string> & parameters)
{
CoreSuspender suspend;
int32_t cursorX, cursorY, cursorZ;
Gui::getCursorCoords(cursorX,cursorY,cursorZ);
// check command options
// by default cursed creatures are only counted
bool giveDetails = false;
bool giveNick = false;
size_t cursecount = 0;
for(size_t i = 0; i < parameters.size();i++)
{
if(parameters[i] == "help" || parameters[i] == "?")
{
out.print( " Search for vampires. With map cursor active only the current tile\n"
" will be checked. Without cursor the whole map/world will be scanned.\n"
" By default cursed creatures are only counted to make it more interesting.\n"
"Options:\n"
" detail - show details (might give false names and ages)\n"
" nick - try to set nickname to CURSED (does not always work)\n"
);
return CR_OK;
}
if(parameters[i] == "detail")
giveDetails = true;
if(parameters[i] == "nick")
giveNick = true;
}
// check whole map if no cursor is active
bool checkWholeMap = false;
if(cursorX == -30000)
{
out.print("No cursor; will check all units on the map.\n");
checkWholeMap = true;
}
for(size_t i = 0; i < world->units.all.size(); i++)
{
df::unit * unit = world->units.all[i];
// bail out if we have a map cursor and creature is not at that specific position
if ( !checkWholeMap && (unit->pos.x != cursorX || unit->pos.y != cursorY || unit->pos.z != cursorZ) )
{
continue;
}
// non-vampires have curse_year == -1
if(unit->relations.curse_year != -1)
{
cursecount++;
if(giveDetails)
{
if(unit->name.has_name)
{
// TODO: avoid printing the first name twice
// currently it behaves like this (copied from dwarfexport):
// firstname nickname restofname(s) - if dwarf has nickname (fine)
// firstname firstname restofname(s) - if dwarf has no nickname (ugly)
// (so better use two strings and check if firstname occurs twice)
string info = unit->name.first_name;
info += " ";
info += Translation::TranslateName(&unit->name, false);
info[0] = toupper(info[0]);
out.print("%s ", info.c_str());
}
else
{
// can that even happen? well, we have unnamed slabs, so maybe it can
out.print("Unnamed creature ");
}
out.print("born in %d, cursed in %d. (%s)\n",
unit->relations.birth_year,
unit->relations.curse_year,
unit->counters.death_id == -1 ? "alive" : "deceased"
);
}
if(giveNick)
setUnitNickname(unit, "CURSED");
}
}
if (checkWholeMap)
out.print("Number of cursed creatures on map: %d \n", cursecount);
else
out.print("Number of cursed creatures on tile: %d \n", cursecount);
return CR_OK;
}