호문클루스 인공지능 스크립트 설명서

 

목차

1.- 소개

2- 스크립트 작동구조

3- 라그나로크 클라이언트 내장 함수 설명

4- 라그나로크 클라이언트 내장 상수값 설명

5- 기본적으로 제공된 스크립트 설명

6- 기타

7- 변경 사항

 

1 – 소개

 

라그나로크(이하 라그) 게임 내의 호문클루스의 행동은, 라그 클라이언트 프로그램이 설치된 폴더의 AI 폴더 안에 있는 AI.lua, Util.lua 의해서 조절됩니다.

라그 게이머는 자신이 원하는 인공지능을 스스로 만들 수도 있고, 다른 사람이 만든 인공지능을 사용할 수도 있게 하는 것이 목적입니다.

 

스크립트는 라그 클라이언트와 연동되는 일종의 프로그램입니다. 문법적인 오류가 있다면 작동이 되지 않으며, 논리적인 오류가 있어도 원하는 대로 작동하지 않습니다.

 

라그 개발실에서는 스크립트에 대한 편집을 하지 않아도 게임을 즐길 있도록 좋은 스크립트를 제공할 예정입니다.

 

프로그래밍에 익숙하지 않은 사용자들에 대해서도 자신이 원하는 인공지능을 구현하는데 도움이 되도록 수정, 보완해 나갈 계획입니다.

 

프로그램밍에 관심이 있는 사용자들을 위해서도 지속적으로 기능을 추가할 계획입니다.

 

스크립트를 작성하는 언어는 루아(LUA) 입니다. (http://www.lua.org)

 

2- 스크립트 작동 구조

 

라그 클라이언트는 클라이언트 프로그램에서 호문클루스가 새로 생성되는 시점에 AI.lua, Util.lua 파일을 해석하여서 인공지능을 작동

시킵니다.

 

호문클루스가 새로 생성되는 시점은 다음과 같습니다.

             1) 호문클루스를 탄생시킬

       2) 죽은 호문클루스를 살려낼

       3) 캐릭터선택창에서 호문클루스를 소유한 캐릭터를 선택하고 게임을 시작할

       4) 호문클루스를 소유한 캐릭터가 파리의 날개, 나비의 날개를 사용할

       5) 호문클루스를 소유한 캐릭터가 워프포탈을 이용할

       6) 호문클루스를 소유한 캐릭터가 카프라 이동서비스를 사용할

      

      

       위의 사항들의 공통점은 호문클루스가 게임 공간에 새로 나타날 때입니다.

 

AI.lua 파일이 해석된 후에, 라그 클라이언트 프로그램은 AI.lua 스크립트에서 AI ( id) 함수를 실행시킵니다. id 게임상에서 호문의 고유번호입니다.  id 라그 클라이언트가 인공지능 스크립트로 전달하는 숫자입니다.  AI(id) 내용을 편집하여 인공지능을 변화시킬 있습니다. 기초적인 호문의 행동, 이동, 공격, 먹기, 스킬사용등의 함수는 라그 클라이언트 프로그램에 내장되어 있습니다. 자신이 구상한 적절한 상황에서, 제공되는 함수를 실행시키면 됩니다.

 

Const.lua 라그 클라이언트 프로그램내의 각종 상수값들에 대한 정보를 담고 있습니다. Const.lua AI.lua Utiil.lua 에서 참조합니다.

Utiil.lua AI.lua 에서 참조하도록 되어 있습니다. 현재 Util.lua 에는 리스트 자료구조, 몇가지 단순한 기능의 계산함수들이 있습니다.

 

호문클루스 인공지능의 필수 조건은 AI.lua 파일과 AI.lua  파일내에 정의된 AI (id) 함수 입니다. 조건 이외는 모두 선택사항입니다. Const.lua, Util.lua 선택적인 것입니다. 하지만 스크립트 작성할   여러가지 정보가 필요하니 있어야겠죠?

 

 

3- 라그나로크 클라이언트 내장 함수 설명

 

       # id : 게임내의 물체가 가지는 고유 번호

 

1)  MoveToOwner (id)

id  : 호문클루스의 id

반환값: 없음

기능 : 호문클루스를 주인 가까이 이동시킨다.

 

2)  Move (id,x,y)

id  : 호문클루스의 id

                           x  : 목적지 가로 좌표

y : 목적지 세로 자표

반환값 : 없음

기능 : 호문클루스를 목적지로 이동시킨다.

 

3)  Attack (id1,id2)

