From befab343a15999412dcab5a34a47b76bbd200afd Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Sat, 14 Jun 2014 23:50:47 +1200 Subject: [PATCH] Add ability to auto allocate rooms to noble positions. --- plugins/buildingplan.cpp | 302 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 301 insertions(+), 1 deletion(-) diff --git a/plugins/buildingplan.cpp b/plugins/buildingplan.cpp index 8b228b49e..803cdf2d4 100644 --- a/plugins/buildingplan.cpp +++ b/plugins/buildingplan.cpp @@ -16,6 +16,7 @@ #include "df/world.h" #include "df/building_constructionst.h" #include "df/building_design.h" +#include "df/entity_position.h" #include "modules/Gui.h" #include "modules/Buildings.h" @@ -38,7 +39,7 @@ using df::global::ui_build_selector; using df::global::world; DFHACK_PLUGIN("buildingplan"); -#define PLUGIN_VERSION 0.12 +#define PLUGIN_VERSION 0.14 struct MaterialDescriptor { @@ -461,6 +462,245 @@ private: static void delete_item_fn(df::job_item *x) { delete x; } +// START Room Reservation +static bool canReserveRoom(df::building *building) +{ + if (!building) + return false; + + if (building->jobs.size() > 0 && building->jobs[0]->job_type == job_type::DestroyBuilding) + return false; + + return building->is_room; +} + +static std::vector getUniqueNoblePositions(df::unit *unit) +{ + std::vector np; + Units::getNoblePositions(&np, unit); + for (auto iter = np.begin(); iter != np.end(); iter++) + { + if (iter->position->code == "MILITIA_CAPTAIN") + { + np.erase(iter); + break; + } + } + + return np; +} + +class ReservedRoom +{ +public: + ReservedRoom(df::building *building, string noble_code) + { + this->building = building; + config = DFHack::World::AddPersistentData("buildingplan/reservedroom"); + config.val() = noble_code; + config.ival(1) = building->id; + pos = df::coord(building->centerx, building->centery, building->z); + } + + ReservedRoom(PersistentDataItem &config, color_ostream &out) + { + this->config = config; + + building = df::building::find(config.ival(1)); + if (!building) + return; + pos = df::coord(building->centerx, building->centery, building->z); + } + + void remove() + { + DFHack::World::DeletePersistentData(config); + } + + bool isValid() + { + if (!building) + return false; + + if (Buildings::findAtTile(pos) != building) + return false; + + return canReserveRoom(building); + } + + int32_t getId() + { + if (!isValid()) + return 0; + + return building->id; + } + + string getCode() + { + return config.val(); + } + + void setCode(const string &noble_code) + { + config.val() = noble_code; + } + + bool checkRoomAssignment() + { + if (!isValid()) + return false; + + auto np = getOwnersNobleCode(); + bool correctOwner = false; + for (auto iter = np.begin(); iter != np.end(); iter++) + { + if (iter->position->code == getCode()) + { + correctOwner = true; + break; + } + } + + if (correctOwner) + return true; + + for (auto iter = world->units.active.begin(); iter != world->units.active.end(); iter++) + { + df::unit* unit = *iter; + if (!Units::isCitizen(unit)) + continue; + + if (DFHack::Units::isDead(unit)) + continue; + + np = getUniqueNoblePositions(unit); + for (auto iter = np.begin(); iter != np.end(); iter++) + { + if (iter->position->code == getCode()) + { + Buildings::setOwner(building, unit); + break; + } + } + } + + return true; + } + +private: + df::building *building; + PersistentDataItem config; + df::coord pos; + + std::vector getOwnersNobleCode() + { + if (!building->owner) + return std::vector (); + + return getUniqueNoblePositions(building->owner); + } +}; + +class RoomMonitor +{ +public: + RoomMonitor() + { + + } + + string getReservedNobleCode(int32_t buildingId) + { + for (auto iter = reservedRooms.begin(); iter != reservedRooms.end(); iter++) + { + if (buildingId == iter->getId()) + return iter->getCode(); + } + + return ""; + } + + void toggleRoomForPosition(int32_t buildingId, string noble_code) + { + bool found = false; + for (auto iter = reservedRooms.begin(); iter != reservedRooms.end(); iter++) + { + if (buildingId != iter->getId()) + { + continue; + } + else + { + if (noble_code == iter->getCode()) + { + iter->remove(); + reservedRooms.erase(iter); + } + else + { + iter->setCode(noble_code); + } + found = true; + break; + } + } + + if (!found) + { + ReservedRoom room(df::building::find(buildingId), noble_code); + reservedRooms.push_back(room); + } + } + + void doCycle() + { + for (auto iter = reservedRooms.begin(); iter != reservedRooms.end();) + { + if (iter->checkRoomAssignment()) + { + ++iter; + } + else + { + iter->remove(); + iter = reservedRooms.erase(iter); + } + } + } + + void reset(color_ostream &out) + { + reservedRooms.clear(); + std::vector items; + DFHack::World::GetPersistentData(&items, "buildingplan/reservedroom"); + + for (auto i = items.begin(); i != items.end(); i++) + { + ReservedRoom rr(*i, out); + if (rr.isValid()) + addRoom(rr); + } + } + +private: + vector reservedRooms; + + void addRoom(ReservedRoom &rr) + { + for (auto iter = reservedRooms.begin(); iter != reservedRooms.end(); iter++) + { + if (iter->getId() == rr.getId()) + return; + } + + reservedRooms.push_back(rr); + } +}; + +static RoomMonitor roomMonitor; + + // START Planning class PlannedBuilding { @@ -896,6 +1136,7 @@ DFhackCExport command_result plugin_onupdate(color_ostream &out) { last_frame_count = world->frame_counter; planner.doCycle(); + roomMonitor.doCycle(); } return CR_OK; @@ -929,6 +1170,37 @@ struct buildingplan_hook : public df::viewscreen_dwarfmodest planner.isPlanableBuilding(ui_build_selector->building_type); } + std::vector getNoblePositionOfSelectedBuildingOwner() + { + std::vector np; + if (ui->main.mode != df::ui_sidebar_mode::QueryBuilding || + !world->selected_building || + !world->selected_building->owner) + { + return np; + } + + switch (world->selected_building->getType()) + { + case building_type::Bed: + case building_type::Chair: + case building_type::Table: + break; + default: + return np; + } + + return getUniqueNoblePositions(world->selected_building->owner); + } + + bool isInNobleRoomQueryMode() + { + if (getNoblePositionOfSelectedBuildingOwner().size() > 0) + return canReserveRoom(world->selected_building); + else + return false; + } + bool handleInput(set *input) { if (isInPlannedBuildingPlacementMode()) @@ -1020,6 +1292,19 @@ struct buildingplan_hook : public df::viewscreen_dwarfmodest } } + else if (isInNobleRoomQueryMode()) + { + auto np = getNoblePositionOfSelectedBuildingOwner(); + df::interface_key last_token = *input->rbegin(); + if (last_token >= interface_key::STRING_A048 && last_token <= interface_key::STRING_A058) + { + int selection = last_token - interface_key::STRING_A048; + if (np.size() < selection) + return false; + roomMonitor.toggleRoomForPosition(world->selected_building->id, np.at(selection-1).position->code); + return true; + } + } return false; } @@ -1130,6 +1415,20 @@ struct buildingplan_hook : public df::viewscreen_dwarfmodest for (auto it = filters.begin(); it != filters.end(); ++it) OutputString(COLOR_BLUE, x, y, "*" + *it, true, left_margin); } + else if (isInNobleRoomQueryMode()) + { + auto np = getNoblePositionOfSelectedBuildingOwner(); + int y = 24; + OutputString(COLOR_BROWN, x, y, "DFHack", true, left_margin); + OutputString(COLOR_WHITE, x, y, "Auto-allocate to:", true, left_margin); + for (int i = 0; i < np.size() && i < 9; i++) + { + bool enabled = (roomMonitor.getReservedNobleCode(world->selected_building->id) + == np[i].position->code); + OutputToggleString(x, y, np[i].position->name[0].c_str(), + int_to_string(i+1).c_str(), enabled, true, left_margin); + } + } else { planner.in_dummmy_screen = false; @@ -1198,6 +1497,7 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan switch (event) { case SC_MAP_LOADED: planner.reset(out); + roomMonitor.reset(out); break; default: break;