diff --git a/docs/changelog.txt b/docs/changelog.txt index 17c766f75..ef2fc825d 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -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` diff --git a/plugins/Plugins.cmake b/plugins/Plugins.cmake index 4468140f7..3aefde807 100644 --- a/plugins/Plugins.cmake +++ b/plugins/Plugins.cmake @@ -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") diff --git a/plugins/proto/DwarfControl.proto b/plugins/proto/DwarfControl.proto new file mode 100644 index 000000000..9bd63b8d3 --- /dev/null +++ b/plugins/proto/DwarfControl.proto @@ -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; +} diff --git a/plugins/proto/RemoteFortressReader.proto b/plugins/proto/RemoteFortressReader.proto index af971d55d..ed7557a2f 100644 --- a/plugins/proto/RemoteFortressReader.proto +++ b/plugins/proto/RemoteFortressReader.proto @@ -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 diff --git a/plugins/proto/ui_sidebar_mode.proto b/plugins/proto/ui_sidebar_mode.proto new file mode 100644 index 000000000..b07bb1baa --- /dev/null +++ b/plugins/proto/ui_sidebar_mode.proto @@ -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; +} \ No newline at end of file diff --git a/plugins/remotefortressreader/CMakeLists.txt b/plugins/remotefortressreader/CMakeLists.txt index 1ff1cac7f..3e998ffb5 100644 --- a/plugins/remotefortressreader/CMakeLists.txt +++ b/plugins/remotefortressreader/CMakeLists.txt @@ -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) diff --git a/plugins/remotefortressreader/building_reader.cpp b/plugins/remotefortressreader/building_reader.cpp index 5a0dd5c96..2d396980a 100644 --- a/plugins/remotefortressreader/building_reader.cpp +++ b/plugins/remotefortressreader/building_reader.cpp @@ -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: diff --git a/plugins/remotefortressreader/df_version_int.h b/plugins/remotefortressreader/df_version_int.h index 35c9ed851..d83fa7548 100644 --- a/plugins/remotefortressreader/df_version_int.h +++ b/plugins/remotefortressreader/df_version_int.h @@ -1,2 +1,2 @@ #pragma once -#define DF_VERSION_INT 44002 +#define DF_VERSION_INT 44012 diff --git a/plugins/remotefortressreader/dwarf_control.cpp b/plugins/remotefortressreader/dwarf_control.cpp new file mode 100644 index 000000000..98b51175f --- /dev/null +++ b/plugins/remotefortressreader/dwarf_control.cpp @@ -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 + +using namespace DFHack; +using namespace RemoteFortressReader; +using namespace df::enums; +using namespace Gui; +using namespace df::global; + +extern std::queue 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; +} diff --git a/plugins/remotefortressreader/dwarf_control.h b/plugins/remotefortressreader/dwarf_control.h new file mode 100644 index 000000000..b16a4365d --- /dev/null +++ b/plugins/remotefortressreader/dwarf_control.h @@ -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 + + diff --git a/plugins/remotefortressreader/item_reader.cpp b/plugins/remotefortressreader/item_reader.cpp index 3884d269f..620e06310 100644 --- a/plugins/remotefortressreader/item_reader.cpp +++ b/plugins/remotefortressreader/item_reader.cpp @@ -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; } diff --git a/plugins/remotefortressreader/remotefortressreader.cpp b/plugins/remotefortressreader/remotefortressreader.cpp index 7dced3b9f..cd9ebd87e 100644 --- a/plugins/remotefortressreader/remotefortressreader.cpp +++ b/plugins/remotefortressreader/remotefortressreader.cpp @@ -1,5 +1,5 @@ #include "df_version_int.h" -#define RFR_VERSION "0.19.1" +#define RFR_VERSION "0.20.2" #include #include @@ -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 & parameter return CR_OK; } -command_result dump_bp_mods(color_ostream &out, vector & 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 ¶meters) { 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 &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(viewScreen)) + { + out->set_value(false); + return CR_OK; + } + else if (strict_virtual_cast(viewScreen)) + { + out->set_value(false); + return CR_OK; + } + out->set_value(true); return CR_OK; }