id1 : 공격자

id2 : 피공격자

반환값 : 없음

기능 : 호문클루스에게 id2 공격하도록 한다.

 

4)  GetV (V_,id) end

V_... : 물체의 속성을 나타내는 상수값

id  : 속성의 대상

반환값 : V_... 따라 달라진다. 예를 들면, V_POSITION 경우는 x, y 좌표 V_HP 경우는 HP 이다.

기능 : id 속성(V_...) 얻는다. 속성을 나타내는 상수값은 Util.lua 정의되어 있다.

속성에 대한 상세내용은 ‘4- 라그나로크 클라이언트 내장 상수값 설명 참조한다.

 

5)  GetActors ()

반환값 : id 들의 집합니다. 루아의 table 형태로 반환된다.

기능 : 캐릭터 시야에 있는 캐릭터, 엔피시, 몬스터, 아이템, 스킬들의 id 얻어온다.

 

6)  GetTick ()

반환값 : 1/1000 단위의 숫자

기능 : 컴퓨터의 시간을 얻어온다. 값은, 컴퓨터가 시작할 0 에서 시작하며,  1/1000 초마다 1 증가하여 현재에 이른 값이다

 

7)  GetMsg (id)

 id  : 호문클루스의 id

반환값 : 라그나로크 클라이언트로부터 전달된 메시지다. 루아의 table 형태로 반환된다.

기능 : 사용자의 직접적인 명령등을 스크립트로 전달한다.

 

8)  GetResMsg (id)

id  : 호문클루스의 id

반환값 : 라그나로크 클라이언트로부터 전달된 예약 메시지다. 루아의 table 형태로 반환된다

기능 : 사용자의 직접적인 예약 명령등을 스크립트로 전달한다.

      

10)  SkillObject (id,level,skill,target)

id : 호문클루스의 id

반환값 : 없음

기능 : id 해당하는 것이 target 해당하는 것에 대하여 level 값에 해당하는 어떤 스킬(skill) 사용한다.

 

11)  SkillGround (id,level,skill,x,y)

id 해당하는 것이 x.y 좌표에 대하여 level 값에 해당하는 skill 사용한다.

 

13)  IsMonster (id)

id : 게임내의 물체

반환값 : id 해당하는 것이 몬스터이면 1 반환하고 그렇지 않다면 0 반환한다.

기능 : 몬스터를 판별한다.

 

             14) TraceAI (string)

string : TraceAI.txt 파일에 기록되는 내용이다. 문자열이여야 한다.

기능 : 실행중인 스크립트의 현재 상태를 기록하여 분석에 이용한다.

 

 

4- 라그나로크 클라이언트 내장 상수값 설명

             내장 상수값들은 Const.lua 정의되어 있습니다.

 

4-1  GetV 함수에 사용되는 상수 값들

 

V_OWNER                                   =           0                                      -- 주인 캐릭터의 번호  반환

V_POSITION                               =           1                                      -- 현재 위치 x, y  반환

V_TYPE                                       =           2                                      -- 어떤 객체인가? (미구현)

V_MOTION                                  =           3                                      -- 현재 동작 반환

V_ATTACKRANGE                    =           4                                      -- 공격 범위 반환 (미구현, 임시로 1)

V_TARGET                                  =           5                                     -- 공격이나 스킬 사용 목표 반환

V_SKILLATTACKRANGE         =           6                                     -- 스킬 공격 범위 (미구현)

V_HOMUNTYPE                         =           7                                      -- 호문클루스 종류 반환

V_HP                                            =           8                                      -- 호문클루스나 주인의 HP

V_SP                                             =           9                                      -- 호문클루스나 주인의 SP

V_MAXHP                                   =           10                                    -- 호문클루스나 주인의 최대 HP

V_MAXSP                                    =           11                                    -- 호문클루스나 주인의 최대 SP

 

 

4-2   GetV (V_MOTION, id) 대한 반환값

 

MOTION_STAND                       =           0                                      : 있는 동작

MOTION_MOVE                         =           1                                      : 이동중인 동작

MOTION_ATTACK                    =          2                                      : 공격중인 동작

MOTION_DEAD                         =          3                                      : 죽은 동작

MOTION_ATTACK2                  =          9                                     : 공격하는 동작

 

 

4-3        GetV (V_HOMUNTYPE, id) 대한 반환값

 

LIF                                                =           1                                      : 리프

AMISTR                                       =           2                                      : 아미스트르

