Merge remote-tracking branch 'angavrilov/master'

develop
Kelly Martin 2012-09-16 16:37:08 -05:00
commit 847dadd3f5
14 changed files with 391 additions and 68 deletions

@ -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.

@ -1213,6 +1213,9 @@ Returns <em>false</em> in case of error.</p>
<li><p class="first"><tt class="docutils literal">dfhack.items.moveToInventory(item,unit,use_mode,body_part)</tt></p>
<p>Move the item to the unit inventory. Returns <em>false</em> if impossible.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.items.remove(item[, no_uncat])</tt></p>
<p>Removes the item, and marks it for garbage collection unless <tt class="docutils literal">no_uncat</tt> is true.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.items.makeProjectile(item)</tt></p>
<p>Turns the item into a projectile, and returns the new object, or <em>nil</em> if impossible.</p>
</li>

61
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.

@ -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

@ -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 }
};

@ -127,6 +127,9 @@ void Process::getMemRanges( vector<t_memrange> & 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<t_memrange> & ranges )
temp.valid = true;
ranges.push_back(temp);
}
fclose(mapFile);
}
uint32_t Process::getBase()

@ -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);
}

@ -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);

@ -1 +1 @@
Subproject commit 2bc8fbdf71143398817d31e06e169a01cce37c50
Subproject commit ee2b63a8ffdbce66489148ca2a9803db1d0b9090

@ -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<string> &parameters)
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);

@ -10,6 +10,7 @@
#include <modules/World.h>
#include <modules/Units.h>
#include <modules/Job.h>
#include <modules/Materials.h>
#include <LuaTools.h>
#include <TileTypes.h>
#include <vector>
@ -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<df::item_actual>(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);
// 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);
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));
}
}

@ -89,6 +89,10 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector <Plugi
" Fixes rendering of creature weight limits in pressure plate build menu.\n"
" tweak stable-temp [disable]\n"
" Fixes performance bug 6012 by squashing jitter in temperature updates.\n"
" tweak fast-heat <max-ticks>\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 <string> &parameters)
{
if (vector_get(parameters, 1) == "disable")
@ -430,6 +480,17 @@ static command_result tweak(color_ostream &out, vector <string> &parameters)
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;

@ -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,6 +13,8 @@ local function update_temp(item,btemp)
local tid = item:getType()
types[tid] = (types[tid] or 0) + 1
end
if apply then
item.temperature = btemp
item.temperature_fraction = 0
@ -18,12 +24,15 @@ local function update_temp(item,btemp)
c.temperature_fraction = 0
end
end
end
for _,sub in ipairs(dfhack.items.getContainedItems(item)) do
update_temp(sub,btemp)
end
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

@ -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