Merge remote-tracking branch 'JapaMala/remote_reader' into develop

develop
lethosor 2019-07-20 19:05:32 -04:00
commit e86e20717e
12 changed files with 944 additions and 163 deletions

@ -37,6 +37,15 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
================================================================================
# Future
## Misc Improvements
- `RemoteFortressReader`:
- added basic framework for controlling and reading the menus in DF, currently only supports the building menu.
- added support for reading item raws.
- added a check for wheather or not the game is currently saving or loading, for utilities to check if it's safe to read from DF.
- guestimate unit facing direction, and position within tiles.
- get unit age.
- get unit wounds.
## Internals
- Fixed some OpenGL build issues with `stonesense`

@ -111,6 +111,7 @@ MACRO(DFHACK_PLUGIN)
SET_SOURCE_FILES_PROPERTIES( ${PLUGIN_PROTO_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE )
LIST(APPEND PLUGIN_SOURCES ${PLUGIN_PROTO_HDRS})
LIST(APPEND PLUGIN_SOURCES ${PLUGIN_PROTO_SRCS})
LIST(APPEND PLUGIN_SOURCES ${PLUGIN_PROTOS})
IF(UNIX)
SET(PLUGIN_COMPILE_FLAGS "${PLUGIN_COMPILE_FLAGS} -include Export.h")

@ -0,0 +1,96 @@
syntax = "proto2";
package DwarfControl;
//Attempts to provide a complete framework for reading everything from a fortress needed for vizualization
option optimize_for = LITE_RUNTIME;
// Plugin: RemoteFortressReader
import "ui_sidebar_mode.proto";
import "RemoteFortressReader.proto";
// RPC GetSideMenu : EmptyMessage -> SidebarState
// RPC SetSideMenu : SidebarCommand -> EmptyMessage
enum BuildCategory
{
NotCategory = 0;
SiegeEngines = 1;
Traps = 2;
Workshops = 3;
Furnaces = 4;
Constructions = 5;
MachineComponents = 6;
Track = 7;
}
enum MenuAction
{
MenuNone = 0;
MenuSelect = 1;
MenuCancel = 2;
MenuSelectAll = 3;
}
enum BuildSelectorStage
{
StageNoMat = 0;
StagePlace = 1;
StageItemSelect = 2;
}
message SidebarState
{
optional proto.enums.ui_sidebar_mode.ui_sidebar_mode mode = 1;
repeated MenuItem menu_items = 2;
optional BuildSelector build_selector = 3;
}
message MenuItem
{
optional RemoteFortressReader.BuildingType building_type = 1;
optional int32 existing_count = 2;
optional BuildCategory build_category = 3;
}
message SidebarCommand
{
optional proto.enums.ui_sidebar_mode.ui_sidebar_mode mode = 1;
optional int32 menu_index = 2;
optional MenuAction action = 3;
optional RemoteFortressReader.Coord selection_coord = 4;
}
message BuiildReqChoice
{
optional int32 distance = 1;
optional string name = 2;
optional int32 num_candidates = 3;
optional int32 used_count = 4;
}
message BuildItemReq
{
//Put filter here = 1
optional int32 count_required = 2;
optional int32 count_max = 3;
optional int32 count_provided = 4;
}
message BuildSelector
{
optional RemoteFortressReader.BuildingType building_type = 1;
optional BuildSelectorStage stage = 2;
repeated BuiildReqChoice choices = 3;
optional int32 sel_index = 4;
repeated BuildItemReq requirements = 5;
optional int32 req_index = 6;
repeated string errors = 7;
optional int32 radius_x_low = 8;
optional int32 radius_y_low = 9;
optional int32 radius_x_high = 10;
optional int32 radius_y_high = 11;
optional RemoteFortressReader.Coord cursor = 12;
repeated int32 tiles = 13;
}

@ -42,6 +42,7 @@ import "ItemdefInstrument.proto";
// RPC MovementSelectCommand : IntMessage -> EmptyMessage
// RPC MiscMoveCommand : MiscMoveParams -> EmptyMessage
// RPC GetLanguage : EmptyMessage -> Language
// RPC GetGameValidity : EmptyMessage -> SingleBool
//We use shapes, etc, because the actual tiletypes may differ between DF versions.
enum TiletypeShape
@ -201,6 +202,14 @@ enum InventoryMode
Strapped = 10;
}
enum ArmorLayer
{
LAYER_UNDER = 0;
LAYER_OVER = 1;
LAYER_ARMOR = 2;
LAYER_COVER = 3;
}
message Coord
{
optional int32 x = 1;
@ -320,6 +329,31 @@ message Item
optional ArtImage image = 18;
}
message PlantTile
{
optional bool trunk = 1;
optional bool connection_east = 2;
optional bool connection_south = 3;
optional bool connection_west = 4;
optional bool connection_north = 5;
optional bool branches = 6;
optional bool twigs = 7;
optional TiletypeSpecial tile_type = 8;
}
message TreeInfo
{
optional Coord size = 1;
repeated PlantTile tiles = 2;
}
message PlantInstance
{
optional int32 plant_type = 1;
optional Coord pos = 2;
optional TreeInfo tree_info = 3;
}
message MapBlock
{
required int32 map_x = 1;
@ -371,6 +405,9 @@ message MaterialDefinition{
optional string name = 3;
optional ColorDefinition state_color = 4; //Simplifying colors to assume room temperature.
optional ItemdefInstrument.InstrumentDef instrument = 5;
optional int32 up_step = 6;
optional int32 down_step = 7;
optional ArmorLayer layer = 8;
}
message BuildingType
@ -426,6 +463,20 @@ message InventoryItem
{
optional InventoryMode mode = 1;
optional Item item = 2;
optional int32 body_part_id = 3;
}
message WoundPart
{
optional int32 global_layer_idx = 1;
optional int32 body_part_id = 2;
optional int32 layer_idx = 3;
}
message UnitWound
{
repeated WoundPart parts = 1;
optional bool severed_part = 2;
}
message UnitDefinition
@ -453,6 +504,9 @@ message UnitDefinition
optional float subpos_x = 21;
optional float subpos_y = 22;
optional float subpos_z = 23;
optional Coord facing = 24;
optional int32 age = 25;
repeated UnitWound wounds = 26;
}
message UnitList
@ -787,6 +841,7 @@ message CreatureRaw
optional int32 adultsize = 9;
repeated CasteRaw caste = 10;
repeated TissueRaw tissues = 11;
repeated bool flags = 12;
}
message CreatureRawList

@ -0,0 +1,63 @@
package proto.enums.ui_sidebar_mode;
//Attempts to provide a complete framework for reading everything from a fortress needed for vizualization
option optimize_for = LITE_RUNTIME;
enum ui_sidebar_mode
{
Default = 0;
Squads = 1;
DesignateMine = 2;
DesignateRemoveRamps = 3;
DesignateUpStair = 4;
DesignateDownStair = 5;
DesignateUpDownStair = 6;
DesignateUpRamp = 7;
DesignateChannel = 8;
DesignateGatherPlants = 9;
DesignateRemoveDesignation = 10;
DesignateSmooth = 11;
DesignateCarveTrack = 12;
DesignateEngrave = 13;
DesignateCarveFortification = 14;
Stockpiles = 15;
Build = 16;
QueryBuilding = 17;
Orders = 18;
OrdersForbid = 19;
OrdersRefuse = 20;
OrdersWorkshop = 21;
OrdersZone = 22;
BuildingItems = 23;
ViewUnits = 24;
LookAround = 25;
DesignateItemsClaim = 26;
DesignateItemsForbid = 27;
DesignateItemsMelt = 28;
DesignateItemsUnmelt = 29;
DesignateItemsDump = 30;
DesignateItemsUndump = 31;
DesignateItemsHide = 32;
DesignateItemsUnhide = 33;
DesignateChopTrees = 34;
DesignateToggleEngravings = 35;
DesignateToggleMarker = 36;
Hotkeys = 37;
DesignateTrafficHigh = 38;
DesignateTrafficNormal = 39;
DesignateTrafficLow = 40;
DesignateTrafficRestricted = 41;
Zones = 42;
ZonesPenInfo = 43;
ZonesPitInfo = 44;
ZonesHospitalInfo = 45;
ZonesGatherInfo = 46;
DesignateRemoveConstruction = 47;
DepotAccess = 48;
NotesPoints = 49;
NotesRoutes = 50;
Burrows = 51;
Hauling = 52;
ArenaWeather = 53;
ArenaTrees = 54;
}

@ -4,27 +4,39 @@ SET(PROJECT_SRCS
remotefortressreader.cpp
adventure_control.cpp
building_reader.cpp
dwarf_control.cpp
item_reader.cpp
)
# A list of headers
SET(PROJECT_HDRS
adventure_control.h
building_reader.h
dwarf_control.h
item_reader.h
df_version_int.h
)
#proto files to include.
SET(PROJECT_PROTO
${CMAKE_CURRENT_SOURCE_DIR}/../proto/RemoteFortressReader.pb.cc
${CMAKE_CURRENT_SOURCE_DIR}/../proto/AdventureControl.pb.cc
${CMAKE_CURRENT_SOURCE_DIR}/../proto/ItemdefInstrument.pb.cc
RemoteFortressReader
AdventureControl
ItemdefInstrument
DwarfControl
ui_sidebar_mode
)
SET_SOURCE_FILES_PROPERTIES( ${PROJECT_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE)
SET_SOURCE_FILES_PROPERTIES( ${PROJECT_PROTO} PROPERTIES GENERATED TRUE)
SET(PLUGIN_PROTOS)
FOREACH(pbuf ${PROJECT_PROTO})
LIST(APPEND PLUGIN_PROTOS ${CMAKE_CURRENT_SOURCE_DIR}/../proto/${pbuf}.proto)
ENDFOREACH()
STRING(REPLACE ".proto" ".pb.cc" PLUGIN_PROTO_SRCS "${PLUGIN_PROTOS}")
STRING(REPLACE ".proto" ".pb.h" PLUGIN_PROTO_HDRS "${PLUGIN_PROTOS}")
SET_SOURCE_FILES_PROPERTIES(${PLUGIN_PROTO_SRCS} ${PLUGIN_PROTO_HDRS} PROPERTIES GENERATED TRUE)
SET_SOURCE_FILES_PROPERTIES( ${PROJECT_HDRS} ${PLUGIN_PROTO_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};${PROJECT_PROTO})
LIST(APPEND PROJECT_SRCS ${PROJECT_HDRS} ${PLUGIN_PROTOS} ${PLUGIN_PROTO_SRCS} ${PLUGIN_PROTO_HDRS})
IF(UNIX AND NOT APPLE)
SET(PROJECT_LIBS ${PROJECT_LIBS} SDL)

@ -78,7 +78,7 @@ DFHack::command_result GetBuildingDefList(DFHack::color_ostream &stream, const D
bld->mutable_building_type()->set_building_subtype(-1);
bld->mutable_building_type()->set_building_custom(-1);
bld->set_id(ENUM_KEY_STR(building_type, bt));
bld->set_name(ENUM_ATTR_STR(building_type, name, bt));
switch (bt)
{
case df::enums::building_type::NONE:
@ -101,6 +101,7 @@ DFHack::command_result GetBuildingDefList(DFHack::color_ostream &stream, const D
bld->mutable_building_type()->set_building_subtype(st);
bld->mutable_building_type()->set_building_custom(-1);
bld->set_id(ENUM_KEY_STR(building_type, bt) + "/" + ENUM_KEY_STR(furnace_type, st));
bld->set_name(ENUM_ATTR_STR(furnace_type, name, st));
if (st == furnace_type::Custom)
{
@ -128,7 +129,7 @@ DFHack::command_result GetBuildingDefList(DFHack::color_ostream &stream, const D
bld->mutable_building_type()->set_building_subtype(st);
bld->mutable_building_type()->set_building_custom(-1);
bld->set_id(ENUM_KEY_STR(building_type, bt) + "/" + ENUM_KEY_STR(shop_type, st));
bld->set_name(ENUM_KEY_STR(shop_type, st));
}
break;
case df::enums::building_type::Door:
@ -149,6 +150,7 @@ DFHack::command_result GetBuildingDefList(DFHack::color_ostream &stream, const D
bld->mutable_building_type()->set_building_subtype(st);
bld->mutable_building_type()->set_building_custom(-1);
bld->set_id(ENUM_KEY_STR(building_type, bt) + "/" + ENUM_KEY_STR(workshop_type, st));
bld->set_name(ENUM_ATTR_STR(workshop_type, name, st));
if (st == workshop_type::Custom)
{
@ -190,7 +192,7 @@ DFHack::command_result GetBuildingDefList(DFHack::color_ostream &stream, const D
bld->mutable_building_type()->set_building_subtype(st);
bld->mutable_building_type()->set_building_custom(-1);
bld->set_id(ENUM_KEY_STR(building_type, bt) + "/" + ENUM_KEY_STR(siegeengine_type, st));
bld->set_name(ENUM_KEY_STR(siegeengine_type, st));
}
break;
case df::enums::building_type::Trap:
@ -201,7 +203,7 @@ DFHack::command_result GetBuildingDefList(DFHack::color_ostream &stream, const D
bld->mutable_building_type()->set_building_subtype(st);
bld->mutable_building_type()->set_building_custom(-1);
bld->set_id(ENUM_KEY_STR(building_type, bt) + "/" + ENUM_KEY_STR(trap_type, st));
bld->set_name(ENUM_KEY_STR(trap_type, st));
}
break;
case df::enums::building_type::AnimalTrap:
@ -224,7 +226,7 @@ DFHack::command_result GetBuildingDefList(DFHack::color_ostream &stream, const D
bld->mutable_building_type()->set_building_subtype(st);
bld->mutable_building_type()->set_building_custom(-1);
bld->set_id(ENUM_KEY_STR(building_type, bt) + "/" + ENUM_KEY_STR(civzone_type, st));
bld->set_name(ENUM_KEY_STR(civzone_type, st));
}
break;
case df::enums::building_type::Weapon:
@ -241,7 +243,7 @@ DFHack::command_result GetBuildingDefList(DFHack::color_ostream &stream, const D
bld->mutable_building_type()->set_building_subtype(st);
bld->mutable_building_type()->set_building_custom(-1);
bld->set_id(ENUM_KEY_STR(building_type, bt) + "/" + ENUM_KEY_STR(construction_type, st));
bld->set_name(ENUM_KEY_STR(construction_type, st));
}
break;
case df::enums::building_type::Hatch:

@ -1,2 +1,2 @@
#pragma once
#define DF_VERSION_INT 44002
#define DF_VERSION_INT 44012

@ -0,0 +1,493 @@
#include "dwarf_control.h"
#include "DataDefs.h"
#include "df_version_int.h"
#include "df/build_req_choice_genst.h"
#include "df/build_req_choice_specst.h"
#include "df/build_req_choicest.h"
#include "df/building_def.h"
#include "df/building_def_furnacest.h"
#include "df/building_def_workshopst.h"
#include "df/job.h"
#include "df/job_list_link.h"
#include "df/interface_button_construction_building_selectorst.h"
#include "df/interface_button_construction_category_selectorst.h"
#include "df/ui.h"
#include "df/ui_build_selector.h"
#include "df/ui_sidebar_menus.h"
#include "df/viewscreen.h"
#include "df/world.h"
#include "modules/Buildings.h"
#include "modules/Gui.h"
#include "modules/Job.h"
#include "modules/MapCache.h"
#include "modules/Maps.h"
#include "modules/World.h"
#include "MiscUtils.h"
#include <queue>
using namespace DFHack;
using namespace RemoteFortressReader;
using namespace df::enums;
using namespace Gui;
using namespace df::global;
extern std::queue<interface_key::interface_key> keyQueue;
void GetBuildingSize(
int16_t type,
int16_t subtype,
int16_t custom,
int16_t &rad_x_low,
int16_t &rad_y_low,
int16_t &rad_x_high,
int16_t &rad_y_high
)
{
rad_x_low = 0;
rad_y_low = 0;
rad_x_high = 0;
rad_y_high = 0;
df::building_def* customBuilding = 0;
switch (type)
{
case building_type::FarmPlot:
case building_type::Bridge:
case building_type::RoadDirt:
case building_type::RoadPaved:
case building_type::Stockpile:
case building_type::Civzone:
case building_type::ScrewPump:
case building_type::Construction:
case building_type::AxleHorizontal:
case building_type::WaterWheel:
case building_type::Rollers:
{
bool widthOdd = world->building_width % 2;
rad_x_low = world->building_width / 2;
if(widthOdd)
rad_x_high = world->building_width / 2;
else
rad_x_high = (world->building_width / 2) - 1;
bool heightOdd = world->building_width % 2;
rad_y_low = world->building_height / 2;
if (widthOdd)
rad_y_high = world->building_height / 2;
else
rad_y_high = (world->building_height / 2) - 1;
}
return;
case building_type::Furnace:
if (subtype != furnace_type::Custom)
{
rad_x_low = rad_y_low = rad_x_high = rad_y_high = 1;
return;
}
customBuilding = world->raws.buildings.furnaces[custom];
break;
case building_type::TradeDepot:
case building_type::Shop:
rad_x_low = rad_y_low = rad_x_high = rad_y_high = 2;
return;
case building_type::Workshop:
switch (subtype)
{
case workshop_type::Carpenters:
case workshop_type::Farmers:
case workshop_type::Masons:
case workshop_type::Craftsdwarfs:
case workshop_type::Jewelers:
case workshop_type::MetalsmithsForge:
case workshop_type::MagmaForge:
case workshop_type::Bowyers:
case workshop_type::Mechanics:
case workshop_type::Butchers:
case workshop_type::Leatherworks:
case workshop_type::Tanners:
case workshop_type::Clothiers:
case workshop_type::Fishery:
case workshop_type::Still:
case workshop_type::Loom:
case workshop_type::Kitchen:
case workshop_type::Ashery:
case workshop_type::Dyers:
rad_x_low = rad_y_low = rad_x_high = rad_y_high = 1;
return;
case workshop_type::Siege:
case workshop_type::Kennels:
rad_x_low = rad_y_low = rad_x_high = rad_y_high = 2;
return;
case workshop_type::Custom:
customBuilding = world->raws.buildings.workshops[custom];
break;
default:
return;
}
break;
case building_type::SiegeEngine:
case building_type::Wagon:
case building_type::Windmill:
rad_x_low = rad_y_low = rad_x_high = rad_y_high = 1;
return;
default:
return;
}
if (customBuilding)
{
rad_x_low = customBuilding->workloc_x;
rad_y_low = customBuilding->workloc_y;
rad_x_high = customBuilding->dim_x - rad_x_low - 1;
rad_y_high = customBuilding->dim_y - rad_y_low - 1;
return;
}
}
command_result SendDigCommand(color_ostream &stream, const DigCommand *in)
{
MapExtras::MapCache mc;
for (int i = 0; i < in->locations_size(); i++)
{
auto pos = in->locations(i);
auto des = mc.designationAt(DFCoord(pos.x(), pos.y(), pos.z()));
switch (in->designation())
{
case NO_DIG:
des.bits.dig = tile_dig_designation::No;
break;
case DEFAULT_DIG:
des.bits.dig = tile_dig_designation::Default;
break;
case UP_DOWN_STAIR_DIG:
des.bits.dig = tile_dig_designation::UpDownStair;
break;
case CHANNEL_DIG:
des.bits.dig = tile_dig_designation::Channel;
break;
case RAMP_DIG:
des.bits.dig = tile_dig_designation::Ramp;
break;
case DOWN_STAIR_DIG:
des.bits.dig = tile_dig_designation::DownStair;
break;
case UP_STAIR_DIG:
des.bits.dig = tile_dig_designation::UpStair;
break;
default:
break;
}
mc.setDesignationAt(DFCoord(pos.x(), pos.y(), pos.z()), des);
#if DF_VERSION_INT >= 43005
//remove and job postings related.
for (df::job_list_link * listing = &(df::global::world->jobs.list); listing != NULL; listing = listing->next)
{
if (listing->item == NULL)
continue;
auto type = listing->item->job_type;
switch (type)
{
case job_type::CarveFortification:
case job_type::DetailWall:
case job_type::DetailFloor:
case job_type::Dig:
case job_type::CarveUpwardStaircase:
case job_type::CarveDownwardStaircase:
case job_type::CarveUpDownStaircase:
case job_type::CarveRamp:
case job_type::DigChannel:
case job_type::FellTree:
case job_type::GatherPlants:
case job_type::RemoveConstruction:
case job_type::CarveTrack:
{
if (listing->item->pos == DFCoord(pos.x(), pos.y(), pos.z()))
{
Job::removeJob(listing->item);
goto JOB_FOUND;
}
break;
}
default:
continue;
}
}
JOB_FOUND:
continue;
#endif
}
mc.WriteAll();
return CR_OK;
}
command_result SetPauseState(color_ostream &stream, const SingleBool *in)
{
DFHack::World::SetPauseState(in->value());
return CR_OK;
}
void CopyBuildMenu(DwarfControl::SidebarState * out)
{
auto menus = df::global::ui_sidebar_menus;
auto build_selector = df::global::ui_build_selector;
if (build_selector->building_type == -1)
for (size_t i = 0; i < menus->building.choices_visible.size(); i++)
{
auto menu_item = menus->building.choices_visible[i];
auto send_item = out->add_menu_items();
STRICT_VIRTUAL_CAST_VAR(building, df::interface_button_construction_building_selectorst, menu_item);
if (building)
{
auto send_bld = send_item->mutable_building_type();
send_bld->set_building_type(building->building_type);
send_bld->set_building_subtype(building->building_subtype);
send_bld->set_building_custom(building->custom_type);
send_item->set_existing_count(building->existing_count);
}
STRICT_VIRTUAL_CAST_VAR(sub_category, df::interface_button_construction_category_selectorst, menu_item);
if (sub_category)
{
send_item->set_build_category((DwarfControl::BuildCategory)sub_category->category_id);
}
}
else
{
auto send_selector = out->mutable_build_selector();
auto send_bld = send_selector->mutable_building_type();
send_bld->set_building_type(build_selector->building_type);
send_bld->set_building_subtype(build_selector->building_subtype);
send_bld->set_building_custom(build_selector->custom_type);
send_selector->set_stage((DwarfControl::BuildSelectorStage)build_selector->stage);
for (size_t i = 0; i < build_selector->errors.size(); i++)
{
if (build_selector->errors[i])
send_selector->add_errors(*build_selector->errors[i]);
}
for (size_t i = 0; i < build_selector->choices.size(); i++)
{
auto choice = build_selector->choices[i];
auto send_choice = send_selector->add_choices();
send_choice->set_distance(choice->distance);
std::string name;
choice->getName(&name);
send_choice->set_name(name);
send_choice->set_num_candidates(choice->getNumCandidates());
send_choice->set_used_count(choice->getUsedCount());
}
int16_t x_low, y_low, x_high, y_high;
GetBuildingSize(build_selector->building_type, build_selector->building_subtype, build_selector->custom_type, x_low, y_low, x_high, y_high);
send_selector->set_radius_x_low(x_low);
send_selector->set_radius_y_low(y_low);
send_selector->set_radius_x_high(x_high);
send_selector->set_radius_y_high(y_high);
if (build_selector->stage >= 1)
{
auto send_cursor = send_selector->mutable_cursor();
send_cursor->set_x(cursor->x);
send_cursor->set_y(cursor->y);
send_cursor->set_z(cursor->z);
}
for (int y = 0; y < (y_low + y_high + 1); y++)
for (int x = 0; x < (x_low + x_high + 1); x++)
{
send_selector->add_tiles(build_selector->tiles[x][y]);
}
}
}
command_result GetSideMenu(DFHack::color_ostream &stream, const dfproto::EmptyMessage *in, DwarfControl::SidebarState *out)
{
auto ui = df::global::ui;
out->set_mode((proto::enums::ui_sidebar_mode::ui_sidebar_mode)ui->main.mode);
auto mode = ui->main.mode;
switch (mode)
{
case ui_sidebar_mode::Default:
break;
case ui_sidebar_mode::Squads:
break;
case ui_sidebar_mode::DesignateMine:
break;
case ui_sidebar_mode::DesignateRemoveRamps:
break;
case ui_sidebar_mode::DesignateUpStair:
break;
case ui_sidebar_mode::DesignateDownStair:
break;
case ui_sidebar_mode::DesignateUpDownStair:
break;
case ui_sidebar_mode::DesignateUpRamp:
break;
case ui_sidebar_mode::DesignateChannel:
break;
case ui_sidebar_mode::DesignateGatherPlants:
break;
case ui_sidebar_mode::DesignateRemoveDesignation:
break;
case ui_sidebar_mode::DesignateSmooth:
break;
case ui_sidebar_mode::DesignateCarveTrack:
break;
case ui_sidebar_mode::DesignateEngrave:
break;
case ui_sidebar_mode::DesignateCarveFortification:
break;
case ui_sidebar_mode::Stockpiles:
break;
case ui_sidebar_mode::Build:
CopyBuildMenu(out);
break;
case ui_sidebar_mode::QueryBuilding:
break;
case ui_sidebar_mode::Orders:
break;
case ui_sidebar_mode::OrdersForbid:
break;
case ui_sidebar_mode::OrdersRefuse:
break;
case ui_sidebar_mode::OrdersWorkshop:
break;
case ui_sidebar_mode::OrdersZone:
break;
case ui_sidebar_mode::BuildingItems:
break;
case ui_sidebar_mode::ViewUnits:
break;
case ui_sidebar_mode::LookAround:
break;
case ui_sidebar_mode::DesignateItemsClaim:
break;
case ui_sidebar_mode::DesignateItemsForbid:
break;
case ui_sidebar_mode::DesignateItemsMelt:
break;
case ui_sidebar_mode::DesignateItemsUnmelt:
break;
case ui_sidebar_mode::DesignateItemsDump:
break;
case ui_sidebar_mode::DesignateItemsUndump:
break;
case ui_sidebar_mode::DesignateItemsHide:
break;
case ui_sidebar_mode::DesignateItemsUnhide:
break;
case ui_sidebar_mode::DesignateChopTrees:
break;
case ui_sidebar_mode::DesignateToggleEngravings:
break;
case ui_sidebar_mode::DesignateToggleMarker:
break;
case ui_sidebar_mode::Hotkeys:
break;
case ui_sidebar_mode::DesignateTrafficHigh:
break;
case ui_sidebar_mode::DesignateTrafficNormal:
break;
case ui_sidebar_mode::DesignateTrafficLow:
break;
case ui_sidebar_mode::DesignateTrafficRestricted:
break;
case ui_sidebar_mode::Zones:
break;
case ui_sidebar_mode::ZonesPenInfo:
break;
case ui_sidebar_mode::ZonesPitInfo:
break;
case ui_sidebar_mode::ZonesHospitalInfo:
break;
case ui_sidebar_mode::ZonesGatherInfo:
break;
case ui_sidebar_mode::DesignateRemoveConstruction:
break;
case ui_sidebar_mode::DepotAccess:
break;
case ui_sidebar_mode::NotesPoints:
break;
case ui_sidebar_mode::NotesRoutes:
break;
case ui_sidebar_mode::Burrows:
break;
case ui_sidebar_mode::Hauling:
break;
case ui_sidebar_mode::ArenaWeather:
break;
case ui_sidebar_mode::ArenaTrees:
break;
default:
break;
}
return CR_OK;
}
command_result SetSideMenu(DFHack::color_ostream &stream, const DwarfControl::SidebarCommand *in)
{
auto ui = df::global::ui;
if (in->has_mode())
{
ui_sidebar_mode::ui_sidebar_mode set_mode = (ui_sidebar_mode::ui_sidebar_mode)in->mode();
if (ui->main.mode != set_mode)
{
ui->main.mode = ui_sidebar_mode::Default;
switch (set_mode)
{
case ui_sidebar_mode::Build:
keyQueue.push(interface_key::D_BUILDING);
break;
default:
ui->main.mode = set_mode;
break;
}
}
}
switch (ui->main.mode)
{
case ui_sidebar_mode::Build:
if (in->has_action())
{
int index = 0;
if (in->has_menu_index())
index = in->menu_index();
if(ui_build_selector->building_type == -1)
df::global::ui_sidebar_menus->building.cursor = index;
if (ui_build_selector->stage == 2)
{
ui_build_selector->sel_index = index;
}
}
if (ui_build_selector->stage == 1)
{
if (in->has_selection_coord())
{
df::global::cursor->x = in->selection_coord().x();
df::global::cursor->y = in->selection_coord().y();
df::global::cursor->z = in->selection_coord().z();
getCurViewscreen()->feed_key(interface_key::CURSOR_LEFT);
getCurViewscreen()->feed_key(interface_key::CURSOR_RIGHT);
}
}
break;
default:
break;
}
auto viewScreen = getCurViewscreen();
if (in->has_action())
{
switch (in->action())
{
case DwarfControl::MenuSelect:
keyQueue.push(interface_key::SELECT);
break;
case DwarfControl::MenuCancel:
keyQueue.push(interface_key::LEAVESCREEN);
break;
default:
break;
}
}
return CR_OK;
}

@ -0,0 +1,15 @@
#ifndef DWARF_CONTROL_H
#define DWARF_CONTROL_H
#include "RemoteClient.h"
#include "RemoteFortressReader.pb.h"
#include "DwarfControl.pb.h"
DFHack::command_result SendDigCommand(DFHack::color_ostream &stream, const RemoteFortressReader::DigCommand *in);
DFHack::command_result SetPauseState(DFHack::color_ostream &stream, const RemoteFortressReader::SingleBool *in);
DFHack::command_result GetSideMenu(DFHack::color_ostream &stream, const dfproto::EmptyMessage *in, DwarfControl::SidebarState *out);
DFHack::command_result SetSideMenu(DFHack::color_ostream &stream, const DwarfControl::SidebarCommand *in);
#endif // !DWARF_CONTROL_H

@ -26,7 +26,13 @@
#include "df/item_statuest.h"
#include "df/item_threadst.h"
#include "df/item_toolst.h"
#include "df/itemdef_armorst.h"
#include "df/itemdef_glovesst.h"
#include "df/itemdef_helmst.h"
#include "df/itemdef_instrumentst.h"
#include "df/itemdef_pantsst.h"
#include "df/itemdef_shieldst.h"
#include "df/itemdef_shoesst.h"
#include "df/itemdef_toolst.h"
#include "df/itemimprovement.h"
#include "df/itemimprovement_art_imagest.h"
@ -658,13 +664,70 @@ DFHack::command_result GetItemList(DFHack::color_ostream &stream, const DFHack::
reg->set_pitch_range_max(instrument->registers[j]->pitch_range_max);
}
send_instrument->set_description(DF2UTF(instrument->description));
break;
}
break;
case df::enums::item_type::TOOL:
{
VIRTUAL_CAST_VAR(tool, df::itemdef_toolst, item);
mat_def->set_name(DF2UTF(tool->name));
}
break;
case df::enums::item_type::ARMOR:
{
if (VIRTUAL_CAST_VAR(armor, df::itemdef_armorst, item))
{
mat_def->set_up_step(armor->ubstep);
mat_def->set_down_step(armor->lbstep);
mat_def->set_layer((ArmorLayer)armor->props.layer);
}
}
break;
case df::enums::item_type::SHOES:
{
if (VIRTUAL_CAST_VAR(armor, df::itemdef_shoesst, item))
{
mat_def->set_up_step(armor->upstep);
mat_def->set_down_step(10000);
mat_def->set_layer((ArmorLayer)armor->props.layer);
}
}
break;
case df::enums::item_type::SHIELD:
{
if (VIRTUAL_CAST_VAR(armor, df::itemdef_shieldst, item))
{
mat_def->set_up_step(armor->upstep);
mat_def->set_down_step(10000);
}
}
break;
case df::enums::item_type::HELM:
{
if (VIRTUAL_CAST_VAR(armor, df::itemdef_helmst, item))
{
mat_def->set_layer((ArmorLayer)armor->props.layer);
}
}
break;
case df::enums::item_type::GLOVES:
{
if (VIRTUAL_CAST_VAR(armor, df::itemdef_glovesst, item))
{
mat_def->set_up_step(armor->upstep);
mat_def->set_down_step(10000);
mat_def->set_layer((ArmorLayer)armor->props.layer);
}
}
break;
case df::enums::item_type::PANTS:
{
if (VIRTUAL_CAST_VAR(armor, df::itemdef_pantsst, item))
{
mat_def->set_down_step(armor->lbstep);
mat_def->set_layer((ArmorLayer)armor->props.layer);
}
}
break;
default:
break;
}

@ -1,5 +1,5 @@
#include "df_version_int.h"
#define RFR_VERSION "0.19.1"
#define RFR_VERSION "0.20.2"
#include <cstdio>
#include <time.h>
@ -63,7 +63,7 @@
#include "df/flow_guide_item_cloudst.h"
#include "df/graphic.h"
#include "df/historical_figure.h"
#include "df/identity.h"
#include "df/job.h"
#include "df/job_type.h"
#include "df/job_item.h"
@ -92,7 +92,10 @@
#include "df/ui.h"
#include "df/unit.h"
#include "df/unit_inventory_item.h"
#include "df/unit_wound.h"
#include "df/viewscreen_choose_start_sitest.h"
#include "df/viewscreen_loadgamest.h"
#include "df/viewscreen_savegamest.h"
#include "df/vehicle.h"
#include "df/world.h"
#include "df/world_data.h"
@ -117,6 +120,7 @@
#include "adventure_control.h"
#include "building_reader.h"
#include "dwarf_control.h"
#include "item_reader.h"
using namespace DFHack;
@ -161,13 +165,11 @@ static command_result GetPlantRaws(color_ostream &stream, const EmptyMessage *in
static command_result GetPartialPlantRaws(color_ostream &stream, const ListRequest *in, PlantRawList *out);
static command_result CopyScreen(color_ostream &stream, const EmptyMessage *in, ScreenCapture *out);
static command_result PassKeyboardEvent(color_ostream &stream, const KeyboardEvent *in);
static command_result SendDigCommand(color_ostream &stream, const DigCommand *in);
static command_result SetPauseState(color_ostream & stream, const SingleBool * in);
static command_result GetPauseState(color_ostream & stream, const EmptyMessage * in, SingleBool * out);
static command_result GetVersionInfo(color_ostream & stream, const EmptyMessage * in, RemoteFortressReader::VersionInfo * out);
static command_result GetReports(color_ostream & stream, const EmptyMessage * in, RemoteFortressReader::Status * out);
static command_result GetLanguage(color_ostream & stream, const EmptyMessage * in, RemoteFortressReader::Language * out);
static command_result GetGameValidity(color_ostream &stream, const EmptyMessage * in, SingleBool *out);
void CopyBlock(df::map_block * DfBlock, RemoteFortressReader::MapBlock * NetBlock, MapExtras::MapCache * MC, DFCoord pos);
@ -208,54 +210,6 @@ command_result loadArtImageChunk(color_ostream &out, vector <string> & parameter
return CR_OK;
}
command_result dump_bp_mods(color_ostream &out, vector <string> & parameters)
{
remove("bp_appearance_mods.csv");
ofstream output;
output.open("bp_appearance_mods.csv");
output << "Race Index;Race;Caste;Bodypart Token;Bodypart Name;Tissue Layer;Modifier Type;Range\n";
for (size_t creatureIndex = 0; creatureIndex < world->raws.creatures.all.size(); creatureIndex++)
{
auto creatureRaw = world->raws.creatures.all[creatureIndex];
for (size_t casteIndex = 0; casteIndex < creatureRaw->caste.size(); casteIndex++)
{
df::caste_raw *casteRaw = creatureRaw->caste[casteIndex];
for (size_t partIndex = 0; partIndex < casteRaw->bp_appearance.part_idx.size(); partIndex++)
{
output << creatureIndex << ";";
output << creatureRaw->creature_id << ";";
output << casteRaw->caste_id << ";";
output << casteRaw->body_info.body_parts[casteRaw->bp_appearance.part_idx[partIndex]]->token << ";";
output << casteRaw->body_info.body_parts[casteRaw->bp_appearance.part_idx[partIndex]]->name_singular[0]->c_str() << ";";
int layer = casteRaw->bp_appearance.layer_idx[partIndex];
if (layer < 0)
output << "N/A;";
else
output << casteRaw->body_info.body_parts[casteRaw->bp_appearance.part_idx[partIndex]]->layers[layer]->layer_name << ";";
output << ENUM_KEY_STR(appearance_modifier_type, casteRaw->bp_appearance.modifiers[casteRaw->bp_appearance.modifier_idx[partIndex]]->type) << ";";
auto appMod = casteRaw->bp_appearance.modifiers[casteRaw->bp_appearance.modifier_idx[partIndex]];
#if DF_VERSION_INT > 34011
if (appMod->growth_rate > 0)
{
output << appMod->growth_min << " - " << appMod->growth_max << "\n";
}
else
#endif
{
output << casteRaw->bp_appearance.modifiers[casteRaw->bp_appearance.modifier_idx[partIndex]]->ranges[0] << " - ";
output << casteRaw->bp_appearance.modifiers[casteRaw->bp_appearance.modifier_idx[partIndex]]->ranges[6] << "\n";
}
}
}
}
output.close();
return CR_OK;
}
command_result RemoteFortressReader_version(color_ostream &out, vector<string> &parameters)
{
out.print(RFR_VERSION);
@ -267,16 +221,6 @@ DFHACK_PLUGIN_IS_ENABLED(enableUpdates);
// Mandatory init function. If you have some global state, create it here.
DFhackCExport command_result plugin_init(color_ostream &out, std::vector <PluginCommand> &commands)
{
//// Fill the command list with your commands.
commands.push_back(PluginCommand(
"dump-bp-mods", "Dump bodypart mods for debugging",
dump_bp_mods, false, /* true means that the command can't be used from non-interactive user interface */
// Extended help string. Used by CR_WRONG_USAGE and the help command:
" This command does nothing at all.\n"
"Example:\n"
" isoworldremote\n"
" Does nothing.\n"
));
commands.push_back(PluginCommand("RemoteFortressReader_version", "List the loaded RemoteFortressReader version", RemoteFortressReader_version, false, "This is used for plugin version checking."));
commands.push_back(PluginCommand(
"load-art-image-chunk",
@ -329,6 +273,9 @@ DFhackCExport RPCService *plugin_rpcconnect(color_ostream &)
svc->addFunction("MovementSelectCommand", MovementSelectCommand, SF_ALLOW_REMOTE);
svc->addFunction("MiscMoveCommand", MiscMoveCommand, SF_ALLOW_REMOTE);
svc->addFunction("GetLanguage", GetLanguage, SF_ALLOW_REMOTE);
svc->addFunction("GetSideMenu", GetSideMenu, SF_ALLOW_REMOTE);
svc->addFunction("SetSideMenu", SetSideMenu, SF_ALLOW_REMOTE);
svc->addFunction("GetGameValidity", GetGameValidity, SF_ALLOW_REMOTE);
return svc;
}
@ -1506,7 +1453,7 @@ static command_result GetBlockList(color_ostream &stream, const BlockRequest *in
goto ItsAir;
}
}
ItsAir:
ItsAir:
if (block->flows.size() > 0)
nonAir = true;
if (nonAir || firstBlock)
@ -1556,9 +1503,9 @@ static command_result GetBlockList(color_ostream &stream, const BlockRequest *in
segment_passed = 0;
// 'rotate' directions
int buffer = di;
int filename = di;
di = -dj;
dj = buffer;
dj = filename;
// increase segment length if necessary
if (dj == 0) {
@ -1702,6 +1649,24 @@ static command_result GetUnitList(color_ostream &stream, const EmptyMessage *in,
return GetUnitListInside(stream, NULL, out);
}
float lerp(float a, float b, float f)
{
return a + f * (b - a);
}
void GetWounds(df::unit_wound * wound, UnitWound * send_wound)
{
for (size_t i = 0; i < wound->parts.size(); i++)
{
auto part = wound->parts[i];
auto send_part = send_wound->add_parts();
send_part->set_global_layer_idx(part->global_layer_idx);
send_part->set_body_part_id(part->body_part_id);
send_part->set_layer_idx(part->layer_idx);
}
send_wound->set_severed_part(wound->flags.bits.severed_part);
}
static command_result GetUnitListInside(color_ostream &stream, const BlockRequest *in, UnitList *out)
{
auto world = df::global::world;
@ -1724,6 +1689,25 @@ static command_result GetUnitListInside(color_ostream &stream, const BlockReques
if (unit->pos.y < in->min_y() * 16 || unit->pos.y >= in->max_y() * 16)
continue;
}
using df::global::cur_year;
using df::global::cur_year_tick;
int year_ticks = 403200;
int birth_time = unit->birth_year * year_ticks + unit->birth_time;
int cur_time = *cur_year * year_ticks + *cur_year_tick;
if (unit->curse_year >= 0)
{
if (auto identity = Units::getIdentity(unit))
{
if (identity->histfig_id < 0)
birth_time = identity->birth_year * year_ticks + identity->birth_second;
}
}
send_unit->set_age(cur_time - birth_time);
ConvertDfColor(Units::getProfessionColor(unit), send_unit->mutable_profession_color());
send_unit->set_flags1(unit->flags1.whole);
send_unit->set_flags2(unit->flags2.whole);
@ -1806,6 +1790,7 @@ static command_result GetUnitListInside(color_ostream &stream, const BlockReques
auto inventory_item = unit->inventory[j];
auto sent_item = send_unit->add_inventory();
sent_item->set_mode((InventoryMode)inventory_item->mode);
sent_item->set_body_part_id(inventory_item->body_part_id);
CopyItem(sent_item->mutable_item(), inventory_item->item);
}
@ -1821,8 +1806,51 @@ static command_result GetUnitListInside(color_ostream &stream, const BlockReques
send_unit->set_subpos_x(item->pos_x / 100000.0);
send_unit->set_subpos_y(item->pos_y / 100000.0);
send_unit->set_subpos_z(item->pos_z / 140000.0);
auto facing = send_unit->mutable_facing();
facing->set_x(item->speed_x);
facing->set_y(item->speed_x);
facing->set_z(item->speed_x);
break;
}
}
else
{
for (size_t i = 0; i < unit->actions.size(); i++)
{
auto action = unit->actions[i];
switch (action->type)
{
case unit_action_type::Move:
if (unit->path.path.x.size() > 0)
{
send_unit->set_subpos_x(lerp(0, unit->path.path.x[0] - unit->pos.x, (float)(action->data.move.timer_init - action->data.move.timer) / action->data.move.timer_init));
send_unit->set_subpos_y(lerp(0, unit->path.path.y[0] - unit->pos.y, (float)(action->data.move.timer_init - action->data.move.timer) / action->data.move.timer_init));
send_unit->set_subpos_z(lerp(0, unit->path.path.z[0] - unit->pos.z, (float)(action->data.move.timer_init - action->data.move.timer) / action->data.move.timer_init));
}
break;
case unit_action_type::Job:
{
auto facing = send_unit->mutable_facing();
facing->set_x(action->data.job.x - unit->pos.x);
facing->set_y(action->data.job.y - unit->pos.y);
facing->set_z(action->data.job.z - unit->pos.z);
}
default:
break;
}
}
if (unit->path.path.x.size() > 0)
{
auto facing = send_unit->mutable_facing();
facing->set_x(unit->path.path.x[0] - unit->pos.x);
facing->set_y(unit->path.path.y[0] - unit->pos.y);
facing->set_z(unit->path.path.z[0] - unit->pos.z);
}
}
for (size_t i = 0; i < unit->body.wounds.size(); i++)
{
GetWounds(unit->body.wounds[i], send_unit->add_wounds());
}
}
return CR_OK;
}
@ -1847,6 +1875,14 @@ static command_result GetViewInfo(color_ostream &stream, const EmptyMessage *in,
}
#endif
auto dims = Gui::getDwarfmodeViewDims();
x += dims.map_x1;
y += dims.map_y1;
w = dims.map_x2 - dims.map_x1;
h = dims.map_y2 - dims.map_y1;
out->set_view_pos_x(x);
out->set_view_pos_y(y);
out->set_view_pos_z(z);
@ -2793,6 +2829,10 @@ static command_result GetPartialCreatureRaws(color_ostream &stream, const ListRe
CopyMat(send_tissue->mutable_material(), orig_tissue->mat_type, orig_tissue->mat_index);
}
FOR_ENUM_ITEMS(creature_raw_flags, flag)
{
send_creature->add_flags(orig_creature->flags.is_set(flag));
}
}
return CR_OK;
@ -2893,94 +2933,26 @@ static command_result PassKeyboardEvent(color_ostream &stream, const KeyboardEve
return CR_OK;
}
static command_result SendDigCommand(color_ostream &stream, const DigCommand *in)
{
MapExtras::MapCache mc;
for (int i = 0; i < in->locations_size(); i++)
{
auto pos = in->locations(i);
auto des = mc.designationAt(DFCoord(pos.x(), pos.y(), pos.z()));
switch (in->designation())
{
case NO_DIG:
des.bits.dig = tile_dig_designation::No;
break;
case DEFAULT_DIG:
des.bits.dig = tile_dig_designation::Default;
break;
case UP_DOWN_STAIR_DIG:
des.bits.dig = tile_dig_designation::UpDownStair;
break;
case CHANNEL_DIG:
des.bits.dig = tile_dig_designation::Channel;
break;
case RAMP_DIG:
des.bits.dig = tile_dig_designation::Ramp;
break;
case DOWN_STAIR_DIG:
des.bits.dig = tile_dig_designation::DownStair;
break;
case UP_STAIR_DIG:
des.bits.dig = tile_dig_designation::UpStair;
break;
default:
break;
}
mc.setDesignationAt(DFCoord(pos.x(), pos.y(), pos.z()), des);
#if DF_VERSION_INT >= 43005
//remove and job postings related.
for (df::job_list_link * listing = &(world->jobs.list); listing != NULL; listing = listing->next)
{
if (listing->item == NULL)
continue;
auto type = listing->item->job_type;
switch (type)
{
case df::enums::job_type::CarveFortification:
case df::enums::job_type::DetailWall:
case df::enums::job_type::DetailFloor:
case df::enums::job_type::Dig:
case df::enums::job_type::CarveUpwardStaircase:
case df::enums::job_type::CarveDownwardStaircase:
case df::enums::job_type::CarveUpDownStaircase:
case df::enums::job_type::CarveRamp:
case df::enums::job_type::DigChannel:
case df::enums::job_type::FellTree:
case df::enums::job_type::GatherPlants:
case df::enums::job_type::RemoveConstruction:
case df::enums::job_type::CarveTrack:
{
if (listing->item->pos == DFCoord(pos.x(), pos.y(), pos.z()))
{
Job::removeJob(listing->item);
goto JOB_FOUND;
}
break;
}
default:
continue;
}
}
JOB_FOUND:
continue;
#endif
}
mc.WriteAll();
return CR_OK;
}
static command_result SetPauseState(color_ostream &stream, const SingleBool *in)
static command_result GetPauseState(color_ostream &stream, const EmptyMessage *in, SingleBool *out)
{
DFHack::World::SetPauseState(in->value());
out->set_value(World::ReadPauseState());
return CR_OK;
}
static command_result GetPauseState(color_ostream &stream, const EmptyMessage *in, SingleBool *out)
static command_result GetGameValidity(color_ostream &stream, const EmptyMessage * in, SingleBool *out)
{
out->set_value(World::ReadPauseState());
auto viewScreen = Gui::getCurViewscreen();
if (strict_virtual_cast<df::viewscreen_loadgamest>(viewScreen))
{
out->set_value(false);
return CR_OK;
}
else if (strict_virtual_cast<df::viewscreen_savegamest>(viewScreen))
{
out->set_value(false);
return CR_OK;
}
out->set_value(true);
return CR_OK;
}