FILIR                                            =           3                                      : 필리르

VANILMIRTH                              =           4                                      : 바닐미르스

LIF_H                                           =           5                                      : 진화한 리프

AMISTR_H                                  =           6                                      : 진화한 아미스트르

FILIR_H                                       =           7                                      : 진화한 필리르

VANILMIRTH_H                         =           8                                      : 진화한 바닐미르스

 

 

 

4-4  GetMsg (id), GetResMsg (id) 의해서 반환되는 테이블의 구조

 

NOME_CMD                               =           0                                      -- 명령없음     

{명령번호}

            

             MOVE_CMD                                =           1                                      -- 이동

             {명령번호,X좌표,Y좌표}

              

STOP_CMD                                 =           2                                      -- 정지

             {명령번호}

 

ATTACT_OBJET_CMD                           =           3                                      -- 공격

             {명령번호,목표ID}

 

ATTACK_AREA_CMD              =          4                                      -- 지역 공격

             {명령번호,X좌표,Y좌표}

 

PATROL_CMD                            =           5                                      -- 정찰

             {명령번호,X좌표,Y좌표}

            

HOLD_CMD                                =           6                                      -- 사수

             {명령번호}

 

SKILL_OBJECT_CMD                =           7                                      -- 스킬사용

             {명령번호,선택레벨,종류,목표ID}

 

SKILL_AREA_CMD                    =           8                                      -- 지역스킬사용

             {명령번호,선택레벨,종류,X좌표,Y좌표}

 

FOLLOW_CMD                           =           9                                      -- 주인을 따라 다닌다

             {명령번호}

 

 

 

5- 기본적으로 제공된 스크립트 설명

 

5-1 인공지능 스크립트의 필수 요소

 

 스크립트 파일은 일반적인 텍스트 파일입니다. 메모장과 같은 일반적인 편집기로 작성하면 됩니다. 단지 LUA 라는 언어로 쓰여졌기 때문에 확장자에 lua 사용하였습니다.

 

 호문클루스 인공지능의 필수 조건은 AI.lua 파일과 AI.lua  파일내에 정의된 AI (id) 함수 입니다. 조건 이외는 모두 선택사항입니다.

예를 들어, 새로운 텍스트 파일을 만들고 나서 이름을 AI.lua 라고 고친 후에 메모장으로 엽니다. 그리고 문서에

 

function              AI (myid)

 

end

 

이렇게 쓰고 나서, AI 폴더에 있는 기존 AI.lua 다른 곳에 옮기거나 이름을 바꾼 후에 새로 작성한 파일을 복사합니다. 그리고 라그 클라이언트를 실행하면 제대로 작동합니다. 단지 호문은 아무것도 안하고 가만히 있습니다.

 

 

5-2 유한 상태 기계

 

 인공지능을 작성하는 방법은 여러 가지가 있습니다. 중에서 단순하면서, 많이 쓰이는 방법이 유한상태기계(FSM, Finite State Machine)입니다.

유한상태기계의 학술적 정의는 관련 서적에 상세히 나와 있겠습니다만은, 상식적으로도 대부분의 사람들이 알고 있는 것입니다.

 

우리의 주제인 호문클루스를 예로 들죠. 우선 호문클루스가 어떻게 행동하여 주기를 바라는 바가 있을 것입니다.

이런 것들을 모두 적어볼까요.

 

주인을 공격하는 물체를 찾아가서 혼내줘라

주위에 몬스터가 있으면 먼저 공격해라.. 또는 피해 다녀라 (다친다 ^^;;)

주인이랑 멀리 떨어지지 말고 일정 거리를 유지해라

등등

 

상황에 따라서 수많은 요구사항이 있습니다.

 

요구사항이 정리가 되었다면 우리가 이제 호문클루스가 되어서 생각해봅시다.

 

주위에 아무런 몬스터가 없고 주인도 가만히 있는 상황이 머리 속에 그려집니다. 호문도 그냥 휴식하는 상태입니다.

 

주인이 공격받기나 내가 공격 받았다면 공격한 물체를 적으로 인식하고 쫓아가야 합니다. 누굴 쫓아가는 호문클루스의 상태입니다.

 

적이 공격 범위 안에 있으면 물리공격을 하거나 스킬을 사용합니다. 무엇과 싸우는 상태입니다.

 

적이 소멸하거나 쫓아 갈수 없을 만큼 멀리 달아나버리면 이제 주인 가까이 가야 합니다. 주인을 따라가는 상태입니다.

 

