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