Use the new API to produce combat reports for aimed siege engine attacks.

This requires exposing the actual operator unit to lua code.
develop
Alexander Gavrilov 2014-04-14 21:57:39 +04:00
parent ee4e3a6168
commit 55cea36c76
4 changed files with 91 additions and 14 deletions

@ -3,6 +3,7 @@ DFHack future
Internals: Internals:
- support for calling a lua function via a protobuf request (demonstrated by dfhack-run --lua). - support for calling a lua function via a protobuf request (demonstrated by dfhack-run --lua).
- Lua API for listing files in directory. Needed for mod-manager. - Lua API for listing files in directory. Needed for mod-manager.
- Lua API for creating unit combat reports and writing to gamelog.
- support for multiple raw/init.d/*.lua init scripts in one save. - support for multiple raw/init.d/*.lua init scripts in one save.
New scripts: New scripts:
@ -17,7 +18,8 @@ DFHack future
Misc improvements: Misc improvements:
- digfort: improved csv parsing, add start() comment handling - digfort: improved csv parsing, add start() comment handling
- exterminate: allow specifying a caste (exterminate gob:male) - exterminate: allow specifying a caste (exterminate gob:male)
- siege-engine: engine quality and distance to target now affect accuracy. - siege-engine: engine quality and distance to target now affect accuracy;
firing the siege engine produces a combat report.
- createitem: in adventure mode it now defaults to the controlled unit as maker. - createitem: in adventure mode it now defaults to the controlled unit as maker.
DFHack v0.34.11-r4 DFHack v0.34.11-r4

@ -457,6 +457,14 @@ function getBuildingCenter(building)
return xyz2pos(building.centerx, building.centery, building.z) return xyz2pos(building.centerx, building.centery, building.z)
end end
function getItemDescription(item,mode)
return call_with_string(item, 'getItemDescription', mode or 0)
end
function getItemDescriptionPrefix(item,mode)
return call_with_string(item, 'getItemDescriptionPrefix', mode or 0)
end
-- Split the string by the given delimiter -- Split the string by the given delimiter
function split_string(self, delimiter) function split_string(self, delimiter)
local result = { } local result = { }

@ -54,9 +54,15 @@ local _ENV = mkmodule('plugins.siege-engine')
]] ]]
local utils = require 'utils'
Z_STEP_COUNT = 15 Z_STEP_COUNT = 15
Z_STEP = 1/31 Z_STEP = 1/31
ANNOUNCEMENT_FLAGS = {
UNIT_COMBAT_REPORT = true
}
function getMetrics(engine, path) function getMetrics(engine, path)
path.metrics = path.metrics or projPathMetrics(engine, path) path.metrics = path.metrics or projPathMetrics(engine, path)
return path.metrics return path.metrics
@ -208,8 +214,61 @@ function pickUniqueTargets(reachable)
return unique return unique
end end
function doAimProjectile(engine, item, target_min, target_max, skill) function describeUnit(unit)
print(item, df.skill_rating[skill]) local desc = dfhack.units.getProfessionName(unit)
local name = dfhack.units.getVisibleName(unit)
if name.has_name then
return desc .. ' ' .. dfhack.TranslateName(name)
end
return desc
end
function produceCombatReport(operator, item, target)
local msg = describeUnit(operator) .. ' launches ' ..
utils.getItemDescriptionPrefix(item) ..
utils.getItemDescription(item) ..
' at '
local pos = operator.pos
if target then
local units = target.units
for i,v in ipairs(units) do
if i > 1 then
if i < #units then
msg = msg .. ', '
elseif i > 2 then
msg = msg .. ', and '
else
msg = msg .. ' and '
end
end
msg = msg .. describeUnit(v)
end
msg = msg .. '!'
pos = target.pos
else
msg = msg .. 'the target area.'
end
local id = dfhack.gui.makeAnnouncement(
df.announcement_type.COMBAT_STRIKE_DETAILS,
ANNOUNCEMENT_FLAGS, pos, msg, COLOR_CYAN, true
)
dfhack.gui.addCombatReport(operator, df.unit_report_type.Hunting, id)
if target then
for i,v in ipairs(target.units) do
dfhack.gui.addCombatReportAuto(v, ANNOUNCEMENT_FLAGS, id)
end
end
end
function doAimProjectile(engine, item, target_min, target_max, operator, skill)
--print(item, df.skill_rating[skill])
local targets = proposeUnitHits(engine) local targets = proposeUnitHits(engine)
local reachable = findReachableTargets(engine, targets) local reachable = findReachableTargets(engine, targets)
@ -219,11 +278,15 @@ function doAimProjectile(engine, item, target_min, target_max, skill)
if #unique > 0 then if #unique > 0 then
local cnt = math.max(math.min(#unique,5), math.min(10, math.floor(#unique/2))) local cnt = math.max(math.min(#unique,5), math.min(10, math.floor(#unique/2)))
local rnd = math.random(cnt) local rnd = math.random(cnt)
for _,u in ipairs(unique[rnd].units) do local target = unique[rnd]
for _,u in ipairs(target.units) do
saveRecent(u) saveRecent(u)
end end
return unique[rnd].path produceCombatReport(operator, item, target)
return target.path
end end
produceCombatReport(operator, item, nil)
end end
return _ENV return _ENV

@ -757,20 +757,20 @@ static df::workshop_profile *saveWorkshopProfile(df::building_siegeenginest *bld
return &engine->profile; return &engine->profile;
} }
static int getOperatorSkill(df::building_siegeenginest *bld, bool force = false) static df::unit *getOperatorUnit(df::building_siegeenginest *bld, bool force = false)
{ {
CHECK_NULL_POINTER(bld); CHECK_NULL_POINTER(bld);
auto engine = find_engine(bld); auto engine = find_engine(bld);
if (!engine) if (!engine)
return 0; return NULL;
if (engine->operator_id != -1 && if (engine->operator_id != -1 &&
(world->frame_counter - engine->operator_frame) <= 5) (world->frame_counter - engine->operator_frame) <= 5)
{ {
auto op_unit = df::unit::find(engine->operator_id); auto op_unit = df::unit::find(engine->operator_id);
if (op_unit) if (op_unit)
return Units::getEffectiveSkill(op_unit, job_skill::SIEGEOPERATE); return op_unit;
} }
if (force) if (force)
@ -781,10 +781,10 @@ static int getOperatorSkill(df::building_siegeenginest *bld, bool force = false)
auto &active = world->units.active; auto &active = world->units.active;
for (size_t i = 0; i < active.size(); i++) for (size_t i = 0; i < active.size(); i++)
if (active[i]->pos == engine->center && Units::isCitizen(active[i])) if (active[i]->pos == engine->center && Units::isCitizen(active[i]))
return Units::getEffectiveSkill(active[i], job_skill::SIEGEOPERATE); return active[i];
} }
return 0; return NULL;
} }
/* /*
@ -1578,7 +1578,8 @@ struct projectile_hook : df::proj_itemst {
color_ostream &out = *Lua::GetOutput(L); color_ostream &out = *Lua::GetOutput(L);
auto proj = (projectile_hook*)lua_touserdata(L, 1); auto proj = (projectile_hook*)lua_touserdata(L, 1);
auto engine = (EngineInfo*)lua_touserdata(L, 2); auto engine = (EngineInfo*)lua_touserdata(L, 2);
int skill = lua_tointeger(L, 3); auto unit = (df::unit*)lua_touserdata(L, 3);
int skill = lua_tointeger(L, 4);
if (!Lua::PushModulePublic(out, L, "plugins.siege-engine", "doAimProjectile")) if (!Lua::PushModulePublic(out, L, "plugins.siege-engine", "doAimProjectile"))
luaL_error(L, "Projectile aiming AI not available"); luaL_error(L, "Projectile aiming AI not available");
@ -1587,9 +1588,10 @@ struct projectile_hook : df::proj_itemst {
Lua::Push(L, proj->item); Lua::Push(L, proj->item);
Lua::Push(L, engine->target.first); Lua::Push(L, engine->target.first);
Lua::Push(L, engine->target.second); Lua::Push(L, engine->target.second);
Lua::PushDFObject(L, unit);
Lua::Push(L, skill); Lua::Push(L, skill);
lua_call(L, 5, 1); lua_call(L, 6, 1);
if (lua_isnil(L, -1)) if (lua_isnil(L, -1))
proj->aimAtArea(engine, skill); proj->aimAtArea(engine, skill);
@ -1613,7 +1615,8 @@ struct projectile_hook : df::proj_itemst {
CoreSuspendClaimer suspend; CoreSuspendClaimer suspend;
color_ostream_proxy out(Core::getInstance().getConsole()); color_ostream_proxy out(Core::getInstance().getConsole());
int skill = getOperatorSkill(engine->bld, true); df::unit *op_unit = getOperatorUnit(engine->bld, true);
int skill = op_unit ? Units::getEffectiveSkill(op_unit, job_skill::SIEGEOPERATE) : 0;
// Dabbling can't aim // Dabbling can't aim
if (skill < skill_rating::Novice) if (skill < skill_rating::Novice)
@ -1623,9 +1626,10 @@ struct projectile_hook : df::proj_itemst {
lua_pushcfunction(L, safeAimProjectile); lua_pushcfunction(L, safeAimProjectile);
lua_pushlightuserdata(L, this); lua_pushlightuserdata(L, this);
lua_pushlightuserdata(L, engine); lua_pushlightuserdata(L, engine);
lua_pushlightuserdata(L, op_unit);
lua_pushinteger(L, skill); lua_pushinteger(L, skill);
if (!Lua::Core::SafeCall(out, 3, 0)) if (!Lua::Core::SafeCall(out, 4, 0))
aimAtArea(engine, skill); aimAtArea(engine, skill);
} }