From 000e3baf27e3d811e673bca08e4381c1f2c632b7 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 14 Sep 2012 20:57:03 +0400 Subject: [PATCH] Implement skill-based miss probability in siege engine. --- plugins/devel/siege-engine.cpp | 83 +++++++++++++++++++++++++++++----- 1 file changed, 72 insertions(+), 11 deletions(-) diff --git a/plugins/devel/siege-engine.cpp b/plugins/devel/siege-engine.cpp index d1e34ced9..b8a2f087b 100644 --- a/plugins/devel/siege-engine.cpp +++ b/plugins/devel/siege-engine.cpp @@ -1286,6 +1286,12 @@ static int proposeUnitHits(lua_State *L) * Projectile hook */ +static const int offsets[8][2] = { + { -1, -1 }, { 0, -1 }, { 1, -1 }, + { -1, 0 }, { 1, 0 }, + { -1, 1 }, { 0, 1 }, { 1, 1 } +}; + struct projectile_hook : df::proj_itemst { typedef df::proj_itemst interpose_base; @@ -1293,6 +1299,9 @@ struct projectile_hook : df::proj_itemst { { target_pos = path.target; + // Debug + Maps::getTileOccupancy(path.goal)->bits.arrow_color = COLOR_LIGHTMAGENTA; + PathMetrics raytrace(path); // Materialize map blocks, or the projectile will crash into them @@ -1320,7 +1329,53 @@ struct projectile_hook : df::proj_itemst { fall_threshold = std::min(fall_threshold, engine->fire_range.second); } - void aimAtArea(EngineInfo *engine) + void aimAtPoint(EngineInfo *engine, int skill, const ProjectilePath &path) + { + df::coord fail_target = path.goal; + + orient_engine(engine->bld, path.goal); + + // Debug + Maps::getTileOccupancy(path.goal)->bits.arrow_color = COLOR_LIGHTRED; + + // Dabbling always hit in 7x7 area + if (skill < skill_rating::Novice) + { + fail_target.x += random_int(7)-3; + fail_target.y += random_int(7)-3; + aimAtPoint(engine, ProjectilePath(path.origin, fail_target)); + return; + } + + // Exact hit chance + float hit_chance = 1.04f - powf(0.8f, skill); + + if (float(rand())/RAND_MAX < hit_chance) + { + aimAtPoint(engine, path); + return; + } + + // Otherwise perturb + if (skill <= skill_rating::Proficient) + { + // 5x5 + fail_target.x += random_int(5)-2; + fail_target.y += random_int(5)-2; + } + else + { + // 3x3 + int idx = random_int(8); + fail_target.x += offsets[idx][0]; + fail_target.y += offsets[idx][1]; + } + + ProjectilePath fail(path.origin, fail_target, path.fudge_delta, path.fudge_factor); + aimAtPoint(engine, fail); + } + + void aimAtArea(EngineInfo *engine, int skill) { df::coord target, last_passable; df::coord tbase = engine->target.first; @@ -1343,7 +1398,7 @@ struct projectile_hook : df::proj_itemst { if (raytrace.hits() && engine->isInRange(raytrace.goal_step)) { - aimAtPoint(engine, path); + aimAtPoint(engine, skill, path); return; } } @@ -1351,7 +1406,7 @@ struct projectile_hook : df::proj_itemst { if (!last_passable.isValid()) last_passable = target; - aimAtPoint(engine, ProjectilePath(engine->center, last_passable)); + aimAtPoint(engine, skill, ProjectilePath(engine->center, last_passable)); } static int safeAimProjectile(lua_State *L) @@ -1373,9 +1428,9 @@ struct projectile_hook : df::proj_itemst { lua_call(L, 5, 1); if (lua_isnil(L, -1)) - proj->aimAtArea(engine); + proj->aimAtArea(engine, skill); else - proj->aimAtPoint(engine, decode_path(L, -1, engine->center)); + proj->aimAtPoint(engine, skill, decode_path(L, -1, engine->center)); return 0; } @@ -1396,13 +1451,19 @@ struct projectile_hook : df::proj_itemst { int skill = getOperatorSkill(engine->bld, true); - lua_pushcfunction(L, safeAimProjectile); - lua_pushlightuserdata(L, this); - lua_pushlightuserdata(L, engine); - lua_pushinteger(L, skill); + // Dabbling can't aim + if (skill < skill_rating::Novice) + aimAtArea(engine, skill); + else + { + lua_pushcfunction(L, safeAimProjectile); + lua_pushlightuserdata(L, this); + lua_pushlightuserdata(L, engine); + lua_pushinteger(L, skill); - if (!Lua::Core::SafeCall(out, 3, 0)) - aimAtArea(engine); + if (!Lua::Core::SafeCall(out, 3, 0)) + aimAtArea(engine, skill); + } switch (item->getType()) {