Index: game/engines/default/data/keybinds/move.lua =================================================================== --- game/engines/default/data/keybinds/move.lua (revision 4963) +++ game/engines/default/data/keybinds/move.lua (working copy) @@ -135,3 +135,9 @@ group = "movement", name = "Auto-explore", } +defineAction{ + default = { "sym:_a:false:false:false:false" }, + type = "STALK", + group = "movement", + name = "Auto-melee", +} Index: game/modules/tome/class/Actor.lua =================================================================== --- game/modules/tome/class/Actor.lua (revision 4963) +++ game/modules/tome/class/Actor.lua (working copy) @@ -3173,7 +3173,12 @@ --- Does the actor have LOS to the target function _M:hasLOS(x, y, what) - if not x or not y then return false, self.x, self.y end + return self:hasLOSFrom(self.x, self.y, x, y, what) +end + +--- Does the actor if it was at sx, sy have LOS to the target +function _M:hasLOSFrom(sx, sy, x, y, what) + if not x or not y then return false, sx, sy end what = what or "block_sight" local lx, ly, is_corner_blocked @@ -3184,7 +3189,7 @@ darkVisionRange = self:getTalentRange(t) end - local l = core.fov.line(self.x, self.y, x, y, "block_sight") + local l = core.fov.line(sx, sy, x, y, "block_sight") local inCreepingDark, lastX, lastY = false lx, ly, is_corner_blocked = l:step() while lx and ly and not is_corner_blocked do @@ -3195,7 +3200,7 @@ break end end - if inCreepingDark and darkVisionRange and core.fov.distance(self.x, self.y, lx, ly) > darkVisionRange then + if inCreepingDark and darkVisionRange and core.fov.distance(sx, sy, lx, ly) > darkVisionRange then lx, ly = lastX or lx, lastY or ly break end @@ -3204,7 +3209,7 @@ lx, ly, is_corner_blocked = l:step() end else - local l = core.fov.line(self.x, self.y, x, y, what) + local l = core.fov.line(sx, sy, x, y, what) lx, ly, is_corner_blocked = l:step() while lx and ly and not is_corner_blocked do if game.level.map:checkAllEntities(lx, ly, what) then break end Index: game/modules/tome/class/Game.lua =================================================================== --- game/modules/tome/class/Game.lua (revision 4963) +++ game/modules/tome/class/Game.lua (working copy) @@ -1160,6 +1160,8 @@ if not ok and err then print(debug.traceback(co)) error(err) end end, + STALK = function() self.player:stalk() end, + RUN_AUTO = function() if self.level and self.zone then local seen = {} Index: game/modules/tome/class/Player.lua =================================================================== --- game/modules/tome/class/Player.lua (revision 4963) +++ game/modules/tome/class/Player.lua (working copy) @@ -873,6 +873,88 @@ function _M:runStopped() if obj then game.level.map.attrs(x, y, "obj_seen", true) end end +--- stalk +local rushedEnemy = nil +function _M:stalk() + local spotted = spotHostiles(self) + + if(#spotted == 0) then + game.logPlayer(self, "No enemy in the field of view to stalk.") + else + + while #spotted > 0 do + local index = 1 + local boss = nil + for i,v in ipairs(spotted) do + local distance = core.fov.distance(v.x, v.y, self.x, self.y) + if(distance < core.fov.distance(spotted[index].x, spotted[index].y, self.x, self.y)) then index = i end + if(v.actor.rank >= 3.5) then --unique upwards + if(boss == nil or boss.rank < v.actor.rank) then + boss = v.actor + end + end + end + + + if(boss ~= nil and boss.rank >= 3.5 and boss.rank < 4 and self.life <= self.max_life / 2) then + game.logPlayer(self, "Auto-Fighting a %s not at your best is not a good idea.", boss.name) + return + elseif(boss ~= nil and boss.rank >= 4 and boss.rank < 5) then + game.logPlayer(self, "%s stands before you - this fight will require your full attention.", boss.name:capitalize()) + return + elseif(boss ~= nil and boss.rank >= 5) then + game.logPlayer(self, "%s is near - instead of attacking you should be thinking of running away!", boss.name:capitalize()) + return + elseif(self.life <= self.max_life / 3) then + if(self.old_life ~= self.life) then game.logPlayer(self, "This fight has gone south. Think to fight again.") + else game.logPlayer(self, "Too much of your life rests on the ground for auto-fighting.") + end + return + end + + local e, x, y = spotted[index].actor, self.x, self.y + + if rushedEnemy + and not rushedEnemy.dead + and self:knowTalent(self.T_HACK_N_BACK) + and core.fov.distance(x,y,rushedEnemy.x,rushedEnemy.y) == 1 + and self:useTalent(self.T_HACK_N_BACK,self,nil,nil,rushedEnemy,true) + then + rushedEnemy = nil + return + end + rushedEnemy = nil + + if self:knowTalent(self.T_RUSH) + and core.fov.distance(x,y,e.x,e.y) > 2 + and self:useTalent(self.T_RUSH,self,nil,nil,e,true) + and self.x~=x or self.y~=y + then + rushedEnemy = e + return + end + + local direction = util.getDir(e.x, e.y, x, y) + local dirSides = util.dirSides(direction, x, y) + + --randomize which alternative direction gets picked first + local coinFlip = math.random(2) == 1 + local firstKey = coinFlip and "left" or "right" + local secondKey = coinFlip and "right" or "left" + local firstX, firstY = util.coordAddDir(x, y, dirSides[firstKey]) + local secondX, secondY = util.coordAddDir(x, y, dirSides[secondKey]) + local zerothX, zerothY = util.coordAddDir(x, y, direction) + + if not ((self:hasLOSFrom(zerothX, zerothY, e.x, e.y) and self:attackOrMoveDir(direction)) or + (self:hasLOSFrom(firstX, firstY, e.x, e.y) and self:attackOrMoveDir(dirSides[firstKey])) or + (self:hasLOSFrom(secondX, secondY, e.x, e.y) and self:attackOrMoveDir(dirSides[secondKey])) + ) then + table.remove(spotted, i) + end + end + end +end + --- Activates a hotkey with a type "inventory" function _M:hotkeyInventory(name) local find = function(name)