<

rs_default_target = 80

rs_hp_target = rs_default_target

rs_mp_target = rs_default_target

rs_target_mode = "both"

rs_max_swings = 200

rs_stop_on_any_monster = false

rs_active = false

rs_left = 0


local rs_dirs = {

  {-1, 0}, {1, 0}, {0, -1}, {0, 1},

  {-1, -1}, {1, -1}, {-1, 1}, {1, 1},

}


local function rs_attack_command(x, y)

  local d2v = {

    [-1] = {

      [-1] = "CMD_ATTACK_UP_LEFT",

      [0] = "CMD_ATTACK_LEFT",

      [1] = "CMD_ATTACK_DOWN_LEFT",

    },

    [0] = {

      [-1] = "CMD_ATTACK_UP",

      [1] = "CMD_ATTACK_DOWN",

    },

    [1] = {

      [-1] = "CMD_ATTACK_UP_RIGHT",

      [0] = "CMD_ATTACK_RIGHT",

      [1] = "CMD_ATTACK_DOWN_RIGHT",

    },

  }

  return d2v[x] and d2v[x][y]

end


local function rs_need_recovery()

  local hp, mhp = you.hp()

  local mp, mmp = you.mp()

  local hp_low = hp * 100 < mhp * rs_hp_target

  local mp_low = mmp > 0 and mp * 100 < mmp * rs_mp_target


  if rs_target_mode == "hp" then

    return hp_low

  elseif rs_target_mode == "mp" then

    return mp_low

  elseif rs_target_mode == "any" then

    if mmp <= 0 then return hp_low end

    return hp_low and mp_low

  end


  return hp_low or mp_low

end


local function rs_bad_status()

  return you.poisoned() or you.sick() or you.confused()

      or you.berserk() or you.on_fire() or you.teleporting()

      or you.constricted()

end


local function rs_monster_seen()

  local los = you.los()

  for x = -los, los do

    for y = -los, los do

      if not (x == 0 and y == 0) and you.see_cell(x, y) then

        if view.invisible_monster(x, y) then

          return true

        end

        local m = monster.get_monster_at(x, y)

        if m and (rs_stop_on_any_monster or not m:is_safe()) then

          return true

        end

      end

    end

  end

  return false

end


local function rs_find_empty_adjacent()

  for _, d in ipairs(rs_dirs) do

    local x, y = d[1], d[2]

    if you.see_cell(x, y)

        and view.is_safe_square(x, y)

        and not view.invisible_monster(x, y)

        and not monster.get_monster_at(x, y) then

      return x, y

    end

  end

  return nil, nil

end


local function rs_stop(msg)

  rs_active = false

  if msg then crawl.mpr(msg) end

end


local function rs_trim(s)

  return s:gsub("^%s+", ""):gsub("%s+$", "")

end


local function rs_parse_target(input)

  input = rs_trim(input):lower()

  if input == "" then

    return "both", rs_default_target

  end


  local target = tonumber(input)

  local mode = "both"


  if not target then

    local prefix, value = input:match("^([a-z]+)%s*(%d+%.?%d*)$")

    target = tonumber(value)

    if prefix == "h" or prefix == "hp" then

      mode = "hp"

    elseif prefix == "m" or prefix == "mp" then

      mode = "mp"

    elseif prefix == "any" then

      mode = "any"

    elseif prefix == "b" or prefix == "both" then

      mode = "both"

    else

      return nil, nil

    end

  end


  if target == nil or target < 1 or target > 100 then

    return nil, nil

  end


  return mode, math.floor(target)

end


local function rs_target_description()

  if rs_target_mode == "hp" then

    return "HP " .. rs_hp_target .. "%"

  elseif rs_target_mode == "mp" then

    return "MP " .. rs_mp_target .. "%"

  elseif rs_target_mode == "any" then

    return "HP or MP " .. rs_hp_target .. "%"

  end

  return "HP/MP " .. rs_hp_target .. "%"

end


local function rs_prompt_target()

  crawl.mpr("Rest swing target? Enter=" .. rs_default_target

      .. ", 80=both, h80=HP, m80=MP, any80=either.", "prompt")

  local input = crawl.c_input_line()

  if input == nil then return false end


  local mode, target = rs_parse_target(input)

  if not mode then

    crawl.mpr("Rest swing cancelled: enter 1-100, hN, mN, or anyN.")

    return false

  end


  rs_target_mode = mode

  rs_hp_target = target

  rs_mp_target = target

  return true

end


function rest_swing_step()

  if not rs_active or you.turn_is_over() then return end

  if not rs_need_recovery() then return rs_stop("Rest swing done.") end

  if rs_left <= 0 then return rs_stop("Rest swing stopped: limit reached.") end

  if rs_bad_status() then return rs_stop("Rest swing stopped: bad status.") end

  if not you.feel_safe() or rs_monster_seen() then

    return rs_stop("Rest swing stopped: monster or danger seen.")

  end


  local x, y = rs_find_empty_adjacent()

  if not x then return rs_stop("Rest swing stopped: no safe empty adjacent cell.") end


  local cmd = rs_attack_command(x, y)

  if not cmd then return rs_stop("Rest swing stopped: no attack command.") end


  rs_left = rs_left - 1

  crawl.do_commands({cmd})

end


function rest_swing_toggle()

  if rs_active then return rs_stop("Rest swing stopped.") end

  if not rs_prompt_target() then return end

  rs_active = true

  rs_left = rs_max_swings

  crawl.mpr("Rest swing started: target " .. rs_target_description() .. ".")

  rest_swing_step()

end


local old_ready = ready

function ready()

  if old_ready then old_ready() end

  rest_swing_step()

end

>


macros += M \{F4} ===rest_swing_toggle




----------------------

by gpt

F4를 누르면 

아래처럼 나옴

hp만 뭐 100까지 회복하고 싶으면 h100, mp는 m100, 수치는 맘대로 h50, m50 이런 것도 되고

적 나타나면 멈추고

허공에 스윙함

턴카운트런할때 귀찮거나 적 나타나는거 모르고 한두번 더 휘둘러서 망하고 이런 일 종종 있는데

그 때 유용하게 쓸 수 있을듯


a15714ab041eb360be3335625683746f0253452cd6a7ec89d63661f69f10cd6e6dd7e9002bc0e81d1ab1eb36