씨앗 농부의 하수인/FfH2 자료/개조 관련

Fall from Heaven 2 개조일람 -11-

죠니 사사키 2011. 10. 24. 16:00
유닛은 거의 항상 생산을 통해 조달하지만, 그렇지 않은 경우가 왕왕 있지.

가장 흔한 경우는 역시 소환. 특히 영구 소환의 경우 실질적으로 기간병 역할을 해 줄 수도 있고.
물론 성능 자체는 제한 시간이 있는 소환수들이 더 좋지만, 급할 때가 아니면 매턴 불러놓기 귀찮다 보니...
게다가 영구 소환수를 미리 불러 놓는다고 일반 소환수를 못 부르는 것도 아니니까.

그 다음으로 보는 경우는 역시 턴랙;;을 소모하면서 자동 충원되는 병력...
성전하의 의용병이나, 차원을 넘어;; 소환(정확히는 생성)되거나, 환생하거나...
그런데 이런 경우는 특정 세력만 되는 거지. 배너, 쉐아임, 인페르날, 머큐리안...
게다가 로또성이 있어. 정도의 차이는 있지만 100% 내가 통제를 할 수 있는 것이 아니야.

그리고 남는 것이...용병이지.
용병은 모든 세력에서 고용이 가능하기는 한데...특정 불가사의(구인 길드)가 있어야만 가능하지. 물론 고용이니까 돈만 들고...
히푸스만 기마 용병을 뽑고 나머지는 그냥 용병인데, 어쨌든 막굴려도 된다는 장점이 있지. 자국민들이 전사자에 대해 애도를 안하거든;;
 기마 용병은 비교대상인 이륜전차보다 확실히 좋아. 방어력이 좋잖아.
 그냥 용병은 투사급 유닛과 비교가 되는데...의용병 능력치라 나쁜 것은 아니지만 아무래도 투사보다 구려서...

그런데...몇몇 세력의 경우, 단순히 그냥 용병을 데려오면 설정에서 살짝 벗어나는 것 같지 않아?
예를 들면, 쉐아임의 경우 투사급 이상의 백병 계열은 아예 생산이고 전직이고 불가능하단 말이야?
그런데 용병은 3티어 백병 유닛이거든. 전용 용병이 있어서 바꿔주는 것도 아니고...구인 길드만 지어 놓으면 그냥 쓸 수 있어.
러취프도 그렇지. 걔들은 아예 도끼병 때부터 백병 계열이 없고 죄다 골렘인데...

아니, 왜 꼭 용병만 구입해야 해? 다른거 주면 안돼?
오늘의 주제는 바로 이것이다.

단골 손님인 CvSpellInterface.py를 열어보자. 그리고 def reqRecruitMercenary(caster):를 찾아.
관련된 함수는 두개가 있다. 간단히 설명까지 써볼까...
def reqRecruitMercenary(caster): ←주문의 필요 조건
pPlayer = gc.getPlayer(caster.getOwner()) ←플레이어를 구하는 함수
if pPlayer.isHuman() == False: ←만일 해당 플레이어가 AI라면
pPlot = caster.plot() ←술자의 좌표 확보인데, 역할이 없는...쓸데 없는 줄이야. 예전에 다른 구문이 있었던 흔적일 수 있다.
if pPlayer.getCivilizationType() == gc.getInfoTypeForString('CIVILIZATION_KHAZAD'): ←카자드일 경우
return False ←사용 불가. 한마디로 AI가 조종하는 카자드 세력은 이 주문을 안 쓴다는 소리
iX = caster.getX() ←좌표의 x값
iY = caster.getY() ←좌표의 y값
eTeam = gc.getTeam(pPlayer.getTeam()) ←팀 구하기. 기술 습득 여부나 교전 여부 등에 쓰는데, 여기선 후자네.
for iiX in range(iX-1, iX+2, 1): ←x값의 전후 1만큼 반복. x좌표가 10이었다면 9~11이라는 소리지.
for iiY in range(iY-1, iY+2, 1): ←마찬가지로 y값에 대해. 즉 위와 합쳐 술자의 반경1 이내에 대해 판정
pPlot = CyMap().plot(iiX,iiY) ←타일 지정. 아홉번 하겠지.
for i in range(pPlot.getNumUnits()): ←해당 타일의 모든 유닛에 대해,
pUnit = pPlot.getUnit(i) ←일단 유닛을 하나 고르고
p2Player = gc.getPlayer(pUnit.getOwner()) ←그 유닛의 소유자가 누구인지 찾고
e2Team = p2Player.getTeam() ←그 소유자의 팀을 찾아서
if eTeam.isAtWar(e2Team) == True: ←두 팀간에 전쟁중이라면,
return True ←사용 가능. 한마디로, AI가 조종하는 유닛의 경우 반경 1이내에 적이 하나라도 보이면 쓴다는 거야.
return False ←사용 불가. 그런 경우가 아니면 AI는 절대 사용하지 않는다는 것이지. 적 근처도 아닌데 용병 고용해서 쟁여놓지 않는다는 뜻
return True ←사용 가능. 물론 인간에게는 그럴 필요가 없지. 알아서 쓸터이니.

def spellRecruitMercenary(caster): ←주문 실행시 결과
pPlayer = gc.getPlayer(caster.getOwner()) ←플레이어 찾기
iUnit = gc.getInfoTypeForString('UNITCLASS_MERCENARY') ←용병 유닛의 클래스 설정
infoCiv = gc.getCivilizationInfo(pPlayer.getCivilizationType()) ←플레이어의 문명 구하기. 보통 대체/고유 유닛 판정을 위해 쓰지.
iUnit = infoCiv.getCivilizationUnits(iUnit) ←해당 클래스를 각 문명에 맞는 유닛으로 변환. 즉 이 과정은 히푸스를 위한...
newUnit = pPlayer.initUnit(iUnit, caster.getX(), caster.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH) ←유닛(용병) 생성. 거의 관용어구 급...
newUnit.finishMoves() ←생성된 유닛의 이동 종료. 이게 없으면? 히푸스의 경우 이동 끝낸 기병대가 기마 용병을 불러서 또 밀겠지...;;
newUnit.setHasCasted(True) ←생성된 유닛의 주문 사용 여부를 사용함으로 설정. 이게 없으면? 용병이 용병을 부르는 대참사--;;;; 네버엔딩스토~리~
if caster.getUnitType() == gc.getInfoTypeForString('UNIT_MAGNADINE'): ←술자가 마그나딘인 경우,
newUnit.setHasPromotion(gc.getInfoTypeForString('PROMOTION_LOYALTY'), True) ←불려온 기마 용병은 충성 승급 획득

과도하게 친절하다고...? 아니 반대로 그간 너무 불친절하지 않았나 싶어서...
자 고쳐볼 것이...조건은 놔두고, 결과만 바꿔보자고. 사실 조건은 사람이 쓰는 경우에는 아예 하나도 없었잖아.
실제로는 구인 길드가 필요하다는 조건은 XML에서 판정이 나므로 python단계에서는 AI에 대해서만 해준거지.
고칠 문명은 일단 둘...인데,
 쉐아임의 경우, 용병 대신 폭좀;;이 나오게 하고,
 러취프의 경우, 철골렘이 대신 나오게 해보자.(철골렘이 투사 위치에 있잖아)
덤으로...발제라프의 경우는 노예가 나오도록 하겠어.
 왜냐면...그냥 두면 용병이 나오는데, 발제라프 특성상 용병을 죄다 모아서 가마솥 있는 도시의 투기장에 집어 넣으면...용병이 전부 투사가 되는 범죄가...--;;;;
 애당초 발제라프가 사기를 치는 것이 한두개가 아니라서 그래. 그놈의 투기장만 아니었어도...;;
 왜 노예를 주느냐고? 노예 특화 문명이잖아. 게다가 투기장에서 죽으면 이득이 안될 수도 있으니...
자 고친 부분은 당연히 붉은 색으로...
def spellRecruitMercenary(caster):
pPlayer = gc.getPlayer(caster.getOwner())
iUnit = gc.getInfoTypeForString('UNITCLASS_MERCENARY')
infoCiv = gc.getCivilizationInfo(pPlayer.getCivilizationType())
iUnit = infoCiv.getCivilizationUnits(iUnit)
iRace = -1
if pPlayer.getCivilizationType() == gc.getInfoTypeForString('CIVILIZATION_SHEAIM'):
iUnit = gc.getInfoTypeForString('UNIT_PYRE_ZOMBIE')
elif pPlayer.getCivilizationType() == gc.getInfoTypeForString('CIVILIZATION_LUCHUIRP'):
iUnit = gc.getInfoTypeForString('UNIT_IRON_GOLEM')
elif pPlayer.getCivilizationType() == gc.getInfoTypeForString('CIVILIZATION_BALSERAPHS'):
iUnit = gc.getInfoTypeForString('UNIT_SLAVE')
iRnd = CyGame().getSorenRandNum(20, "Slave")
if (iRnd >= 10 and iRnd < 13):
iRace = gc.getInfoTypeForString('PROMOTION_DWARF')
if (iRnd >= 13 and iRnd < 15):
iRace = gc.getInfoTypeForString('PROMOTION_ELF')
if (iRnd >= 15 and iRnd < 17):
iRace = gc.getInfoTypeForString('PROMOTION_DARK_ELF')
if (iRnd >= 17):
iRace = gc.getInfoTypeForString('PROMOTION_ORC')
newUnit = pPlayer.initUnit(iUnit, caster.getX(), caster.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
newUnit.finishMoves()
newUnit.setHasCasted(True)
if caster.getUnitType() == gc.getInfoTypeForString('UNIT_MAGNADINE'):
newUnit.setHasPromotion(gc.getInfoTypeForString('PROMOTION_LOYALTY'), True)
if iRace != -1:
newUnit.setHasPromotion(iRace, True)

설명은 뭐 예고한 대로고...노예를 주는 구문(발제라프용)은 많이 확장이 되었는데,
쉐아임이나 러취프의 예처럼 두면, 인간 노예만 얻게 되잖아. 물론 인간 노예를 얻을 비율이 원래 높지. 아인종 세력이 얼마 안되니까.
그래서 반은 인간 노예가 나오고...나머지는 15%/10%/10%/15%확률로 드워프/엘프/다크 엘프/오크 노예를 얻게 한거야.


더 짧게 줄여볼 수도 있는데, 일단 있던 것에서 잘라다 붙인거라...흠 좀 비효율적으로 보이기도 하겠네.
사실 발제라프는 이렇게 해놔도 꼼수가 있지...정신병원이 있을 경우 노예를 그리 데려가서 미치광이로 전직시킨 다음 투기장에다 밀어넣으면...역시 깔끔하게 투사가 된당;;
소용 없는 짓이라고 생각되면 발제라프 부분은 빼거나, 반대로 페널티를 줄 수 있겠지^^ 용병으로 그냥 두되, 무조건(확률로?) 미친/돌연변이 승급을 얻게 한다든지...
뭐 이 이상은 각자의 응용 영역으로 남겨둘까?