위에서 말한 가지 상태를 각각  휴식(IDLE), 추적(CHASE),공격(ATTACK),회귀(FOLLOW) 상태라고 임의로 정합니다.

그리고 상태에서 어떤 변화가 생기면 지금이 아닌 다른 상태로 변화하는지 결정합니다. 그리고 상태에서 해야 일들을

정해 줍니다. 가지 조건 검사를 추가한 이것을 그림으로 그려보면 다음과 같습니다.

 

 

 

 

휴식

추적

공격

회귀

1. 적이 안보임

2. 적소멸

3. 적이

공격범위

주인과

가까움

3. 적이

공격범위

1. 주인이 공격 받음

2. 공격 받음

1. 적소멸

2. 안보임

주인과

멀어짐

4.시야안

위치 변화

4.재충전

 

 

 최초의 상태는 휴식 상태입니다. 주위 상황이 변함에 따라서 호문의 상태는 변화합니다. 그리고 그때 마다 적절한 행동을 하도록 설정하면 됩니다. 이제 계획을 세웠으니 가지 준비를 하고 실질적인 스크립트를 작성해 봅니다.

 

 

5-3 Util.lua

 

어떤 작업을 , 우리는 편리한 작업을 위하여 도구들을 준비해 둡니다.

 

호문클루스의 인공지능을 만들려면 필수 요소인 AI.lua AI(id) 함수만 있으면 되지만, 미리 가지 도구들을 만들어 두는 것이 좋습니다.

도구들은 주로 테이블이나 함수의 형태입니다. 이것들을 Util.lua 작성하고, AI.lua 에서 참조하도록 합니다

 

파일의 맨위에 Const.lua 참조하도록

require           “./AI/Const.lua”

적어 줍니다.

 

어떤 데이터들을 순서대로 저장하고 순서대로 꺼내기 위한 자료구조가 필요한 경우가 있습니다. 호문클루스의 인공지능의 경우에는

예약 명령어를 순서대로 저장하고 꺼내 필요가 있습니다.  자료구조의 이름을 List 라고 하고 몇가지 함수를 추가합니다.

(Util.lua 참고)

 

List.new ()                                              -- 새로운 리스트 반환

List.pushleft (list, value)                                      -- list 좌측에 요소 추가

List.pushright (list, value)                      -- list 우측에 요소 추가

List.popleft (list)                                    -- list 좌측 첫번째 값을 빼온다

List.popright (list)                                  -- list 우측 첫번째 값을 빼온다

List.clear (list)                                                     -- list 비운다.

List.size (list)                                         -- list 들어 있는 요소들의 개수

 

 

자주 쓰이는 몇가지 계산 함수들도 추가합니다.

GetDistance (x1,y1,x2,y2)                      -- 좌표사이의 거리 (정수값)

GetDistance2 (id1, id2)                         -- 물체사이의 거리 (정수값)

GetOwnerPosition (id)                                        -- 주인의 위치

GetDistanceFromOwner (id)                 -- 주인과의 거리

IsOutOfSight (id1,id2)                           -- id1 id2 서로 보이는 거리이면 true, 보이지 않는 거리이면 false 반환한다.

IsInAttackSight (id1,id2)                        -- id1 공격이나 스킬사용범위에 id2 있으면 true, 아니면 false 반환한다.

 

 

 

5-4 AI.lua

 

 이제 AI.lua 작성해 봅니다. 텅빈 텍스트 파일을 하나 만들고, AI.lua 이름을 바꿉니다. 메모장으로 AI.lua 파일을 엽니다.

파일의 맨위에 Const.lua , Util.lua 참조하도록

require           “./AI/Const.lua”

require           “./AI/Util.lua”

적어 줍니다.

 

하얗게(또는 까맣게) 공간에 필수요소

function AI (myid)

 

end

 

적습니다.

 

여기까지도 하나의 인공지능을 완성한 것입니다. 하지만 텅빈 AI(myid) 함수를 가진 인공지능을 사용하면, 게임에서 호문클루스는 그냥

가만히 있습니다. 이것은 우리의 계획이 아닙니다.

 

계획했던 몇가지 상태를 정의합니다.

 

IDLE_ST                                                    = 0 --- 휴식

FOLLOW_ST                                             = 1 --- 회귀

CHASE_ST                                                = 2 --- 추적

ATTACK_ST                                             = 3 --- 공격

 

그리고 현재 호문클루스의 상태를 기억해 변수가 필요합니다. 호문클루스의 id, id, 목적지 좌표 등등도 기억해 필요가 있습니다.

호문클루스의 상태는 최초에는 휴식 상태입니다.

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

-- global variable

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

MyState                           = IDLE_ST        -- 최초의 상태는 휴식

MyEnemy                                      = 0                      -- id

MyDestX                                      = 0                      -- 목적지 x

MyDestY                                      = 0                      -- 목적지 y

MyPatrolX                                    = 0                      -- 정찰 목적지 x

MyPatrolY                                    = 0                      -- 정찰 목적지 y

ResCmdList                                  = List.new()       -- 예약 명령어 리스트

MyID                                            = 0                      -- 호문클루스 id

MySkill                                         = 0                      -- 호문클루스의 스킬

MySkillLevel                                = 0                      -- 호문클루스의 스킬 레벨

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

 

function AI (myid)

MyID = myid

 

end

 

휴식 상태를 처리하는 함수 OnIDLE_ST() 정의합니다. 그리고 AI(myid) 함수내에 작동하도록 편집합니다.

 

function OnIDLE_ST ()

             local     object = GetOwnerEnemy (MyID)                                                     -- 주인의 적을 찾아낸다.

             if (object ~= 0) then                                                                                         -- 주인의 적이 있는가?

                           MyState = CHASE_ST                                                                       -- 주인의 적이 있으므로 추적상태로 전환

                           MyEnemy = object                                                                            -- 나의 번호를 기억

                           return                                                                                                 -- 함수 종료

             end

 

             object = GetMyEnemy (MyID)                                                                        -- 나의 적을 찾아낸다.

             if (object ~= 0) then                                                                                         -- 나의 적이 있는가?

                           MyState = CHASE_ST                                                                       -- 나의 적이 있으므로 추적상태로 전환

                           MyEnemy = object                                                                             -- 나의 번호를 기억

                           return

             end

 

             local distance = GetDistanceFromOwner(MyID)                                            -- 주인과의 거리를 얻는다.

             if ( distance > 3 or distance == -1) then                                                           -- 주인과의 거리가 3 이상이거나, 주인의 시야 밖이면

                           MyState = FOLLOW_ST                                                      -- 회귀 상태로 전환

                           return;

             end

end

 

 

function AI (myid)

MyID = myid

if (MyState == IDLE_ST) then

OnIDLE_ST ()

             end

end

 

주인이 공격받거나 나의 적이 있을 경우 추적 상태로 전이합니다. 해당하지 않는 경우 주인과의 거리를 계산해서 일정거리 이상 떨어져 있으면

회귀 상태로 전환합니다. 그렇다면주인이 공격받는다”, “나의 적이 있다라는 조건은 어떻게 알까요.?

 

주인이 공격받는다 조건은 주위의 물체들 중에서 몬스터인 것이 대상이 주인일 경우가 되겠습니다. 그렇다면 몬스터가 아닌 일반 플레이어의 캐릭터가

주인을 공격할 경우는 어떻게 알까요? 캐릭터의 동작을 조사해 보면 있습니다.

 

function GetOwnerEnemy (myid)                                                                                  -- 주인의 적을 찾아낸다.

             local result = 0

             local owner  = GetV (V_OWNER,myid)                                                       -- 주인

             local actors = GetActors ()                                                                              -- 주인 시야안의 물체들

             local enemys = {}                                                                                            -- 주인의 적들을 담는 테이블

             local index = 1                                                                                                

             local target

             for i,v in ipairs(actors) do                                                                                             -- 주인 시야안의 물체들을 모두 순회한다.

                           if (v ~= owner and v ~= myid) then                                                   -- 어떤 물체의 목표물

                                        target = GetV (V_TARGET,v)

                                        if (target == owner) then                                                       -- 목표물이 주인이면

                                                      if (IsMonster(v) == 1) then                                      -- 어떤 물체가 몬스터이면

                                                                   enemys[index] = v                                      -- 주인의 적들에 추가

                                                                   index = index+1

                                                      else                                                                          -- 어떤 물체가 몬스터가 아니면

                                                                   local motion = GetV(V_MOTION,i)          -- 어떤 물체의 현재 동작

                                                                   if (motion == MOTION_ATTACK or motion == MOTION_ATTACK2) then – 공격동작이면

                                                                                enemys[index] = v             -- 주인의 적들에 추가

                                                                                index = index+1

                                                                   end

                                                     end

                                        end

                           end

             end

 

             local min_dis = 100                                                                                         -- 최소 거리를 100 으로 설정

             local dis

             for i,v in ipairs(enemys) do                                                                             -- 주인의 적들을 모두 순회

                           dis = GetDistance2 (myid,v)                                                              -- 주인과 어떤 적과의 거리

                           if (dis < min_dis) then                                                                        -- 거리가 최소 거리보다 작으면

                                        result = v                                                                               -- 최종 적을 설정

                                        min_dis = dis                                                                        -- 최소 거리 설정

                           end

             end

            

             return result                                                                                                      -- 최종 결환 반환 ( 0이면 주인의 적이 없는 것이다. )

