summaryrefslogtreecommitdiff
path: root/plugins/devel
diff options
context:
space:
mode:
authorAlexander Gavrilov2012-09-12 12:15:12 +0400
committerAlexander Gavrilov2012-09-12 12:15:12 +0400
commit448d7e3633efd5171feaa3c3a0cdc4afbfafe105 (patch)
treeedd566d773acf0a427a08da39caf8284f1aee515 /plugins/devel
parentb0938d7e0d80720ef1a6fd264418ba20c375d9ba (diff)
downloaddfhack-448d7e3633efd5171feaa3c3a0cdc4afbfafe105.tar.gz
dfhack-448d7e3633efd5171feaa3c3a0cdc4afbfafe105.tar.bz2
dfhack-448d7e3633efd5171feaa3c3a0cdc4afbfafe105.tar.xz
Support linking siege engines to stockpiles.
Since they can't do that natively, the links object has to be maintained in dfhack memory, and with dfhack persistence.
Diffstat (limited to 'plugins/devel')
-rw-r--r--plugins/devel/siege-engine.cpp203
1 files changed, 194 insertions, 9 deletions
diff --git a/plugins/devel/siege-engine.cpp b/plugins/devel/siege-engine.cpp
index 2720c62f..6385111b 100644
--- a/plugins/devel/siege-engine.cpp
+++ b/plugins/devel/siege-engine.cpp
@@ -46,6 +46,8 @@
#include "df/job_item.h"
#include "df/item.h"
#include "df/items_other_id.h"
+#include "df/building_stockpilest.h"
+#include "df/stockpile_links.h"
#include "MiscUtils.h"
@@ -158,6 +160,9 @@ struct EngineInfo {
int operator_id, operator_frame;
+ std::set<int> stockpiles;
+ df::stockpile_links links;
+
bool hasTarget() { return is_range_valid(target); }
bool onTarget(df::coord pos) { return is_in_range(target, pos); }
df::coord getTargetSize() { return target.second - target.first; }
@@ -170,6 +175,8 @@ struct EngineInfo {
static std::map<df::building*, EngineInfo> engines;
static std::map<df::coord, df::building*> coord_engines;
+static std::set<df::building*> recheck_piles;
+
static EngineInfo *find_engine(df::building *bld, bool create = false)
{
auto ebld = strict_virtual_cast<df::building_siegeenginest>(bld);
@@ -209,12 +216,12 @@ static EngineInfo *find_engine(df::building *bld, bool create = false)
return obj;
}
-static EngineInfo *find_engine(lua_State *L, int idx, bool create = false)
+static EngineInfo *find_engine(lua_State *L, int idx, bool create = false, bool silent = false)
{
auto bld = Lua::CheckDFObject<df::building_siegeenginest>(L, idx);
auto engine = find_engine(bld, create);
- if (!engine)
+ if (!engine && !silent)
luaL_error(L, "no such engine");
return engine;
@@ -241,6 +248,7 @@ static void clear_engines()
{
engines.clear();
coord_engines.clear();
+ recheck_piles.clear();
}
static void load_engines()
@@ -267,13 +275,33 @@ static void load_engines()
engine->ammo_vector_id = (df::job_item_vector_id)it->ival(1);
engine->ammo_item_type = (df::item_type)it->ival(2);
}
+
+ pworld->GetPersistentData(&vec, "siege-engine/stockpiles/", true);
+ for (auto it = vec.begin(); it != vec.end(); ++it)
+ {
+ auto engine = find_engine(df::building::find(it->ival(0)), true);
+ if (!engine)
+ continue;
+ auto pile = df::building::find(it->ival(1));
+ if (!pile || pile->getType() != building_type::Stockpile)
+ {
+ pworld->DeletePersistentData(*it);
+ continue;;
+ }
+ auto plinks = pile->getStockpileLinks();
+ if (!plinks)
+ continue;
+
+ engine->stockpiles.insert(it->ival(1));
+
+ insert_into_vector(engine->links.take_from_pile, &df::building::id, pile);
+ insert_into_vector(plinks->give_to_workshop, &df::building::id, (df::building*)engine->bld);
+ }
}
static int getTargetArea(lua_State *L)
{
- auto bld = Lua::CheckDFObject<df::building>(L, 1);
- if (!bld) luaL_argerror(L, 1, "null building");
- auto engine = find_engine(bld);
+ auto engine = find_engine(L, 1, false, true);
if (engine && engine->hasTarget())
{
@@ -335,8 +363,11 @@ static bool setTargetArea(df::building_siegeenginest *bld, df::coord target_min,
static int getAmmoItem(lua_State *L)
{
- auto engine = find_engine(L, 1, true);
- Lua::Push(L, engine->ammo_item_type);
+ auto engine = find_engine(L, 1, false, true);
+ if (!engine)
+ Lua::Push(L, item_type::BOULDER);
+ else
+ Lua::Push(L, engine->ammo_item_type);
return 1;
}
@@ -378,6 +409,123 @@ static int setAmmoItem(lua_State *L)
return 1;
}
+static int getStockpileLinks(lua_State *L)
+{
+ auto engine = find_engine(L, 1, false, true);
+ if (!engine || engine->stockpiles.empty())
+ return 0;
+
+ int idx = 1;
+ lua_createtable(L, engine->stockpiles.size(), 0);
+
+ for (auto it = engine->stockpiles.begin(); it != engine->stockpiles.end(); ++it)
+ {
+ auto pile = df::building::find(*it);
+ if (!pile) continue;
+ Lua::Push(L, pile);
+ lua_rawseti(L, -2, idx++);
+ }
+
+ return 1;
+}
+
+static bool isLinkedToPile(df::building_siegeenginest *bld, df::building_stockpilest *pile)
+{
+ CHECK_NULL_POINTER(bld);
+ CHECK_NULL_POINTER(pile);
+
+ auto engine = find_engine(bld);
+
+ return engine && engine->stockpiles.count(pile->id);
+}
+
+static bool addStockpileLink(df::building_siegeenginest *bld, df::building_stockpilest *pile)
+{
+ CHECK_NULL_POINTER(bld);
+ CHECK_NULL_POINTER(pile);
+
+ auto plinks = pile->getStockpileLinks();
+ CHECK_NULL_POINTER(plinks);
+
+ if (!enable_plugin())
+ return false;
+
+ auto pworld = Core::getInstance().getWorld();
+ auto key = stl_sprintf("siege-engine/stockpiles/%d/%d", bld->id, pile->id);
+ auto entry = pworld->GetPersistentData(key, NULL);
+ if (!entry.isValid())
+ return false;
+
+ auto engine = find_engine(bld, true);
+
+ entry.ival(0) = bld->id;
+ entry.ival(1) = pile->id;
+
+ engine->stockpiles.insert(pile->id);
+
+ insert_into_vector(engine->links.take_from_pile, &df::building::id, (df::building*)pile);
+ insert_into_vector(plinks->give_to_workshop, &df::building::id, (df::building*)engine->bld);
+ return true;
+}
+
+static void forgetStockpileLink(EngineInfo *engine, int pile_id)
+{
+ engine->stockpiles.erase(pile_id);
+
+ auto pworld = Core::getInstance().getWorld();
+ auto key = stl_sprintf("siege-engine/stockpiles/%d/%d", engine->id, pile_id);
+ pworld->DeletePersistentData(pworld->GetPersistentData(key));
+}
+
+static bool removeStockpileLink(df::building_siegeenginest *bld, df::building_stockpilest *pile)
+{
+ CHECK_NULL_POINTER(bld);
+ CHECK_NULL_POINTER(pile);
+
+ if (auto engine = find_engine(bld))
+ {
+ forgetStockpileLink(engine, pile->id);
+
+ auto plinks = pile->getStockpileLinks();
+ erase_from_vector(engine->links.take_from_pile, &df::building::id, pile->id);
+ erase_from_vector(plinks->give_to_workshop, &df::building::id, bld->id);
+ return true;
+ }
+
+ return false;
+}
+
+static void recheck_pile_links(color_ostream &out, EngineInfo *engine)
+{
+ auto removed = engine->stockpiles;
+
+ out.print("rechecking piles in %d\n", engine->id);
+
+ // Detect and save changes in take links
+ for (size_t i = 0; i < engine->links.take_from_pile.size(); i++)
+ {
+ auto pile = engine->links.take_from_pile[i];
+
+ removed.erase(pile->id);
+
+ if (!engine->stockpiles.count(pile->id))
+ addStockpileLink(engine->bld, (df::building_stockpilest*)pile);
+ }
+
+ for (auto it = removed.begin(); it != removed.end(); it++)
+ forgetStockpileLink(engine, *it);
+
+ // Remove give links
+ for (size_t i = 0; i < engine->links.give_to_pile.size(); i++)
+ {
+ auto pile = engine->links.give_to_pile[i];
+ auto plinks = pile->getStockpileLinks();
+ erase_from_vector(plinks->take_from_workshop, &df::building::id, engine->id);
+ }
+
+ engine->links.give_to_pile.clear();
+}
+
static int getShotSkill(df::building_siegeenginest *bld)
{
CHECK_NULL_POINTER(bld);
@@ -1122,6 +1270,25 @@ IMPLEMENT_VMETHOD_INTERPOSE(projectile_hook, checkMovement);
struct building_hook : df::building_siegeenginest {
typedef df::building_siegeenginest interpose_base;
+ DEFINE_VMETHOD_INTERPOSE(bool, canLinkToStockpile, ())
+ {
+ if (find_engine(this, true))
+ return true;
+
+ return INTERPOSE_NEXT(canLinkToStockpile)();
+ }
+
+ DEFINE_VMETHOD_INTERPOSE(df::stockpile_links*, getStockpileLinks, ())
+ {
+ if (auto engine = find_engine(this))
+ {
+ recheck_piles.insert(this);
+ return &engine->links;
+ }
+
+ return INTERPOSE_NEXT(getStockpileLinks)();
+ }
+
DEFINE_VMETHOD_INTERPOSE(void, updateAction, ())
{
INTERPOSE_NEXT(updateAction)();
@@ -1186,6 +1353,8 @@ struct building_hook : df::building_siegeenginest {
}
};
+IMPLEMENT_VMETHOD_INTERPOSE(building_hook, canLinkToStockpile);
+IMPLEMENT_VMETHOD_INTERPOSE(building_hook, getStockpileLinks);
IMPLEMENT_VMETHOD_INTERPOSE(building_hook, updateAction);
/*
@@ -1195,6 +1364,9 @@ IMPLEMENT_VMETHOD_INTERPOSE(building_hook, updateAction);
DFHACK_PLUGIN_LUA_FUNCTIONS {
DFHACK_LUA_FUNCTION(clearTargetArea),
DFHACK_LUA_FUNCTION(setTargetArea),
+ DFHACK_LUA_FUNCTION(isLinkedToPile),
+ DFHACK_LUA_FUNCTION(addStockpileLink),
+ DFHACK_LUA_FUNCTION(removeStockpileLink),
DFHACK_LUA_FUNCTION(getTileStatus),
DFHACK_LUA_FUNCTION(paintAimScreen),
DFHACK_LUA_FUNCTION(canTargetUnit),
@@ -1208,6 +1380,7 @@ DFHACK_PLUGIN_LUA_COMMANDS {
DFHACK_LUA_COMMAND(getTargetArea),
DFHACK_LUA_COMMAND(getAmmoItem),
DFHACK_LUA_COMMAND(setAmmoItem),
+ DFHACK_LUA_COMMAND(getStockpileLinks),
DFHACK_LUA_COMMAND(projPosAtStep),
DFHACK_LUA_COMMAND(projPathMetrics),
DFHACK_LUA_COMMAND(adjustToTarget),
@@ -1224,6 +1397,9 @@ static void enable_hooks(bool enable)
is_enabled = enable;
INTERPOSE_HOOK(projectile_hook, checkMovement).apply(enable);
+
+ INTERPOSE_HOOK(building_hook, canLinkToStockpile).apply(enable);
+ INTERPOSE_HOOK(building_hook, getStockpileLinks).apply(enable);
INTERPOSE_HOOK(building_hook, updateAction).apply(enable);
if (enable)
@@ -1246,7 +1422,7 @@ static bool enable_plugin()
return true;
}
-static void clear_caches()
+static void clear_caches(color_ostream &out)
{
if (!UnitPath::cache.empty())
{
@@ -1255,6 +1431,15 @@ static void clear_caches()
UnitPath::cache.clear();
}
+
+ if (!recheck_piles.empty())
+ {
+ for (auto it = recheck_piles.begin(); it != recheck_piles.end(); ++it)
+ if (auto engine = find_engine(*it))
+ recheck_pile_links(out, engine);
+
+ recheck_piles.clear();
+ }
}
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
@@ -1300,6 +1485,6 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out )
DFhackCExport command_result plugin_onupdate ( color_ostream &out )
{
- clear_caches();
+ clear_caches(out);
return CR_OK;
}