Lua 스크립팅 요리책

Lua API에 익숙해지고 사용 방법을 배우는 데 도움이 되는 코드 조각들입니다. 이 예제들을 테스트하려면 ` (백틱) 키를 눌러 게임 내 Lua 콘솔에 코드를 붙여넣으세요.

아이템

인벤토리에서 장착 및 착용한 모든 아이템 목록 가져오기

local you = gapi.get_avatar()
local items = you:all_items(false)

for _, item in pairs(items) do
  local status = ""
  if you:is_wielding(item) then
    status = "들고 있음: "
  elseif you:is_wearing(item) then
    status = "착용 중: "
  end
  print(status .. item:tname(1, false, 0))
end
예제 출력
들고 있음: 스마트폰
착용 중: 브래지어
착용 중: 팬티
착용 중: 양말
착용 중: 청바지
착용 중: 긴팔 셔츠
착용 중: 운동화
착용 중: 메신저백
착용 중: 손목시계
포켓칼
성냥갑
깨끗한 물 (플라스틱 병)
깨끗한 물

몬스터

플레이어 근처에 개 소환하기

local avatar = gapi.get_avatar()
local coords = avatar:get_pos_ms()
local dog_mtype = MtypeId.new("mon_dog_bcollie")
local doggy = gapi.place_monster_around(dog_mtype, coords, 5)
if doggy == nil then
    gdebug.log_info("개를 스폰할 수 없습니다 :(")
else
    gdebug.log_info(string.format("개를 %s 위치에 스폰했습니다", doggy:get_pos_ms()))
end

전투

전투 기술 사용 시 세부 정보 출력하기

먼저 함수를 정의합니다.

on_creature_performed_technique = function(params)
  local char = params.char
  local technique = params.technique
  local target = params.target
  local damage_instance = params.damage_instance
  local move_cost = params.move_cost
  gdebug.log_info(
          string.format(
                  "%s가(이) %s을(를) %s에게 사용했습니다 (DI: %s , MC: %s)",
                  char:get_name(),
                  technique.name,
                  target:get_name(),
                  damage_instance:total_damage(),
                  move_cost
          )
  )
end

그런 다음 훅을 함수에 한 번만 연결합니다.

table.insert(game.hooks.on_creature_performed_technique, function(...) return on_creature_performed_technique(...) end)
예제 출력
Ramiro Waters가(이) zombie에게 Power Hit을(를) 사용했습니다 (DI: 27.96 , MC: 58)

아이템 내구도

아이템 손상도 확인 및 수정하기

local you = gapi.get_avatar()
local wielding = you:all_items(false)[1]
print(wielding:get_damage())
print(wielding:get_damage_level(4))
wielding:mod_damage(2000)
print(wielding:get_damage_level(4))

몬스터

몬스터 인벤토리에 아이템 추가하기

local target_monster = -- [[ 당신의 몬스터 참조 ]]
local scraps = gapi.create_item(ItypeId.new("scrap"), 3)
target_monster:as_monster():add_item(scraps)

날씨 훅

날씨 변화에 반응하기

먼저 preload.lua에서 훅을 설정합니다:

local mod = game.mod_runtime[game.current_mod]
table.insert(game.hooks.on_weather_changed, function(...) mod.weather_changed_alert(...) end)
table.insert(game.hooks.on_weather_updated, function(...) mod.weather_report(...) end)

그 다음 main.lua에서 핸들러를 정의합니다:

local mod = game.mod_runtime[game.current_mod]

-- 날씨가 변할 때 호출됨 (예: 맑음 -> 비)
mod.weather_changed_alert = function(params)
    local msg = string.format(
        "날씨가 %s에서 %s로 변경되었습니다!",
        params.old_weather_id,
        params.weather_id
    )
    gdebug.log_info(msg)
end

-- 5분마다 현재 날씨 데이터와 함께 호출됨
mod.weather_report = function(params)
    local msg = string.format(
        "현재 날씨: %s, 온도: %.1f°C, 바람: %d, 습도: %d%%",
        params.weather_id,
        params.temperature,
        params.windspeed,
        params.humidity
    )
    gdebug.log_info(msg)
end

원거리 전투

발사된 총과 던져진 아이템에 반응하기

먼저 preload.lua에서 훅을 설정합니다:

local mod = game.mod_runtime[game.current_mod]
table.insert(game.hooks.on_shoot, function(...) return mod.on_shoot_fun(...) end)
table.insert(game.hooks.on_throw, function(...) return mod.on_throw_fun(...) end)

그 다음 main.lua에서 핸들러를 정의합니다:

local mod = game.mod_runtime[game.current_mod]

mod.on_shoot_fun = function(params)
    ---@type Item
    local gun = params.gun
    ---@type Item
    local ammo_item = params.ammo
    local ammo = ItypeId.NULL_ID()
    if not ammo_item then
        ammo = gun:ammo_current()
    else
        ammo = ammo_item:get_type()
    end
    local shoot_noise = ammo:obj():slot_ammo().loudness
    gdebug.log_info(string.format("총소리: %d.", shoot_noise))
end

mod.on_throw_fun = function(params)
    ---@type Character
    local thrower = params.thrower
    ---@type Item
    local thrown = params.item
    if thrown:is_gun() then
        gdebug.log_info("어라! 총은 던지는 것이 아닙니다!")
    end
end

오버맵 쿼리

오버맵에서 아이템 찾기 및 조작하기

-- 특정 위치의 오버맵에서 모든 아이템 찾기
local om_pos = OmPos.new(0, 0, 0)
local items = gapi.overmap_find_items_around(om_pos, 0)

-- 맵에서 아이템을 가져와서 맵이 언로드되어도 Lua에서 유지하기
local map_pos = MapPos.new(100, 100, 0)
local item = gapi.get_map():find_item_at(map_pos)
if item then
    local detached = gapi.create_detached_item(item)
    -- 나중에 다시 위치에 부착할 수 있습니다
    local reattached = gapi.reattach_item(detached, map_pos)
end

-- 같은 맵 내에서 아이템 이동하기
local source_pos = MapPos.new(100, 100, 0)
local dest_pos = MapPos.new(110, 110, 0)
gapi.get_map():move_item_at(source_pos, dest_pos)

사망 훅

몬스터 사망 추적하기

-- preload.lua에서
local mod = game.mod_runtime[game.current_mod]
table.insert(game.hooks.on_mon_death, function(...) return mod.on_mon_death(...) end)
-- main.lua에서
local mod = game.mod_runtime[game.current_mod]

mod.on_mon_death = function(params)
    ---@type Creature
    local monster = params.creature
    ---@type Character|nil
    local killer = params.killer

    local killer_name = killer and killer:get_name() or "알 수 없음"
    gdebug.log_info(string.format("%s가(이) %s에게 죽었습니다", monster:get_name(), killer_name))
end

캐릭터 사망 추적하기

-- preload.lua에서
local mod = game.mod_runtime[game.current_mod]
table.insert(game.hooks.on_char_death, function(...) return mod.on_char_death(...) end)
-- main.lua에서
local mod = game.mod_runtime[game.current_mod]

mod.on_char_death = function(params)
    ---@type Character
    local char = params.char
    ---@type Character|nil
    local killer = params.killer

    if char == gapi.get_avatar() then
        gdebug.log_info("당신이 죽었습니다!")
    end
end

캐릭터 전투 스탯

공격 및 스태미나 비용 확인하기

local you = gapi.get_avatar()
local items = you:all_items(false)

for _, item in pairs(items) do
    print(
        item:tname(1, false, 0) 
        .. " { 공격 비용: " .. item:attack_cost() 
        .. ", 스태미나 비용: " .. item:stamina_cost()
        .. ", 근접 스태미나 비용: " .. you:get_melee_stamina_cost(item)
        .. " }"
    )
end

-- 특수 능력 확인
print("Uncanny dodge: " .. (you:uncanny_dodge() and "네" or "아니오"))

동적 아이템 액션

Lua에서 커스텀 아이템 사용 함수 만들기

-- tick과 can_use 함수로 아이템의 사용 동작 정의
game.iuse_functions["my_custom_item"] = {
    use = function(params)
        local user = params.user
        local item = params.item
        gdebug.log_info("사용 중: " .. item:tname(1))
        return 0  -- 이동 단위로 시간 비용 반환
    end,

    can_use = function(params)
        local user = params.user
        local item = params.item
        -- 사용을 허용하려면 true, 방지하려면 false 반환
        return true
    end,

    tick = function(params)
        local user = params.user
        local item = params.item
        -- 아이템이 활성화되어 있는 동안 주기적으로 호출됨
        if item:get_countdown() == 0 then
            gdebug.log_info("아이템 카운트다운이 완료되었습니다!")
        end
    end
}

-- 주기적 틱을 트리거하기 위해 아이템에 카운트다운 설정
local item = gapi.create_item(ItypeId.new("some_item"), 1)
item:set_countdown(100)  -- 100턴 동안 틱

더 많은 전투 훅

회피, 방어 및 기술 이벤트에 반응하기

-- preload.lua에서
local mod = game.mod_runtime[game.current_mod]
table.insert(game.hooks.on_creature_dodged, function(...) return mod.on_creature_dodged(...) end)
table.insert(game.hooks.on_creature_blocked, function(...) return mod.on_creature_blocked(...) end)
table.insert(game.hooks.on_creature_performed_technique, function(...) return mod.on_creature_performed_technique(...) end)
table.insert(game.hooks.on_creature_melee_attacked, function(...) return mod.on_creature_melee_attacked(...) end)
-- main.lua에서
local mod = game.mod_runtime[game.current_mod]

mod.on_creature_dodged = function(params)
    ---@type Character
    local char = params.char
    ---@type Creature
    local source = params.source
    local difficulty = params.difficulty
    gdebug.log_info(string.format("%s가(이) %s를(을) 회피했습니다 (DC: %d)", char:get_name(), source:get_name(), difficulty))
end

mod.on_creature_blocked = function(params)
    ---@type Character
    local char = params.char
    ---@type Creature
    local source = params.source
    local bodypart_id = params.bodypart_id
    local damage_blocked = params.damage_blocked
    gdebug.log_info(string.format(
        "%s가(이) %s를(을) %s에서 방어했습니다 (방어함: %.1f 데미지)",
        char:get_name(),
        source:get_name(),
        bodypart_id,
        damage_blocked
    ))
end

mod.on_creature_melee_attacked = function(params)
    ---@type Character
    local char = params.char
    ---@type Creature
    local target = params.target
    if params.success then
        gdebug.log_info(string.format("%s가(이) %s를(을) 맞췄습니다", char:get_name(), target:get_name()))
    else
        gdebug.log_info(string.format("%s가(이) %s를(을) 빗맞혔습니다", char:get_name(), target:get_name()))
    end
end

아이템 타입 정보

ItypeId를 통한 아이템 타입 속성 쿼리

local item_type = ItypeId.new("9mm")

-- 아이템 타입 객체(ItypeRaw) 가져오기
local itype_raw = item_type:obj()

-- 아이템 타입별 데이터 접근 (예: 탄약)
if itype_raw:slot_ammo() then
    local ammo_data = itype_raw:slot_ammo()
    print("탄약 데미지: " .. ammo_data.damage)
    print("탄약 범위: " .. ammo_data.range)
end

-- 컨테이너의 경우
if itype_raw:slot_container() then
    local container_data = itype_raw:slot_container()
    print("수용량: " .. container_data.capacity)
end

-- 도구의 경우
if itype_raw:slot_tool() then
    local tool_data = itype_raw:slot_tool()
    print("도구 품질: " .. tool_data.quality)
end