|
사망 중인 캐릭터라도 가문 정보는 여전히 남아 있고, 캐릭터 플래그 역시 제거되지 않았으므로 이 방법은 문제 없이 사용이 가능합니다. 타 가문에 상속되면 게임이 종료되어 버리므로, 이 플래그를 붙일 수 있는 캐릭터는 오직 내 가문의 캐릭터로만 제한할 겁니다. 실제로 지정하는 것은 디시전으로 할 예정이니, 여기서는 일단 그렇게 지정되어 있을 거라고 확인만 해 두십시오.
핵심은 immediate = { } 내의 while = { limit = { } } 절입니다. 그 부분만 따로 떼서 자세히 살펴봅니다.
while = { limit = { ROOT = { current_heir = { NOT = { has_character_flag = mod_event_succ_to } } } } ROOT = { current_heir = { add_trait = out_of_succession character_event = { id = MYSUCC.1002 days = 1 } } } }
while = { } 명령문은 if = { } 문과 비슷한 구조를 갖는데요. if = { } 문이 limit = { } 절의 조건을 만족하는 경우 if = { } 문 내부의 명령어 세트를 1회 실행되는 것과는 달리, while = { } 명령문은 limit = { } 문 내의 조건이 거짓이 될 때까지 계속 while = { } 문 내부의 구문들을 실행합니다. 그 이야기는, 현재 ROOT(=사망 중인 플레이어 캐릭터)의 후계자가 mod_event_succ_to 캐릭터 플래그를 갖고 있지 않는 한, 계속 그 아래의 명령문을 실행하게 될 건데요. 그럼 그 실행할 명령문이 뭔고 하니, 현재의 후계자에게 out_of_succession 트레잇을 붙이고, 그 캐릭터에게 1일 후에 MYSUCC.1002 이벤트를 실행하는 겁니다. 이름에서 짐작하시듯 out_of_succession 트레잇은 상속권 박탈 트레잇입니다. 즉, 이 루프를 돌면서, 플레이어가 지정한 캐릭터보다 상속 순위가 높은 캐릭터는 차례대로 상속권이 제척되고, 플레이어가 지정한 캐릭터가 상속자가 되면 limit = { } 내부가 거짓이 되면서 while = { } 루프는 종료가 됩니다. 중요한 것은, while = { } 루프가 도는 동안은 이 이벤트가 끝나지 않았기 때문에, on_death = { } 온 액션 이벤트 트리거 역시 종료되지 않고, 그 결과 플레이어 역시 사망하지 않는다는 것입니다. 원하는 상속자가 지정될 때까지 사망 판정이 늦춰지는 거죠. 상속 서열 내에 플레이어가 지정한 캐릭터가 존재하기만 한다면, 지정 캐릭터로의 완벽한 상속을 보장하는 것입니다.
제척된 캐릭터에게 1일 후에 실행되는 MYSUCC.1002 이벤트는, 상속 순위 제척을 위해 추가했던 상속 배제 트레잇을 제거해주는 이벤트입니다. 상속이 끝난 이상 이 트레잇을 더 이상 붙여둘 필요가 없기 때문에, 그리고 이 트레잇으로 인해 캐릭터들이 상속을 받을 수가 없어서 다른 문제를 발생시키는 것을 막기 위해서 가능한 한 빨리 떼어 주는 것이 좋습니다.
character_event = { id = MYSUCC.1002 hide_window = yes is_triggered_only = yes immediate = { remove_trait = out_of_succession recalc_succession = yes } }
앞에서도 나왔던 recalc_succession = yes 명령어는 현재 상태에서 상속 순위를 다시 계산하는 명령어입니다. 상속 순위에 영향이 가는 명령어를 실행한 후에는 한번쯤 실행해 주는 것도 나쁘지 않죠.
다음은, 실제로 특정 캐릭터를 후계자로 지정하는 디시전이 필요하겠지요. 디시전 폴더(/decisions)에 다음과 같이 간단하게 만들어 넣었습니다.
targetted_decisions = { mod_set_my_next_heir = { filter = dynasty ai_target_filter = dynasty from_potential = { ai = no } potential = { ai = yes is_alive = yes dynasty = FROM } allow = { FROM = { NOT = { has_character_flag = mod_event_succ_from } } } effect = { ROOT = { set_character_flag = mod_event_succ_to } FROM = { set_character_flag = mod_event_succ_from } } revoke_allowed = { always = no } ai_will_do = { factor = 0 } } mod_clr_my_next_heir = { filter = dynasty ai_target_filter = dynasty from_potential = { ai = no has_character_flag = mod_event_succ_from } potential = { ai = yes dynasty = FROM is_alive = yes has_character_flag = mod_event_succ_to } allow = { FROM = { has_character_flag = mod_event_succ_from } ROOT = { has_character_flag = mod_event_succ_to } } effect = { ROOT = { clr_character_flag = mod_event_succ_to } FROM = { clr_character_flag = mod_event_succ_from } } revoke_allowed = { always = no } ai_will_do = { factor = 0 } } }
mod_set_my_next_heir 디시전은 후계자를 지정할 때, mod_clr_my_next_heir 디시전은 이미 지정했던 후계자를 해제할 때에 사용할 수 있는 디시전입니다.
우선 mod_set_my_next_heir 디시전을 확인해 봅시다.
from_potential = { } 섹션 안에는 오직 ai = no 만 존재합니다. 즉 플레이어 캐릭터만 이 디시전을 사용할 수 있겠지요.
potential = { }, 즉 대상 캐릭터가 될 수 있는 조건은 플레이어와 가문이 동일할 것, 살아 있을 것, AI 일 것, 이렇게 세 가지입니다.
allow = { } 내부에는 현재 플레이어 캐릭터가 mod_event_succ_from 캐릭터 플래그를 갖고 있지 않을 것이라는 조건이 붙어 있네요. 왜인지는 아시죠? 이미 다른 캐릭터를 후계자로 지정한 상태에서 다른 캐릭터에게 중복으로 후계자 지정을 하지 못하도록 하는 가장 간단한 (부담 적은) 방법이기 때문입니다. 다른 플레이어에게 후계자 지정을 할 때, 플레이어 캐릭터에게도 캐릭터 플래그가 붙으니까요.
혹시나 이해가 잘 되지 않을까 싶어서 좀더 설명을 드리자면, 플레이어가 상속자를 이미 설정했는지 여부를 확인하는 방법은 두 가지가 있습니다. 하나는 플레이어 측 캐릭터에 mod_event_succ_from 캐릭터 플래그가 붙어있는지 확인하는 방법, 다른 하나는 가문 내 캐릭터들 중 mod_event_succ_to 캐릭터 플래그가 붙어있는 캐릭터가 존재하는지를 확인하는 방법입니다. 두 가지 방법 모두 다 사용할 수 있는 유효한 방법인데, 어느 쪽을 확인하는 것이 더 간편하겠습니까? 몇 개나 될지도 모르는 모든 가문원을 조사해서 특정 캐릭터 플래그를 갖고 있는지 확인하는 것보다는, 현재 플레이어가 캐릭터 플래그를 갖고 있는지 확인하는 편이 훨씬 간편하고 부담이 적겠죠. 그래서 그렇게 하는 것입니다.
실행 부분인 effect = { } 부분은 어려울 게 없습니다. 그냥 플레이어 캐릭터와 후계자로 지정된 캐릭터에게 적절하게 캐릭터 플래그를 붙여주는 것 뿐입니다.
한 가지 팁을 더 드려볼까요. 이미 상속자를 설정했는지 확인하기 위한 구문인, 플레이어 캐릭터에 mod_event_succ_from 캐릭터 플래그가 없음 조건문을 왜 from_potential = { } 에 넣지 않고 allow = { } 에 넣었을까요?
꼭 그래야 하는 이유는 없습니다. 다만 제가 생각하기에, 이 조건을 from_potential = { } 에 넣어버리면 이미 후계자를 설정한 경우에는 디시전 자체가 등장하지 않게 됩니다. 그러면 이게 버그가 나서 안 보이는 건지, 내가 후계자를 지정을 이미 했기 때문에 안 나오는 건지 혼동이 옵니다. allow = { } 에 넣으면 디시전은 보이지만 비활성화되어 실행할 수 없는 형태가 되므로 (그리고 어떤 조건을 만족하지 못하여 실행할 수 없는지 확인되므로) 내가 이미 후계자 지정을 했었다는 걸 알 수 있는 거죠. 즉, 플레이어에게 특정 조건에 대해 확인시켜 줄 가치가 있는 것이라면 from_potential = { } 에 넣기보다는 allow = { } 에 넣어서 조건을 규제하는 것이 낫다는 것입니다. 물론 디시전 출력 관려 측면에선 조건에 안 맞으면 아예 보이지 않는 from_potential = { } 이 더 유용합니다만.
다음으로 이미 지정했던 후계자 캐릭터를 해제하는 디시전인 mod_clr_my_next_heir 디시전을 볼까요?
위와 크게 다르지 않습니다만, 플레이어가 이미 후계자 지정을 했었는지를 확인하는 데 사용되는, 플레이어 측에 설정되는 mod_event_succ_from 캐릭터 플래그를 확인하는 조건문이 from_potential = { } 쪽에 붙어있는 것을 확인할 수 있습니다. (아직 지정하지 않은 상태라면 해제 디시전은 아예 나타나지 않는 것이 자연스럽기 때문입니다. 바로 위의 설명과도 연결되는 문제점이죠.)
potential = { } 내부는 나머지는 다 같고, 이미 상속자로 지정되어 있는 캐릭터에게만 이 디시전이 보여야 하니 mod_event_succ_to 캐릭터 플래그를 검사하는 조건문이 들어 있습니다.
allow = { } 나 effect = { } 내부는 딱히 설명할 게 없네요. 지정했던 후계자를 해제할 것이므로 양쪽 캐릭터에 붙였던 캐릭터 플래그를 모두 해제하여 기존 후계자 캐릭터 지정을 해제하고, 따라서 다른 캐릭터를 지정할 수 있도록 해 주는 것입니다.
어렵지 않죠?
트레잇 하나를 추가하기 위해서는 세 개의 정보가 입력되어야 합니다.
위 세 가지 중 어느 하나라도 부족하면 트레잇이 출력되거나 이용되는 데에 문제가 발생합니다.
하나씩 볼까요? 우선 트레잇 정보 파일을 봅시다.
out_of_succession = { random = no inherit_chance = 0 customizer = no hidden = yes cannot_inherit = yes }
트레잇 모딩을 좀 해보신 분은 어렵지 않은 내용일 겁니다.
random = no 옵션은 이 트레잇이 캐릭터 생성/탄생 과정에서 랜덤으로 붙지 않아야 한다는 것을 의미합니다. 따라서 이 트레잇은 이벤트 등으로 명시적으로 캐릭터에 붙이는 방법으로만 캐릭터에게 부여할 수 있습니다.
inherit_chance = 0 옵션은 이 트레잇은 자녀에게 상속될 수 없음을 의미합니다. 유전 트레잇이 아니라는 거죠.
customizer = no 옵션은 이 트레잇을 룰러 디자이너를 통해 붙일 수 없다는 것을 의미합니다. 이벤트 전용 트레잇이니 이 옵션은 반드시 필요하죠.
hidden = yes 는 낯선 분들도 많으실 겁니다. 2.6에서 추가된 옵션으로, 이 옵션이 붙은 트레잇은 붙어있더라도 캐릭터의 트레잇 정보 창에 보이지 않습니다. 굳이 보일 필요가 없는 트레잇이나, 반드시 숨겨야 하는 트레잇들은 이 옵션을 줄 수 있겠지요.
cannot_inherit = yes 옵션이 핵심이죠. 이 옵션은 이 트레잇을 가진 캐릭터는 상속 순위에서 제척되어야 함을 의미합니다.
트레잇 이미지 파일은 직접 만들었건, 복사를 해 왔건 일단 트레잇과 같은 이름의 .DDS 파일(out_of_succession.dds)로 정위치에 들어있다고 생각하겠습니다.
이제 트레잇 이미지와 트레잇 정보를 연결하는 인터페이스 정보를 입력해야 합니다. (제 가이드에는 트레잇 인덱스 파일이라고 기술했을 텐데, 인터페이스 정보가 좀 더 정확할 것 같아요.) 이미지가 연결되는 인터페이스 정보는 모두 interface 폴더 내에 .gfx 라는 확장자를 가진 텍스트 파일로 저장됩니다. 트레잇 인터페이스 정보를 저장하는 파일을 반드시 어떤 파일명으로 지정해야 한다 같은 제한은 없기 때문에, 임의의 이름으로 새로 만들어 넣으셔도 무방합니다.
spriteTypes = { spriteType = { name = "GFX_trait_out_of_succession" texturefile = "gfx/traits/out_of_succession.dds" noOfFrames = 1 norefcount = yes effectFile = "gfx/FX/buttonstate.lua" } }
이 정보를 입력할 때 주의할 점은, 트레잇은 항상 name 값이 "GFX_trait_트레잇 이름" 의 형태로 입력된다는 것입니다. 또, texturefile 값에는 트레잇 이미지 파일의 위치를 기록해서 트레잇과 트레잇 이미지를 연결해 주는 거고요. 이 두 값이 핵심입니다. 나머지는 그냥 왜 그렇게 쓰는지 알 필요도 없이 똑같이 입력해주면 됩니다.
일단 모두 끝난 것 같죠? 디시전 이름이 코드로 표시되는 것이 좀 보기 흉하다면 언어 파일 작성 정도는 더 할 수도 있겠네요. 물론 언어 파일은 작성 후 변환이 필수입니다.
mod_set_my_next_heir;이 캐릭터를 상속자로 지정하기;;;;;;;x; mod_clr_my_next_heir;이 캐릭터의 상속자 지정 해제하기;;;;;;;x;
오늘의 테스트를 위해서 고대의 신들 시대의 카롤링거 가문이 수고해 주시겠습니다. 오늘의 주인공 서프랑크의 왕 Charles 2세 "대머리"입니다. 보시다시피, 후계자는 아키텐의 왕이자 Charles 2세 왕의 아들인 Louis "말더듬이" 로 설정되어 있지요. 하지만 Charles 2세는, 자신의 작위를 첫째 아들이 아닌 열일곱살의 삼남(이남이는 사망했네요) Carloman 에게 주고 싶습니다.
그래서, 다음과 같이 설정을 합니다.
아무런 변화도 일어나지 않은 것 같죠? 하지만 charinfo 치트를 사용해 보면...
이와 같이 플레이어 캐릭터인 Charles 2세와 상속자로 지정된 캐릭터 Carloman 에게 플래그가 저장된 것을 확인할 수 있습니다. (단, 화면상 상속자는 변경되지 않았습니다. 이 부분은 뒤에서 다시 이야기하겠습니다.)
그냥은 재미 없으니 한 일주일 정도 시간이 흐르게 한 후, 다시 시간을 멈추게 합니다. 그리고 kill 콘솔 명령어를 사용하여 Charles 2세를 죽여 보겠습니다.
눈에 보이는 대로라면 서프랑크의 왕 작위는 Louis 에게 가야 합니다. 그러나...
보시다시피 설정했던 Carloman 이 무사히 작위를 이어받았습니다!
제가 게임을 Pause 상태로 해놓았기 때문에, 사망으로부터 아직 날짜가 진행되지 않은 상태입니다. 아래 화면을 보시면 원래의 상속자였던 Louis 의 캐릭터 정보를 볼 수 있습니다. 트레잇이 6개로, 아직 달려있어야 할 상속 배제 트레잇이 보이지 않습니다.
그러나, 세이브 파일을 열어 보면...
보시다시피 트레잇이 7개입니다. 하나가 숨어 있다는 거죠. 또한, 세이브 파일의 딜레이드 이벤트 항목을 찾아보시면, 1일 후에 Louis 에게 MYSUCC.1002 이벤트가 실행될 예정임을 확인할 수 있습니다.
이제 하루(넉넉잡고 이틀)가 지나고, 다시 세이브 파일을 열어보시면...
이와 같이 트레잇 하나가 사라져 있는 것을 확인하실 수 있습니다. 위의 Delayed Event 에 등록된 MYSUCC.1002 가 실행된 결과겠죠. 제대로 잘 돌아가고 있군요!
이와 같이 지정한 상속자에게 디시전을 호출하면 상속자 설정을 해제할 수 있습니다. 한번 해제한 후에 Charles 2세를 사망시켜 보겠습니다.
원래의 상속자인 Louis 가 작위를 상속받는 것을 확인할 수 있습니다!
프로토타입 테스트 결과는 매우 잘 돌아가고 있는 것처럼 보입니다. 하지만, 위 코드는 프로토타입으로 작성한 단순화된 코드이기 때문에 다음과 같은 잠재적 문제점들이 존재합니다.
만약 설정한 캐릭터가 상속 순위에 들어가지 않는 캐릭터라면(예: 남성 상속제에서 여성에게 상속자를 지정) 게임은 무한 루프를 돌게 될 것입니다. 정확히는 게임 엔진 레벨의 안전 장치로서 일정 이상의 루프를 돌고 나면 자동으로 빠져나가게 되어 있긴 합니다만. 따라서, 상속제에 따라서 상속 가능한 캐릭터만 지정이 가능하도록 할 필요가 있습니다.
같은 의미로, 상속제가 변경되어서 기존에 설정한 지정 상속자가 상속 불가능이 되는 경우 이를 알려주고 상속자를 바꾸도록 할 필요가 있습니다.
만약 지정 상속자가 상속 전에 사망한 경우, 플레이어 캐릭터에 붙어 있는 mod_event_succ_from 이 남아 있기 때문에 새로 지정 상속자를 설정할 수가 없게 됩니다. 따라서, 지정 상속자 지정 후 사망 등으로 지정 상속자가 없게 된 경우, 플레이어의 mod_event_succ_from 캐릭터 플래그를 지워주는 처리가 필요합니다.
지정 상속자를 지정하더라도, 게임 화면상의 후계자가 변경되지는 않습니다. 그도 그럴 것이, 이 방법은 기존 후계자를 변경하지 않고, 사망 순간에 후계자를 바꿔치기하는 방식이기 때문입니다. 이 4번의 경우에는 해결이 불가능합니다. 대신 현재의 후계자가 지정되어 있는지, 있다면 누구인지를 확인할 수 있는 디시전이나 이벤트의 필요성이 있습니다.
이 코드에 존재하는 이와 같은 잠재적 문제점들 - 더 있을 수도 있습니다 - 은 당장 이 문서에서 해결하지는 않을 것입니다. 하나 하나가 상당히 섬세한 작업이 필요하겠네요(당장은 해결 불가능일 수도 있습니다). 독자분들의 연구 과제로 남겨볼까 합니다.
첨부물은 위에서 작성해 본 코드입니다.
첫댓글 정말 감사합니다! 예전에 올려주신 글인 자주빛 상속을 타 팩션에서 구현해주신 모딩을 쓰고 있습니다만, 손자의 대부터 꼬이는 현상이 발생하더군요...ㅠㅠ 그래서 구현해보려고 강의해주신 모딩법 글을 정독했습니다만 제 재주로는 부족하여 하염없이 기다렸는데 드디어 올려주시는군요! 감사히 잘 쓰겠습니다! 모딩 공부도 열심히 하겠습니다 ㅎㅎ
데스포데스랑 비슷하게 만들면 저런 문제점 없이 지정상속이 가능 하지 않을까요??
비잔티움 이외에도 데스퍼트가 동작하던가요?
"상속제의 동작 루틴 자체는 완벽하게 하드 코딩으로 숨겨져 있기 때문이지요."
생각해보니 데스포데스 = 상속제.. ㅠㅠㅠ