파이썬 수행 평가의 TRPG의 간단한 스토리라인을 만들려고 한다. 이걸 참고해서 코딩한다면 알고리즘 정도는 될 것 같다.
스토리라인 짜는데 중요한 것은 아마도 길일 것 같다. 길???
갑자기 吉 (길) 이라고 하면 "나는 대길이 더 좋은데....." 라고 할 수도 있겠지만, 여기서 길이라는 것은 플레이어의 행동에 따라서 얼마나 많은 이벤트를 보여주고 선택을 할 수 있게 하는 것이라고 볼 수 있다. 만약에 스토리를 이어나가는데 플레이어 적과 싸우다가 HP가 다 떨어졌을 때 "플레이어가 죽었다" 라는 것보다, "플레이어는 쓰러지면서 점점 시야가 어두워진다... 어두워지는 시야로 누군가 나타나서 적을 단칼에 베었다." 이렇게 하는 것이 스토리를 이어나가고 개연성을 만드는 작업이다. 절대 "용사님은 사망했습니다." 라는 멘트를 보이지마라 게임이 그렇게 끝났을 때 이미 플레이어의 흥미도 끝이 났을 것이다.( 길을 걸어가다가 길이 끝나는 느낌, 플레이어 입장에서는 다른 안 끊어진 길을 찾아서 다시 가거나, 어디로 가야하지 하거나, 길을 걷는 걸 멈추고 만다.) 아까의 예시는 플레이어 앞에 새로운 캐릭터의 등장을 예고해서 기대를 하게하는 점이 있다나 없다나~
한 가지의 선택지를 정하고 간다면 어떻게 될까? 만약에 플레이 할 수 있는게 "대검전사" 하나라고 생각해보자. 어떤 사람은 "흠.... 솔직히...히... 대검전사는 힘만 믿고 싸우는 방식이 무식하다고 생각해요.... 그럴 바에는 "마법사" 멋진 마술이 좀 더 재밌을 것 같아요..." 라고 할지도 모르는 일이다. 그러니 선택지를 "대검전사", "마법사", "도적"으로 정할려고 한다.
앞에서부터 대검전사는 높은 공방체을 활용해서 적의 공격을 버텨가면서 크게 한방 먹이는 플레이라면, 도적의 플레이는 이와 정반대로 높은 민첩과 지능을 바탕으로 다양한 공격수단이랑 약하지만 빠르고 날카로운 공격으로 상대에게 출혈을 일으켜서 상대를 한다. 마법사는 높은 지능과 매력으로 상대에게 매혹을 부여해서 공격타임을 많이 가지고 오는 플레이이다.
미르친 스토리 짜는데 이걸 3개의 선택을 주면 엄청 정말 야바이하게 힘들다는 걸 알아버렸다. 그래서 대검전사 하나의 선택지로 하는 것이 정베일 뜻;;;;;;;;
각각의 캐릭터마다의 스토리가 있다고하면, 스토리를 다 하나씩을 짜기에는 가능할려면 가능하겠다. But 코드를 짤 때마다 다른 구조의 전투를 캐릭터마다 부여하고하면 할 일이 너무 많다. 그래서 스토리라인은 다르게 해도 적의 배치, 보스의 등장, 패배 시의 상황 같은 것은 같게 할 예정이다.
전투를 이어가기 전에 데미지 계산과 회피 판정은 아래와 같이 정한다.
공격력 & 지능 // 2 - 방어력 // 4을 데미지로 판정한다.
회피 판정은 민첩의 최대 수치만큼의 주사위를 굴린 후에 자신의 민첩 수치의 이하의 수가 나오면 회피 성공이다. 이외는 실패이다.
대검전사를 선택 시에의 이벤트이다.
체력 : 27 / 최대치 40
공격력 : 16 / 최대치 24
방어력 : 16 / 최대치 18
민첩 : 11 / 최대치 18
매력 : 11 / 최대치 18
지능 : 9 / 최대치 18
체력 35 / 최대치 50
공격력 22 / 최대치 30
방어력 12 / 최대치 25
민첩 18 / 최대치 25
지능 15 / 최대치 25
다양한 스킬이 있으면 더욱이 재미있을 듯, 초반에 스킬을 4개 정도 주고 그거에 따라서 공격력의 추가나 부가효과 등을 하면 더욱이 전투가 풍부해질 것 같다. 또한 보스의 전투 스킬도 설정해두면 좋을 것 같다.
하지만 개발을 하지도 않는 내가 저걸 설정할려면 너무 어려워 보이고 해서 코드 짜는 얘들이 정해야 할 뜻......;;;;;;;;;
웃는 사신은 "OOO"의 격렬한 검신에 치명적인 일격을 맞고 쓰러졌다.
사신은 쓰러지면서 다음의 싸움을 예견하듯 몸을 어둠 속에 숨기면서, 말을 건다. 이건 유흥에 불과해..... 다음에 잘난 네 놈이라도 동료를 원하면서 절망에 빠지게 될 것이다............
"OOO" : 비겁한 자식 도망치기냐!!!!
그리고 난 동료를 다시는 만들지 않아... 나 혼자서 아무도 위험하지 않게 하겠어.....!
웃는 사신은 사라지면서 하나의 물건을 남기고 갔다.
검은 등각나무의 반지를 남기고 갔다.
"OOO" : 이건 반지인가.... 기분 나쁜 물건이지만 그녀석을 만났을 때 유용하게 쓸 수 있을 것 같군...
실패 시의 루트) 영혼을 빼앗기고 되고 웃는 사신은 더욱이 크게 웃으면서 자신의 몸을 부풀리면서 말을 건다. "이..... 아름다운 영혼은 내가 아주.... 잘 써주지 ㅋㅋㅋㅋ"
"OOO"는 이말을 끝으로 웃는 사신의 능력으로 인해 공허로 떨어지게 된다.
이...... 개자식 너를 꼭 죽여주겠어...!
import random
import time
# ----------------------------------------------------------------------------------------------------------------------
# 기본 함수
def wait():
time.sleep(2)
def line():
print("\n" + "-" * 100 + "\n")
# ----------------------------------------------------------------------------------------------------------------------
# 캐릭터 생성
name = input("이름을 입력해주세요 : ")
gender = input("성별을 입력해주세요 : ")
line()
print(f"{name}의 정보")
print(f"성별 : {gender}")
player = {
"체력": 30,
"최대체력": 40,
"공격력": 16,
"방어력": 18,
"민첩": 11,
"매력": 11,
"지능": 9,
"광폭화": False
}
print(f"체력 : {player['체력']} / 최대치 {player['최대체력']}")
print(f"공격력 : {player['공격력']} / 최대치 24")
print(f"방어력 : {player['방어력']} / 최대치 18")
print(f"민첩 : {player['민첩']} / 최대치 18")
print(f"매력 : {player['매력']} / 최대치 18")
print(f"지능 : {player['지능']} / 최대치 18")
line()
# ----------------------------------------------------------------------------------------------------------------------
# 웃는 사신 등장
print(f"{name}은 웃는 사신을 조우하고 크게 적대감을 보이며 검을 뽑았다.")
wait()
boss = {
"이름": "웃는 사신",
"체력": 35,
"최대체력": 50,
"공격력": 22,
"방어력": 12,
"민첩": 18,
"지능": 15
}
print("\n웃는 사신의 정보")
print(f"체력 : {boss['체력']} / 최대치 {boss['최대체력']}")
print(f"공격력 : {boss['공격력']} / 최대치 30")
print(f"방어력 : {boss['방어력']} / 최대치 25")
print(f"민첩 : {boss['민첩']} / 최대치 25")
print(f"지능 : {boss['지능']} / 최대치 25")
line()
# ----------------------------------------------------------------------------------------------------------------------
# 스킬
skills = {
"1": {
"이름": "강격",
"배율": 1.5,
"설명": "강한 일격"
},
"2": {
"이름": "연속 베기",
"배율": 1.2,
"설명": "빠르게 베어낸다"
},
"3": {
"이름": "방어 태세",
"배율": 0.7,
"설명": "피해를 줄이며 공격"
},
"4": {
"이름": "필사의 참격",
"배율": 2.0,
"설명": "모든 힘을 담은 공격"
}
}
# ----------------------------------------------------------------------------------------------------------------------
# 회피 판정
def dodge(character):
dice = random.randint(1, character["민첩"])
if dice <= character["민첩"] // 2:
return True
return False
# ----------------------------------------------------------------------------------------------------------------------
# 데미지 계산
def damage(attacker, defender, multiplier=1):
dmg = ((attacker["공격력"] + attacker["지능"]) // 2) - (defender["방어력"] // 4)
dmg = int(dmg * multiplier)
if dmg <= 0:
dmg = 1
return dmg
# ----------------------------------------------------------------------------------------------------------------------
# 전투 시작
phase_two = False
while player["체력"] > 0 and boss["체력"] > 0:
line()
print(f"{name} 체력 : {player['체력']} / {player['최대체력']}")
print(f"웃는 사신 체력 : {boss['체력']} / {boss['최대체력']}")
line()
print("스킬 목록")
for key, skill in skills.items():
print(f"{key}. {skill['이름']} - {skill['설명']}")
skill_choice = input("\n사용할 스킬 번호 : ")
if skill_choice not in skills:
print("잘못 입력했습니다.")
continue
skill = skills[skill_choice]
line()
print(f"{name}은(는) {skill['이름']}을 사용했다!")
wait()
# 플레이어 공격
if dodge(boss):
print("웃는 사신이 공격을 회피했다!")
else:
dmg = damage(player, boss, skill["배율"])
boss["체력"] -= dmg
print(f"웃는 사신에게 {dmg}의 피해!")
wait()
# 광폭화 효과
if player["광폭화"]:
if random.randint(1, 100) <= 10:
player["체력"] -= 2
print("광폭화의 부작용으로 자신의 몸을 상처입혔다...!")
heal = player["공격력"] // 4
player["체력"] += heal
if player["체력"] > player["최대체력"]:
player["체력"] = player["최대체력"]
print(f"광폭화의 힘으로 {heal}의 체력을 회복했다.")
# ------------------------------------------------------------------------------------------------------------------
# 웃는 사신 필살기
if boss["체력"] <= 10 and phase_two is False:
phase_two = True
line()
print("웃는 사신이 광기 어린 웃음을 터뜨린다.")
print('"아하하하하하하하하!!!!"')
wait()
print("검은 안개가 주변을 뒤덮는다...")
wait()
print("\n웃는 사신이 필살기를 사용한다!")
wait()
# 회피 성공
if dodge(player):
print(f"{name}은(는) 필살기를 회피했다!")
wait()
print(f"{name}은(는) 빈틈을 놓치지 않고 검을 휘둘렀다!")
boss["체력"] = 0
break
# 회피 실패
else:
half = player["체력"] // 2
player["체력"] -= half
print(f"{name}은(는) 필살기에 맞아 {half}의 피해를 입었다!")
wait()
# 체력이 0 이하일 경우
if player["체력"] <= 0:
print("웃는 사신은 더욱 크게 웃기 시작한다.")
wait()
print('"이..... 아름다운 영혼은 내가 아주.... 잘 써주지 ㅋㅋㅋㅋ"')
player["공격력"] += 4
player["광폭화"] = True
print("\n광폭화 상태가 되었다...")
print("공격력 +4")
wait()
print(f'\n{name} : "이...... 개자식 너를 꼭 죽여주겠어...!"')
wait()
print(f"{name}은(는) 공허 속으로 떨어졌다...")
break
# 살아남았을 경우
else:
print(f"{name}은(는) 간신히 살아남았다!")
wait()
print("웃는 사신은 비틀거리며 웃는다.")
# ------------------------------------------------------------------------------------------------------------------
# 보스 공격
if boss["체력"] > 0:
line()
print("웃는 사신이 공격한다!")
wait()
if dodge(player):
print(f"{name}은(는) 공격을 회피했다!")
else:
boss_dmg = damage(boss, player)
player["체력"] -= boss_dmg
print(f"{name}은(는) {boss_dmg}의 피해를 입었다!")
wait()
# ----------------------------------------------------------------------------------------------------------------------
# 엔딩
line()
# 승리 루트
if boss["체력"] <= 0:
print("웃는 사신은 격렬한 검격에 치명상을 입고 쓰러졌다.")
wait()
print('웃는 사신 : "이건 유흥에 불과해....."')
print('"다음에 잘난 네 놈이라도 동료를 원하면서 절망에 빠지게 될 것이다............"')
wait()
print(f'\n{name} : "비겁한 자식 도망치기냐!!!!"')
print('"그리고 난 동료를 다시는 만들지 않아..."')
print('"나 혼자서 아무도 위험하지 않게 하겠어.....!"')
wait()
print("\n웃는 사신은 어둠 속으로 사라졌다.")
wait()
print("\n검은 등각나무의 반지를 얻었다.")
line()
print("3갈래의 길이 나타났다.")
print("""
1. 검은 숲
2. 버려진 성당
3. 심연의 협곡
""")
route = input("갈 길을 선택하세요 : ")
if route == "1":
print("\n검은 숲으로 향합니다...")
elif route == "2":
print("\n버려진 성당으로 향합니다...")
elif route == "3":
print("\n심연의 협곡으로 향합니다...")
else:
print("\n길을 선택하지 못했다...")
# 패배 루트
elif player["체력"] <= 0:
print(f"{name}은(는) 공허 속으로 떨어졌다...")
print("광폭화의 힘만이 몸을 지탱하고 있다...")
line()
print("스토리 종료")