Implement skill-based miss probability in siege engine.

develop
Alexander Gavrilov 2012-09-14 20:57:03 +04:00
parent 811c096c0e
commit 000e3baf27
1 changed files with 72 additions and 11 deletions

@ -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);
// 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);
aimAtArea(engine, skill);
}
switch (item->getType())
{