summaryrefslogtreecommitdiff
path: root/plugins/devel
diff options
context:
space:
mode:
authorAlexander Gavrilov2012-09-09 20:54:12 +0400
committerAlexander Gavrilov2012-09-09 20:54:12 +0400
commit8ab615f6d0dce167a1952c4684922a7e48263c23 (patch)
tree97bef262abbc6aa4f4c4efc9ffba9d8858dd830a /plugins/devel
parent9679b7729c79888d8bdac99484dfad7a5e07d13e (diff)
downloaddfhack-8ab615f6d0dce167a1952c4684922a7e48263c23.tar.gz
dfhack-8ab615f6d0dce167a1952c4684922a7e48263c23.tar.bz2
dfhack-8ab615f6d0dce167a1952c4684922a7e48263c23.tar.xz
Implement unit path prediction in siege engine.
Diffstat (limited to 'plugins/devel')
-rw-r--r--plugins/devel/siege-engine.cpp317
1 files changed, 316 insertions, 1 deletions
diff --git a/plugins/devel/siege-engine.cpp b/plugins/devel/siege-engine.cpp
index 4c6cebaa..c36e9fb3 100644
--- a/plugins/devel/siege-engine.cpp
+++ b/plugins/devel/siege-engine.cpp
@@ -124,6 +124,11 @@ static int random_int(int val)
return int(int64_t(rand())*val/RAND_MAX);
}
+static int point_distance(df::coord speed)
+{
+ return std::max(abs(speed.x), std::max(abs(speed.y), abs(speed.z)));
+}
+
/*
* Configuration management
*/
@@ -134,6 +139,7 @@ struct EngineInfo {
int id;
coord_range target;
df::coord center;
+ int proj_speed, hit_delay;
bool hasTarget() { return is_range_valid(target); }
bool onTarget(df::coord pos) { return is_in_range(target, pos); }
@@ -155,8 +161,11 @@ static EngineInfo *find_engine(df::building *bld, bool create = false)
return NULL;
auto *obj = &engines[bld];
+
obj->id = bld->id;
obj->center = df::coord(bld->centerx, bld->centery, bld->z);
+ obj->proj_speed = 2;
+ obj->hit_delay = 3;
coord_engines[obj->center] = bld;
return obj;
@@ -285,7 +294,7 @@ struct ProjectilePath {
void calc_line()
{
speed = target - origin;
- divisor = std::max(abs(speed.x), std::max(abs(speed.y), abs(speed.z)));
+ divisor = point_distance(speed);
if (divisor <= 0) divisor = 1;
direction = df::coord(speed.x>=0?1:-1,speed.y>=0?1:-1,speed.z>=0?1:-1);
}
@@ -504,6 +513,292 @@ static void paintAimScreen(df::building_siegeenginest *bld, df::coord view, df::
* Unit tracking
*/
+static const float MAX_TIME = 1000000.0f;
+
+struct UnitPath {
+ df::unit *unit;
+ std::map<float, df::coord> path;
+
+ struct Hit {
+ UnitPath *path;
+ df::coord pos;
+ int dist;
+ float time, lmargin, rmargin;
+ };
+
+ static std::map<df::unit*, UnitPath*> cache;
+
+ static UnitPath *get(df::unit *unit)
+ {
+ auto &cv = cache[unit];
+ if (!cv) cv = new UnitPath(unit);
+ return cv;
+ };
+
+ UnitPath(df::unit *unit) : unit(unit)
+ {
+ df::coord pos = unit->pos;
+ df::coord dest = unit->path.dest;
+ auto &upath = unit->path.path;
+
+ if (dest.isValid() && !upath.x.empty())
+ {
+ float time = unit->counters.job_counter+0.5f;
+ float speed = Units::computeMovementSpeed(unit)/100.0f;
+
+ for (size_t i = 0; i < upath.size(); i++)
+ {
+ df::coord new_pos = upath[i];
+ if (new_pos == pos)
+ continue;
+
+ float delay = speed;
+ if (new_pos.x != pos.x && new_pos.y != pos.y)
+ delay *= 362.0/256.0;
+
+ path[time] = pos;
+ pos = new_pos;
+ time += delay + 1;
+ }
+ }
+
+ path[MAX_TIME] = pos;
+ }
+
+ void get_margin(std::map<float,df::coord>::iterator &it, float time, float *lmargin, float *rmargin)
+ {
+ auto it2 = it;
+ *lmargin = (it == path.begin()) ? MAX_TIME : time - (--it2)->first;
+ *rmargin = (it->first == MAX_TIME) ? MAX_TIME : it->first - time;
+ }
+
+ df::coord posAtTime(float time, float *lmargin = NULL, float *rmargin = NULL)
+ {
+ CHECK_INVALID_ARGUMENT(time < MAX_TIME);
+
+ auto it = path.upper_bound(time);
+ if (lmargin)
+ get_margin(it, time, lmargin, rmargin);
+ return it->second;
+ }
+
+ bool findHits(EngineInfo *engine, std::vector<Hit> *hit_points, float bias)
+ {
+ df::coord origin = engine->center;
+
+ Hit info;
+ info.path = this;
+
+ for (auto it = path.begin(); it != path.end(); ++it)
+ {
+ info.pos = it->second;
+ info.dist = point_distance(origin - info.pos);
+ info.time = float(info.dist)*(engine->proj_speed+1) + engine->hit_delay + bias;
+ get_margin(it, info.time, &info.lmargin, &info.rmargin);
+
+ if (info.lmargin > 0 && info.rmargin > 0)
+ {
+ if (engine->onTarget(info.pos))
+ hit_points->push_back(info);
+ }
+ }
+
+ return !hit_points->empty();
+ }
+};
+
+std::map<df::unit*, UnitPath*> UnitPath::cache;
+
+static void push_margin(lua_State *L, float margin)
+{
+ if (margin == MAX_TIME)
+ lua_pushnil(L);
+ else
+ lua_pushnumber(L, margin);
+}
+
+static int traceUnitPath(lua_State *L)
+{
+ auto unit = Lua::CheckDFObject<df::unit>(L, 1);
+
+ CHECK_NULL_POINTER(unit);
+
+ size_t idx = 1;
+ auto info = UnitPath::get(unit);
+ lua_createtable(L, info->path.size(), 0);
+
+ float last_time = 0.0f;
+ for (auto it = info->path.begin(); it != info->path.end(); ++it)
+ {
+ Lua::Push(L, it->second);
+ if (idx > 1)
+ {
+ lua_pushnumber(L, last_time);
+ lua_setfield(L, -2, "from");
+ }
+ if (idx < info->path.size())
+ {
+ lua_pushnumber(L, it->first);
+ lua_setfield(L, -2, "to");
+ }
+ lua_rawseti(L, -2, idx++);
+ last_time = it->first;
+ }
+
+ return 1;
+}
+
+static int unitPosAtTime(lua_State *L)
+{
+ auto unit = Lua::CheckDFObject<df::unit>(L, 1);
+ float time = luaL_checknumber(L, 2);
+
+ CHECK_NULL_POINTER(unit);
+
+ float lmargin, rmargin;
+ auto info = UnitPath::get(unit);
+
+ Lua::Push(L, info->posAtTime(time, &lmargin, &rmargin));
+ push_margin(L, lmargin);
+ push_margin(L, rmargin);
+ return 3;
+}
+
+static void proposeUnitHits(EngineInfo *engine, std::vector<UnitPath::Hit> *hits, float bias)
+{
+ auto &active = world->units.active;
+
+ for (size_t i = 0; i < active.size(); i++)
+ {
+ auto unit = active[i];
+
+ if (unit->flags1.bits.dead ||
+ unit->flags3.bits.ghostly ||
+ unit->flags1.bits.caged ||
+ unit->flags1.bits.chained ||
+ unit->flags1.bits.rider ||
+ unit->flags1.bits.hidden_in_ambush)
+ continue;
+
+ UnitPath::get(unit)->findHits(engine, hits, bias);
+ }
+}
+
+static int proposeUnitHits(lua_State *L)
+{
+ auto bld = Lua::CheckDFObject<df::building_siegeenginest>(L, 1);
+ float bias = luaL_optnumber(L, 2, 0);
+
+ auto engine = find_engine(bld);
+ if (!engine)
+ luaL_error(L, "no such engine");
+ if (!engine->hasTarget())
+ luaL_error(L, "target not set");
+
+ std::vector<UnitPath::Hit> hits;
+ proposeUnitHits(engine, &hits, bias);
+
+ lua_createtable(L, hits.size(), 0);
+
+ for (size_t i = 0; i < hits.size(); i++)
+ {
+ auto &hit = hits[i];
+ lua_createtable(L, 0, 6);
+ Lua::PushDFObject(L, hit.path->unit); lua_setfield(L, -2, "unit");
+ Lua::Push(L, hit.pos); lua_setfield(L, -2, "pos");
+ lua_pushnumber(L, hit.dist); lua_setfield(L, -2, "dist");
+ lua_pushnumber(L, hit.time); lua_setfield(L, -2, "time");
+ push_margin(L, hit.lmargin); lua_setfield(L, -2, "lmargin");
+ push_margin(L, hit.rmargin); lua_setfield(L, -2, "rmargin");
+ lua_rawseti(L, -2, i+1);
+ }
+
+ return 1;
+}
+
+#if 0
+struct UnitContext {
+ AimContext &ctx;
+
+ struct UnitInfo {
+ df::unit *unit;
+
+ UnitPath path;
+ float score;
+
+ UnitInfo(df::unit *unit) : unit(unit), path(unit) {}
+ };
+
+ std::map<df::unit*, UnitInfo*> units;
+
+ UnitContext(AimContext &ctx) : ctx(ctx) {}
+
+ ~UnitContext()
+ {
+ for (auto it = units.begin(); it != units.end(); ++it)
+ delete it->second;
+ }
+
+ float unit_score(df::unit *unit)
+ {
+ float score = 1.0f;
+
+ if (unit->flags1.bits.tame && unit->civ_id == ui->civ_id)
+ score = -1.0f;
+ if (unit->flags1.bits.diplomat || unit->flags1.bits.merchant)
+ score = -2.0f;
+ else if (Units::isCitizen(unit))
+ score = -10.0f;
+ else
+ {
+ if (unit->flags1.bits.marauder)
+ score += 0.5f;
+ if (unit->flags1.bits.active_invader)
+ score += 1.0f;
+ if (unit->flags1.bits.invader_origin)
+ score += 1.0f;
+ if (unit->flags1.bits.invades)
+ score += 1.0f;
+ if (unit->flags1.bits.hidden_ambusher)
+ score += 1.0f;
+ }
+
+ if (unit->flags1.bits.ridden)
+ {
+ for (size_t i = 0; i < unit->refs.size(); i++)
+ {
+ if (!unit->refs[i]->getType() == general_ref_type::UNIT_RIDER)
+ continue;
+ if (auto rider = unit->refs[i]->getUnit())
+ score += unit_score(rider);
+ }
+ }
+ }
+
+ void select_units()
+ {
+ auto &active = world->units.active;
+
+ for (size_t i = 0; i < active.size(); i++)
+ {
+ auto unit = active[i];
+ if (unit->flags1.bits.dead ||
+ unit->flags3.bits.ghostly ||
+ unit->flags1.bits.caged ||
+ unit->flags1.bits.chained ||
+ unit->flags1.bits.rider ||
+ unit->flags1.bits.hidden_in_ambush)
+ continue;
+
+ auto info = units[unit] = new UnitInfo(unit);
+
+ info->findHits(ctx, ctx.proj_hit_delay);
+ info->score = unit_score(unit);
+ }
+ }
+};
+#endif
+
/*
* Projectile hook
*/
@@ -617,6 +912,9 @@ DFHACK_PLUGIN_LUA_FUNCTIONS {
DFHACK_PLUGIN_LUA_COMMANDS {
DFHACK_LUA_COMMAND(getTargetArea),
+ DFHACK_LUA_COMMAND(traceUnitPath),
+ DFHACK_LUA_COMMAND(unitPosAtTime),
+ DFHACK_LUA_COMMAND(proposeUnitHits),
DFHACK_LUA_END
};
@@ -648,6 +946,17 @@ static bool enable_plugin()
return true;
}
+static void clear_caches()
+{
+ if (!UnitPath::cache.empty())
+ {
+ for (auto it = UnitPath::cache.begin(); it != UnitPath::cache.end(); ++it)
+ delete it->second;
+
+ UnitPath::cache.clear();
+ }
+}
+
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
{
switch (event) {
@@ -688,3 +997,9 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out )
enable_hooks(false);
return CR_OK;
}
+
+DFhackCExport command_result plugin_onupdate ( color_ostream &out )
+{
+ clear_caches();
+ return CR_OK;
+}