Merge branch 'master' of https://github.com/angavrilov/dfhack.git
Conflicts: plugins/devel/CMakeLists.txtdevelop
commit
8d278a87db
@ -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
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,476 @@
|
|||||||
|
#include "PluginManager.h"
|
||||||
|
#include "Export.h"
|
||||||
|
#include "DataDefs.h"
|
||||||
|
#include "Core.h"
|
||||||
|
|
||||||
|
#include "modules/EventManager.h"
|
||||||
|
#include "modules/Job.h"
|
||||||
|
#include "modules/Maps.h"
|
||||||
|
|
||||||
|
#include "df/building.h"
|
||||||
|
#include "df/caste_raw.h"
|
||||||
|
#include "df/creature_raw.h"
|
||||||
|
#include "df/global_objects.h"
|
||||||
|
#include "df/item.h"
|
||||||
|
#include "df/item_boulderst.h"
|
||||||
|
#include "df/job.h"
|
||||||
|
#include "df/job_type.h"
|
||||||
|
#include "df/reaction.h"
|
||||||
|
#include "df/reaction_product.h"
|
||||||
|
#include "df/reaction_product_type.h"
|
||||||
|
#include "df/reaction_product_itemst.h"
|
||||||
|
#include "df/syndrome.h"
|
||||||
|
#include "df/unit_syndrome.h"
|
||||||
|
#include "df/ui.h"
|
||||||
|
#include "df/unit.h"
|
||||||
|
#include "df/general_ref.h"
|
||||||
|
#include "df/general_ref_building_holderst.h"
|
||||||
|
#include "df/general_ref_type.h"
|
||||||
|
#include "df/general_ref_unit_workerst.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace DFHack;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Example usage:
|
||||||
|
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
//In file inorganic_duck.txt
|
||||||
|
inorganic_stone_duck
|
||||||
|
|
||||||
|
[OBJECT:INORGANIC]
|
||||||
|
|
||||||
|
[INORGANIC:DUCK_ROCK]
|
||||||
|
[USE_MATERIAL_TEMPLATE:STONE_TEMPLATE]
|
||||||
|
[STATE_NAME_ADJ:ALL_SOLID:drakium][DISPLAY_COLOR:0:7:0][TILE:'.']
|
||||||
|
[IS_STONE]
|
||||||
|
[SOLID_DENSITY:1][MELTING_POINT:25000]
|
||||||
|
[BOILING_POINT:9999] //This is the critical line: boiling point must be <= 10000
|
||||||
|
[SYNDROME]
|
||||||
|
[SYN_NAME:Chronic Duck Syndrome]
|
||||||
|
[CE_BODY_TRANSFORMATION:PROB:100:START:0]
|
||||||
|
[CE:CREATURE:BIRD_DUCK:MALE] //even though we don't have SYN_INHALED, the plugin will add it
|
||||||
|
///////////////////////////////////////////////
|
||||||
|
//In file building_duck.txt
|
||||||
|
building_duck
|
||||||
|
|
||||||
|
[OBJECT:BUILDING]
|
||||||
|
|
||||||
|
[BUILDING_WORKSHOP:DUCK_WORKSHOP]
|
||||||
|
[NAME:Duck Workshop]
|
||||||
|
[NAME_COLOR:7:0:1]
|
||||||
|
[DIM:1:1]
|
||||||
|
[WORK_LOCATION:1:1]
|
||||||
|
[BLOCK:1:0:0:0]
|
||||||
|
[TILE:0:1:236]
|
||||||
|
[COLOR:0:1:0:0:1]
|
||||||
|
[TILE:1:1:' ']
|
||||||
|
[COLOR:1:1:0:0:0]
|
||||||
|
[TILE:2:1:8]
|
||||||
|
[COLOR:2:1:0:0:1]
|
||||||
|
[TILE:3:1:8]
|
||||||
|
[COLOR:3:2:0:4:1]
|
||||||
|
[BUILD_ITEM:1:NONE:NONE:NONE:NONE]
|
||||||
|
[BUILDMAT]
|
||||||
|
[WORTHLESS_STONE_ONLY]
|
||||||
|
[CAN_USE_ARTIFACT]
|
||||||
|
///////////////////////////////////////////////
|
||||||
|
//In file reaction_duck.txt
|
||||||
|
reaction_duck
|
||||||
|
|
||||||
|
[OBJECT:REACTION]
|
||||||
|
|
||||||
|
[REACTION:DUCKIFICATION]
|
||||||
|
[NAME:become a duck]
|
||||||
|
[BUILDING:DUCK_WORKSHOP:NONE]
|
||||||
|
[PRODUCT:100:100:STONE:NO_SUBTYPE:STONE:DUCK_ROCK]
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
//Add the following lines to your entity in entity_default.txt (or wherever it is)
|
||||||
|
[PERMITTED_BUILDING:DUCK_WORKSHOP]
|
||||||
|
[PERMITTED_REACTION:DUCKIFICATION]
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
|
||||||
|
Next, start a new fort in a new world, build a duck workshop, then have someone become a duck.
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool enabled = true;
|
||||||
|
|
||||||
|
DFHACK_PLUGIN("autoSyndrome");
|
||||||
|
|
||||||
|
command_result autoSyndrome(color_ostream& out, vector<string>& parameters);
|
||||||
|
void processJob(color_ostream& out, void* jobPtr);
|
||||||
|
int32_t giveSyndrome(color_ostream& out, int32_t workerId, df::syndrome* syndrome);
|
||||||
|
|
||||||
|
DFhackCExport command_result plugin_init(color_ostream& out, vector<PluginCommand> &commands) {
|
||||||
|
commands.push_back(PluginCommand("autoSyndrome", "Automatically give units syndromes when they complete jobs, as configured in the raw files.\n", &autoSyndrome, false,
|
||||||
|
"autoSyndrome:\n"
|
||||||
|
" autoSyndrome 0 //disable\n"
|
||||||
|
" autoSyndrome 1 //enable\n"
|
||||||
|
" autoSyndrome disable //disable\n"
|
||||||
|
" autoSyndrome enable //enable\n"
|
||||||
|
"\n"
|
||||||
|
"autoSyndrome looks for recently completed jobs matching certain conditions, and if it finds one, then it will give the dwarf that finished that job the syndrome specified in the raw files.\n"
|
||||||
|
"\n"
|
||||||
|
"Requirements:\n"
|
||||||
|
" 1) The job must be a custom reaction.\n"
|
||||||
|
" 2) The job must produce a stone of some inorganic material.\n"
|
||||||
|
" 3) The stone must have a boiling temperature less than or equal to 9000.\n"
|
||||||
|
"\n"
|
||||||
|
"When these conditions are met, the unit that completed the job will immediately become afflicted with all applicable syndromes associated with the inorganic material of the stone, or stones. It should correctly check for whether the creature or caste is affected or immune, and it should also correctly account for affected and immune creature classes.\n"
|
||||||
|
"Multiple syndromes per stone, or multiple boiling rocks produced with the same reaction should work fine.\n"
|
||||||
|
));
|
||||||
|
|
||||||
|
|
||||||
|
EventManager::EventHandler handle(processJob, 5);
|
||||||
|
EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, handle, plugin_self);
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
DFhackCExport command_result plugin_shutdown(color_ostream& out) {
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*DFhackCExport command_result plugin_onstatechange(color_ostream& out, state_change_event e) {
|
||||||
|
return CR_OK;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
command_result autoSyndrome(color_ostream& out, vector<string>& parameters) {
|
||||||
|
if ( parameters.size() > 1 )
|
||||||
|
return CR_WRONG_USAGE;
|
||||||
|
|
||||||
|
bool wasEnabled = enabled;
|
||||||
|
if ( parameters.size() == 1 ) {
|
||||||
|
if ( parameters[0] == "enable" ) {
|
||||||
|
enabled = true;
|
||||||
|
} else if ( parameters[0] == "disable" ) {
|
||||||
|
enabled = false;
|
||||||
|
} else {
|
||||||
|
int32_t a = atoi(parameters[0].c_str());
|
||||||
|
if ( a < 0 || a > 1 )
|
||||||
|
return CR_WRONG_USAGE;
|
||||||
|
|
||||||
|
enabled = (bool)a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out.print("autoSyndrome is %s\n", enabled ? "enabled" : "disabled");
|
||||||
|
if ( enabled == wasEnabled )
|
||||||
|
return CR_OK;
|
||||||
|
|
||||||
|
Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("autoSyndrome");
|
||||||
|
if ( enabled ) {
|
||||||
|
EventManager::EventHandler handle(processJob, 5);
|
||||||
|
EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, handle, me);
|
||||||
|
} else {
|
||||||
|
EventManager::unregisterAll(me);
|
||||||
|
}
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool maybeApply(color_ostream& out, df::syndrome* syndrome, int32_t workerId, df::unit* unit) {
|
||||||
|
df::creature_raw* creature = df::global::world->raws.creatures.all[unit->race];
|
||||||
|
df::caste_raw* caste = creature->caste[unit->caste];
|
||||||
|
std::string& creature_name = creature->creature_id;
|
||||||
|
std::string& creature_caste = caste->caste_id;
|
||||||
|
//check that the syndrome applies to that guy
|
||||||
|
/*
|
||||||
|
* If there is no affected class or affected creature, then anybody who isn't immune is fair game.
|
||||||
|
*
|
||||||
|
* Otherwise, it works like this:
|
||||||
|
* add all the affected class creatures
|
||||||
|
* add all the affected creatures
|
||||||
|
* remove all the immune class creatures
|
||||||
|
* remove all the immune creatures
|
||||||
|
* you're affected if and only if you're in the remaining list after all of that
|
||||||
|
**/
|
||||||
|
bool applies = syndrome->syn_affected_class.size() == 0 && syndrome->syn_affected_creature.size() == 0;
|
||||||
|
for ( size_t c = 0; c < syndrome->syn_affected_class.size(); c++ ) {
|
||||||
|
if ( applies )
|
||||||
|
break;
|
||||||
|
for ( size_t d = 0; d < caste->creature_class.size(); d++ ) {
|
||||||
|
if ( *syndrome->syn_affected_class[c] == *caste->creature_class[d] ) {
|
||||||
|
applies = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for ( size_t c = 0; c < syndrome->syn_immune_class.size(); c++ ) {
|
||||||
|
if ( !applies )
|
||||||
|
break;
|
||||||
|
for ( size_t d = 0; d < caste->creature_class.size(); d++ ) {
|
||||||
|
if ( *syndrome->syn_immune_class[c] == *caste->creature_class[d] ) {
|
||||||
|
applies = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( syndrome->syn_affected_creature.size() != syndrome->syn_affected_caste.size() ) {
|
||||||
|
out.print("%s, line %d: different affected creature/caste sizes.\n", __FILE__, __LINE__);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for ( size_t c = 0; c < syndrome->syn_affected_creature.size(); c++ ) {
|
||||||
|
if ( creature_name != *syndrome->syn_affected_creature[c] )
|
||||||
|
continue;
|
||||||
|
if ( *syndrome->syn_affected_caste[c] == "ALL" ||
|
||||||
|
*syndrome->syn_affected_caste[c] == creature_caste ) {
|
||||||
|
applies = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for ( size_t c = 0; c < syndrome->syn_immune_creature.size(); c++ ) {
|
||||||
|
if ( creature_name != *syndrome->syn_immune_creature[c] )
|
||||||
|
continue;
|
||||||
|
if ( *syndrome->syn_immune_caste[c] == "ALL" ||
|
||||||
|
*syndrome->syn_immune_caste[c] == creature_caste ) {
|
||||||
|
applies = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( !applies ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( giveSyndrome(out, workerId, syndrome) < 0 )
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void processJob(color_ostream& out, void* jobPtr) {
|
||||||
|
df::job* job = (df::job*)jobPtr;
|
||||||
|
if ( job == NULL ) {
|
||||||
|
out.print("Error %s line %d: null job.\n", __FILE__, __LINE__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( job->completion_timer > 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( job->job_type != df::job_type::CustomReaction )
|
||||||
|
return;
|
||||||
|
|
||||||
|
df::reaction* reaction = NULL;
|
||||||
|
for ( size_t a = 0; a < df::global::world->raws.reactions.size(); a++ ) {
|
||||||
|
df::reaction* candidate = df::global::world->raws.reactions[a];
|
||||||
|
if ( candidate->code != job->reaction_name )
|
||||||
|
continue;
|
||||||
|
reaction = candidate;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ( reaction == NULL ) {
|
||||||
|
out.print("%s, line %d: could not find reaction \"%s\".\n", __FILE__, __LINE__, job->reaction_name.c_str() );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t workerId = -1;
|
||||||
|
for ( size_t a = 0; a < job->general_refs.size(); a++ ) {
|
||||||
|
if ( job->general_refs[a]->getType() != df::enums::general_ref_type::UNIT_WORKER )
|
||||||
|
continue;
|
||||||
|
if ( workerId != -1 ) {
|
||||||
|
out.print("%s, line %d: Found two workers on the same job.\n", __FILE__, __LINE__);
|
||||||
|
}
|
||||||
|
workerId = ((df::general_ref_unit_workerst*)job->general_refs[a])->unit_id;
|
||||||
|
if (workerId == -1) {
|
||||||
|
out.print("%s, line %d: invalid worker.\n", __FILE__, __LINE__);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t workerIndex = df::unit::binsearch_index(df::global::world->units.all, workerId);
|
||||||
|
if ( workerIndex < 0 ) {
|
||||||
|
out.print("%s line %d: Couldn't find unit %d.\n", __FILE__, __LINE__, workerId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
df::unit* worker = df::global::world->units.all[workerIndex];
|
||||||
|
//find the building that made it
|
||||||
|
int32_t buildingId = -1;
|
||||||
|
for ( size_t a = 0; a < job->general_refs.size(); a++ ) {
|
||||||
|
if ( job->general_refs[a]->getType() != df::enums::general_ref_type::BUILDING_HOLDER )
|
||||||
|
continue;
|
||||||
|
if ( buildingId != -1 ) {
|
||||||
|
out.print("%s, line %d: Found two buildings for the same job.\n", __FILE__, __LINE__);
|
||||||
|
}
|
||||||
|
buildingId = ((df::general_ref_building_holderst*)job->general_refs[a])->building_id;
|
||||||
|
if (buildingId == -1) {
|
||||||
|
out.print("%s, line %d: invalid building.\n", __FILE__, __LINE__);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
df::building* building;
|
||||||
|
{
|
||||||
|
int32_t index = df::building::binsearch_index(df::global::world->buildings.all, buildingId);
|
||||||
|
if ( index == -1 ) {
|
||||||
|
out.print("%s, line %d: error: couldn't find building %d.\n", __FILE__, __LINE__, buildingId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
building = df::global::world->buildings.all[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
//find all of the products it makes. Look for a stone with a low boiling point.
|
||||||
|
bool appliedSomething = false;
|
||||||
|
for ( size_t a = 0; a < reaction->products.size(); a++ ) {
|
||||||
|
df::reaction_product_type type = reaction->products[a]->getType();
|
||||||
|
//out.print("type = %d\n", (int32_t)type);
|
||||||
|
if ( type != df::enums::reaction_product_type::item )
|
||||||
|
continue;
|
||||||
|
df::reaction_product_itemst* bob = (df::reaction_product_itemst*)reaction->products[a];
|
||||||
|
//out.print("item_type = %d\n", (int32_t)bob->item_type);
|
||||||
|
if ( bob->item_type != df::enums::item_type::BOULDER )
|
||||||
|
continue;
|
||||||
|
//for now don't worry about subtype
|
||||||
|
|
||||||
|
//must be a boiling rock syndrome
|
||||||
|
df::inorganic_raw* inorganic = df::global::world->raws.inorganics[bob->mat_index];
|
||||||
|
if ( inorganic->material.heat.boiling_point > 9000 ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( size_t b = 0; b < inorganic->material.syndrome.size(); b++ ) {
|
||||||
|
//add each syndrome to the guy who did the job
|
||||||
|
df::syndrome* syndrome = inorganic->material.syndrome[b];
|
||||||
|
bool workerOnly = false;
|
||||||
|
bool allowMultipleTargets = false;
|
||||||
|
bool foundCommand = false;
|
||||||
|
bool destroyRock = true;
|
||||||
|
string commandStr;
|
||||||
|
vector<string> args;
|
||||||
|
for ( size_t c = 0; c < syndrome->syn_class.size(); c++ ) {
|
||||||
|
std::string* clazz = syndrome->syn_class[c];
|
||||||
|
if ( foundCommand ) {
|
||||||
|
if ( commandStr == "" ) {
|
||||||
|
if ( *clazz == "\\WORKER_ONLY" ) {
|
||||||
|
workerOnly = true;
|
||||||
|
} else if ( *clazz == "\\ALLOW_MULTIPLE_TARGETS" ) {
|
||||||
|
allowMultipleTargets = true;
|
||||||
|
} else if ( *clazz == "\\PRESERVE_ROCK" ) {
|
||||||
|
destroyRock = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
commandStr = *clazz;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stringstream bob;
|
||||||
|
if ( *clazz == "\\LOCATION" ) {
|
||||||
|
bob << job->pos.x;
|
||||||
|
args.push_back(bob.str());
|
||||||
|
bob.str("");
|
||||||
|
bob.clear();
|
||||||
|
|
||||||
|
bob << job->pos.y;
|
||||||
|
args.push_back(bob.str());
|
||||||
|
bob.str("");
|
||||||
|
bob.clear();
|
||||||
|
|
||||||
|
bob << job->pos.z;
|
||||||
|
args.push_back(bob.str());
|
||||||
|
bob.str("");
|
||||||
|
bob.clear();
|
||||||
|
} else if ( *clazz == "\\WORKER_ID" ) {
|
||||||
|
bob << workerId;
|
||||||
|
args.push_back(bob.str());
|
||||||
|
} else if ( *clazz == "\\REACTION_INDEX" ) {
|
||||||
|
bob << reaction->index;
|
||||||
|
args.push_back(bob.str());
|
||||||
|
} else {
|
||||||
|
args.push_back(*clazz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ( *clazz == "\\COMMAND" ) {
|
||||||
|
foundCommand = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( commandStr != "" ) {
|
||||||
|
Core::getInstance().runCommand(out, commandStr, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( destroyRock ) {
|
||||||
|
//find the rock and kill it before it can boil and cause problems and ugliness
|
||||||
|
for ( size_t c = 0; c < df::global::world->items.all.size(); c++ ) {
|
||||||
|
df::item* item = df::global::world->items.all[c];
|
||||||
|
if ( item->pos.z != building->z )
|
||||||
|
continue;
|
||||||
|
if ( item->pos.x < building->x1 || item->pos.x > building->x2 )
|
||||||
|
continue;
|
||||||
|
if ( item->pos.y < building->y1 || item->pos.y > building->y2 )
|
||||||
|
continue;
|
||||||
|
if ( item->getType() != df::enums::item_type::BOULDER )
|
||||||
|
continue;
|
||||||
|
//make sure it's the right type of boulder
|
||||||
|
df::item_boulderst* boulder = (df::item_boulderst*)item;
|
||||||
|
if ( boulder->mat_index != bob->mat_index )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
boulder->flags.bits.garbage_collect = true;
|
||||||
|
boulder->flags.bits.forbid = true;
|
||||||
|
boulder->flags.bits.hidden = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//only one syndrome per reaction will be applied, unless multiples are allowed.
|
||||||
|
if ( appliedSomething && !allowMultipleTargets )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ( maybeApply(out, syndrome, workerId, worker) ) {
|
||||||
|
appliedSomething = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( workerOnly )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
//now try applying it to everybody inside the building
|
||||||
|
for ( size_t a = 0; a < df::global::world->units.active.size(); a++ ) {
|
||||||
|
df::unit* unit = df::global::world->units.active[a];
|
||||||
|
if ( unit == worker )
|
||||||
|
continue;
|
||||||
|
if ( unit->pos.z != building->z )
|
||||||
|
continue;
|
||||||
|
if ( unit->pos.x < building->x1 || unit->pos.x > building->x2 )
|
||||||
|
continue;
|
||||||
|
if ( unit->pos.y < building->y1 || unit->pos.y > building->y2 )
|
||||||
|
continue;
|
||||||
|
if ( maybeApply(out, syndrome, unit->id, unit) ) {
|
||||||
|
appliedSomething = true;
|
||||||
|
if ( !allowMultipleTargets )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Heavily based on https://gist.github.com/4061959/
|
||||||
|
**/
|
||||||
|
int32_t giveSyndrome(color_ostream& out, int32_t workerId, df::syndrome* syndrome) {
|
||||||
|
int32_t index = df::unit::binsearch_index(df::global::world->units.all, workerId);
|
||||||
|
if ( index < 0 ) {
|
||||||
|
out.print("%s line %d: Couldn't find unit %d.\n", __FILE__, __LINE__, workerId);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
df::unit* unit = df::global::world->units.all[index];
|
||||||
|
|
||||||
|
df::unit_syndrome* unitSyndrome = new df::unit_syndrome();
|
||||||
|
unitSyndrome->type = syndrome->id;
|
||||||
|
unitSyndrome->year = 0;
|
||||||
|
unitSyndrome->year_time = 0;
|
||||||
|
unitSyndrome->ticks = 1;
|
||||||
|
unitSyndrome->unk1 = 1;
|
||||||
|
unitSyndrome->flags = 0; //typecast
|
||||||
|
|
||||||
|
for ( size_t a = 0; a < syndrome->ce.size(); a++ ) {
|
||||||
|
df::unit_syndrome::T_symptoms* symptom = new df::unit_syndrome::T_symptoms();
|
||||||
|
symptom->unk1 = 0;
|
||||||
|
symptom->unk2 = 0;
|
||||||
|
symptom->ticks = 1;
|
||||||
|
symptom->flags = 2; //TODO: ???
|
||||||
|
unitSyndrome->symptoms.push_back(symptom);
|
||||||
|
}
|
||||||
|
unit->syndromes.active.push_back(unitSyndrome);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,108 @@
|
|||||||
|
|
||||||
|
#include "Console.h"
|
||||||
|
#include "Core.h"
|
||||||
|
#include "Export.h"
|
||||||
|
#include "PluginManager.h"
|
||||||
|
#include "modules/EventManager.h"
|
||||||
|
#include "DataDefs.h"
|
||||||
|
|
||||||
|
#include "df/item.h"
|
||||||
|
#include "df/world.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace DFHack;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
DFHACK_PLUGIN("eventExample");
|
||||||
|
|
||||||
|
void jobInitiated(color_ostream& out, void* job);
|
||||||
|
void jobCompleted(color_ostream& out, void* job);
|
||||||
|
void timePassed(color_ostream& out, void* ptr);
|
||||||
|
void unitDeath(color_ostream& out, void* ptr);
|
||||||
|
void itemCreate(color_ostream& out, void* ptr);
|
||||||
|
void building(color_ostream& out, void* ptr);
|
||||||
|
void construction(color_ostream& out, void* ptr);
|
||||||
|
void syndrome(color_ostream& out, void* ptr);
|
||||||
|
void invasion(color_ostream& out, void* ptr);
|
||||||
|
|
||||||
|
command_result eventExample(color_ostream& out, vector<string>& parameters);
|
||||||
|
|
||||||
|
DFhackCExport command_result plugin_init(color_ostream &out, std::vector<PluginCommand> &commands) {
|
||||||
|
commands.push_back(PluginCommand("eventExample", "Sets up a few event triggers.",eventExample));
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
command_result eventExample(color_ostream& out, vector<string>& parameters) {
|
||||||
|
EventManager::EventHandler initiateHandler(jobInitiated, 10);
|
||||||
|
EventManager::EventHandler completeHandler(jobCompleted, 5);
|
||||||
|
EventManager::EventHandler timeHandler(timePassed, 1);
|
||||||
|
EventManager::EventHandler deathHandler(unitDeath, 500);
|
||||||
|
EventManager::EventHandler itemHandler(itemCreate, 1000);
|
||||||
|
EventManager::EventHandler buildingHandler(building, 500);
|
||||||
|
EventManager::EventHandler constructionHandler(construction, 100);
|
||||||
|
EventManager::EventHandler syndromeHandler(syndrome, 1);
|
||||||
|
EventManager::EventHandler invasionHandler(invasion, 1000);
|
||||||
|
Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("eventExample");
|
||||||
|
EventManager::unregisterAll(me);
|
||||||
|
|
||||||
|
EventManager::registerListener(EventManager::EventType::JOB_INITIATED, initiateHandler, me);
|
||||||
|
EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, completeHandler, me);
|
||||||
|
EventManager::registerTick(timeHandler, 1, me);
|
||||||
|
EventManager::registerTick(timeHandler, 2, me);
|
||||||
|
EventManager::registerTick(timeHandler, 4, me);
|
||||||
|
EventManager::registerTick(timeHandler, 8, me);
|
||||||
|
EventManager::registerListener(EventManager::EventType::UNIT_DEATH, deathHandler, me);
|
||||||
|
EventManager::registerListener(EventManager::EventType::ITEM_CREATED, itemHandler, me);
|
||||||
|
EventManager::registerListener(EventManager::EventType::BUILDING, buildingHandler, me);
|
||||||
|
EventManager::registerListener(EventManager::EventType::CONSTRUCTION, constructionHandler, me);
|
||||||
|
EventManager::registerListener(EventManager::EventType::SYNDROME, syndromeHandler, me);
|
||||||
|
EventManager::registerListener(EventManager::EventType::INVASION, invasionHandler, me);
|
||||||
|
out.print("Events registered.\n");
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jobInitiated(color_ostream& out, void* job) {
|
||||||
|
out.print("Job initiated! 0x%X\n", job);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jobCompleted(color_ostream& out, void* job) {
|
||||||
|
out.print("Job completed! 0x%X\n", job);
|
||||||
|
}
|
||||||
|
|
||||||
|
void timePassed(color_ostream& out, void* ptr) {
|
||||||
|
out.print("Time: %d\n", (int32_t)(ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
void unitDeath(color_ostream& out, void* ptr) {
|
||||||
|
out.print("Death: %d\n", (int32_t)(ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
void itemCreate(color_ostream& out, void* ptr) {
|
||||||
|
int32_t item_index = df::item::binsearch_index(df::global::world->items.all, (int32_t)ptr);
|
||||||
|
if ( item_index == -1 ) {
|
||||||
|
out.print("%s, %d: Error.\n", __FILE__, __LINE__);
|
||||||
|
}
|
||||||
|
df::item* item = df::global::world->items.all[item_index];
|
||||||
|
df::item_type type = item->getType();
|
||||||
|
df::coord pos = item->pos;
|
||||||
|
out.print("Item created: %d, %s, at (%d,%d,%d)\n", (int32_t)(ptr), ENUM_KEY_STR(item_type, type).c_str(), pos.x, pos.y, pos.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
void building(color_ostream& out, void* ptr) {
|
||||||
|
out.print("Building created/destroyed: %d\n", (int32_t)ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void construction(color_ostream& out, void* ptr) {
|
||||||
|
out.print("Construction created/destroyed: 0x%X\n", ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void syndrome(color_ostream& out, void* ptr) {
|
||||||
|
EventManager::SyndromeData* data = (EventManager::SyndromeData*)ptr;
|
||||||
|
out.print("Syndrome started: unit %d, syndrome %d.\n", data->unitId, data->syndromeIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void invasion(color_ostream& out, void* ptr) {
|
||||||
|
out.print("New invasion! %d\n", (int32_t)ptr);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
#include "Console.h"
|
||||||
|
#include "Core.h"
|
||||||
|
#include "DataDefs.h"
|
||||||
|
#include "Export.h"
|
||||||
|
#include "PluginManager.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace DFHack;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
command_result printArgs (color_ostream &out, std::vector <std::string> & parameters);
|
||||||
|
|
||||||
|
DFHACK_PLUGIN("printArgs");
|
||||||
|
|
||||||
|
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
|
||||||
|
{
|
||||||
|
commands.push_back(PluginCommand(
|
||||||
|
"printArgs", "Print the arguments given.",
|
||||||
|
printArgs, false
|
||||||
|
));
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
command_result printArgs (color_ostream &out, std::vector <std::string> & parameters)
|
||||||
|
{
|
||||||
|
for ( size_t a = 0; a < parameters.size(); a++ ) {
|
||||||
|
out << "Argument " << (a+1) << ": \"" << parameters[a] << "\"" << endl;
|
||||||
|
}
|
||||||
|
return CR_OK;
|
||||||
|
}
|
@ -0,0 +1,87 @@
|
|||||||
|
|
||||||
|
#include "Core.h"
|
||||||
|
#include <Console.h>
|
||||||
|
#include <Export.h>
|
||||||
|
#include <PluginManager.h>
|
||||||
|
|
||||||
|
// DF data structure definition headers
|
||||||
|
#include "DataDefs.h"
|
||||||
|
#include "df/world.h"
|
||||||
|
|
||||||
|
#include "modules/Gui.h"
|
||||||
|
#include "modules/Maps.h"
|
||||||
|
|
||||||
|
using namespace DFHack;
|
||||||
|
using namespace df::enums;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
command_result stepBetween (color_ostream &out, std::vector <std::string> & parameters);
|
||||||
|
|
||||||
|
// A plugin must be able to return its name and version.
|
||||||
|
// The name string provided must correspond to the filename - skeleton.plug.so or skeleton.plug.dll in this case
|
||||||
|
DFHACK_PLUGIN("stepBetween");
|
||||||
|
|
||||||
|
// Mandatory init function. If you have some global state, create it here.
|
||||||
|
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
|
||||||
|
{
|
||||||
|
// Fill the command list with your commands.
|
||||||
|
commands.push_back(PluginCommand(
|
||||||
|
"stepBetween", "Do nothing, look pretty.",
|
||||||
|
stepBetween, false, /* true means that the command can't be used from non-interactive user interface */
|
||||||
|
// Extended help string. Used by CR_WRONG_USAGE and the help command:
|
||||||
|
" This command does nothing at all.\n"
|
||||||
|
));
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called right before the plugin library is removed from memory.
|
||||||
|
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
|
||||||
|
{
|
||||||
|
// You *MUST* kill all threads you created before this returns.
|
||||||
|
// If everything fails, just return CR_FAILURE. Your plugin will be
|
||||||
|
// in a zombie state, but things won't crash.
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called to notify the plugin about important state changes.
|
||||||
|
// Invoked with DF suspended, and always before the matching plugin_onupdate.
|
||||||
|
// More event codes may be added in the future.
|
||||||
|
/*
|
||||||
|
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
|
||||||
|
{
|
||||||
|
switch (event) {
|
||||||
|
case SC_GAME_LOADED:
|
||||||
|
// initialize from the world just loaded
|
||||||
|
break;
|
||||||
|
case SC_GAME_UNLOADED:
|
||||||
|
// cleanup
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Whatever you put here will be done in each game step. Don't abuse it.
|
||||||
|
// It's optional, so you can just comment it out like this if you don't need it.
|
||||||
|
/*
|
||||||
|
DFhackCExport command_result plugin_onupdate ( color_ostream &out )
|
||||||
|
{
|
||||||
|
// whetever. You don't need to suspend DF execution here.
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
df::coord prev;
|
||||||
|
|
||||||
|
// A command! It sits around and looks pretty. And it's nice and friendly.
|
||||||
|
command_result stepBetween (color_ostream &out, std::vector <std::string> & parameters)
|
||||||
|
{
|
||||||
|
df::coord bob = Gui::getCursorPos();
|
||||||
|
out.print("(%d,%d,%d), (%d,%d,%d): canStepBetween = %d, canWalkBetween = %d\n", prev.x, prev.y, prev.z, bob.x, bob.y, bob.z, Maps::canStepBetween(prev, bob), Maps::canWalkBetween(prev,bob));
|
||||||
|
|
||||||
|
prev = bob;
|
||||||
|
return CR_OK;
|
||||||
|
}
|
@ -0,0 +1,327 @@
|
|||||||
|
#include "Core.h"
|
||||||
|
#include <Console.h>
|
||||||
|
#include <Export.h>
|
||||||
|
#include <PluginManager.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <VTableInterpose.h>
|
||||||
|
|
||||||
|
#include "df/building_workshopst.h"
|
||||||
|
|
||||||
|
#include "df/unit.h"
|
||||||
|
#include "df/item.h"
|
||||||
|
#include "df/item_actual.h"
|
||||||
|
#include "df/unit_wound.h"
|
||||||
|
#include "df/world.h"
|
||||||
|
#include "df/reaction.h"
|
||||||
|
#include "df/reaction_reagent_itemst.h"
|
||||||
|
#include "df/reaction_product_itemst.h"
|
||||||
|
|
||||||
|
#include "df/proj_itemst.h"
|
||||||
|
#include "df/proj_unitst.h"
|
||||||
|
|
||||||
|
#include "MiscUtils.h"
|
||||||
|
#include "LuaTools.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_itemst item_product;
|
||||||
|
|
||||||
|
DFHACK_PLUGIN("eventful");
|
||||||
|
|
||||||
|
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;
|
||||||
|
item_product *product;
|
||||||
|
|
||||||
|
MaterialSource material;
|
||||||
|
|
||||||
|
bool isValid() {
|
||||||
|
return (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_lua_hook(const std::string &name)
|
||||||
|
{
|
||||||
|
return name.size() > 9 && memcmp(name.data(), "LUA_HOOK_", 9) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hooks
|
||||||
|
*/
|
||||||
|
static void handle_fillsidebar(color_ostream &out,df::building_workshopst*,bool *call_native){};
|
||||||
|
static void handle_postfillsidebar(color_ostream &out,df::building_workshopst*){};
|
||||||
|
|
||||||
|
static void handle_reaction_done(color_ostream &out,df::reaction*, df::unit *unit, std::vector<df::item*> *in_items,std::vector<df::reaction_reagent*> *in_reag
|
||||||
|
, std::vector<df::item*> *out_items,bool *call_native){};
|
||||||
|
static void handle_contaminate_wound(color_ostream &out,df::item_actual*,df::unit* unit, df::unit_wound* wound, uint8_t a1, int16_t a2){};
|
||||||
|
static void handle_projitem_ci(color_ostream &out,df::proj_itemst*,bool){};
|
||||||
|
static void handle_projitem_cm(color_ostream &out,df::proj_itemst*){};
|
||||||
|
static void handle_projunit_ci(color_ostream &out,df::proj_unitst*,bool){};
|
||||||
|
static void handle_projunit_cm(color_ostream &out,df::proj_unitst*){};
|
||||||
|
|
||||||
|
DEFINE_LUA_EVENT_2(onWorkshopFillSidebarMenu, handle_fillsidebar, df::building_workshopst*,bool* );
|
||||||
|
DEFINE_LUA_EVENT_1(postWorkshopFillSidebarMenu, handle_postfillsidebar, df::building_workshopst*);
|
||||||
|
|
||||||
|
DEFINE_LUA_EVENT_6(onReactionComplete, handle_reaction_done,df::reaction*, df::unit *, std::vector<df::item*> *,std::vector<df::reaction_reagent*> *,std::vector<df::item*> *,bool *);
|
||||||
|
DEFINE_LUA_EVENT_5(onItemContaminateWound, handle_contaminate_wound, df::item_actual*,df::unit* , df::unit_wound* , uint8_t , int16_t );
|
||||||
|
//projectiles
|
||||||
|
DEFINE_LUA_EVENT_2(onProjItemCheckImpact, handle_projitem_ci, df::proj_itemst*,bool );
|
||||||
|
DEFINE_LUA_EVENT_1(onProjItemCheckMovement, handle_projitem_cm, df::proj_itemst*);
|
||||||
|
DEFINE_LUA_EVENT_2(onProjUnitCheckImpact, handle_projunit_ci, df::proj_unitst*,bool );
|
||||||
|
DEFINE_LUA_EVENT_1(onProjUnitCheckMovement, handle_projunit_cm, df::proj_unitst* );
|
||||||
|
|
||||||
|
DFHACK_PLUGIN_LUA_EVENTS {
|
||||||
|
DFHACK_LUA_EVENT(onWorkshopFillSidebarMenu),
|
||||||
|
DFHACK_LUA_EVENT(postWorkshopFillSidebarMenu),
|
||||||
|
DFHACK_LUA_EVENT(onReactionComplete),
|
||||||
|
DFHACK_LUA_EVENT(onItemContaminateWound),
|
||||||
|
DFHACK_LUA_EVENT(onProjItemCheckImpact),
|
||||||
|
DFHACK_LUA_EVENT(onProjItemCheckMovement),
|
||||||
|
DFHACK_LUA_EVENT(onProjUnitCheckImpact),
|
||||||
|
DFHACK_LUA_EVENT(onProjUnitCheckMovement),
|
||||||
|
DFHACK_LUA_END
|
||||||
|
};
|
||||||
|
struct workshop_hook : df::building_workshopst{
|
||||||
|
typedef df::building_workshopst interpose_base;
|
||||||
|
DEFINE_VMETHOD_INTERPOSE(void,fillSidebarMenu,())
|
||||||
|
{
|
||||||
|
CoreSuspendClaimer suspend;
|
||||||
|
color_ostream_proxy out(Core::getInstance().getConsole());
|
||||||
|
bool call_native=true;
|
||||||
|
onWorkshopFillSidebarMenu(out,this,&call_native);
|
||||||
|
if(call_native)
|
||||||
|
INTERPOSE_NEXT(fillSidebarMenu)();
|
||||||
|
postWorkshopFillSidebarMenu(out,this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
IMPLEMENT_VMETHOD_INTERPOSE(workshop_hook, fillSidebarMenu);
|
||||||
|
struct product_hook : item_product {
|
||||||
|
typedef item_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, df::job_skill skill,
|
||||||
|
df::historical_entity *entity, df::world_site *site)
|
||||||
|
) {
|
||||||
|
if (auto product = products[this])
|
||||||
|
{
|
||||||
|
df::reaction* this_reaction=product->react;
|
||||||
|
CoreSuspendClaimer suspend;
|
||||||
|
color_ostream_proxy out(Core::getInstance().getConsole());
|
||||||
|
bool call_native=true;
|
||||||
|
onReactionComplete(out,this_reaction,unit,in_items,in_reag,out_items,&call_native);
|
||||||
|
if(!call_native)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
INTERPOSE_NEXT(produce)(unit, out_items, in_reag, in_items, quantity, skill, entity, site);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
IMPLEMENT_VMETHOD_INTERPOSE(product_hook, produce);
|
||||||
|
|
||||||
|
|
||||||
|
struct item_hooks :df::item_actual {
|
||||||
|
typedef df::item_actual interpose_base;
|
||||||
|
|
||||||
|
DEFINE_VMETHOD_INTERPOSE(void, contaminateWound,(df::unit* unit, df::unit_wound* wound, uint8_t a1, int16_t a2))
|
||||||
|
{
|
||||||
|
CoreSuspendClaimer suspend;
|
||||||
|
color_ostream_proxy out(Core::getInstance().getConsole());
|
||||||
|
onItemContaminateWound(out,this,unit,wound,a1,a2);
|
||||||
|
INTERPOSE_NEXT(contaminateWound)(unit,wound,a1,a2);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
IMPLEMENT_VMETHOD_INTERPOSE(item_hooks, contaminateWound);
|
||||||
|
|
||||||
|
struct proj_item_hook: df::proj_itemst{
|
||||||
|
typedef df::proj_itemst interpose_base;
|
||||||
|
DEFINE_VMETHOD_INTERPOSE(bool,checkImpact,(bool mode))
|
||||||
|
{
|
||||||
|
CoreSuspendClaimer suspend;
|
||||||
|
color_ostream_proxy out(Core::getInstance().getConsole());
|
||||||
|
onProjItemCheckImpact(out,this,mode);
|
||||||
|
return INTERPOSE_NEXT(checkImpact)(mode); //returns destroy item or not?
|
||||||
|
}
|
||||||
|
DEFINE_VMETHOD_INTERPOSE(bool,checkMovement,())
|
||||||
|
{
|
||||||
|
CoreSuspendClaimer suspend;
|
||||||
|
color_ostream_proxy out(Core::getInstance().getConsole());
|
||||||
|
onProjItemCheckMovement(out,this);
|
||||||
|
return INTERPOSE_NEXT(checkMovement)();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
IMPLEMENT_VMETHOD_INTERPOSE(proj_item_hook,checkImpact);
|
||||||
|
IMPLEMENT_VMETHOD_INTERPOSE(proj_item_hook,checkMovement);
|
||||||
|
|
||||||
|
struct proj_unit_hook: df::proj_unitst{
|
||||||
|
typedef df::proj_unitst interpose_base;
|
||||||
|
DEFINE_VMETHOD_INTERPOSE(bool,checkImpact,(bool mode))
|
||||||
|
{
|
||||||
|
CoreSuspendClaimer suspend;
|
||||||
|
color_ostream_proxy out(Core::getInstance().getConsole());
|
||||||
|
onProjUnitCheckImpact(out,this,mode);
|
||||||
|
return INTERPOSE_NEXT(checkImpact)(mode); //returns destroy item or not?
|
||||||
|
}
|
||||||
|
DEFINE_VMETHOD_INTERPOSE(bool,checkMovement,())
|
||||||
|
{
|
||||||
|
CoreSuspendClaimer suspend;
|
||||||
|
color_ostream_proxy out(Core::getInstance().getConsole());
|
||||||
|
onProjUnitCheckMovement(out,this);
|
||||||
|
return INTERPOSE_NEXT(checkMovement)();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
IMPLEMENT_VMETHOD_INTERPOSE(proj_unit_hook,checkImpact);
|
||||||
|
IMPLEMENT_VMETHOD_INTERPOSE(proj_unit_hook,checkMovement);
|
||||||
|
/*
|
||||||
|
* Scan raws for matching reactions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
static void parse_product(
|
||||||
|
color_ostream &out, ProductInfo &info, df::reaction *react, item_product *prod
|
||||||
|
) {
|
||||||
|
info.react = react;
|
||||||
|
info.product = prod;
|
||||||
|
info.material.mat_type = prod->mat_type;
|
||||||
|
info.material.mat_index = prod->mat_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool find_reactions(color_ostream &out)
|
||||||
|
{
|
||||||
|
reactions.clear();
|
||||||
|
|
||||||
|
auto &rlist = world->raws.reactions;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < rlist.size(); i++)
|
||||||
|
{
|
||||||
|
if (!is_lua_hook(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<item_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(workshop_hook,fillSidebarMenu).apply(enable);
|
||||||
|
INTERPOSE_HOOK(item_hooks,contaminateWound).apply(enable);
|
||||||
|
INTERPOSE_HOOK(proj_unit_hook,checkImpact).apply(enable);
|
||||||
|
INTERPOSE_HOOK(proj_unit_hook,checkMovement).apply(enable);
|
||||||
|
INTERPOSE_HOOK(proj_item_hook,checkImpact).apply(enable);
|
||||||
|
INTERPOSE_HOOK(proj_item_hook,checkMovement).apply(enable);
|
||||||
|
}
|
||||||
|
static void world_specific_hooks(color_ostream &out,bool enable)
|
||||||
|
{
|
||||||
|
if(enable && find_reactions(out))
|
||||||
|
{
|
||||||
|
out.print("Detected reaction hooks - enabling plugin.\n");
|
||||||
|
INTERPOSE_HOOK(product_hook, produce).apply(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
INTERPOSE_HOOK(product_hook, produce).apply(false);
|
||||||
|
reactions.clear();
|
||||||
|
products.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void disable_all_hooks(color_ostream &out)
|
||||||
|
{
|
||||||
|
world_specific_hooks(out,false);
|
||||||
|
enable_hooks(false);
|
||||||
|
}
|
||||||
|
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
|
||||||
|
{
|
||||||
|
switch (event) {
|
||||||
|
case SC_WORLD_LOADED:
|
||||||
|
world_specific_hooks(out,true);
|
||||||
|
break;
|
||||||
|
case SC_WORLD_UNLOADED:
|
||||||
|
world_specific_hooks(out,false);
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
|
||||||
|
{
|
||||||
|
if (Core::getInstance().isWorldLoaded())
|
||||||
|
plugin_onstatechange(out, SC_WORLD_LOADED);
|
||||||
|
enable_hooks(true);
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
|
||||||
|
{
|
||||||
|
disable_all_hooks(out);
|
||||||
|
return CR_OK;
|
||||||
|
}
|
@ -0,0 +1,182 @@
|
|||||||
|
|
||||||
|
#include "Core.h"
|
||||||
|
#include "Console.h"
|
||||||
|
#include "DataDefs.h"
|
||||||
|
#include "Export.h"
|
||||||
|
#include "PluginManager.h"
|
||||||
|
|
||||||
|
#include "modules/World.h"
|
||||||
|
|
||||||
|
#include "df/construction.h"
|
||||||
|
#include "df/game_mode.h"
|
||||||
|
#include "df/map_block.h"
|
||||||
|
#include "df/map_block_column.h"
|
||||||
|
#include "df/world.h"
|
||||||
|
#include "df/z_level_flags.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
using namespace DFHack;
|
||||||
|
using namespace df::enums;
|
||||||
|
|
||||||
|
command_result infiniteSky (color_ostream &out, std::vector <std::string> & parameters);
|
||||||
|
|
||||||
|
DFHACK_PLUGIN("infiniteSky");
|
||||||
|
|
||||||
|
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
|
||||||
|
{
|
||||||
|
commands.push_back(PluginCommand(
|
||||||
|
"infiniteSky",
|
||||||
|
"Creates new sky levels on request, or as you construct up.",
|
||||||
|
infiniteSky, false,
|
||||||
|
"Usage:\n"
|
||||||
|
" infiniteSky\n"
|
||||||
|
" creates one more z-level\n"
|
||||||
|
" infiniteSky [n]\n"
|
||||||
|
" creates n more z-level(s)\n"
|
||||||
|
" infiniteSky enable\n"
|
||||||
|
" enables monitoring of constructions\n"
|
||||||
|
" infiniteSky disable\n"
|
||||||
|
" disable monitoring of constructions\n"
|
||||||
|
"\n"
|
||||||
|
"If construction monitoring is enabled, then the plugin will automatically create new sky z-levels as you construct upward.\n"
|
||||||
|
));
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
|
||||||
|
{
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
|
||||||
|
{
|
||||||
|
switch (event) {
|
||||||
|
case SC_GAME_LOADED:
|
||||||
|
// initialize from the world just loaded
|
||||||
|
break;
|
||||||
|
case SC_GAME_UNLOADED:
|
||||||
|
// cleanup
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
static size_t constructionSize = 0;
|
||||||
|
static bool enabled = false;
|
||||||
|
void doInfiniteSky(color_ostream& out, int32_t howMany);
|
||||||
|
|
||||||
|
DFhackCExport command_result plugin_onupdate ( color_ostream &out )
|
||||||
|
{
|
||||||
|
if ( !enabled )
|
||||||
|
return CR_OK;
|
||||||
|
if ( !Core::getInstance().isMapLoaded() )
|
||||||
|
return CR_OK;
|
||||||
|
{
|
||||||
|
t_gamemodes mode;
|
||||||
|
if ( !World::ReadGameMode(mode) )
|
||||||
|
return CR_FAILURE;
|
||||||
|
if ( mode.g_mode != df::enums::game_mode::DWARF )
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( df::global::world->constructions.size() == constructionSize )
|
||||||
|
return CR_OK;
|
||||||
|
int32_t zNow = df::global::world->map.z_count_block;
|
||||||
|
for ( size_t a = constructionSize; a < df::global::world->constructions.size(); a++ ) {
|
||||||
|
df::construction* construct = df::global::world->constructions[a];
|
||||||
|
if ( construct->pos.z+2 < zNow )
|
||||||
|
continue;
|
||||||
|
doInfiniteSky(out, 1);
|
||||||
|
zNow = df::global::world->map.z_count_block;
|
||||||
|
///break;
|
||||||
|
}
|
||||||
|
constructionSize = df::global::world->constructions.size();
|
||||||
|
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void doInfiniteSky(color_ostream& out, int32_t howMany) {
|
||||||
|
df::world* world = df::global::world;
|
||||||
|
CoreSuspender suspend;
|
||||||
|
int32_t x_count_block = world->map.x_count_block;
|
||||||
|
int32_t y_count_block = world->map.y_count_block;
|
||||||
|
for ( int32_t count = 0; count < howMany; count++ ) {
|
||||||
|
//change the size of the pointer stuff
|
||||||
|
int32_t z_count_block = world->map.z_count_block;
|
||||||
|
df::map_block**** block_index = world->map.block_index;
|
||||||
|
for ( int32_t a = 0; a < x_count_block; a++ ) {
|
||||||
|
for ( int32_t b = 0; b < y_count_block; b++ ) {
|
||||||
|
df::map_block** blockColumn = new df::map_block*[z_count_block+1];
|
||||||
|
memcpy(blockColumn, block_index[a][b], z_count_block*sizeof(df::map_block*));
|
||||||
|
blockColumn[z_count_block] = NULL;
|
||||||
|
delete[] block_index[a][b];
|
||||||
|
block_index[a][b] = blockColumn;
|
||||||
|
|
||||||
|
//deal with map_block_column stuff even though it'd probably be fine
|
||||||
|
df::map_block_column* column = world->map.column_index[a][b];
|
||||||
|
if ( !column ) {
|
||||||
|
out.print("%s, line %d: column is null (%d, %d).\n", __FILE__, __LINE__, a, b);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
df::map_block_column::T_unmined_glyphs* glyphs = new df::map_block_column::T_unmined_glyphs;
|
||||||
|
glyphs->x[0] = 0;
|
||||||
|
glyphs->x[1] = 1;
|
||||||
|
glyphs->x[2] = 2;
|
||||||
|
glyphs->x[3] = 3;
|
||||||
|
glyphs->y[0] = 0;
|
||||||
|
glyphs->y[1] = 0;
|
||||||
|
glyphs->y[2] = 0;
|
||||||
|
glyphs->y[3] = 0;
|
||||||
|
glyphs->tile[0] = 'e';
|
||||||
|
glyphs->tile[1] = 'x';
|
||||||
|
glyphs->tile[2] = 'p';
|
||||||
|
glyphs->tile[3] = '^';
|
||||||
|
column->unmined_glyphs.push_back(glyphs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
df::z_level_flags* flags = new df::z_level_flags[z_count_block+1];
|
||||||
|
memcpy(flags, world->map.z_level_flags, z_count_block*sizeof(df::z_level_flags));
|
||||||
|
flags[z_count_block].whole = 0;
|
||||||
|
flags[z_count_block].bits.update = 1;
|
||||||
|
world->map.z_count_block++;
|
||||||
|
world->map.z_count++;
|
||||||
|
delete[] world->map.z_level_flags;
|
||||||
|
world->map.z_level_flags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
command_result infiniteSky (color_ostream &out, std::vector <std::string> & parameters)
|
||||||
|
{
|
||||||
|
if ( parameters.size() > 1 )
|
||||||
|
return CR_WRONG_USAGE;
|
||||||
|
if ( parameters.size() == 0 ) {
|
||||||
|
out.print("Construction monitoring is %s.\n", enabled ? "enabled" : "disabled");
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
if (parameters[0] == "enable") {
|
||||||
|
enabled = true;
|
||||||
|
out.print("Construction monitoring enabled.\n");
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
if (parameters[0] == "disable") {
|
||||||
|
enabled = false;
|
||||||
|
out.print("Construction monitoring disabled.\n");
|
||||||
|
constructionSize = 0;
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
int32_t howMany = 0;
|
||||||
|
howMany = atoi(parameters[0].c_str());
|
||||||
|
out.print("InfiniteSky: creating %d new z-level%s of sky.\n", howMany, howMany == 1 ? "" : "s" );
|
||||||
|
doInfiniteSky(out, howMany);
|
||||||
|
return CR_OK;
|
||||||
|
}
|
@ -0,0 +1,112 @@
|
|||||||
|
local _ENV = mkmodule('plugins.eventful')
|
||||||
|
--[[
|
||||||
|
|
||||||
|
|
||||||
|
--]]
|
||||||
|
local function getShopName(btype,bsubtype,bcustom)
|
||||||
|
local typenames_shop={[df.workshop_type.Carpenters]="CARPENTERS",[df.workshop_type.Farmers]="FARMERS",
|
||||||
|
[df.workshop_type.Masons]="MASONS",[df.workshop_type.Craftsdwarfs]="CRAFTSDWARFS",
|
||||||
|
[df.workshop_type.Jewelers]="JEWELERS",[df.workshop_type.MetalsmithsForge]="METALSMITHSFORGE",
|
||||||
|
[df.workshop_type.MagmaForge]="MAGMAFORGE",[df.workshop_type.Bowyers]="BOWYERS",
|
||||||
|
[df.workshop_type.Mechanics]="MECHANICS",[df.workshop_type.Siege]="SIEGE",
|
||||||
|
[df.workshop_type.Butchers]="BUTCHERS",[df.workshop_type.Leatherworks]="LEATHERWORKS",
|
||||||
|
[df.workshop_type.Tanners]="TANNERS",[df.workshop_type.Clothiers]="CLOTHIERS",
|
||||||
|
[df.workshop_type.Fishery]="FISHERY",[df.workshop_type.Still]="STILL",
|
||||||
|
[df.workshop_type.Loom]="LOOM",[df.workshop_type.Quern]="QUERN",
|
||||||
|
[df.workshop_type.Kennels]="KENNELS",[df.workshop_type.Ashery]="ASHERY",
|
||||||
|
[df.workshop_type.Kitchen]="KITCHEN",[df.workshop_type.Dyers]="DYERS",
|
||||||
|
[df.workshop_type.Tool]="TOOL",[df.workshop_type.Millstone]="MILLSTONE",
|
||||||
|
}
|
||||||
|
local typenames_furnace={[df.furnace_type.WoodFurnace]="WOOD_FURNACE",[df.furnace_type.Smelter]="SMELTER",
|
||||||
|
[df.furnace_type.GlassFurnace]="GLASS_FURNACE",[df.furnace_type.MagmaSmelter]="MAGMA_SMELTER",
|
||||||
|
[df.furnace_type.MagmaGlassFurnace]="MAGMA_GLASS_FURNACE",[df.furnace_type.MagmaKiln]="MAGMA_KILN",
|
||||||
|
[df.furnace_type.Kiln]="KILN"}
|
||||||
|
if btype==df.building_type.Workshop then
|
||||||
|
if typenames_shop[bsubtype]~=nil then
|
||||||
|
return typenames_shop[bsubtype]
|
||||||
|
else
|
||||||
|
return nil --todo add custom (not very useful)
|
||||||
|
end
|
||||||
|
elseif btype==df.building_type.Furnace then
|
||||||
|
if typenames_furnace[bsubtype]~=nil then
|
||||||
|
return typenames_furnace[bsubtype]
|
||||||
|
else
|
||||||
|
return nil --todo add custom (not very useful)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
_registeredStuff={}
|
||||||
|
local function unregall(state)
|
||||||
|
if state==SC_WORLD_UNLOADED then
|
||||||
|
onReactionComplete._library=nil
|
||||||
|
postWorkshopFillSidebarMenu._library=nil
|
||||||
|
dfhack.onStateChange.eventful= nil
|
||||||
|
_registeredStuff={}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local function onReact(reaction,unit,input_items,input_reagents,output_items,call_native)
|
||||||
|
if _registeredStuff.reactionCallbacks and _registeredStuff.reactionCallbacks[reaction.code] then
|
||||||
|
_registeredStuff.reactionCallbacks[reaction.code](reaction,unit,input_items,input_reagents,output_items,call_native)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local function onPostSidebar(workshop)
|
||||||
|
local shop_id=getShopName(workshop:getType(),workshop:getSubtype(),workshop:getCustomType())
|
||||||
|
if shop_id then
|
||||||
|
if _registeredStuff.shopNonNative and _registeredStuff.shopNonNative[shop_id] then
|
||||||
|
if _registeredStuff.shopNonNative[shop_id].all then
|
||||||
|
--[[for _,button in ipairs(df.global.ui_sidebar_menus.workshop_job.choices_all) do
|
||||||
|
button.is_hidden=true
|
||||||
|
end]]
|
||||||
|
df.global.ui_sidebar_menus.workshop_job.choices_visible:resize(0)
|
||||||
|
else
|
||||||
|
--todo by name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if _registeredStuff.reactionToShop and _registeredStuff.reactionToShop[shop_id] then
|
||||||
|
for _,reaction_name in ipairs(_registeredStuff.reactionToShop[shop_id]) do
|
||||||
|
local new_button=df.interface_button_building_new_jobst:new()
|
||||||
|
--new_button.hotkey_id=--todo get hotkey
|
||||||
|
new_button.is_hidden=false
|
||||||
|
new_button.building=workshop
|
||||||
|
new_button.job_type=df.job_type.CustomReaction --could be used for other stuff too i guess...
|
||||||
|
new_button.reaction_name=reaction_name
|
||||||
|
new_button.is_custom=true
|
||||||
|
local wjob=df.global.ui_sidebar_menus.workshop_job
|
||||||
|
wjob.choices_all:insert("#",new_button)
|
||||||
|
wjob.choices_visible:insert("#",new_button)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function registerReaction(reaction_name,callback)
|
||||||
|
_registeredStuff.reactionCallbacks=_registeredStuff.reactionCallbacks or {}
|
||||||
|
_registeredStuff.reactionCallbacks[reaction_name]=callback
|
||||||
|
onReactionComplete._library=onReact
|
||||||
|
dfhack.onStateChange.eventful=unregall
|
||||||
|
end
|
||||||
|
|
||||||
|
function removeNative(shop_name,name)
|
||||||
|
_registeredStuff.shopNonNative=_registeredStuff.shopNonNative or {}
|
||||||
|
local shops=_registeredStuff.shopNonNative
|
||||||
|
shops[shop_name]=shops[shop_name] or {}
|
||||||
|
if name~=nil then
|
||||||
|
table.insert(shops[shop_name],name)
|
||||||
|
else
|
||||||
|
shops[shop_name].all=true
|
||||||
|
end
|
||||||
|
postWorkshopFillSidebarMenu._library=onPostSidebar
|
||||||
|
dfhack.onStateChange.eventful=unregall
|
||||||
|
end
|
||||||
|
|
||||||
|
function addReactionToShop(reaction_name,shop_name)
|
||||||
|
_registeredStuff.reactionToShop=_registeredStuff.reactionToShop or {}
|
||||||
|
local shops=_registeredStuff.reactionToShop
|
||||||
|
shops[shop_name]=shops[shop_name] or {}
|
||||||
|
table.insert(shops[shop_name],reaction_name)
|
||||||
|
postWorkshopFillSidebarMenu._library=onPostSidebar
|
||||||
|
dfhack.onStateChange.eventful=unregall
|
||||||
|
end
|
||||||
|
|
||||||
|
return _ENV
|
@ -1,13 +0,0 @@
|
|||||||
local _ENV = mkmodule('plugins.reactionhooks')
|
|
||||||
|
|
||||||
--[[
|
|
||||||
|
|
||||||
Native events:
|
|
||||||
|
|
||||||
* onReactionComplete(burrow)
|
|
||||||
|
|
||||||
--]]
|
|
||||||
|
|
||||||
rawset_default(_ENV, dfhack.reactionhooks)
|
|
||||||
|
|
||||||
return _ENV
|
|
@ -1,322 +0,0 @@
|
|||||||
#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_itemst.h"
|
|
||||||
#include "df/matter_state.h"
|
|
||||||
#include "df/contaminant.h"
|
|
||||||
|
|
||||||
#include "MiscUtils.h"
|
|
||||||
#include "LuaTools.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_itemst item_product;
|
|
||||||
|
|
||||||
DFHACK_PLUGIN("reactionhooks");
|
|
||||||
|
|
||||||
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;
|
|
||||||
item_product *product;
|
|
||||||
|
|
||||||
MaterialSource material;
|
|
||||||
|
|
||||||
bool isValid() {
|
|
||||||
return (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_lua_hook(const std::string &name)
|
|
||||||
{
|
|
||||||
return name.size() > 9 && memcmp(name.data(), "LUA_HOOK_", 9) == 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_reaction_done(color_ostream &out,df::reaction*, df::unit *unit, std::vector<df::item*> *in_items,std::vector<df::reaction_reagent*> *in_reag
|
|
||||||
, std::vector<df::item*> *out_items,bool *call_native){};
|
|
||||||
|
|
||||||
DEFINE_LUA_EVENT_6(onReactionComplete, handle_reaction_done,df::reaction*, df::unit *, std::vector<df::item*> *,std::vector<df::reaction_reagent*> *,std::vector<df::item*> *,bool *);
|
|
||||||
|
|
||||||
|
|
||||||
DFHACK_PLUGIN_LUA_EVENTS {
|
|
||||||
DFHACK_LUA_EVENT(onReactionComplete),
|
|
||||||
DFHACK_LUA_END
|
|
||||||
};
|
|
||||||
|
|
||||||
struct product_hook : item_product {
|
|
||||||
typedef item_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, df::job_skill skill,
|
|
||||||
df::historical_entity *entity, df::world_site *site)
|
|
||||||
) {
|
|
||||||
if (auto product = products[this])
|
|
||||||
{
|
|
||||||
df::reaction* this_reaction=product->react;
|
|
||||||
CoreSuspendClaimer suspend;
|
|
||||||
color_ostream_proxy out(Core::getInstance().getConsole());
|
|
||||||
bool call_native=true;
|
|
||||||
onReactionComplete(out,this_reaction,unit,in_items,in_reag,out_items,&call_native);
|
|
||||||
if(!call_native)
|
|
||||||
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 parse_product(
|
|
||||||
color_ostream &out, ProductInfo &info, df::reaction *react, item_product *prod
|
|
||||||
) {
|
|
||||||
info.react = react;
|
|
||||||
info.product = prod;
|
|
||||||
info.material.mat_type = prod->mat_type;
|
|
||||||
info.material.mat_index = prod->mat_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool find_reactions(color_ostream &out)
|
|
||||||
{
|
|
||||||
reactions.clear();
|
|
||||||
|
|
||||||
auto &rlist = world->raws.reactions;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < rlist.size(); i++)
|
|
||||||
{
|
|
||||||
if (!is_lua_hook(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<item_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(product_hook, produce).apply(enable);
|
|
||||||
}
|
|
||||||
|
|
||||||
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
|
|
||||||
{
|
|
||||||
switch (event) {
|
|
||||||
case SC_WORLD_LOADED:
|
|
||||||
if (find_reactions(out))
|
|
||||||
{
|
|
||||||
out.print("Detected reaction hooks - enabling plugin.\n");
|
|
||||||
enable_hooks(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
enable_hooks(false);
|
|
||||||
break;
|
|
||||||
case SC_WORLD_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().isWorldLoaded())
|
|
||||||
plugin_onstatechange(out, SC_WORLD_LOADED);
|
|
||||||
|
|
||||||
return CR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
|
|
||||||
{
|
|
||||||
enable_hooks(false);
|
|
||||||
return CR_OK;
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,87 @@
|
|||||||
|
|
||||||
|
#include "Core.h"
|
||||||
|
#include "Console.h"
|
||||||
|
#include "DataDefs.h"
|
||||||
|
#include "Export.h"
|
||||||
|
#include "PluginManager.h"
|
||||||
|
|
||||||
|
#include "modules/EventManager.h"
|
||||||
|
|
||||||
|
#include "df/caste_raw.h"
|
||||||
|
#include "df/creature_raw.h"
|
||||||
|
#include "df/syndrome.h"
|
||||||
|
#include "df/unit.h"
|
||||||
|
#include "df/unit_syndrome.h"
|
||||||
|
#include "df/world.h"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
using namespace DFHack;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
DFHACK_PLUGIN("trueTransformation");
|
||||||
|
|
||||||
|
void syndromeHandler(color_ostream& out, void* ptr);
|
||||||
|
|
||||||
|
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
|
||||||
|
{
|
||||||
|
EventManager::EventHandler syndrome(syndromeHandler, 1);
|
||||||
|
EventManager::registerListener(EventManager::EventType::SYNDROME, syndrome, plugin_self);
|
||||||
|
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void syndromeHandler(color_ostream& out, void* ptr) {
|
||||||
|
EventManager::SyndromeData* data = (EventManager::SyndromeData*)ptr;
|
||||||
|
//out.print("Syndrome started: unit %d, syndrome %d.\n", data->unitId, data->syndromeIndex);
|
||||||
|
|
||||||
|
int32_t index = df::unit::binsearch_index(df::global::world->units.active, data->unitId);
|
||||||
|
if ( index < 0 ) {
|
||||||
|
out.print("%s, line %d: couldn't find unit.\n", __FILE__, __LINE__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
df::unit* unit = df::global::world->units.active[index];
|
||||||
|
df::unit_syndrome* unit_syndrome = unit->syndromes.active[data->syndromeIndex];
|
||||||
|
df::syndrome* syndrome = df::global::world->raws.syndromes.all[unit_syndrome->type];
|
||||||
|
|
||||||
|
bool foundIt = false;
|
||||||
|
int32_t raceId = -1;
|
||||||
|
df::creature_raw* creatureRaw = NULL;
|
||||||
|
int32_t casteId = -1;
|
||||||
|
for ( size_t a = 0; a < syndrome->syn_class.size(); a++ ) {
|
||||||
|
if ( *syndrome->syn_class[a] == "\\PERMANENT" ) {
|
||||||
|
foundIt = true;
|
||||||
|
}
|
||||||
|
if ( foundIt && raceId == -1 ) {
|
||||||
|
//find the race with the name
|
||||||
|
string& name = *syndrome->syn_class[a];
|
||||||
|
for ( size_t b = 0; b < df::global::world->raws.creatures.all.size(); b++ ) {
|
||||||
|
df::creature_raw* creature = df::global::world->raws.creatures.all[b];
|
||||||
|
if ( creature->creature_id != name )
|
||||||
|
continue;
|
||||||
|
raceId = b;
|
||||||
|
creatureRaw = creature;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( foundIt && raceId != -1 ) {
|
||||||
|
string& name = *syndrome->syn_class[a];
|
||||||
|
for ( size_t b = 0; b < creatureRaw->caste.size(); b++ ) {
|
||||||
|
df::caste_raw* caste = creatureRaw->caste[b];
|
||||||
|
if ( caste->caste_id != name )
|
||||||
|
continue;
|
||||||
|
casteId = b;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( !foundIt || raceId == -1 || casteId == -1 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
unit->enemy.normal_race = raceId;
|
||||||
|
unit->enemy.normal_caste = casteId;
|
||||||
|
//that's it!
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,70 @@
|
|||||||
|
#include "Core.h"
|
||||||
|
#include "Console.h"
|
||||||
|
#include "Export.h"
|
||||||
|
#include "PluginManager.h"
|
||||||
|
#include "DataDefs.h"
|
||||||
|
#include "modules/World.h"
|
||||||
|
#include "df/global_objects.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
using namespace std;
|
||||||
|
using namespace DFHack;
|
||||||
|
|
||||||
|
DFHACK_PLUGIN("workNow");
|
||||||
|
|
||||||
|
static bool active = false;
|
||||||
|
|
||||||
|
DFhackCExport command_result workNow(color_ostream& out, vector<string>& parameters);
|
||||||
|
|
||||||
|
DFhackCExport command_result plugin_init(color_ostream& out, std::vector<PluginCommand> &commands) {
|
||||||
|
commands.push_back(PluginCommand("workNow", "makes dwarves look for jobs every time you pause", workNow, false, "When workNow is active, every time the game pauses, DF will make dwarves perform any appropriate available jobs. This includes when you one step through the game using the pause menu.\n"
|
||||||
|
"workNow 1\n"
|
||||||
|
" activate workNow\n"
|
||||||
|
"workNow 0\n"
|
||||||
|
" deactivate workNow\n"));
|
||||||
|
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
DFhackCExport command_result plugin_shutdown ( color_ostream &out ) {
|
||||||
|
active = false;
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event e) {
|
||||||
|
if ( !active )
|
||||||
|
return CR_OK;
|
||||||
|
if ( e == DFHack::SC_WORLD_UNLOADED ) {
|
||||||
|
active = false;
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
if ( e != DFHack::SC_PAUSED )
|
||||||
|
return CR_OK;
|
||||||
|
|
||||||
|
*df::global::process_jobs = true;
|
||||||
|
*df::global::process_dig = true;
|
||||||
|
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
DFhackCExport command_result workNow(color_ostream& out, vector<string>& parameters) {
|
||||||
|
if ( parameters.size() == 0 ) {
|
||||||
|
out.print("workNow status = %s\n", active ? "active" : "inactive");
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
if ( parameters.size() > 1 ) {
|
||||||
|
return CR_WRONG_USAGE;
|
||||||
|
}
|
||||||
|
int32_t a = atoi(parameters[0].c_str());
|
||||||
|
|
||||||
|
if (a < 0 || a > 1)
|
||||||
|
return CR_WRONG_USAGE;
|
||||||
|
|
||||||
|
active = (bool)a;
|
||||||
|
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,161 @@
|
|||||||
|
# create arbitrary items under cursor
|
||||||
|
|
||||||
|
category = $script_args[0] || 'help'
|
||||||
|
mat_raw = $script_args[1] || 'list'
|
||||||
|
count = $script_args[2]
|
||||||
|
|
||||||
|
|
||||||
|
category = df.match_rawname(category, ['help', 'bars', 'boulders', 'plants', 'logs', 'webs']) || 'help'
|
||||||
|
|
||||||
|
if category == 'help'
|
||||||
|
puts <<EOS
|
||||||
|
Create items under the cursor.
|
||||||
|
Usage:
|
||||||
|
create [category] [raws token] [number]
|
||||||
|
|
||||||
|
Item categories:
|
||||||
|
bars, boulders, plants, logs, web
|
||||||
|
|
||||||
|
Raw token:
|
||||||
|
either a full token (PLANT_MAT:ADLER:WOOD) or the middle part only
|
||||||
|
(the missing part is autocompleted depending on the item category)
|
||||||
|
use 'list' to show all possibilities
|
||||||
|
|
||||||
|
Exemples:
|
||||||
|
create boulders hematite 30
|
||||||
|
create bars CREATURE_MAT:CAT:SOAP 10
|
||||||
|
create web cave_giant
|
||||||
|
create plants list
|
||||||
|
|
||||||
|
EOS
|
||||||
|
throw :script_finished
|
||||||
|
|
||||||
|
elsif mat_raw == 'list'
|
||||||
|
# allowed with no cursor
|
||||||
|
|
||||||
|
elsif df.cursor.x == -30000
|
||||||
|
puts "Please place the game cursor somewhere"
|
||||||
|
throw :script_finished
|
||||||
|
|
||||||
|
elsif !(maptile = df.map_tile_at(df.cursor))
|
||||||
|
puts "Error: unallocated map block !"
|
||||||
|
throw :script_finished
|
||||||
|
|
||||||
|
elsif !maptile.shape_passablehigh
|
||||||
|
puts "Error: impassible tile !"
|
||||||
|
throw :script_finished
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def match_list(tok, list)
|
||||||
|
if tok != 'list'
|
||||||
|
tok = df.match_rawname(tok, list)
|
||||||
|
if not tok
|
||||||
|
puts "Invalid raws token, use one in:"
|
||||||
|
tok = 'list'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if tok == 'list'
|
||||||
|
puts list.map { |w| w =~ /[^\w]/ ? w.inspect : w }.join(' ')
|
||||||
|
throw :script_finished
|
||||||
|
end
|
||||||
|
tok
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
case category
|
||||||
|
when 'bars'
|
||||||
|
# create metal bar, eg createbar INORGANIC:IRON
|
||||||
|
cls = DFHack::ItemBarst
|
||||||
|
if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil)
|
||||||
|
list = df.world.raws.inorganics.find_all { |ino|
|
||||||
|
ino.material.flags[:IS_METAL]
|
||||||
|
}.map { |ino| ino.id }
|
||||||
|
mat_raw = match_list(mat_raw, list)
|
||||||
|
mat_raw = "INORGANIC:#{mat_raw}"
|
||||||
|
puts mat_raw
|
||||||
|
end
|
||||||
|
customize = lambda { |item|
|
||||||
|
item.dimension = 150
|
||||||
|
item.subtype = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
when 'boulders'
|
||||||
|
cls = DFHack::ItemBoulderst
|
||||||
|
if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil)
|
||||||
|
list = df.world.raws.inorganics.find_all { |ino|
|
||||||
|
ino.material.flags[:IS_STONE]
|
||||||
|
}.map { |ino| ino.id }
|
||||||
|
mat_raw = match_list(mat_raw, list)
|
||||||
|
mat_raw = "INORGANIC:#{mat_raw}"
|
||||||
|
puts mat_raw
|
||||||
|
end
|
||||||
|
|
||||||
|
when 'plants'
|
||||||
|
cls = DFHack::ItemPlantst
|
||||||
|
if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil)
|
||||||
|
list = df.world.raws.plants.all.find_all { |plt|
|
||||||
|
plt.material.find { |mat| mat.id == 'STRUCTURAL' }
|
||||||
|
}.map { |plt| plt.id }
|
||||||
|
mat_raw = match_list(mat_raw, list)
|
||||||
|
mat_raw = "PLANT_MAT:#{mat_raw}:STRUCTURAL"
|
||||||
|
puts mat_raw
|
||||||
|
end
|
||||||
|
|
||||||
|
when 'logs'
|
||||||
|
cls = DFHack::ItemWoodst
|
||||||
|
if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil)
|
||||||
|
list = df.world.raws.plants.all.find_all { |plt|
|
||||||
|
plt.material.find { |mat| mat.id == 'WOOD' }
|
||||||
|
}.map { |plt| plt.id }
|
||||||
|
mat_raw = match_list(mat_raw, list)
|
||||||
|
mat_raw = "PLANT_MAT:#{mat_raw}:WOOD"
|
||||||
|
puts mat_raw
|
||||||
|
end
|
||||||
|
|
||||||
|
when 'webs'
|
||||||
|
cls = DFHack::ItemThreadst
|
||||||
|
if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil)
|
||||||
|
list = df.world.raws.creatures.all.find_all { |cre|
|
||||||
|
cre.material.find { |mat| mat.id == 'SILK' }
|
||||||
|
}.map { |cre| cre.creature_id }
|
||||||
|
mat_raw = match_list(mat_raw, list)
|
||||||
|
mat_raw = "CREATURE_MAT:#{mat_raw}:SILK"
|
||||||
|
puts mat_raw
|
||||||
|
end
|
||||||
|
count ||= 1
|
||||||
|
customize = lambda { |item|
|
||||||
|
item.flags.spider_web = true
|
||||||
|
item.dimension = 15000 # XXX may depend on creature (this is for GCS)
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
mat = df.decode_mat mat_raw
|
||||||
|
|
||||||
|
count ||= 20
|
||||||
|
count.to_i.times {
|
||||||
|
item = cls.cpp_new
|
||||||
|
item.id = df.item_next_id
|
||||||
|
item.stack_size = 1
|
||||||
|
item.mat_type = mat.mat_type
|
||||||
|
item.mat_index = mat.mat_index
|
||||||
|
|
||||||
|
customize[item] if customize
|
||||||
|
|
||||||
|
df.item_next_id += 1
|
||||||
|
item.categorize(true)
|
||||||
|
df.world.items.all << item
|
||||||
|
|
||||||
|
item.pos = df.cursor
|
||||||
|
item.flags.on_ground = true
|
||||||
|
df.map_tile_at.mapblock.items << item.id
|
||||||
|
df.map_tile_at.occupancy.item = true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# move game view, so that the ui menu updates
|
||||||
|
df.curview.feed_keys(:CURSOR_UP_Z)
|
||||||
|
df.curview.feed_keys(:CURSOR_DOWN_Z)
|
||||||
|
|
@ -0,0 +1,80 @@
|
|||||||
|
-- Fixes cloth/thread stockpiles by correcting material object data.
|
||||||
|
|
||||||
|
local raws = df.global.world.raws
|
||||||
|
local organic_types = raws.mat_table.organic_types
|
||||||
|
local organic_indexes = raws.mat_table.organic_indexes
|
||||||
|
|
||||||
|
local function verify(category,idx,vtype,vidx)
|
||||||
|
if idx == -1 then
|
||||||
|
-- Purely for reporting reasons
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
local tvec = organic_types[category]
|
||||||
|
if idx < 0 or idx >= #tvec or tvec[idx] ~= vtype then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return organic_indexes[category][idx] == vidx
|
||||||
|
end
|
||||||
|
|
||||||
|
local patched_cnt = 0
|
||||||
|
local mat_cnt = 0
|
||||||
|
|
||||||
|
function patch_material(mat,mat_type,mat_index)
|
||||||
|
local idxarr = mat.food_mat_index
|
||||||
|
|
||||||
|
-- These refer to fish/eggs, i.e. castes and not materials
|
||||||
|
idxarr[1] = -1
|
||||||
|
idxarr[2] = -1
|
||||||
|
idxarr[3] = -1
|
||||||
|
|
||||||
|
for i = 0,#idxarr-1 do
|
||||||
|
if not verify(i,idxarr[i],mat_type,mat_index) then
|
||||||
|
idxarr[i] = -1
|
||||||
|
patched_cnt = patched_cnt+1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
mat_cnt = mat_cnt + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
function patch_materials()
|
||||||
|
patched_cnt = 0
|
||||||
|
mat_cnt = 0
|
||||||
|
|
||||||
|
print('Fixing cloth stockpile handling (bug 5739)...')
|
||||||
|
|
||||||
|
for i,v in ipairs(raws.inorganics) do
|
||||||
|
patch_material(v.material, 0, i)
|
||||||
|
end
|
||||||
|
|
||||||
|
for i,v in ipairs(raws.creatures.all) do
|
||||||
|
for j,m in ipairs(v.material) do
|
||||||
|
patch_material(m, 19+j, i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for i,v in ipairs(raws.plants.all) do
|
||||||
|
for j,m in ipairs(v.material) do
|
||||||
|
patch_material(m, 419+j, i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
print('Patched '..patched_cnt..' bad references in '..mat_cnt..' materials.')
|
||||||
|
end
|
||||||
|
|
||||||
|
local args = {...}
|
||||||
|
|
||||||
|
if args[1] == 'enable' then
|
||||||
|
dfhack.onStateChange[_ENV] = function(sc)
|
||||||
|
if sc == SC_WORLD_LOADED then
|
||||||
|
patch_materials()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if dfhack.isWorldLoaded() then
|
||||||
|
patch_materials()
|
||||||
|
end
|
||||||
|
elseif args[1] == 'disable' then
|
||||||
|
dfhack.onStateChange[_ENV] = nil
|
||||||
|
else
|
||||||
|
patch_materials()
|
||||||
|
end
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,31 @@
|
|||||||
|
--set target unit as king/queen
|
||||||
|
local unit=dfhack.gui.getSelectedUnit()
|
||||||
|
if not unit then qerror("No unit selected") end
|
||||||
|
local newfig=dfhack.units.getNemesis(unit).figure
|
||||||
|
local my_entity=df.historical_entity.find(df.global.ui.civ_id)
|
||||||
|
local monarch_id
|
||||||
|
for k,v in pairs(my_entity.positions.own) do
|
||||||
|
if v.code=="MONARCH" then
|
||||||
|
monarch_id=v.id
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not monarch_id then qerror("No monarch found!") end
|
||||||
|
local old_id
|
||||||
|
for pos_id,v in pairs(my_entity.positions.assignments) do
|
||||||
|
if v.position_id==monarch_id then
|
||||||
|
old_id=v.histfig
|
||||||
|
v.histfig=newfig.id
|
||||||
|
local oldfig=df.historical_figure.find(old_id)
|
||||||
|
|
||||||
|
for k,v in pairs(oldfig.entity_links) do
|
||||||
|
if df.histfig_entity_link_positionst:is_instance(v) and v.assignment_id==pos_id and v.entity_id==df.global.ui.civ_id then
|
||||||
|
oldfig.entity_links:erase(k)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
newfig.entity_links:insert("#",{new=df.histfig_entity_link_positionst,entity_id=df.global.ui.civ_id,
|
||||||
|
link_strength=100,assignment_id=pos_id,start_year=df.global.cur_year})
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue