Support setting workshop profile for siege engines.

Since can't use built-in viewscreen, UI limited to skills only.
develop
Alexander Gavrilov 2012-09-13 20:20:56 +04:00
parent 5690a26439
commit 881fed41de
2 changed files with 122 additions and 5 deletions

@ -49,6 +49,7 @@
#include "df/items_other_id.h" #include "df/items_other_id.h"
#include "df/building_stockpilest.h" #include "df/building_stockpilest.h"
#include "df/stockpile_links.h" #include "df/stockpile_links.h"
#include "df/workshop_profile.h"
#include "MiscUtils.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(); static bool enable_plugin();
@ -187,6 +188,7 @@ struct EngineInfo {
std::set<int> stockpiles; std::set<int> stockpiles;
df::stockpile_links links; df::stockpile_links links;
df::workshop_profile profile;
bool hasTarget() { return is_range_valid(target); } bool hasTarget() { return is_range_valid(target); }
bool onTarget(df::coord pos) { return is_in_range(target, pos); } bool onTarget(df::coord pos) { return is_in_range(target, pos); }
@ -268,6 +270,10 @@ static EngineInfo *find_engine(df::coord pos)
return engine; return engine;
} }
/*
* Configuration management
*/
static void clear_engines() static void clear_engines()
{ {
for (auto it = engines.begin(); it != engines.end(); ++it) for (auto it = engines.begin(); it != engines.end(); ++it)
@ -316,6 +322,30 @@ static void load_engines()
engine->stockpiles.insert(it->ival(1)); 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);
}
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));
}
} }
static int getTargetArea(lua_State *L) static int getTargetArea(lua_State *L)
@ -521,6 +551,52 @@ static bool removeStockpileLink(df::building_siegeenginest *bld, df::building_st
return false; return false;
} }
static df::workshop_profile *saveWorkshopProfile(df::building_siegeenginest *bld)
{
CHECK_NULL_POINTER(bld);
if (!enable_plugin())
return NULL;
// 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);
entry.ival(0) = engine->id;
entry.ival(1) = engine->profile.min_level;
entry.ival(2) = engine->profile.max_level;
// 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 = vec.begin(); it != vec.end(); ++it)
{
if (linear_index(workers, it->ival(1)) < 0)
pworld->DeletePersistentData(*it);
}
for (size_t i = 0; i < workers.size(); i++)
{
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];
}
return &engine->profile;
}
static int getOperatorSkill(df::building_siegeenginest *bld, bool force = false) static int getOperatorSkill(df::building_siegeenginest *bld, bool force = false)
{ {
CHECK_NULL_POINTER(bld); CHECK_NULL_POINTER(bld);
@ -552,7 +628,7 @@ static int getOperatorSkill(df::building_siegeenginest *bld, bool force = false)
} }
/* /*
* Trajectory * Trajectory raytracing
*/ */
struct ProjectilePath { struct ProjectilePath {
@ -1363,6 +1439,14 @@ IMPLEMENT_VMETHOD_INTERPOSE(projectile_hook, checkImpact);
struct building_hook : df::building_siegeenginest { struct building_hook : df::building_siegeenginest {
typedef df::building_siegeenginest interpose_base; typedef df::building_siegeenginest interpose_base;
DEFINE_VMETHOD_INTERPOSE(df::workshop_profile*, getWorkshopProfile, ())
{
if (auto engine = find_engine(this))
return &engine->profile;
return INTERPOSE_NEXT(getWorkshopProfile)();
}
DEFINE_VMETHOD_INTERPOSE(df::stockpile_links*, getStockpileLinks, ()) DEFINE_VMETHOD_INTERPOSE(df::stockpile_links*, getStockpileLinks, ())
{ {
if (auto engine = find_engine(this)) if (auto engine = find_engine(this))
@ -1435,6 +1519,7 @@ struct building_hook : df::building_siegeenginest {
} }
}; };
IMPLEMENT_VMETHOD_INTERPOSE(building_hook, getWorkshopProfile);
IMPLEMENT_VMETHOD_INTERPOSE(building_hook, getStockpileLinks); IMPLEMENT_VMETHOD_INTERPOSE(building_hook, getStockpileLinks);
IMPLEMENT_VMETHOD_INTERPOSE(building_hook, updateAction); IMPLEMENT_VMETHOD_INTERPOSE(building_hook, updateAction);
@ -1448,6 +1533,7 @@ DFHACK_PLUGIN_LUA_FUNCTIONS {
DFHACK_LUA_FUNCTION(isLinkedToPile), DFHACK_LUA_FUNCTION(isLinkedToPile),
DFHACK_LUA_FUNCTION(addStockpileLink), DFHACK_LUA_FUNCTION(addStockpileLink),
DFHACK_LUA_FUNCTION(removeStockpileLink), DFHACK_LUA_FUNCTION(removeStockpileLink),
DFHACK_LUA_FUNCTION(saveWorkshopProfile),
DFHACK_LUA_FUNCTION(getTileStatus), DFHACK_LUA_FUNCTION(getTileStatus),
DFHACK_LUA_FUNCTION(paintAimScreen), DFHACK_LUA_FUNCTION(paintAimScreen),
DFHACK_LUA_FUNCTION(canTargetUnit), DFHACK_LUA_FUNCTION(canTargetUnit),
@ -1480,6 +1566,7 @@ static void enable_hooks(bool enable)
INTERPOSE_HOOK(projectile_hook, checkMovement).apply(enable); INTERPOSE_HOOK(projectile_hook, checkMovement).apply(enable);
INTERPOSE_HOOK(projectile_hook, checkImpact).apply(enable); INTERPOSE_HOOK(projectile_hook, checkImpact).apply(enable);
INTERPOSE_HOOK(building_hook, getWorkshopProfile).apply(enable);
INTERPOSE_HOOK(building_hook, getStockpileLinks).apply(enable); INTERPOSE_HOOK(building_hook, getStockpileLinks).apply(enable);
INTERPOSE_HOOK(building_hook, updateAction).apply(enable); INTERPOSE_HOOK(building_hook, updateAction).apply(enable);

@ -8,6 +8,8 @@ local dlg = require 'gui.dialogs'
local plugin = require 'plugins.siege-engine' local plugin = require 'plugins.siege-engine'
local wmap = df.global.world.map local wmap = df.global.world.map
local LEGENDARY = df.skill_rating.Legendary
-- Globals kept between script calls -- Globals kept between script calls
last_target_min = last_target_min or nil last_target_min = last_target_min or nil
last_target_max = last_target_max or nil last_target_max = last_target_max or nil
@ -64,7 +66,10 @@ function SiegeEngine:onShow()
end end
function SiegeEngine:onDestroy() 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) self:selectBuilding(self.building, self.old_cursor, self.old_viewport, 10)
end end
end end
@ -221,12 +226,20 @@ function SiegeEngine:onRenderBody_main(dc)
dc:newline():newline(1) dc:newline():newline(1)
dc:string("t",COLOR_LIGHTGREEN):string(": Take from stockpile"):newline(3) dc:string("t",COLOR_LIGHTGREEN):string(": Take from stockpile"):newline(3)
local links = plugin.getStockpileLinks(self.building) local links = plugin.getStockpileLinks(self.building)
local bottom = dc.height - 5
if links then if links then
dc:string("d",COLOR_LIGHTGREEN):string(": Delete, ") dc:string("d",COLOR_LIGHTGREEN):string(": Delete, ")
dc:string("o",COLOR_LIGHTGREEN):string(": Zoom"):newline() 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 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 if self.target_select_first then
self:renderTargetView(self.target_select_first, guidm.getCursorPos()) self:renderTargetView(self.target_select_first, guidm.getCursorPos())
else else
@ -295,6 +308,23 @@ function SiegeEngine:onInput_main(keys)
self.mode = self.mode_pile self.mode = self.mode_pile
self:sendInputToParent('CURSOR_DOWN_Z') self:sendInputToParent('CURSOR_DOWN_Z')
self:sendInputToParent('CURSOR_UP_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 elseif self:simulateViewScroll(keys) then
self.cursor = nil self.cursor = nil
else else
@ -439,7 +469,7 @@ function SiegeEngine:onInput(keys)
self:dismiss() self:dismiss()
elseif keys.LEAVESCREEN_ALL then elseif keys.LEAVESCREEN_ALL then
self:dismiss() self:dismiss()
self.building = nil self.no_select_building = true
guidm.clearCursorPos() guidm.clearCursorPos()
df.global.ui.main.mode = df.ui_sidebar_mode.Default df.global.ui.main.mode = df.ui_sidebar_mode.Default
df.global.world.selected_building = nil df.global.world.selected_building = nil