Merge branch 'master' of https://github.com/peterix/dfhack
Conflicts: library/xml plugins/CMakeLists.txtdevelop
commit
8917892473
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "Export.h"
|
||||
#include <string>
|
||||
|
||||
namespace DFHack {
|
||||
namespace Once {
|
||||
DFHACK_EXPORT bool alreadyDone(std::string);
|
||||
DFHACK_EXPORT bool doOnce(std::string);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,131 @@
|
||||
#pragma once
|
||||
|
||||
namespace DFHack {
|
||||
namespace Random {
|
||||
|
||||
/*
|
||||
* A good explanation:
|
||||
* http://webstaff.itn.liu.se/~stegu/TNM022-2005/perlinnoiselinks/perlin-noise-math-faq.html
|
||||
*/
|
||||
|
||||
// Interpolation functions
|
||||
|
||||
template<class T>
|
||||
inline T s_curve(T t)
|
||||
{
|
||||
// Classical function
|
||||
//return t * t * (3 - 2*t);
|
||||
|
||||
// 2002 version from http://mrl.nyu.edu/~perlin/paper445.pdf
|
||||
return t * t * t * (t * (t * 6 - 15) + 10);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline T lerp(T s, T a, T b)
|
||||
{
|
||||
return a + s * (b-a);
|
||||
}
|
||||
|
||||
// Dot product of VSIZE vectors pointed by pa, pb
|
||||
|
||||
template<class T, unsigned i>
|
||||
struct DotProduct {
|
||||
static inline T eval(T *pa, T *pb);
|
||||
};
|
||||
template<class T>
|
||||
struct DotProduct<T,0> {
|
||||
static inline T eval(T *pa, T *pb) { return pa[0]*pb[0]; }
|
||||
};
|
||||
template<class T, unsigned i>
|
||||
inline T DotProduct<T,i>::eval(T *pa, T *pb) {
|
||||
return DotProduct<T,i-1>::eval(pa, pb) + pa[i]*pb[i];
|
||||
}
|
||||
|
||||
// Templates used to force unrolling and inlining of the loops
|
||||
|
||||
template<class T, unsigned VSIZE, unsigned BITS, class IDXT>
|
||||
template<unsigned mask>
|
||||
struct PerlinNoise<T,VSIZE,BITS,IDXT>::Impl<mask,-1> {
|
||||
typedef typename PerlinNoise<T,VSIZE,BITS,IDXT>::Temp Temp;
|
||||
static inline void setup(PerlinNoise<T,VSIZE,BITS,IDXT> *, const T *, Temp *) {}
|
||||
static inline T eval(PerlinNoise<T,VSIZE,BITS,IDXT> *self, Temp *pt, unsigned idx, T *pq);
|
||||
};
|
||||
|
||||
// Initialization of the temporaries from input coordinates
|
||||
|
||||
template<class T, unsigned VSIZE, unsigned BITS, class IDXT>
|
||||
template<unsigned mask, int i>
|
||||
inline void PerlinNoise<T,VSIZE,BITS,IDXT>::Impl<mask,i>::setup(
|
||||
PerlinNoise<T,VSIZE,BITS,IDXT> *self, const T *pv, Temp *pt
|
||||
) {
|
||||
Impl<mask,i-1>::setup(self, pv, pt);
|
||||
|
||||
int32_t t = int32_t(pv[i]);
|
||||
t -= (pv[i]<t);
|
||||
pt[i].s = s_curve(pt[i].r0 = pv[i] - t);
|
||||
|
||||
unsigned b = unsigned(int32_t(t));
|
||||
pt[i].b0 = self->idxmap[i][b & mask];
|
||||
pt[i].b1 = self->idxmap[i][(b+1) & mask];
|
||||
}
|
||||
|
||||
// Main recursion. Uses tables from self and pt.
|
||||
// Recursion changes current index idx, and current offset vector pq.
|
||||
|
||||
template<class T, unsigned VSIZE, unsigned BITS, class IDXT>
|
||||
template<unsigned mask>
|
||||
inline T PerlinNoise<T,VSIZE,BITS,IDXT>::Impl<mask, -1>::eval(
|
||||
PerlinNoise<T,VSIZE,BITS,IDXT> *self, Temp *pt, unsigned idx, T *pq
|
||||
) {
|
||||
return DotProduct<T,VSIZE-1>::eval(pq, self->gradients[idx]);
|
||||
}
|
||||
|
||||
template<class T, unsigned VSIZE, unsigned BITS, class IDXT>
|
||||
template<unsigned mask, int i>
|
||||
inline T PerlinNoise<T,VSIZE,BITS,IDXT>::Impl<mask,i>::eval(
|
||||
PerlinNoise<T,VSIZE,BITS,IDXT> *self, Temp *pt, unsigned idx, T *pq
|
||||
) {
|
||||
pq[i] = pt[i].r0;
|
||||
T u = Impl<mask,i-1>::eval(self, pt, idx ^ pt[i].b0, pq);
|
||||
|
||||
pq[i] -= 1;
|
||||
T v = Impl<mask,i-1>::eval(self, pt, idx ^ pt[i].b1, pq);
|
||||
|
||||
return lerp(pt[i].s, u, v);
|
||||
}
|
||||
|
||||
// Actual methods of the object
|
||||
|
||||
template<class T, unsigned VSIZE, unsigned BITS, class IDXT>
|
||||
void PerlinNoise<T,VSIZE,BITS,IDXT>::init(MersenneRNG &rng)
|
||||
{
|
||||
STATIC_ASSERT(VSIZE > 0 && BITS <= 8*sizeof(IDXT));
|
||||
|
||||
// Random unit gradient vectors
|
||||
for (unsigned i = 0; i < TSIZE; i++)
|
||||
rng.unitvector(gradients[i], VSIZE);
|
||||
|
||||
// Random permutation tables
|
||||
for (unsigned j = 0; j < VSIZE; j++)
|
||||
{
|
||||
for (unsigned i = 0; i < TSIZE; i++)
|
||||
idxmap[j][i] = i;
|
||||
|
||||
rng.permute(idxmap[j], TSIZE);
|
||||
}
|
||||
}
|
||||
|
||||
template<class T, unsigned VSIZE, unsigned BITS, class IDXT>
|
||||
T PerlinNoise<T,VSIZE,BITS,IDXT>::eval(const T coords[VSIZE])
|
||||
{
|
||||
// Precomputed properties from the coordinates
|
||||
Temp tmp[VSIZE];
|
||||
// Temporary used to build the current offset vector
|
||||
T q[VSIZE];
|
||||
|
||||
Impl<TSIZE-1,VSIZE-1>::setup(this, coords, tmp);
|
||||
|
||||
return Impl<TSIZE-1,VSIZE-1>::eval(this, tmp, 0, q);
|
||||
}
|
||||
|
||||
}} // namespace
|
@ -0,0 +1,179 @@
|
||||
/*
|
||||
https://github.com/peterix/dfhack
|
||||
Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any
|
||||
damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it and
|
||||
redistribute it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must
|
||||
not claim that you wrote the original software. If you use this
|
||||
software in a product, an acknowledgment in the product documentation
|
||||
would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and
|
||||
must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef CL_MOD_RANDOM
|
||||
#define CL_MOD_RANDOM
|
||||
/**
|
||||
* \defgroup grp_random Random: Random number and noise generation
|
||||
* @ingroup grp_modules
|
||||
*/
|
||||
|
||||
#include "Export.h"
|
||||
#include "Module.h"
|
||||
#include "Types.h"
|
||||
|
||||
#include "DataDefs.h"
|
||||
|
||||
namespace DFHack
|
||||
{
|
||||
namespace Random
|
||||
{
|
||||
class DFHACK_EXPORT MersenneRNG
|
||||
{
|
||||
static const unsigned MT_LEN = 624;
|
||||
|
||||
unsigned mt_index;
|
||||
uint32_t mt_buffer[MT_LEN];
|
||||
|
||||
void twist();
|
||||
void prefill(unsigned step, int twist_cnt);
|
||||
|
||||
public:
|
||||
/* No constructor or destructor - safe to treat as data */
|
||||
|
||||
void init(const uint32_t *pseed, unsigned cnt, int twist_cnt = 1);
|
||||
|
||||
void init(); // uses time
|
||||
void init(uint32_t seed, int twist_cnt = 1) { init(&seed, 1, twist_cnt); }
|
||||
|
||||
// [0, 2^32)
|
||||
uint32_t random() {
|
||||
if (mt_index >= MT_LEN) twist();
|
||||
return mt_buffer[mt_index++];
|
||||
}
|
||||
// [0, limit)
|
||||
uint32_t random(uint32_t limit) {
|
||||
return uint32_t(uint64_t(random())*limit >> 32);
|
||||
}
|
||||
// (0, 1)
|
||||
double drandom0() {
|
||||
return (double(random())+1)/4294967297.0;
|
||||
}
|
||||
// [0, 1)
|
||||
double drandom() {
|
||||
return double(random())/4294967296.0;
|
||||
}
|
||||
// [0, 1]
|
||||
double drandom1() {
|
||||
return double(random())/4294967295.0;
|
||||
}
|
||||
// [-1, 1]
|
||||
double unitrandom() {
|
||||
return drandom1()*2.0 - 1.0;
|
||||
}
|
||||
|
||||
// Two exact replicas of functions in DF code
|
||||
int32_t df_trandom(uint32_t max=2147483647LU);
|
||||
int32_t df_loadtrandom(uint32_t max=2147483647LU);
|
||||
|
||||
template<class T>
|
||||
void unitvector(T *p, int size);
|
||||
|
||||
template<class T>
|
||||
void permute(T *p, int size) {
|
||||
while(size > 1)
|
||||
{
|
||||
int j = random(size--);
|
||||
T c = p[j]; p[j] = p[size]; p[size] = c;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef DFHACK_RANDOM_CPP
|
||||
extern template void MersenneRNG::unitvector<float>(float *p, int size);
|
||||
extern template void MersenneRNG::unitvector<double>(double *p, int size);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Classical Perlin noise function in template form.
|
||||
* http://mrl.nyu.edu/~perlin/doc/oscar.html#noise
|
||||
*
|
||||
* Using an improved hash function from:
|
||||
* http://www.cs.utah.edu/~aek/research/noise.pdf
|
||||
*/
|
||||
|
||||
template<class T, unsigned VSIZE, unsigned BITS = 8, class IDXT = uint8_t>
|
||||
class PerlinNoise
|
||||
{
|
||||
// Size of randomness tables
|
||||
static const unsigned TSIZE = 1<<BITS;
|
||||
|
||||
T gradients[TSIZE][VSIZE];
|
||||
IDXT idxmap[VSIZE][TSIZE];
|
||||
|
||||
// Templates used to unwind and inline recursion and loops
|
||||
struct Temp {
|
||||
T r0, s;
|
||||
unsigned b0, b1;
|
||||
};
|
||||
template<unsigned mask, int i>
|
||||
struct Impl {
|
||||
static inline void setup(PerlinNoise<T,VSIZE,BITS,IDXT> *self, const T *pv, Temp *pt);
|
||||
static inline T eval(PerlinNoise<T,VSIZE,BITS,IDXT> *self, Temp *pt, unsigned idx, T *pq);
|
||||
};
|
||||
|
||||
public:
|
||||
/* No constructor or destructor - safe to treat as data */
|
||||
|
||||
void init(MersenneRNG &rng);
|
||||
|
||||
T eval(const T coords[VSIZE]);
|
||||
};
|
||||
|
||||
#ifndef DFHACK_RANDOM_CPP
|
||||
extern template class DFHACK_IMPORT PerlinNoise<float, 1>;
|
||||
extern template class DFHACK_IMPORT PerlinNoise<float, 2>;
|
||||
extern template class DFHACK_IMPORT PerlinNoise<float, 3>;
|
||||
#endif
|
||||
|
||||
template<class T, unsigned BITS = 8, class IDXT = uint8_t>
|
||||
class PerlinNoise1D : public PerlinNoise<T, 1, BITS, IDXT>
|
||||
{
|
||||
public:
|
||||
T operator() (T x) { return this->eval(&x); }
|
||||
};
|
||||
|
||||
template<class T, unsigned BITS = 8, class IDXT = uint8_t>
|
||||
class PerlinNoise2D : public PerlinNoise<T, 2, BITS, IDXT>
|
||||
{
|
||||
public:
|
||||
T operator() (T x, T y) {
|
||||
T tmp[2] = { x, y };
|
||||
return this->eval(tmp);
|
||||
}
|
||||
};
|
||||
|
||||
template<class T, unsigned BITS = 8, class IDXT = uint8_t>
|
||||
class PerlinNoise3D : public PerlinNoise<T, 3, BITS, IDXT>
|
||||
{
|
||||
public:
|
||||
T operator() (T x, T y, T z) {
|
||||
T tmp[3] = { x, y, z };
|
||||
return this->eval(tmp);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
@ -0,0 +1,302 @@
|
||||
local _ENV = mkmodule('makeown')
|
||||
--[[
|
||||
'tweak makeown' as a lua include
|
||||
make_own(unit) -- removes foreign flags, sets civ_id to fort civ_id, and sets clothes ownership
|
||||
make_citizen(unit) -- called by make_own if unit.race == fort race
|
||||
|
||||
eventually ought to migrate to hack/lua/plugins/tweak.lua
|
||||
and local _ENV = mkmodule('plugin.tweak')
|
||||
in order to link to functions in the compiled plugin (when/if they become available to lua)
|
||||
--]]
|
||||
local utils = require 'utils'
|
||||
|
||||
|
||||
local function fix_clothing_ownership(unit)
|
||||
-- extracted/translated from tweak makeown plugin
|
||||
-- to be called by tweak-fixmigrant/makeown
|
||||
-- units forced into the fort by removing the flags do not own their clothes
|
||||
-- which has the result that they drop all their clothes and become unhappy because they are naked
|
||||
-- so we need to make them own their clothes and add them to their uniform
|
||||
local fixcount = 0 --int fixcount = 0;
|
||||
for j=0,#unit.inventory-1 do --for(size_t j=0; j<unit->inventory.size(); j++)
|
||||
local inv_item = unit.inventory[j] --unidf::unit_inventory_item* inv_item = unit->inventory[j];
|
||||
local item = inv_item.item --df::item* item = inv_item->item;
|
||||
-- unforbid items (for the case of kidnapping caravan escorts who have their stuff forbidden by default)
|
||||
-- moved forbid false to inside if so that armor/weapons stay equiped
|
||||
if inv_item.mode == df.unit_inventory_item.T_mode.Worn then --if(inv_item->mode == df::unit_inventory_item::T_mode::Worn)
|
||||
-- ignore armor?
|
||||
-- it could be leather boots, for example, in which case it would not be nice to forbid ownership
|
||||
--if(item->getEffectiveArmorLevel() != 0)
|
||||
-- continue;
|
||||
|
||||
if not dfhack.items.getOwner(item) then --if(!Items::getOwner(item))
|
||||
if dfhack.items.setOwner(item,unit) then --if(Items::setOwner(item, unit))
|
||||
item.flags.forbid = false --inv_item->item->flags.bits.forbid = 0;
|
||||
-- add to uniform, so they know they should wear their clothes
|
||||
unit.military.uniforms[0]:insert('#',item.id) --insert_into_vector(unit->military.uniforms[0], item->id);
|
||||
fixcount = fixcount + 1 --fixcount++;
|
||||
else
|
||||
----out << "could not change ownership for item!" << endl;
|
||||
print("Makeown: could not change ownership for an item!")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- clear uniform_drop (without this they would drop their clothes and pick them up some time later)
|
||||
-- dirty?
|
||||
unit.military.uniform_drop:resize(0) --unit->military.uniform_drop.clear();
|
||||
----out << "ownership for " << fixcount << " clothes fixed" << endl;
|
||||
print("Makeown: claimed ownership for "..tostring(fixcount).." worn items")
|
||||
--return true --return CR_OK;
|
||||
end
|
||||
|
||||
local function entity_link(hf, eid, do_event, add, replace_idx)
|
||||
do_event = (do_event == nil) and true or do_event
|
||||
add = (add == nil) and true or add
|
||||
replace_idx = replace_idx or -1
|
||||
|
||||
local link = add and df.histfig_entity_link_memberst:new() or df.histfig_entity_link_former_memberst:new()
|
||||
link.entity_id = eid
|
||||
if replace_idx > -1 then
|
||||
local e = hf.entity_links[replace_idx]
|
||||
link.link_strength = (e.link_strength > 3) and (e.link_strength - 2) or e.link_strength
|
||||
hf.entity_links[replace_idx] = link -- replace member link with former member link
|
||||
e:delete()
|
||||
else
|
||||
link.link_strength = 100
|
||||
hf.entity_links:insert('#', link)
|
||||
end
|
||||
if do_event then
|
||||
event = add and df.history_event_add_hf_entity_linkst:new() or df.history_event_remove_hf_entity_linkst:new()
|
||||
event.year = df.global.cur_year
|
||||
event.seconds = df.global.cur_year_tick
|
||||
event.civ = eid
|
||||
event.histfig = hf.id
|
||||
event.link_type = 0
|
||||
event.position_id = -1
|
||||
event.id = df.global.hist_event_next_id
|
||||
df.global.world.history.events:insert('#',event)
|
||||
df.global.hist_event_next_id = df.global.hist_event_next_id + 1
|
||||
end
|
||||
end
|
||||
|
||||
local function change_state(hf, site_id, pos)
|
||||
hf.info.unk_14.unk_0 = 3 -- state? arrived?
|
||||
hf.info.unk_14.region:assign(pos)
|
||||
hf.info.unk_14.site = site_id
|
||||
event = df.history_event_change_hf_statest:new()
|
||||
event.year = df.global.cur_year
|
||||
event.seconds = df.global.cur_year_tick
|
||||
event.hfid = hf.id
|
||||
event.state = 3
|
||||
event.site = site_id
|
||||
event.region_pos:assign(pos)
|
||||
event.substate = -1; event.region = -1; event.layer = -1;
|
||||
event.id = df.global.hist_event_next_id
|
||||
df.global.world.history.events:insert('#',event)
|
||||
df.global.hist_event_next_id = df.global.hist_event_next_id + 1
|
||||
end
|
||||
|
||||
|
||||
function make_citizen(unit)
|
||||
local dfg = df.global
|
||||
local civ_id = dfg.ui.civ_id
|
||||
local group_id = dfg.ui.group_id
|
||||
local events = dfg.world.history.events
|
||||
local fortent = dfg.ui.main.fortress_entity
|
||||
local civent = fortent and df.historical_entity.find(fortent.entity_links[0].target)
|
||||
-- utils.binsearch(dfg.world.entities.all, fortent.entity_links[0].target, 'id')
|
||||
local event
|
||||
local region_pos = df.world_site.find(dfg.ui.site_id).pos -- used with state events and hf state
|
||||
|
||||
local hf
|
||||
-- assume that hf id 1 and hf id 2 are equal. I am unaware of instances of when they are not.
|
||||
-- occationally a unit does not have both flags set (missing flags1.important_historical_figure)
|
||||
-- and I don't know what that means yet.
|
||||
if unit.flags1.important_historical_figure and unit.flags2.important_historical_figure then
|
||||
-- aready hf, find it (unlikely to happen)
|
||||
hf = utils.binsearch(dfg.world.history.figures, unit.hist_figure_id, 'id')
|
||||
--elseif unit.flags1.important_historical_figure or unit.flags2.important_historical_figure then
|
||||
-- something wrong, try to fix it?
|
||||
--[[
|
||||
if unit.hist_figure_id == -1 then
|
||||
unit.hist_figure_id = unit.hist_figure_id2
|
||||
end
|
||||
if unit.hist_figure_id > -1 then
|
||||
unit.hist_figure_id2 = unit.hist_figure_id
|
||||
unit.flags1.important_historical_figure = true
|
||||
unit.flags2.important_historical_figure = true
|
||||
hf = utils.binsearch(dfg.world.history.figures, unit.hist_figure_id, 'id')
|
||||
else
|
||||
unit.flags1.important_historical_figure = false
|
||||
unit.flags2.important_historical_figure = false
|
||||
end
|
||||
--]]
|
||||
--else
|
||||
-- make one
|
||||
end
|
||||
--local new_hf = false
|
||||
if not hf then
|
||||
--new_hf = true
|
||||
hf = df.historical_figure:new()
|
||||
hf.profession = unit.profession
|
||||
hf.race = unit.race
|
||||
hf.caste = unit.caste
|
||||
hf.sex = unit.sex
|
||||
hf.appeared_year = dfg.cur_year
|
||||
hf.born_year = unit.relations.birth_year
|
||||
hf.born_seconds = unit.relations.birth_time
|
||||
hf.curse_year = unit.relations.curse_year
|
||||
hf.curse_seconds = unit.relations.curse_time
|
||||
hf.anon_1 = unit.relations.anon_2
|
||||
hf.anon_2 = unit.relations.anon_3
|
||||
hf.old_year = unit.relations.old_year
|
||||
hf.old_seconds = unit.relations.old_time
|
||||
hf.died_year = -1
|
||||
hf.died_seconds = -1
|
||||
hf.name:assign(unit.name)
|
||||
hf.civ_id = unit.civ_id
|
||||
hf.population_id = unit.population_id
|
||||
hf.breed_id = -1
|
||||
hf.unit_id = unit.id
|
||||
hf.id = dfg.hist_figure_next_id -- id must be set before adding links (for the events)
|
||||
|
||||
--history_event_add_hf_entity_linkst not reported for civ on starting 7
|
||||
entity_link(hf, civ_id, false) -- so lets skip event here
|
||||
entity_link(hf, group_id)
|
||||
|
||||
hf.info = df.historical_figure_info:new()
|
||||
hf.info.unk_14 = df.historical_figure_info.T_unk_14:new() -- hf state?
|
||||
--unk_14.region_id = -1; unk_14.beast_id = -1; unk_14.unk_14 = 0
|
||||
hf.info.unk_14.unk_18 = -1; hf.info.unk_14.unk_1c = -1
|
||||
-- set values that seem related to state and do event
|
||||
change_state(hf, dfg.ui.site_id, region_pos)
|
||||
|
||||
|
||||
--lets skip skills for now
|
||||
--local skills = df.historical_figure_info.T_skills:new() -- skills snap shot
|
||||
-- ...
|
||||
--info.skills = skills
|
||||
|
||||
dfg.world.history.figures:insert('#', hf)
|
||||
dfg.hist_figure_next_id = dfg.hist_figure_next_id + 1
|
||||
|
||||
--new_hf_loc = df.global.world.history.figures[#df.global.world.history.figures - 1]
|
||||
fortent.histfig_ids:insert('#', hf.id)
|
||||
fortent.hist_figures:insert('#', hf)
|
||||
civent.histfig_ids:insert('#', hf.id)
|
||||
civent.hist_figures:insert('#', hf)
|
||||
|
||||
unit.flags1.important_historical_figure = true
|
||||
unit.flags2.important_historical_figure = true
|
||||
unit.hist_figure_id = hf.id
|
||||
unit.hist_figure_id2 = hf.id
|
||||
print("Makeown-citizen: created historical figure")
|
||||
else
|
||||
-- only insert into civ/fort if not already there
|
||||
-- Migrants change previous histfig_entity_link_memberst to histfig_entity_link_former_memberst
|
||||
-- for group entities, add link_member for new group, and reports events for remove from group,
|
||||
-- remove from civ, change state, add civ, and add group
|
||||
|
||||
hf.civ_id = civ_id -- ensure current civ_id
|
||||
|
||||
local found_civlink = false
|
||||
local found_fortlink = false
|
||||
local v = hf.entity_links
|
||||
for k=#v-1,0,-1 do
|
||||
if df.histfig_entity_link_memberst:is_instance(v[k]) then
|
||||
entity_link(hf, v[k].entity_id, true, false, k)
|
||||
end
|
||||
end
|
||||
|
||||
if hf.info and hf.info.unk_14 then
|
||||
change_state(hf, dfg.ui.site_id, region_pos)
|
||||
-- leave info nil if not found for now
|
||||
end
|
||||
|
||||
if not found_civlink then entity_link(hf,civ_id) end
|
||||
if not found_fortlink then entity_link(hf,group_id) end
|
||||
|
||||
--change entity_links
|
||||
local found = false
|
||||
for _,v in ipairs(civent.histfig_ids) do
|
||||
if v == hf.id then found = true; break end
|
||||
end
|
||||
if not found then
|
||||
civent.histfig_ids:insert('#', hf.id)
|
||||
civent.hist_figures:insert('#', hf)
|
||||
end
|
||||
found = false
|
||||
for _,v in ipairs(fortent.histfig_ids) do
|
||||
if v == hf.id then found = true; break end
|
||||
end
|
||||
if not found then
|
||||
fortent.histfig_ids:insert('#', hf.id)
|
||||
fortent.hist_figures:insert('#', hf)
|
||||
end
|
||||
print("Makeown-citizen: migrated historical figure")
|
||||
end -- hf
|
||||
|
||||
local nemesis = dfhack.units.getNemesis(unit)
|
||||
if not nemesis then
|
||||
nemesis = df.nemesis_record:new()
|
||||
nemesis.figure = hf
|
||||
nemesis.unit = unit
|
||||
nemesis.unit_id = unit.id
|
||||
nemesis.save_file_id = civent.save_file_id
|
||||
nemesis.unk10, nemesis.unk11, nemesis.unk12 = -1, -1, -1
|
||||
--group_leader_id = -1
|
||||
nemesis.id = dfg.nemesis_next_id
|
||||
nemesis.member_idx = civent.next_member_idx
|
||||
civent.next_member_idx = civent.next_member_idx + 1
|
||||
|
||||
dfg.world.nemesis.all:insert('#', nemesis)
|
||||
dfg.nemesis_next_id = dfg.nemesis_next_id + 1
|
||||
|
||||
nemesis_link = df.general_ref_is_nemesisst:new()
|
||||
nemesis_link.nemesis_id = nemesis.id
|
||||
unit.general_refs:insert('#', nemesis_link)
|
||||
|
||||
--new_nemesis_loc = df.global.world.nemesis.all[#df.global.world.nemesis.all - 1]
|
||||
fortent.nemesis_ids:insert('#', nemesis.id)
|
||||
fortent.nemesis:insert('#', nemesis)
|
||||
civent.nemesis_ids:insert('#', nemesis.id)
|
||||
civent.nemesis:insert('#', nemesis)
|
||||
print("Makeown-citizen: created nemesis entry")
|
||||
else-- only insert into civ/fort if not already there
|
||||
local found = false
|
||||
for _,v in ipairs(civent.nemesis_ids) do
|
||||
if v == nemesis.id then found = true; break end
|
||||
end
|
||||
if not found then
|
||||
civent.nemesis_ids:insert('#', nemesis.id)
|
||||
civent.nemesis:insert('#', nemesis)
|
||||
end
|
||||
found = false
|
||||
for _,v in ipairs(fortent.nemesis_ids) do
|
||||
if v == nemesis.id then found = true; break end
|
||||
end
|
||||
if not found then
|
||||
fortent.nemesis_ids:insert('#', nemesis.id)
|
||||
fortent.nemesis:insert('#', nemesis)
|
||||
end
|
||||
print("Makeown-citizen: migrated nemesis entry")
|
||||
end -- nemesis
|
||||
end
|
||||
|
||||
|
||||
function make_own(unit)
|
||||
--tweak makeown
|
||||
unit.flags2.resident = false; unit.flags1.merchant = false; unit.flags1.forest = false;
|
||||
unit.civ_id = df.global.ui.civ_id
|
||||
if unit.profession == df.profession.MERCHANT then unit.profession = df.profession.TRADER end
|
||||
if unit.profession2 == df.profession.MERCHANT then unit.profession2 = df.profession.TRADER end
|
||||
fix_clothing_ownership(unit)
|
||||
if unit.race == df.global.ui.race_id then
|
||||
make_citizen(unit)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
return _ENV
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,16 @@
|
||||
|
||||
#include "modules/Once.h"
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace std;
|
||||
|
||||
static unordered_set<string> thingsDone;
|
||||
|
||||
bool DFHack::Once::alreadyDone(string bob) {
|
||||
return thingsDone.find(bob) != thingsDone.end();
|
||||
}
|
||||
|
||||
bool DFHack::Once::doOnce(string bob) {
|
||||
return thingsDone.insert(bob).second;
|
||||
}
|
||||
|
@ -0,0 +1,156 @@
|
||||
/*
|
||||
https://github.com/peterix/dfhack
|
||||
Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any
|
||||
damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it and
|
||||
redistribute it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must
|
||||
not claim that you wrote the original software. If you use this
|
||||
software in a product, an acknowledgment in the product documentation
|
||||
would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and
|
||||
must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
#include "Internal.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
using namespace std;
|
||||
|
||||
#define DFHACK_RANDOM_CPP
|
||||
|
||||
#include "modules/Random.h"
|
||||
#include "VersionInfo.h"
|
||||
#include "MemAccess.h"
|
||||
#include "Types.h"
|
||||
#include "ModuleFactory.h"
|
||||
#include "Core.h"
|
||||
#include "Error.h"
|
||||
#include "VTableInterpose.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
using namespace DFHack;
|
||||
using namespace df::enums;
|
||||
|
||||
using namespace DFHack::Random;
|
||||
|
||||
//public domain RNG stuff by Michael Brundage
|
||||
//modified to be compatible with the version in DF
|
||||
|
||||
#define MT_IA 397
|
||||
#define MT_IB (MT_LEN - MT_IA)
|
||||
#define UPPER_MASK 0x80000000
|
||||
#define LOWER_MASK 0x7FFFFFFF
|
||||
#define MATRIX_A 0x9908B0DF
|
||||
#define TWIST(b,i,j) ((b)[i] & UPPER_MASK) | ((b)[j] & LOWER_MASK)
|
||||
#define MAGIC(s) (((s)&1)*MATRIX_A)
|
||||
|
||||
void MersenneRNG::twist()
|
||||
{
|
||||
uint32_t *b = mt_buffer;
|
||||
uint32_t s;
|
||||
unsigned i;
|
||||
|
||||
i = 0;
|
||||
for (; i < MT_IB; i++) {
|
||||
s = TWIST(b, i, i+1);
|
||||
b[i] = b[i + MT_IA] ^ (s >> 1) ^ MAGIC(s);
|
||||
}
|
||||
for (; i < MT_LEN-1; i++) {
|
||||
s = TWIST(b, i, i+1);
|
||||
b[i] = b[i - MT_IB] ^ (s >> 1) ^ MAGIC(s);
|
||||
}
|
||||
|
||||
s = TWIST(b, MT_LEN-1, 0);
|
||||
b[MT_LEN-1] = b[MT_IA-1] ^ (s >> 1) ^ MAGIC(s);
|
||||
|
||||
mt_index = 0;
|
||||
}
|
||||
|
||||
void MersenneRNG::prefill(unsigned step, int twist_cnt)
|
||||
{
|
||||
for(unsigned i=step;i<MT_LEN;i++)
|
||||
{
|
||||
//2010: better init line from wikipedia, ultimate source unknown
|
||||
mt_buffer[i]=1812433253UL * (mt_buffer[i-step] ^ (mt_buffer[i-step]>>30)) + i;
|
||||
}
|
||||
|
||||
mt_index = 0;
|
||||
|
||||
for(int j=0;j<twist_cnt;j++)
|
||||
twist();
|
||||
}
|
||||
|
||||
void MersenneRNG::init()
|
||||
{
|
||||
init(Core::getInstance().p->getTickCount(), 20);
|
||||
}
|
||||
|
||||
void MersenneRNG::init(const uint32_t *pseed, unsigned cnt, int twist_cnt)
|
||||
{
|
||||
memcpy(mt_buffer, pseed, cnt*sizeof(uint32_t));
|
||||
|
||||
prefill(cnt, twist_cnt);
|
||||
}
|
||||
|
||||
int32_t MersenneRNG::df_trandom(uint32_t max)
|
||||
{
|
||||
if(max<=1)return 0;
|
||||
uint32_t seed=random();
|
||||
seed=seed%2147483647LU;
|
||||
seed=seed/((2147483647LU/max)+1);
|
||||
|
||||
return((int32_t)seed);
|
||||
}
|
||||
|
||||
int32_t MersenneRNG::df_loadtrandom(uint32_t max)
|
||||
{
|
||||
uint32_t seed=random();
|
||||
seed=seed%max;
|
||||
|
||||
return((int32_t)seed);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void MersenneRNG::unitvector(T *p, int size)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
T rsqr = 0;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
p[i] = (T)unitrandom();
|
||||
rsqr += p[i]*p[i];
|
||||
}
|
||||
|
||||
if (rsqr > 0 && rsqr <= 1)
|
||||
{
|
||||
rsqr = std::sqrt(rsqr);
|
||||
for (int i = 0; i < size; i++)
|
||||
p[i] /= rsqr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template void MersenneRNG::unitvector<float>(float *p, int size);
|
||||
template void MersenneRNG::unitvector<double>(double *p, int size);
|
||||
|
||||
#include "modules/PerlinNoise.inc"
|
||||
|
||||
template class DFHACK_EXPORT PerlinNoise<float, 1>;
|
||||
template class DFHACK_EXPORT PerlinNoise<float, 2>;
|
||||
template class DFHACK_EXPORT PerlinNoise<float, 3>;
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,35 @@
|
||||
|
||||
#include "Core.h"
|
||||
#include "Console.h"
|
||||
#include "DataDefs.h"
|
||||
#include "Export.h"
|
||||
#include "PluginManager.h"
|
||||
|
||||
#include "modules/Once.h"
|
||||
|
||||
using namespace DFHack;
|
||||
using namespace df::enums;
|
||||
|
||||
command_result onceExample (color_ostream &out, std::vector <std::string> & parameters);
|
||||
|
||||
DFHACK_PLUGIN("onceExample");
|
||||
|
||||
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
|
||||
{
|
||||
commands.push_back(PluginCommand(
|
||||
"onceExample", "Test the doOnce command.",
|
||||
onceExample, false,
|
||||
" This command tests the doOnce command..\n"
|
||||
));
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
command_result onceExample (color_ostream &out, std::vector <std::string> & parameters)
|
||||
{
|
||||
out.print("Already done = %d.\n", DFHack::Once::alreadyDone("onceExample_1"));
|
||||
if ( DFHack::Once::doOnce("onceExample_1") ) {
|
||||
out.print("Printing this message once!\n");
|
||||
}
|
||||
return CR_OK;
|
||||
}
|
||||
|
@ -0,0 +1,211 @@
|
||||
#include "Core.h"
|
||||
#include "DataDefs.h"
|
||||
#include "Export.h"
|
||||
#include "PluginManager.h"
|
||||
|
||||
#include "modules/EventManager.h"
|
||||
#include "modules/MapCache.h"
|
||||
#include "modules/Maps.h"
|
||||
|
||||
#include "df/coord.h"
|
||||
#include "df/global_objects.h"
|
||||
#include "df/job.h"
|
||||
#include "df/map_block.h"
|
||||
#include "df/tile_dig_designation.h"
|
||||
#include "df/world.h"
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace DFHack;
|
||||
using namespace std;
|
||||
|
||||
command_result digFlood (color_ostream &out, std::vector <std::string> & parameters);
|
||||
|
||||
DFHACK_PLUGIN("digFlood");
|
||||
|
||||
void onDig(color_ostream& out, void* ptr);
|
||||
void maybeExplore(color_ostream& out, MapExtras::MapCache& cache, df::coord pt, set<df::coord>& jobLocations);
|
||||
EventManager::EventHandler digHandler(onDig, 0);
|
||||
|
||||
//bool enabled = false;
|
||||
DFHACK_PLUGIN_IS_ENABLED(enabled);
|
||||
bool digAll = false;
|
||||
set<string> autodigMaterials;
|
||||
|
||||
DFhackCExport command_result plugin_enable(color_ostream& out, bool enable) {
|
||||
if (enabled == enable)
|
||||
return CR_OK;
|
||||
|
||||
enabled = enable;
|
||||
if ( enabled ) {
|
||||
EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, digHandler, plugin_self);
|
||||
} else {
|
||||
EventManager::unregisterAll(plugin_self);
|
||||
}
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
|
||||
{
|
||||
commands.push_back(PluginCommand(
|
||||
"digFlood", "Automatically dig out veins as you discover them.",
|
||||
digFlood, false,
|
||||
"Example:\n"
|
||||
" digFlood 0\n"
|
||||
" disable plugin\n"
|
||||
" digFlood 1\n"
|
||||
" enable plugin\n"
|
||||
" digFlood 0 MICROCLINE COAL_BITUMINOUS 1\n"
|
||||
" disable plugin and remove microcline and bituminous coal from being monitored, then re-enable plugin"
|
||||
" digFlood 1 MICROCLINE 0 COAL_BITUMINOUS 1\n"
|
||||
" do monitor microcline, don't monitor COAL_BITUMINOUS, then enable plugin\n"
|
||||
" digFlood CLEAR\n"
|
||||
" remove all inorganics from monitoring\n"
|
||||
" digFlood digAll1\n"
|
||||
" enable digAll mode: dig any vein, regardless of the monitor list\n"
|
||||
" digFlood digAll0\n"
|
||||
" disable digAll mode\n"
|
||||
"\n"
|
||||
"Note that while order matters, multiple commands can be sequenced in one line. It is recommended to alter your dfhack.init file so that you won't have to type in every mineral type you want to dig every time you start the game. Material names are case sensitive.\n"
|
||||
));
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
void onDig(color_ostream& out, void* ptr) {
|
||||
CoreSuspender bob;
|
||||
df::job* job = (df::job*)ptr;
|
||||
if ( job->completion_timer > 0 )
|
||||
return;
|
||||
|
||||
if ( job->job_type != df::enums::job_type::Dig &&
|
||||
job->job_type != df::enums::job_type::CarveUpwardStaircase &&
|
||||
job->job_type != df::enums::job_type::CarveDownwardStaircase &&
|
||||
job->job_type != df::enums::job_type::CarveUpDownStaircase &&
|
||||
job->job_type != df::enums::job_type::CarveRamp &&
|
||||
job->job_type != df::enums::job_type::DigChannel )
|
||||
return;
|
||||
|
||||
set<df::coord> jobLocations;
|
||||
for ( df::job_list_link* link = &df::global::world->job_list; link != NULL; link = link->next ) {
|
||||
if ( link->item == NULL )
|
||||
continue;
|
||||
|
||||
if ( link->item->job_type != df::enums::job_type::Dig &&
|
||||
link->item->job_type != df::enums::job_type::CarveUpwardStaircase &&
|
||||
link->item->job_type != df::enums::job_type::CarveDownwardStaircase &&
|
||||
link->item->job_type != df::enums::job_type::CarveUpDownStaircase &&
|
||||
link->item->job_type != df::enums::job_type::CarveRamp &&
|
||||
link->item->job_type != df::enums::job_type::DigChannel )
|
||||
continue;
|
||||
|
||||
jobLocations.insert(link->item->pos);
|
||||
}
|
||||
|
||||
MapExtras::MapCache cache;
|
||||
df::coord pos = job->pos;
|
||||
for ( int16_t a = -1; a <= 1; a++ ) {
|
||||
for ( int16_t b = -1; b <= 1; b++ ) {
|
||||
maybeExplore(out, cache, df::coord(pos.x+a,pos.y+b,pos.z), jobLocations);
|
||||
}
|
||||
}
|
||||
cache.trash();
|
||||
}
|
||||
|
||||
void maybeExplore(color_ostream& out, MapExtras::MapCache& cache, df::coord pt, set<df::coord>& jobLocations) {
|
||||
if ( !Maps::isValidTilePos(pt) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
df::map_block* block = Maps::getTileBlock(pt);
|
||||
if (!block)
|
||||
return;
|
||||
|
||||
if ( block->designation[pt.x&0xF][pt.y&0xF].bits.hidden )
|
||||
return;
|
||||
|
||||
df::tiletype type = block->tiletype[pt.x&0xF][pt.y&0xF];
|
||||
if ( ENUM_ATTR(tiletype, material, type) != df::enums::tiletype_material::MINERAL )
|
||||
return;
|
||||
if ( ENUM_ATTR(tiletype, shape, type) != df::enums::tiletype_shape::WALL )
|
||||
return;
|
||||
|
||||
if ( block->designation[pt.x&0xF][pt.y&0xF].bits.dig != df::enums::tile_dig_designation::No )
|
||||
return;
|
||||
|
||||
uint32_t xMax,yMax,zMax;
|
||||
Maps::getSize(xMax,yMax,zMax);
|
||||
if ( pt.x == 0 || pt.y == 0 || pt.x+1 == xMax*16 || pt.y+1 == yMax*16 )
|
||||
return;
|
||||
if ( jobLocations.find(pt) != jobLocations.end() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
int16_t mat = cache.veinMaterialAt(pt);
|
||||
if ( mat == -1 )
|
||||
return;
|
||||
if ( !digAll ) {
|
||||
df::inorganic_raw* inorganic = df::global::world->raws.inorganics[mat];
|
||||
if ( autodigMaterials.find(inorganic->id) == autodigMaterials.end() ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
block->designation[pt.x&0xF][pt.y&0xF].bits.dig = df::enums::tile_dig_designation::Default;
|
||||
block->flags.bits.designated = true;
|
||||
// *df::global::process_dig = true;
|
||||
// *df::global::process_jobs = true;
|
||||
}
|
||||
|
||||
command_result digFlood (color_ostream &out, std::vector <std::string> & parameters)
|
||||
{
|
||||
bool adding = true;
|
||||
set<string> toAdd, toRemove;
|
||||
for ( size_t a = 0; a < parameters.size(); a++ ) {
|
||||
int32_t i = (int32_t)strtol(parameters[a].c_str(), NULL, 0);
|
||||
if ( i == 0 && parameters[a] == "0" ) {
|
||||
plugin_enable(out, false);
|
||||
adding = false;
|
||||
continue;
|
||||
} else if ( i == 1 ) {
|
||||
plugin_enable(out, true);
|
||||
adding = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( parameters[a] == "CLEAR" )
|
||||
autodigMaterials.clear();
|
||||
|
||||
if ( parameters[a] == "digAll0" ) {
|
||||
digAll = false;
|
||||
continue;
|
||||
}
|
||||
if ( parameters[a] == "digAll1" ) {
|
||||
digAll = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
for ( size_t b = 0; b < df::global::world->raws.inorganics.size(); b++ ) {
|
||||
df::inorganic_raw* inorganic = df::global::world->raws.inorganics[b];
|
||||
if ( parameters[a] == inorganic->id ) {
|
||||
if ( adding )
|
||||
toAdd.insert(parameters[a]);
|
||||
else
|
||||
toRemove.insert(parameters[a]);
|
||||
goto loop;
|
||||
}
|
||||
}
|
||||
|
||||
out.print("Could not find material \"%s\".\n", parameters[a].c_str());
|
||||
return CR_WRONG_USAGE;
|
||||
|
||||
loop: continue;
|
||||
}
|
||||
|
||||
autodigMaterials.insert(toAdd.begin(), toAdd.end());
|
||||
for ( auto a = toRemove.begin(); a != toRemove.end(); a++ )
|
||||
autodigMaterials.erase(*a);
|
||||
|
||||
return CR_OK;
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
PROJECT (diggingInvaders)
|
||||
# A list of source files
|
||||
SET(PROJECT_SRCS
|
||||
diggingInvaders.cpp
|
||||
edgeCost.cpp
|
||||
assignJob.cpp
|
||||
)
|
||||
# A list of headers
|
||||
SET(PROJECT_HDRS
|
||||
edgeCost.h
|
||||
assignJob.h
|
||||
)
|
||||
SET_SOURCE_FILES_PROPERTIES( ${PROJECT_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||
|
||||
# mash them together (headers are marked as headers and nothing will try to compile them)
|
||||
LIST(APPEND PROJECT_SRCS ${PROJECT_HDRS})
|
||||
|
||||
#linux
|
||||
IF(UNIX)
|
||||
add_definitions(-DLINUX_BUILD)
|
||||
SET(PROJECT_LIBS
|
||||
# add any extra linux libs here
|
||||
${PROJECT_LIBS}
|
||||
)
|
||||
# windows
|
||||
ELSE(UNIX)
|
||||
SET(PROJECT_LIBS
|
||||
# add any extra linux libs here
|
||||
${PROJECT_LIBS}
|
||||
$(NOINHERIT)
|
||||
)
|
||||
ENDIF(UNIX)
|
||||
# this makes sure all the stuff is put in proper places and linked to dfhack
|
||||
|
||||
DFHACK_PLUGIN(diggingInvaders ${PROJECT_SRCS} LINK_LIBRARIES ${PROJECT_LIBS})
|
@ -0,0 +1,300 @@
|
||||
#include "assignJob.h"
|
||||
|
||||
#include "modules/Buildings.h"
|
||||
#include "modules/Items.h"
|
||||
#include "modules/Job.h"
|
||||
#include "modules/Materials.h"
|
||||
|
||||
#include "df/building.h"
|
||||
#include "df/construction.h"
|
||||
#include "df/coord.h"
|
||||
#include "df/general_ref.h"
|
||||
#include "df/general_ref_building_holderst.h"
|
||||
#include "df/general_ref_unit.h"
|
||||
//#include "df/general_ref_unit_holderst.h"
|
||||
#include "df/general_ref_unit_workerst.h"
|
||||
#include "df/historical_entity.h"
|
||||
#include "df/item.h"
|
||||
#include "df/itemdef_weaponst.h"
|
||||
#include "df/item_quality.h"
|
||||
#include "df/item_type.h"
|
||||
#include "df/item_weaponst.h"
|
||||
#include "df/job.h"
|
||||
#include "df/job_skill.h"
|
||||
#include "df/job_type.h"
|
||||
#include "df/reaction_product_itemst.h"
|
||||
#include "df/reaction_reagent.h"
|
||||
#include "df/ui.h"
|
||||
#include "df/unit.h"
|
||||
#include "df/unit_inventory_item.h"
|
||||
#include "df/world_site.h"
|
||||
|
||||
void getRidOfOldJob(df::unit* unit) {
|
||||
if ( unit->job.current_job == NULL ) {
|
||||
return;
|
||||
}
|
||||
|
||||
df::job* job = unit->job.current_job;
|
||||
unit->job.current_job = NULL;
|
||||
if ( job->list_link->prev != NULL ) {
|
||||
job->list_link->prev->next = job->list_link->next;
|
||||
}
|
||||
if ( job->list_link->next != NULL ) {
|
||||
job->list_link->next->prev = job->list_link->prev;
|
||||
}
|
||||
//TODO: consider building pointers?
|
||||
//for now, just let the memory leak TODO: fix
|
||||
//delete job->list_link;
|
||||
//delete job;
|
||||
}
|
||||
|
||||
int32_t assignJob(color_ostream& out, Edge firstImportantEdge, unordered_map<df::coord,df::coord,PointHash> parentMap, unordered_map<df::coord,cost_t,PointHash>& costMap, vector<int32_t>& invaders, unordered_set<df::coord,PointHash>& requiresZNeg, unordered_set<df::coord,PointHash>& requiresZPos, MapExtras::MapCache& cache, DigAbilities& abilities ) {
|
||||
df::unit* firstInvader = df::unit::find(invaders[0]);
|
||||
if ( !firstInvader ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
//do whatever you need to do at the first important edge
|
||||
df::coord pt1 = firstImportantEdge.p1;
|
||||
df::coord pt2 = firstImportantEdge.p2;
|
||||
if ( costMap[pt1] > costMap[pt2] ) {
|
||||
df::coord temp = pt1;
|
||||
pt1 = pt2;
|
||||
pt2 = temp;
|
||||
}
|
||||
//out.print("first important edge: (%d,%d,%d) -> (%d,%d,%d)\n", pt1.x,pt1.y,pt1.z, pt2.x,pt2.y,pt2.z);
|
||||
|
||||
int32_t jobId = -1;
|
||||
|
||||
df::map_block* block1 = Maps::getTileBlock(pt1);
|
||||
df::map_block* block2 = Maps::getTileBlock(pt2);
|
||||
bool passable1 = block1->walkable[pt1.x&0xF][pt1.y&0xF];
|
||||
bool passable2 = block2->walkable[pt2.x&0xF][pt2.y&0xF];
|
||||
|
||||
df::coord location;
|
||||
df::building* building = Buildings::findAtTile(pt2);
|
||||
df::coord buildingPos = pt2;
|
||||
if ( pt1.z > pt2.z ) {
|
||||
building = Buildings::findAtTile(df::coord(pt2.x,pt2.y,pt2.z+1));
|
||||
buildingPos = df::coord(pt2.x,pt2.y,pt2.z+1);
|
||||
}
|
||||
if ( building != NULL ) {
|
||||
df::coord destroyFrom = parentMap[buildingPos];
|
||||
if ( destroyFrom.z != buildingPos.z ) {
|
||||
//TODO: deal with this
|
||||
}
|
||||
//out.print("%s, line %d: Destroying building %d at (%d,%d,%d) from (%d,%d,%d).\n", __FILE__, __LINE__, building->id, buildingPos.x,buildingPos.y,buildingPos.z, destroyFrom.x,destroyFrom.y,destroyFrom.z);
|
||||
|
||||
df::job* job = new df::job;
|
||||
job->job_type = df::enums::job_type::DestroyBuilding;
|
||||
//job->flags.bits.special = 1;
|
||||
df::general_ref_building_holderst* buildingRef = new df::general_ref_building_holderst;
|
||||
buildingRef->building_id = building->id;
|
||||
job->general_refs.push_back(buildingRef);
|
||||
df::general_ref_unit_workerst* workerRef = new df::general_ref_unit_workerst;
|
||||
workerRef->unit_id = firstInvader->id;
|
||||
job->general_refs.push_back(workerRef);
|
||||
getRidOfOldJob(firstInvader);
|
||||
firstInvader->job.current_job = job;
|
||||
firstInvader->path.path.x.clear();
|
||||
firstInvader->path.path.y.clear();
|
||||
firstInvader->path.path.z.clear();
|
||||
firstInvader->path.dest = destroyFrom;
|
||||
location = destroyFrom;
|
||||
firstInvader->job.hunt_target = NULL;
|
||||
firstInvader->job.destroy_target = NULL;
|
||||
|
||||
building->jobs.clear();
|
||||
building->jobs.push_back(job);
|
||||
Job::linkIntoWorld(job);
|
||||
jobId = job->id;
|
||||
job->completion_timer = abilities.jobDelay[CostDimension::DestroyBuilding];
|
||||
} else {
|
||||
df::tiletype* type1 = Maps::getTileType(pt1);
|
||||
df::tiletype* type2 = Maps::getTileType(pt2);
|
||||
df::tiletype_shape shape1 = ENUM_ATTR(tiletype, shape, *type1);
|
||||
df::tiletype_shape shape2 = ENUM_ATTR(tiletype, shape, *type2);
|
||||
bool construction2 = ENUM_ATTR(tiletype, material, *type2) == df::enums::tiletype_material::CONSTRUCTION;
|
||||
if ( construction2 ) {
|
||||
df::job* job = new df::job;
|
||||
job->job_type = df::enums::job_type::RemoveConstruction;
|
||||
df::general_ref_unit_workerst* workerRef = new df::general_ref_unit_workerst;
|
||||
workerRef->unit_id = firstInvader->id;
|
||||
job->general_refs.push_back(workerRef);
|
||||
job->pos = pt2;
|
||||
getRidOfOldJob(firstInvader);
|
||||
firstInvader->job.current_job = job;
|
||||
firstInvader->path.path.x.clear();
|
||||
firstInvader->path.path.y.clear();
|
||||
firstInvader->path.path.z.clear();
|
||||
firstInvader->path.dest = pt1;
|
||||
location = pt1;
|
||||
firstInvader->job.hunt_target = NULL;
|
||||
firstInvader->job.destroy_target = NULL;
|
||||
Job::linkIntoWorld(job);
|
||||
jobId = job->id;
|
||||
df::construction* constr = df::construction::find(pt2);
|
||||
bool smooth = constr != NULL && constr->item_type != df::enums::item_type::BOULDER;
|
||||
if ( smooth )
|
||||
job->completion_timer = abilities.jobDelay[CostDimension::DestroySmoothConstruction];
|
||||
else
|
||||
job->completion_timer = abilities.jobDelay[CostDimension::DestroyRoughConstruction];
|
||||
} else {
|
||||
bool walkable_low1 = shape1 == df::tiletype_shape::STAIR_DOWN || shape1 == df::tiletype_shape::STAIR_UPDOWN;
|
||||
bool walkable_low2 = shape2 == df::tiletype_shape::STAIR_DOWN || shape2 == df::tiletype_shape::STAIR_UPDOWN;
|
||||
bool walkable_high1 = shape1 == df::tiletype_shape::STAIR_UP || shape1 == df::tiletype_shape::STAIR_UPDOWN;
|
||||
bool walkable_high2 = shape2 == df::tiletype_shape::STAIR_UP || shape2 == df::tiletype_shape::STAIR_UPDOWN;
|
||||
//must be a dig job
|
||||
bool up1 = !walkable_high1 && requiresZPos.find(pt1) != requiresZPos.end();
|
||||
bool up2 = !walkable_high2 && requiresZPos.find(pt2) != requiresZPos.end();
|
||||
bool down1 = !walkable_low1 && requiresZNeg.find(pt1) != requiresZNeg.end();
|
||||
bool down2 = !walkable_low2 && requiresZNeg.find(pt2) != requiresZNeg.end();
|
||||
bool up;
|
||||
bool down;
|
||||
df::coord goHere;
|
||||
df::coord workHere;
|
||||
if ( pt1.z == pt2.z ) {
|
||||
up = up2;
|
||||
down = down2;
|
||||
goHere = pt1;
|
||||
workHere = pt2;
|
||||
} else {
|
||||
if ( up1 || down1 ) {
|
||||
up = up1;
|
||||
down = down1;
|
||||
goHere = pt1;
|
||||
workHere = pt1;
|
||||
} else {
|
||||
up = up2;
|
||||
down = down2;
|
||||
goHere = pt1;
|
||||
workHere = pt2;
|
||||
}
|
||||
}
|
||||
df::job* job = new df::job;
|
||||
if ( up && down ) {
|
||||
job->job_type = df::enums::job_type::CarveUpDownStaircase;
|
||||
//out.print("%s, line %d: type = up/down\n", __FILE__, __LINE__);
|
||||
} else if ( up && !down ) {
|
||||
job->job_type = df::enums::job_type::CarveUpwardStaircase;
|
||||
//out.print("%s, line %d: type = up\n", __FILE__, __LINE__);
|
||||
} else if ( !up && down ) {
|
||||
job->job_type = df::enums::job_type::CarveDownwardStaircase;
|
||||
//out.print("%s, line %d: type = down\n", __FILE__, __LINE__);
|
||||
} else {
|
||||
job->job_type = df::enums::job_type::Dig;
|
||||
//out.print("%s, line %d: type = dig\n", __FILE__, __LINE__);
|
||||
}
|
||||
//out.print("%s, line %d: up=%d,up1=%d,up2=%d, down=%d,down1=%d,down2=%d\n", __FILE__, __LINE__, up,up1,up2, down,down1,down2);
|
||||
job->pos = workHere;
|
||||
firstInvader->path.dest = goHere;
|
||||
location = goHere;
|
||||
df::general_ref_unit_workerst* ref = new df::general_ref_unit_workerst;
|
||||
ref->unit_id = firstInvader->id;
|
||||
job->general_refs.push_back(ref);
|
||||
firstInvader->job.hunt_target = NULL;
|
||||
firstInvader->job.destroy_target = NULL;
|
||||
getRidOfOldJob(firstInvader);
|
||||
firstInvader->job.current_job = job;
|
||||
firstInvader->path.path.x.clear();
|
||||
firstInvader->path.path.y.clear();
|
||||
firstInvader->path.path.z.clear();
|
||||
Job::linkIntoWorld(job);
|
||||
jobId = job->id;
|
||||
job->completion_timer = abilities.jobDelay[CostDimension::Dig];
|
||||
|
||||
//TODO: test if he already has a pick
|
||||
bool hasPick = false;
|
||||
for ( size_t a = 0; a < firstInvader->inventory.size(); a++ ) {
|
||||
df::unit_inventory_item* inv_item = firstInvader->inventory[a];
|
||||
if ( inv_item->mode != df::unit_inventory_item::Weapon || inv_item->body_part_id != firstInvader->body.weapon_bp )
|
||||
continue;
|
||||
df::item* oldItem = inv_item->item;
|
||||
if ( oldItem->getType() != df::enums::item_type::WEAPON )
|
||||
continue;
|
||||
df::item_weaponst* oldWeapon = (df::item_weaponst*)oldItem;
|
||||
df::itemdef_weaponst* oldType = oldWeapon->subtype;
|
||||
if ( oldType->skill_melee != df::enums::job_skill::MINING )
|
||||
continue;
|
||||
hasPick = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( hasPick )
|
||||
return firstInvader->id;
|
||||
|
||||
//create and give a pick
|
||||
//based on createitem.cpp
|
||||
df::reaction_product_itemst *prod = NULL;
|
||||
//TODO: consider filtering based on entity/civ stuff
|
||||
for ( size_t a = 0; a < df::global::world->raws.itemdefs.weapons.size(); a++ ) {
|
||||
df::itemdef_weaponst* oldType = df::global::world->raws.itemdefs.weapons[a];
|
||||
if ( oldType->skill_melee != df::enums::job_skill::MINING )
|
||||
continue;
|
||||
prod = df::allocate<df::reaction_product_itemst>();
|
||||
prod->item_type = df::item_type::WEAPON;
|
||||
prod->item_subtype = a;
|
||||
break;
|
||||
}
|
||||
if ( prod == NULL ) {
|
||||
out.print("%s, %d: no valid item.\n", __FILE__, __LINE__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
DFHack::MaterialInfo material;
|
||||
if ( !material.find("OBSIDIAN") ) {
|
||||
out.print("%s, %d: no water.\n", __FILE__, __LINE__);
|
||||
return -1;
|
||||
}
|
||||
prod->mat_type = material.type;
|
||||
prod->mat_index = material.index;
|
||||
prod->probability = 100;
|
||||
prod->count = 1;
|
||||
prod->product_dimension = 1;
|
||||
|
||||
vector<df::item*> out_items;
|
||||
vector<df::reaction_reagent*> in_reag;
|
||||
vector<df::item*> in_items;
|
||||
prod->produce(firstInvader, &out_items, &in_reag, &in_items, 1, df::job_skill::NONE,
|
||||
df::historical_entity::find(firstInvader->civ_id),
|
||||
df::world_site::find(df::global::ui->site_id));
|
||||
|
||||
if ( out_items.size() != 1 ) {
|
||||
out.print("%s, %d: wrong size: %d.\n", __FILE__, __LINE__, out_items.size());
|
||||
return -1;
|
||||
}
|
||||
out_items[0]->moveToGround(firstInvader->pos.x, firstInvader->pos.y, firstInvader->pos.z);
|
||||
|
||||
#if 0
|
||||
//check for existing item there
|
||||
for ( size_t a = 0; a < firstInvader->inventory.size(); a++ ) {
|
||||
df::unit_inventory_item* inv_item = firstInvader->inventory[a];
|
||||
if ( false || inv_item->body_part_id == part ) {
|
||||
//throw it on the ground
|
||||
Items::moveToGround(cache, inv_item->item, firstInvader->pos);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Items::moveToInventory(cache, out_items[0], firstInvader, df::unit_inventory_item::T_mode::Weapon, firstInvader->body.weapon_bp);
|
||||
|
||||
delete prod;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
//tell EVERYONE to move there
|
||||
for ( size_t a = 0; a < invaders.size(); a++ ) {
|
||||
df::unit* invader = invaders[a];
|
||||
invader->path.path.x.clear();
|
||||
invader->path.path.y.clear();
|
||||
invader->path.path.z.clear();
|
||||
invader->path.dest = location;
|
||||
//invader->flags1.bits.invades = true;
|
||||
//invader->flags1.bits.marauder = true;
|
||||
//invader->flags2.bits.visitor_uninvited = true;
|
||||
invader->relations.group_leader_id = invader->id;
|
||||
}
|
||||
#endif
|
||||
|
||||
return firstInvader->id;
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "edgeCost.h"
|
||||
|
||||
#include "modules/MapCache.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int32_t assignJob(color_ostream& out, Edge firstImportantEdge, unordered_map<df::coord,df::coord,PointHash> parentMap, unordered_map<df::coord,cost_t,PointHash>& costMap, vector<int32_t>& invaders, unordered_set<df::coord,PointHash>& requiresZNeg, unordered_set<df::coord,PointHash>& requiresZPos, MapExtras::MapCache& cache, DigAbilities& abilities);
|
||||
|
@ -0,0 +1,631 @@
|
||||
#include "assignJob.h"
|
||||
#include "edgeCost.h"
|
||||
|
||||
#include "Core.h"
|
||||
#include "Console.h"
|
||||
#include "DataDefs.h"
|
||||
#include "Export.h"
|
||||
#include "PluginManager.h"
|
||||
#include "Types.h"
|
||||
|
||||
#include "modules/Buildings.h"
|
||||
#include "modules/EventManager.h"
|
||||
#include "modules/Gui.h"
|
||||
#include "modules/Job.h"
|
||||
#include "modules/Maps.h"
|
||||
#include "modules/MapCache.h"
|
||||
#include "modules/Units.h"
|
||||
#include "modules/World.h"
|
||||
|
||||
#include "df/body_part_raw_flags.h"
|
||||
#include "df/building.h"
|
||||
#include "df/building_type.h"
|
||||
#include "df/caste_body_info.h"
|
||||
#include "df/coord.h"
|
||||
#include "df/creature_raw.h"
|
||||
#include "df/general_ref.h"
|
||||
#include "df/general_ref_building_holderst.h"
|
||||
#include "df/general_ref_unit.h"
|
||||
#include "df/general_ref_unit_holderst.h"
|
||||
#include "df/general_ref_unit_workerst.h"
|
||||
#include "df/global_objects.h"
|
||||
#include "df/invasion_info.h"
|
||||
#include "df/item.h"
|
||||
#include "df/itemdef_weaponst.h"
|
||||
#include "df/item_quality.h"
|
||||
#include "df/item_weaponst.h"
|
||||
#include "df/inorganic_raw.h"
|
||||
#include "df/job.h"
|
||||
#include "df/job_list_link.h"
|
||||
#include "df/job_skill.h"
|
||||
#include "df/job_type.h"
|
||||
#include "df/map_block.h"
|
||||
#include "df/strain_type.h"
|
||||
#include "df/tile_building_occ.h"
|
||||
#include "df/tile_occupancy.h"
|
||||
#include "df/tiletype.h"
|
||||
#include "df/tiletype_material.h"
|
||||
#include "df/tiletype_shape.h"
|
||||
#include "df/tiletype_shape_basic.h"
|
||||
#include "df/ui.h"
|
||||
#include "df/unit.h"
|
||||
#include "df/unit_inventory_item.h"
|
||||
#include "df/world.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace std;
|
||||
|
||||
using namespace DFHack;
|
||||
using namespace df::enums;
|
||||
|
||||
command_result diggingInvadersCommand(color_ostream &out, std::vector <std::string> & parameters);
|
||||
void watchForJobComplete(color_ostream& out, void* ptr);
|
||||
void newInvasionHandler(color_ostream& out, void* ptr);
|
||||
void clearDijkstra();
|
||||
void findAndAssignInvasionJob(color_ostream& out, void*);
|
||||
//int32_t manageInvasion(color_ostream& out);
|
||||
|
||||
DFHACK_PLUGIN_IS_ENABLED(enabled);
|
||||
DFHACK_PLUGIN("diggingInvaders");
|
||||
|
||||
//TODO: when world unloads
|
||||
static int32_t lastInvasionJob=-1;
|
||||
static int32_t lastInvasionDigger = -1;
|
||||
static int32_t edgesPerTick = 100;
|
||||
//static EventManager::EventHandler jobCompleteHandler(watchForJobComplete, 5);
|
||||
static bool activeDigging=false;
|
||||
static unordered_set<string> diggingRaces;
|
||||
static unordered_set<int32_t> invaderJobs;
|
||||
static df::coord lastDebugEdgeCostPoint;
|
||||
unordered_map<string, DigAbilities> digAbilities;
|
||||
|
||||
static cost_t costWeightDefault[] = {
|
||||
//Distance
|
||||
1,
|
||||
//Destroy Building
|
||||
2,
|
||||
//Dig
|
||||
10000,
|
||||
//DestroyRoughConstruction
|
||||
1000,
|
||||
//DestroySmoothConstruction
|
||||
100,
|
||||
};
|
||||
|
||||
static int32_t jobDelayDefault[] = {
|
||||
//Distance
|
||||
-1,
|
||||
//Destroy Building
|
||||
1000,
|
||||
//Dig
|
||||
1000,
|
||||
//DestroyRoughConstruction
|
||||
1000,
|
||||
//DestroySmoothConstruction
|
||||
100,
|
||||
};
|
||||
|
||||
DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands)
|
||||
{
|
||||
commands.push_back(PluginCommand(
|
||||
"diggingInvaders", "Makes invaders dig to your dwarves.",
|
||||
diggingInvadersCommand, false, /* true means that the command can't be used from non-interactive user interface */
|
||||
" diggingInvaders 0\n disables the plugin\n"
|
||||
" diggingInvaders 1\n enables the plugin\n"
|
||||
" diggingInvaders enable\n enables the plugin\n"
|
||||
" diggingInvaders disable\n disables the plugin\n"
|
||||
" diggingInvaders add GOBLIN\n registers the race GOBLIN as a digging invader. Case-sensitive.\n"
|
||||
" diggingInvaders remove GOBLIN\n unregisters the race GOBLIN as a digging invader. Case-sensitive.\n"
|
||||
" diggingInvaders setCost GOBLIN walk n\n sets the walk cost in the path algorithm for the race GOBLIN\n"
|
||||
" diggingInvaders setCost GOBLIN destroyBuilding n\n"
|
||||
" diggingInvaders setCost GOBLIN dig n\n"
|
||||
" diggingInvaders setCost GOBLIN destroyRoughConstruction n\n rough constructions are made from boulders\n"
|
||||
" diggingInvaders setCost GOBLIN destroySmoothConstruction n\n smooth constructions are made from blocks or bars instead of boulders\n"
|
||||
" diggingInvaders setDelay GOBLIN destroyBuilding n\n adds to the job_completion_timer of destroy building jobs that are assigned to invaders\n"
|
||||
" diggingInvaders setDelay GOBLIN dig n\n"
|
||||
" diggingInvaders setDelay GOBLIN destroyRoughConstruction n\n"
|
||||
" diggingInvaders setDelay GOBLIN destroySmoothConstruction n\n"
|
||||
" diggingInvaders now\n makes invaders try to dig now, if plugin is enabled\n"
|
||||
" diggingInvaders clear\n clears all digging invader races\n"
|
||||
" diggingInvaders edgesPerTick n\n makes the pathfinding algorithm work on at most n edges per tick. Set to 0 or lower to make it unlimited."
|
||||
// " diggingInvaders\n Makes invaders try to dig now.\n"
|
||||
));
|
||||
|
||||
//*df::global::debug_showambush = true;
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
|
||||
{
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_enable(color_ostream& out, bool enable) {
|
||||
if ( enabled == enable )
|
||||
return CR_OK;
|
||||
|
||||
enabled = enable;
|
||||
EventManager::unregisterAll(plugin_self);
|
||||
clearDijkstra();
|
||||
lastInvasionJob = lastInvasionDigger = -1;
|
||||
activeDigging = false;
|
||||
invaderJobs.clear();
|
||||
if ( enabled ) {
|
||||
EventManager::EventHandler handler(newInvasionHandler, 1000);
|
||||
EventManager::registerListener(EventManager::EventType::INVASION, handler, plugin_self);
|
||||
findAndAssignInvasionJob(out, (void*)0);
|
||||
}
|
||||
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
|
||||
{
|
||||
switch (event) {
|
||||
case DFHack::SC_WORLD_LOADED:
|
||||
//TODO: check game mode
|
||||
//in case there are invaders when the game is loaded, we check if there's work to be done
|
||||
activeDigging = enabled;
|
||||
clearDijkstra();
|
||||
findAndAssignInvasionJob(out, (void*)0);
|
||||
break;
|
||||
case DFHack::SC_WORLD_UNLOADED:
|
||||
// cleanup
|
||||
plugin_enable(out, false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
df::coord getRoot(df::coord point, unordered_map<df::coord, df::coord>& rootMap);
|
||||
|
||||
class PointComp {
|
||||
public:
|
||||
unordered_map<df::coord, cost_t, PointHash> *pointCost;
|
||||
PointComp(unordered_map<df::coord, cost_t, PointHash> *p): pointCost(p) {
|
||||
|
||||
}
|
||||
|
||||
int32_t operator()(df::coord p1, df::coord p2) {
|
||||
if ( p1 == p2 ) return 0;
|
||||
auto i1 = pointCost->find(p1);
|
||||
auto i2 = pointCost->find(p2);
|
||||
if ( i1 == pointCost->end() && i2 == pointCost->end() )
|
||||
return p1 < p2;
|
||||
if ( i1 == pointCost->end() )
|
||||
return true;
|
||||
if ( i2 == pointCost->end() )
|
||||
return false;
|
||||
cost_t c1 = (*i1).second;
|
||||
cost_t c2 = (*i2).second;
|
||||
if ( c1 != c2 )
|
||||
return c1 < c2;
|
||||
return p1 < p2;
|
||||
}
|
||||
};
|
||||
|
||||
//bool important(df::coord pos, map<df::coord, set<Edge> >& edges, df::coord prev, set<df::coord>& importantPoints, set<Edge>& importantEdges);
|
||||
|
||||
void newInvasionHandler(color_ostream& out, void* ptr) {
|
||||
if ( activeDigging )
|
||||
return;
|
||||
activeDigging = true;
|
||||
findAndAssignInvasionJob(out, (void*)0);
|
||||
}
|
||||
|
||||
command_result diggingInvadersCommand(color_ostream& out, std::vector<std::string>& parameters) {
|
||||
for ( size_t a = 0; a < parameters.size(); a++ ) {
|
||||
if ( parameters[a] == "1" || parameters[a] == "enable" ) {
|
||||
plugin_enable(out,true);
|
||||
} else if ( parameters[a] == "0" || parameters[a] == "disable" ) {
|
||||
plugin_enable(out,false);
|
||||
} else if ( parameters[a] == "add" || parameters[a] == "remove" ) {
|
||||
if ( a+1 >= parameters.size() )
|
||||
return CR_WRONG_USAGE;
|
||||
string race = parameters[a+1];
|
||||
if ( parameters[a] == "add" ) {
|
||||
diggingRaces.insert(race);
|
||||
DigAbilities& abilities = digAbilities[race];
|
||||
memcpy(abilities.costWeight, costWeightDefault, costDim*sizeof(cost_t));
|
||||
memcpy(abilities.jobDelay, jobDelayDefault, costDim*sizeof(int32_t));
|
||||
} else {
|
||||
diggingRaces.erase(race);
|
||||
digAbilities.erase(race);
|
||||
}
|
||||
a++;
|
||||
|
||||
} else if ( parameters[a] == "setCost" || parameters[a] == "setDelay" ) {
|
||||
if ( a+3 >= parameters.size() )
|
||||
return CR_WRONG_USAGE;
|
||||
|
||||
string raceString = parameters[a+1];
|
||||
if ( digAbilities.find(raceString) == digAbilities.end() ) {
|
||||
DigAbilities bob;
|
||||
memset(&bob, 0xFF, sizeof(bob));
|
||||
digAbilities[raceString] = bob;
|
||||
}
|
||||
DigAbilities& abilities = digAbilities[raceString];
|
||||
|
||||
string costStr = parameters[a+2];
|
||||
int32_t costDim = -1;
|
||||
if ( costStr == "walk" ) {
|
||||
costDim = CostDimension::Walk;
|
||||
if ( parameters[a] == "setDelay" )
|
||||
return CR_WRONG_USAGE;
|
||||
} else if ( costStr == "destroyBuilding" ) {
|
||||
costDim = CostDimension::DestroyBuilding;
|
||||
} else if ( costStr == "dig" ) {
|
||||
costDim = CostDimension::Dig;
|
||||
} else if ( costStr == "destroyRoughConstruction" ) {
|
||||
costDim = CostDimension::DestroyRoughConstruction;
|
||||
} else if ( costStr == "destroySmoothConstruction" ) {
|
||||
costDim = CostDimension::DestroySmoothConstruction;
|
||||
} else {
|
||||
return CR_WRONG_USAGE;
|
||||
}
|
||||
|
||||
cost_t value;
|
||||
stringstream asdf(parameters[a+3]);
|
||||
asdf >> value;
|
||||
//if ( parameters[a] == "setCost" && value <= 0 )
|
||||
// return CR_WRONG_USAGE;
|
||||
if ( parameters[a] == "setCost" ) {
|
||||
abilities.costWeight[costDim] = value;
|
||||
} else {
|
||||
abilities.jobDelay[costDim] = value;
|
||||
}
|
||||
a += 3;
|
||||
} else if ( parameters[a] == "edgeCost" ) {
|
||||
if ( a+1 >= parameters.size() )
|
||||
return CR_WRONG_USAGE;
|
||||
|
||||
string raceString = parameters[a+1];
|
||||
|
||||
if ( digAbilities.find(raceString) == digAbilities.end() ) {
|
||||
out.print("Race %s does not have dig abilities assigned.\n", raceString.c_str());
|
||||
return CR_WRONG_USAGE;
|
||||
}
|
||||
DigAbilities& abilities = digAbilities[raceString];
|
||||
|
||||
df::coord bob = Gui::getCursorPos();
|
||||
out.print("(%d,%d,%d), (%d,%d,%d): cost = %lld\n", lastDebugEdgeCostPoint.x, lastDebugEdgeCostPoint.y, lastDebugEdgeCostPoint.z, bob.x, bob.y, bob.z, getEdgeCost(out, lastDebugEdgeCostPoint, bob, abilities));
|
||||
lastDebugEdgeCostPoint = bob;
|
||||
a++;
|
||||
} else if ( parameters[a] == "now" ) {
|
||||
activeDigging = true;
|
||||
findAndAssignInvasionJob(out, (void*)0);
|
||||
} else if ( parameters[a] == "clear" ) {
|
||||
diggingRaces.clear();
|
||||
digAbilities.clear();
|
||||
} else if ( parameters[a] == "edgesPerTick" ) {
|
||||
if ( a+1 >= parameters.size() )
|
||||
return CR_WRONG_USAGE;
|
||||
stringstream asdf(parameters[a+1]);
|
||||
int32_t edgeCount = 100;
|
||||
asdf >> edgeCount;
|
||||
edgesPerTick = edgeCount;
|
||||
a++;
|
||||
}
|
||||
else {
|
||||
return CR_WRONG_USAGE;
|
||||
}
|
||||
}
|
||||
activeDigging = enabled;
|
||||
out.print("diggingInvaders: enabled = %d, activeDigging = %d, edgesPerTick = %d\n", enabled, activeDigging, edgesPerTick);
|
||||
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
//dijkstra globals
|
||||
vector<int32_t> invaders;
|
||||
unordered_set<df::coord, PointHash> invaderPts;
|
||||
unordered_set<df::coord, PointHash> localPts;
|
||||
unordered_map<df::coord,df::coord,PointHash> parentMap;
|
||||
unordered_map<df::coord,cost_t,PointHash> costMap;
|
||||
|
||||
PointComp comp(&costMap);
|
||||
set<df::coord, PointComp> fringe(comp);
|
||||
EventManager::EventHandler findJobTickHandler(findAndAssignInvasionJob, 1);
|
||||
|
||||
int32_t localPtsFound = 0;
|
||||
unordered_set<df::coord,PointHash> closedSet;
|
||||
unordered_map<df::coord,int32_t,PointHash> workNeeded; //non-walking work needed to get there
|
||||
bool foundTarget = false;
|
||||
int32_t edgeCount = 0;
|
||||
|
||||
void clearDijkstra() {
|
||||
invaders.clear();
|
||||
invaderPts.clear();
|
||||
localPts.clear();
|
||||
parentMap.clear();
|
||||
costMap.clear();
|
||||
comp = PointComp(&costMap);
|
||||
fringe = set<df::coord,PointComp>(comp);
|
||||
localPtsFound = edgeCount = 0;
|
||||
foundTarget = false;
|
||||
closedSet.clear();
|
||||
workNeeded.clear();
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void findAndAssignInvasionJob(color_ostream& out, void* tickTime) {
|
||||
CoreSuspender suspend;
|
||||
//returns the worker id of the job created //used to
|
||||
//out.print("%s, %d: %d\n", __FILE__, __LINE__, (int32_t)tickTime);
|
||||
|
||||
if ( !enabled || !activeDigging ) {
|
||||
clearDijkstra();
|
||||
return;
|
||||
}
|
||||
EventManager::unregister(EventManager::EventType::TICK, findJobTickHandler, plugin_self);
|
||||
EventManager::registerTick(findJobTickHandler, 1, plugin_self);
|
||||
|
||||
if ( fringe.empty() ) {
|
||||
df::unit* lastDigger = df::unit::find(lastInvasionDigger);
|
||||
if ( lastDigger && lastDigger->job.current_job && lastDigger->job.current_job->id == lastInvasionJob ) {
|
||||
return;
|
||||
}
|
||||
//out.print("%s,%d: lastDigger = %d, last job = %d, last digger's job = %d\n", __FILE__, __LINE__, lastInvasionDigger, lastInvasionJob, !lastDigger ? -1 : (!lastDigger->job.current_job ? -1 : lastDigger->job.current_job->id));
|
||||
lastInvasionDigger = lastInvasionJob = -1;
|
||||
|
||||
clearDijkstra();
|
||||
unordered_set<uint16_t> invaderConnectivity;
|
||||
unordered_set<uint16_t> localConnectivity;
|
||||
|
||||
//find all locals and invaders
|
||||
for ( size_t a = 0; a < df::global::world->units.all.size(); a++ ) {
|
||||
df::unit* unit = df::global::world->units.all[a];
|
||||
if ( unit->flags1.bits.dead )
|
||||
continue;
|
||||
if ( Units::isCitizen(unit) ) {
|
||||
if ( localPts.find(unit->pos) != localPts.end() )
|
||||
continue;
|
||||
localPts.insert(unit->pos);
|
||||
df::map_block* block = Maps::getTileBlock(unit->pos);
|
||||
localConnectivity.insert(block->walkable[unit->pos.x&0xF][unit->pos.y&0xF]);
|
||||
} else if ( unit->flags1.bits.active_invader ) {
|
||||
df::creature_raw* raw = df::creature_raw::find(unit->race);
|
||||
if ( raw == NULL ) {
|
||||
out.print("%s,%d: WTF? Couldn't find creature raw.\n", __FILE__, __LINE__);
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
if ( diggingRaces.find(raw->creature_id) == diggingRaces.end() )
|
||||
continue;
|
||||
*/
|
||||
if ( digAbilities.find(raw->creature_id) == digAbilities.end() )
|
||||
continue;
|
||||
if ( invaderPts.find(unit->pos) != invaderPts.end() )
|
||||
continue;
|
||||
//must be able to wield a pick: this is overly pessimistic
|
||||
if ( unit->status2.limbs_grasp_max <= 0 || unit->status2.limbs_grasp_count < unit->status2.limbs_grasp_max )
|
||||
continue;
|
||||
df::map_block* block = Maps::getTileBlock(unit->pos);
|
||||
invaderConnectivity.insert(block->walkable[unit->pos.x&0xF][unit->pos.y&0xF]);
|
||||
if ( invaderPts.size() > 0 )
|
||||
continue;
|
||||
invaderPts.insert(unit->pos);
|
||||
costMap[unit->pos] = 0;
|
||||
fringe.insert(unit->pos);
|
||||
invaders.push_back(unit->id);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ( invaders.empty() || localPts.empty() ) {
|
||||
activeDigging = false;
|
||||
return;
|
||||
}
|
||||
|
||||
//if local connectivity is not disjoint from invader connectivity, no digging required
|
||||
bool overlap = false;
|
||||
for ( auto a = localConnectivity.begin(); a != localConnectivity.end(); a++ ) {
|
||||
uint16_t conn = *a;
|
||||
if ( invaderConnectivity.find(conn) == invaderConnectivity.end() )
|
||||
continue;
|
||||
overlap = true;
|
||||
break;
|
||||
}
|
||||
if ( overlap ) {
|
||||
//still keep checking next frame: might kill a few outsiders then dig down
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
df::unit* firstInvader = df::unit::find(invaders[0]);
|
||||
if ( firstInvader == NULL ) {
|
||||
fringe.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
df::creature_raw* creature_raw = df::creature_raw::find(firstInvader->race);
|
||||
if ( creature_raw == NULL || digAbilities.find(creature_raw->creature_id) == digAbilities.end() ) {
|
||||
//inappropriate digger: no dig abilities
|
||||
fringe.clear();
|
||||
return;
|
||||
}
|
||||
DigAbilities& abilities = digAbilities[creature_raw->creature_id];
|
||||
//TODO: check that firstInvader is an appropriate digger
|
||||
//out << firstInvader->id << endl;
|
||||
//out << firstInvader->pos.x << ", " << firstInvader->pos.y << ", " << firstInvader->pos.z << endl;
|
||||
//out << __LINE__ << endl;
|
||||
|
||||
uint32_t xMax, yMax, zMax;
|
||||
Maps::getSize(xMax,yMax,zMax);
|
||||
xMax *= 16;
|
||||
yMax *= 16;
|
||||
MapExtras::MapCache cache;
|
||||
|
||||
clock_t t0 = clock();
|
||||
clock_t totalEdgeTime = 0;
|
||||
int32_t edgesExpanded = 0;
|
||||
while(!fringe.empty()) {
|
||||
if ( edgesPerTick > 0 && edgesExpanded++ >= edgesPerTick ) {
|
||||
return;
|
||||
}
|
||||
df::coord pt = *(fringe.begin());
|
||||
fringe.erase(fringe.begin());
|
||||
//out.print("line %d: fringe size = %d, localPtsFound = %d / %d, closedSetSize = %d, pt = %d,%d,%d\n", __LINE__, fringe.size(), localPtsFound, localPts.size(), closedSet.size(), pt.x,pt.y,pt.z);
|
||||
if ( closedSet.find(pt) != closedSet.end() ) {
|
||||
out.print("%s, line %d: Double closure! Bad!\n", __FILE__, __LINE__);
|
||||
break;
|
||||
}
|
||||
closedSet.insert(pt);
|
||||
|
||||
if ( localPts.find(pt) != localPts.end() ) {
|
||||
localPtsFound++;
|
||||
if ( true || localPtsFound >= localPts.size() ) {
|
||||
foundTarget = true;
|
||||
break;
|
||||
}
|
||||
if ( workNeeded.find(pt) == workNeeded.end() || workNeeded[pt] == 0 ) {
|
||||
//there are still dwarves to kill that don't require digging to get to
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
cost_t myCost = costMap[pt];
|
||||
clock_t edgeTime = clock();
|
||||
vector<Edge>* myEdges = getEdgeSet(out, pt, cache, xMax, yMax, zMax, abilities);
|
||||
totalEdgeTime += (clock() - edgeTime);
|
||||
for ( auto a = myEdges->begin(); a != myEdges->end(); a++ ) {
|
||||
Edge &e = *a;
|
||||
if ( e.p1 == df::coord() )
|
||||
break;
|
||||
edgeCount++;
|
||||
df::coord& other = e.p1;
|
||||
if ( other == pt )
|
||||
other = e.p2;
|
||||
//if ( closedSet.find(other) != closedSet.end() )
|
||||
// continue;
|
||||
auto i = costMap.find(other);
|
||||
if ( i != costMap.end() ) {
|
||||
cost_t cost = (*i).second;
|
||||
if ( cost <= myCost + e.cost ) {
|
||||
continue;
|
||||
}
|
||||
fringe.erase((*i).first);
|
||||
}
|
||||
costMap[other] = myCost + e.cost;
|
||||
fringe.insert(other);
|
||||
parentMap[other] = pt;
|
||||
workNeeded[other] = (e.cost > 1 ? 1 : 0) + workNeeded[pt];
|
||||
}
|
||||
delete myEdges;
|
||||
}
|
||||
clock_t time = clock() - t0;
|
||||
//out.print("tickTime = %d, time = %d, totalEdgeTime = %d, total points = %d, total edges = %d, time per point = %.3f, time per edge = %.3f, clocks/sec = %d\n", (int32_t)tickTime, time, totalEdgeTime, closedSet.size(), edgeCount, (float)time / closedSet.size(), (float)time / edgeCount, CLOCKS_PER_SEC);
|
||||
fringe.clear();
|
||||
|
||||
if ( !foundTarget )
|
||||
return;
|
||||
|
||||
unordered_set<df::coord, PointHash> requiresZNeg;
|
||||
unordered_set<df::coord, PointHash> requiresZPos;
|
||||
|
||||
//find important edges
|
||||
Edge firstImportantEdge(df::coord(), df::coord(), -1);
|
||||
//df::coord closest;
|
||||
//cost_t closestCostEstimate=0;
|
||||
//cost_t closestCostActual=0;
|
||||
for ( auto i = localPts.begin(); i != localPts.end(); i++ ) {
|
||||
df::coord pt = *i;
|
||||
if ( costMap.find(pt) == costMap.end() )
|
||||
continue;
|
||||
if ( parentMap.find(pt) == parentMap.end() )
|
||||
continue;
|
||||
//closest = pt;
|
||||
//closestCostEstimate = costMap[closest];
|
||||
//if ( workNeeded[pt] == 0 )
|
||||
// continue;
|
||||
while ( parentMap.find(pt) != parentMap.end() ) {
|
||||
//out.print("(%d,%d,%d)\n", pt.x, pt.y, pt.z);
|
||||
df::coord parent = parentMap[pt];
|
||||
cost_t cost = getEdgeCost(out, parent, pt, abilities);
|
||||
if ( cost < 0 ) {
|
||||
//path invalidated
|
||||
return;
|
||||
}
|
||||
//closestCostActual += cost;
|
||||
if ( Maps::canStepBetween(parent, pt) ) {
|
||||
|
||||
} else {
|
||||
if ( pt.x == parent.x && pt.y == parent.y ) {
|
||||
if ( pt.z < parent.z ) {
|
||||
requiresZNeg.insert(parent);
|
||||
requiresZPos.insert(pt);
|
||||
} else if ( pt.z > parent.z ) {
|
||||
requiresZNeg.insert(pt);
|
||||
requiresZPos.insert(parent);
|
||||
}
|
||||
}
|
||||
//if ( workNeeded[pt] > workNeeded[parent] ) {
|
||||
//importantEdges.push_front(Edge(pt,parent,0));
|
||||
//}
|
||||
firstImportantEdge = Edge(pt,parent,0);
|
||||
//out.print("(%d,%d,%d) -> (%d,%d,%d)\n", parent.x,parent.y,parent.z, pt.x,pt.y,pt.z);
|
||||
}
|
||||
pt = parent;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if ( firstImportantEdge.p1 == df::coord() )
|
||||
return;
|
||||
|
||||
/*
|
||||
if ( closestCostActual != closestCostEstimate ) {
|
||||
out.print("%s,%d: closest = (%d,%d,%d), estimate = %lld != actual = %lld\n", __FILE__, __LINE__, closest.x,closest.y,closest.z, closestCostEstimate, closestCostActual);
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
assignJob(out, firstImportantEdge, parentMap, costMap, invaders, requiresZNeg, requiresZPos, cache, abilities);
|
||||
lastInvasionDigger = firstInvader->id;
|
||||
lastInvasionJob = firstInvader->job.current_job ? firstInvader->job.current_job->id : -1;
|
||||
invaderJobs.erase(lastInvasionJob);
|
||||
for ( df::job_list_link* link = &df::global::world->job_list; link != NULL; link = link->next ) {
|
||||
if ( link->item == NULL )
|
||||
continue;
|
||||
df::job* job = link->item;
|
||||
if ( invaderJobs.find(job->id) == invaderJobs.end() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//cancel it
|
||||
job->flags.bits.item_lost = 1;
|
||||
out.print("%s,%d: cancelling job %d.\n", __FILE__,__LINE__, job->id);
|
||||
//invaderJobs.remove(job->id);
|
||||
}
|
||||
invaderJobs.erase(lastInvasionJob);
|
||||
return;
|
||||
}
|
||||
|
||||
df::coord getRoot(df::coord point, map<df::coord, df::coord>& rootMap) {
|
||||
map<df::coord, df::coord>::iterator i = rootMap.find(point);
|
||||
if ( i == rootMap.end() ) {
|
||||
rootMap[point] = point;
|
||||
return point;
|
||||
}
|
||||
df::coord parent = (*i).second;
|
||||
if ( parent == point )
|
||||
return parent;
|
||||
df::coord root = getRoot(parent, rootMap);
|
||||
rootMap[point] = root;
|
||||
return root;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,481 @@
|
||||
#include "edgeCost.h"
|
||||
|
||||
#include "modules/Buildings.h"
|
||||
#include "modules/Maps.h"
|
||||
#include "modules/MapCache.h"
|
||||
|
||||
#include "df/building.h"
|
||||
#include "df/building_bridgest.h"
|
||||
#include "df/building_hatchst.h"
|
||||
#include "df/building_type.h"
|
||||
#include "df/construction.h"
|
||||
#include "df/coord.h"
|
||||
#include "df/item_type.h"
|
||||
#include "df/map_block.h"
|
||||
#include "df/tile_building_occ.h"
|
||||
#include "df/tiletype.h"
|
||||
#include "df/tiletype_material.h"
|
||||
#include "df/tiletype_shape.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
/*
|
||||
cost_t costWeight[] = {
|
||||
//Distance
|
||||
1,
|
||||
//Destroy Building
|
||||
2,
|
||||
//Dig
|
||||
10000,
|
||||
//DestroyConstruction
|
||||
100,
|
||||
};
|
||||
|
||||
int32_t jobDelay[] = {
|
||||
//Distance
|
||||
-1,
|
||||
//Destroy Building
|
||||
1000,
|
||||
//Dig
|
||||
1000,
|
||||
//DestroyConstruction
|
||||
1000
|
||||
};
|
||||
*/
|
||||
|
||||
using namespace std;
|
||||
|
||||
/*
|
||||
limitations
|
||||
ramps
|
||||
cave-ins
|
||||
*/
|
||||
cost_t getEdgeCost(color_ostream& out, df::coord pt1, df::coord pt2, DigAbilities& abilities) {
|
||||
int32_t dx = pt2.x - pt1.x;
|
||||
int32_t dy = pt2.y - pt1.y;
|
||||
int32_t dz = pt2.z - pt1.z;
|
||||
cost_t cost = abilities.costWeight[CostDimension::Walk];
|
||||
if ( cost < 0 )
|
||||
return -1;
|
||||
|
||||
if ( Maps::getTileBlock(pt1) == NULL || Maps::getTileBlock(pt2) == NULL )
|
||||
return -1;
|
||||
|
||||
df::tiletype* type2 = Maps::getTileType(pt2);
|
||||
df::tiletype_shape shape2 = ENUM_ATTR(tiletype, shape, *type2);
|
||||
|
||||
if ( Maps::getTileBlock(pt1)->designation[pt1.x&0xF][pt1.y&0xF].bits.flow_size >= 4 )
|
||||
return -1;
|
||||
if ( Maps::getTileBlock(pt2)->designation[pt2.x&0xF][pt2.y&0xF].bits.flow_size >= 4 )
|
||||
return -1;
|
||||
|
||||
if ( shape2 == df::enums::tiletype_shape::EMPTY ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( shape2 == df::enums::tiletype_shape::TREE )
|
||||
return -1;
|
||||
|
||||
/*
|
||||
if () {
|
||||
df::map_block* temp = Maps::getTileBlock(df::coord(pt1.x,pt1.y,pt1.z-1));
|
||||
if ( temp && temp->designation[pt1.x&0xF][pt1.y&0xF]
|
||||
}
|
||||
*/
|
||||
|
||||
if ( Maps::canStepBetween(pt1, pt2) ) {
|
||||
return cost;
|
||||
}
|
||||
|
||||
df::building* building2 = Buildings::findAtTile(pt2);
|
||||
if ( building2 ) {
|
||||
if ( abilities.costWeight[CostDimension::DestroyBuilding] < 0 )
|
||||
return -1;
|
||||
cost += abilities.costWeight[CostDimension::DestroyBuilding];
|
||||
if ( dx*dx + dy*dy > 1 )
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool construction2 = ENUM_ATTR(tiletype, material, *type2) == df::enums::tiletype_material::CONSTRUCTION;
|
||||
if ( construction2 ) {
|
||||
//smooth or not?
|
||||
df::construction* constr = df::construction::find(pt2);
|
||||
bool smooth = constr != NULL && constr->item_type != df::enums::item_type::BOULDER;
|
||||
if ( smooth ) {
|
||||
if ( abilities.costWeight[CostDimension::DestroySmoothConstruction] < 0 )
|
||||
return -1;
|
||||
cost += abilities.costWeight[CostDimension::DestroySmoothConstruction];
|
||||
} else {
|
||||
if ( abilities.costWeight[CostDimension::DestroyRoughConstruction] < 0 )
|
||||
return -1;
|
||||
cost += abilities.costWeight[CostDimension::DestroyRoughConstruction];
|
||||
}
|
||||
}
|
||||
|
||||
if ( dz == 0 ) {
|
||||
if ( !building2 && !construction2 ) {
|
||||
//it has to be a wall
|
||||
if ( shape2 == df::enums::tiletype_shape::RAMP_TOP ) {
|
||||
return -1;
|
||||
} else if ( shape2 != df::enums::tiletype_shape::WALL ) {
|
||||
//out << "shape = " << (int32_t)shape2 << endl;
|
||||
//out << __FILE__ << ", line " << __LINE__ << ": WTF?" << endl;
|
||||
return cost;
|
||||
}
|
||||
if ( abilities.costWeight[CostDimension::Dig] < 0 ) {
|
||||
return -1;
|
||||
}
|
||||
cost += abilities.costWeight[CostDimension::Dig];
|
||||
}
|
||||
} else {
|
||||
if ( dx == 0 && dy == 0 ) {
|
||||
df::tiletype* type1 = Maps::getTileType(pt1);
|
||||
df::tiletype_shape shape1 = ENUM_ATTR(tiletype, shape, *type1);
|
||||
if ( dz > 0 ) {
|
||||
bool walkable_low2 = shape2 == df::tiletype_shape::STAIR_DOWN || shape2 == df::tiletype_shape::STAIR_UPDOWN;
|
||||
if ( !walkable_low2 ) {
|
||||
if ( building2 || construction2 )
|
||||
return -1;
|
||||
if ( abilities.costWeight[CostDimension::Dig] < 0 ) {
|
||||
return -1;
|
||||
}
|
||||
cost += abilities.costWeight[CostDimension::Dig];
|
||||
}
|
||||
|
||||
bool walkable_high1 = shape1 == df::tiletype_shape::STAIR_UP || shape1 == df::tiletype_shape::STAIR_UPDOWN;
|
||||
if ( !walkable_high1 ) {
|
||||
if ( shape1 != df::enums::tiletype_shape::WALL ) {
|
||||
return -1;
|
||||
}
|
||||
if ( abilities.costWeight[CostDimension::Dig] < 0 ) {
|
||||
return -1;
|
||||
}
|
||||
cost += abilities.costWeight[CostDimension::Dig];
|
||||
}
|
||||
|
||||
if ( building2 ) {
|
||||
//moving up through an open bridge or a usable hatch is fine. other buildings are not
|
||||
bool unforbiddenHatch = false;
|
||||
if ( building2->getType() == df::building_type::Hatch ) {
|
||||
df::building_hatchst* hatch = (df::building_hatchst*)building2;
|
||||
if ( !hatch->door_flags.bits.forbidden && !(hatch->door_flags.bits.operated_by_mechanisms&&hatch->door_flags.bits.closed) )
|
||||
unforbiddenHatch = true;
|
||||
}
|
||||
bool inactiveBridge = false;
|
||||
if ( building2->getType() == df::building_type::Bridge ) {
|
||||
df::building_bridgest* bridge = (df::building_bridgest*)building2;
|
||||
bool xMin = pt2.x == bridge->x1;
|
||||
bool xMax = pt2.x == bridge->x2;
|
||||
bool yMin = pt2.y == bridge->y1;
|
||||
bool yMax = pt2.y == bridge->y2;
|
||||
if ( !bridge->gate_flags.bits.closed ) {
|
||||
//if it's open, we could still be in the busy part of it
|
||||
if ( bridge->direction == df::building_bridgest::T_direction::Left && !xMin ) {
|
||||
inactiveBridge = true;
|
||||
} else if ( bridge->direction == df::building_bridgest::T_direction::Right && !xMax ) {
|
||||
inactiveBridge = true;
|
||||
} else if ( bridge->direction == df::building_bridgest::T_direction::Up && !yMax ) {
|
||||
inactiveBridge = true;
|
||||
} else if ( bridge->direction == df::building_bridgest::T_direction::Down && !yMin ) {
|
||||
inactiveBridge = true;
|
||||
} else if ( bridge->direction == df::building_bridgest::T_direction::Retracting ) {
|
||||
inactiveBridge = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( !unforbiddenHatch && !inactiveBridge )
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*bool forbidden = false;
|
||||
if ( building2 && building2->getType() == df::building_type::Hatch ) {
|
||||
df::building_hatchst* hatch = (df::building_hatchst*)building2;
|
||||
if ( hatch->door_flags.bits.forbidden )
|
||||
forbidden = true;
|
||||
}
|
||||
if ( forbidden )
|
||||
return -1;*/
|
||||
} else {
|
||||
bool walkable_high2 = shape2 == df::tiletype_shape::STAIR_UP || shape2 == df::tiletype_shape::STAIR_UPDOWN;
|
||||
if ( !walkable_high2 ) {
|
||||
if ( building2 || construction2 )
|
||||
return -1;
|
||||
|
||||
if ( shape2 != df::enums::tiletype_shape::WALL )
|
||||
return -1;
|
||||
if ( abilities.costWeight[CostDimension::Dig] < 0 ) {
|
||||
return -1;
|
||||
}
|
||||
cost += abilities.costWeight[CostDimension::Dig];
|
||||
}
|
||||
bool walkable_low1 = shape1 == df::tiletype_shape::STAIR_DOWN || shape1 == df::tiletype_shape::STAIR_UPDOWN;
|
||||
if ( !walkable_low1 ) {
|
||||
//if ( building1 || construction1 )
|
||||
//return -1;
|
||||
//TODO: consider ramps
|
||||
if ( shape1 == df::tiletype_shape::RAMP )
|
||||
return -1;
|
||||
if ( abilities.costWeight[CostDimension::Dig] < 0 ) {
|
||||
return -1;
|
||||
}
|
||||
cost += abilities.costWeight[CostDimension::Dig];
|
||||
}
|
||||
|
||||
df::building* building1 = Buildings::findAtTile(pt1);
|
||||
//if you're moving down, and you're on a bridge, and that bridge is lowered, then you can't do it
|
||||
if ( building1 && building1->getType() == df::building_type::Bridge ) {
|
||||
df::building_bridgest* bridge = (df::building_bridgest*)building2;
|
||||
if ( bridge->gate_flags.bits.closed ) {
|
||||
return -1;
|
||||
}
|
||||
//open bridges moving down, standing on bad spot
|
||||
if ( bridge->direction == df::building_bridgest::T_direction::Left && pt1.x == bridge->x1 )
|
||||
return -1;
|
||||
if ( bridge->direction == df::building_bridgest::T_direction::Right && pt1.x == bridge->x2 )
|
||||
return -1;
|
||||
if ( bridge->direction == df::building_bridgest::T_direction::Up && pt1.y == bridge->y1 )
|
||||
return -1;
|
||||
if ( bridge->direction == df::building_bridgest::T_direction::Down && pt1.y == bridge->y2 )
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool forbidden = false;
|
||||
if ( building1 && building1->getType() == df::building_type::Hatch ) {
|
||||
df::building_hatchst* hatch = (df::building_hatchst*)building1;
|
||||
if ( hatch->door_flags.bits.forbidden || hatch->door_flags.bits.closed && hatch->door_flags.bits.operated_by_mechanisms )
|
||||
forbidden = true;
|
||||
}
|
||||
|
||||
//you can deconstruct a hatch from the side
|
||||
if ( building1 && forbidden /*&& building1->getType() == df::building_type::Hatch*/ ) {
|
||||
/*
|
||||
df::coord support[] = {df::coord(pt1.x-1, pt1.y, pt1.z), df::coord(pt1.x+1,pt1.y,pt1.z), df::coord(pt1.x,pt1.y-1,pt1.z), df::coord(pt1.x,pt1.y+1,pt1.z)};
|
||||
if ( abilities.costWeight[CostDimension::DestroyBuilding] < 0 ) {
|
||||
return -1;
|
||||
}
|
||||
cost_t minCost = -1;
|
||||
for ( size_t a = 0; a < 4; a++ ) {
|
||||
df::tiletype* supportType = Maps::getTileType(support[a]);
|
||||
df::tiletype_shape shape = ENUM_ATTR(tiletype, shape, *supportType);
|
||||
df::tiletype_shape_basic basic = ENUM_ATTR(tiletype_shape, basic_shape, shape);
|
||||
cost_t cost2 = 2*abilities.costWeight[CostDimension::Walk] + abilities.costWeight[CostDimension::DestroyBuilding];
|
||||
if ( !Maps::canStepBetween(pt1, support[a]) ) {
|
||||
switch(basic) {
|
||||
case tiletype_shape_basic::Open:
|
||||
//TODO: check for a hatch or a bridge: that makes it ok
|
||||
continue;
|
||||
case tiletype_shape_basic::Wall:
|
||||
if ( ENUM_ATTR(tiletype, material, *supportType) == df::enums::tiletype_material::CONSTRUCTION ) {
|
||||
if ( abilities.costWeight[CostDimension::DestroyConstruction] < 0 ) {
|
||||
continue;
|
||||
}
|
||||
cost2 += abilities.costWeight[CostDimension::DestroyConstruction];
|
||||
} else {
|
||||
if ( abilities.costWeight[CostDimension::Dig] < 0 ) {
|
||||
continue;
|
||||
}
|
||||
cost2 += abilities.costWeight[CostDimension::Dig];
|
||||
}
|
||||
case tiletype_shape_basic::Ramp:
|
||||
//TODO: check for a hatch or a bridge: that makes it ok
|
||||
if ( shape == df::enums::tiletype_shape::RAMP_TOP ) {
|
||||
continue;
|
||||
}
|
||||
case tiletype_shape_basic::Stair:
|
||||
case tiletype_shape_basic::Floor:
|
||||
break;
|
||||
}
|
||||
if ( Buildings::findAtTile(support[a]) ) {
|
||||
if ( abilities.costWeight[CostDimension::DestroyBuilding] < 0 ) {
|
||||
continue;
|
||||
}
|
||||
cost2 += abilities.costWeight[CostDimension::DestroyBuilding];
|
||||
}
|
||||
}
|
||||
if ( minCost == -1 || cost2 < minCost )
|
||||
minCost = cost2;
|
||||
}
|
||||
if ( minCost == -1 )
|
||||
return -1;
|
||||
cost += minCost;
|
||||
|
||||
*/
|
||||
//note: assignJob is not ready for this level of sophistication, so don't allow it
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//nonvertical
|
||||
//out.print("%s, line %d: (%d,%d,%d)->(%d,%d,%d)\n", __FILE__, __LINE__, pt1.x,pt1.y,pt1.z, pt2.x,pt2.y,pt2.z);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
/*
|
||||
cost_t getEdgeCostOld(color_ostream& out, df::coord pt1, df::coord pt2) {
|
||||
//first, list all the facts
|
||||
int32_t dx = pt2.x - pt1.x;
|
||||
int32_t dy = pt2.y - pt1.y;
|
||||
int32_t dz = pt2.z - pt1.z;
|
||||
cost_t cost = costWeight[CostDimension::Walk];
|
||||
|
||||
if ( false ) {
|
||||
if ( Maps::canStepBetween(pt1,pt2) )
|
||||
return cost;
|
||||
return 100 + cost;
|
||||
}
|
||||
|
||||
Maps::ensureTileBlock(pt1);
|
||||
Maps::ensureTileBlock(pt2);
|
||||
df::tiletype* type1 = Maps::getTileType(pt1);
|
||||
df::tiletype* type2 = Maps::getTileType(pt2);
|
||||
df::map_block* block1 = Maps::getTileBlock(pt1);
|
||||
df::map_block* block2 = Maps::getTileBlock(pt2);
|
||||
df::tiletype_shape shape1 = ENUM_ATTR(tiletype, shape, *type1);
|
||||
df::tiletype_shape shape2 = ENUM_ATTR(tiletype, shape, *type2);
|
||||
|
||||
bool construction1 = ENUM_ATTR(tiletype, material, *type1) == df::enums::tiletype_material::CONSTRUCTION;
|
||||
bool construction2 = ENUM_ATTR(tiletype, material, *type2) == df::enums::tiletype_material::CONSTRUCTION;
|
||||
bool passable1 = block1->walkable[pt1.x&0xF][pt1.y&0xF] != 0;
|
||||
bool passable2 = block2->walkable[pt2.x&0xF][pt2.y&0xF] != 0;
|
||||
bool passable_high1 = ENUM_ATTR(tiletype_shape, passable_high, shape1);
|
||||
bool passable_high2 = ENUM_ATTR(tiletype_shape, passable_high, shape2);
|
||||
bool passable_low1 = ENUM_ATTR(tiletype_shape, passable_low, shape1);
|
||||
bool passable_low2 = ENUM_ATTR(tiletype_shape, passable_low, shape2);
|
||||
|
||||
bool building1, building2;
|
||||
bool sameBuilding = false;
|
||||
{
|
||||
df::enums::tile_building_occ::tile_building_occ awk = block1->occupancy[pt1.x&0x0F][pt1.y&0x0F].bits.building;
|
||||
building1 = awk == df::enums::tile_building_occ::Obstacle || awk == df::enums::tile_building_occ::Impassable;
|
||||
awk = block2->occupancy[pt2.x&0x0F][pt2.y&0x0F].bits.building;
|
||||
building2 = awk == df::enums::tile_building_occ::Obstacle || awk == df::enums::tile_building_occ::Impassable;
|
||||
if ( building1 && building2 ) {
|
||||
df::building* b1 = Buildings::findAtTile(pt1);
|
||||
df::building* b2 = Buildings::findAtTile(pt2);
|
||||
sameBuilding = b1 == b2;
|
||||
}
|
||||
}
|
||||
|
||||
if ( Maps::canStepBetween(pt1, pt2) ) {
|
||||
if ( building2 && !sameBuilding ) {
|
||||
cost += costWeight[CostDimension::DestroyBuilding];
|
||||
}
|
||||
return cost;
|
||||
}
|
||||
|
||||
if ( shape2 == df::enums::tiletype_shape::EMPTY ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
//cannot step between: find out why
|
||||
if ( dz == 0 ) {
|
||||
if ( passable2 && !passable1 ) {
|
||||
return cost;
|
||||
}
|
||||
if ( passable1 && passable2 ) {
|
||||
out << __FILE__ << ", line " << __LINE__ << ": WTF?" << endl;
|
||||
return cost;
|
||||
}
|
||||
//pt2 is not passable. it must be a construction, a building, or a wall.
|
||||
if ( building2 ) {
|
||||
if ( sameBuilding ) {
|
||||
//don't charge twice for destroying the same building
|
||||
return cost;
|
||||
}
|
||||
cost += costWeight[CostDimension::DestroyBuilding];
|
||||
return cost;
|
||||
}
|
||||
if ( construction2 ) {
|
||||
//impassible constructions must be deconstructed
|
||||
cost += costWeight[CostDimension::DestroyConstruction];
|
||||
return cost;
|
||||
}
|
||||
|
||||
if ( shape2 == df::enums::tiletype_shape::TREE ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( shape2 == df::enums::tiletype_shape::RAMP_TOP ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
//it has to be a wall
|
||||
if ( shape2 != df::enums::tiletype_shape::WALL ) {
|
||||
out << "shape = " << (int32_t)shape2 << endl;
|
||||
out << __FILE__ << ", line " << __LINE__ << ": WTF?" << endl;
|
||||
return cost;
|
||||
}
|
||||
|
||||
cost += costWeight[CostDimension::Dig];
|
||||
return cost;
|
||||
}
|
||||
|
||||
//dz != 0
|
||||
if ( dx == 0 && dy == 0 ) {
|
||||
if ( dz > 0 ) {
|
||||
if ( passable_low2 )
|
||||
return cost;
|
||||
if ( building2 || construction2 ) {
|
||||
return -1;
|
||||
}
|
||||
cost += costWeight[CostDimension::Dig];
|
||||
return cost;
|
||||
}
|
||||
|
||||
//descending
|
||||
if ( passable_high2 )
|
||||
return cost;
|
||||
|
||||
if ( building2 || construction2 ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
//must be a wall?
|
||||
if ( shape2 != df::enums::tiletype_shape::WALL ) {
|
||||
out.print("%s, line %n: WTF?\n", __FILE__, __LINE__);
|
||||
return cost;
|
||||
}
|
||||
|
||||
cost += costWeight[CostDimension::Dig];
|
||||
return cost;
|
||||
}
|
||||
|
||||
//moving diagonally
|
||||
return -1;
|
||||
}
|
||||
*/
|
||||
|
||||
vector<Edge>* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCache& cache, int32_t xMax, int32_t yMax, int32_t zMax, DigAbilities& abilities) {
|
||||
//vector<Edge>* result = new vector<Edge>(26);
|
||||
vector<Edge>* result = new vector<Edge>();
|
||||
result->reserve(26);
|
||||
|
||||
//size_t count = 0;
|
||||
for ( int32_t dx = -1; dx <= 1; dx++ ) {
|
||||
for ( int32_t dy = -1; dy <= 1; dy++ ) {
|
||||
for ( int32_t dz = -1; dz <= 1; dz++ ) {
|
||||
df::coord neighbor(point.x+dx, point.y+dy, point.z+dz);
|
||||
if ( !Maps::isValidTilePos(neighbor) )
|
||||
continue;
|
||||
if ( dz != 0 && /*(point.x == 0 || point.y == 0 || point.z == 0 || point.x == xMax-1 || point.y == yMax-1 || point.z == zMax-1) ||*/ (neighbor.x == 0 || neighbor.y == 0 || neighbor.z == 0 || neighbor.x == xMax-1 || neighbor.y == yMax-1 || neighbor.z == zMax-1) )
|
||||
continue;
|
||||
if ( dx == 0 && dy == 0 && dz == 0 )
|
||||
continue;
|
||||
cost_t cost = getEdgeCost(out, point, neighbor, abilities);
|
||||
if ( cost == -1 )
|
||||
continue;
|
||||
Edge edge(point, neighbor, cost);
|
||||
//(*result)[count] = edge;
|
||||
result->push_back(edge);
|
||||
//count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
|
||||
#include "Core.h"
|
||||
#include "Console.h"
|
||||
#include "DataDefs.h"
|
||||
|
||||
#include "modules/Maps.h"
|
||||
#include "modules/MapCache.h"
|
||||
|
||||
#include "df/coord.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
//cost is [path cost, building destruction cost, dig cost, construct cost]. Minimize constructions, then minimize dig cost, then minimize path cost.
|
||||
enum CostDimension {
|
||||
Walk,
|
||||
DestroyBuilding,
|
||||
Dig,
|
||||
DestroyRoughConstruction,
|
||||
DestroySmoothConstruction,
|
||||
//Construct,
|
||||
costDim
|
||||
};
|
||||
|
||||
typedef int64_t cost_t;
|
||||
|
||||
struct DigAbilities {
|
||||
cost_t costWeight[costDim];
|
||||
int32_t jobDelay[costDim];
|
||||
};
|
||||
|
||||
//extern cost_t costWeight[costDim];
|
||||
//extern int32_t jobDelay[costDim];
|
||||
extern std::unordered_map<std::string, DigAbilities> digAbilities;
|
||||
/*
|
||||
const cost_t costWeight[] = {
|
||||
//Distance
|
||||
1,
|
||||
//Destroy Building
|
||||
2,
|
||||
//Dig
|
||||
10000,
|
||||
//DestroyConstruction
|
||||
100,
|
||||
};
|
||||
*/
|
||||
|
||||
class Edge {
|
||||
public:
|
||||
//static map<df::coord, int32_t> pointCost;
|
||||
df::coord p1;
|
||||
df::coord p2;
|
||||
cost_t cost;
|
||||
Edge() {
|
||||
cost = -1;
|
||||
}
|
||||
Edge(const Edge& e): p1(e.p1), p2(e.p2), cost(e.cost) {
|
||||
|
||||
}
|
||||
Edge(df::coord p1In, df::coord p2In, cost_t costIn): cost(costIn) {
|
||||
if ( p2In < p1In ) {
|
||||
p1 = p2In;
|
||||
p2 = p1In;
|
||||
} else {
|
||||
p1 = p1In;
|
||||
p2 = p2In;
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const Edge& e) const {
|
||||
return (cost == e.cost && p1 == e.p1 && p2 == e.p2);
|
||||
}
|
||||
|
||||
bool operator<(const Edge& e) const {
|
||||
if ( cost != e.cost )
|
||||
return cost < e.cost;
|
||||
if ( p1.z != e.p1.z )
|
||||
return p1.z < e.p1.z;
|
||||
if ( p1 != e.p1 )
|
||||
return p1 < e.p1;
|
||||
if ( p2.z != e.p2.z )
|
||||
return p2.z < e.p2.z;
|
||||
if ( p2 != e.p2 )
|
||||
return p2 < e.p2;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct PointHash {
|
||||
size_t operator()(const df::coord c) const {
|
||||
return c.x * 65537 + c.y * 17 + c.z;
|
||||
}
|
||||
};
|
||||
|
||||
cost_t getEdgeCost(color_ostream& out, df::coord pt1, df::coord pt2, DigAbilities& abilities);
|
||||
std::vector<Edge>* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCache& cache, int32_t xMax, int32_t yMax, int32_t zMax, DigAbilities& abilities);
|
||||
|
@ -0,0 +1,63 @@
|
||||
|
||||
#include "Console.h"
|
||||
#include "Core.h"
|
||||
#include "DataDefs.h"
|
||||
#include "Export.h"
|
||||
#include "PluginManager.h"
|
||||
|
||||
#include "modules/Buildings.h"
|
||||
#include "modules/EventManager.h"
|
||||
#include "modules/Maps.h"
|
||||
|
||||
#include "df/coord.h"
|
||||
#include "df/building.h"
|
||||
#include "df/building_def.h"
|
||||
#include "df/map_block.h"
|
||||
#include "df/tile_designation.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace DFHack;
|
||||
using namespace std;
|
||||
|
||||
DFHACK_PLUGIN("outsideOnly");
|
||||
|
||||
void buildingCreated(color_ostream& out, void* data);
|
||||
|
||||
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
|
||||
{
|
||||
EventManager::EventHandler handler(buildingCreated,1);
|
||||
EventManager::registerListener(EventManager::EventType::BUILDING, handler, plugin_self);
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
// This is called right before the plugin library is removed from memory.
|
||||
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
|
||||
{
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
void buildingCreated(color_ostream& out, void* data) {
|
||||
int32_t id = (int32_t)data;
|
||||
df::building* building = df::building::find(id);
|
||||
if ( building == NULL )
|
||||
return;
|
||||
|
||||
if ( building->getCustomType() < 0 )
|
||||
return;
|
||||
string prefix("OUTSIDE_ONLY");
|
||||
df::building_def* def = df::global::world->raws.buildings.all[building->getCustomType()];
|
||||
if (def->code.compare(0, prefix.size(), prefix)) {
|
||||
return;
|
||||
}
|
||||
|
||||
//now, just check if it was created inside, and if so, scuttle it
|
||||
df::coord pos(building->centerx,building->centery,building->z);
|
||||
|
||||
df::tile_designation* des = Maps::getTileDesignation(pos);
|
||||
if ( des->bits.outside )
|
||||
return;
|
||||
|
||||
Buildings::deconstruct(building);
|
||||
}
|
||||
|
@ -0,0 +1,40 @@
|
||||
#include "Console.h"
|
||||
#include "Core.h"
|
||||
#include "DataDefs.h"
|
||||
#include "Export.h"
|
||||
#include "PluginManager.h"
|
||||
|
||||
//#include "df/world.h"
|
||||
|
||||
using namespace DFHack;
|
||||
|
||||
command_result skeleton2 (color_ostream &out, std::vector <std::string> & parameters);
|
||||
|
||||
DFHACK_PLUGIN("skeleton2");
|
||||
|
||||
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
|
||||
{
|
||||
commands.push_back(PluginCommand(
|
||||
"skeleton2",
|
||||
"shortHelpString",
|
||||
skeleton2,
|
||||
false, //allow non-interactive use
|
||||
"longHelpString"
|
||||
));
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
|
||||
{
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
command_result skeleton2 (color_ostream &out, std::vector <std::string> & parameters)
|
||||
{
|
||||
if (!parameters.empty())
|
||||
return CR_WRONG_USAGE;
|
||||
CoreSuspender suspend;
|
||||
out.print("blah");
|
||||
return CR_OK;
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 25eae7ef6f19a24b7fc7e0d72ed45cbef7ebe092
|
||||
Subproject commit 580ee33fd545fa7198690daecfdd92938c21d65f
|
@ -0,0 +1,198 @@
|
||||
|
||||
#include "Core.h"
|
||||
#include "Console.h"
|
||||
#include "DataDefs.h"
|
||||
#include "Export.h"
|
||||
#include "PluginManager.h"
|
||||
|
||||
#include "modules/EventManager.h"
|
||||
#include "modules/Once.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;
|
||||
|
||||
static bool enabled = false;
|
||||
|
||||
DFHACK_PLUGIN("syndromeTrigger");
|
||||
|
||||
void syndromeHandler(color_ostream& out, void* ptr);
|
||||
|
||||
command_result syndromeTrigger(color_ostream& out, vector<string>& parameters);
|
||||
|
||||
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
|
||||
{
|
||||
commands.push_back(PluginCommand("syndromeTrigger", "Run commands and enable true transformations, configured by the raw files.\n", &syndromeTrigger, false,
|
||||
"syndromeTrigger:\n"
|
||||
" syndromeTrigger 0 //disable\n"
|
||||
" syndromeTrigger 1 //enable\n"
|
||||
" syndromeTrigger disable //disable\n"
|
||||
" syndromeTrigger enable //enable\n"
|
||||
"\n"
|
||||
"See Readme.rst for details.\n"
|
||||
));
|
||||
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
command_result syndromeTrigger(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("syndromeTrigger is %s\n", enabled ? "enabled" : "disabled");
|
||||
if ( enabled == wasEnabled )
|
||||
return CR_OK;
|
||||
|
||||
EventManager::unregisterAll(plugin_self);
|
||||
if ( enabled ) {
|
||||
EventManager::EventHandler handle(syndromeHandler, 1);
|
||||
EventManager::registerListener(EventManager::EventType::SYNDROME, handle, plugin_self);
|
||||
}
|
||||
return CR_OK;
|
||||
|
||||
}
|
||||
|
||||
void syndromeHandler(color_ostream& out, void* ptr) {
|
||||
EventManager::SyndromeData* data = (EventManager::SyndromeData*)ptr;
|
||||
|
||||
if ( !ptr ) {
|
||||
if ( DFHack::Once::doOnce("syndromeTrigger_null data") ) {
|
||||
out.print("%s, %d: null pointer from EventManager.\n", __FILE__, __LINE__);
|
||||
}
|
||||
return;
|
||||
}
|
||||
//out.print("Syndrome started: unit %d, syndrome %d.\n", data->unitId, data->syndromeIndex);
|
||||
|
||||
df::unit* unit = df::unit::find(data->unitId);
|
||||
if (!unit) {
|
||||
if ( DFHack::Once::doOnce("syndromeTrigger_no find unit" ) )
|
||||
out.print("%s, line %d: couldn't find unit.\n", __FILE__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
df::unit_syndrome* unit_syndrome = unit->syndromes.active[data->syndromeIndex];
|
||||
//out.print(" syndrome type %d\n", unit_syndrome->type);
|
||||
df::syndrome* syndrome = df::global::world->raws.syndromes.all[unit_syndrome->type];
|
||||
|
||||
bool foundPermanent = false;
|
||||
bool foundCommand = false;
|
||||
bool foundAutoSyndrome = false;
|
||||
string commandStr;
|
||||
vector<string> args;
|
||||
int32_t raceId = -1;
|
||||
df::creature_raw* creatureRaw = NULL;
|
||||
int32_t casteId = -1;
|
||||
for ( size_t a = 0; a < syndrome->syn_class.size(); a++ ) {
|
||||
std::string& clazz = *syndrome->syn_class[a];
|
||||
//out.print(" clazz %d = %s\n", a, clazz.c_str());
|
||||
if ( foundCommand ) {
|
||||
if ( commandStr == "" ) {
|
||||
commandStr = clazz;
|
||||
continue;
|
||||
}
|
||||
stringstream bob;
|
||||
if ( clazz == "\\LOCATION" ) {
|
||||
bob << unit->pos.x;
|
||||
args.push_back(bob.str());
|
||||
bob.str("");
|
||||
bob.clear();
|
||||
|
||||
bob << unit->pos.y;
|
||||
args.push_back(bob.str());
|
||||
bob.str("");
|
||||
bob.clear();
|
||||
|
||||
bob << unit->pos.z;
|
||||
args.push_back(bob.str());
|
||||
bob.str("");
|
||||
bob.clear();
|
||||
} else if ( clazz == "\\UNIT_ID" ) {
|
||||
bob << unit->id;
|
||||
args.push_back(bob.str());
|
||||
bob.str("");
|
||||
bob.clear();
|
||||
} else if ( clazz == "\\SYNDROME_ID" ) {
|
||||
bob << unit_syndrome->type;
|
||||
args.push_back(bob.str());
|
||||
bob.str("");
|
||||
bob.clear();
|
||||
} else {
|
||||
args.push_back(clazz);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ( clazz == "\\AUTO_SYNDROME" ) {
|
||||
foundAutoSyndrome = true;
|
||||
continue;
|
||||
}
|
||||
if ( clazz == "\\COMMAND" ) {
|
||||
foundCommand = true;
|
||||
continue;
|
||||
}
|
||||
if ( clazz == "\\PERMANENT" ) {
|
||||
foundPermanent = true;
|
||||
continue;
|
||||
}
|
||||
if ( !foundPermanent )
|
||||
continue;
|
||||
if ( 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 ( raceId != -1 && casteId == -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;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !foundAutoSyndrome && commandStr != "" ) {
|
||||
Core::getInstance().runCommand(out, commandStr, args);
|
||||
}
|
||||
|
||||
if ( !foundPermanent || raceId == -1 || casteId == -1 )
|
||||
return;
|
||||
|
||||
unit->enemy.normal_race = raceId;
|
||||
unit->enemy.normal_caste = casteId;
|
||||
//that's it!
|
||||
}
|
||||
|
@ -0,0 +1,156 @@
|
||||
#include "Console.h"
|
||||
#include "Core.h"
|
||||
#include "DataDefs.h"
|
||||
#include "Export.h"
|
||||
#include "PluginManager.h"
|
||||
|
||||
#include "modules/EventManager.h"
|
||||
#include "modules/Once.h"
|
||||
|
||||
#include "df/block_burrow.h"
|
||||
#include "df/block_burrow_link.h"
|
||||
#include "df/burrow.h"
|
||||
#include "df/map_block.h"
|
||||
#include "df/tile_bitmask.h"
|
||||
#include "df/tile_dig_designation.h"
|
||||
#include "df/tiletype.h"
|
||||
#include "df/tiletype_shape.h"
|
||||
#include "df/world.h"
|
||||
|
||||
//#include "df/world.h"
|
||||
|
||||
using namespace DFHack;
|
||||
|
||||
void checkFarms(color_ostream& out, void* ptr);
|
||||
command_result treefarm (color_ostream &out, std::vector <std::string> & parameters);
|
||||
|
||||
EventManager::EventHandler handler(&checkFarms, -1);
|
||||
int32_t frequency = 1200*30;
|
||||
|
||||
DFHACK_PLUGIN_IS_ENABLED(enabled);
|
||||
DFHACK_PLUGIN("treefarm");
|
||||
|
||||
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
|
||||
{
|
||||
commands.push_back(PluginCommand(
|
||||
"treefarm",
|
||||
"automatically manages special burrows and regularly schedules tree chopping and digging when appropriate",
|
||||
treefarm,
|
||||
false, //allow non-interactive use
|
||||
"treefarm\n"
|
||||
" enables treefarm monitoring, starting next frame\n"
|
||||
"treefarm n\n"
|
||||
" enables treefarm monitoring, starting next frame\n"
|
||||
" sets monitoring interval to n frames\n"
|
||||
" if n is less than one, disables monitoring\n"
|
||||
"\n"
|
||||
"Every time the plugin runs, it checks for burrows with a name containing the string \"treefarm\". For each such burrow, it checks every tile in it for fully-grown trees and for diggable walls. For each fully-grown tree it finds, it designates the tree to be chopped, and for each natural wall it finds, it designates the wall to be dug.\n"
|
||||
));
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_enable(color_ostream& out, bool enable) {
|
||||
enabled = enable;
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
|
||||
{
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
void checkFarms(color_ostream& out, void* ptr) {
|
||||
EventManager::unregisterAll(plugin_self);
|
||||
if ( !enabled )
|
||||
return;
|
||||
EventManager::registerTick(handler, frequency, plugin_self);
|
||||
CoreSuspender suspend;
|
||||
|
||||
df::world* world = df::global::world;
|
||||
df::ui* ui = df::global::ui;
|
||||
int32_t xOffset = world->map.region_x*3;
|
||||
int32_t yOffset = world->map.region_y*3;
|
||||
int32_t zOffset = world->map.region_z;
|
||||
//for each burrow named treefarm or obsidianfarm, check if you can dig/chop any obsidian/trees
|
||||
for ( size_t a = 0; a < df::burrow::get_vector().size(); a++ ) {
|
||||
df::burrow* burrow = df::burrow::get_vector()[a];
|
||||
if ( !burrow || burrow->name.find("treefarm") == std::string::npos )
|
||||
continue;
|
||||
|
||||
if ( burrow->block_x.size() != burrow->block_y.size() || burrow->block_x.size() != burrow->block_z.size() )
|
||||
continue;
|
||||
|
||||
for ( size_t b = 0; b < burrow->block_x.size(); b++ ) {
|
||||
int32_t x=burrow->block_x[b] - xOffset;
|
||||
int32_t y=burrow->block_y[b] - yOffset;
|
||||
int32_t z=burrow->block_z[b] - zOffset;
|
||||
|
||||
df::map_block* block = world->map.block_index[x][y][z];
|
||||
if ( !block )
|
||||
continue;
|
||||
|
||||
df::block_burrow_link* link = &block->block_burrows;
|
||||
df::tile_bitmask mask;
|
||||
for ( ; link != NULL; link = link->next ) {
|
||||
if ( link->item == NULL )
|
||||
continue;
|
||||
if ( link->item->id == burrow->id ) {
|
||||
mask = link->item->tile_bitmask;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( link == NULL )
|
||||
continue;
|
||||
|
||||
for ( int32_t x = 0; x < 16; x++ ) {
|
||||
for ( int32_t y = 0; y < 16; y++ ) {
|
||||
if ( !mask.getassignment(x,y) )
|
||||
continue;
|
||||
df::tiletype type = block->tiletype[x][y];
|
||||
df::tiletype_shape shape = ENUM_ATTR(tiletype, shape, type);
|
||||
if ( !block->designation[x][y].bits.hidden &&
|
||||
shape != df::enums::tiletype_shape::WALL &&
|
||||
shape != df::enums::tiletype_shape::TREE )
|
||||
continue;
|
||||
if ( shape != df::enums::tiletype_shape::TREE ) {
|
||||
if ( x == 0 && (block->map_pos.x/16) == 0 )
|
||||
continue;
|
||||
if ( y == 0 && (block->map_pos.y/16) == 0 )
|
||||
continue;
|
||||
if ( x == 15 && (block->map_pos.x/16) == world->map.x_count_block-1 )
|
||||
continue;
|
||||
if ( y == 15 && (block->map_pos.y/16) == world->map.y_count_block-1 )
|
||||
continue;
|
||||
}
|
||||
|
||||
block->designation[x][y].bits.dig = df::enums::tile_dig_designation::Default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
command_result treefarm (color_ostream &out, std::vector <std::string> & parameters)
|
||||
{
|
||||
EventManager::unregisterAll(plugin_self);
|
||||
|
||||
if ( parameters.size() > 1 )
|
||||
return CR_WRONG_USAGE;
|
||||
if ( parameters.size() == 1 ) {
|
||||
int32_t i = atoi(parameters[0].c_str());
|
||||
if ( i < 1 ) {
|
||||
plugin_enable(out, false);
|
||||
out.print("treefarm disabled\n");
|
||||
return CR_OK;
|
||||
}
|
||||
plugin_enable(out, true);
|
||||
frequency = i;
|
||||
}
|
||||
|
||||
if ( enabled ) {
|
||||
EventManager::registerTick(handler, 1, plugin_self);
|
||||
out.print("treefarm enabled with update frequency %d ticks\n", frequency);
|
||||
}
|
||||
return CR_OK;
|
||||
}
|
||||
|
@ -1,87 +0,0 @@
|
||||
|
||||
#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);
|
||||
|
||||
df::unit* unit = df::unit::find(data->unitId);
|
||||
if (!unit) {
|
||||
out.print("%s, line %d: couldn't find unit.\n", __FILE__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
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!
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue