From ce3ee386fdc48e39d624014e7d87b9cca71b2b83 Mon Sep 17 00:00:00 2001 From: 20k Date: Sat, 21 Jan 2023 20:07:12 +0000 Subject: [PATCH 01/13] makeSquad, updateRoomAssignments --- docs/dev/Lua API.rst | 13 ++ library/LuaApi.cpp | 2 + library/include/modules/Units.h | 3 + library/modules/Units.cpp | 278 ++++++++++++++++++++++++++++++++ 4 files changed, 296 insertions(+) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index c2cc7f5cd..b089fb8ea 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1589,6 +1589,19 @@ Units module Returns a table of the cutoffs used by the above stress level functions. +* ``dfhack.units.makeSquad(assignment_id)`` + + Creates a new squad associated with the assignment. Fails if one already exists + Note: This function does not name the squad, but they are otherwise complete + +* ``dfhack.units.updateRoomAssignments(squad_id, assignment_id, squad_use_flags)`` + + Sets the sleep, train, indiv_eq, and squad_eq flags when training at a barracks + +* ``dfhack.units.getSquadName(squad)`` + + Returns the name of a squad + Action Timer API ~~~~~~~~~~~~~~~~ diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 426c87566..af56bb4c3 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1812,6 +1812,8 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = { WRAPM(Units, getGoalType), WRAPM(Units, getGoalName), WRAPM(Units, isGoalAchieved), + WRAPM(Units, makeSquad), + WRAPM(Units, updateRoomAssignments), WRAPM(Units, getSquadName), WRAPM(Units, getPhysicalDescription), WRAPM(Units, getRaceName), diff --git a/library/include/modules/Units.h b/library/include/modules/Units.h index 0edebacdc..f3c7baf6a 100644 --- a/library/include/modules/Units.h +++ b/library/include/modules/Units.h @@ -37,6 +37,7 @@ distribution. #include "df/mental_attribute_type.h" #include "df/misc_trait_type.h" #include "df/physical_attribute_type.h" +#include "df/squad.h" #include "df/unit.h" #include "df/unit_action.h" #include "df/unit_action_type_group.h" @@ -223,6 +224,8 @@ DFHACK_EXPORT std::string getGoalName(df::unit *unit, size_t goalIndex = 0); DFHACK_EXPORT bool isGoalAchieved(df::unit *unit, size_t goalIndex = 0); DFHACK_EXPORT std::string getSquadName(df::unit *unit); +DFHACK_EXPORT df::squad* makeSquad(int32_t assignment_id); +DFHACK_EXPORT void updateRoomAssignments(int32_t squad_id, int32_t civzone_id, df::squad_use_flags flags); DFHACK_EXPORT df::activity_entry *getMainSocialActivity(df::unit *unit); DFHACK_EXPORT df::activity_event *getMainSocialEvent(df::unit *unit); diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 9a415fb34..0ab507517 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -33,6 +33,7 @@ distribution. #include #include #include +#include using namespace std; #include "VersionInfo.h" @@ -51,6 +52,7 @@ using namespace std; #include "MiscUtils.h" #include "df/activity_entry.h" +#include "df/building_civzonest.h" #include "df/burrow.h" #include "df/caste_raw.h" #include "df/creature_raw.h" @@ -61,6 +63,7 @@ using namespace std; #include "df/entity_raw_flags.h" #include "df/identity_type.h" #include "df/game_mode.h" +#include "df/global_objects.h" #include "df/histfig_entity_link_positionst.h" #include "df/histfig_relationship_type.h" #include "df/historical_entity.h" @@ -72,6 +75,10 @@ using namespace std; #include "df/job.h" #include "df/nemesis_record.h" #include "df/squad.h" +#include "df/squad_position.h" +#include "df/squad_schedule_order.h" +#include "df/squad_order.h" +#include "df/squad_order_trainst.h" #include "df/tile_occupancy.h" #include "df/plotinfost.h" #include "df/unit_inventory_item.h" @@ -1972,6 +1979,277 @@ std::string Units::getSquadName(df::unit *unit) return Translation::TranslateName(&squad->name, true); } +//only works for making squads for fort mode player controlled dwarf squads +//could be extended straightforwardly by passing in entity +df::squad* Units::makeSquad(int32_t assignment_id) +{ + if (df::global::squad_next_id == nullptr || df::global::plotinfo == nullptr) + return nullptr; + + df::language_name name; + name.type = df::language_name_type::Squad; + + for (int i=0; i < 7; i++) + { + name.words[i] = -1; + name.parts_of_speech[i] = df::part_of_speech::Noun; + } + + df::historical_entity* fort = df::historical_entity::find(df::global::plotinfo->group_id); + + df::entity_position_assignment* found_assignment = nullptr; + + for (auto* assignment : fort->positions.assignments) + { + if (assignment->id == assignment_id) + { + found_assignment = assignment; + break; + } + } + + if (found_assignment == nullptr) + return nullptr; + + //this function does not attempt to delete or replace squads for assignments + if (found_assignment->squad_id != -1) + return nullptr; + + df::entity_position* corresponding_position = nullptr; + + for (auto* position : fort->positions.own) + { + if (position->id == found_assignment->position_id) + { + corresponding_position = position; + break; + } + } + + if (corresponding_position == nullptr) + return nullptr; + + df::squad* result = new df::squad(); + result->id = *df::global::squad_next_id; + result->cur_routine_idx = 0; + result->uniform_priority = result->id + 1; //no idea why, but seems to hold + result->activity = -1; //?? + result->carry_food = 2; + result->carry_water = 1; + result->entity_id = df::global::plotinfo->group_id; + result->leader_position = corresponding_position->id; + result->leader_assignment = found_assignment->id; + result->unk_1 = -1; + result->name = name; + result->ammo.unk_v50_1 = 0; + + int16_t squad_size = corresponding_position->squad_size; + + for (int i=0; i < squad_size; i++) + { + //construct for squad_position seems to set all the attributes correctly + //except I've observed unk_2 is -1 generally + df::squad_position* pos = new df::squad_position(); + pos->unk_2 = -1; + pos->flags.whole = 0; + + result->positions.push_back(pos); + } + + const auto& routines = df::global::plotinfo->alerts.routines; + + for (const auto& routine : routines) + { + df::squad_schedule_entry* asched = (df::squad_schedule_entry*)malloc(sizeof(df::squad_schedule_entry) * 12); + + for(int kk=0; kk < 12; kk++) + { + new (&asched[kk]) df::squad_schedule_entry; + + for(int jj=0; jj < squad_size; jj++) + { + int32_t* order_assignments = new int32_t(); + *order_assignments = -1; + + asched[kk].order_assignments.push_back(order_assignments); + } + } + + auto insert_training_order = [asched, squad_size](int month) + { + df::squad_schedule_order* order = new df::squad_schedule_order(); + order->min_count = squad_size; + //assumed + order->positions.resize(squad_size); + + df::squad_order* s_order = df::allocate(); + + s_order->unk_v40_1 = -1; + s_order->unk_v40_2 = -1; + s_order->year = *df::global::cur_year; + s_order->year_tick = *df::global::cur_year_tick; + s_order->unk_v40_3 = -1; + s_order->unk_1 = 0; + + order->order = s_order; + + asched[month].orders.push_back(order); + //wear uniform while training + asched[month].uniform_mode = 0; + }; + + //I thought this was a terrible hack, but its literally how dwarf fortress does it 1:1 + //Off duty: No orders, Sleep/room at will. Equip/orders only + if (routine->name == "Off duty") + { + for (int i=0; i < 12; i++) + { + asched[i].sleep_mode = 0; + asched[i].uniform_mode = 1; + } + } + //Staggered Training: Training orders at 3 4 5, 9 10 11, sleep/room at will. Equip/orders only, except train months which are equip/always + //always seen the training indices 0 1 2 6 7 8, so its unclear. Check if squad id matters + else if (routine->name == "Staggered training") + { + //this is semi randomised for different squads + //appears to be something like squad.id & 1, it isn't smart + //if you alternate squad creation, its 'correctly' staggered + //but it'll also happily not stagger them if you eg delete a squad and make another + std::array indices; + + if ((*df::global::squad_next_id) & 1) + { + indices = {3, 4, 5, 9, 10, 11}; + } + else + { + indices = {0, 1, 2, 6, 7, 8}; + } + + for (int index : indices) + { + insert_training_order(index); + //still sleep in room at will even when training + asched[index].sleep_mode = 0; + } + } + //see above, but with all indices + else if (routine->name == "Constant training") + { + for (int i=0; i < 12; i++) + { + insert_training_order(i); + //still sleep in room at will even when training + asched[i].sleep_mode = 0; + } + } + else if (routine->name == "Ready") + { + for (int i=0; i < 12; i++) + { + asched[i].sleep_mode = 2; + asched[i].uniform_mode = 0; + } + } + else + { + for (int i=0; i < 12; i++) + { + asched[i].sleep_mode = 0; + asched[i].uniform_mode = 0; + } + } + + result->schedule.push_back(reinterpret_cast(asched)); + } + + //all we've done so far is leak memory if anything goes wrong + //modify state + (*df::global::squad_next_id)++; + fort->squads.push_back(result->id); + df::global::world->squads.all.push_back(result); + found_assignment->squad_id = result->id; + + //todo: find and modify old squad + + return result; +} + +void Units::updateRoomAssignments(int32_t squad_id, int32_t civzone_id, df::squad_use_flags flags) +{ + df::squad* squad = df::squad::find(squad_id); + df::building* bzone = df::building::find(civzone_id); + + df::building_civzonest* zone = strict_virtual_cast(bzone); + + if (squad == nullptr || zone == nullptr) + return; + + df::squad::T_rooms* room_from_squad = nullptr; + df::building_civzonest::T_squad_room_info* room_from_building = nullptr; + + for (auto room : squad->rooms) + { + if (room->building_id == civzone_id) + { + room_from_squad = room; + break; + } + } + + for (auto room : zone->squad_room_info) + { + if (room->squad_id == squad_id) + { + room_from_building = room; + break; + } + } + + if (flags.whole == 0 && room_from_squad == nullptr && room_from_building == nullptr) + return; + + //if we're setting 0 flags, and there's no room already, don't set a room + bool avoiding_squad_roundtrip = flags.whole == 0 && room_from_squad == nullptr; + + if (!avoiding_squad_roundtrip && room_from_squad == nullptr) + { + room_from_squad = new df::squad::T_rooms(); + room_from_squad->building_id = civzone_id; + squad->rooms.push_back(room_from_squad); + + std::sort(squad->rooms.begin(), squad->rooms.end(), [](df::squad::T_rooms* a, df::squad::T_rooms* b){return a->building_id < b->building_id;}); + } + + if (room_from_building == nullptr) + { + room_from_building = new df::building_civzonest::T_squad_room_info(); + room_from_building->squad_id = squad_id; + zone->squad_room_info.push_back(room_from_building); + + std::sort(zone->squad_room_info.begin(), zone->squad_room_info.end(), [](df::building_civzonest::T_squad_room_info* a, df::building_civzonest::T_squad_room_info* b){return a->squad_id < b->squad_id;}); + } + + if (room_from_squad) + room_from_squad->mode = flags; + + room_from_building->mode = flags; + + if (flags.whole == 0 && !avoiding_squad_roundtrip) + { + for (int i=0; i < (int)squad->rooms.size(); i++) + { + if (squad->rooms[i]->building_id == civzone_id) + { + delete squad->rooms[i]; + squad->rooms.erase(squad->rooms.begin() + i); + i--; + } + } + } +} + df::activity_entry *Units::getMainSocialActivity(df::unit *unit) { CHECK_NULL_POINTER(unit); From 3912c6290f632d3cb1f41620aeeae1a0e050eaf3 Mon Sep 17 00:00:00 2001 From: 20k Date: Mon, 30 Jan 2023 06:28:23 +0000 Subject: [PATCH 02/13] Military module start --- docs/dev/Lua API.rst | 9 +- library/CMakeLists.txt | 2 + library/LuaApi.cpp | 14 +- library/include/modules/Military.h | 19 ++ library/include/modules/Units.h | 5 - library/modules/Military.cpp | 307 +++++++++++++++++++++++++++++ library/modules/Units.cpp | 284 -------------------------- 7 files changed, 345 insertions(+), 295 deletions(-) create mode 100644 library/include/modules/Military.h create mode 100644 library/modules/Military.cpp diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index b089fb8ea..9125ca902 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1589,16 +1589,19 @@ Units module Returns a table of the cutoffs used by the above stress level functions. -* ``dfhack.units.makeSquad(assignment_id)`` +Military Module API +~~~~~~~~~~~~~~~~~~~ + +* ``dfhack.military.makeSquad(assignment_id)`` Creates a new squad associated with the assignment. Fails if one already exists Note: This function does not name the squad, but they are otherwise complete -* ``dfhack.units.updateRoomAssignments(squad_id, assignment_id, squad_use_flags)`` +* ``dfhack.military.updateRoomAssignments(squad_id, assignment_id, squad_use_flags)`` Sets the sleep, train, indiv_eq, and squad_eq flags when training at a barracks -* ``dfhack.units.getSquadName(squad)`` +* ``dfhack.military.getSquadName(squad)`` Returns the name of a squad diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 87f869493..92db43563 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -137,6 +137,7 @@ set(MODULE_HEADERS include/modules/MapCache.h include/modules/Maps.h include/modules/Materials.h + include/modules/Military.h include/modules/Once.h include/modules/Persistence.h include/modules/Random.h @@ -164,6 +165,7 @@ set(MODULE_SOURCES modules/MapCache.cpp modules/Maps.cpp modules/Materials.cpp + modules/Military.cpp modules/Once.cpp modules/Persistence.cpp modules/Random.cpp diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index af56bb4c3..13ea1a7fe 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -55,6 +55,7 @@ distribution. #include "modules/MapCache.h" #include "modules/Maps.h" #include "modules/Materials.h" +#include "modules/Military.h" #include "modules/Random.h" #include "modules/Screen.h" #include "modules/Textures.h" @@ -1812,9 +1813,6 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = { WRAPM(Units, getGoalType), WRAPM(Units, getGoalName), WRAPM(Units, isGoalAchieved), - WRAPM(Units, makeSquad), - WRAPM(Units, updateRoomAssignments), - WRAPM(Units, getSquadName), WRAPM(Units, getPhysicalDescription), WRAPM(Units, getRaceName), WRAPM(Units, getRaceNamePlural), @@ -1939,6 +1937,15 @@ static const luaL_Reg dfhack_units_funcs[] = { { NULL, NULL } }; +/***** Military Module *****/ + +static const LuaWrapper::FunctionReg dfhack_military_module[] = { + WRAPM(Military, makeSquad), + WRAPM(Military, updateRoomAssignments), + WRAPM(Military, getSquadName), + { NULL, NULL } +}; + /***** Items module *****/ static bool items_moveToGround(df::item *item, df::coord pos) @@ -3449,6 +3456,7 @@ void OpenDFHackApi(lua_State *state) OpenModule(state, "job", dfhack_job_module, dfhack_job_funcs); OpenModule(state, "textures", dfhack_textures_module); OpenModule(state, "units", dfhack_units_module, dfhack_units_funcs); + OpenModule(state, "military", dfhack_military_module); OpenModule(state, "items", dfhack_items_module, dfhack_items_funcs); OpenModule(state, "maps", dfhack_maps_module, dfhack_maps_funcs); OpenModule(state, "world", dfhack_world_module, dfhack_world_funcs); diff --git a/library/include/modules/Military.h b/library/include/modules/Military.h new file mode 100644 index 000000000..19ed47ee2 --- /dev/null +++ b/library/include/modules/Military.h @@ -0,0 +1,19 @@ +#pragma once + +#include "Export.h" +#include "DataDefs.h" + +#include "df/squad.h" +#include "df/unit.h" + +namespace DFHack +{ +namespace Military +{ + +DFHACK_EXPORT std::string getSquadName(df::unit *unit); +DFHACK_EXPORT df::squad* makeSquad(int32_t assignment_id); +DFHACK_EXPORT void updateRoomAssignments(int32_t squad_id, int32_t civzone_id, df::squad_use_flags flags); + +} +} \ No newline at end of file diff --git a/library/include/modules/Units.h b/library/include/modules/Units.h index f3c7baf6a..4fd9246aa 100644 --- a/library/include/modules/Units.h +++ b/library/include/modules/Units.h @@ -37,7 +37,6 @@ distribution. #include "df/mental_attribute_type.h" #include "df/misc_trait_type.h" #include "df/physical_attribute_type.h" -#include "df/squad.h" #include "df/unit.h" #include "df/unit_action.h" #include "df/unit_action_type_group.h" @@ -223,10 +222,6 @@ DFHACK_EXPORT df::goal_type getGoalType(df::unit *unit, size_t goalIndex = 0); DFHACK_EXPORT std::string getGoalName(df::unit *unit, size_t goalIndex = 0); DFHACK_EXPORT bool isGoalAchieved(df::unit *unit, size_t goalIndex = 0); -DFHACK_EXPORT std::string getSquadName(df::unit *unit); -DFHACK_EXPORT df::squad* makeSquad(int32_t assignment_id); -DFHACK_EXPORT void updateRoomAssignments(int32_t squad_id, int32_t civzone_id, df::squad_use_flags flags); - DFHACK_EXPORT df::activity_entry *getMainSocialActivity(df::unit *unit); DFHACK_EXPORT df::activity_event *getMainSocialEvent(df::unit *unit); diff --git a/library/modules/Military.cpp b/library/modules/Military.cpp new file mode 100644 index 000000000..b67b3a129 --- /dev/null +++ b/library/modules/Military.cpp @@ -0,0 +1,307 @@ +#include +#include +#include +#include "modules/Military.h" +#include "modules/Translation.h" +#include "df/building.h" +#include "df/building_civzonest.h" +#include "df/historical_figure.h" +#include "df/historical_entity.h" +#include "df/entity_position.h" +#include "df/entity_position_assignment.h" +#include "df/plotinfost.h" +#include "df/squad.h" +#include "df/squad_position.h" +#include "df/squad_schedule_order.h" +#include "df/squad_order.h" +#include "df/squad_order_trainst.h" +#include "df/world.h" + +using namespace DFHack; +using namespace df::enums; +using df::global::world; +using df::global::plotinfo; + +std::string Military::getSquadName(df::unit *unit) +{ + CHECK_NULL_POINTER(unit); + if (unit->military.squad_id == -1) + return ""; + df::squad *squad = df::squad::find(unit->military.squad_id); + if (!squad) + return ""; + if (squad->alias.size() > 0) + return squad->alias; + return Translation::TranslateName(&squad->name, true); +} + +//only works for making squads for fort mode player controlled dwarf squads +//could be extended straightforwardly by passing in entity +df::squad* Military::makeSquad(int32_t assignment_id) +{ + if (df::global::squad_next_id == nullptr || df::global::plotinfo == nullptr) + return nullptr; + + df::language_name name; + name.type = df::language_name_type::Squad; + + for (int i=0; i < 7; i++) + { + name.words[i] = -1; + name.parts_of_speech[i] = df::part_of_speech::Noun; + } + + df::historical_entity* fort = df::historical_entity::find(df::global::plotinfo->group_id); + + df::entity_position_assignment* found_assignment = nullptr; + + for (auto* assignment : fort->positions.assignments) + { + if (assignment->id == assignment_id) + { + found_assignment = assignment; + break; + } + } + + if (found_assignment == nullptr) + return nullptr; + + //this function does not attempt to delete or replace squads for assignments + if (found_assignment->squad_id != -1) + return nullptr; + + df::entity_position* corresponding_position = nullptr; + + for (auto* position : fort->positions.own) + { + if (position->id == found_assignment->position_id) + { + corresponding_position = position; + break; + } + } + + if (corresponding_position == nullptr) + return nullptr; + + df::squad* result = new df::squad(); + result->id = *df::global::squad_next_id; + result->cur_routine_idx = 0; + result->uniform_priority = result->id + 1; //no idea why, but seems to hold + result->activity = -1; //?? + result->carry_food = 2; + result->carry_water = 1; + result->entity_id = df::global::plotinfo->group_id; + result->leader_position = corresponding_position->id; + result->leader_assignment = found_assignment->id; + result->unk_1 = -1; + result->name = name; + result->ammo.unk_v50_1 = 0; + + int16_t squad_size = corresponding_position->squad_size; + + for (int i=0; i < squad_size; i++) + { + //construct for squad_position seems to set all the attributes correctly + //except I've observed unk_2 is -1 generally + df::squad_position* pos = new df::squad_position(); + pos->unk_2 = -1; + pos->flags.whole = 0; + + result->positions.push_back(pos); + } + + const auto& routines = df::global::plotinfo->alerts.routines; + + for (const auto& routine : routines) + { + df::squad_schedule_entry* asched = (df::squad_schedule_entry*)malloc(sizeof(df::squad_schedule_entry) * 12); + + for(int kk=0; kk < 12; kk++) + { + new (&asched[kk]) df::squad_schedule_entry; + + for(int jj=0; jj < squad_size; jj++) + { + int32_t* order_assignments = new int32_t(); + *order_assignments = -1; + + asched[kk].order_assignments.push_back(order_assignments); + } + } + + auto insert_training_order = [asched, squad_size](int month) + { + df::squad_schedule_order* order = new df::squad_schedule_order(); + order->min_count = squad_size; + //assumed + order->positions.resize(squad_size); + + df::squad_order* s_order = df::allocate(); + + s_order->unk_v40_1 = -1; + s_order->unk_v40_2 = -1; + s_order->year = *df::global::cur_year; + s_order->year_tick = *df::global::cur_year_tick; + s_order->unk_v40_3 = -1; + s_order->unk_1 = 0; + + order->order = s_order; + + asched[month].orders.push_back(order); + //wear uniform while training + asched[month].uniform_mode = 0; + }; + + //I thought this was a terrible hack, but its literally how dwarf fortress does it 1:1 + //Off duty: No orders, Sleep/room at will. Equip/orders only + if (routine->name == "Off duty") + { + for (int i=0; i < 12; i++) + { + asched[i].sleep_mode = 0; + asched[i].uniform_mode = 1; + } + } + //Staggered Training: Training orders at 3 4 5, 9 10 11, sleep/room at will. Equip/orders only, except train months which are equip/always + //always seen the training indices 0 1 2 6 7 8, so its unclear. Check if squad id matters + else if (routine->name == "Staggered training") + { + //this is semi randomised for different squads + //appears to be something like squad.id & 1, it isn't smart + //if you alternate squad creation, its 'correctly' staggered + //but it'll also happily not stagger them if you eg delete a squad and make another + std::array indices; + + if ((*df::global::squad_next_id) & 1) + { + indices = {3, 4, 5, 9, 10, 11}; + } + else + { + indices = {0, 1, 2, 6, 7, 8}; + } + + for (int index : indices) + { + insert_training_order(index); + //still sleep in room at will even when training + asched[index].sleep_mode = 0; + } + } + //see above, but with all indices + else if (routine->name == "Constant training") + { + for (int i=0; i < 12; i++) + { + insert_training_order(i); + //still sleep in room at will even when training + asched[i].sleep_mode = 0; + } + } + else if (routine->name == "Ready") + { + for (int i=0; i < 12; i++) + { + asched[i].sleep_mode = 2; + asched[i].uniform_mode = 0; + } + } + else + { + for (int i=0; i < 12; i++) + { + asched[i].sleep_mode = 0; + asched[i].uniform_mode = 0; + } + } + + result->schedule.push_back(reinterpret_cast(asched)); + } + + //all we've done so far is leak memory if anything goes wrong + //modify state + (*df::global::squad_next_id)++; + fort->squads.push_back(result->id); + df::global::world->squads.all.push_back(result); + found_assignment->squad_id = result->id; + + //todo: find and modify old squad + + return result; +} + +void Military::updateRoomAssignments(int32_t squad_id, int32_t civzone_id, df::squad_use_flags flags) +{ + df::squad* squad = df::squad::find(squad_id); + df::building* bzone = df::building::find(civzone_id); + + df::building_civzonest* zone = strict_virtual_cast(bzone); + + if (squad == nullptr || zone == nullptr) + return; + + df::squad::T_rooms* room_from_squad = nullptr; + df::building_civzonest::T_squad_room_info* room_from_building = nullptr; + + for (auto room : squad->rooms) + { + if (room->building_id == civzone_id) + { + room_from_squad = room; + break; + } + } + + for (auto room : zone->squad_room_info) + { + if (room->squad_id == squad_id) + { + room_from_building = room; + break; + } + } + + if (flags.whole == 0 && room_from_squad == nullptr && room_from_building == nullptr) + return; + + //if we're setting 0 flags, and there's no room already, don't set a room + bool avoiding_squad_roundtrip = flags.whole == 0 && room_from_squad == nullptr; + + if (!avoiding_squad_roundtrip && room_from_squad == nullptr) + { + room_from_squad = new df::squad::T_rooms(); + room_from_squad->building_id = civzone_id; + squad->rooms.push_back(room_from_squad); + + std::sort(squad->rooms.begin(), squad->rooms.end(), [](df::squad::T_rooms* a, df::squad::T_rooms* b){return a->building_id < b->building_id;}); + } + + if (room_from_building == nullptr) + { + room_from_building = new df::building_civzonest::T_squad_room_info(); + room_from_building->squad_id = squad_id; + zone->squad_room_info.push_back(room_from_building); + + std::sort(zone->squad_room_info.begin(), zone->squad_room_info.end(), [](df::building_civzonest::T_squad_room_info* a, df::building_civzonest::T_squad_room_info* b){return a->squad_id < b->squad_id;}); + } + + if (room_from_squad) + room_from_squad->mode = flags; + + room_from_building->mode = flags; + + if (flags.whole == 0 && !avoiding_squad_roundtrip) + { + for (int i=0; i < (int)squad->rooms.size(); i++) + { + if (squad->rooms[i]->building_id == civzone_id) + { + delete squad->rooms[i]; + squad->rooms.erase(squad->rooms.begin() + i); + i--; + } + } + } +} \ No newline at end of file diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 0ab507517..137a07da6 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -1966,290 +1966,6 @@ bool Units::isGoalAchieved(df::unit *unit, size_t goalIndex) && unit->status.current_soul->personality.dreams[goalIndex]->flags.whole != 0; } -std::string Units::getSquadName(df::unit *unit) -{ - CHECK_NULL_POINTER(unit); - if (unit->military.squad_id == -1) - return ""; - df::squad *squad = df::squad::find(unit->military.squad_id); - if (!squad) - return ""; - if (squad->alias.size() > 0) - return squad->alias; - return Translation::TranslateName(&squad->name, true); -} - -//only works for making squads for fort mode player controlled dwarf squads -//could be extended straightforwardly by passing in entity -df::squad* Units::makeSquad(int32_t assignment_id) -{ - if (df::global::squad_next_id == nullptr || df::global::plotinfo == nullptr) - return nullptr; - - df::language_name name; - name.type = df::language_name_type::Squad; - - for (int i=0; i < 7; i++) - { - name.words[i] = -1; - name.parts_of_speech[i] = df::part_of_speech::Noun; - } - - df::historical_entity* fort = df::historical_entity::find(df::global::plotinfo->group_id); - - df::entity_position_assignment* found_assignment = nullptr; - - for (auto* assignment : fort->positions.assignments) - { - if (assignment->id == assignment_id) - { - found_assignment = assignment; - break; - } - } - - if (found_assignment == nullptr) - return nullptr; - - //this function does not attempt to delete or replace squads for assignments - if (found_assignment->squad_id != -1) - return nullptr; - - df::entity_position* corresponding_position = nullptr; - - for (auto* position : fort->positions.own) - { - if (position->id == found_assignment->position_id) - { - corresponding_position = position; - break; - } - } - - if (corresponding_position == nullptr) - return nullptr; - - df::squad* result = new df::squad(); - result->id = *df::global::squad_next_id; - result->cur_routine_idx = 0; - result->uniform_priority = result->id + 1; //no idea why, but seems to hold - result->activity = -1; //?? - result->carry_food = 2; - result->carry_water = 1; - result->entity_id = df::global::plotinfo->group_id; - result->leader_position = corresponding_position->id; - result->leader_assignment = found_assignment->id; - result->unk_1 = -1; - result->name = name; - result->ammo.unk_v50_1 = 0; - - int16_t squad_size = corresponding_position->squad_size; - - for (int i=0; i < squad_size; i++) - { - //construct for squad_position seems to set all the attributes correctly - //except I've observed unk_2 is -1 generally - df::squad_position* pos = new df::squad_position(); - pos->unk_2 = -1; - pos->flags.whole = 0; - - result->positions.push_back(pos); - } - - const auto& routines = df::global::plotinfo->alerts.routines; - - for (const auto& routine : routines) - { - df::squad_schedule_entry* asched = (df::squad_schedule_entry*)malloc(sizeof(df::squad_schedule_entry) * 12); - - for(int kk=0; kk < 12; kk++) - { - new (&asched[kk]) df::squad_schedule_entry; - - for(int jj=0; jj < squad_size; jj++) - { - int32_t* order_assignments = new int32_t(); - *order_assignments = -1; - - asched[kk].order_assignments.push_back(order_assignments); - } - } - - auto insert_training_order = [asched, squad_size](int month) - { - df::squad_schedule_order* order = new df::squad_schedule_order(); - order->min_count = squad_size; - //assumed - order->positions.resize(squad_size); - - df::squad_order* s_order = df::allocate(); - - s_order->unk_v40_1 = -1; - s_order->unk_v40_2 = -1; - s_order->year = *df::global::cur_year; - s_order->year_tick = *df::global::cur_year_tick; - s_order->unk_v40_3 = -1; - s_order->unk_1 = 0; - - order->order = s_order; - - asched[month].orders.push_back(order); - //wear uniform while training - asched[month].uniform_mode = 0; - }; - - //I thought this was a terrible hack, but its literally how dwarf fortress does it 1:1 - //Off duty: No orders, Sleep/room at will. Equip/orders only - if (routine->name == "Off duty") - { - for (int i=0; i < 12; i++) - { - asched[i].sleep_mode = 0; - asched[i].uniform_mode = 1; - } - } - //Staggered Training: Training orders at 3 4 5, 9 10 11, sleep/room at will. Equip/orders only, except train months which are equip/always - //always seen the training indices 0 1 2 6 7 8, so its unclear. Check if squad id matters - else if (routine->name == "Staggered training") - { - //this is semi randomised for different squads - //appears to be something like squad.id & 1, it isn't smart - //if you alternate squad creation, its 'correctly' staggered - //but it'll also happily not stagger them if you eg delete a squad and make another - std::array indices; - - if ((*df::global::squad_next_id) & 1) - { - indices = {3, 4, 5, 9, 10, 11}; - } - else - { - indices = {0, 1, 2, 6, 7, 8}; - } - - for (int index : indices) - { - insert_training_order(index); - //still sleep in room at will even when training - asched[index].sleep_mode = 0; - } - } - //see above, but with all indices - else if (routine->name == "Constant training") - { - for (int i=0; i < 12; i++) - { - insert_training_order(i); - //still sleep in room at will even when training - asched[i].sleep_mode = 0; - } - } - else if (routine->name == "Ready") - { - for (int i=0; i < 12; i++) - { - asched[i].sleep_mode = 2; - asched[i].uniform_mode = 0; - } - } - else - { - for (int i=0; i < 12; i++) - { - asched[i].sleep_mode = 0; - asched[i].uniform_mode = 0; - } - } - - result->schedule.push_back(reinterpret_cast(asched)); - } - - //all we've done so far is leak memory if anything goes wrong - //modify state - (*df::global::squad_next_id)++; - fort->squads.push_back(result->id); - df::global::world->squads.all.push_back(result); - found_assignment->squad_id = result->id; - - //todo: find and modify old squad - - return result; -} - -void Units::updateRoomAssignments(int32_t squad_id, int32_t civzone_id, df::squad_use_flags flags) -{ - df::squad* squad = df::squad::find(squad_id); - df::building* bzone = df::building::find(civzone_id); - - df::building_civzonest* zone = strict_virtual_cast(bzone); - - if (squad == nullptr || zone == nullptr) - return; - - df::squad::T_rooms* room_from_squad = nullptr; - df::building_civzonest::T_squad_room_info* room_from_building = nullptr; - - for (auto room : squad->rooms) - { - if (room->building_id == civzone_id) - { - room_from_squad = room; - break; - } - } - - for (auto room : zone->squad_room_info) - { - if (room->squad_id == squad_id) - { - room_from_building = room; - break; - } - } - - if (flags.whole == 0 && room_from_squad == nullptr && room_from_building == nullptr) - return; - - //if we're setting 0 flags, and there's no room already, don't set a room - bool avoiding_squad_roundtrip = flags.whole == 0 && room_from_squad == nullptr; - - if (!avoiding_squad_roundtrip && room_from_squad == nullptr) - { - room_from_squad = new df::squad::T_rooms(); - room_from_squad->building_id = civzone_id; - squad->rooms.push_back(room_from_squad); - - std::sort(squad->rooms.begin(), squad->rooms.end(), [](df::squad::T_rooms* a, df::squad::T_rooms* b){return a->building_id < b->building_id;}); - } - - if (room_from_building == nullptr) - { - room_from_building = new df::building_civzonest::T_squad_room_info(); - room_from_building->squad_id = squad_id; - zone->squad_room_info.push_back(room_from_building); - - std::sort(zone->squad_room_info.begin(), zone->squad_room_info.end(), [](df::building_civzonest::T_squad_room_info* a, df::building_civzonest::T_squad_room_info* b){return a->squad_id < b->squad_id;}); - } - - if (room_from_squad) - room_from_squad->mode = flags; - - room_from_building->mode = flags; - - if (flags.whole == 0 && !avoiding_squad_roundtrip) - { - for (int i=0; i < (int)squad->rooms.size(); i++) - { - if (squad->rooms[i]->building_id == civzone_id) - { - delete squad->rooms[i]; - squad->rooms.erase(squad->rooms.begin() + i); - i--; - } - } - } -} - df::activity_entry *Units::getMainSocialActivity(df::unit *unit) { CHECK_NULL_POINTER(unit); From d84b1187678ce65a1106a3cec54a73fa3c5d9fbe Mon Sep 17 00:00:00 2001 From: 20k Date: Mon, 30 Jan 2023 07:11:42 +0000 Subject: [PATCH 03/13] docs, rework, rename --- docs/changelog.txt | 7 +++++++ docs/dev/Lua API.rst | 10 +++++----- library/LuaApi.cpp | 2 +- library/include/modules/Military.h | 6 +++--- library/modules/Military.cpp | 9 +++------ plugins/manipulator.cpp | 3 ++- 6 files changed, 21 insertions(+), 16 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index dd109a218..2da870ca7 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -171,6 +171,12 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``Screen::Pen``: now accepts ``top_of_text`` and ``bottom_of_text`` properties to support offset text in graphics mode - `overlay`: overlay widgets can now specify a default enabled state if they are not already set in the player's overlay config file - ``Lua::Push``: now supports ``std::unordered_map`` +- `Military`: New module for military functionality +- `Military`: new ``makeSquad`` to create a squad +- `Military`: changed ``getSquadName`` to take a squad identifier +- `Military`: new ``updateRoomAssignments`` for assigning a squad to a barracks and archery range +- ``Maps::GetBiomeType`` renamed to ``Maps::getBiomeType`` for consistency +- ``Maps::GetBiomeTypeRef`` renamed to ``Maps::getBiomeTypeRef`` for consistency ## Lua - `helpdb`: new function: ``helpdb.refresh()`` to force a refresh of the database. Call if you are a developer adding new scripts, loading new plugins, or changing help text during play @@ -181,6 +187,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: -@ ``gui.ZScreen``: new attribute: ``defocusable`` for controlling whether a window loses keyboard focus when the map is clicked - ``widgets.Label``: token ``tile`` properties can now be either pens or numeric texture ids - `tiletypes`: now has a Lua API! ``tiletypes_setTile`` +- ``maps.getBiomeType``: exposed preexisting function to Lua ## Removed - `autohauler`: no plans to port to v50, as it just doesn't make sense with the new work detail system diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 9125ca902..304ce6651 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1594,16 +1594,16 @@ Military Module API * ``dfhack.military.makeSquad(assignment_id)`` - Creates a new squad associated with the assignment. Fails if one already exists - Note: This function does not name the squad, but they are otherwise complete + Creates a new squad associated with the assignment. Fails if one already exists. + Note: This function does not name the squad, but they are otherwise complete. * ``dfhack.military.updateRoomAssignments(squad_id, assignment_id, squad_use_flags)`` - Sets the sleep, train, indiv_eq, and squad_eq flags when training at a barracks + Sets the sleep, train, indiv_eq, and squad_eq flags when training at a barracks. -* ``dfhack.military.getSquadName(squad)`` +* ``dfhack.military.getSquadName(squad_id)`` - Returns the name of a squad + Returns the name of a squad/ Action Timer API ~~~~~~~~~~~~~~~~ diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 13ea1a7fe..c9bdc3021 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1940,7 +1940,7 @@ static const luaL_Reg dfhack_units_funcs[] = { /***** Military Module *****/ static const LuaWrapper::FunctionReg dfhack_military_module[] = { - WRAPM(Military, makeSquad), + WRAPM(Military, makeSquad), WRAPM(Military, updateRoomAssignments), WRAPM(Military, getSquadName), { NULL, NULL } diff --git a/library/include/modules/Military.h b/library/include/modules/Military.h index 19ed47ee2..3a9710687 100644 --- a/library/include/modules/Military.h +++ b/library/include/modules/Military.h @@ -10,10 +10,10 @@ namespace DFHack { namespace Military { - -DFHACK_EXPORT std::string getSquadName(df::unit *unit); + +DFHACK_EXPORT std::string getSquadName(int32_t squad_id); DFHACK_EXPORT df::squad* makeSquad(int32_t assignment_id); DFHACK_EXPORT void updateRoomAssignments(int32_t squad_id, int32_t civzone_id, df::squad_use_flags flags); } -} \ No newline at end of file +} diff --git a/library/modules/Military.cpp b/library/modules/Military.cpp index b67b3a129..1dd71f11d 100644 --- a/library/modules/Military.cpp +++ b/library/modules/Military.cpp @@ -22,12 +22,9 @@ using namespace df::enums; using df::global::world; using df::global::plotinfo; -std::string Military::getSquadName(df::unit *unit) +std::string Military::getSquadName(int32_t squad_id) { - CHECK_NULL_POINTER(unit); - if (unit->military.squad_id == -1) - return ""; - df::squad *squad = df::squad::find(unit->military.squad_id); + df::squad *squad = df::squad::find(squad_id); if (!squad) return ""; if (squad->alias.size() > 0) @@ -304,4 +301,4 @@ void Military::updateRoomAssignments(int32_t squad_id, int32_t civzone_id, df::s } } } -} \ No newline at end of file +} diff --git a/plugins/manipulator.cpp b/plugins/manipulator.cpp index 6731aa513..b8f6ce706 100644 --- a/plugins/manipulator.cpp +++ b/plugins/manipulator.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -1305,7 +1306,7 @@ void viewscreen_unitlaborsst::refreshNames() cur->job_mode = UnitInfo::JOB; } if (unit->military.squad_id > -1) { - cur->squad_effective_name = Units::getSquadName(unit); + cur->squad_effective_name = Military::getSquadName(unit->military.squad_id); cur->squad_info = stl_sprintf("%i", unit->military.squad_position + 1) + "." + cur->squad_effective_name; } else { cur->squad_effective_name = ""; From 2bd48f1f90ec2ac5d8b07dd7fd3c216041c11f10 Mon Sep 17 00:00:00 2001 From: 20k Date: Tue, 31 Jan 2023 05:31:58 +0000 Subject: [PATCH 04/13] address some review comments --- docs/dev/Lua API.rst | 4 ++-- library/include/modules/Military.h | 1 - library/modules/Units.cpp | 8 -------- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 304ce6651..ec7ff57df 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1589,7 +1589,7 @@ Units module Returns a table of the cutoffs used by the above stress level functions. -Military Module API +Military module ~~~~~~~~~~~~~~~~~~~ * ``dfhack.military.makeSquad(assignment_id)`` @@ -1603,7 +1603,7 @@ Military Module API * ``dfhack.military.getSquadName(squad_id)`` - Returns the name of a squad/ + Returns the name of a squad. Action Timer API ~~~~~~~~~~~~~~~~ diff --git a/library/include/modules/Military.h b/library/include/modules/Military.h index 3a9710687..8ceb987b5 100644 --- a/library/include/modules/Military.h +++ b/library/include/modules/Military.h @@ -4,7 +4,6 @@ #include "DataDefs.h" #include "df/squad.h" -#include "df/unit.h" namespace DFHack { diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 137a07da6..a636310e1 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -33,7 +33,6 @@ distribution. #include #include #include -#include using namespace std; #include "VersionInfo.h" @@ -52,7 +51,6 @@ using namespace std; #include "MiscUtils.h" #include "df/activity_entry.h" -#include "df/building_civzonest.h" #include "df/burrow.h" #include "df/caste_raw.h" #include "df/creature_raw.h" @@ -63,7 +61,6 @@ using namespace std; #include "df/entity_raw_flags.h" #include "df/identity_type.h" #include "df/game_mode.h" -#include "df/global_objects.h" #include "df/histfig_entity_link_positionst.h" #include "df/histfig_relationship_type.h" #include "df/historical_entity.h" @@ -74,11 +71,6 @@ using namespace std; #include "df/identity.h" #include "df/job.h" #include "df/nemesis_record.h" -#include "df/squad.h" -#include "df/squad_position.h" -#include "df/squad_schedule_order.h" -#include "df/squad_order.h" -#include "df/squad_order_trainst.h" #include "df/tile_occupancy.h" #include "df/plotinfost.h" #include "df/unit_inventory_item.h" From 1eeefdd598007d24940ba95fd2b3febdb33710c4 Mon Sep 17 00:00:00 2001 From: 20k Date: Mon, 20 Feb 2023 07:45:08 +0000 Subject: [PATCH 05/13] clean up a variety of unks --- library/modules/Military.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/library/modules/Military.cpp b/library/modules/Military.cpp index 1dd71f11d..a0d3cea2a 100644 --- a/library/modules/Military.cpp +++ b/library/modules/Military.cpp @@ -92,18 +92,15 @@ df::squad* Military::makeSquad(int32_t assignment_id) result->entity_id = df::global::plotinfo->group_id; result->leader_position = corresponding_position->id; result->leader_assignment = found_assignment->id; - result->unk_1 = -1; result->name = name; - result->ammo.unk_v50_1 = 0; + result->ammo.update = 0; int16_t squad_size = corresponding_position->squad_size; for (int i=0; i < squad_size; i++) { //construct for squad_position seems to set all the attributes correctly - //except I've observed unk_2 is -1 generally df::squad_position* pos = new df::squad_position(); - pos->unk_2 = -1; pos->flags.whole = 0; result->positions.push_back(pos); @@ -137,12 +134,9 @@ df::squad* Military::makeSquad(int32_t assignment_id) df::squad_order* s_order = df::allocate(); - s_order->unk_v40_1 = -1; - s_order->unk_v40_2 = -1; s_order->year = *df::global::cur_year; s_order->year_tick = *df::global::cur_year_tick; s_order->unk_v40_3 = -1; - s_order->unk_1 = 0; order->order = s_order; From 19616f7e32fe6329c5ea363d07cd6faac401bb01 Mon Sep 17 00:00:00 2001 From: 20k Date: Mon, 20 Feb 2023 07:48:41 +0000 Subject: [PATCH 06/13] fix changelog issues # Conflicts: # docs/changelog.txt --- docs/changelog.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 2da870ca7..6f24ee642 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -79,6 +79,12 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API - ``Gui::any_civzone_hotkey``, ``Gui::getAnyCivZone``, ``Gui::getSelectedCivZone``: new functions to operate on the new zone system - Units module: added new predicates for ``isGeldable()``, ``isMarkedForGelding()``, and ``isPet()`` +- `Military`: New module for military functionality +- `Military`: new ``makeSquad`` to create a squad +- `Military`: changed ``getSquadName`` to take a squad identifier +- `Military`: new ``updateRoomAssignments`` for assigning a squad to a barracks and archery range- ``Maps::GetBiomeType`` renamed to ``Maps::getBiomeType`` for consistency +- ``Maps::GetBiomeType`` renamed to ``Maps::getBiomeType`` for consistency +- ``Maps::GetBiomeTypeRef`` renamed to ``Maps::getBiomeTypeRef`` for consistency ## Lua - ``dfhack.gui.getSelectedCivZone``: returns the Zone that the user has selected currently @@ -171,12 +177,6 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``Screen::Pen``: now accepts ``top_of_text`` and ``bottom_of_text`` properties to support offset text in graphics mode - `overlay`: overlay widgets can now specify a default enabled state if they are not already set in the player's overlay config file - ``Lua::Push``: now supports ``std::unordered_map`` -- `Military`: New module for military functionality -- `Military`: new ``makeSquad`` to create a squad -- `Military`: changed ``getSquadName`` to take a squad identifier -- `Military`: new ``updateRoomAssignments`` for assigning a squad to a barracks and archery range -- ``Maps::GetBiomeType`` renamed to ``Maps::getBiomeType`` for consistency -- ``Maps::GetBiomeTypeRef`` renamed to ``Maps::getBiomeTypeRef`` for consistency ## Lua - `helpdb`: new function: ``helpdb.refresh()`` to force a refresh of the database. Call if you are a developer adding new scripts, loading new plugins, or changing help text during play From 837f32fdee04bb50fef7b2d2b338b7bba4c855ad Mon Sep 17 00:00:00 2001 From: 20k Date: Mon, 20 Feb 2023 07:51:16 +0000 Subject: [PATCH 07/13] more changelog fixes --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 6f24ee642..4753371e0 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -90,6 +90,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``dfhack.gui.getSelectedCivZone``: returns the Zone that the user has selected currently - ``widgets.FilteredList``: Added ``edit_on_change`` optional parameter to allow a custom callback on filter edit change. - ``widgets.TabBar``: new library widget (migrated from control-panel.lua) +- ``maps.getBiomeType``: exposed preexisting function to Lua # 50.07-alpha1 @@ -187,7 +188,6 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: -@ ``gui.ZScreen``: new attribute: ``defocusable`` for controlling whether a window loses keyboard focus when the map is clicked - ``widgets.Label``: token ``tile`` properties can now be either pens or numeric texture ids - `tiletypes`: now has a Lua API! ``tiletypes_setTile`` -- ``maps.getBiomeType``: exposed preexisting function to Lua ## Removed - `autohauler`: no plans to port to v50, as it just doesn't make sense with the new work detail system From e50f3dbb64cb6c9b18611b7566e937159fbc2f86 Mon Sep 17 00:00:00 2001 From: 20k Date: Mon, 20 Feb 2023 18:02:47 +0000 Subject: [PATCH 08/13] remove unnecessary init --- library/modules/Military.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/library/modules/Military.cpp b/library/modules/Military.cpp index a0d3cea2a..e30ea0ae4 100644 --- a/library/modules/Military.cpp +++ b/library/modules/Military.cpp @@ -101,7 +101,6 @@ df::squad* Military::makeSquad(int32_t assignment_id) { //construct for squad_position seems to set all the attributes correctly df::squad_position* pos = new df::squad_position(); - pos->flags.whole = 0; result->positions.push_back(pos); } From 0c9a9c8b9e4fa2bae92fafdb97964ab687d278f7 Mon Sep 17 00:00:00 2001 From: 20k Date: Tue, 21 Feb 2023 20:27:32 +0000 Subject: [PATCH 09/13] cleanup remaining unk --- library/modules/Military.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/library/modules/Military.cpp b/library/modules/Military.cpp index e30ea0ae4..9a1fed614 100644 --- a/library/modules/Military.cpp +++ b/library/modules/Military.cpp @@ -135,7 +135,6 @@ df::squad* Military::makeSquad(int32_t assignment_id) s_order->year = *df::global::cur_year; s_order->year_tick = *df::global::cur_year_tick; - s_order->unk_v40_3 = -1; order->order = s_order; From 3c1d3ce21c57f8fa3bbd06f90fb6ee09f30b86e1 Mon Sep 17 00:00:00 2001 From: 20k Date: Mon, 27 Feb 2023 01:45:10 +0000 Subject: [PATCH 10/13] rework docs, comments, clean up unnecessary init --- docs/changelog.txt | 2 +- docs/dev/Lua API.rst | 8 +++++--- library/modules/Military.cpp | 13 +++---------- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 4753371e0..412ee07a0 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -82,7 +82,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `Military`: New module for military functionality - `Military`: new ``makeSquad`` to create a squad - `Military`: changed ``getSquadName`` to take a squad identifier -- `Military`: new ``updateRoomAssignments`` for assigning a squad to a barracks and archery range- ``Maps::GetBiomeType`` renamed to ``Maps::getBiomeType`` for consistency +- `Military`: new ``updateRoomAssignments`` for assigning a squad to a barracks and archery range - ``Maps::GetBiomeType`` renamed to ``Maps::getBiomeType`` for consistency - ``Maps::GetBiomeTypeRef`` renamed to ``Maps::getBiomeTypeRef`` for consistency diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index ec7ff57df..54ac4d48e 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1594,8 +1594,10 @@ Military module * ``dfhack.military.makeSquad(assignment_id)`` - Creates a new squad associated with the assignment. Fails if one already exists. - Note: This function does not name the squad, but they are otherwise complete. + Creates a new squad associated with the assignment (ie df::entity_position_assignment, via `id``) and returns it. + Fails if a squad already exists that is associated with that assignment, or if the assignment is not a fort mode player controlled squad. + Note: This function does not name the squad: consider setting a nickname (under result.name.nickname), and/or filling out the language_name object at result.name. + The returned squad is otherwise complete and requires no more setup to work correctly. * ``dfhack.military.updateRoomAssignments(squad_id, assignment_id, squad_use_flags)`` @@ -1603,7 +1605,7 @@ Military module * ``dfhack.military.getSquadName(squad_id)`` - Returns the name of a squad. + Returns the name of a squad as a string. Action Timer API ~~~~~~~~~~~~~~~~ diff --git a/library/modules/Military.cpp b/library/modules/Military.cpp index 9a1fed614..07925e8e4 100644 --- a/library/modules/Military.cpp +++ b/library/modules/Military.cpp @@ -84,16 +84,13 @@ df::squad* Military::makeSquad(int32_t assignment_id) df::squad* result = new df::squad(); result->id = *df::global::squad_next_id; - result->cur_routine_idx = 0; result->uniform_priority = result->id + 1; //no idea why, but seems to hold - result->activity = -1; //?? result->carry_food = 2; result->carry_water = 1; result->entity_id = df::global::plotinfo->group_id; result->leader_position = corresponding_position->id; result->leader_assignment = found_assignment->id; result->name = name; - result->ammo.update = 0; int16_t squad_size = corresponding_position->squad_size; @@ -143,7 +140,7 @@ df::squad* Military::makeSquad(int32_t assignment_id) asched[month].uniform_mode = 0; }; - //I thought this was a terrible hack, but its literally how dwarf fortress does it 1:1 + //Dwarf fortress does do this via a series of string comparisons //Off duty: No orders, Sleep/room at will. Equip/orders only if (routine->name == "Off duty") { @@ -153,8 +150,7 @@ df::squad* Military::makeSquad(int32_t assignment_id) asched[i].uniform_mode = 1; } } - //Staggered Training: Training orders at 3 4 5, 9 10 11, sleep/room at will. Equip/orders only, except train months which are equip/always - //always seen the training indices 0 1 2 6 7 8, so its unclear. Check if squad id matters + //Staggered Training: Training orders at months 3 4 5 9 10 11, *or* 0 1 2 6 7 8, sleep/room at will. Equip/orders only, except train months which are equip/always else if (routine->name == "Staggered training") { //this is semi randomised for different squads @@ -209,15 +205,12 @@ df::squad* Military::makeSquad(int32_t assignment_id) result->schedule.push_back(reinterpret_cast(asched)); } - //all we've done so far is leak memory if anything goes wrong - //modify state + //Modify necessary world state (*df::global::squad_next_id)++; fort->squads.push_back(result->id); df::global::world->squads.all.push_back(result); found_assignment->squad_id = result->id; - //todo: find and modify old squad - return result; } From c0bd452c86577aa49d159dba8858078d2179f23a Mon Sep 17 00:00:00 2001 From: 20k Date: Mon, 27 Feb 2023 02:06:36 +0000 Subject: [PATCH 11/13] add a failure case check just in case --- library/modules/Military.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/modules/Military.cpp b/library/modules/Military.cpp index 07925e8e4..3b7ce7bec 100644 --- a/library/modules/Military.cpp +++ b/library/modules/Military.cpp @@ -50,6 +50,9 @@ df::squad* Military::makeSquad(int32_t assignment_id) df::historical_entity* fort = df::historical_entity::find(df::global::plotinfo->group_id); + if (fort == nullptr) + return nullptr; + df::entity_position_assignment* found_assignment = nullptr; for (auto* assignment : fort->positions.assignments) From c38a288eee9775706decba2b0f69b43c4550d4c3 Mon Sep 17 00:00:00 2001 From: 20k Date: Mon, 27 Feb 2023 02:15:26 +0000 Subject: [PATCH 12/13] use insert_into_vector, tweak docs again --- docs/dev/Lua API.rst | 4 ++-- library/modules/Military.cpp | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 54ac4d48e..63803a66a 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1594,9 +1594,9 @@ Military module * ``dfhack.military.makeSquad(assignment_id)`` - Creates a new squad associated with the assignment (ie df::entity_position_assignment, via `id``) and returns it. + Creates a new squad associated with the assignment (ie ``df::entity_position_assignment``, via ``id``) and returns it. Fails if a squad already exists that is associated with that assignment, or if the assignment is not a fort mode player controlled squad. - Note: This function does not name the squad: consider setting a nickname (under result.name.nickname), and/or filling out the language_name object at result.name. + Note: This function does not name the squad: consider setting a nickname (under ``squad.name.nickname``), and/or filling out the ``language_name`` object at ``squad.name``. The returned squad is otherwise complete and requires no more setup to work correctly. * ``dfhack.military.updateRoomAssignments(squad_id, assignment_id, squad_use_flags)`` diff --git a/library/modules/Military.cpp b/library/modules/Military.cpp index 3b7ce7bec..b402cc0fa 100644 --- a/library/modules/Military.cpp +++ b/library/modules/Military.cpp @@ -1,6 +1,7 @@ #include #include #include +#include "MiscUtils.h" #include "modules/Military.h" #include "modules/Translation.h" #include "df/building.h" @@ -258,18 +259,16 @@ void Military::updateRoomAssignments(int32_t squad_id, int32_t civzone_id, df::s { room_from_squad = new df::squad::T_rooms(); room_from_squad->building_id = civzone_id; - squad->rooms.push_back(room_from_squad); - std::sort(squad->rooms.begin(), squad->rooms.end(), [](df::squad::T_rooms* a, df::squad::T_rooms* b){return a->building_id < b->building_id;}); + insert_into_vector(squad->rooms, &df::squad::T_rooms::building_id, room_from_squad); } if (room_from_building == nullptr) { room_from_building = new df::building_civzonest::T_squad_room_info(); room_from_building->squad_id = squad_id; - zone->squad_room_info.push_back(room_from_building); - std::sort(zone->squad_room_info.begin(), zone->squad_room_info.end(), [](df::building_civzonest::T_squad_room_info* a, df::building_civzonest::T_squad_room_info* b){return a->squad_id < b->squad_id;}); + insert_into_vector(zone->squad_room_info, &df::building_civzonest::T_squad_room_info::squad_id, room_from_building); } if (room_from_squad) From d2da06acc637bc95c3031efaaa0d1d9c250f74c7 Mon Sep 17 00:00:00 2001 From: Myk Date: Sun, 26 Feb 2023 21:37:02 -0800 Subject: [PATCH 13/13] Update docs/changelog.txt --- docs/changelog.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 412ee07a0..0d29f5f3c 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -79,10 +79,10 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API - ``Gui::any_civzone_hotkey``, ``Gui::getAnyCivZone``, ``Gui::getSelectedCivZone``: new functions to operate on the new zone system - Units module: added new predicates for ``isGeldable()``, ``isMarkedForGelding()``, and ``isPet()`` -- `Military`: New module for military functionality -- `Military`: new ``makeSquad`` to create a squad -- `Military`: changed ``getSquadName`` to take a squad identifier -- `Military`: new ``updateRoomAssignments`` for assigning a squad to a barracks and archery range +- ``Military``: New module for military functionality +- ``Military``: new ``makeSquad`` to create a squad +- ``Military``: changed ``getSquadName`` to take a squad identifier +- ``Military``: new ``updateRoomAssignments`` for assigning a squad to a barracks and archery range - ``Maps::GetBiomeType`` renamed to ``Maps::getBiomeType`` for consistency - ``Maps::GetBiomeTypeRef`` renamed to ``Maps::getBiomeTypeRef`` for consistency