Merge branch 'master' of git://github.com/angavrilov/dfhack
commit
27fd3f5fc7
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,150 @@
|
||||
-- A trivial reloadable class system
|
||||
|
||||
local _ENV = mkmodule('class')
|
||||
|
||||
-- Metatable template for a class
|
||||
class_obj = {} or class_obj
|
||||
|
||||
-- Methods shared by all classes
|
||||
common_methods = {} or common_methods
|
||||
|
||||
-- Forbidden names for class fields and methods.
|
||||
reserved_names = { super = true, ATTRS = true }
|
||||
|
||||
-- Attribute table metatable
|
||||
attrs_meta = {} or attrs_meta
|
||||
|
||||
-- Create or updates a class; a class has metamethods and thus own metatable.
|
||||
function defclass(class,parent)
|
||||
class = class or {}
|
||||
|
||||
local meta = getmetatable(class)
|
||||
if not meta then
|
||||
meta = {}
|
||||
setmetatable(class, meta)
|
||||
end
|
||||
|
||||
for k,v in pairs(class_obj) do meta[k] = v end
|
||||
|
||||
meta.__index = parent or common_methods
|
||||
|
||||
local attrs = rawget(class, 'ATTRS') or {}
|
||||
setmetatable(attrs, attrs_meta)
|
||||
|
||||
rawset(class, 'super', parent)
|
||||
rawset(class, 'ATTRS', attrs)
|
||||
rawset(class, '__index', rawget(class, '__index') or class)
|
||||
|
||||
return class
|
||||
end
|
||||
|
||||
-- An instance uses the class as metatable
|
||||
function mkinstance(class,table)
|
||||
table = table or {}
|
||||
setmetatable(table, class)
|
||||
return table
|
||||
end
|
||||
|
||||
-- Patch the stubs in the global environment
|
||||
dfhack.BASE_G.defclass = _ENV.defclass
|
||||
dfhack.BASE_G.mkinstance = _ENV.mkinstance
|
||||
|
||||
-- Just verify the name, and then set.
|
||||
function class_obj:__newindex(name,val)
|
||||
if reserved_names[name] or common_methods[name] then
|
||||
error('Method name '..name..' is reserved.')
|
||||
end
|
||||
rawset(self, name, val)
|
||||
end
|
||||
|
||||
function attrs_meta:__call(attrs)
|
||||
for k,v in pairs(attrs) do
|
||||
self[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
local function apply_attrs(obj, attrs, init_table)
|
||||
for k,v in pairs(attrs) do
|
||||
if v == DEFAULT_NIL then
|
||||
v = nil
|
||||
end
|
||||
obj[k] = init_table[k] or v
|
||||
end
|
||||
end
|
||||
|
||||
local function invoke_before_rec(self, class, method, ...)
|
||||
local meta = getmetatable(class)
|
||||
if meta then
|
||||
local fun = rawget(class, method)
|
||||
if fun then
|
||||
fun(self, ...)
|
||||
end
|
||||
|
||||
invoke_before_rec(self, meta.__index, method, ...)
|
||||
end
|
||||
end
|
||||
|
||||
local function invoke_after_rec(self, class, method, ...)
|
||||
local meta = getmetatable(class)
|
||||
if meta then
|
||||
invoke_after_rec(self, meta.__index, method, ...)
|
||||
|
||||
local fun = rawget(class, method)
|
||||
if fun then
|
||||
fun(self, ...)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function init_attrs_rec(obj, class, init_table)
|
||||
local meta = getmetatable(class)
|
||||
if meta then
|
||||
init_attrs_rec(obj, meta.__index, init_table)
|
||||
apply_attrs(obj, rawget(class, 'ATTRS'), init_table)
|
||||
end
|
||||
end
|
||||
|
||||
-- Call metamethod constructs the object
|
||||
function class_obj:__call(init_table)
|
||||
-- The table is assumed to be a scratch temporary.
|
||||
-- If it is not, copy it yourself before calling.
|
||||
init_table = init_table or {}
|
||||
|
||||
local obj = mkinstance(self)
|
||||
|
||||
-- This initialization sequence is broadly based on how the
|
||||
-- Common Lisp initialize-instance generic function works.
|
||||
|
||||
-- preinit screens input arguments in subclass to superclass order
|
||||
invoke_before_rec(obj, self, 'preinit', init_table)
|
||||
-- initialize the instance table from init table
|
||||
init_attrs_rec(obj, self, init_table)
|
||||
-- init in superclass -> subclass
|
||||
invoke_after_rec(obj, self, 'init', init_table)
|
||||
-- postinit in superclass -> subclass
|
||||
invoke_after_rec(obj, self, 'postinit', init_table)
|
||||
|
||||
return obj
|
||||
end
|
||||
|
||||
-- Common methods for all instances:
|
||||
|
||||
function common_methods:callback(method, ...)
|
||||
return dfhack.curry(self[method], self, ...)
|
||||
end
|
||||
|
||||
function common_methods:assign(data)
|
||||
for k,v in pairs(data) do
|
||||
self[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
function common_methods:invoke_before(method, ...)
|
||||
return invoke_before_rec(self, getmetatable(self), method, ...)
|
||||
end
|
||||
|
||||
function common_methods:invoke_after(method, ...)
|
||||
return invoke_after_rec(self, getmetatable(self), method, ...)
|
||||
end
|
||||
|
||||
return _ENV
|
@ -1 +1 @@
|
||||
Subproject commit 2bc8fbdf71143398817d31e06e169a01cce37c50
|
||||
Subproject commit 8a78bfa218817765b0a80431e0cf25435ffb2179
|
@ -0,0 +1,433 @@
|
||||
#include "Core.h"
|
||||
#include <Console.h>
|
||||
#include <Export.h>
|
||||
#include <PluginManager.h>
|
||||
#include <modules/Gui.h>
|
||||
#include <modules/Screen.h>
|
||||
#include <modules/Maps.h>
|
||||
#include <modules/Job.h>
|
||||
#include <modules/Items.h>
|
||||
#include <modules/Units.h>
|
||||
#include <TileTypes.h>
|
||||
#include <vector>
|
||||
#include <cstdio>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <cmath>
|
||||
#include <string.h>
|
||||
|
||||
#include <VTableInterpose.h>
|
||||
#include "df/item_liquid_miscst.h"
|
||||
#include "df/item_constructed.h"
|
||||
#include "df/builtin_mats.h"
|
||||
#include "df/world.h"
|
||||
#include "df/job.h"
|
||||
#include "df/job_item.h"
|
||||
#include "df/job_item_ref.h"
|
||||
#include "df/ui.h"
|
||||
#include "df/report.h"
|
||||
#include "df/reaction.h"
|
||||
#include "df/reaction_reagent_itemst.h"
|
||||
#include "df/reaction_product_item_improvementst.h"
|
||||
#include "df/reaction_product_improvement_flags.h"
|
||||
#include "df/matter_state.h"
|
||||
#include "df/contaminant.h"
|
||||
|
||||
#include "MiscUtils.h"
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
using std::stack;
|
||||
using namespace DFHack;
|
||||
using namespace df::enums;
|
||||
|
||||
using df::global::gps;
|
||||
using df::global::world;
|
||||
using df::global::ui;
|
||||
|
||||
typedef df::reaction_product_item_improvementst improvement_product;
|
||||
|
||||
DFHACK_PLUGIN("add-spatter");
|
||||
|
||||
struct ReagentSource {
|
||||
int idx;
|
||||
df::reaction_reagent *reagent;
|
||||
|
||||
ReagentSource() : idx(-1), reagent(NULL) {}
|
||||
};
|
||||
|
||||
struct MaterialSource : ReagentSource {
|
||||
bool product;
|
||||
std::string product_name;
|
||||
|
||||
int mat_type, mat_index;
|
||||
|
||||
MaterialSource() : product(false), mat_type(-1), mat_index(-1) {}
|
||||
};
|
||||
|
||||
struct ProductInfo {
|
||||
df::reaction *react;
|
||||
improvement_product *product;
|
||||
|
||||
ReagentSource object;
|
||||
MaterialSource material;
|
||||
|
||||
bool isValid() {
|
||||
return object.reagent && (material.mat_type >= 0 || material.reagent);
|
||||
}
|
||||
};
|
||||
|
||||
struct ReactionInfo {
|
||||
df::reaction *react;
|
||||
|
||||
std::vector<ProductInfo> products;
|
||||
};
|
||||
|
||||
static std::map<std::string, ReactionInfo> reactions;
|
||||
static std::map<df::reaction_product*, ProductInfo*> products;
|
||||
|
||||
static ReactionInfo *find_reaction(const std::string &name)
|
||||
{
|
||||
auto it = reactions.find(name);
|
||||
return (it != reactions.end()) ? &it->second : NULL;
|
||||
}
|
||||
|
||||
static bool is_add_spatter(const std::string &name)
|
||||
{
|
||||
return name.size() > 12 && memcmp(name.data(), "SPATTER_ADD_", 12) == 0;
|
||||
}
|
||||
|
||||
static void find_material(int *type, int *index, df::item *input, MaterialSource &mat)
|
||||
{
|
||||
if (input && mat.reagent)
|
||||
{
|
||||
MaterialInfo info(input);
|
||||
|
||||
if (mat.product)
|
||||
{
|
||||
if (!info.findProduct(info, mat.product_name))
|
||||
{
|
||||
color_ostream_proxy out(Core::getInstance().getConsole());
|
||||
out.printerr("Cannot find product '%s'\n", mat.product_name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
*type = info.type;
|
||||
*index = info.index;
|
||||
}
|
||||
else
|
||||
{
|
||||
*type = mat.mat_type;
|
||||
*index = mat.mat_index;
|
||||
}
|
||||
}
|
||||
|
||||
static int has_contaminant(df::item_actual *item, int type, int index)
|
||||
{
|
||||
auto cont = item->contaminants;
|
||||
if (!cont)
|
||||
return 0;
|
||||
|
||||
int size = 0;
|
||||
|
||||
for (size_t i = 0; i < cont->size(); i++)
|
||||
{
|
||||
auto cur = (*cont)[i];
|
||||
if (cur->mat_type == type && cur->mat_index == index)
|
||||
size += cur->size;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Hooks
|
||||
*/
|
||||
|
||||
typedef std::map<int, std::vector<df::item*> > item_table;
|
||||
|
||||
static void index_items(item_table &table, df::job *job, ReactionInfo *info)
|
||||
{
|
||||
for (int i = job->items.size()-1; i >= 0; i--)
|
||||
{
|
||||
auto iref = job->items[i];
|
||||
if (iref->job_item_idx < 0) continue;
|
||||
auto iitem = job->job_items[iref->job_item_idx];
|
||||
|
||||
if (iitem->contains.empty())
|
||||
{
|
||||
table[iitem->reagent_index].push_back(iref->item);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<df::item*> contents;
|
||||
Items::getContainedItems(iref->item, &contents);
|
||||
|
||||
for (int j = contents.size()-1; j >= 0; j--)
|
||||
{
|
||||
for (int k = iitem->contains.size()-1; k >= 0; k--)
|
||||
{
|
||||
int ridx = iitem->contains[k];
|
||||
auto reag = info->react->reagents[ridx];
|
||||
|
||||
if (reag->matchesChild(contents[j], info->react, iitem->reaction_id))
|
||||
table[ridx].push_back(contents[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
df::item* find_item(ReagentSource &info, item_table &table)
|
||||
{
|
||||
if (!info.reagent)
|
||||
return NULL;
|
||||
if (table[info.idx].empty())
|
||||
return NULL;
|
||||
return table[info.idx].back();
|
||||
}
|
||||
|
||||
struct item_hook : df::item_constructed {
|
||||
typedef df::item_constructed interpose_base;
|
||||
|
||||
DEFINE_VMETHOD_INTERPOSE(bool, isImprovable, (df::job *job, int16_t mat_type, int32_t mat_index))
|
||||
{
|
||||
ReactionInfo *info;
|
||||
|
||||
if (job && job->job_type == job_type::CustomReaction &&
|
||||
(info = find_reaction(job->reaction_name)) != NULL)
|
||||
{
|
||||
if (!contaminants || contaminants->empty())
|
||||
return true;
|
||||
|
||||
item_table table;
|
||||
index_items(table, job, info);
|
||||
|
||||
for (size_t i = 0; i < info->products.size(); i++)
|
||||
{
|
||||
auto &product = info->products[i];
|
||||
|
||||
int mattype, matindex;
|
||||
auto material = find_item(info->products[i].material, table);
|
||||
|
||||
find_material(&mattype, &matindex, material, product.material);
|
||||
|
||||
if (mattype < 0 || has_contaminant(this, mattype, matindex) >= 50)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return INTERPOSE_NEXT(isImprovable)(job, mat_type, mat_index);
|
||||
}
|
||||
};
|
||||
|
||||
IMPLEMENT_VMETHOD_INTERPOSE(item_hook, isImprovable);
|
||||
|
||||
df::item* find_item(
|
||||
ReagentSource &info,
|
||||
std::vector<df::reaction_reagent*> *in_reag,
|
||||
std::vector<df::item*> *in_items
|
||||
) {
|
||||
if (!info.reagent)
|
||||
return NULL;
|
||||
for (int i = in_items->size(); i >= 0; i--)
|
||||
if ((*in_reag)[i] == info.reagent)
|
||||
return (*in_items)[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct product_hook : improvement_product {
|
||||
typedef improvement_product interpose_base;
|
||||
|
||||
DEFINE_VMETHOD_INTERPOSE(
|
||||
void, produce,
|
||||
(df::unit *unit, std::vector<df::item*> *out_items,
|
||||
std::vector<df::reaction_reagent*> *in_reag,
|
||||
std::vector<df::item*> *in_items,
|
||||
int32_t quantity, int16_t skill,
|
||||
df::historical_entity *entity, df::world_site *site)
|
||||
) {
|
||||
if (auto product = products[this])
|
||||
{
|
||||
auto object = find_item(product->object, in_reag, in_items);
|
||||
auto material = find_item(product->material, in_reag, in_items);
|
||||
|
||||
if (object && (material || !product->material.reagent))
|
||||
{
|
||||
using namespace df::enums::improvement_type;
|
||||
|
||||
int mattype, matindex;
|
||||
find_material(&mattype, &matindex, material, product->material);
|
||||
|
||||
df::matter_state state = matter_state::Liquid;
|
||||
|
||||
switch (improvement_type)
|
||||
{
|
||||
case COVERED:
|
||||
if (flags.is_set(reaction_product_improvement_flags::GLAZED))
|
||||
state = matter_state::Solid;
|
||||
break;
|
||||
case BANDS:
|
||||
state = matter_state::Paste;
|
||||
break;
|
||||
case SPIKES:
|
||||
state = matter_state::Powder;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
int rating = unit ? Units::getEffectiveSkill(unit, df::job_skill(skill)) : 0;
|
||||
int size = int(probability*(1.0f + 0.06f*rating)); // +90% at legendary
|
||||
|
||||
object->addContaminant(
|
||||
mattype, matindex, state,
|
||||
object->getTemperature(),
|
||||
size, -1,
|
||||
0x8000 // not washed by water, and 'clean items' safe.
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
INTERPOSE_NEXT(produce)(unit, out_items, in_reag, in_items, quantity, skill, entity, site);
|
||||
}
|
||||
};
|
||||
|
||||
IMPLEMENT_VMETHOD_INTERPOSE(product_hook, produce);
|
||||
|
||||
/*
|
||||
* Scan raws for matching reactions.
|
||||
*/
|
||||
|
||||
static void find_reagent(
|
||||
color_ostream &out, ReagentSource &info, df::reaction *react, std::string name
|
||||
) {
|
||||
for (size_t i = 0; i < react->reagents.size(); i++)
|
||||
{
|
||||
if (react->reagents[i]->code != name)
|
||||
continue;
|
||||
|
||||
info.idx = i;
|
||||
info.reagent = react->reagents[i];
|
||||
return;
|
||||
}
|
||||
|
||||
out.printerr("Invalid reagent name '%s' in '%s'\n", name.c_str(), react->code.c_str());
|
||||
}
|
||||
|
||||
static void parse_product(
|
||||
color_ostream &out, ProductInfo &info, df::reaction *react, improvement_product *prod
|
||||
) {
|
||||
using namespace df::enums::reaction_product_improvement_flags;
|
||||
|
||||
info.react = react;
|
||||
info.product = prod;
|
||||
|
||||
find_reagent(out, info.object, react, prod->target_reagent);
|
||||
|
||||
auto ritem = strict_virtual_cast<df::reaction_reagent_itemst>(info.object.reagent);
|
||||
if (ritem)
|
||||
ritem->flags1.bits.improvable = true;
|
||||
|
||||
info.material.mat_type = prod->mat_type;
|
||||
info.material.mat_index = prod->mat_index;
|
||||
|
||||
if (prod->flags.is_set(GET_MATERIAL_PRODUCT))
|
||||
{
|
||||
find_reagent(out, info.material, react, prod->get_material.reagent_code);
|
||||
|
||||
info.material.product = true;
|
||||
info.material.product_name = prod->get_material.product_code;
|
||||
}
|
||||
else if (prod->flags.is_set(GET_MATERIAL_SAME))
|
||||
{
|
||||
find_reagent(out, info.material, react, prod->get_material.reagent_code);
|
||||
}
|
||||
}
|
||||
|
||||
static bool find_reactions(color_ostream &out)
|
||||
{
|
||||
reactions.clear();
|
||||
products.clear();
|
||||
|
||||
auto &rlist = world->raws.reactions;
|
||||
|
||||
for (size_t i = 0; i < rlist.size(); i++)
|
||||
{
|
||||
if (!is_add_spatter(rlist[i]->code))
|
||||
continue;
|
||||
|
||||
reactions[rlist[i]->code].react = rlist[i];
|
||||
}
|
||||
|
||||
for (auto it = reactions.begin(); it != reactions.end(); ++it)
|
||||
{
|
||||
auto &prod = it->second.react->products;
|
||||
auto &out_prod = it->second.products;
|
||||
|
||||
for (size_t i = 0; i < prod.size(); i++)
|
||||
{
|
||||
auto itprod = strict_virtual_cast<improvement_product>(prod[i]);
|
||||
if (!itprod) continue;
|
||||
|
||||
out_prod.push_back(ProductInfo());
|
||||
parse_product(out, out_prod.back(), it->second.react, itprod);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < prod.size(); i++)
|
||||
{
|
||||
if (out_prod[i].isValid())
|
||||
products[out_prod[i].product] = &out_prod[i];
|
||||
}
|
||||
}
|
||||
|
||||
return !products.empty();
|
||||
}
|
||||
|
||||
static void enable_hooks(bool enable)
|
||||
{
|
||||
INTERPOSE_HOOK(item_hook, isImprovable).apply(enable);
|
||||
INTERPOSE_HOOK(product_hook, produce).apply(enable);
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
|
||||
{
|
||||
switch (event) {
|
||||
case SC_MAP_LOADED:
|
||||
if (find_reactions(out))
|
||||
{
|
||||
out.print("Detected spatter add reactions - enabling plugin.\n");
|
||||
enable_hooks(true);
|
||||
}
|
||||
else
|
||||
enable_hooks(false);
|
||||
break;
|
||||
case SC_MAP_UNLOADED:
|
||||
enable_hooks(false);
|
||||
reactions.clear();
|
||||
products.clear();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
|
||||
{
|
||||
if (Core::getInstance().isMapLoaded())
|
||||
plugin_onstatechange(out, SC_MAP_LOADED);
|
||||
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
|
||||
{
|
||||
enable_hooks(false);
|
||||
return CR_OK;
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
--- ../objects.old/entity_default.txt 2012-09-17 17:59:28.853898702 +0400
|
||||
+++ entity_default.txt 2012-09-17 17:59:28.684899429 +0400
|
||||
@@ -49,6 +49,7 @@
|
||||
[TRAPCOMP:ITEM_TRAPCOMP_SPIKEDBALL]
|
||||
[TRAPCOMP:ITEM_TRAPCOMP_LARGESERRATEDDISC]
|
||||
[TRAPCOMP:ITEM_TRAPCOMP_MENACINGSPIKE]
|
||||
+ [TRAPCOMP:ITEM_TRAPCOMP_STEAM_PISTON]
|
||||
[TOY:ITEM_TOY_PUZZLEBOX]
|
||||
[TOY:ITEM_TOY_BOAT]
|
||||
[TOY:ITEM_TOY_HAMMER]
|
||||
@@ -204,6 +205,8 @@
|
||||
[PERMITTED_JOB:WAX_WORKER]
|
||||
[PERMITTED_BUILDING:SOAP_MAKER]
|
||||
[PERMITTED_BUILDING:SCREW_PRESS]
|
||||
+ [PERMITTED_BUILDING:STEAM_ENGINE]
|
||||
+ [PERMITTED_BUILDING:MAGMA_STEAM_ENGINE]
|
||||
[PERMITTED_REACTION:TAN_A_HIDE]
|
||||
[PERMITTED_REACTION:RENDER_FAT]
|
||||
[PERMITTED_REACTION:MAKE_SOAP_FROM_TALLOW]
|
||||
@@ -248,6 +251,9 @@
|
||||
[PERMITTED_REACTION:ROSE_GOLD_MAKING]
|
||||
[PERMITTED_REACTION:BISMUTH_BRONZE_MAKING]
|
||||
[PERMITTED_REACTION:ADAMANTINE_WAFERS]
|
||||
+ [PERMITTED_REACTION:STOKE_BOILER]
|
||||
+ [PERMITTED_REACTION:SPATTER_ADD_WEAPON_EXTRACT]
|
||||
+ [PERMITTED_REACTION:SPATTER_ADD_AMMO_EXTRACT]
|
||||
[WORLD_CONSTRUCTION:TUNNEL]
|
||||
[WORLD_CONSTRUCTION:BRIDGE]
|
||||
[WORLD_CONSTRUCTION:ROAD]
|
@ -0,0 +1,10 @@
|
||||
--- ../objects.old/material_template_default.txt 2012-09-17 17:59:28.907898469 +0400
|
||||
+++ material_template_default.txt 2012-09-17 17:59:28.695899382 +0400
|
||||
@@ -2374,6 +2374,7 @@
|
||||
[MAX_EDGE:500]
|
||||
[ABSORPTION:100]
|
||||
[LIQUID_MISC_CREATURE]
|
||||
+ [REACTION_CLASS:CREATURE_EXTRACT]
|
||||
[ROTS]
|
||||
|
||||
This is for creatures that are "made of fire". Right now there isn't a good format for that.
|
@ -0,0 +1,154 @@
|
||||
reaction_spatter
|
||||
|
||||
[OBJECT:REACTION]
|
||||
|
||||
Reaction name must start with 'SPATTER_ADD_':
|
||||
|
||||
[REACTION:SPATTER_ADD_OBJECT_LIQUID]
|
||||
[NAME:coat object with liquid]
|
||||
[ADVENTURE_MODE_ENABLED]
|
||||
[SKILL:WAX_WORKING]
|
||||
[REAGENT:extract:150:LIQUID_MISC:NONE:NONE:NONE]
|
||||
[MIN_DIMENSION:150]
|
||||
[DOES_NOT_DETERMINE_PRODUCT_AMOUNT]
|
||||
[REAGENT:extract container:1:NONE:NONE:NONE:NONE]
|
||||
[CONTAINS:extract]
|
||||
[PRESERVE_REAGENT]
|
||||
[DOES_NOT_DETERMINE_PRODUCT_AMOUNT]
|
||||
The object to improve must be after the input mat, so that it is known:
|
||||
[REAGENT:object:1:NONE:NONE:NONE:NONE]
|
||||
[PRESERVE_REAGENT]
|
||||
[DOES_NOT_DETERMINE_PRODUCT_AMOUNT]
|
||||
Need some excuse why the spatter is water-resistant:
|
||||
[REAGENT:grease:1:GLOB:NONE:NONE:NONE][REACTION_CLASS:FAT][UNROTTEN]
|
||||
[DOES_NOT_DETERMINE_PRODUCT_AMOUNT]
|
||||
The probability is used as spatter size; Legendary gives +90%:
|
||||
COVERED = liquid, GLAZED = solid, BANDS = paste, SPIKES = powder
|
||||
[IMPROVEMENT:800:object:COVERED:GET_MATERIAL_FROM_REAGENT:extract:NONE]
|
||||
|
||||
[REACTION:SPATTER_ADD_WEAPON_EXTRACT]
|
||||
[NAME:coat weapon with extract]
|
||||
[BUILDING:CRAFTSMAN:NONE]
|
||||
[SKILL:WAX_WORKING]
|
||||
[REAGENT:extract:150:LIQUID_MISC:NONE:NONE:NONE]
|
||||
[MIN_DIMENSION:150]
|
||||
[REACTION_CLASS:CREATURE_EXTRACT]
|
||||
[DOES_NOT_DETERMINE_PRODUCT_AMOUNT]
|
||||
[REAGENT:extract container:1:NONE:NONE:NONE:NONE]
|
||||
[CONTAINS:extract]
|
||||
[PRESERVE_REAGENT]
|
||||
[DOES_NOT_DETERMINE_PRODUCT_AMOUNT]
|
||||
The object to improve must be after the input mat, so that it is known:
|
||||
[REAGENT:object:1:WEAPON:NONE:NONE:NONE]
|
||||
[PRESERVE_REAGENT]
|
||||
[DOES_NOT_DETERMINE_PRODUCT_AMOUNT]
|
||||
Need some excuse why the spatter is water-resistant:
|
||||
[REAGENT:grease:1:GLOB:NONE:NONE:NONE][REACTION_CLASS:TALLOW][UNROTTEN]
|
||||
[DOES_NOT_DETERMINE_PRODUCT_AMOUNT]
|
||||
The probability is used as spatter size; Legendary gives +90%:
|
||||
COVERED = liquid, GLAZED = solid, BANDS = paste, SPIKES = powder
|
||||
[IMPROVEMENT:800:object:COVERED:GET_MATERIAL_FROM_REAGENT:extract:NONE]
|
||||
|
||||
[REACTION:SPATTER_ADD_AMMO_EXTRACT]
|
||||
[NAME:coat ammo with extract]
|
||||
[BUILDING:CRAFTSMAN:NONE]
|
||||
[SKILL:WAX_WORKING]
|
||||
[REAGENT:extract:50:LIQUID_MISC:NONE:NONE:NONE]
|
||||
[MIN_DIMENSION:50]
|
||||
[REACTION_CLASS:CREATURE_EXTRACT]
|
||||
[DOES_NOT_DETERMINE_PRODUCT_AMOUNT]
|
||||
[REAGENT:extract container:1:NONE:NONE:NONE:NONE]
|
||||
[CONTAINS:extract]
|
||||
[PRESERVE_REAGENT]
|
||||
[DOES_NOT_DETERMINE_PRODUCT_AMOUNT]
|
||||
The object to improve must be after the input mat, so that it is known:
|
||||
[REAGENT:object:1:AMMO:NONE:NONE:NONE]
|
||||
[PRESERVE_REAGENT]
|
||||
[MIN_DIMENSION:5] don't waste materials on single bolts
|
||||
[DOES_NOT_DETERMINE_PRODUCT_AMOUNT]
|
||||
Need some excuse why the spatter is water-resistant:
|
||||
[REAGENT:grease:1:GLOB:NONE:NONE:NONE][REACTION_CLASS:TALLOW][UNROTTEN]
|
||||
[DOES_NOT_DETERMINE_PRODUCT_AMOUNT]
|
||||
The probability is used as spatter size; Legendary gives +90%:
|
||||
COVERED = liquid, GLAZED = solid, BANDS = paste, SPIKES = powder
|
||||
[IMPROVEMENT:200:object:COVERED:GET_MATERIAL_FROM_REAGENT:extract:NONE]
|
||||
|
||||
[REACTION:SPATTER_ADD_WEAPON_GCS]
|
||||
[NAME:coat weapon with GCS venom]
|
||||
[BUILDING:CRAFTSMAN:NONE]
|
||||
[SKILL:WAX_WORKING]
|
||||
[REAGENT:extract:150:LIQUID_MISC:NONE:CAVE_SPIDER_GIANT:POISON]
|
||||
[MIN_DIMENSION:150]
|
||||
[REACTION_CLASS:CREATURE_EXTRACT]
|
||||
[REAGENT:extract container:1:NONE:NONE:NONE:NONE]
|
||||
[CONTAINS:extract]
|
||||
[PRESERVE_REAGENT]
|
||||
[DOES_NOT_DETERMINE_PRODUCT_AMOUNT]
|
||||
The object to improve must be after the input mat, so that it is known:
|
||||
[REAGENT:object:1:WEAPON:NONE:NONE:NONE]
|
||||
[PRESERVE_REAGENT]
|
||||
Need some excuse why the spatter is water-resistant:
|
||||
[REAGENT:grease:1:GLOB:NONE:NONE:NONE][REACTION_CLASS:TALLOW][UNROTTEN]
|
||||
The probability is used as spatter size; Legendary gives +90%:
|
||||
COVERED = liquid, GLAZED = solid, BANDS = paste, SPIKES = powder
|
||||
[IMPROVEMENT:800:object:COVERED:GET_MATERIAL_FROM_REAGENT:extract:NONE]
|
||||
|
||||
[REACTION:SPATTER_ADD_AMMO_GCS]
|
||||
[NAME:coat ammo with GCS venom]
|
||||
[BUILDING:CRAFTSMAN:NONE]
|
||||
[SKILL:WAX_WORKING]
|
||||
[REAGENT:extract:50:LIQUID_MISC:NONE:CAVE_SPIDER_GIANT:POISON]
|
||||
[MIN_DIMENSION:50]
|
||||
[REACTION_CLASS:CREATURE_EXTRACT]
|
||||
[REAGENT:extract container:1:NONE:NONE:NONE:NONE]
|
||||
[CONTAINS:extract]
|
||||
[PRESERVE_REAGENT]
|
||||
[DOES_NOT_DETERMINE_PRODUCT_AMOUNT]
|
||||
The object to improve must be after the input mat, so that it is known:
|
||||
[REAGENT:object:1:AMMO:NONE:NONE:NONE]
|
||||
[PRESERVE_REAGENT]
|
||||
Need some excuse why the spatter is water-resistant:
|
||||
[REAGENT:grease:1:GLOB:NONE:NONE:NONE][REACTION_CLASS:TALLOW][UNROTTEN]
|
||||
The probability is used as spatter size; Legendary gives +90%:
|
||||
COVERED = liquid, GLAZED = solid, BANDS = paste, SPIKES = powder
|
||||
[IMPROVEMENT:200:object:COVERED:GET_MATERIAL_FROM_REAGENT:extract:NONE]
|
||||
|
||||
[REACTION:SPATTER_ADD_WEAPON_GDS]
|
||||
[NAME:coat weapon with GDS venom]
|
||||
[BUILDING:CRAFTSMAN:NONE]
|
||||
[SKILL:WAX_WORKING]
|
||||
[REAGENT:extract:150:LIQUID_MISC:NONE:SCORPION_DESERT_GIANT:POISON]
|
||||
[MIN_DIMENSION:150]
|
||||
[REACTION_CLASS:CREATURE_EXTRACT]
|
||||
[REAGENT:extract container:1:NONE:NONE:NONE:NONE]
|
||||
[CONTAINS:extract]
|
||||
[PRESERVE_REAGENT]
|
||||
[DOES_NOT_DETERMINE_PRODUCT_AMOUNT]
|
||||
The object to improve must be after the input mat, so that it is known:
|
||||
[REAGENT:object:1:WEAPON:NONE:NONE:NONE]
|
||||
[PRESERVE_REAGENT]
|
||||
Need some excuse why the spatter is water-resistant:
|
||||
[REAGENT:grease:1:GLOB:NONE:NONE:NONE][REACTION_CLASS:TALLOW][UNROTTEN]
|
||||
The probability is used as spatter size; Legendary gives +90%:
|
||||
COVERED = liquid, GLAZED = solid, BANDS = paste, SPIKES = powder
|
||||
[IMPROVEMENT:800:object:COVERED:GET_MATERIAL_FROM_REAGENT:extract:NONE]
|
||||
|
||||
[REACTION:SPATTER_ADD_AMMO_GDS]
|
||||
[NAME:coat ammo with GDS venom]
|
||||
[BUILDING:CRAFTSMAN:NONE]
|
||||
[SKILL:WAX_WORKING]
|
||||
[REAGENT:extract:50:LIQUID_MISC:NONE:SCORPION_DESERT_GIANT:POISON]
|
||||
[MIN_DIMENSION:50]
|
||||
[REACTION_CLASS:CREATURE_EXTRACT]
|
||||
[REAGENT:extract container:1:NONE:NONE:NONE:NONE]
|
||||
[CONTAINS:extract]
|
||||
[PRESERVE_REAGENT]
|
||||
[DOES_NOT_DETERMINE_PRODUCT_AMOUNT]
|
||||
The object to improve must be after the input mat, so that it is known:
|
||||
[REAGENT:object:1:AMMO:NONE:NONE:NONE]
|
||||
[PRESERVE_REAGENT]
|
||||
Need some excuse why the spatter is water-resistant:
|
||||
[REAGENT:grease:1:GLOB:NONE:NONE:NONE][REACTION_CLASS:TALLOW][UNROTTEN]
|
||||
The probability is used as spatter size; Legendary gives +90%:
|
||||
COVERED = liquid, GLAZED = solid, BANDS = paste, SPIKES = powder
|
||||
[IMPROVEMENT:200:object:COVERED:GET_MATERIAL_FROM_REAGENT:extract:NONE]
|
Loading…
Reference in New Issue