Merge branch 'master' of http://github.com/peterix/dfhack
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 5.1 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 7.5 KiB |
After Width: | Height: | Size: 7.4 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 6.5 KiB |
After Width: | Height: | Size: 6.0 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 6.8 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 6.6 KiB |
After Width: | Height: | Size: 7.6 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 5.6 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 5.4 KiB |
After Width: | Height: | Size: 6.3 KiB |
@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
#ifndef EVENT_MANAGER_H_INCLUDED
|
||||
#define EVENT_MANAGER_H_INCLUDED
|
||||
|
||||
#include "Core.h"
|
||||
#include "Export.h"
|
||||
#include "ColorText.h"
|
||||
#include "PluginManager.h"
|
||||
#include "Console.h"
|
||||
|
||||
namespace DFHack {
|
||||
namespace EventManager {
|
||||
namespace EventType {
|
||||
enum EventType {
|
||||
TICK,
|
||||
JOB_INITIATED,
|
||||
JOB_COMPLETED,
|
||||
UNIT_DEATH,
|
||||
ITEM_CREATED,
|
||||
BUILDING,
|
||||
CONSTRUCTION,
|
||||
SYNDROME,
|
||||
INVASION,
|
||||
EVENT_MAX
|
||||
};
|
||||
}
|
||||
|
||||
struct EventHandler {
|
||||
void (*eventHandler)(color_ostream&, void*); //called when the event happens
|
||||
int32_t freq;
|
||||
|
||||
EventHandler(void (*eventHandlerIn)(color_ostream&, void*), int32_t freqIn): eventHandler(eventHandlerIn), freq(freqIn) {
|
||||
}
|
||||
|
||||
bool operator==(EventHandler& handle) const {
|
||||
return eventHandler == handle.eventHandler && freq == handle.freq;
|
||||
}
|
||||
bool operator!=(EventHandler& handle) const {
|
||||
return !( *this == handle);
|
||||
}
|
||||
};
|
||||
|
||||
struct SyndromeData {
|
||||
int32_t unitId;
|
||||
int32_t syndromeIndex;
|
||||
SyndromeData(int32_t unitId_in, int32_t syndromeIndex_in): unitId(unitId_in), syndromeIndex(syndromeIndex_in) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
DFHACK_EXPORT void registerListener(EventType::EventType e, EventHandler handler, Plugin* plugin);
|
||||
DFHACK_EXPORT void registerTick(EventHandler handler, int32_t when, Plugin* plugin, bool absolute=false);
|
||||
DFHACK_EXPORT void unregister(EventType::EventType e, EventHandler handler, Plugin* plugin);
|
||||
DFHACK_EXPORT void unregisterAll(Plugin* plugin);
|
||||
void manageEvents(color_ostream& out);
|
||||
void onStateChange(color_ostream& out, state_change_event event);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,70 +0,0 @@
|
||||
/*
|
||||
https://github.com/peterix/dfhack
|
||||
Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any
|
||||
damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it and
|
||||
redistribute it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must
|
||||
not claim that you wrote the original software. If you use this
|
||||
software in a product, an acknowledgment in the product documentation
|
||||
would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and
|
||||
must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef CL_MOD_VEGETATION
|
||||
#define CL_MOD_VEGETATION
|
||||
/**
|
||||
* \defgroup grp_vegetation Vegetation : stuff that grows and gets cut down or trampled by dwarves
|
||||
* @ingroup grp_modules
|
||||
*/
|
||||
|
||||
#include "Export.h"
|
||||
#include "DataDefs.h"
|
||||
#include "df/plant.h"
|
||||
|
||||
namespace DFHack
|
||||
{
|
||||
namespace Vegetation
|
||||
{
|
||||
const uint32_t sapling_to_tree_threshold = 120 * 28 * 12 * 3; // 3 years
|
||||
|
||||
// "Simplified" copy of plant
|
||||
struct t_plant {
|
||||
df::language_name name;
|
||||
df::plant_flags flags;
|
||||
int16_t material;
|
||||
df::coord pos;
|
||||
int32_t grow_counter;
|
||||
uint16_t temperature_1;
|
||||
uint16_t temperature_2;
|
||||
int32_t is_burning;
|
||||
int32_t hitpoints;
|
||||
int16_t update_order;
|
||||
//std::vector<void *> unk1;
|
||||
//int32_t unk2;
|
||||
//uint16_t temperature_3;
|
||||
//uint16_t temperature_4;
|
||||
//uint16_t temperature_5;
|
||||
// Pointer to original object, in case you want to modify it
|
||||
df::plant *origin;
|
||||
};
|
||||
|
||||
DFHACK_EXPORT bool isValid();
|
||||
DFHACK_EXPORT uint32_t getCount();
|
||||
DFHACK_EXPORT df::plant * getPlant(const int32_t index);
|
||||
DFHACK_EXPORT bool copyPlant (const int32_t index, t_plant &out);
|
||||
}
|
||||
}
|
||||
#endif
|
@ -0,0 +1,121 @@
|
||||
-- Simple binary patch with IDA dif file support.
|
||||
|
||||
local function load_patch(name)
|
||||
local filename = name
|
||||
if not string.match(filename, '[./\\]') then
|
||||
filename = dfhack.getHackPath()..'/patches/'..dfhack.getDFVersion()..'/'..name..'.dif'
|
||||
end
|
||||
|
||||
local file, err = io.open(filename, 'r')
|
||||
if not file then
|
||||
if string.match(err, ': No such file or directory') then
|
||||
return nil, 'patch not found'
|
||||
end
|
||||
end
|
||||
|
||||
local old_bytes = {}
|
||||
local new_bytes = {}
|
||||
|
||||
for line in file:lines() do
|
||||
if string.match(line, '^%x+:') then
|
||||
local offset, oldv, newv = string.match(line, '^(%x+):%s*(%x+)%s+(%x+)%s*$')
|
||||
if not offset then
|
||||
file:close()
|
||||
return nil, 'could not parse: '..line
|
||||
end
|
||||
|
||||
offset, oldv, newv = tonumber(offset,16), tonumber(oldv,16), tonumber(newv,16)
|
||||
if oldv > 255 or newv > 255 then
|
||||
file:close()
|
||||
return nil, 'invalid byte values: '..line
|
||||
end
|
||||
|
||||
old_bytes[offset] = oldv
|
||||
new_bytes[offset] = newv
|
||||
end
|
||||
end
|
||||
|
||||
return { name = name, old_bytes = old_bytes, new_bytes = new_bytes }
|
||||
end
|
||||
|
||||
local function rebase_table(input)
|
||||
local output = {}
|
||||
local base = dfhack.internal.getImageBase()
|
||||
for k,v in pairs(input) do
|
||||
local offset = dfhack.internal.adjustOffset(k)
|
||||
if not offset then
|
||||
return nil, string.format('invalid offset: %x', k)
|
||||
end
|
||||
output[base + offset] = v
|
||||
end
|
||||
return output
|
||||
end
|
||||
|
||||
local function rebase_patch(patch)
|
||||
local nold, err = rebase_table(patch.old_bytes)
|
||||
if not nold then return nil, err end
|
||||
local nnew, err = rebase_table(patch.new_bytes)
|
||||
if not nnew then return nil, err end
|
||||
return { name = patch.name, old_bytes = nold, new_bytes = nnew }
|
||||
end
|
||||
|
||||
BinaryPatch = defclass(BinaryPatch)
|
||||
|
||||
BinaryPatch.ATTRS {
|
||||
name = DEFAULT_NIL,
|
||||
old_bytes = DEFAULT_NIL,
|
||||
new_bytes = DEFAULT_NIL,
|
||||
}
|
||||
|
||||
function load_dif_file(name)
|
||||
local patch, err = load_patch(name)
|
||||
if not patch then return nil, err end
|
||||
|
||||
local rpatch, err = rebase_patch(patch)
|
||||
if not rpatch then return nil, err end
|
||||
|
||||
return BinaryPatch(rpatch)
|
||||
end
|
||||
|
||||
function BinaryPatch:status()
|
||||
local old_ok, err, addr = dfhack.internal.patchBytes({}, self.old_bytes)
|
||||
if old_ok then
|
||||
return 'removed'
|
||||
elseif dfhack.internal.patchBytes({}, self.new_bytes) then
|
||||
return 'applied'
|
||||
else
|
||||
return 'conflict', addr
|
||||
end
|
||||
end
|
||||
|
||||
function BinaryPatch:isApplied()
|
||||
return dfhack.internal.patchBytes({}, self.new_bytes)
|
||||
end
|
||||
|
||||
function BinaryPatch:apply()
|
||||
local ok, err, addr = dfhack.internal.patchBytes(self.new_bytes, self.old_bytes)
|
||||
if ok then
|
||||
return true, 'applied the patch'
|
||||
elseif dfhack.internal.patchBytes({}, self.new_bytes) then
|
||||
return true, 'patch is already applied'
|
||||
else
|
||||
return false, string.format('conflict at address %x', addr)
|
||||
end
|
||||
end
|
||||
|
||||
function BinaryPatch:isRemoved()
|
||||
return dfhack.internal.patchBytes({}, self.old_bytes)
|
||||
end
|
||||
|
||||
function BinaryPatch:remove()
|
||||
local ok, err, addr = dfhack.internal.patchBytes(self.old_bytes, self.new_bytes)
|
||||
if ok then
|
||||
return true, 'removed the patch'
|
||||
elseif dfhack.internal.patchBytes({}, self.old_bytes) then
|
||||
return true, 'patch is already removed'
|
||||
else
|
||||
return false, string.format('conflict at address %x', addr)
|
||||
end
|
||||
end
|
||||
|
||||
return _ENV
|
@ -0,0 +1,591 @@
|
||||
local _ENV = mkmodule('dfhack.workshops')
|
||||
|
||||
local utils = require 'utils'
|
||||
|
||||
input_filter_defaults = {
|
||||
item_type = -1,
|
||||
item_subtype = -1,
|
||||
mat_type = -1,
|
||||
mat_index = -1,
|
||||
flags1 = {},
|
||||
-- Instead of noting those that allow artifacts, mark those that forbid them.
|
||||
-- Leaves actually enabling artifacts to the discretion of the API user,
|
||||
-- which is the right thing because unlike the game UI these filters are
|
||||
-- used in a way that does not give the user a chance to choose manually.
|
||||
flags2 = { allow_artifact = true },
|
||||
flags3 = {},
|
||||
flags4 = 0,
|
||||
flags5 = 0,
|
||||
reaction_class = '',
|
||||
has_material_reaction_product = '',
|
||||
metal_ore = -1,
|
||||
min_dimension = -1,
|
||||
has_tool_use = -1,
|
||||
quantity = 1
|
||||
}
|
||||
local fuel={item_type=df.item_type.BAR,mat_type=df.builtin_mats.COAL}
|
||||
jobs_furnace={
|
||||
[df.furnace_type.Smelter]={
|
||||
{
|
||||
name="Melt metal object",
|
||||
items={fuel,{flags2={allow_melt_dump=true}}},--also maybe melt_designated
|
||||
job_fields={job_type=df.job_type.MeltMetalObject}
|
||||
}
|
||||
},
|
||||
[df.furnace_type.MagmaSmelter]={
|
||||
{
|
||||
name="Melt metal object",
|
||||
items={{flags2={allow_melt_dump=true}}},--also maybe melt_designated
|
||||
job_fields={job_type=df.job_type.MeltMetalObject}
|
||||
}
|
||||
},
|
||||
--[[ [df.furnace_type.MetalsmithsForge]={
|
||||
unpack(concat(furnaces,mechanism,anvil,crafts,coins,flask))
|
||||
|
||||
},
|
||||
]]
|
||||
--MetalsmithsForge,
|
||||
--MagmaForge
|
||||
--[[
|
||||
forges:
|
||||
weapons and ammo-> from raws...
|
||||
armor -> raws
|
||||
furniture -> builtins?
|
||||
siege eq-> builtin (only balista head)
|
||||
trap eq -> from raws+ mechanisms
|
||||
other object-> anvil, crafts, goblets,toys,instruments,nestbox... (raws?) flask, coins,stud with iron
|
||||
metal clothing-> raws???
|
||||
]]
|
||||
[df.furnace_type.GlassFurnace]={
|
||||
{
|
||||
name="collect sand",
|
||||
items={},
|
||||
job_fields={job_type=df.job_type.CollectSand}
|
||||
},
|
||||
--glass crafts x3
|
||||
},
|
||||
[df.furnace_type.WoodFurnace]={
|
||||
defaults={item_type=df.item_type.WOOD,vector_id=df.job_item_vector_id.WOOD},
|
||||
{
|
||||
name="make charcoal",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.MakeCharcoal}
|
||||
},
|
||||
{
|
||||
name="make ash",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.MakeAsh}
|
||||
}
|
||||
},
|
||||
[df.furnace_type.Kiln]={
|
||||
{
|
||||
name="collect clay",
|
||||
items={},
|
||||
job_fields={job_type=df.job_type.CollectClay}
|
||||
}
|
||||
},
|
||||
}
|
||||
jobs_workshop={
|
||||
|
||||
[df.workshop_type.Jewelers]={
|
||||
{
|
||||
name="cut gems",
|
||||
items={{item_type=df.item_type.ROUGH,flags1={unrotten=true}}},
|
||||
job_fields={job_type=df.job_type.CutGems}
|
||||
},
|
||||
{
|
||||
name="encrust finished goods with gems",
|
||||
items={{item_type=df.item_type.SMALLGEM},{flags1={improvable=true,finished_goods=true}}},
|
||||
job_fields={job_type=df.job_type.EncrustWithGems}
|
||||
},
|
||||
{
|
||||
name="encrust ammo with gems",
|
||||
items={{item_type=df.item_type.SMALLGEM},{flags1={improvable=true,ammo=true}}},
|
||||
job_fields={job_type=df.job_type.EncrustWithGems}
|
||||
},
|
||||
{
|
||||
name="encrust furniture with gems",
|
||||
items={{item_type=df.item_type.SMALLGEM},{flags1={improvable=true,furniture=true}}},
|
||||
job_fields={job_type=df.job_type.EncrustWithGems}
|
||||
},
|
||||
},
|
||||
[df.workshop_type.Fishery]={
|
||||
{
|
||||
name="prepare raw fish",
|
||||
items={{item_type=df.item_type.FISH_RAW,flags1={unrotten=true}}},
|
||||
job_fields={job_type=df.job_type.PrepareRawFish}
|
||||
},
|
||||
{
|
||||
name="extract from raw fish",
|
||||
items={{flags1={unrotten=true,extract_bearing_fish=true}},{item_type=df.item_type.FLASK,flags1={empty=true,glass=true}}},
|
||||
job_fields={job_type=df.job_type.ExtractFromRawFish}
|
||||
},
|
||||
{
|
||||
name="catch live fish",
|
||||
items={},
|
||||
job_fields={job_type=df.job_type.CatchLiveFish}
|
||||
}, -- no items?
|
||||
},
|
||||
[df.workshop_type.Still]={
|
||||
{
|
||||
name="brew drink",
|
||||
items={{flags1={distillable=true},vector_id=22},{flags1={empty=true},flags3={food_storage=true}}},
|
||||
job_fields={job_type=df.job_type.BrewDrink}
|
||||
},
|
||||
{
|
||||
name="extract from plants",
|
||||
items={{item_type=df.item_type.PLANT,flags1={unrotten=true,extract_bearing_plant=true}},{item_type=df.item_type.FLASK,flags1={empty=true}}},
|
||||
job_fields={job_type=df.job_type.ExtractFromPlants}
|
||||
},
|
||||
--mead from raws?
|
||||
},
|
||||
[df.workshop_type.Masons]={
|
||||
defaults={item_type=df.item_type.BOULDER,item_subtype=-1,vector_id=df.job_item_vector_id.BOULDER, mat_type=0,mat_index=-1,flags3={hard=true}},--flags2={non_economic=true},
|
||||
{
|
||||
name="construct armor stand",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructArmorStand}
|
||||
},
|
||||
|
||||
{
|
||||
name="construct blocks",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructBlocks}
|
||||
},
|
||||
{
|
||||
name="construct throne",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructThrone}
|
||||
},
|
||||
{
|
||||
name="construct coffin",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructCoffin}
|
||||
},
|
||||
{
|
||||
name="construct door",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructDoor}
|
||||
},
|
||||
{
|
||||
name="construct floodgate",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructFloodgate}
|
||||
},
|
||||
{
|
||||
name="construct hatch cover",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructHatchCover}
|
||||
},
|
||||
{
|
||||
name="construct grate",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructGrate}
|
||||
},
|
||||
{
|
||||
name="construct cabinet",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructCabinet}
|
||||
},
|
||||
{
|
||||
name="construct chest",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructChest}
|
||||
},
|
||||
{
|
||||
name="construct statue",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructStatue}
|
||||
},
|
||||
{
|
||||
name="construct slab",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructSlab}
|
||||
},
|
||||
{
|
||||
name="construct table",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructTable}
|
||||
},
|
||||
{
|
||||
name="construct weapon rack",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructWeaponRack}
|
||||
},
|
||||
{
|
||||
name="construct quern",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructQuern}
|
||||
},
|
||||
{
|
||||
name="construct millstone",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructMillstone}
|
||||
},
|
||||
},
|
||||
[df.workshop_type.Carpenters]={
|
||||
--training weapons, wooden shields
|
||||
defaults={item_type=df.item_type.WOOD,vector_id=df.job_item_vector_id.WOOD},
|
||||
|
||||
{
|
||||
name="make barrel",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.MakeBarrel}
|
||||
},
|
||||
|
||||
{
|
||||
name="make bucket",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.MakeBucket}
|
||||
},
|
||||
{
|
||||
name="make animal trap",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.MakeAnimalTrap}
|
||||
},
|
||||
{
|
||||
name="make cage",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.MakeCage}
|
||||
},
|
||||
{
|
||||
name="construct bed",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructBed}
|
||||
},
|
||||
{
|
||||
name="construct bin",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructBin}
|
||||
},
|
||||
{
|
||||
name="construct armor stand",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructArmorStand}
|
||||
},
|
||||
{
|
||||
name="construct blocks",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructBlocks}
|
||||
},
|
||||
{
|
||||
name="construct throne",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructThrone}
|
||||
},
|
||||
{
|
||||
name="construct coffin",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructCoffin}
|
||||
},
|
||||
{
|
||||
name="construct door",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructDoor}
|
||||
},
|
||||
{
|
||||
name="construct floodgate",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructFloodgate}
|
||||
},
|
||||
{
|
||||
name="construct hatch cover",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructHatchCover}
|
||||
},
|
||||
{
|
||||
name="construct grate",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructGrate}
|
||||
},
|
||||
{
|
||||
name="construct cabinet",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructCabinet}
|
||||
},
|
||||
{
|
||||
name="construct chest",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructChest}
|
||||
},
|
||||
{
|
||||
name="construct statue",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructStatue}
|
||||
},
|
||||
{
|
||||
name="construct table",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructTable}
|
||||
},
|
||||
{
|
||||
name="construct weapon rack",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructWeaponRack}
|
||||
},
|
||||
{
|
||||
name="construct splint",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructSplint}
|
||||
},
|
||||
{
|
||||
name="construct crutch",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructCrutch}
|
||||
},
|
||||
},
|
||||
[df.workshop_type.Kitchen]={
|
||||
--mat_type=2,3,4
|
||||
defaults={flags1={unrotten=true,cookable=true}},
|
||||
{
|
||||
name="prepare easy meal",
|
||||
items={{flags1={solid=true}},{}},
|
||||
job_fields={job_type=df.job_type.PrepareMeal,mat_type=2}
|
||||
},
|
||||
{
|
||||
name="prepare fine meal",
|
||||
items={{flags1={solid=true}},{},{}},
|
||||
job_fields={job_type=df.job_type.PrepareMeal,mat_type=3}
|
||||
},
|
||||
{
|
||||
name="prepare lavish meal",
|
||||
items={{flags1={solid=true}},{},{},{}},
|
||||
job_fields={job_type=df.job_type.PrepareMeal,mat_type=4}
|
||||
},
|
||||
},
|
||||
[df.workshop_type.Butchers]={
|
||||
{
|
||||
name="butcher an animal",
|
||||
items={{flags1={butcherable=true,unrotten=true,nearby=true}}},
|
||||
job_fields={job_type=df.job_type.ButcherAnimal}
|
||||
},
|
||||
{
|
||||
name="extract from land animal",
|
||||
items={{flags1={extract_bearing_vermin=true,unrotten=true}},{item_type=df.item_type.FLASK,flags1={empty=true,glass=true}}},
|
||||
job_fields={job_type=df.job_type.ExtractFromLandAnimal}
|
||||
},
|
||||
{
|
||||
name="catch live land animal",
|
||||
items={},
|
||||
job_fields={job_type=df.job_type.CatchLiveLandAnimal}
|
||||
},
|
||||
},
|
||||
[df.workshop_type.Mechanics]={
|
||||
{
|
||||
name="construct mechanisms",
|
||||
items={{item_type=df.item_type.BOULDER,item_subtype=-1,vector_id=df.job_item_vector_id.BOULDER, mat_type=0,mat_index=-1,quantity=1,
|
||||
flags3={hard=true}}},
|
||||
job_fields={job_type=df.job_type.ConstructMechanisms}
|
||||
},
|
||||
{
|
||||
name="construct traction bench",
|
||||
items={{item_type=df.item_type.TABLE},{item_type=df.item_type.MECHANISM},{item_type=df.item_type.CHAIN}},
|
||||
job_fields={job_type=df.job_type.ConstructTractionBench}
|
||||
},
|
||||
},
|
||||
[df.workshop_type.Loom]={
|
||||
{
|
||||
name="weave plant thread cloth",
|
||||
items={{item_type=df.item_type.THREAD,quantity=15000,min_dimension=15000,flags1={collected=true},flags2={plant=true}}},
|
||||
job_fields={job_type=df.job_type.WeaveCloth}
|
||||
},
|
||||
{
|
||||
name="weave silk thread cloth",
|
||||
items={{item_type=df.item_type.THREAD,quantity=15000,min_dimension=15000,flags1={collected=true},flags2={silk=true}}},
|
||||
job_fields={job_type=df.job_type.WeaveCloth}
|
||||
},
|
||||
{
|
||||
name="weave yarn cloth",
|
||||
items={{item_type=df.item_type.THREAD,quantity=15000,min_dimension=15000,flags1={collected=true},flags2={yarn=true}}},
|
||||
job_fields={job_type=df.job_type.WeaveCloth}
|
||||
},
|
||||
{
|
||||
name="weave inorganic cloth",
|
||||
items={{item_type=df.item_type.THREAD,quantity=15000,min_dimension=15000,flags1={collected=true},mat_type=0}},
|
||||
job_fields={job_type=df.job_type.WeaveCloth}
|
||||
},
|
||||
{
|
||||
name="collect webs",
|
||||
items={{item_type=df.item_type.THREAD,quantity=10,min_dimension=10,flags1={undisturbed=true}}},
|
||||
job_fields={job_type=df.job_type.CollectWebs}
|
||||
},
|
||||
},
|
||||
[df.workshop_type.Leatherworks]={
|
||||
defaults={item_type=SKIN_TANNED},
|
||||
{
|
||||
name="construct leather bag",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.ConstructChest}
|
||||
},
|
||||
{
|
||||
name="construct waterskin",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.MakeFlask}
|
||||
},
|
||||
{
|
||||
name="construct backpack",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.MakeBackpack}
|
||||
},
|
||||
{
|
||||
name="construct quiver",
|
||||
items={{}},
|
||||
job_fields={job_type=df.job_type.MakeQuiver}
|
||||
},
|
||||
{
|
||||
name="sew leather image",
|
||||
items={{item_type=-1,flags1={empty=true},flags2={sewn_imageless=true}},{}},
|
||||
job_fields={job_type=df.job_type.SewImage}
|
||||
},
|
||||
},
|
||||
[df.workshop_type.Dyers]={
|
||||
{
|
||||
name="dye thread",
|
||||
items={{item_type=df.item_type.THREAD,quantity=15000,min_dimension=15000,flags1={collected=true},flags2={dyeable=true}},
|
||||
{flags1={unrotten=true},flags2={dye=true}}},
|
||||
job_fields={job_type=df.job_type.DyeThread}
|
||||
},
|
||||
{
|
||||
name="dye cloth",
|
||||
items={{item_type=df.item_type.CLOTH,quantity=10000,min_dimension=10000,flags2={dyeable=true}},
|
||||
{flags1={unrotten=true},flags2={dye=true}}},
|
||||
job_fields={job_type=df.job_type.DyeThread}
|
||||
},
|
||||
},
|
||||
[df.workshop_type.Siege]={
|
||||
{
|
||||
name="construct balista parts",
|
||||
items={{item_type=df.item_type.WOOD}},
|
||||
job_fields={job_type=df.job_type.ConstructBallistaParts}
|
||||
},
|
||||
{
|
||||
name="construct catapult parts",
|
||||
items={{item_type=df.item_type.WOOD}},
|
||||
job_fields={job_type=df.job_type.ConstructCatapultParts}
|
||||
},
|
||||
{
|
||||
name="assemble balista arrow",
|
||||
items={{item_type=df.item_type.WOOD}},
|
||||
job_fields={job_type=df.job_type.AssembleSiegeAmmo}
|
||||
},
|
||||
{
|
||||
name="assemble tipped balista arrow",
|
||||
items={{item_type=df.item_type.WOOD},{item_type=df.item_type.BALLISTAARROWHEAD}},
|
||||
job_fields={job_type=df.job_type.AssembleSiegeAmmo}
|
||||
},
|
||||
},
|
||||
}
|
||||
local function matchIds(bid1,wid1,cid1,bid2,wid2,cid2)
|
||||
if bid1~=-1 and bid2~=-1 and bid1~=bid2 then
|
||||
return false
|
||||
end
|
||||
if wid1~=-1 and wid2~=-1 and wid1~=wid2 then
|
||||
return false
|
||||
end
|
||||
if cid1~=-1 and cid2~=-1 and cid1~=cid2 then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
local function scanRawsReaction(buildingId,workshopId,customId)
|
||||
local ret={}
|
||||
for idx,reaction in ipairs(df.global.world.raws.reactions) do
|
||||
for k,v in pairs(reaction.building.type) do
|
||||
if matchIds(buildingId,workshopId,customId,v,reaction.building.subtype[k],reaction.building.custom[k]) then
|
||||
table.insert(ret,reaction)
|
||||
end
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
local function reagentToJobItem(reagent,react_id,reagentId)
|
||||
local ret_item
|
||||
ret_item=utils.clone_with_default(reagent, input_filter_defaults)
|
||||
ret_item.reaction_id=react_id
|
||||
ret_item.reagent_index=reagentId
|
||||
return ret_item
|
||||
end
|
||||
local function addReactionJobs(ret,bid,wid,cid)
|
||||
local reactions=scanRawsReaction(bid,wid or -1,cid or -1)
|
||||
for idx,react in pairs(reactions) do
|
||||
local job={name=react.name,
|
||||
items={},job_fields={job_type=df.job_type.CustomReaction,reaction_name=react.code}
|
||||
}
|
||||
for reagentId,reagent in pairs(react.reagents) do
|
||||
table.insert(job.items,reagentToJobItem(reagent,idx,reagentId))
|
||||
end
|
||||
if react.flags.FUEL then
|
||||
table.insert(job.items,fuel)
|
||||
end
|
||||
table.insert(ret,job)
|
||||
end
|
||||
end
|
||||
local function scanRawsOres()
|
||||
local ret={}
|
||||
for idx,ore in ipairs(df.global.world.raws.inorganics) do
|
||||
if #ore.metal_ore.mat_index~=0 then
|
||||
ret[idx]=ore
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
local function addSmeltJobs(ret,use_fuel)
|
||||
local ores=scanRawsOres()
|
||||
for idx,ore in pairs(ores) do
|
||||
print("adding:",ore.material.state_name.Solid)
|
||||
printall(ore)
|
||||
local job={name="smelt "..ore.material.state_name.Solid,job_fields={job_type=df.job_type.SmeltOre,mat_type=df.builtin_mats.INORGANIC,mat_index=idx},items={
|
||||
{item_type=df.item_type.BOULDER,mat_type=df.builtin_mats.INORGANIC,mat_index=idx,vector_id=df.job_item_vector_id.BOULDER}}}
|
||||
if use_fuel then
|
||||
table.insert(job.items,fuel)
|
||||
end
|
||||
table.insert(ret,job)
|
||||
end
|
||||
return ret
|
||||
end
|
||||
function getJobs(buildingId,workshopId,customId)
|
||||
local ret={}
|
||||
local c_jobs
|
||||
if buildingId==df.building_type.Workshop then
|
||||
c_jobs=jobs_workshop[workshopId]
|
||||
elseif buildingId==df.building_type.Furnace then
|
||||
c_jobs=jobs_furnace[workshopId]
|
||||
|
||||
if workshopId == df.furnace_type.Smelter or workshopId == df.furnace_type.MagmaSmelter then
|
||||
c_jobs=utils.clone(c_jobs,true)
|
||||
addSmeltJobs(c_jobs,workshopId == df.furnace_type.Smelter)
|
||||
end
|
||||
else
|
||||
return nil
|
||||
end
|
||||
if c_jobs==nil then
|
||||
c_jobs={}
|
||||
else
|
||||
c_jobs=utils.clone(c_jobs,true)
|
||||
end
|
||||
|
||||
addReactionJobs(c_jobs,buildingId,workshopId,customId)
|
||||
for jobId,contents in pairs(c_jobs) do
|
||||
if jobId~="defaults" then
|
||||
local entry={}
|
||||
entry.name=contents.name
|
||||
local lclDefaults=utils.clone(input_filter_defaults,true)
|
||||
if c_jobs.defaults ~=nil then
|
||||
utils.assign(lclDefaults,c_jobs.defaults)
|
||||
end
|
||||
entry.items={}
|
||||
for k,item in pairs(contents.items) do
|
||||
entry.items[k]=utils.clone(lclDefaults,true)
|
||||
utils.assign(entry.items[k],item)
|
||||
end
|
||||
if contents.job_fields~=nil then
|
||||
entry.job_fields={}
|
||||
utils.assign(entry.job_fields,contents.job_fields)
|
||||
end
|
||||
ret[jobId]=entry
|
||||
end
|
||||
end
|
||||
--get jobs, add in from raws
|
||||
return ret
|
||||
end
|
||||
return _ENV
|
@ -0,0 +1,285 @@
|
||||
-- Stock dialog for selecting buildings
|
||||
|
||||
local _ENV = mkmodule('gui.buildings')
|
||||
|
||||
local gui = require('gui')
|
||||
local widgets = require('gui.widgets')
|
||||
local dlg = require('gui.dialogs')
|
||||
local utils = require('utils')
|
||||
|
||||
ARROW = string.char(26)
|
||||
|
||||
WORKSHOP_ABSTRACT={
|
||||
[df.building_type.Civzone]=true,[df.building_type.Stockpile]=true,
|
||||
}
|
||||
WORKSHOP_SPECIAL={
|
||||
[df.building_type.Workshop]=true,[df.building_type.Furnace]=true,[df.building_type.Trap]=true,
|
||||
[df.building_type.Construction]=true,[df.building_type.SiegeEngine]=true
|
||||
}
|
||||
BuildingDialog = defclass(BuildingDialog, gui.FramedScreen)
|
||||
|
||||
BuildingDialog.focus_path = 'BuildingDialog'
|
||||
|
||||
BuildingDialog.ATTRS{
|
||||
prompt = 'Type or select a building from this list',
|
||||
frame_style = gui.GREY_LINE_FRAME,
|
||||
frame_inset = 1,
|
||||
frame_title = 'Select Building',
|
||||
-- new attrs
|
||||
none_caption = 'none',
|
||||
hide_none = false,
|
||||
use_abstract = true,
|
||||
use_workshops = true,
|
||||
use_tool_workshop=true,
|
||||
use_furnace = true,
|
||||
use_construction = true,
|
||||
use_siege = true,
|
||||
use_trap = true,
|
||||
use_custom = true,
|
||||
building_filter = DEFAULT_NIL,
|
||||
on_select = DEFAULT_NIL,
|
||||
on_cancel = DEFAULT_NIL,
|
||||
on_close = DEFAULT_NIL,
|
||||
}
|
||||
|
||||
function BuildingDialog:init(info)
|
||||
self:addviews{
|
||||
widgets.Label{
|
||||
text = {
|
||||
self.prompt, '\n\n',
|
||||
'Category: ', { text = self:cb_getfield('context_str'), pen = COLOR_CYAN }
|
||||
},
|
||||
text_pen = COLOR_WHITE,
|
||||
frame = { l = 0, t = 0 },
|
||||
},
|
||||
widgets.Label{
|
||||
view_id = 'back',
|
||||
visible = false,
|
||||
text = { { key = 'LEAVESCREEN', text = ': Back' } },
|
||||
frame = { r = 0, b = 0 },
|
||||
auto_width = true,
|
||||
},
|
||||
widgets.FilteredList{
|
||||
view_id = 'list',
|
||||
not_found_label = 'No matching buildings',
|
||||
frame = { l = 0, r = 0, t = 4, b = 2 },
|
||||
icon_width = 2,
|
||||
on_submit = self:callback('onSubmitItem'),
|
||||
},
|
||||
widgets.Label{
|
||||
text = { {
|
||||
key = 'SELECT', text = ': Select',
|
||||
disabled = function() return not self.subviews.list:canSubmit() end
|
||||
} },
|
||||
frame = { l = 0, b = 0 },
|
||||
}
|
||||
}
|
||||
self:initBuiltinMode()
|
||||
end
|
||||
|
||||
function BuildingDialog:getWantedFrameSize(rect)
|
||||
return math.max(self.frame_width or 40, #self.prompt), math.min(28, rect.height-8)
|
||||
end
|
||||
|
||||
function BuildingDialog:onDestroy()
|
||||
if self.on_close then
|
||||
self.on_close()
|
||||
end
|
||||
end
|
||||
|
||||
function BuildingDialog:initBuiltinMode()
|
||||
local choices = {}
|
||||
if not self.hide_none then
|
||||
table.insert(choices, { text = self.none_caption, type_id = -1, subtype_id = -1, custom_id=-1})
|
||||
end
|
||||
|
||||
if self.use_workshops then
|
||||
table.insert(choices, {
|
||||
icon = ARROW, text = 'workshop', key = 'CUSTOM_SHIFT_W',
|
||||
cb = self:callback('initWorkshopMode')
|
||||
})
|
||||
end
|
||||
if self.use_furnace then
|
||||
table.insert(choices, {
|
||||
icon = ARROW, text = 'furnaces', key = 'CUSTOM_SHIFT_F',
|
||||
cb = self:callback('initFurnaceMode')
|
||||
})
|
||||
end
|
||||
if self.use_trap then
|
||||
table.insert(choices, {
|
||||
icon = ARROW, text = 'traps', key = 'CUSTOM_SHIFT_T',
|
||||
cb = self:callback('initTrapMode')
|
||||
})
|
||||
end
|
||||
if self.use_construction then
|
||||
table.insert(choices, {
|
||||
icon = ARROW, text = 'constructions', key = 'CUSTOM_SHIFT_C',
|
||||
cb = self:callback('initConstructionMode')
|
||||
})
|
||||
end
|
||||
if self.use_siege then
|
||||
table.insert(choices, {
|
||||
icon = ARROW, text = 'siege engine', key = 'CUSTOM_SHIFT_S',
|
||||
cb = self:callback('initSiegeMode')
|
||||
})
|
||||
end
|
||||
if self.use_custom then
|
||||
table.insert(choices, {
|
||||
icon = ARROW, text = 'custom workshop', key = 'CUSTOM_SHIFT_U',
|
||||
cb = self:callback('initCustomMode')
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
|
||||
for i=0,df.building_type._last_item do
|
||||
if (not WORKSHOP_ABSTRACT[i] or self.use_abstract)and not WORKSHOP_SPECIAL[i] then
|
||||
self:addBuilding(choices, df.building_type[i], i, -1,-1,nil)
|
||||
end
|
||||
end
|
||||
|
||||
self:pushContext('Any building', choices)
|
||||
end
|
||||
|
||||
function BuildingDialog:initWorkshopMode()
|
||||
local choices = {}
|
||||
|
||||
for i=0,df.workshop_type._last_item do
|
||||
if i~=df.workshop_type.Custom and (i~=df.workshop_type.Tool or self.use_tool_workshop) then
|
||||
self:addBuilding(choices, df.workshop_type[i], df.building_type.Workshop, i,-1,nil)
|
||||
end
|
||||
end
|
||||
|
||||
self:pushContext('Workshops', choices)
|
||||
end
|
||||
function BuildingDialog:initTrapMode()
|
||||
local choices = {}
|
||||
|
||||
for i=0,df.trap_type._last_item do
|
||||
self:addBuilding(choices, df.trap_type[i], df.building_type.Trap, i,-1,nil)
|
||||
end
|
||||
|
||||
self:pushContext('Traps', choices)
|
||||
end
|
||||
|
||||
function BuildingDialog:initConstructionMode()
|
||||
local choices = {}
|
||||
|
||||
for i=0,df.construction_type._last_item do
|
||||
self:addBuilding(choices, df.construction_type[i], df.building_type.Construction, i,-1,nil)
|
||||
end
|
||||
|
||||
self:pushContext('Constructions', choices)
|
||||
end
|
||||
|
||||
function BuildingDialog:initFurnaceMode()
|
||||
local choices = {}
|
||||
|
||||
for i=0,df.furnace_type._last_item do
|
||||
self:addBuilding(choices, df.furnace_type[i], df.building_type.Furnace, i,-1,nil)
|
||||
end
|
||||
|
||||
self:pushContext('Furnaces', choices)
|
||||
end
|
||||
|
||||
function BuildingDialog:initSiegeMode()
|
||||
local choices = {}
|
||||
|
||||
for i=0,df.siegeengine_type._last_item do
|
||||
self:addBuilding(choices, df.siegeengine_type[i], df.building_type.SiegeEngine, i,-1,nil)
|
||||
end
|
||||
|
||||
self:pushContext('Siege weapons', choices)
|
||||
end
|
||||
function BuildingDialog:initCustomMode()
|
||||
local choices = {}
|
||||
local raws=df.global.world.raws.buildings.all
|
||||
for k,v in pairs(raws) do
|
||||
self:addBuilding(choices, v.name, df.building_type.Workshop,df.workshop_type.Custom,v.id,v)
|
||||
end
|
||||
|
||||
self:pushContext('Custom workshops', choices)
|
||||
end
|
||||
|
||||
function BuildingDialog:addBuilding(choices, name,type_id, subtype_id, custom_id, parent)
|
||||
-- Check the filter
|
||||
if self.building_filter and not self.building_filter(name,type_id,subtype_id,custom_id, parent) then
|
||||
return
|
||||
end
|
||||
|
||||
table.insert(choices, {
|
||||
text = name:lower(),
|
||||
customshop = parent,
|
||||
type_id = type_id, subtype_id = subtype_id, custom_id=custom_id
|
||||
})
|
||||
end
|
||||
|
||||
function BuildingDialog:pushContext(name, choices)
|
||||
if not self.back_stack then
|
||||
self.back_stack = {}
|
||||
self.subviews.back.visible = false
|
||||
else
|
||||
table.insert(self.back_stack, {
|
||||
context_str = self.context_str,
|
||||
all_choices = self.subviews.list:getChoices(),
|
||||
edit_text = self.subviews.list:getFilter(),
|
||||
selected = self.subviews.list:getSelected(),
|
||||
})
|
||||
self.subviews.back.visible = true
|
||||
end
|
||||
|
||||
self.context_str = name
|
||||
self.subviews.list:setChoices(choices, 1)
|
||||
end
|
||||
|
||||
function BuildingDialog:onGoBack()
|
||||
local save = table.remove(self.back_stack)
|
||||
self.subviews.back.visible = (#self.back_stack > 0)
|
||||
|
||||
self.context_str = save.context_str
|
||||
self.subviews.list:setChoices(save.all_choices)
|
||||
self.subviews.list:setFilter(save.edit_text, save.selected)
|
||||
end
|
||||
|
||||
function BuildingDialog:submitBuilding(type_id,subtype_id,custom_id,choice,index)
|
||||
self:dismiss()
|
||||
|
||||
if self.on_select then
|
||||
self.on_select(type_id,subtype_id,custom_id,choice,index)
|
||||
end
|
||||
end
|
||||
|
||||
function BuildingDialog:onSubmitItem(idx, item)
|
||||
if item.cb then
|
||||
item:cb(idx)
|
||||
else
|
||||
self:submitBuilding(item.type_id, item.subtype_id,item.custom_id,item,idx)
|
||||
end
|
||||
end
|
||||
|
||||
function BuildingDialog:onInput(keys)
|
||||
if keys.LEAVESCREEN or keys.LEAVESCREEN_ALL then
|
||||
if self.subviews.back.visible and not keys.LEAVESCREEN_ALL then
|
||||
self:onGoBack()
|
||||
else
|
||||
self:dismiss()
|
||||
if self.on_cancel then
|
||||
self.on_cancel()
|
||||
end
|
||||
end
|
||||
else
|
||||
self:inputToSubviews(keys)
|
||||
end
|
||||
end
|
||||
|
||||
function showBuildingPrompt(title, prompt, on_select, on_cancel, build_filter)
|
||||
BuildingDialog{
|
||||
frame_title = title,
|
||||
prompt = prompt,
|
||||
building_filter = build_filter,
|
||||
on_select = on_select,
|
||||
on_cancel = on_cancel,
|
||||
}:show()
|
||||
end
|
||||
|
||||
return _ENV
|
@ -0,0 +1,503 @@
|
||||
#include "Core.h"
|
||||
#include "Console.h"
|
||||
#include "modules/Buildings.h"
|
||||
#include "modules/Constructions.h"
|
||||
#include "modules/EventManager.h"
|
||||
#include "modules/Job.h"
|
||||
#include "modules/World.h"
|
||||
|
||||
#include "df/building.h"
|
||||
#include "df/construction.h"
|
||||
#include "df/global_objects.h"
|
||||
#include "df/item.h"
|
||||
#include "df/job.h"
|
||||
#include "df/job_list_link.h"
|
||||
#include "df/ui.h"
|
||||
#include "df/unit.h"
|
||||
#include "df/unit_syndrome.h"
|
||||
#include "df/world.h"
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace std;
|
||||
using namespace DFHack;
|
||||
using namespace EventManager;
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* error checking
|
||||
* consider a typedef instead of a struct for EventHandler
|
||||
**/
|
||||
|
||||
//map<uint32_t, vector<DFHack::EventManager::EventHandler> > tickQueue;
|
||||
multimap<uint32_t, EventHandler> tickQueue;
|
||||
|
||||
//TODO: consider unordered_map of pairs, or unordered_map of unordered_set, or whatever
|
||||
multimap<Plugin*, EventHandler> handlers[EventType::EVENT_MAX];
|
||||
uint32_t eventLastTick[EventType::EVENT_MAX];
|
||||
|
||||
const uint32_t ticksPerYear = 403200;
|
||||
|
||||
void DFHack::EventManager::registerListener(EventType::EventType e, EventHandler handler, Plugin* plugin) {
|
||||
handlers[e].insert(pair<Plugin*, EventHandler>(plugin, handler));
|
||||
}
|
||||
|
||||
void DFHack::EventManager::registerTick(EventHandler handler, int32_t when, Plugin* plugin, bool absolute) {
|
||||
uint32_t tick = DFHack::World::ReadCurrentYear()*ticksPerYear
|
||||
+ DFHack::World::ReadCurrentTick();
|
||||
if ( !Core::getInstance().isWorldLoaded() ) {
|
||||
tick = 0;
|
||||
if ( absolute ) {
|
||||
Core::getInstance().getConsole().print("Warning: absolute flag will not be honored.\n");
|
||||
}
|
||||
}
|
||||
if ( absolute ) {
|
||||
tick = 0;
|
||||
}
|
||||
|
||||
tickQueue.insert(pair<uint32_t, EventHandler>(tick+(uint32_t)when, handler));
|
||||
handlers[EventType::TICK].insert(pair<Plugin*,EventHandler>(plugin,handler));
|
||||
return;
|
||||
}
|
||||
|
||||
void DFHack::EventManager::unregister(EventType::EventType e, EventHandler handler, Plugin* plugin) {
|
||||
for ( multimap<Plugin*, EventHandler>::iterator i = handlers[e].find(plugin); i != handlers[e].end(); i++ ) {
|
||||
if ( (*i).first != plugin )
|
||||
break;
|
||||
EventHandler handle = (*i).second;
|
||||
if ( handle == handler ) {
|
||||
handlers[e].erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void DFHack::EventManager::unregisterAll(Plugin* plugin) {
|
||||
for ( auto i = handlers[EventType::TICK].find(plugin); i != handlers[EventType::TICK].end(); i++ ) {
|
||||
if ( (*i).first != plugin )
|
||||
break;
|
||||
|
||||
//shenanigans to avoid concurrent modification
|
||||
EventHandler getRidOf = (*i).second;
|
||||
bool didSomething;
|
||||
do {
|
||||
didSomething = false;
|
||||
for ( auto j = tickQueue.begin(); j != tickQueue.end(); j++ ) {
|
||||
EventHandler candidate = (*j).second;
|
||||
if ( getRidOf != candidate )
|
||||
continue;
|
||||
tickQueue.erase(j);
|
||||
didSomething = true;
|
||||
break;
|
||||
}
|
||||
} while(didSomething);
|
||||
}
|
||||
for ( size_t a = 0; a < (size_t)EventType::EVENT_MAX; a++ ) {
|
||||
handlers[a].erase(plugin);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void manageTickEvent(color_ostream& out);
|
||||
static void manageJobInitiatedEvent(color_ostream& out);
|
||||
static void manageJobCompletedEvent(color_ostream& out);
|
||||
static void manageUnitDeathEvent(color_ostream& out);
|
||||
static void manageItemCreationEvent(color_ostream& out);
|
||||
static void manageBuildingEvent(color_ostream& out);
|
||||
static void manageConstructionEvent(color_ostream& out);
|
||||
static void manageSyndromeEvent(color_ostream& out);
|
||||
static void manageInvasionEvent(color_ostream& out);
|
||||
|
||||
//tick event
|
||||
static uint32_t lastTick = 0;
|
||||
|
||||
//job initiated
|
||||
static int32_t lastJobId = -1;
|
||||
|
||||
//job completed
|
||||
static unordered_map<int32_t, df::job*> prevJobs;
|
||||
|
||||
//unit death
|
||||
static unordered_set<int32_t> livingUnits;
|
||||
|
||||
//item creation
|
||||
static int32_t nextItem;
|
||||
|
||||
//building
|
||||
static int32_t nextBuilding;
|
||||
static unordered_set<int32_t> buildings;
|
||||
|
||||
//construction
|
||||
static unordered_set<df::construction*> constructions;
|
||||
static bool gameLoaded;
|
||||
|
||||
//invasion
|
||||
static int32_t nextInvasion;
|
||||
|
||||
void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event event) {
|
||||
static bool doOnce = false;
|
||||
if ( !doOnce ) {
|
||||
//TODO: put this somewhere else
|
||||
doOnce = true;
|
||||
EventHandler buildingHandler(Buildings::updateBuildings, 100);
|
||||
DFHack::EventManager::registerListener(EventType::BUILDING, buildingHandler, NULL);
|
||||
//out.print("Registered listeners.\n %d", __LINE__);
|
||||
}
|
||||
if ( event == DFHack::SC_WORLD_UNLOADED ) {
|
||||
lastTick = 0;
|
||||
lastJobId = -1;
|
||||
for ( auto i = prevJobs.begin(); i != prevJobs.end(); i++ ) {
|
||||
Job::deleteJobStruct((*i).second);
|
||||
}
|
||||
prevJobs.clear();
|
||||
tickQueue.clear();
|
||||
livingUnits.clear();
|
||||
nextItem = -1;
|
||||
nextBuilding = -1;
|
||||
buildings.clear();
|
||||
constructions.clear();
|
||||
|
||||
Buildings::clearBuildings(out);
|
||||
gameLoaded = false;
|
||||
nextInvasion = -1;
|
||||
} else if ( event == DFHack::SC_WORLD_LOADED ) {
|
||||
uint32_t tick = DFHack::World::ReadCurrentYear()*ticksPerYear
|
||||
+ DFHack::World::ReadCurrentTick();
|
||||
multimap<uint32_t,EventHandler> newTickQueue;
|
||||
for ( auto i = tickQueue.begin(); i != tickQueue.end(); i++ ) {
|
||||
newTickQueue.insert(pair<uint32_t,EventHandler>(tick + (*i).first, (*i).second));
|
||||
}
|
||||
tickQueue.clear();
|
||||
|
||||
tickQueue.insert(newTickQueue.begin(), newTickQueue.end());
|
||||
|
||||
nextItem = 0;
|
||||
nextBuilding = 0;
|
||||
lastTick = 0;
|
||||
nextInvasion = df::global::ui->invasions.next_id;
|
||||
gameLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
void DFHack::EventManager::manageEvents(color_ostream& out) {
|
||||
if ( !gameLoaded ) {
|
||||
return;
|
||||
}
|
||||
uint32_t tick = DFHack::World::ReadCurrentYear()*ticksPerYear
|
||||
+ DFHack::World::ReadCurrentTick();
|
||||
|
||||
if ( tick <= lastTick )
|
||||
return;
|
||||
lastTick = tick;
|
||||
|
||||
int32_t eventFrequency[EventType::EVENT_MAX];
|
||||
for ( size_t a = 0; a < EventType::EVENT_MAX; a++ ) {
|
||||
int32_t min = 1000000000;
|
||||
for ( auto b = handlers[a].begin(); b != handlers[a].end(); b++ ) {
|
||||
EventHandler bob = (*b).second;
|
||||
if ( bob.freq < min )
|
||||
min = bob.freq;
|
||||
}
|
||||
eventFrequency[a] = min;
|
||||
}
|
||||
|
||||
manageTickEvent(out);
|
||||
if ( tick - eventLastTick[EventType::JOB_INITIATED] >= eventFrequency[EventType::JOB_INITIATED] ) {
|
||||
manageJobInitiatedEvent(out);
|
||||
eventLastTick[EventType::JOB_INITIATED] = tick;
|
||||
}
|
||||
if ( tick - eventLastTick[EventType::JOB_COMPLETED] >= eventFrequency[EventType::JOB_COMPLETED] ) {
|
||||
manageJobCompletedEvent(out);
|
||||
eventLastTick[EventType::JOB_COMPLETED] = tick;
|
||||
}
|
||||
if ( tick - eventLastTick[EventType::UNIT_DEATH] >= eventFrequency[EventType::UNIT_DEATH] ) {
|
||||
manageUnitDeathEvent(out);
|
||||
eventLastTick[EventType::UNIT_DEATH] = tick;
|
||||
}
|
||||
if ( tick - eventLastTick[EventType::ITEM_CREATED] >= eventFrequency[EventType::ITEM_CREATED] ) {
|
||||
manageItemCreationEvent(out);
|
||||
eventLastTick[EventType::ITEM_CREATED] = tick;
|
||||
}
|
||||
if ( tick - eventLastTick[EventType::BUILDING] >= eventFrequency[EventType::BUILDING] ) {
|
||||
manageBuildingEvent(out);
|
||||
eventLastTick[EventType::BUILDING] = tick;
|
||||
}
|
||||
if ( tick - eventLastTick[EventType::CONSTRUCTION] >= eventFrequency[EventType::CONSTRUCTION] ) {
|
||||
manageConstructionEvent(out);
|
||||
eventLastTick[EventType::CONSTRUCTION] = tick;
|
||||
}
|
||||
if ( tick - eventLastTick[EventType::SYNDROME] >= eventFrequency[EventType::SYNDROME] ) {
|
||||
manageSyndromeEvent(out);
|
||||
eventLastTick[EventType::SYNDROME] = tick;
|
||||
}
|
||||
if ( tick - eventLastTick[EventType::INVASION] >= eventFrequency[EventType::INVASION] ) {
|
||||
manageInvasionEvent(out);
|
||||
eventLastTick[EventType::INVASION] = tick;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void manageTickEvent(color_ostream& out) {
|
||||
uint32_t tick = DFHack::World::ReadCurrentYear()*ticksPerYear
|
||||
+ DFHack::World::ReadCurrentTick();
|
||||
while ( !tickQueue.empty() ) {
|
||||
if ( tick < (*tickQueue.begin()).first )
|
||||
break;
|
||||
EventHandler handle = (*tickQueue.begin()).second;
|
||||
tickQueue.erase(tickQueue.begin());
|
||||
handle.eventHandler(out, (void*)tick);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void manageJobInitiatedEvent(color_ostream& out) {
|
||||
if ( handlers[EventType::JOB_INITIATED].empty() )
|
||||
return;
|
||||
|
||||
if ( lastJobId == -1 ) {
|
||||
lastJobId = *df::global::job_next_id - 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( lastJobId+1 == *df::global::job_next_id ) {
|
||||
return; //no new jobs
|
||||
}
|
||||
multimap<Plugin*,EventHandler> copy(handlers[EventType::JOB_INITIATED].begin(), handlers[EventType::JOB_INITIATED].end());
|
||||
|
||||
for ( df::job_list_link* link = &df::global::world->job_list; link != NULL; link = link->next ) {
|
||||
if ( link->item == NULL )
|
||||
continue;
|
||||
if ( link->item->id <= lastJobId )
|
||||
continue;
|
||||
for ( auto i = copy.begin(); i != copy.end(); i++ ) {
|
||||
(*i).second.eventHandler(out, (void*)link->item);
|
||||
}
|
||||
}
|
||||
|
||||
lastJobId = *df::global::job_next_id - 1;
|
||||
}
|
||||
|
||||
static void manageJobCompletedEvent(color_ostream& out) {
|
||||
if ( handlers[EventType::JOB_COMPLETED].empty() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
multimap<Plugin*,EventHandler> copy(handlers[EventType::JOB_COMPLETED].begin(), handlers[EventType::JOB_COMPLETED].end());
|
||||
map<int32_t, df::job*> nowJobs;
|
||||
for ( df::job_list_link* link = &df::global::world->job_list; link != NULL; link = link->next ) {
|
||||
if ( link->item == NULL )
|
||||
continue;
|
||||
nowJobs[link->item->id] = link->item;
|
||||
}
|
||||
|
||||
for ( auto i = prevJobs.begin(); i != prevJobs.end(); i++ ) {
|
||||
if ( nowJobs.find((*i).first) != nowJobs.end() )
|
||||
continue;
|
||||
|
||||
//recently finished or cancelled job!
|
||||
for ( auto j = copy.begin(); j != copy.end(); j++ ) {
|
||||
(*j).second.eventHandler(out, (void*)(*i).second);
|
||||
}
|
||||
}
|
||||
|
||||
//erase old jobs, copy over possibly altered jobs
|
||||
for ( auto i = prevJobs.begin(); i != prevJobs.end(); i++ ) {
|
||||
Job::deleteJobStruct((*i).second);
|
||||
}
|
||||
prevJobs.clear();
|
||||
|
||||
//create new jobs
|
||||
for ( auto j = nowJobs.begin(); j != nowJobs.end(); j++ ) {
|
||||
/*map<int32_t, df::job*>::iterator i = prevJobs.find((*j).first);
|
||||
if ( i != prevJobs.end() ) {
|
||||
continue;
|
||||
}*/
|
||||
|
||||
df::job* newJob = Job::cloneJobStruct((*j).second, true);
|
||||
prevJobs[newJob->id] = newJob;
|
||||
}
|
||||
|
||||
/*//get rid of old pointers to deallocated jobs
|
||||
for ( size_t a = 0; a < toDelete.size(); a++ ) {
|
||||
prevJobs.erase(a);
|
||||
}*/
|
||||
}
|
||||
|
||||
static void manageUnitDeathEvent(color_ostream& out) {
|
||||
if ( handlers[EventType::UNIT_DEATH].empty() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
multimap<Plugin*,EventHandler> copy(handlers[EventType::UNIT_DEATH].begin(), handlers[EventType::UNIT_DEATH].end());
|
||||
for ( size_t a = 0; a < df::global::world->units.active.size(); a++ ) {
|
||||
df::unit* unit = df::global::world->units.active[a];
|
||||
if ( unit->counters.death_id == -1 ) {
|
||||
livingUnits.insert(unit->id);
|
||||
continue;
|
||||
}
|
||||
//dead: if dead since last check, trigger events
|
||||
if ( livingUnits.find(unit->id) == livingUnits.end() )
|
||||
continue;
|
||||
|
||||
for ( auto i = copy.begin(); i != copy.end(); i++ ) {
|
||||
(*i).second.eventHandler(out, (void*)unit->id);
|
||||
}
|
||||
livingUnits.erase(unit->id);
|
||||
}
|
||||
}
|
||||
|
||||
static void manageItemCreationEvent(color_ostream& out) {
|
||||
if ( handlers[EventType::ITEM_CREATED].empty() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( nextItem >= *df::global::item_next_id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
multimap<Plugin*,EventHandler> copy(handlers[EventType::ITEM_CREATED].begin(), handlers[EventType::ITEM_CREATED].end());
|
||||
size_t index = df::item::binsearch_index(df::global::world->items.all, nextItem, false);
|
||||
for ( size_t a = index; a < df::global::world->items.all.size(); a++ ) {
|
||||
df::item* item = df::global::world->items.all[a];
|
||||
//invaders
|
||||
if ( item->flags.bits.foreign )
|
||||
continue;
|
||||
//traders who bring back your items?
|
||||
if ( item->flags.bits.trader )
|
||||
continue;
|
||||
//migrants
|
||||
if ( item->flags.bits.owned )
|
||||
continue;
|
||||
//spider webs don't count
|
||||
if ( item->flags.bits.spider_web )
|
||||
continue;
|
||||
for ( auto i = copy.begin(); i != copy.end(); i++ ) {
|
||||
(*i).second.eventHandler(out, (void*)item->id);
|
||||
}
|
||||
}
|
||||
nextItem = *df::global::item_next_id;
|
||||
}
|
||||
|
||||
static void manageBuildingEvent(color_ostream& out) {
|
||||
/*
|
||||
* TODO: could be faster
|
||||
* consider looking at jobs: building creation / destruction
|
||||
**/
|
||||
if ( handlers[EventType::BUILDING].empty() )
|
||||
return;
|
||||
|
||||
multimap<Plugin*,EventHandler> copy(handlers[EventType::BUILDING].begin(), handlers[EventType::BUILDING].end());
|
||||
//first alert people about new buildings
|
||||
for ( int32_t a = nextBuilding; a < *df::global::building_next_id; a++ ) {
|
||||
int32_t index = df::building::binsearch_index(df::global::world->buildings.all, a);
|
||||
if ( index == -1 ) {
|
||||
//out.print("%s, line %d: Couldn't find new building with id %d.\n", __FILE__, __LINE__, a);
|
||||
//the tricky thing is that when the game first starts, it's ok to skip buildings, but otherwise, if you skip buildings, something is probably wrong. TODO: make this smarter
|
||||
continue;
|
||||
}
|
||||
buildings.insert(a);
|
||||
for ( auto b = copy.begin(); b != copy.end(); b++ ) {
|
||||
EventHandler bob = (*b).second;
|
||||
bob.eventHandler(out, (void*)a);
|
||||
}
|
||||
}
|
||||
nextBuilding = *df::global::building_next_id;
|
||||
|
||||
//now alert people about destroyed buildings
|
||||
unordered_set<int32_t> toDelete;
|
||||
for ( auto a = buildings.begin(); a != buildings.end(); a++ ) {
|
||||
int32_t id = *a;
|
||||
int32_t index = df::building::binsearch_index(df::global::world->buildings.all,id);
|
||||
if ( index != -1 )
|
||||
continue;
|
||||
toDelete.insert(id);
|
||||
|
||||
for ( auto b = copy.begin(); b != copy.end(); b++ ) {
|
||||
EventHandler bob = (*b).second;
|
||||
bob.eventHandler(out, (void*)id);
|
||||
}
|
||||
}
|
||||
|
||||
for ( auto a = toDelete.begin(); a != toDelete.end(); a++ ) {
|
||||
int32_t id = *a;
|
||||
buildings.erase(id);
|
||||
}
|
||||
|
||||
//out.print("Sent building event.\n %d", __LINE__);
|
||||
}
|
||||
|
||||
static void manageConstructionEvent(color_ostream& out) {
|
||||
if ( handlers[EventType::CONSTRUCTION].empty() )
|
||||
return;
|
||||
|
||||
unordered_set<df::construction*> constructionsNow(df::global::world->constructions.begin(), df::global::world->constructions.end());
|
||||
|
||||
multimap<Plugin*,EventHandler> copy(handlers[EventType::CONSTRUCTION].begin(), handlers[EventType::CONSTRUCTION].end());
|
||||
for ( auto a = constructions.begin(); a != constructions.end(); a++ ) {
|
||||
df::construction* construction = *a;
|
||||
if ( constructionsNow.find(construction) != constructionsNow.end() )
|
||||
continue;
|
||||
for ( auto b = copy.begin(); b != copy.end(); b++ ) {
|
||||
EventHandler handle = (*b).second;
|
||||
handle.eventHandler(out, (void*)construction);
|
||||
}
|
||||
}
|
||||
|
||||
for ( auto a = constructionsNow.begin(); a != constructionsNow.end(); a++ ) {
|
||||
df::construction* construction = *a;
|
||||
if ( constructions.find(construction) != constructions.end() )
|
||||
continue;
|
||||
for ( auto b = copy.begin(); b != copy.end(); b++ ) {
|
||||
EventHandler handle = (*b).second;
|
||||
handle.eventHandler(out, (void*)construction);
|
||||
}
|
||||
}
|
||||
|
||||
constructions.clear();
|
||||
constructions.insert(constructionsNow.begin(), constructionsNow.end());
|
||||
}
|
||||
|
||||
static void manageSyndromeEvent(color_ostream& out) {
|
||||
if ( handlers[EventType::SYNDROME].empty() )
|
||||
return;
|
||||
|
||||
multimap<Plugin*,EventHandler> copy(handlers[EventType::SYNDROME].begin(), handlers[EventType::SYNDROME].end());
|
||||
for ( auto a = df::global::world->units.active.begin(); a != df::global::world->units.active.end(); a++ ) {
|
||||
df::unit* unit = *a;
|
||||
if ( unit->flags1.bits.dead )
|
||||
continue;
|
||||
for ( size_t b = 0; b < unit->syndromes.active.size(); b++ ) {
|
||||
df::unit_syndrome* syndrome = unit->syndromes.active[b];
|
||||
uint32_t startTime = syndrome->year*ticksPerYear + syndrome->year_time;
|
||||
if ( startTime <= eventLastTick[EventType::SYNDROME] )
|
||||
continue;
|
||||
|
||||
SyndromeData data(unit->id, b);
|
||||
for ( auto c = copy.begin(); c != copy.end(); c++ ) {
|
||||
EventHandler handle = (*c).second;
|
||||
handle.eventHandler(out, (void*)&data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void manageInvasionEvent(color_ostream& out) {
|
||||
if ( handlers[EventType::INVASION].empty() )
|
||||
return;
|
||||
|
||||
multimap<Plugin*,EventHandler> copy(handlers[EventType::INVASION].begin(), handlers[EventType::INVASION].end());
|
||||
|
||||
if ( df::global::ui->invasions.next_id <= nextInvasion )
|
||||
return;
|
||||
nextInvasion = df::global::ui->invasions.next_id;
|
||||
|
||||
for ( auto a = copy.begin(); a != copy.end(); a++ ) {
|
||||
EventHandler handle = (*a).second;
|
||||
handle.eventHandler(out, (void*)nextInvasion);
|
||||
}
|
||||
}
|
||||
|
@ -1,85 +0,0 @@
|
||||
/*
|
||||
https://github.com/peterix/dfhack
|
||||
Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any
|
||||
damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it and
|
||||
redistribute it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must
|
||||
not claim that you wrote the original software. If you use this
|
||||
software in a product, an acknowledgment in the product documentation
|
||||
would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and
|
||||
must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
|
||||
#include "Internal.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
using namespace std;
|
||||
|
||||
#include "VersionInfo.h"
|
||||
#include "MemAccess.h"
|
||||
#include "Types.h"
|
||||
#include "Core.h"
|
||||
using namespace DFHack;
|
||||
|
||||
#include "modules/Vegetation.h"
|
||||
#include "df/world.h"
|
||||
|
||||
using namespace DFHack;
|
||||
using df::global::world;
|
||||
|
||||
bool Vegetation::isValid()
|
||||
{
|
||||
return (world != NULL);
|
||||
}
|
||||
|
||||
uint32_t Vegetation::getCount()
|
||||
{
|
||||
return world->plants.all.size();
|
||||
}
|
||||
|
||||
df::plant * Vegetation::getPlant(const int32_t index)
|
||||
{
|
||||
if (uint32_t(index) >= getCount())
|
||||
return NULL;
|
||||
return world->plants.all[index];
|
||||
}
|
||||
|
||||
bool Vegetation::copyPlant(const int32_t index, t_plant &out)
|
||||
{
|
||||
if (uint32_t(index) >= getCount())
|
||||
return false;
|
||||
|
||||
out.origin = world->plants.all[index];
|
||||
|
||||
out.name = out.origin->name;
|
||||
out.flags = out.origin->flags;
|
||||
out.material = out.origin->material;
|
||||
out.pos = out.origin->pos;
|
||||
out.grow_counter = out.origin->grow_counter;
|
||||
out.temperature_1 = out.origin->temperature_1;
|
||||
out.temperature_2 = out.origin->temperature_2;
|
||||
out.is_burning = out.origin->is_burning;
|
||||
out.hitpoints = out.origin->hitpoints;
|
||||
out.update_order = out.origin->update_order;
|
||||
//out.unk1 = out.origin->anon_1;
|
||||
//out.unk2 = out.origin->anon_2;
|
||||
//out.temperature_3 = out.origin->temperature_3;
|
||||
//out.temperature_4 = out.origin->temperature_4;
|
||||
//out.temperature_5 = out.origin->temperature_5;
|
||||
return true;
|
||||
}
|
@ -1 +1 @@
|
||||
Subproject commit aaedb5148f87f71cc82d1320f43f90153531dafa
|
||||
Subproject commit c7e2c28febd6dca06ff7e9951090982fbbee12b5
|
@ -0,0 +1,142 @@
|
||||
http://www.bay12games.com/dwarves/mantisbt/view.php?id=1445
|
||||
|
||||
0x2ac6b
|
||||
CC CC CC CC CC
|
||||
66 39 E8 EB 53
|
||||
|
||||
.text:0042B86B loc_42B86B:
|
||||
.text:0042B86B cmp ax, bp
|
||||
.text:0042B86E jmp short loc_42B8C3
|
||||
|
||||
0x2ac7b
|
||||
CC CC CC CC CC
|
||||
E9 96 A2 00 00
|
||||
|
||||
.text:0042B87B loc_42B87B:
|
||||
.text:0042B87B jmp loc_435B16
|
||||
|
||||
0x2acc3
|
||||
CC CC CC CC CC CC CC CC CC CC CC CC CC
|
||||
75 0A 66 FF 4C 24 16 79 03 58 EB AC C3
|
||||
|
||||
.text:0042B8C3 loc_42B8C3:
|
||||
.text:0042B8C3 jnz short locret_42B8CF
|
||||
.text:0042B8C5 dec word ptr [esp+16h] ; 4+8+8+2
|
||||
.text:0042B8CA jns short locret_42B8CF
|
||||
.text:0042B8CC pop eax
|
||||
.text:0042B8CD jmp short loc_42B87B
|
||||
.text:0042B8CF locret_42B8CF:
|
||||
.text:0042B8CF retn
|
||||
|
||||
0x2b2a1
|
||||
CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
|
||||
66 C7 44 24 0E 01 00 8B 90 44 01 00 00 C3 CC
|
||||
|
||||
.text:0042BEA1 loc_42BEA1:
|
||||
.text:0042BEA1 mov word ptr [esp+0Eh], 1 ; 4+8+2
|
||||
.text:0042BEA8 mov edx, [eax+144h]
|
||||
.text:0042BEAE retn
|
||||
|
||||
0x34d91
|
||||
8B 90 44 01 00 00
|
||||
E8 0B 65 FF FF 90
|
||||
|
||||
<<<<
|
||||
.text:00435991 mov edx, [eax+144h]
|
||||
====
|
||||
.text:00435991 call loc_42BEA1
|
||||
.text:00435996 nop
|
||||
>>>>
|
||||
|
||||
0x34e53
|
||||
0F 84 BD 00 00 00
|
||||
E8 6B 5E FF FF 90
|
||||
|
||||
<<<<
|
||||
.text:00435A53 jz loc_435B16
|
||||
====
|
||||
.text:00435A53 call loc_42B8C3
|
||||
.text:00435A58 nop
|
||||
>>>>
|
||||
|
||||
0x34ef3
|
||||
66 3B C5 74 1E
|
||||
E8 73 5D FF FF
|
||||
|
||||
<<<<
|
||||
.text:00435AF3 cmp ax, bp
|
||||
.text:00435AF6 jz short loc_435B16
|
||||
====
|
||||
.text:00435AF3 call loc_42B86B
|
||||
>>>>
|
||||
|
||||
|
||||
basically:
|
||||
|
||||
+ int allowed_count = 1; // to mean 2
|
||||
...
|
||||
- if (type(item) == new_type)
|
||||
+ if (type(item) == new_type && --allowed_count < 0)
|
||||
return false;
|
||||
|
||||
to allow up to two items of the same type at the same time
|
||||
|
||||
|
||||
---8<---
|
||||
This difference file is created by The Interactive Disassembler
|
||||
|
||||
Dwarf Fortress.exe
|
||||
0002AC6B: CC 66
|
||||
0002AC6C: CC 39
|
||||
0002AC6D: CC E8
|
||||
0002AC6E: CC EB
|
||||
0002AC6F: CC 53
|
||||
0002AC7B: CC E9
|
||||
0002AC7C: CC 96
|
||||
0002AC7D: CC A2
|
||||
0002AC7E: CC 00
|
||||
0002AC7F: CC 00
|
||||
0002ACC3: CC 75
|
||||
0002ACC4: CC 0A
|
||||
0002ACC5: CC 66
|
||||
0002ACC6: CC FF
|
||||
0002ACC7: CC 4C
|
||||
0002ACC8: CC 24
|
||||
0002ACC9: CC 16
|
||||
0002ACCA: CC 79
|
||||
0002ACCB: CC 03
|
||||
0002ACCC: CC 58
|
||||
0002ACCD: CC EB
|
||||
0002ACCE: CC AC
|
||||
0002ACCF: CC C3
|
||||
0002B2A1: CC 66
|
||||
0002B2A2: CC C7
|
||||
0002B2A3: CC 44
|
||||
0002B2A4: CC 24
|
||||
0002B2A5: CC 0E
|
||||
0002B2A6: CC 01
|
||||
0002B2A7: CC 00
|
||||
0002B2A8: CC 8B
|
||||
0002B2A9: CC 90
|
||||
0002B2AA: CC 44
|
||||
0002B2AB: CC 01
|
||||
0002B2AC: CC 00
|
||||
0002B2AD: CC 00
|
||||
0002B2AE: CC C3
|
||||
00034D91: 8B E8
|
||||
00034D92: 90 0B
|
||||
00034D93: 44 65
|
||||
00034D94: 01 FF
|
||||
00034D95: 00 FF
|
||||
00034D96: 00 90
|
||||
00034E53: 0F E8
|
||||
00034E54: 84 6B
|
||||
00034E55: BD 5E
|
||||
00034E56: 00 FF
|
||||
00034E57: 00 FF
|
||||
00034E58: 00 90
|
||||
00034EF3: 66 E8
|
||||
00034EF4: 3B 73
|
||||
00034EF5: C5 5D
|
||||
00034EF6: 74 FF
|
||||
00034EF7: 1E FF
|
@ -0,0 +1,91 @@
|
||||
http://www.bay12games.com/dwarves/mantisbt/view.php?id=808
|
||||
|
||||
Original code:
|
||||
|
||||
.text:00916BCE mov edi, ebp
|
||||
.text:00916BD0 call eax
|
||||
.text:00916BD2 test eax, eax
|
||||
.text:00916BD4 jnz short loc_916C1C
|
||||
|
||||
.text:00916C0A mov edi, ebp
|
||||
|
||||
.text:00916C14 mov edi, ebp
|
||||
|
||||
Patch:
|
||||
|
||||
0x2ac34:
|
||||
CC CC CC CC CC CC CC CC CC CC CC CC
|
||||
8B 7C 24 78 8B 3C B7 FF D0 EB 25 CC
|
||||
|
||||
.text:0042B834 loc_42B834:
|
||||
.text:0042B834 mov edi, [esp+78h]
|
||||
.text:0042B838 mov edi, [edi+esi*4]
|
||||
.text:0042B83B call eax
|
||||
.text:0042B83D jmp short unk_42B864
|
||||
|
||||
0x2ac64
|
||||
CC CC CC CC CC CC CC CC CC CC CC CC
|
||||
85 C0 E9 69 B3 4E 00 CC CC CC CC CC
|
||||
|
||||
.text:0042B864 loc_42B864:
|
||||
.text:0042B864 test eax, eax
|
||||
.text:0042B866 jmp loc_916BD4
|
||||
|
||||
0x515fce
|
||||
8B FD FF D0 85 C0
|
||||
E9 61 4C B1 FF 90
|
||||
|
||||
.text:00916BCE jmp loc_42B834
|
||||
.text:00916BD3 nop
|
||||
.text:00916BD4 loc_916BD4:
|
||||
|
||||
0x51600a
|
||||
8B FD
|
||||
90 90
|
||||
|
||||
.text:00916C0A nop
|
||||
.text:00916C0B nop
|
||||
|
||||
0x516014
|
||||
8B FD
|
||||
90 90
|
||||
|
||||
.text:00916C14 nop
|
||||
.text:00916C15 nop
|
||||
|
||||
|
||||
You can use this script to apply the generated patch below:
|
||||
http://stalkr.net/files/ida/idadif.py
|
||||
|
||||
----8<----
|
||||
This difference file is created by The Interactive Disassembler
|
||||
|
||||
Dwarf Fortress.exe
|
||||
0002AC34: CC 8B
|
||||
0002AC35: CC 7C
|
||||
0002AC36: CC 24
|
||||
0002AC37: CC 78
|
||||
0002AC38: CC 8B
|
||||
0002AC39: CC 3C
|
||||
0002AC3A: CC B7
|
||||
0002AC3B: CC FF
|
||||
0002AC3C: CC D0
|
||||
0002AC3D: CC EB
|
||||
0002AC3E: CC 25
|
||||
0002AC64: CC 85
|
||||
0002AC65: CC C0
|
||||
0002AC66: CC E9
|
||||
0002AC67: CC 69
|
||||
0002AC68: CC B3
|
||||
0002AC69: CC 4E
|
||||
0002AC6A: CC 00
|
||||
00515FCE: 8B E9
|
||||
00515FCF: FD 61
|
||||
00515FD0: FF 4C
|
||||
00515FD1: D0 B1
|
||||
00515FD2: 85 FF
|
||||
00515FD3: C0 90
|
||||
0051600A: 8B 90
|
||||
0051600B: FD 90
|
||||
00516014: 8B 90
|
||||
00516015: FD 90
|