diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 6ebe3ad6d..230194b46 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -158,7 +158,7 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(tiletypes tiletypes.cpp Brushes.h) # DFHACK_PLUGIN(treefarm treefarm.cpp) DFHACK_PLUGIN(tubefill tubefill.cpp) - DFHACK_PLUGIN(tweak tweak.cpp) + add_subdirectory(tweak) DFHACK_PLUGIN(weather weather.cpp) DFHACK_PLUGIN(workflow workflow.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(workNow workNow.cpp) diff --git a/plugins/tweak/CMakeLists.txt b/plugins/tweak/CMakeLists.txt new file mode 100644 index 000000000..77b0d7ae1 --- /dev/null +++ b/plugins/tweak/CMakeLists.txt @@ -0,0 +1,6 @@ +PROJECT (tweak) +SET(PROJECT_SRCS + tweak.cpp +) + +DFHACK_PLUGIN(tweak ${PROJECT_SRCS}) diff --git a/plugins/tweak.cpp b/plugins/tweak/tweak.cpp similarity index 53% rename from plugins/tweak.cpp rename to plugins/tweak/tweak.cpp index 1e114fc43..2cdec0668 100644 --- a/plugins/tweak.cpp +++ b/plugins/tweak/tweak.cpp @@ -68,6 +68,16 @@ //#include "df/building_hivest.h" #include +#include + +#include "tweaks/adamantine-cloth-wear.h" +#include "tweaks/advmode-contained.h" +#include "tweaks/craft-age-wear.h" +#include "tweaks/fast-heat.h" +#include "tweaks/fast-trade.h" +#include "tweaks/manager-quantity.h" +#include "tweaks/military-assign.h" +#include "tweaks/stable-cursor.h" using std::set; using std::vector; @@ -83,12 +93,15 @@ using df::global::ui_menu_width; using df::global::ui_area_map_width; using namespace DFHack::Gui; -using Screen::Pen; static command_result tweak(color_ostream &out, vector & parameters); +static std::multimap tweak_hooks; DFHACK_PLUGIN("tweak"); +#define TWEAK_HOOK(tweak, cls, func) tweak_hooks.insert(std::pair\ + (tweak, INTERPOSE_HOOK(cls, func))) + DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand( @@ -115,14 +128,6 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector \n" " Further improves temperature updates by ensuring that 1 degree of\n" " item temperature is crossed in no more than specified number of frames\n" @@ -146,11 +151,39 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector main.mode) { - case ui_sidebar_mode::Default: - return true; - - case ui_sidebar_mode::Build: - return ui_build_selector && - (ui_build_selector->building_type < 0 || - ui_build_selector->stage < 1); - - default: - return false; - } - } - - DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) - { - bool was_default = check_default(); - df::coord view = Gui::getViewportPos(); - df::coord cursor = Gui::getCursorPos(); - - INTERPOSE_NEXT(feed)(input); - - bool is_default = check_default(); - df::coord cur_cursor = Gui::getCursorPos(); - - if (is_default && !was_default) - { - last_view = view; last_cursor = cursor; - } - else if (!is_default && was_default && - Gui::getViewportPos() == last_view && - last_cursor.isValid() && cur_cursor.isValid()) - { - Gui::setCursorCoords(last_cursor.x, last_cursor.y, last_cursor.z); - - // Force update of ui state - set tmp; - if (last_cursor.z < 2) - tmp.insert(interface_key::CURSOR_UP_Z); - else - tmp.insert(interface_key::CURSOR_DOWN_Z); - INTERPOSE_NEXT(feed)(&tmp); - tmp.clear(); - if (last_cursor.z < 2) - tmp.insert(interface_key::CURSOR_DOWN_Z); - else - tmp.insert(interface_key::CURSOR_UP_Z); - INTERPOSE_NEXT(feed)(&tmp); - } - else if (!is_default && cur_cursor.isValid()) - { - last_cursor = df::coord(); - } - } -}; - -IMPLEMENT_VMETHOD_INTERPOSE(stable_cursor_hook, feed); - -struct patrol_duty_hook : df::squad_order_trainst -{ - typedef df::squad_order_trainst interpose_base; - - DEFINE_VMETHOD_INTERPOSE(bool, isPatrol, ()) - { - return false; - } -}; - -IMPLEMENT_VMETHOD_INTERPOSE(patrol_duty_hook, isPatrol); - -enum confirm_embark_states -{ - ECS_INACTIVE = 0, - ECS_CONFIRM, - ECS_ACCEPTED -}; -static confirm_embark_states confirm_embark_state = ECS_INACTIVE; - -struct confirm_embark_hook : df::viewscreen_setupdwarfgamest -{ - typedef df::viewscreen_setupdwarfgamest interpose_base; - - void OutputString(int8_t fg, int &x, int y, std::string text) - { - Screen::paintString(Screen::Pen(' ', fg, COLOR_BLACK), x, y, text); - x += text.length(); - } - - DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) - { - bool intercept = false; - if (this->show_play_now == 0) - { - if (confirm_embark_state == ECS_INACTIVE) - { - if (input->count(df::interface_key::SETUP_EMBARK)) - { - confirm_embark_state = ECS_CONFIRM; - intercept = true; - } - } - else if (confirm_embark_state == ECS_CONFIRM) - { - intercept = true; - if (input->count(df::interface_key::MENU_CONFIRM)) - confirm_embark_state = ECS_ACCEPTED; - else if (input->size()) - confirm_embark_state = ECS_INACTIVE; - } - } - - if (!intercept) - INTERPOSE_NEXT(feed)(input); - } - - DEFINE_VMETHOD_INTERPOSE(bool, key_conflict, (df::interface_key key)) - { - if (confirm_embark_state == ECS_CONFIRM) - { - if (key == df::interface_key::OPTIONS) - return true; - } - return INTERPOSE_NEXT(key_conflict)(key); - } - - DEFINE_VMETHOD_INTERPOSE(void, render, ()) - { - INTERPOSE_NEXT(render)(); - auto dim = Screen::getWindowSize(); - int x = 0, y = 0; - if (confirm_embark_state != ECS_INACTIVE) - { - Screen::fillRect(Screen::Pen(' ', COLOR_BLACK, COLOR_BLACK), 0, 0, dim.x - 1, dim.y - 1); - } - if (confirm_embark_state == ECS_CONFIRM) - { - x = 2, y = 2; - OutputString(COLOR_WHITE, x, y, "Really embark? ("); - OutputString(COLOR_LIGHTGREEN, x, y, Screen::getKeyDisplay(df::interface_key::MENU_CONFIRM)); - OutputString(COLOR_WHITE, x, y, " = yes, other = no)"); - x = 2, y = 4; - int32_t points = this->points_remaining; - OutputString(COLOR_WHITE, x, y, "Points left: "); - OutputString((points ? COLOR_YELLOW : COLOR_LIGHTGREEN), x, y, std::to_string((unsigned long long/*won't compile on windows otherwise*/)points)); - x = dim.x - 10, y = dim.y - 1; - OutputString(COLOR_WHITE, x, y, "DFHack"); - } - else if (confirm_embark_state == ECS_ACCEPTED) - { - std::set input; - input.insert(df::interface_key::SETUP_EMBARK); - this->feed(&input); - confirm_embark_state = ECS_INACTIVE; - } - } -}; - -IMPLEMENT_VMETHOD_INTERPOSE(confirm_embark_hook, feed); -IMPLEMENT_VMETHOD_INTERPOSE(confirm_embark_hook, key_conflict); -IMPLEMENT_VMETHOD_INTERPOSE(confirm_embark_hook, render); - -struct stable_temp_hook : df::item_actual { - typedef df::item_actual interpose_base; - - DEFINE_VMETHOD_INTERPOSE(bool, adjustTemperature, (uint16_t temp, int32_t rate_mult)) - { - if (temperature.whole != temp) - { - // Bug 6012 is caused by fixed-point precision mismatch jitter - // when an item is being pushed by two sources at N and N+1. - // This check suppresses it altogether. - if (temp == temperature.whole+1 || - (temp == temperature.whole-1 && temperature.fraction == 0)) - temp = temperature.whole; - // When SPEC_HEAT is NONE, the original function seems to not - // change the temperature, yet return true, which is silly. - else if (getSpecHeat() == 60001) - temp = temperature.whole; - } - - return INTERPOSE_NEXT(adjustTemperature)(temp, rate_mult); - } - - DEFINE_VMETHOD_INTERPOSE(bool, updateContaminants, ()) - { - if (contaminants) - { - // Force 1-degree difference in contaminant temperature to 0 - for (size_t i = 0; i < contaminants->size(); i++) - { - auto obj = (*contaminants)[i]; - - if (abs(obj->temperature.whole - temperature.whole) == 1) - { - obj->temperature.whole = temperature.whole; - obj->temperature.fraction = temperature.fraction; - } - } - } - - return INTERPOSE_NEXT(updateContaminants)(); - } -}; - -IMPLEMENT_VMETHOD_INTERPOSE(stable_temp_hook, adjustTemperature); -IMPLEMENT_VMETHOD_INTERPOSE(stable_temp_hook, updateContaminants); - -static int map_temp_mult = -1; -static int max_heat_ticks = 0; - -struct fast_heat_hook : df::item_actual { - typedef df::item_actual interpose_base; - - DEFINE_VMETHOD_INTERPOSE( - bool, updateTempFromMap, - (bool local, bool contained, bool adjust, int32_t rate_mult) - ) { - int cmult = map_temp_mult; - map_temp_mult = rate_mult; - - bool rv = INTERPOSE_NEXT(updateTempFromMap)(local, contained, adjust, rate_mult); - map_temp_mult = cmult; - return rv; - } - - DEFINE_VMETHOD_INTERPOSE( - bool, updateTemperature, - (uint16_t temp, bool local, bool contained, bool adjust, int32_t rate_mult) - ) { - // Some items take ages to cross the last degree, so speed them up - if (map_temp_mult > 0 && temp != temperature.whole && max_heat_ticks > 0) - { - int spec = getSpecHeat(); - if (spec != 60001) - rate_mult = std::max(map_temp_mult, spec/max_heat_ticks/abs(temp - temperature.whole)); - } - - return INTERPOSE_NEXT(updateTemperature)(temp, local, contained, adjust, rate_mult); - } - - DEFINE_VMETHOD_INTERPOSE(bool, adjustTemperature, (uint16_t temp, int32_t rate_mult)) - { - if (map_temp_mult > 0) - rate_mult = map_temp_mult; - - return INTERPOSE_NEXT(adjustTemperature)(temp, rate_mult); - } -}; - -IMPLEMENT_VMETHOD_INTERPOSE(fast_heat_hook, updateTempFromMap); -IMPLEMENT_VMETHOD_INTERPOSE(fast_heat_hook, updateTemperature); -IMPLEMENT_VMETHOD_INTERPOSE(fast_heat_hook, adjustTemperature); - static void correct_dimension(df::item_actual *self, int32_t &delta, int32_t dim) { // Zero dimension or remainder? @@ -570,201 +338,6 @@ struct dimension_cloth_hook : df::item_clothst { IMPLEMENT_VMETHOD_INTERPOSE(dimension_cloth_hook, subtractDimension); -struct advmode_contained_hook : df::viewscreen_layer_unit_actionst { - typedef df::viewscreen_layer_unit_actionst interpose_base; - - DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) - { - auto old_reaction = cur_reaction; - auto old_reagent = reagent; - - INTERPOSE_NEXT(feed)(input); - - if (cur_reaction && (cur_reaction != old_reaction || reagent != old_reagent)) - { - old_reagent = reagent; - - // Skip reagents already contained by others - while (reagent < (int)cur_reaction->reagents.size()-1) - { - if (!cur_reaction->reagents[reagent]->flags.bits.IN_CONTAINER) - break; - reagent++; - } - - if (old_reagent != reagent) - { - // Reproduces a tiny part of the orginal screen code - choice_items.clear(); - - auto preagent = cur_reaction->reagents[reagent]; - reagent_amnt_left = preagent->quantity; - - for (int i = held_items.size()-1; i >= 0; i--) - { - if (!preagent->matchesRoot(held_items[i], cur_reaction->index)) - continue; - if (linear_index(sel_items, held_items[i]) >= 0) - continue; - choice_items.push_back(held_items[i]); - } - - layer_objects[6]->setListLength(choice_items.size()); - - if (!choice_items.empty()) - { - layer_objects[4]->active = layer_objects[5]->active = false; - layer_objects[6]->active = true; - } - else if (layer_objects[6]->active) - { - layer_objects[6]->active = false; - layer_objects[5]->active = true; - } - } - } - } -}; - -IMPLEMENT_VMETHOD_INTERPOSE(advmode_contained_hook, feed); - -struct fast_trade_assign_hook : df::viewscreen_layer_assigntradest { - typedef df::viewscreen_layer_assigntradest interpose_base; - - DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) - { - if (layer_objects[1]->active && input->count(interface_key::SELECT_ALL)) - { - set tmp; tmp.insert(interface_key::SELECT); - INTERPOSE_NEXT(feed)(&tmp); - tmp.clear(); tmp.insert(interface_key::STANDARDSCROLL_DOWN); - INTERPOSE_NEXT(feed)(&tmp); - } - else - INTERPOSE_NEXT(feed)(input); - } -}; - -IMPLEMENT_VMETHOD_INTERPOSE(fast_trade_assign_hook, feed); - -struct fast_trade_select_hook : df::viewscreen_tradegoodsst { - typedef df::viewscreen_tradegoodsst interpose_base; - - DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) - { - if (!(is_unloading || !has_traders || in_edit_count) - && input->count(interface_key::SELECT_ALL)) - { - set tmp; tmp.insert(interface_key::SELECT); - INTERPOSE_NEXT(feed)(&tmp); - if (in_edit_count) - INTERPOSE_NEXT(feed)(&tmp); - tmp.clear(); tmp.insert(interface_key::STANDARDSCROLL_DOWN); - INTERPOSE_NEXT(feed)(&tmp); - } - else - INTERPOSE_NEXT(feed)(input); - } -}; - -IMPLEMENT_VMETHOD_INTERPOSE(fast_trade_select_hook, feed); - -struct military_assign_hook : df::viewscreen_layer_militaryst { - typedef df::viewscreen_layer_militaryst interpose_base; - - inline bool inPositionsMode() { - return page == Positions && !(in_create_squad || in_new_squad); - } - - DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) - { - if (inPositionsMode() && !layer_objects[0]->active) - { - auto pos_list = layer_objects[1]; - auto plist = layer_objects[2]; - auto &cand = positions.candidates; - - // Save the candidate list and cursors - std::vector copy = cand; - int cursor = plist->getListCursor(); - int pos_cursor = pos_list->getListCursor(); - - INTERPOSE_NEXT(feed)(input); - - if (inPositionsMode() && !layer_objects[0]->active) - { - bool is_select = input->count(interface_key::SELECT); - - // Resort the candidate list and restore cursor - // on add to squad OR scroll in the position list. - if (!plist->active || is_select) - { - // Since we don't know the actual sorting order, preserve - // the ordering of the items in the list before keypress. - // This does the right thing even if the list was sorted - // with sort-units. - std::set prev, next; - prev.insert(copy.begin(), copy.end()); - next.insert(cand.begin(), cand.end()); - std::vector out; - - // (old-before-cursor) (new) |cursor| (old-after-cursor) - for (int i = 0; i < cursor && i < (int)copy.size(); i++) - if (next.count(copy[i])) out.push_back(copy[i]); - for (size_t i = 0; i < cand.size(); i++) - if (!prev.count(cand[i])) out.push_back(cand[i]); - int new_cursor = out.size(); - for (int i = cursor; i < (int)copy.size(); i++) - if (next.count(copy[i])) out.push_back(copy[i]); - - cand.swap(out); - plist->setListLength(cand.size()); - if (new_cursor < (int)cand.size()) - plist->setListCursor(new_cursor); - } - - // Preserve the position list index on remove from squad - if (pos_list->active && is_select) - pos_list->setListCursor(pos_cursor); - } - } - else - INTERPOSE_NEXT(feed)(input); - } - - DEFINE_VMETHOD_INTERPOSE(void, render, ()) - { - INTERPOSE_NEXT(render)(); - - if (inPositionsMode()) - { - auto plist = layer_objects[2]; - int x1 = plist->getX1(), y1 = plist->getY1(); - int x2 = plist->getX2(), y2 = plist->getY2(); - int i1 = plist->getFirstVisible(), i2 = plist->getLastVisible(); - int si = plist->getListCursor(); - - for (int y = y1, i = i1; i <= i2; i++, y++) - { - auto unit = vector_get(positions.candidates, i); - if (!unit || unit->military.squad_id < 0) - continue; - - for (int x = x1; x <= x2; x++) - { - Pen cur_tile = Screen::readTile(x, y); - if (!cur_tile.valid()) continue; - cur_tile.fg = (i == si) ? COLOR_BROWN : COLOR_GREEN; - Screen::paintTile(cur_tile, x, y); - } - } - } - } -}; - -IMPLEMENT_VMETHOD_INTERPOSE(military_assign_hook, feed); -IMPLEMENT_VMETHOD_INTERPOSE(military_assign_hook, render); - // Unit updates are executed based on an action divisor variable, // which is computed from the alive unit count and has range 10-100. static int adjust_unit_divisor(int value) { @@ -1007,104 +580,6 @@ struct military_training_id_hook : df::activity_event_individual_skill_drillst { IMPLEMENT_VMETHOD_INTERPOSE(military_training_id_hook, process); */ -struct craft_age_wear_hook : df::item_crafted { - typedef df::item_crafted interpose_base; - - DEFINE_VMETHOD_INTERPOSE(bool, ageItem, (int amount)) - { - int orig_age = age; - age += amount; - if (age > 200000000) - age = 200000000; - if (age == orig_age) - return false; - - MaterialInfo mat(mat_type, mat_index); - if (!mat.isValid()) - return false; - int wear = 0; - - if (mat.material->flags.is_set(material_flags::WOOD)) - wear = 5; - else if (mat.material->flags.is_set(material_flags::LEATHER) || - mat.material->flags.is_set(material_flags::THREAD_PLANT) || - mat.material->flags.is_set(material_flags::SILK) || - mat.material->flags.is_set(material_flags::YARN)) - wear = 1; - else - return false; - wear = ((orig_age % wear) + (age - orig_age)) / wear; - if (wear > 0) - return incWearTimer(wear); - else - return false; - } -}; -IMPLEMENT_VMETHOD_INTERPOSE(craft_age_wear_hook, ageItem); - -static bool inc_wear_timer (df::item_constructed *item, int amount) -{ - if (item->flags.bits.artifact) - return false; - - MaterialInfo mat(item->mat_type, item->mat_index); - if (mat.isInorganic() && mat.inorganic->flags.is_set(inorganic_flags::DEEP_SPECIAL)) - return false; - - item->wear_timer += amount; - return (item->wear_timer > 806400); -} - -struct adamantine_cloth_wear_armor_hook : df::item_armorst { - typedef df::item_armorst interpose_base; - - DEFINE_VMETHOD_INTERPOSE(bool, incWearTimer, (int amount)) - { - return inc_wear_timer(this, amount); - } -}; -IMPLEMENT_VMETHOD_INTERPOSE(adamantine_cloth_wear_armor_hook, incWearTimer); - -struct adamantine_cloth_wear_helm_hook : df::item_helmst { - typedef df::item_helmst interpose_base; - - DEFINE_VMETHOD_INTERPOSE(bool, incWearTimer, (int amount)) - { - return inc_wear_timer(this, amount); - } -}; -IMPLEMENT_VMETHOD_INTERPOSE(adamantine_cloth_wear_helm_hook, incWearTimer); - -struct adamantine_cloth_wear_gloves_hook : df::item_glovesst { - typedef df::item_glovesst interpose_base; - - DEFINE_VMETHOD_INTERPOSE(bool, incWearTimer, (int amount)) - { - return inc_wear_timer(this, amount); - } -}; -IMPLEMENT_VMETHOD_INTERPOSE(adamantine_cloth_wear_gloves_hook, incWearTimer); - -struct adamantine_cloth_wear_shoes_hook : df::item_shoesst { - typedef df::item_shoesst interpose_base; - - DEFINE_VMETHOD_INTERPOSE(bool, incWearTimer, (int amount)) - { - return inc_wear_timer(this, amount); - } -}; -IMPLEMENT_VMETHOD_INTERPOSE(adamantine_cloth_wear_shoes_hook, incWearTimer); - -struct adamantine_cloth_wear_pants_hook : df::item_pantsst { - typedef df::item_pantsst interpose_base; - - DEFINE_VMETHOD_INTERPOSE(bool, incWearTimer, (int amount)) - { - return inc_wear_timer(this, amount); - } -}; -IMPLEMENT_VMETHOD_INTERPOSE(adamantine_cloth_wear_pants_hook, incWearTimer); - static void enable_hook(color_ostream &out, VMethodInterposeLinkBase &hook, vector ¶meters) { if (vector_get(parameters, 1) == "disable") @@ -1121,6 +596,26 @@ static void enable_hook(color_ostream &out, VMethodInterposeLinkBase &hook, vect } } +static command_result enable_tweak(string tweak, color_ostream &out, vector ¶meters) +{ + bool recognized = false; + string cmd = parameters[0]; + for (auto it = tweak_hooks.begin(); it != tweak_hooks.end(); ++it) + { + if (it->first == cmd) + { + recognized = true; + enable_hook(out, it->second, parameters); + } + } + if (!recognized) + { + out.printerr("Unrecognized tweak: %s\n", cmd.c_str()); + return CR_WRONG_USAGE; + } + return CR_OK; +} + static command_result tweak(color_ostream &out, vector ¶meters) { CoreSuspender suspend; @@ -1219,25 +714,6 @@ static command_result tweak(color_ostream &out, vector ¶meters) unit->profession2 = df::profession::TRADER; return fix_clothing_ownership(out, unit); } - else if (cmd == "stable-cursor") - { - enable_hook(out, INTERPOSE_HOOK(stable_cursor_hook, feed), parameters); - } - else if (cmd == "patrol-duty") - { - enable_hook(out, INTERPOSE_HOOK(patrol_duty_hook, isPatrol), parameters); - } - else if (cmd == "confirm-embark") - { - enable_hook(out, INTERPOSE_HOOK(confirm_embark_hook, feed), parameters); - enable_hook(out, INTERPOSE_HOOK(confirm_embark_hook, key_conflict), parameters); - enable_hook(out, INTERPOSE_HOOK(confirm_embark_hook, render), parameters); - } - else if (cmd == "stable-temp") - { - enable_hook(out, INTERPOSE_HOOK(stable_temp_hook, adjustTemperature), parameters); - enable_hook(out, INTERPOSE_HOOK(stable_temp_hook, updateContaminants), parameters); - } else if (cmd == "fast-heat") { if (parameters.size() < 2) @@ -1245,9 +721,8 @@ static command_result tweak(color_ostream &out, vector ¶meters) max_heat_ticks = atoi(parameters[1].c_str()); if (max_heat_ticks <= 0) parameters[1] = "disable"; - enable_hook(out, INTERPOSE_HOOK(fast_heat_hook, updateTempFromMap), parameters); - enable_hook(out, INTERPOSE_HOOK(fast_heat_hook, updateTemperature), parameters); - enable_hook(out, INTERPOSE_HOOK(fast_heat_hook, adjustTemperature), parameters); + enable_tweak(cmd, out, parameters); + return CR_OK; } /*else if (cmd == "fix-dimensions") { @@ -1257,23 +732,6 @@ static command_result tweak(color_ostream &out, vector ¶meters) enable_hook(out, INTERPOSE_HOOK(dimension_thread_hook, subtractDimension), parameters); enable_hook(out, INTERPOSE_HOOK(dimension_cloth_hook, subtractDimension), parameters); }*/ - else if (cmd == "advmode-contained") - { - enable_hook(out, INTERPOSE_HOOK(advmode_contained_hook, feed), parameters); - } - else if (cmd == "fast-trade") - { - enable_hook(out, INTERPOSE_HOOK(fast_trade_assign_hook, feed), parameters); - enable_hook(out, INTERPOSE_HOOK(fast_trade_select_hook, feed), parameters); - } - else if (cmd == "military-stable-assign") - { - enable_hook(out, INTERPOSE_HOOK(military_assign_hook, feed), parameters); - } - else if (cmd == "military-color-assigned") - { - enable_hook(out, INTERPOSE_HOOK(military_assign_hook, render), parameters); - } /* else if (cmd == "military-training") { @@ -1282,20 +740,10 @@ static command_result tweak(color_ostream &out, vector ¶meters) enable_hook(out, INTERPOSE_HOOK(military_training_sp_hook, process), parameters); enable_hook(out, INTERPOSE_HOOK(military_training_id_hook, process), parameters); }*/ - else if (cmd == "craft-age-wear") - { - enable_hook(out, INTERPOSE_HOOK(craft_age_wear_hook, ageItem), parameters); - } - else if (cmd == "adamantine-cloth-wear") + else { - enable_hook(out, INTERPOSE_HOOK(adamantine_cloth_wear_armor_hook, incWearTimer), parameters); - enable_hook(out, INTERPOSE_HOOK(adamantine_cloth_wear_helm_hook, incWearTimer), parameters); - enable_hook(out, INTERPOSE_HOOK(adamantine_cloth_wear_gloves_hook, incWearTimer), parameters); - enable_hook(out, INTERPOSE_HOOK(adamantine_cloth_wear_shoes_hook, incWearTimer), parameters); - enable_hook(out, INTERPOSE_HOOK(adamantine_cloth_wear_pants_hook, incWearTimer), parameters); + return enable_tweak(cmd, out, parameters); } - else - return CR_WRONG_USAGE; return CR_OK; } diff --git a/plugins/tweak/tweaks/adamantine-cloth-wear.h b/plugins/tweak/tweaks/adamantine-cloth-wear.h new file mode 100644 index 000000000..b0524c91c --- /dev/null +++ b/plugins/tweak/tweaks/adamantine-cloth-wear.h @@ -0,0 +1,65 @@ +using namespace DFHack; +using namespace df::enums; + +static bool inc_wear_timer (df::item_constructed *item, int amount) +{ + if (item->flags.bits.artifact) + return false; + + MaterialInfo mat(item->mat_type, item->mat_index); + if (mat.isInorganic() && mat.inorganic->flags.is_set(inorganic_flags::DEEP_SPECIAL)) + return false; + + item->wear_timer += amount; + return (item->wear_timer > 806400); +} + +struct adamantine_cloth_wear_armor_hook : df::item_armorst { + typedef df::item_armorst interpose_base; + + DEFINE_VMETHOD_INTERPOSE(bool, incWearTimer, (int amount)) + { + return inc_wear_timer(this, amount); + } +}; +IMPLEMENT_VMETHOD_INTERPOSE(adamantine_cloth_wear_armor_hook, incWearTimer); + +struct adamantine_cloth_wear_helm_hook : df::item_helmst { + typedef df::item_helmst interpose_base; + + DEFINE_VMETHOD_INTERPOSE(bool, incWearTimer, (int amount)) + { + return inc_wear_timer(this, amount); + } +}; +IMPLEMENT_VMETHOD_INTERPOSE(adamantine_cloth_wear_helm_hook, incWearTimer); + +struct adamantine_cloth_wear_gloves_hook : df::item_glovesst { + typedef df::item_glovesst interpose_base; + + DEFINE_VMETHOD_INTERPOSE(bool, incWearTimer, (int amount)) + { + return inc_wear_timer(this, amount); + } +}; +IMPLEMENT_VMETHOD_INTERPOSE(adamantine_cloth_wear_gloves_hook, incWearTimer); + +struct adamantine_cloth_wear_shoes_hook : df::item_shoesst { + typedef df::item_shoesst interpose_base; + + DEFINE_VMETHOD_INTERPOSE(bool, incWearTimer, (int amount)) + { + return inc_wear_timer(this, amount); + } +}; +IMPLEMENT_VMETHOD_INTERPOSE(adamantine_cloth_wear_shoes_hook, incWearTimer); + +struct adamantine_cloth_wear_pants_hook : df::item_pantsst { + typedef df::item_pantsst interpose_base; + + DEFINE_VMETHOD_INTERPOSE(bool, incWearTimer, (int amount)) + { + return inc_wear_timer(this, amount); + } +}; +IMPLEMENT_VMETHOD_INTERPOSE(adamantine_cloth_wear_pants_hook, incWearTimer); diff --git a/plugins/tweak/tweaks/advmode-contained.h b/plugins/tweak/tweaks/advmode-contained.h new file mode 100644 index 000000000..0e60859c4 --- /dev/null +++ b/plugins/tweak/tweaks/advmode-contained.h @@ -0,0 +1,59 @@ +using namespace std; + +struct advmode_contained_hook : df::viewscreen_layer_unit_actionst { + typedef df::viewscreen_layer_unit_actionst interpose_base; + + DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) + { + auto old_reaction = cur_reaction; + auto old_reagent = reagent; + + INTERPOSE_NEXT(feed)(input); + + if (cur_reaction && (cur_reaction != old_reaction || reagent != old_reagent)) + { + old_reagent = reagent; + + // Skip reagents already contained by others + while (reagent < (int)cur_reaction->reagents.size()-1) + { + if (!cur_reaction->reagents[reagent]->flags.bits.IN_CONTAINER) + break; + reagent++; + } + + if (old_reagent != reagent) + { + // Reproduces a tiny part of the orginal screen code + choice_items.clear(); + + auto preagent = cur_reaction->reagents[reagent]; + reagent_amnt_left = preagent->quantity; + + for (int i = held_items.size()-1; i >= 0; i--) + { + if (!preagent->matchesRoot(held_items[i], cur_reaction->index)) + continue; + if (linear_index(sel_items, held_items[i]) >= 0) + continue; + choice_items.push_back(held_items[i]); + } + + layer_objects[6]->setListLength(choice_items.size()); + + if (!choice_items.empty()) + { + layer_objects[4]->active = layer_objects[5]->active = false; + layer_objects[6]->active = true; + } + else if (layer_objects[6]->active) + { + layer_objects[6]->active = false; + layer_objects[5]->active = true; + } + } + } + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(advmode_contained_hook, feed); diff --git a/plugins/tweak/tweaks/craft-age-wear.h b/plugins/tweak/tweaks/craft-age-wear.h new file mode 100644 index 000000000..edc3e9a18 --- /dev/null +++ b/plugins/tweak/tweaks/craft-age-wear.h @@ -0,0 +1,34 @@ +struct craft_age_wear_hook : df::item_crafted { + typedef df::item_crafted interpose_base; + + DEFINE_VMETHOD_INTERPOSE(bool, ageItem, (int amount)) + { + int orig_age = age; + age += amount; + if (age > 200000000) + age = 200000000; + if (age == orig_age) + return false; + + MaterialInfo mat(mat_type, mat_index); + if (!mat.isValid()) + return false; + int wear = 0; + + if (mat.material->flags.is_set(material_flags::WOOD)) + wear = 5; + else if (mat.material->flags.is_set(material_flags::LEATHER) || + mat.material->flags.is_set(material_flags::THREAD_PLANT) || + mat.material->flags.is_set(material_flags::SILK) || + mat.material->flags.is_set(material_flags::YARN)) + wear = 1; + else + return false; + wear = ((orig_age % wear) + (age - orig_age)) / wear; + if (wear > 0) + return incWearTimer(wear); + else + return false; + } +}; +IMPLEMENT_VMETHOD_INTERPOSE(craft_age_wear_hook, ageItem); diff --git a/plugins/tweak/tweaks/fast-heat.h b/plugins/tweak/tweaks/fast-heat.h new file mode 100644 index 000000000..706ca4ffe --- /dev/null +++ b/plugins/tweak/tweaks/fast-heat.h @@ -0,0 +1,48 @@ +using namespace df::enums; + +static int map_temp_mult = -1; +static int max_heat_ticks = 0; + +struct fast_heat_hook : df::item_actual { + typedef df::item_actual interpose_base; + + DEFINE_VMETHOD_INTERPOSE( + bool, updateTempFromMap, + (bool local, bool contained, bool adjust, int32_t rate_mult) + ) { + int cmult = map_temp_mult; + map_temp_mult = rate_mult; + + bool rv = INTERPOSE_NEXT(updateTempFromMap)(local, contained, adjust, rate_mult); + map_temp_mult = cmult; + return rv; + } + + DEFINE_VMETHOD_INTERPOSE( + bool, updateTemperature, + (uint16_t temp, bool local, bool contained, bool adjust, int32_t rate_mult) + ) { + // Some items take ages to cross the last degree, so speed them up + if (map_temp_mult > 0 && temp != temperature.whole && max_heat_ticks > 0) + { + int spec = getSpecHeat(); + if (spec != 60001) + rate_mult = std::max(map_temp_mult, spec/max_heat_ticks/abs(temp - temperature.whole)); + } + + return INTERPOSE_NEXT(updateTemperature)(temp, local, contained, adjust, rate_mult); + } + + DEFINE_VMETHOD_INTERPOSE(bool, adjustTemperature, (uint16_t temp, int32_t rate_mult)) + { + if (map_temp_mult > 0) + rate_mult = map_temp_mult; + + return INTERPOSE_NEXT(adjustTemperature)(temp, rate_mult); + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(fast_heat_hook, updateTempFromMap); +IMPLEMENT_VMETHOD_INTERPOSE(fast_heat_hook, updateTemperature); +IMPLEMENT_VMETHOD_INTERPOSE(fast_heat_hook, adjustTemperature); + diff --git a/plugins/tweak/tweaks/fast-trade.h b/plugins/tweak/tweaks/fast-trade.h new file mode 100644 index 000000000..66e1c9637 --- /dev/null +++ b/plugins/tweak/tweaks/fast-trade.h @@ -0,0 +1,43 @@ +using namespace std; + +struct fast_trade_assign_hook : df::viewscreen_layer_assigntradest { + typedef df::viewscreen_layer_assigntradest interpose_base; + + DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) + { + if (layer_objects[1]->active && input->count(interface_key::SELECT_ALL)) + { + set tmp; tmp.insert(interface_key::SELECT); + INTERPOSE_NEXT(feed)(&tmp); + tmp.clear(); tmp.insert(interface_key::STANDARDSCROLL_DOWN); + INTERPOSE_NEXT(feed)(&tmp); + } + else + INTERPOSE_NEXT(feed)(input); + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(fast_trade_assign_hook, feed); + +struct fast_trade_select_hook : df::viewscreen_tradegoodsst { + typedef df::viewscreen_tradegoodsst interpose_base; + + DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) + { + if (!(is_unloading || !has_traders || in_edit_count) + && input->count(interface_key::SELECT_ALL)) + { + set tmp; tmp.insert(interface_key::SELECT); + INTERPOSE_NEXT(feed)(&tmp); + if (in_edit_count) + INTERPOSE_NEXT(feed)(&tmp); + tmp.clear(); tmp.insert(interface_key::STANDARDSCROLL_DOWN); + INTERPOSE_NEXT(feed)(&tmp); + } + else + INTERPOSE_NEXT(feed)(input); + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(fast_trade_select_hook, feed); + diff --git a/plugins/tweak/tweaks/manager-quantity.h b/plugins/tweak/tweaks/manager-quantity.h new file mode 100644 index 000000000..821dc32d6 --- /dev/null +++ b/plugins/tweak/tweaks/manager-quantity.h @@ -0,0 +1,53 @@ +#include + +#include "df/viewscreen_createquotast.h" +#include "df/manager_order.h" + +using df::global::world; + +struct manager_quantity_hook : df::viewscreen_createquotast { + typedef df::viewscreen_createquotast interpose_base; + DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set* input)) + { + bool cancel = false; + bool wanted_quantity = want_quantity; + if (want_quantity) + { + for (auto it = input->begin(); it != input->end(); ++it) + { + char c = DFHack::Screen::keyToChar(*it); + if (c >= '0' && c <= '9') + { + cancel = true; + size_t len = strlen(str_filter); + if (len < 5) + { + str_filter[len] = c; + str_filter[len + 1] = '\0'; + } + } + } + } + if (cancel) + return; + // Native feed() adds manager order, updates want_quantity, and removes SELECT from input + int select = input->count(df::interface_key::SELECT); + INTERPOSE_NEXT(feed)(input); + if (wanted_quantity && select && strlen(str_filter) > 0) + { + df::manager_order* order = world->manager_orders[world->manager_orders.size() - 1]; + int16_t count = 1; + try + { + count = std::stoi(str_filter); + } + catch (...) { } + if (count < 1) + count = 1; + order->amount_total = count; + order->amount_left = count; + } + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(manager_quantity_hook, feed); diff --git a/plugins/tweak/tweaks/military-assign.h b/plugins/tweak/tweaks/military-assign.h new file mode 100644 index 000000000..45f97eac3 --- /dev/null +++ b/plugins/tweak/tweaks/military-assign.h @@ -0,0 +1,99 @@ +using namespace DFHack; +using namespace df::enums; +using Screen::Pen; + +struct military_assign_hook : df::viewscreen_layer_militaryst { + typedef df::viewscreen_layer_militaryst interpose_base; + + inline bool inPositionsMode() { + return page == Positions && !(in_create_squad || in_new_squad); + } + + DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) + { + if (inPositionsMode() && !layer_objects[0]->active) + { + auto pos_list = layer_objects[1]; + auto plist = layer_objects[2]; + auto &cand = positions.candidates; + + // Save the candidate list and cursors + std::vector copy = cand; + int cursor = plist->getListCursor(); + int pos_cursor = pos_list->getListCursor(); + + INTERPOSE_NEXT(feed)(input); + + if (inPositionsMode() && !layer_objects[0]->active) + { + bool is_select = input->count(interface_key::SELECT); + + // Resort the candidate list and restore cursor + // on add to squad OR scroll in the position list. + if (!plist->active || is_select) + { + // Since we don't know the actual sorting order, preserve + // the ordering of the items in the list before keypress. + // This does the right thing even if the list was sorted + // with sort-units. + std::set prev, next; + prev.insert(copy.begin(), copy.end()); + next.insert(cand.begin(), cand.end()); + std::vector out; + + // (old-before-cursor) (new) |cursor| (old-after-cursor) + for (int i = 0; i < cursor && i < (int)copy.size(); i++) + if (next.count(copy[i])) out.push_back(copy[i]); + for (size_t i = 0; i < cand.size(); i++) + if (!prev.count(cand[i])) out.push_back(cand[i]); + int new_cursor = out.size(); + for (int i = cursor; i < (int)copy.size(); i++) + if (next.count(copy[i])) out.push_back(copy[i]); + + cand.swap(out); + plist->setListLength(cand.size()); + if (new_cursor < (int)cand.size()) + plist->setListCursor(new_cursor); + } + + // Preserve the position list index on remove from squad + if (pos_list->active && is_select) + pos_list->setListCursor(pos_cursor); + } + } + else + INTERPOSE_NEXT(feed)(input); + } + + DEFINE_VMETHOD_INTERPOSE(void, render, ()) + { + INTERPOSE_NEXT(render)(); + + if (inPositionsMode()) + { + auto plist = layer_objects[2]; + int x1 = plist->getX1(), y1 = plist->getY1(); + int x2 = plist->getX2(), y2 = plist->getY2(); + int i1 = plist->getFirstVisible(), i2 = plist->getLastVisible(); + int si = plist->getListCursor(); + + for (int y = y1, i = i1; i <= i2; i++, y++) + { + auto unit = vector_get(positions.candidates, i); + if (!unit || unit->military.squad_id < 0) + continue; + + for (int x = x1; x <= x2; x++) + { + Pen cur_tile = Screen::readTile(x, y); + if (!cur_tile.valid()) continue; + cur_tile.fg = (i == si) ? COLOR_BROWN : COLOR_GREEN; + Screen::paintTile(cur_tile, x, y); + } + } + } + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(military_assign_hook, feed); +IMPLEMENT_VMETHOD_INTERPOSE(military_assign_hook, render); diff --git a/plugins/tweak/tweaks/stable-cursor.h b/plugins/tweak/tweaks/stable-cursor.h new file mode 100644 index 000000000..105510c8b --- /dev/null +++ b/plugins/tweak/tweaks/stable-cursor.h @@ -0,0 +1,77 @@ +/* + * Save or restore cursor position on change to/from main dwarfmode menu. + */ + +using namespace std; +using namespace DFHack; +using namespace df::enums; + +using df::global::ui; +using df::global::ui_build_selector; +using df::global::ui_menu_width; + +static df::coord last_view, last_cursor; + +struct stable_cursor_hook : df::viewscreen_dwarfmodest +{ + typedef df::viewscreen_dwarfmodest interpose_base; + + bool check_default() + { + switch (ui->main.mode) { + case ui_sidebar_mode::Default: + return true; + + case ui_sidebar_mode::Build: + return ui_build_selector && + (ui_build_selector->building_type < 0 || + ui_build_selector->stage < 1); + + default: + return false; + } + } + + DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) + { + bool was_default = check_default(); + df::coord view = Gui::getViewportPos(); + df::coord cursor = Gui::getCursorPos(); + + INTERPOSE_NEXT(feed)(input); + + bool is_default = check_default(); + df::coord cur_cursor = Gui::getCursorPos(); + + if (is_default && !was_default) + { + last_view = view; last_cursor = cursor; + } + else if (!is_default && was_default && + Gui::getViewportPos() == last_view && + last_cursor.isValid() && cur_cursor.isValid()) + { + Gui::setCursorCoords(last_cursor.x, last_cursor.y, last_cursor.z); + + // Force update of ui state + set tmp; + if (last_cursor.z < 2) + tmp.insert(interface_key::CURSOR_UP_Z); + else + tmp.insert(interface_key::CURSOR_DOWN_Z); + INTERPOSE_NEXT(feed)(&tmp); + tmp.clear(); + if (last_cursor.z < 2) + tmp.insert(interface_key::CURSOR_DOWN_Z); + else + tmp.insert(interface_key::CURSOR_UP_Z); + INTERPOSE_NEXT(feed)(&tmp); + } + else if (!is_default && cur_cursor.isValid()) + { + last_cursor = df::coord(); + } + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(stable_cursor_hook, feed);