|
|
|
@ -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())
|
|
|
|
|
{
|
|
|
|
|