diff --git a/LUA_API.rst b/LUA_API.rst index 1ffdada0a..c5f9a1c58 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -999,6 +999,10 @@ Items module Move the item to the unit inventory. Returns *false* if impossible. +* ``dfhack.items.remove(item[, no_uncat])`` + + Removes the item, and marks it for garbage collection unless ``no_uncat`` is true. + * ``dfhack.items.makeProjectile(item)`` Turns the item into a projectile, and returns the new object, or *nil* if impossible. diff --git a/Lua API.html b/Lua API.html index 168f7dcc6..07f038bc4 100644 --- a/Lua API.html +++ b/Lua API.html @@ -1213,6 +1213,9 @@ Returns false in case of error.

  • dfhack.items.moveToInventory(item,unit,use_mode,body_part)

    Move the item to the unit inventory. Returns false if impossible.

  • +
  • dfhack.items.remove(item[, no_uncat])

    +

    Removes the item, and marks it for garbage collection unless no_uncat is true.

    +
  • dfhack.items.makeProjectile(item)

    Turns the item into a projectile, and returns the new object, or nil if impossible.

  • diff --git a/NEWS b/NEWS new file mode 100644 index 000000000..43707f9a7 --- /dev/null +++ b/NEWS @@ -0,0 +1,61 @@ +DFHack v0.34.11-r2 (UNRELEASED) + + Internals: + - full support for Mac OS X. + - a plugin that adds scripting in ruby. + - support for interposing virtual methods in DF from C++ plugins. + - support for creating new interface screens from C++ and lua. + - added various other API functions. + Notable bugfixes: + - better terminal reset after exit on linux. + - seedwatch now works on reclaim. + - the sort plugin won't crash on cages anymore. + Misc improvements: + - autodump: can move items to any walkable tile, not just floors. + - stripcaged: by default keep armor, new dumparmor option. + - zone: allow non-domesticated birds in nestboxes. + - workflow: quality range in constraints. + - cleanplants: new command to remove rain water from plants. + - liquids: can paint permaflow, i.e. what makes rivers power water wheels. + - prospect: pre-embark prospector accounts for caves & magma sea in its estimate. + - rename: supports renaming stockpiles, workshops, traps, siege engines. + New tweaks: + - tweak stable-cursor: keeps exact cursor position between d/k/t/q/v etc menus. + - tweak patrol-duty: makes Train orders reduce patrol timer, like the binary patch does. + - tweak readable-build-plate: fix unreadable truncation in unit pressure plate build ui. + - tweak stable-temp: fixes bug 6012; may improve FPS by 50-100% on a slow item-heavy fort. + - tweak fast-heat: speeds up item heating & cooling, thus making stable-temp act faster. + New scripts: + - fixnaked: removes thoughts about nakedness. + - setfps: set FPS cap at runtime, in case you want slow motion or speed-up. + - fix/population-cap: run after every migrant wave to prevent exceeding the cap. + - fix/stable-temp: counts items with temperature updates; does instant one-shot stable-temp. + New GUI scripts: + - gui/mechanisms: browse mechanism links of the current building. + - gui/room-list: browse other rooms owned by the unit when assigning one. + - gui/liquids: a GUI front-end for the liquids plugin. + - gui/rename: renaming stockpiles, workshops and units via an in-game dialog. + - gui/power-meter: front-end for the Power Meter plugin. + - gui/siege-engine: front-end for the Siege Engine plugin. + Autolabor plugin: + - can set nonidle hauler percentage. + - broker excluded from all labors when needed at depot. + - likewise, anybody with a scheduled diplomat meeting. + New Dwarf Manipulator plugin: + Open the unit list, and press 'l' to access a Dwarf Therapist like UI in the game. + New Steam Engine plugin: + Dwarven Water Reactors don't make any sense whatsoever, so this is a potential + replacement for those concerned by it. The plugin detects if a workshop with a + certain name is in the raws used by the current world, and provides the necessary + behavior. See hack/raw/*_steam_engine.txt for the necessary raw definitions. + Note: Stuff like animal treadmills might be more period, but can't be done with dfhack. + New Power Meter plugin: + When activated, implements a pressure plate modification that detects power in gear + boxes built on the four adjacent N/S/W/E tiles. The gui/power-meter script implements + the build configuration UI. + New Siege Engine plugin (INCOMPLETE): + When enabled and configured via gui/siege-engine, allows aiming siege engines + at a designated rectangular area across Z levels. Also supports loading catapults + with non-boulder projectiles, taking from a stockpile, and restricting operator + skill range, like with ordinary workshops. + diff --git a/dfhack.init-example b/dfhack.init-example index 5af527099..a9b69b826 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -51,7 +51,7 @@ keybinding add Shift-G "job-material GLASS_GREEN" keybinding add Ctrl-M@dwarfmode/QueryBuilding/Some gui/mechanisms # browse rooms of same owner -keybinding add Alt-R@dwarfmode/QueryBuilding/Some gui/room-list.work +keybinding add Alt-R@dwarfmode/QueryBuilding/Some gui/room-list # interface for the liquids plugin keybinding add Alt-L@dwarfmode/LookAround gui/liquids @@ -59,9 +59,9 @@ keybinding add Alt-L@dwarfmode/LookAround gui/liquids # machine power sensitive pressure plate construction keybinding add Ctrl-Shift-M@dwarfmode/Build/Position/Trap gui/power-meter -################### -# UI logic tweaks # -################### +############################ +# UI and game logic tweaks # +############################ # stabilize the cursor of dwarfmode when switching menus tweak stable-cursor @@ -74,3 +74,8 @@ tweak readable-build-plate # improve FPS by squashing endless item temperature update loops tweak stable-temp + +# speed up items reaching temp equilibrium with environment by +# capping the rate to no less than 1 degree change per 500 frames +# Note: will also cause stuff to melt faster in magma etc +tweak fast-heat 500 diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index f69fa7a1b..f8497569e 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -886,6 +886,12 @@ static bool items_moveToInventory return Items::moveToInventory(mc, item, unit, mode, body_part); } +static bool items_remove(df::item *item, bool no_uncat) +{ + MapExtras::MapCache mc; + return Items::remove(mc, item, no_uncat); +} + static df::proj_itemst *items_makeProjectile(df::item *item) { MapExtras::MapCache mc; @@ -904,6 +910,7 @@ static const LuaWrapper::FunctionReg dfhack_items_module[] = { WRAPN(moveToBuilding, items_moveToBuilding), WRAPN(moveToInventory, items_moveToInventory), WRAPN(makeProjectile, items_makeProjectile), + WRAPN(remove, items_remove), { NULL, NULL } }; diff --git a/library/Process-linux.cpp b/library/Process-linux.cpp index 4a66470f9..1fecbab78 100644 --- a/library/Process-linux.cpp +++ b/library/Process-linux.cpp @@ -127,6 +127,9 @@ void Process::getMemRanges( vector & ranges ) char permissions[5]; // r/-, w/-, x/-, p/s, 0 FILE *mapFile = ::fopen("/proc/self/maps", "r"); + if (!mapFile) + return; + size_t start, end, offset, device1, device2, node; while (fgets(buffer, 1024, mapFile)) @@ -148,6 +151,8 @@ void Process::getMemRanges( vector & ranges ) temp.valid = true; ranges.push_back(temp); } + + fclose(mapFile); } uint32_t Process::getBase() diff --git a/library/include/modules/Items.h b/library/include/modules/Items.h index 7493d22fc..81c8e1285 100644 --- a/library/include/modules/Items.h +++ b/library/include/modules/Items.h @@ -157,6 +157,9 @@ DFHACK_EXPORT bool moveToBuilding(MapExtras::MapCache &mc, df::item *item, df::b DFHACK_EXPORT bool moveToInventory(MapExtras::MapCache &mc, df::item *item, df::unit *unit, df::unit_inventory_item::T_mode mode = df::unit_inventory_item::Carried, int body_part = -1); +/// Makes the item removed and marked for garbage collection +DFHACK_EXPORT bool remove(MapExtras::MapCache &mc, df::item *item, bool no_uncat = false); + /// Detaches the items from its current location and turns it into a projectile DFHACK_EXPORT df::proj_itemst *makeProjectile(MapExtras::MapCache &mc, df::item *item); } diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index 751797f06..b8c697a48 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -730,6 +730,18 @@ static bool detachItem(MapExtras::MapCache &mc, df::item *item) item->flags.bits.in_inventory = false; return true; } + else if (item->flags.bits.removed) + { + item->flags.bits.removed = false; + + if (item->flags.bits.garbage_collect) + { + item->flags.bits.garbage_collect = false; + item->categorize(true); + } + + return true; + } else return false; } @@ -871,6 +883,26 @@ bool DFHack::Items::moveToInventory( return true; } +bool Items::remove(MapExtras::MapCache &mc, df::item *item, bool no_uncat) +{ + CHECK_NULL_POINTER(item); + + auto pos = getPosition(item); + + if (!detachItem(mc, item)) + return false; + + if (pos.isValid()) + item->pos = pos; + + if (!no_uncat) + item->uncategorize(); + + item->flags.bits.removed = true; + item->flags.bits.garbage_collect = !no_uncat; + return true; +} + df::proj_itemst *Items::makeProjectile(MapExtras::MapCache &mc, df::item *item) { CHECK_NULL_POINTER(item); diff --git a/library/xml b/library/xml index 2bc8fbdf7..ee2b63a8f 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 2bc8fbdf71143398817d31e06e169a01cce37c50 +Subproject commit ee2b63a8ffdbce66489148ca2a9803db1d0b9090 diff --git a/plugins/devel/dumpmats.cpp b/plugins/devel/dumpmats.cpp index ba888e7cf..0af1fce50 100644 --- a/plugins/devel/dumpmats.cpp +++ b/plugins/devel/dumpmats.cpp @@ -11,6 +11,7 @@ #include "df/matter_state.h" #include "df/descriptor_color.h" #include "df/item_type.h" +#include "df/strain_type.h" using std::string; using std::vector; @@ -195,47 +196,17 @@ command_result df_dumpmats (color_ostream &out, vector ¶meters) if (mat->molar_mass != 0xFBBC7818) out.print("\t[MOLAR_MASS:%i]\n", mat->molar_mass); - if (mat->strength.impact_yield != 10000) - out.print("\t[IMPACT_YIELD:%i]\n", mat->strength.impact_yield); - if (mat->strength.impact_fracture != 10000) - out.print("\t[IMPACT_FRACTURE:%i]\n", mat->strength.impact_fracture); - if (mat->strength.impact_strain_at_yield != 0) - out.print("\t[IMPACT_STRAIN_AT_YIELD:%i]\n", mat->strength.impact_strain_at_yield); - - if (mat->strength.compressive_yield != 10000) - out.print("\t[COMPRESSIVE_YIELD:%i]\n", mat->strength.compressive_yield); - if (mat->strength.compressive_fracture != 10000) - out.print("\t[COMPRESSIVE_FRACTURE:%i]\n", mat->strength.compressive_fracture); - if (mat->strength.compressive_strain_at_yield != 0) - out.print("\t[COMPRESSIVE_STRAIN_AT_YIELD:%i]\n", mat->strength.compressive_strain_at_yield); - - if (mat->strength.tensile_yield != 10000) - out.print("\t[TENSILE_YIELD:%i]\n", mat->strength.tensile_yield); - if (mat->strength.tensile_fracture != 10000) - out.print("\t[TENSILE_FRACTURE:%i]\n", mat->strength.tensile_fracture); - if (mat->strength.tensile_strain_at_yield != 0) - out.print("\t[TENSILE_STRAIN_AT_YIELD:%i]\n", mat->strength.tensile_strain_at_yield); - - if (mat->strength.torsion_yield != 10000) - out.print("\t[TORSION_YIELD:%i]\n", mat->strength.torsion_yield); - if (mat->strength.torsion_fracture != 10000) - out.print("\t[TORSION_FRACTURE:%i]\n", mat->strength.torsion_fracture); - if (mat->strength.torsion_strain_at_yield != 0) - out.print("\t[TORSION_STRAIN_AT_YIELD:%i]\n", mat->strength.torsion_strain_at_yield); - - if (mat->strength.shear_yield != 10000) - out.print("\t[SHEAR_YIELD:%i]\n", mat->strength.shear_yield); - if (mat->strength.shear_fracture != 10000) - out.print("\t[SHEAR_FRACTURE:%i]\n", mat->strength.shear_fracture); - if (mat->strength.shear_strain_at_yield != 0) - out.print("\t[SHEAR_STRAIN_AT_YIELD:%i]\n", mat->strength.shear_strain_at_yield); - - if (mat->strength.bending_yield != 10000) - out.print("\t[BENDING_YIELD:%i]\n", mat->strength.bending_yield); - if (mat->strength.bending_fracture != 10000) - out.print("\t[BENDING_FRACTURE:%i]\n", mat->strength.bending_fracture); - if (mat->strength.bending_strain_at_yield != 0) - out.print("\t[BENDING_STRAIN_AT_YIELD:%i]\n", mat->strength.bending_strain_at_yield); + FOR_ENUM_ITEMS(strain_type, strain) + { + auto name = ENUM_KEY_STR(strain_type,strain); + + if (mat->strength.yield[strain] != 10000) + out.print("\t[%s_YIELD:%i]\n", name.c_str(), mat->strength.yield[strain]); + if (mat->strength.fracture[strain] != 10000) + out.print("\t[%s_FRACTURE:%i]\n", name.c_str(), mat->strength.fracture[strain]); + if (mat->strength.strain_at_yield[strain] != 0) + out.print("\t[%s_STRAIN_AT_YIELD:%i]\n", name.c_str(), mat->strength.strain_at_yield[strain]); + } if (mat->strength.max_edge != 0) out.print("\t[MAX_EDGE:%i]\n", mat->strength.max_edge); diff --git a/plugins/devel/siege-engine.cpp b/plugins/devel/siege-engine.cpp index a41bfe5f7..b8a2f087b 100644 --- a/plugins/devel/siege-engine.cpp +++ b/plugins/devel/siege-engine.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -45,11 +46,14 @@ #include "df/unit_misc_trait.h" #include "df/job.h" #include "df/job_item.h" -#include "df/item.h" +#include "df/item_actual.h" #include "df/items_other_id.h" #include "df/building_stockpilest.h" #include "df/stockpile_links.h" #include "df/workshop_profile.h" +#include "df/strain_type.h" +#include "df/material.h" +#include "df/flow_type.h" #include "MiscUtils.h" @@ -162,6 +166,63 @@ static void random_direction(float &x, float &y, float &z) z = 1.0f - 2.0f*d; } +static const int WEAR_TICKS = 806400; + +static bool apply_impact_damage(df::item *item, int minv, int maxv) +{ + MaterialInfo info(item); + if (!info.isValid()) + { + item->setWear(3); + return false; + } + + auto &strength = info.material->strength; + + // Use random strain type excluding COMPRESSIVE (conveniently last) + int type = random_int(strain_type::COMPRESSIVE); + int power = minv + random_int(maxv-minv+1); + + // High elasticity materials just bend + if (strength.strain_at_yield[type] >= 5000) + return true; + + // Instant fracture? + int fracture = strength.fracture[type]; + if (fracture <= power) + { + item->setWear(3); + return false; + } + + // Impact within elastic strain range? + int yield = strength.yield[type]; + if (yield > power) + return true; + + // Can wear? + auto actual = virtual_cast(item); + if (!actual) + return false; + + // Transform plastic deformation to wear + int max_wear = WEAR_TICKS * 4; + int cur_wear = WEAR_TICKS * actual->wear + actual->wear_timer; + cur_wear += int64_t(power - yield)*max_wear/(fracture - yield); + + if (cur_wear >= max_wear) + { + actual->wear = 3; + return false; + } + else + { + actual->wear = cur_wear / WEAR_TICKS; + actual->wear_timer = cur_wear % WEAR_TICKS; + return true; + } +} + /* * Configuration object */ @@ -1225,6 +1286,12 @@ static int proposeUnitHits(lua_State *L) * Projectile hook */ +static const int offsets[8][2] = { + { -1, -1 }, { 0, -1 }, { 1, -1 }, + { -1, 0 }, { 1, 0 }, + { -1, 1 }, { 0, 1 }, { 1, 1 } +}; + struct projectile_hook : df::proj_itemst { typedef df::proj_itemst interpose_base; @@ -1232,6 +1299,9 @@ struct projectile_hook : df::proj_itemst { { target_pos = path.target; + // Debug + Maps::getTileOccupancy(path.goal)->bits.arrow_color = COLOR_LIGHTMAGENTA; + PathMetrics raytrace(path); // Materialize map blocks, or the projectile will crash into them @@ -1259,7 +1329,53 @@ struct projectile_hook : df::proj_itemst { fall_threshold = std::min(fall_threshold, engine->fire_range.second); } - void aimAtArea(EngineInfo *engine) + void aimAtPoint(EngineInfo *engine, int skill, const ProjectilePath &path) + { + df::coord fail_target = path.goal; + + orient_engine(engine->bld, path.goal); + + // Debug + Maps::getTileOccupancy(path.goal)->bits.arrow_color = COLOR_LIGHTRED; + + // Dabbling always hit in 7x7 area + if (skill < skill_rating::Novice) + { + fail_target.x += random_int(7)-3; + fail_target.y += random_int(7)-3; + aimAtPoint(engine, ProjectilePath(path.origin, fail_target)); + return; + } + + // Exact hit chance + float hit_chance = 1.04f - powf(0.8f, skill); + + if (float(rand())/RAND_MAX < hit_chance) + { + aimAtPoint(engine, path); + return; + } + + // Otherwise perturb + if (skill <= skill_rating::Proficient) + { + // 5x5 + fail_target.x += random_int(5)-2; + fail_target.y += random_int(5)-2; + } + else + { + // 3x3 + int idx = random_int(8); + fail_target.x += offsets[idx][0]; + fail_target.y += offsets[idx][1]; + } + + ProjectilePath fail(path.origin, fail_target, path.fudge_delta, path.fudge_factor); + aimAtPoint(engine, fail); + } + + void aimAtArea(EngineInfo *engine, int skill) { df::coord target, last_passable; df::coord tbase = engine->target.first; @@ -1282,7 +1398,7 @@ struct projectile_hook : df::proj_itemst { if (raytrace.hits() && engine->isInRange(raytrace.goal_step)) { - aimAtPoint(engine, path); + aimAtPoint(engine, skill, path); return; } } @@ -1290,7 +1406,7 @@ struct projectile_hook : df::proj_itemst { if (!last_passable.isValid()) last_passable = target; - aimAtPoint(engine, ProjectilePath(engine->center, last_passable)); + aimAtPoint(engine, skill, ProjectilePath(engine->center, last_passable)); } static int safeAimProjectile(lua_State *L) @@ -1312,9 +1428,9 @@ struct projectile_hook : df::proj_itemst { lua_call(L, 5, 1); if (lua_isnil(L, -1)) - proj->aimAtArea(engine); + proj->aimAtArea(engine, skill); else - proj->aimAtPoint(engine, decode_path(L, -1, engine->center)); + proj->aimAtPoint(engine, skill, decode_path(L, -1, engine->center)); return 0; } @@ -1335,13 +1451,19 @@ struct projectile_hook : df::proj_itemst { int skill = getOperatorSkill(engine->bld, true); - lua_pushcfunction(L, safeAimProjectile); - lua_pushlightuserdata(L, this); - lua_pushlightuserdata(L, engine); - lua_pushinteger(L, skill); + // Dabbling can't aim + if (skill < skill_rating::Novice) + aimAtArea(engine, skill); + else + { + lua_pushcfunction(L, safeAimProjectile); + lua_pushlightuserdata(L, this); + lua_pushlightuserdata(L, engine); + lua_pushinteger(L, skill); - if (!Lua::Core::SafeCall(out, 3, 0)) - aimAtArea(engine); + if (!Lua::Core::SafeCall(out, 3, 0)) + aimAtArea(engine, skill); + } switch (item->getType()) { @@ -1363,6 +1485,10 @@ struct projectile_hook : df::proj_itemst { float speed = 100000.0f / (fall_delay + 1); int min_zspeed = (fall_delay+1)*4900; + float bonus = 1.0f + 0.1f*(origin_pos.z -cur_pos.z); + bonus *= 1.0f + (distance_flown - 60) / 200.0f; + speed *= bonus; + // Flight direction vector df::coord dist = target_pos - origin_pos; float vx = dist.x, vy = dist.y, vz = fabs(dist.z); @@ -1383,10 +1509,28 @@ struct projectile_hook : df::proj_itemst { for (size_t i = 0; i < contents.size(); i++) { auto child = contents[i]; + + // Liquids are vaporized so that they cover nearby units + if (child->isLiquid()) + { + auto flow = Maps::spawnFlow( + cur_pos, + flow_type::MaterialVapor, + child->getMaterial(), child->getMaterialIndex(), + 100 + ); + + // should it leave a puddle too?.. + if (flow && Items::remove(mc, child)) + continue; + } + auto proj = Items::makeProjectile(mc, child); if (!proj) continue; - proj->flags.bits.no_impact_destroy = true; + bool keep = apply_impact_damage(child, 50000, int(250000*bonus)); + + proj->flags.bits.no_impact_destroy = keep; //proj->flags.bits.bouncing = true; proj->flags.bits.piercing = true; proj->flags.bits.parabolic = true; @@ -1403,7 +1547,7 @@ struct projectile_hook : df::proj_itemst { proj->speed_x = int(speed * sx); proj->speed_y = int(speed * sy); - proj->speed_z = int(speed * sz); + proj->speed_z = std::max(min_zspeed, int(speed * sz)); } } diff --git a/plugins/tweak.cpp b/plugins/tweak.cpp index fbea30231..bebc346c5 100644 --- a/plugins/tweak.cpp +++ b/plugins/tweak.cpp @@ -89,6 +89,10 @@ 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" + " when updating from the environment temperature. Use 0 to disable.\n" )); return CR_OK; } @@ -293,6 +297,52 @@ struct stable_temp_hook : df::item_actual { 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 && max_heat_ticks > 0) + { + int spec = getSpecHeat(); + if (spec != 60001) + rate_mult = std::max(map_temp_mult, spec/max_heat_ticks/abs(temp - temperature)); + } + + 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 enable_hook(color_ostream &out, VMethodInterposeLinkBase &hook, vector ¶meters) { if (vector_get(parameters, 1) == "disable") @@ -430,6 +480,17 @@ static command_result tweak(color_ostream &out, vector ¶meters) 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) + return CR_WRONG_USAGE; + 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); + } else return CR_WRONG_USAGE; diff --git a/scripts/fix/stable-temp.lua b/scripts/fix/stable-temp.lua index d06d0fcce..27a88ef7b 100644 --- a/scripts/fix/stable-temp.lua +++ b/scripts/fix/stable-temp.lua @@ -1,5 +1,9 @@ -- Reset item temperature to the value of their tile. +local args = {...} + +local apply = (args[1] == 'apply') + local count = 0 local types = {} @@ -9,13 +13,16 @@ local function update_temp(item,btemp) local tid = item:getType() types[tid] = (types[tid] or 0) + 1 end - item.temperature = btemp - item.temperature_fraction = 0 - if item.contaminants then - for _,c in ipairs(item.contaminants) do - c.temperature = btemp - c.temperature_fraction = 0 + if apply then + item.temperature = btemp + item.temperature_fraction = 0 + + if item.contaminants then + for _,c in ipairs(item.contaminants) do + c.temperature = btemp + c.temperature_fraction = 0 + end end end @@ -23,7 +30,9 @@ local function update_temp(item,btemp) update_temp(sub,btemp) end - item:checkTemperatureDamage() + if apply then + item:checkTemperatureDamage() + end end local last_frame = df.global.world.frame_counter-1 @@ -39,7 +48,11 @@ for _,item in ipairs(df.global.world.items.all) do end end -print('Items updated: '..count) +if apply then + print('Items updated: '..count) +else + print('Items not in equilibrium: '..count) +end local tlist = {} for k,_ in pairs(types) do tlist[#tlist+1] = k end @@ -47,3 +60,7 @@ table.sort(tlist, function(a,b) return types[a] > types[b] end) for _,k in ipairs(tlist) do print(' '..df.item_type[k]..':', types[k]) end + +if not apply then + print("Use 'fix/stable-temp apply' to force-change temperature.") +end diff --git a/scripts/setfps.lua b/scripts/setfps.lua new file mode 100644 index 000000000..690f82702 --- /dev/null +++ b/scripts/setfps.lua @@ -0,0 +1,10 @@ +-- Set the FPS cap at runtime. + +local cap = ... +local capnum = tonumber(cap) + +if not capnum or capnum < 1 then + qerror('Invalid FPS cap value: '..cap) +end + +df.global.enabler.fps = capnum