end

 

결과 값이 0 아닐 경우 주인은 공격을 받고 있는 것입니다.

 

 

나의 적이 있다있다 라는 조건은 어떻게 판단할까요?  만약 호문클루스가 몬스터를 먼저 공격하게 하고 싶다면, 주위에 몬스터가 있다면

적이 있다라고 판단하면 됩니다. 만약 호문클루스가 공격 당할 경우만 호문클루스가 반응하도록 하고 싶다면, 호문클루스를 공격하는 대상이

있는지 검사하면 됩니다. 두가지 하나의 선택은 선공, 비선공 호문클루스를 결정짓습니다.

 

 

function GetMyEnemy (myid)                                                                                        -- 나의 적을 찾는다.

             local result = 0

 

             local type = GetV (V_HOMUNTYPE,myid)                                                   -- 나는 어떤 호문클루스인가

             if (type == LIF or type == LIF_H or type == AMSTR or type == AMSTR_H) then

                           result = GetMyEnemyA (myid)                                                          -- 비선공형 찾기 함수

             elseif (type == FILIR or type == FILIR_H or type == VANILMIRTH or type == VANILMIRTH_H) then

                           result = GetMyEnemyB (myid)                                                           -- 선공형 찾기 함수

             end

             return result

end

 

 

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

--  비선공형 GetMyEnemy

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

function GetMyEnemyA (myid)

             local result = 0

             local owner  = GetV (V_OWNER,myid)

             local actors = GetActors ()

             local enemys = {}

             local index = 1

             local target

             for i,v in ipairs(actors) do

                           if (v ~= owner and v ~= myid) then

                                        target = GetV (V_TARGET,v)                                             

                                        if (target == myid) then                                                        

                                                      enemys[index] = v                                                   -- 나를 공격하는 물체를 나의 적으로 설정

                                                      index = index+1

                                        end

                           end

             end

 

             local min_dis = 100

             local dis

             for i,v in ipairs(enemys) do

                           dis = GetDistance2 (myid,v)

                           if (dis < min_dis) then

                                        result = v

                                        min_dis = dis

                           end

             end

 

             return result

end

 

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

--  선공형 GetMyEnemy

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

function GetMyEnemyB (myid)

             local result = 0

             local owner  = GetV (V_OWNER,myid)

             local actors = GetActors ()

             local enemys = {}

             local index = 1

             local type

             for i,v in ipairs(actors) do

                           if (v ~= owner and v ~= myid) then

                                        if (1 == IsMonster(v))     then

                                                      enemys[index] = v                                                   -- 주변의 몬스터를 나의 적으로 설정

                                                      index = index+1

                                        end

                           end

             end

 

             local min_dis = 100

             local dis

             for i,v in ipairs(enemys) do

                           dis = GetDistance2 (myid,v)

                           if (dis < min_dis) then

                                        result = v

                                        min_dis = dis

                           end

             end

 

             return result

end

 

 

 위와 같이 휴식 상태를 처리하는 프로그램을 하였습니다. 휴식상태에서 전환되는 추적상태, 회귀 상태도

비슷한 방식으로 작성하고 나머지 상태들도 마찬가지입니다.

 

마지막으로 호문클루스가 사용자의 직접적인 명령을 수행하도록 하는 것이 있습니다.                     

마우스로 특정위치로 이동시키거나 특정 몬스터를 공격시키거나, 키보드의 특정키를 눌러서 그자리에서만

가만히 있도록 하거나 하는 처리입니다.

 

사용자가 내리는 명령은 메시지 형태로 스크립트에 전달됩니다. 메시지를 해석하여, 특정명령을 수행하는

상태로 전환시켜주면 됩니다. 그럴려면 메시지를 받고 해석하는 부분과 특정명령을 처리하는 상태를 추가하고

상태처리 함수를 추가하면 됩니다. AI(myid) 도입부에 다음을 추가합니다.            

 

             MyID = myid

             local msg            = GetMsg (myid)                                        -- 명령 받아오기

             local rmsg          = GetResMsg (myid)                                  -- 예약 명령 받아오기

 

             ProcessCommand (msg)                                                        -- 명령 처리

            

             -- 예약 명령어 저장

             if msg[1] == NONE_CMD then

                           if rmsg[1] ~= NONE_CMD then

                                        if List.size(ResCmdList) < 10 then                          -- 예약 명령은 10 개만 담자

                                                      List.pushright (ResCmdList,rmsg)

                                        end

                           end

             else

                           List.clear (ResCmdList)                                          -- 새로운 명령이 들어올 마다 기존 예약 명령들은 취소한다.

             end

            

 

메세지를 처리하는 ProcessCommand (msg) 메시지 처리함수들 OnMOVE_CMD (msg[2],msg[3]) 등을 추가하며,

각각의 명령수행상태 처리함수 (예를 들면 OnMOVE_CMD_ST ()) 들도 작성합니다.  명령수행상태가 필요한 이유는 명령 수행이 완료될

까지 명령 수행 완료를 검사하기 위한 것입니다.

 

 

5-5 문법 오류 검사와 TraceAI.txt  작성

 

완성된 스크립트 파일은 문법적인 오류를 담고 있을 있습니다. 문법오류가 있는 스크립트는 스크립트가 클라이언트 프로그램에서

해석될 오류 메시지를 내보냅니다.

 

클라이어트 프로그램을 실행하지 않는 상태에서 일차적인 문법검사를 있습니다.

라그나로크 설치폴더에 있는 lua.exe 파일을 더블클릭하면 명령프로프트가 나옵니다.

> dofile ‘./AI/AI.lua’

입력하고 엔터를 칩니다. 문법오류가 있다면 해당 메시지가 출력됩니다. 없다면 아무것도 출력되지 않습니다.

 

검사에서 문법적인 오류가 없더라도 호문이 있는 상태에서 게임을 시작하게되면 오류 메시지가 나올 경우가 있습니다.

이때도 마찬가지로 오류가 있는 라인으로 이동하여 수정하고 다시 게임을 시작합니다.

 

문법적인 오류가 없이 스크립트가 실행되어 호문클루스가 작동되더라도 논리적이 오류가 있어서 예상대로 움직이지 않을 있다. 이러한 경우는

호문클루스의 상태변화와 각종 수치의 변화를 기록하여 분석할 필요성이 생깁니다.

 

예를 들어,  호문클루스가 적을 추적하여 가까이 이후로 공격을 하지 않는다면 추적 상태에서 뭔가 논리적 오류가 발생한다고 추측할 있습니다.

 

function OnCHASE_ST ()

 

             TraceAI ("OnCHASE_ST")

 

             if (true == IsOutOfSight(MyID,MyEnemy)) then     -- ENEMY_OUTSIGHT_IN

                           MyState = IDLE_ST

                           MyEnemy = 0

                           MyDestX, MyDestY = 0,0

                           TraceAI ("CHASE_ST -> IDLE_ST : ENEMY_OUTSIGHT_IN")

                           return

             end

             if (true == IsInAttackSight(MyID,MyEnemy)) then  -- ENEMY_INATTACKSIGHT_IN

                           MyState = ATTACK_ST

                           TraceAI ("CHASE_ST -> ATTACK_ST : ENEMY_INATTACKSIGHT_IN")

                           return

             end

 

             local x, y = GetV (V_POSITION,MyEnemy)

             if (MyDestX ~= x or MyDestY ~= y) then                            -- DESTCHANGED_IN

                           MyDestX, MyDestY = GetV (V_POSITION,MyEnemy);

                           Move (MyID,MyDestX,MyDestY)

                           TraceAI ("CHASE_ST -> CHASE_ST : DESTCHANGED_IN")

                           return

             end

 

             TraceAI (string.format(“OnChase_ST end MyEnemy: %d, EnemyX : %d, EnemyY:%d, MyDestX:%d, MyDestY:%d\n”,MyEnemy,x,y,MyDestX,MyDestY))

 

end

 

위와 같이 특정 상태 처리 함수에서, 알고 싶은 내용들을  TraceAI 넣어 줍니다.

그리고 게임 대화창에 /traceai 라고 치면 라그나로크가 설치된 폴더에 있는 TraceAI.txt 파일에 내용들이 기록됩니다.

