From 55cea36c76fbbb8b67e4999930e001db4cb17d0e Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 14 Apr 2014 21:57:39 +0400 Subject: [PATCH] Use the new API to produce combat reports for aimed siege engine attacks. This requires exposing the actual operator unit to lua code. --- NEWS | 4 +- library/lua/utils.lua | 8 ++++ plugins/lua/siege-engine.lua | 71 ++++++++++++++++++++++++++++++++++-- plugins/siege-engine.cpp | 22 ++++++----- 4 files changed, 91 insertions(+), 14 deletions(-) diff --git a/NEWS b/NEWS index a2626513a..d017e172c 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,7 @@ DFHack future Internals: - 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 creating unit combat reports and writing to gamelog. - support for multiple raw/init.d/*.lua init scripts in one save. New scripts: @@ -17,7 +18,8 @@ DFHack future Misc improvements: - digfort: improved csv parsing, add start() comment handling - 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. DFHack v0.34.11-r4 diff --git a/library/lua/utils.lua b/library/lua/utils.lua index 6d6b4de5c..4a7a8d510 100644 --- a/library/lua/utils.lua +++ b/library/lua/utils.lua @@ -457,6 +457,14 @@ function getBuildingCenter(building) return xyz2pos(building.centerx, building.centery, building.z) 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 function split_string(self, delimiter) local result = { } diff --git a/plugins/lua/siege-engine.lua b/plugins/lua/siege-engine.lua index 33e120feb..7da7ca710 100644 --- a/plugins/lua/siege-engine.lua +++ b/plugins/lua/siege-engine.lua @@ -54,9 +54,15 @@ local _ENV = mkmodule('plugins.siege-engine') ]] +local utils = require 'utils' + Z_STEP_COUNT = 15 Z_STEP = 1/31 +ANNOUNCEMENT_FLAGS = { + UNIT_COMBAT_REPORT = true +} + function getMetrics(engine, path) path.metrics = path.metrics or projPathMetrics(engine, path) return path.metrics @@ -208,8 +214,61 @@ function pickUniqueTargets(reachable) return unique end -function doAimProjectile(engine, item, target_min, target_max, skill) - print(item, df.skill_rating[skill]) +function describeUnit(unit) + 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 reachable = findReachableTargets(engine, targets) @@ -219,11 +278,15 @@ function doAimProjectile(engine, item, target_min, target_max, skill) if #unique > 0 then local cnt = math.max(math.min(#unique,5), math.min(10, math.floor(#unique/2))) 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) end - return unique[rnd].path + produceCombatReport(operator, item, target) + return target.path end + + produceCombatReport(operator, item, nil) end return _ENV \ No newline at end of file diff --git a/plugins/siege-engine.cpp b/plugins/siege-engine.cpp index 4e9550584..7d2d6415d 100644 --- a/plugins/siege-engine.cpp +++ b/plugins/siege-engine.cpp @@ -757,20 +757,20 @@ static df::workshop_profile *saveWorkshopProfile(df::building_siegeenginest *bld 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); auto engine = find_engine(bld); if (!engine) - return 0; + return NULL; if (engine->operator_id != -1 && (world->frame_counter - engine->operator_frame) <= 5) { auto op_unit = df::unit::find(engine->operator_id); if (op_unit) - return Units::getEffectiveSkill(op_unit, job_skill::SIEGEOPERATE); + return op_unit; } if (force) @@ -781,10 +781,10 @@ static int getOperatorSkill(df::building_siegeenginest *bld, bool force = false) auto &active = world->units.active; for (size_t i = 0; i < active.size(); 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); auto proj = (projectile_hook*)lua_touserdata(L, 1); 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")) 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, engine->target.first); Lua::Push(L, engine->target.second); + Lua::PushDFObject(L, unit); Lua::Push(L, skill); - lua_call(L, 5, 1); + lua_call(L, 6, 1); if (lua_isnil(L, -1)) proj->aimAtArea(engine, skill); @@ -1613,7 +1615,8 @@ struct projectile_hook : df::proj_itemst { CoreSuspendClaimer suspend; 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 if (skill < skill_rating::Novice) @@ -1623,9 +1626,10 @@ struct projectile_hook : df::proj_itemst { lua_pushcfunction(L, safeAimProjectile); lua_pushlightuserdata(L, this); lua_pushlightuserdata(L, engine); + lua_pushlightuserdata(L, op_unit); lua_pushinteger(L, skill); - if (!Lua::Core::SafeCall(out, 3, 0)) + if (!Lua::Core::SafeCall(out, 4, 0)) aimAtArea(engine, skill); }