develop
Quietust 2012-09-13 14:08:26 -05:00
commit cae01250ff
2 changed files with 139 additions and 74 deletions

@ -49,6 +49,7 @@
#include "df/items_other_id.h"
#include "df/building_stockpilest.h"
#include "df/stockpile_links.h"
#include "df/workshop_profile.h"
#include "MiscUtils.h"
@ -162,7 +163,7 @@ static void random_direction(float &x, float &y, float &z)
}
/*
* Configuration management
* Configuration object
*/
static bool enable_plugin();
@ -187,6 +188,7 @@ struct EngineInfo {
std::set<int> stockpiles;
df::stockpile_links links;
df::workshop_profile profile;
bool hasTarget() { return is_range_valid(target); }
bool onTarget(df::coord pos) { return is_in_range(target, pos); }
@ -200,8 +202,6 @@ 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);
@ -270,13 +270,16 @@ static EngineInfo *find_engine(df::coord pos)
return engine;
}
/*
* Configuration management
*/
static void clear_engines()
{
for (auto it = engines.begin(); it != engines.end(); ++it)
delete it->second;
engines.clear();
coord_engines.clear();
recheck_piles.clear();
}
static void load_engines()
@ -316,14 +319,32 @@ static void load_engines()
pworld->DeletePersistentData(*it);
continue;;
}
auto plinks = pile->getStockpileLinks();
if (!plinks)
continue;
engine->stockpiles.insert(it->ival(1));
}
pworld->GetPersistentData(&vec, "siege-engine/profiles/", true);
for (auto it = vec.begin(); it != vec.end(); ++it)
{
auto engine = find_engine(df::building::find(it->ival(0)), true);
if (!engine) continue;
engine->profile.min_level = it->ival(1);
engine->profile.max_level = it->ival(2);
}
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);
pworld->GetPersistentData(&vec, "siege-engine/profile-workers/", 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 unit = df::unit::find(it->ival(1));
if (!unit || !Units::isCitizen(unit))
{
pworld->DeletePersistentData(*it);
continue;
}
engine->profile.permitted_workers.push_back(it->ival(1));
}
}
@ -437,21 +458,47 @@ static int setAmmoItem(lua_State *L)
return 1;
}
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 void update_stockpile_links(EngineInfo *engine)
{
engine->links.take_from_pile.clear();
for (auto it = engine->stockpiles.begin(); it != engine->stockpiles.end(); )
{
int id = *it; ++it;
auto pile = df::building::find(id);
if (!pile || pile->getType() != building_type::Stockpile)
forgetStockpileLink(engine, id);
else
// The vector is sorted, but we are iterating through a sorted set
engine->links.take_from_pile.push_back(pile);
}
}
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);
update_stockpile_links(engine);
auto &links = engine->links.take_from_pile;
lua_createtable(L, links.size(), 0);
for (auto it = engine->stockpiles.begin(); it != engine->stockpiles.end(); ++it)
for (size_t i = 0; i < links.size(); i++)
{
auto pile = df::building::find(*it);
if (!pile) continue;
Lua::Push(L, pile);
lua_rawseti(L, -2, idx++);
Lua::Push(L, links[i]);
lua_rawseti(L, -2, i+1);
}
return 1;
@ -472,9 +519,6 @@ static bool addStockpileLink(df::building_siegeenginest *bld, df::building_stock
CHECK_NULL_POINTER(bld);
CHECK_NULL_POINTER(pile);
auto plinks = pile->getStockpileLinks();
CHECK_NULL_POINTER(plinks);
if (!enable_plugin())
return false;
@ -490,21 +534,9 @@ static bool addStockpileLink(df::building_siegeenginest *bld, df::building_stock
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);
@ -513,45 +545,56 @@ static bool removeStockpileLink(df::building_siegeenginest *bld, df::building_st
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)
static df::workshop_profile *saveWorkshopProfile(df::building_siegeenginest *bld)
{
auto removed = engine->stockpiles;
CHECK_NULL_POINTER(bld);
out.print("rechecking piles in %d\n", engine->id);
if (!enable_plugin())
return NULL;
// 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];
// Save skill limits
auto pworld = Core::getInstance().getWorld();
auto key = stl_sprintf("siege-engine/profiles/%d", bld->id);
auto entry = pworld->GetPersistentData(key, NULL);
if (!entry.isValid())
return NULL;
auto engine = find_engine(bld, true);
removed.erase(pile->id);
entry.ival(0) = engine->id;
entry.ival(1) = engine->profile.min_level;
entry.ival(2) = engine->profile.max_level;
if (!engine->stockpiles.count(pile->id))
addStockpileLink(engine->bld, (df::building_stockpilest*)pile);
}
// Save worker list
std::vector<PersistentDataItem> vec;
auto &workers = engine->profile.permitted_workers;
key = stl_sprintf("siege-engine/profile-workers/%d", bld->id);
pworld->GetPersistentData(&vec, key, true);
for (auto it = removed.begin(); it != removed.end(); it++)
forgetStockpileLink(engine, *it);
for (auto it = vec.begin(); it != vec.end(); ++it)
{
if (linear_index(workers, it->ival(1)) < 0)
pworld->DeletePersistentData(*it);
}
// Remove give links
for (size_t i = 0; i < engine->links.give_to_pile.size(); i++)
for (size_t i = 0; i < workers.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);
key = stl_sprintf("siege-engine/profile-workers/%d/%d", bld->id, workers[i]);
entry = pworld->GetPersistentData(key, NULL);
if (!entry.isValid())
continue;
entry.ival(0) = engine->id;
entry.ival(1) = workers[i];
}
engine->links.give_to_pile.clear();
return &engine->profile;
}
static int getOperatorSkill(df::building_siegeenginest *bld, bool force = false)
@ -585,7 +628,7 @@ static int getOperatorSkill(df::building_siegeenginest *bld, bool force = false)
}
/*
* Trajectory
* Trajectory raytracing
*/
struct ProjectilePath {
@ -1396,19 +1439,19 @@ IMPLEMENT_VMETHOD_INTERPOSE(projectile_hook, checkImpact);
struct building_hook : df::building_siegeenginest {
typedef df::building_siegeenginest interpose_base;
DEFINE_VMETHOD_INTERPOSE(bool, canLinkToStockpile, ())
DEFINE_VMETHOD_INTERPOSE(df::workshop_profile*, getWorkshopProfile, ())
{
if (find_engine(this, true))
return true;
if (auto engine = find_engine(this))
return &engine->profile;
return INTERPOSE_NEXT(canLinkToStockpile)();
return INTERPOSE_NEXT(getWorkshopProfile)();
}
DEFINE_VMETHOD_INTERPOSE(df::stockpile_links*, getStockpileLinks, ())
{
if (auto engine = find_engine(this))
{
recheck_piles.insert(this);
update_stockpile_links(engine);
return &engine->links;
}
@ -1476,7 +1519,7 @@ struct building_hook : df::building_siegeenginest {
}
};
IMPLEMENT_VMETHOD_INTERPOSE(building_hook, canLinkToStockpile);
IMPLEMENT_VMETHOD_INTERPOSE(building_hook, getWorkshopProfile);
IMPLEMENT_VMETHOD_INTERPOSE(building_hook, getStockpileLinks);
IMPLEMENT_VMETHOD_INTERPOSE(building_hook, updateAction);
@ -1490,6 +1533,7 @@ DFHACK_PLUGIN_LUA_FUNCTIONS {
DFHACK_LUA_FUNCTION(isLinkedToPile),
DFHACK_LUA_FUNCTION(addStockpileLink),
DFHACK_LUA_FUNCTION(removeStockpileLink),
DFHACK_LUA_FUNCTION(saveWorkshopProfile),
DFHACK_LUA_FUNCTION(getTileStatus),
DFHACK_LUA_FUNCTION(paintAimScreen),
DFHACK_LUA_FUNCTION(canTargetUnit),
@ -1522,7 +1566,7 @@ static void enable_hooks(bool enable)
INTERPOSE_HOOK(projectile_hook, checkMovement).apply(enable);
INTERPOSE_HOOK(projectile_hook, checkImpact).apply(enable);
INTERPOSE_HOOK(building_hook, canLinkToStockpile).apply(enable);
INTERPOSE_HOOK(building_hook, getWorkshopProfile).apply(enable);
INTERPOSE_HOOK(building_hook, getStockpileLinks).apply(enable);
INTERPOSE_HOOK(building_hook, updateAction).apply(enable);
@ -1555,15 +1599,6 @@ static void clear_caches(color_ostream &out)
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)

@ -8,6 +8,8 @@ local dlg = require 'gui.dialogs'
local plugin = require 'plugins.siege-engine'
local wmap = df.global.world.map
local LEGENDARY = df.skill_rating.Legendary
-- Globals kept between script calls
last_target_min = last_target_min or nil
last_target_max = last_target_max or nil
@ -64,7 +66,10 @@ function SiegeEngine:onShow()
end
function SiegeEngine:onDestroy()
if self.building then
if self.save_profile then
plugin.saveWorkshopProfile(self.building)
end
if not self.no_select_building then
self:selectBuilding(self.building, self.old_cursor, self.old_viewport, 10)
end
end
@ -221,12 +226,20 @@ function SiegeEngine:onRenderBody_main(dc)
dc:newline():newline(1)
dc:string("t",COLOR_LIGHTGREEN):string(": Take from stockpile"):newline(3)
local links = plugin.getStockpileLinks(self.building)
local bottom = dc.height - 5
if links then
dc:string("d",COLOR_LIGHTGREEN):string(": Delete, ")
dc:string("o",COLOR_LIGHTGREEN):string(": Zoom"):newline()
self:renderStockpiles(dc, links, 19-dc:localY())
self:renderStockpiles(dc, links, bottom-2-dc:localY())
dc:newline():newline()
end
local prof = self.building:getWorkshopProfile() or {}
dc:seek(1,math.max(dc:localY(),19)):string('ghjk',COLOR_LIGHTGREEN)dc:string(': ')
dc:string(df.skill_rating.attrs[prof.min_level or 0].caption):string('-')
dc:string(df.skill_rating.attrs[math.min(LEGENDARY,prof.max_level or 3000)].caption)
dc:newline():newline()
if self.target_select_first then
self:renderTargetView(self.target_select_first, guidm.getCursorPos())
else
@ -295,6 +308,23 @@ function SiegeEngine:onInput_main(keys)
self.mode = self.mode_pile
self:sendInputToParent('CURSOR_DOWN_Z')
self:sendInputToParent('CURSOR_UP_Z')
elseif keys.CUSTOM_G then
local prof = plugin.saveWorkshopProfile(self.building)
prof.min_level = math.max(0, prof.min_level-1)
plugin.saveWorkshopProfile(self.building)
elseif keys.CUSTOM_H then
local prof = plugin.saveWorkshopProfile(self.building)
prof.min_level = math.min(LEGENDARY, prof.min_level+1)
plugin.saveWorkshopProfile(self.building)
elseif keys.CUSTOM_J then
local prof = plugin.saveWorkshopProfile(self.building)
prof.max_level = math.max(0, math.min(LEGENDARY,prof.max_level)-1)
plugin.saveWorkshopProfile(self.building)
elseif keys.CUSTOM_K then
local prof = plugin.saveWorkshopProfile(self.building)
prof.max_level = math.min(LEGENDARY, prof.max_level+1)
if prof.max_level >= LEGENDARY then prof.max_level = 3000 end
plugin.saveWorkshopProfile(self.building)
elseif self:simulateViewScroll(keys) then
self.cursor = nil
else
@ -439,7 +469,7 @@ function SiegeEngine:onInput(keys)
self:dismiss()
elseif keys.LEAVESCREEN_ALL then
self:dismiss()
self.building = nil
self.no_select_building = true
guidm.clearCursorPos()
df.global.ui.main.mode = df.ui_sidebar_mode.Default
df.global.world.selected_building = nil