다시 /traceai 입력하면 기록이 멈춥니다. 

어떤 변수값들을 기록하고 싶을 때는 string.format 이용하여 문자열을 조작합니다.

 

 

 

 

6- 기타

             프로그래밍 언어 루아 (LUA) 홈페이지         : http://www.lua.org

             한글 루아 사이트                                             : http://www.redwiki.net/wiki/wiki.php/Lua

 

 

 

7-변경 사항

 

2005.09.27

1) 새로운 종류의 호문클루스가 추가되어서, Const.lua 호문의 종류 부분과 , AI.lua GetMyEnemy 함수가 수정되었습니다.

사용자 정의 인공지능을 사용하시는 경우에,  위의 부분을 수정하여야, 호문클루스가 제대로 작동합니다.

 

 

2005-07-19

 

1) 이동명령과 공격을 예약할 있습니다.

Shift키와 Alt 키를 누른 상태에서 바닥에 마우스 오른쪽 클릭을 하면 이동명령이 예약됩니다.

Shift키와 Alt 키를 누른 상태에서 공격 대상에 마우스 오른쪽 클릭을 하면 공격명령이 예약됩니다.

 

function AI(myid)

      

       if msg[1] == NONE_CMD then

                     if rmsg[1] ~= NONE_CMD then

                                  if List.size(ResCmdList) < 10 then

                                                List.pushright (ResCmdList,rmsg) -- 예약명령 저장

end

                     end

       else

                     List.clear (ResCmdList)  -- 새로운 명령이 입력되면 예약 명령들은 삭제한다. 

                     ProcessCommand (msg)  -- 명령어 처리

       end

      

       위에서 보는 바와 같이, 명령이 없으면 예약명령을 조사하고

예약명령이 있으면 예약명령 리스트(ResCmdList) 뒤에 추가합니다.

 

function        OnIDLE_ST ()

       TraceAI ("OnIDLE_ST")

       local cmd = List.popleft(ResCmdList)

       if (cmd ~= nil) then                      

                     ProcessCommand (cmd)  -- 예약 명령어 처리

                     return

       end

      

 

휴식상태(IDLE_ST)처리에서 먼저 예약명령어 리스트를 조사하여, 예약명령을 처리합니다.

어떤 상태에서 휴식상태로 전환되면 예약명령을 처리하게 하는 구조입니다.

 

function        OnMOVE_CMD (x,y)

      

local curX, curY = GetV (V_POSITION,MyID)

       if (math.abs(x-curX)+math.abs(y-curY) > 15) then               -- 목적지가 일정 거리 이상이면 (서버에서 먼거리는 처리하지 않기 때문에)

                     List.pushleft (ResCmdList,{MOVE_CMD,x,y})     -- 원래 목적지로의 이동을 예약한다.       

                     x = math.floor((x+curX)/2)                                      -- 중간지점으로 먼저 이동한다. 

                     y = math.floor((y+curY)/2)                                                                                           --

       end

  

end

 

이동명령의 목적지가 너무 멀게 되면 서버에서 처리하지 않습니다.

그래서 원래 목적지로 이동명령을 예약명령리스트의 앞에 넣어주고

중간지점으로 일단 이동요청을 합니다.

 

2) Const.lua 있는 명령번호 NOME_CMD 오타를 NONE_CMD 수정하였습니다.

 

3) 사용자 인공지능  폴더를 추가하였고, 기본 인공지능과 사용자 인공지능 이를

전환하는 키입력과 대화창 명령어를 추가하였습니다.

사용자 인공지능 폴더는 기본적으로 제공하는 인공지능의 업데이트에 영향을

받지 않도록 만들었습니다. 사용자 인공지능 폴더에 있는 인공지능 스크립트가

작동하는 필요조건은 AI.lua 파일과 AI.lua 파일에 정의하는 AI (myid) 함수입니다.

/호문지능 또는 /hoai 대화창에 입력하면 기본 인공지능과 사용자 인공지능 사이를

전환합니다. 라그나로크 사용자들이 직접 제작한 인공 지능 스크립트를 사용하시고자

한다면 기존 AI 폴더 안의 USER_AI 폴더에 스크립트를 복사하십시오.

 

4) 대기명령상태(FOLLOW_CMD_ST)에서 Alt+T (또는 호문클루스 메뉴창에서 대기) 누르면

   휴식상태(IDLE_ST) 전환하게 AI.lua funciton OnFOLLOW_CMD 수정했습니다.