import abc, collections, enum, numbers, os, re, math, thing, natuum.util, natuum.cutscene, natuum.relationship, natuum.gameClass
from natuum.gameClass.common import CarveResultEnum
logQuest = os.environ.get('NT_QUEST_LOG', '0') != '0'
if logQuest:
if not __final__:
LOG_DEBUG = natuum.getLogger('quest').debug
LOG_INFO = natuum.getLogger('quest').info
else:
def LOG_DEBUG(*args):
return True
QUEST_STATE_KEY_STATE = 'state'
QUEST_STATE_KEY_PENDING_NEXT_STATE = 'pendingNextState'
QUEST_STATE_KEY_RESULT_STATE = 'resultState'
QUEST_STATE_KEY_INSTANCE_ID = 'instanceID'
QUEST_STATE_KEY_VARIABLES = 'variables'
QUEST_STATE_KEY_TOKEN_ID = 'tokenID'
QUEST_STATE_KEY_PATTERN_QUEST_KEY = 'patternQuestKey'
QUEST_STATE_KEY_QUEST_GIVER_UID = 'questGiverUID'
QUEST_STATE_KEY_FORWARD_REVERTIBLE = 'forwardRevertible'
QUEST_STATE_KEY_BACKWARD_REVERTIBLE = 'backwardRevertible'
QUEST_STATE_KEY_TRIGGERED_FORWARD_INDEX = 'triggeredForwardIndex'
QUEST_STATE_KEY_PLAYED_COMPLETE_CUTSCENE = 'playedCompleteCutscene'
QUEST_STATE_KEY_REWARD_APPLIED = 'rewardApplied'
QUEST_STATE_CHANGE_TIMESTAMP = 'changedTime'
QUEST_STATE_KEY_SELECTED = 'selected'
QUEST_VAR_ELAPSED_TIME = 'elapsedTime'
QUEST_VAR_FINISH_TIME = 'finishTime'
QUEST_VAR_KEY_COUNT = 'count'
QUEST_VAR_KEY_TARGET_CLASS_ID = 'targetClassID'
QUEST_VAR_KEY_TARGET_COUNT = 'targetCount'
SYSTEM_MESSAGE_ID_ITEM_TURNED_IN = 'Quest_msg_item_turned_in'
_STRING_TRIGGER_TYPE_CONVERSATION = 'conversation'
_STRING_TRIGGER_TYPE_PROXIMITY_ENTER = 'enter'
_STRING_TRIGGER_TYPE_KILL_COUNT = 'killCount'
_STRING_TRIGGER_TYPE_ITEM_COUNT = 'itemCount'
_STRING_TRIGGER_TYPE_RANDOM_KILL_COUNT = 'randomKillCount'
_STRING_TRIGGER_TYPE_RANDOM_ITEM_COUNT = 'randomItemCount'
_STRING_TRIGGER_TYPE_TIMER = 'timer'
_STRING_TRIGGER_TYPE_SELECTION = 'selection'
_STRING_TRIGGER_TYPE_GUARDIAN_CHANGED = 'guardianChanged'
_STRING_TRIGGER_TYPE_PROPERTY = 'property'
_STRING_TRIGGER_TYPE_GUARDIAN_PROPERTY = 'guardianProperty'
_STRING_TRIGGER_TYPE_CONTRACT = 'contract'
_STRING_TRIGGER_TYPE_HAND = 'hand'
_STRING_TRIGGER_TYPE_DECK = 'deck'
_STRING_TRIGGER_TYPE_ACTION = 'action'
_STRING_TRIGGER_TYPE_RELATIONSHIP = 'relationship'
_STRING_TRIGGER_TYPE_DINE = 'dine'
TutorialQuestsForStats = {v:i + 2000 for i, v in enumerate(('QDtutorial', 'QDtutorial2',
'QDtutorial3', 'QDtutorial3_1',
'QDtutorial4', 'QDtutorial4_1',
'QDtutorial4_2', 'QDtutorial5',
'QDtutorial5_1', 'QDtutorial6',
'QDtutorial7'))}
MainQuestsForStats = {v:i + 3000 for i, v in enumerate(('CB1_QMR1000', 'CB1_QMR1001',
'CB1_QMR1002', 'CB1_QMR1004',
'CB1_QMR1003', 'CB1_QMR1005',
'CB1_QMR1006', 'CB1_QMR1007',
'CB1_QMR1008', 'CB1_QMR1009',
'CB1_QMR1042', 'CB1_QMR1010',
'CB1_QMR1011', 'CB1_QMR1066',
'CB1_QMR1067', 'CB1_QMR1068',
'CB1_QMR1012', 'CB1_QMR1013',
'CB1_QMR1014', 'CB1_QMR1015',
'CB1_QMR1017', 'CB1_QMR1016',
'CB1_QMR1018', 'CB1_QMR1019',
'CB1_QMR1020', 'CB1_QMR1021',
'CB1_QMR1022', 'CB1_QMR1023',
'CB1_QMR1043', 'CB1_QMR1024',
'CB1_QMR1025', 'CB1_QMR1060',
'CB1_QMR1061', 'CB1_QMR1062',
'CB1_QMR1026', 'CB1_QMR1027',
'CB1_QMR1028', 'CB1_QMR1029',
'CB1_QMR1031', 'CB1_QMR1030',
'CB1_QMR1032', 'CB1_QMR1033',
'CB1_QMR1034', 'CB1_QMR1035',
'CB1_QMR1036', 'CB1_QMR1037',
'CB1_QMR1044', 'CB1_QMR1038',
'CB1_QMR1039', 'CB1_QMR1063',
'CB1_QMR1064', 'CB1_QMR1065',
'CB1_QMR1040', 'CB1_QMR1041',
'CB1_QS1121', 'CB1_QS1122',
'CB1_QS1123', 'CB1_QS1204',
'CB1_QS1207', 'CB1_QS1208',
'CB1_QS1213', 'CB1_QS1137',
'CB1_QS1138', 'CB1_QS1139',
'CB1_QS1205', 'CB1_QS1209',
'CB1_QS1210', 'CB1_QS1215',
'CB1_QS1140', 'CB1_QS1141',
'CB1_QS1142', 'CB1_QS1206',
'CB1_QS1211', 'CB1_QS1212',
'CB1_QS1217'))}
MainQuestsForStats.update(TutorialQuestsForStats)
class ReportTargetType(enum.IntEnum):
All = 0
QuestGiver = 1
@staticmethod
def convert(value: 'str or None') -> 'ReportTargetType':
if value is None or (value == natuum.quest.ReportTargetType.All.name):
return natuum.quest.ReportTargetType.All
return natuum.quest.ReportTargetType.QuestGiver
class QuestType(enum.IntEnum):
MainQuest = 0
SubQuest = 1
DailyQuest = 2
GroupQuest = 3
@staticmethod
def convert(questType: 'str or None') -> 'QuestType':
if questType is None or (questType == natuum.quest.QuestType.MainQuest.name):
return natuum.quest.QuestType.MainQuest
if questType == natuum.quest.QuestType.SubQuest.name:
return natuum.quest.QuestType.SubQuest
if questType == natuum.quest.QuestType.DailyQuest.name:
return natuum.quest.QuestType.DailyQuest
return natuum.quest.QuestType.GroupQuest
class CountTypes(enum.Enum):
LessThan = 'lessThan'
GreaterThanOrEqualTo = 'greaterThanOrEqualTo'
@staticmethod
def convert(countType: 'None or str') -> 'CountTypes':
if countType is None:
return CountTypes.GreaterThanOrEqualTo
return CountTypes(countType)
class ComparisonTypes(enum.Enum):
EQUAL = '=='
NOT_EQUAL = '!='
GREATER_THAN = '>'
GREATER_THAN_OR_EQUAL_TO = '>='
LESS_THAN = '<'
LESS_THAN_OR_EQUAL_TO = '<='
CONTAINS = 'contains'
IN = 'in'
RANGE = 'range'
[USER=437263]clas[/USER]smethod
def compare(cls, compType: 'ComparisonTypes', value: 'var', target: 'var'):
if cls.CONTAINS == compType:
return target in value
if cls.IN == compType:
return value in target
if cls.RANGE == compType:
return target[0] <= value and target[1] > value
if cls.EQUAL == compType:
return value == target
if cls.NOT_EQUAL == compType:
return value != target
if cls.GREATER_THAN == compType:
return value > target
if cls.GREATER_THAN_OR_EQUAL_TO == compType:
return value >= target
if cls.LESS_THAN == compType:
return value < target
if cls.LESS_THAN_OR_EQUAL_TO == compType:
return value <= target
raise ValueError('invalid comparison type : {}'.format(compType))
class QuantifierTypes(enum.Enum):
FOR_ALL = 'forAll'
THERE_EXSITS = 'thereExists'
class ActionRoleTypes(enum.Enum):
SUBJECT = 'subject'
OBJECT = 'object'
INDIRECT_OBJECT = 'indirectObject'
class MarkerTypes(enum.Enum):
EMPHASIS = '강조 환상'
MOVE = '이동'
STARTABLE = '시작 가능'
UNSTARTABLE = '시작 불가능'
ACTIVE = '진행 중'
COMPLETE = '완료 가능'
REQUEST_SELECTION = '선택 요청 환상'
@staticmethod
def isQuestStateMarker(markerType: 'MarkerTypes'):
return MarkerTypes.STARTABLE == markerType or MarkerTypes.UNSTARTABLE == markerType or MarkerTypes.ACTIVE == markerType or MarkerTypes.COMPLETE == markerType
class UIStates(enum.Enum):
DEFAULT = 'Default'
ACTIVE_COMPLETED = 'ActiveCompleted'
ACTIVE_FAILED = 'ActiveFailed'
INACTIVE_FINISHED = 'InactiveFinished'
INACTIVE_NOT_STARTED = 'InactiveNotStarted'
[USER=437263]clas[/USER]smethod
def isActiveQuestState(cls, uiStates: 'UIStates') -> 'bool':
return cls.DEFAULT == uiStates or cls.ACTIVE_COMPLETED == uiStates or cls.ACTIVE_FAILED == uiStates
[USER=437263]clas[/USER]smethod
def isDetailedActiveQuestState(cls, uiStates: 'UIStates') -> 'bool':
return cls.ACTIVE_COMPLETED == uiStates or cls.ACTIVE_FAILED == uiStates
[USER=437263]clas[/USER]smethod
def iterateActiveStates(cls) -> 'generator of UIStates':
for state in cls:
if cls.isActiveQuestState(state):
yield state
[USER=437263]clas[/USER]smethod
def convertToUIStateMap(cls, stateMap: 'collections.Mapping', finished: 'collections.Sequence') -> 'collections.Mapping':
uiStateMap = dict()
for questID, subMap in stateMap.items():
uiState = cls.getUIState(subMap)
if uiState is None:
continue
else:
uiStateMap[questID] = uiState
for questID in finished:
uiStateMap[questID] = cls.INACTIVE_FINISHED
return uiStateMap
[USER=437263]clas[/USER]smethod
def toDescString(cls, value: 'UIStates') -> 'str':
if cls.DEFAULT == value:
return '기본'
if cls.ACTIVE_COMPLETED == value:
return '진행(조건 만족 상태)'
if cls.ACTIVE_FAILED == value:
return '진행(실패 상태)'
if cls.INACTIVE_FINISHED == value:
return '완료'
if cls.INACTIVE_NOT_STARTED == value:
return '시작 전'
return ''
[USER=437263]clas[/USER]smethod
def getUIState(cls, subMap: 'collections.Mapping') -> 'None or UIStates':
try:
state = subMap[QUEST_STATE_KEY_STATE]
except KeyError:
return
else:
uiState = cls._translate(QuestStates(state))
return uiState
[USER=437263]clas[/USER]smethod
def _translate(cls, state: 'QuestStates') -> 'None or UIStates':
if QuestStates.COMPLETED == state or (QuestStates.FINISHED_CUTSCENE == state or QuestStates.FINISHED == state):
return cls.ACTIVE_COMPLETED
if QuestStates.ACCEPTED == state or (QuestStates.WATCH == state):
return cls.DEFAULT
if QuestStates.FAILED == state:
return cls.ACTIVE_FAILED
class MarkerStates(enum.Enum):
ALL = 'All'
ACCEPTED = 'Accepted'
COMPLETED = 'Completed'
FAILED = 'Failed'
[USER=437263]clas[/USER]smethod
def _translate(cls, state: 'MarkerStates') -> 'None or UIStates':
if cls.ACCEPTED == state:
return UIStates.DEFAULT
if cls.COMPLETED == state:
return UIStates.ACTIVE_COMPLETED
if cls.FAILED == state:
return UIStates.ACTIVE_FAILED
class UIStateBoundInfo:
def __init__(self, rawMap: 'collections.Mapping'):
self._UIStateBoundInfo__infoMap = dict()
for key, value in rawMap.items():
self._UIStateBoundInfo__infoMap[UIStates(key)] = value
def getValue(self, uiState: 'UIStates') -> 'None or var':
if uiState in self._UIStateBoundInfo__infoMap:
return self._UIStateBoundInfo__infoMap[uiState]
return self._UIStateBoundInfo__infoMap.get(UIStates.DEFAULT)
def ensureType(self, expectedType: 'var'):
for value in self._UIStateBoundInfo__infoMap.values():
if not isinstance(value, expectedType):
msg = 'invalid value : {}, expected type : {}'
raise ValueError(msg.format(value, expectedType))
def ensureStates(self, checkActive: 'bool'=False, isNotDetailed: 'bool'=False):
if checkActive:
for state in self._UIStateBoundInfo__infoMap.keys():
if not UIStates.isActiveQuestState(state):
msg = 'invalid state : {}, expected active ui state'
raise ValueError(msg.format(state))
if isNotDetailed:
for state in self._UIStateBoundInfo__infoMap.keys():
if UIStates.isDetailedActiveQuestState(state):
msg = 'invalid state : {}, unexpected ui state'
raise ValueError(msg.format(state))
_UIStateBoundInfo__infoMap = None
class UIInfo:
StateInfo = collections.namedtuple('StateInfo', ('Title', 'Description', 'SimpleDescription'))
MarkerInfo = collections.namedtuple('MarkerInfo', ('Target', 'MarkerType', 'Tooltip',
'QuestType', 'Repeatable'))
_UIInfo__FINISHED_TRIGGER_FORMAT = '<del>{}</del>'
def __init__(self, questType: 'natuum.quest.QuestType', repeatable: 'bool', title: 'str', rawMarkerInfos: 'None or collections.Mapping', rawGuideTarget: 'None or collections.Mapping', rewardDesc: 'str', rawUIDesc: 'None or col
lections.Mapping', rawUIDescPreq: 'None or collections.Mapping', rawUIDescTrigger: 'None or collections.Mapping'):
self._UIInfo__title = title
if rawMarkerInfos:
stateMap = dict()
markerMap = dict()
for state, value in rawMarkerInfos.items():
markerState = MarkerStates(state)
stateMap[markerState] = self.StateInfo(title, '', value['questMarkerText'])
infoList = list()
for marker in value['markers']:
infoList.append(self.MarkerInfo(marker['markerTarget'], natuum.quest.MarkerTypes(marker['markerType']), marker.get('tooltip'), questType, repeatable))
markerMap[markerState] = infoList
self._UIInfo__stringInfo = self._UIInfo__translateInfoMap(stateMap)
self._UIInfo__markerInfo = self._UIInfo__translateInfoMap(markerMap)
if rawGuideTarget:
guideTarget = dict()
for key, subMap in rawGuideTarget.items():
guideTarget[key] = AuxInfo.GuideTarget(subMap)
self._UIInfo__guideTarget = UIStateBoundInfo(guideTarget)
self._UIInfo__guideTarget.ensureType(AuxInfo.GuideTarget)
self._UIInfo__rewardDesc = rewardDesc
if rawUIDesc:
self._UIInfo__uiDesc = UIStateBoundInfo(rawUIDesc)
self._UIInfo__uiDesc.ensureType((str, type(None)))
self._UIInfo__uiDesc.ensureStates(isNotDetailed=True)
if rawUIDescPreq:
self._UIInfo__uiDescPreq = UIStateBoundInfo(rawUIDescPreq)
self._UIInfo__uiDescPreq.ensureType((str, type(None)))
self._UIInfo__uiDescPreq.ensureStates(isNotDetailed=True)
if rawUIDescTrigger:
self._UIInfo__uiDescTrigger = UIStateBoundInfo(rawUIDescTrigger)
self._UIInfo__uiDescTrigger.ensureType((str, type(None)))
[USER=1333341624]Property[/USER]
def Title(self) -> 'str':
return self._UIInfo__title
[USER=1333341624]Property[/USER]
def RewardDesc(self) -> 'str':
return self._UIInfo__rewardDesc
[USER=1333341624]Property[/USER]
def HasGuideTarget(self) -> 'bool':
return self._UIInfo__guideTarget is not None
def getInfo(self, uiState: 'UIStates') -> 'None or .StateInfo':
if self._UIInfo__stringInfo is not None:
return self._UIInfo__stringInfo.getValue(uiState)
def getMarkerInfo(self, uiState: 'UIStates') -> 'None or sequence of .MarkerInfo':
if self._UIInfo__markerInfo is not None:
return self._UIInfo__markerInfo.getValue(uiState)
def getGuideTarget(self, uiState: 'UIStates') -> 'None or AuxInfo.GuideTarget':
if self._UIInfo__guideTarget is not None:
return self._UIInfo__guideTarget.getValue(uiState)
def getDesc(self, uiState: 'UIStates') -> 'None or str':
if self._UIInfo__uiDesc is not None:
return self._UIInfo__uiDesc.getValue(uiState)
def getPrerequisiteDesc(self, uiState: 'UIStates') -> 'None or str':
if self._UIInfo__uiDescPreq is not None:
return self._UIInfo__uiDescPreq.getValue(uiState)
def getRawTriggerDesc(self, uiState: 'UIStates') -> 'None or str':
if self._UIInfo__uiDescTrigger is not None:
return self._UIInfo__uiDescTrigger.getValue(uiState)
def getTriggerDesc(self, uiState: 'UIStates', descTriggerAux: 'None or str') -> 'None or str':
raw = self.getRawTriggerDesc(uiState)
if UIStates.DEFAULT == uiState:
textList = list()
if raw:
textList.append(raw)
if descTriggerAux:
textList.append(descTriggerAux)
text = '\n'.join(textList)
elif UIStates.ACTIVE_COMPLETED == uiState:
textList = list()
defaultText = self.getRawTriggerDesc(UIStates.DEFAULT)
if defaultText:
textList.append(self._UIInfo__FINISHED_TRIGGER_FORMAT.format(defaultText))
if descTriggerAux:
textList.append(self._UIInfo__FINISHED_TRIGGER_FORMAT.format(descTriggerAux))
if raw:
if defaultText != raw:
textList.append(raw)
text = '\n'.join(textList)
else:
text = raw
if text:
return text
@staticmethod
def __translateInfoMap(infoMap: 'collections.Mapping') -> 'UIStateBoundInfo':
translated = dict()
for markerState, info in infoMap.items():
key = MarkerStates._translate(markerState)
if key is not None:
translated[key] = info
defaultInfo = infoMap.get(MarkerStates.ALL)
if defaultInfo is None:
defaultInfo = infoMap.get(MarkerStates.ACCEPTED)
if defaultInfo is not None:
translated[UIStates.DEFAULT] = defaultInfo
return UIStateBoundInfo(translated)
_UIInfo__title = None
_UIInfo__stringInfo = None
_UIInfo__markerInfo = None
_UIInfo__guideTarget = None
_UIInfo__rewardDesc = None
_UIInfo__uiDesc = None
_UIInfo__uiDescPreq = None
_UIInfo__uiDescTrigger = None
class AuxInfo:
_AuxInfo__AUX_KEY_GUIDED_QUEST_ID = 'guidedQuestID'
class GuideTarget:
class Types(enum.Enum):
PERSON_NAME = 'PersonName'
PERSON_UID = 'PersonUID'
QUEST_ROLE = 'QuestRole'
ITEM_NAME = 'ItemName'
SIGNPOST = 'Signpost'
Target = thing.accessor.ri(None, doc='str')
TargetType = thing.accessor.ri(None, doc='.Types')
TargetName = thing.accessor.ri(None, doc='str')
ZoneName = thing.accessor.ri(None, doc='str')
def __init__(self, subMap: 'collections.Mapping'):
target = subMap['target']
if not isinstance(target, str):
raise ValueError('invalid guide target : {}'.format(target))
self.Target = target
self.TargetType = self.Types(subMap['targetType'])
self.TargetName = subMap.get('targetName')
self.ZoneName = subMap.get('zoneName')
[USER=437263]clas[/USER]smethod
def getGuidedQuestID(cls, auxInfo: 'collections.Mapping') -> 'None or str':
guidedQuestID = auxInfo.get(cls._AuxInfo__AUX_KEY_GUIDED_QUEST_ID)
return guidedQuestID
[USER=437263]clas[/USER]smethod
def setGuidedQuestID(cls, auxInfo: 'collections.Mapping', questID: 'None or str') -> 'collections.Mapping':
if questID is None:
auxInfo.pop(cls._AuxInfo__AUX_KEY_GUIDED_QUEST_ID, None)
else:
auxInfo[cls._AuxInfo__AUX_KEY_GUIDED_QUEST_ID] = questID
return auxInfo
class QuestStates(enum.IntEnum):
INIT = 0
SELECT = 1
ACCEPTED = 2
REJECTED = 3
WATCH = 4
COMPLETED = 5
FAILED = 6
FINISHED_CUTSCENE = 7
CANCELED_CUTSCENE = 8
CANCELED = 9
FINISHED = 10
@staticmethod
def isActive(state: 'QuestStates', isRejectable: 'bool') -> 'bool':
if isRejectable:
return state >= QuestStates.WATCH
return True
[USER=437263]clas[/USER]smethod
def toString(cls, value: 'QuestStates') -> 'str':
if cls.INIT == value:
return '시작'
if cls.SELECT == value:
return '선택'
if cls.ACCEPTED == value:
return '수락'
if cls.REJECTED == value:
return '거절'
if cls.WATCH == value:
return '감시'
if cls.COMPLETED == value:
return '조건 만족'
if cls.FAILED == value:
return '실패'
if cls.FINISHED_CUTSCENE == value:
return '완료 컷신'
if cls.CANCELED_CUTSCENE == value:
return '취소 컷신'
if cls.CANCELED == value:
return '취소'
if cls.FINISHED == value:
return '완료'
return '알 수 없는 상태'
[USER=437263]clas[/USER]smethod
def convertToStringMap(cls, stateMap: 'collections.Mapping', finished: 'collections.Sequence') -> 'collections.Mapping':
textMap = dict()
for questID, subMap in stateMap.items():
state = subMap.get(QUEST_STATE_KEY_STATE)
if state is not None:
textMap[questID] = cls.toString(state)
for questID in finished:
textMap[questID] = cls.toString(cls.FINISHED)
return textMap
[USER=437263]clas[/USER]smethod
def isCompletable(cls, subMap: 'collections.Mapping') -> 'bool':
return cls.COMPLETED == subMap.get(QUEST_STATE_KEY_STATE)
class ResultStates(enum.IntEnum):
SUCCEEDED = 0
FAILED = 1
FAILED_INVALID_ITEM_INFO = 2
FAILED_ENTER_LIMIT = 3
FAILED_CARVE_TARGET_NOT_FOUND = 4
FAILED_CARVE_INVALID_KIRANA = 5
FAILED_CARVE_INVALID_ITEM = 6
FAILED_CARVE_ALREADY_CARVED = 7
FAILED_CARVE_RESERVED_KIRANA = 8
[USER=437263]clas[/USER]smethod
def convertFromCarveResult(cls, value: 'int') -> 'ResultStates':
if CarveResultEnum.SUCCEEDED == value:
return cls.SUCCEEDED
if CarveResultEnum.FAILED_ALREADY_CARVED == value:
return cls.FAILED_CARVE_ALREADY_CARVED
if CarveResultEnum.FAILED_INVALID_ITEM == value:
return cls.FAILED_CARVE_INVALID_ITEM
if CarveResultEnum.FAILED_INVALID_KIRANA == value:
return cls.FAILED_CARVE_INVALID_KIRANA
if CarveResultEnum.FAILED_RESERVED_KIRANA == value:
return cls.FAILED_CARVE_RESERVED_KIRANA
return cls.FAILED_ENTER_LIMIT
[USER=437263]clas[/USER]smethod
def convertToFailedMessageID(cls, value: 'int') -> 'None or str':
if cls.FAILED == value:
return 'Quest_msg_result_failed_default'
if cls.FAILED_INVALID_ITEM_INFO == value:
return 'Quest_msg_result_failed_invalid_item_info'
if cls.FAILED_ENTER_LIMIT == value:
return 'Quest_msg_result_failed_enter_limit'
if cls.FAILED_CARVE_TARGET_NOT_FOUND == value:
return 'Quest_msg_result_failed_carve_target_not_found'
if cls.FAILED_CARVE_INVALID_KIRANA == value:
return 'Quest_msg_result_failed_carve_invalid_kirana'
if cls.FAILED_CARVE_INVALID_ITEM == value:
return 'Quest_msg_result_failed_carve_invalid_item'
if cls.FAILED_CARVE_ALREADY_CARVED == value:
return 'Quest_msg_result_failed_carve_already_carved'
if cls.FAILED_CARVE_RESERVED_KIRANA == value:
return 'Quest_msg_result_failed_carve_reserved_kirana'
class TriggerTypes(enum.IntEnum):
CONVERSATION = 0
PROXIMITY_ENTER = 1
ITEM_COUNT = 2
KILL_COUNT = 3
RANDOM_ITEM_COUNT = 4
RANDOM_KILL_COUNT = 5
ACTION = 6
SELECTION = 7
TIMER = 8
GUARDIAN_CHANGED = 9
PROPERTY = 10
GUARDIAN_PROPERTY = 11
SELECT_STATE = 12
CONTRACT = 13
DECK = 14
HAND = 15
RELATIONSHIP = 16
DINE = 17
@staticmethod
def isRevertible(triggerType: 'TriggerTypes') -> 'bool':
return TriggerTypes.RANDOM_ITEM_COUNT == triggerType or TriggerTypes.PROPERTY == triggerType or TriggerTypes.GUARDIAN_PROPERTY == triggerType or TriggerTypes.ITEM_COUNT == triggerType or TriggerTypes.DECK == triggerType or
TriggerTypes.HAND == triggerType
@staticmethod
def isInstant(triggerType: 'TriggerTypes') -> 'bool':
return TriggerTypes.SELECTION == triggerType or TriggerTypes.SELECT_STATE == triggerType
@staticmethod
def _convert(value: 'str'):
if value == _STRING_TRIGGER_TYPE_CONVERSATION:
return TriggerTypes.CONVERSATION
if value == _STRING_TRIGGER_TYPE_PROXIMITY_ENTER:
return TriggerTypes.PROXIMITY_ENTER
if value == _STRING_TRIGGER_TYPE_ITEM_COUNT:
return TriggerTypes.ITEM_COUNT
if value == _STRING_TRIGGER_TYPE_KILL_COUNT:
return TriggerTypes.KILL_COUNT
if value == _STRING_TRIGGER_TYPE_RANDOM_ITEM_COUNT:
return TriggerTypes.RANDOM_ITEM_COUNT
if value == _STRING_TRIGGER_TYPE_RANDOM_KILL_COUNT:
return TriggerTypes.RANDOM_KILL_COUNT
if value == _STRING_TRIGGER_TYPE_TIMER:
return TriggerTypes.TIMER
if value == _STRING_TRIGGER_TYPE_SELECTION:
return TriggerTypes.SELECTION
if value == _STRING_TRIGGER_TYPE_GUARDIAN_CHANGED:
return TriggerTypes.GUARDIAN_CHANGED
if value == _STRING_TRIGGER_TYPE_PROPERTY:
return TriggerTypes.PROPERTY
if value == _STRING_TRIGGER_TYPE_GUARDIAN_PROPERTY:
return TriggerTypes.GUARDIAN_PROPERTY
if value == _STRING_TRIGGER_TYPE_CONTRACT:
return TriggerTypes.CONTRACT
if value == _STRING_TRIGGER_TYPE_DECK:
return TriggerTypes.DECK
if value == _STRING_TRIGGER_TYPE_HAND:
return TriggerTypes.HAND
if value == _STRING_TRIGGER_TYPE_ACTION:
return TriggerTypes.ACTION
if value == _STRING_TRIGGER_TYPE_RELATIONSHIP:
return TriggerTypes.RELATIONSHIP
if value == _STRING_TRIGGER_TYPE_DINE:
return TriggerTypes.DINE
raise ValueError('invalid trigger type : {}'.format(value))
class TriggerUsageTypes(enum.IntEnum):
START = 0
UPDATE = 1
class DescDelims(enum.Enum):
DungeonDesc = '<D>'
EpicKiranaClassIDs = '<E>'
TownDesc = '<T>'
class DungeonDescDelims(enum.Enum):
KiranaClassIDs = '<DA>'
DungeonRecordKey = '<DB>'
class ConversationTriggerInfo:
QuestID = thing.accessor.ri(None, doc='str')
UsageType = thing.accessor.ri(None, doc='TriggerUsageTypes')
ListenerQuestRole = thing.accessor.ri(None, doc='str')
SelectionText = thing.accessor.ri(None, doc='str')
ConfirmText = thing.accessor.ri(None, doc='None or str')
def __init__(self, listenerQuestRole: 'str', selectionText: 'str', confirmText: 'None or str', questID: 'str', usageType: 'TriggerUsageTypes'):
self.QuestID = questID
self.ListenerQuestRole = listenerQuestRole
self.SelectionText = selectionText
self.ConfirmText = confirmText
self.UsageType = usageType
class ContractInfo:
TargetClassID = thing.accessor.ri(None, doc='thing.system.uuid')
def __init__(self, targetClassID: 'str'):
self.TargetClassID = natuum.util.convertToUUID(targetClassID)
class DeckInfo:
ClassIDCounts = thing.accessor.ri(None, doc='list')
def __init__(self, classIDs: 'collections.Sequence'):
countMap = dict()
for classIDStr in classIDs:
classID = natuum.util.convertToUUID(classIDStr)
if classID not in countMap:
countMap[classID] = 0
else:
countMap[classID] += 1
self.ClassIDCounts = list()
for classID, count in countMap.items():
self.ClassIDCounts.append((classID, count))
class HandInfo:
class HandCondition:
Quantifier = thing.accessor.ri(None, doc='None or QuantifierTypes')
ClassIDs = thing.accessor.ri(None, doc='None or collections.Sequence')
PropertyCondition = thing.accessor.ri(None, doc='None or PropertyCondition')
def initialize(self, raw: 'None or collections.Mapping') -> 'bool':
if raw is not None:
self.Quantifier = QuantifierTypes(raw['quantifier'])
classIDs = raw.get('classIDs')
if classIDs:
self.ClassIDs = list()
for classID in classIDs:
self.ClassIDs.append(natuum.util.convertToUUID(classID))
property = raw.get('property')
if property:
self.PropertyCondition = PropertyCondition(property)
return self.Quantifier and (self.ClassIDs or self.PropertyCondition)
AverageLevel = thing.accessor.ri(None, doc='float')
Count = thing.accessor.ri(None, doc='int')
Condition = thing.accessor.ri(None, doc='None or HandInfo.HandCondition')
def __init__(self, raw: 'collections.Mapping'):
self.AverageLevel = raw.get('minAverageLevel', 0.0)
self.Count = int(raw['minCount'])
condition = self.HandCondition()
if condition.initialize(raw.get('condition')):
self.Condition = condition
class ActionInfo:
ActionID = thing.accessor.ri(None, doc='str')
HostRole = thing.accessor.ri(None, doc='str')
def __init__(self, actionID: 'str', hostRole: 'str'):
if not isinstance(actionID, str):
raise ValueError('invalid actionID : {}'.format(actionID))
self.ActionID = actionID
self.HostRole = ActionRoleTypes(hostRole)
class CountTriggerInfo:
TargetClassID = thing.accessor.ri(None, doc='thing.system.uuid')
TargetCount = thing.accessor.ri(None, doc='int')
Type = thing.accessor.ri(None, doc='CountTypes')
PropertyCondition = thing.accessor.ri(None, doc='None or PropertyCondition')
TargetQuestRole = thing.accessor.ri(None, doc='None or str')
UIName = thing.accessor.ri(None, doc='None or str')
def __init__(self, targetClassID: 'str', targetCount: 'int', countType: 'str'=None, propertyCondition: 'PropertyCondition'=None, questRole: 'str'=None, uiName: 'str'=None):
self.TargetClassID = natuum.util.convertToUUID(targetClassID)
try:
natuum.gameClass.getClassByID(self.TargetClassID)
except Exception:
raise ValueError('unknown classID : {}'.format(targetClassID))
self.TargetCount = targetCount
self.Type = CountTypes.convert(countType)
self.PropertyCondition = propertyCondition
self.TargetQuestRole = questRole
self.UIName = uiName
class RandomCountTriggerInfo:
TargetClassIDCandidates = thing.accessor.ri(None, doc='list')
TargetCountRangeMax = thing.accessor.ri(None, doc='int')
TargetCountRangeMin = thing.accessor.ri(None, doc='int')
Type = thing.accessor.ri(None, doc='CountTypes')
def __init__(self, candidates: 'collections.Sequence', maxCount: 'int', minCount: 'int', countType: 'str'=None):
if not candidates:
raise ValueError('random trigger should have at least one candidate')
if maxCount < minCount or (maxCount < 1 or minCount < 1):
msg = 'invalid range : [ {}, {} ]'
raise ValueError(msg.format(minCount, maxCount))
self.TargetClassIDCandidates = list()
for value in candidates:
classID = natuum.util.convertToUUID(value)
try:
natuum.gameClass.getClassByID(classID)
except Exception:
raise ValueError('unknown classID : {}'.format(value))
self.TargetClassIDCandidates.append(classID)
self.TargetCountRangeMax = maxCount
self.TargetCountRangeMin = minCount
self.Type = CountTypes.convert(countType)
class EnterTriggerInfo:
class TargetTypes(enum.IntEnum):
QuestRole = 0
Name = 1
Signpost = 2
@staticmethod
def convert(value: 'str') -> 'EnterTriggerInfo.TargetTypes':
if value == EnterTriggerInfo.TargetTypes.QuestRole.name:
return EnterTriggerInfo.TargetTypes.QuestRole
if value == EnterTriggerInfo.TargetTypes.Name.name:
return EnterTriggerInfo.TargetTypes.Name
if value == EnterTriggerInfo.TargetTypes.Signpost.name:
return EnterTriggerInfo.TargetTypes.Signpost
msg = 'invalid enter trigger target type : {}'
raise ValueError(msg.format(value))
Target = thing.accessor.ri(None, doc='str')
Range = thing.accessor.ri(None, doc='float')
Type = thing.accessor.ri(None, doc='EnterTriggerInfo.TargetTypes')
def __init__(self, target: 'str', targetType: 'str', range: 'float'):
self.Target = target
self.Type = EnterTriggerInfo.TargetTypes.convert(targetType)
self.Range = range
class AcceptTriggerInfo:
def __init__(self, acceptText: 'None or str', rejectText: 'None or str'):
self._AcceptTriggerInfo__selectionMap = dict()
self._AcceptTriggerInfo__keys = list()
if acceptText is not None:
self._AcceptTriggerInfo__selectionMap[acceptText] = QuestStates.ACCEPTED
self._AcceptTriggerInfo__keys.append(acceptText)
if rejectText is not None:
self._AcceptTriggerInfo__selectionMap[rejectText] = QuestStates.REJECTED
self._AcceptTriggerInfo__keys.append(rejectText)
self._AcceptTriggerInfo__isRejectable = True
else:
self._AcceptTriggerInfo__isRejectable = False
[USER=1333341624]Property[/USER]
def Keys(self) -> 'collections.Sequence':
return self._AcceptTriggerInfo__keys
[USER=1333341624]Property[/USER]
def IsRejectable(self) -> 'bool':
return self._AcceptTriggerInfo__isRejectable
def getValue(self, key: 'str') -> 'None or str':
return self._AcceptTriggerInfo__selectionMap.get(key)
_AcceptTriggerInfo__selectionMap = None
_AcceptTriggerInfo__keys = None
_AcceptTriggerInfo__isRejectable = None
class SelectionTriggerInfo:
Results = collections.namedtuple('Results', ('NextQuestIDs', ))
def __init__(self, selectionMap: 'collecctions.Mapping'):
self._SelectionTriggerInfo__selectionMap = collections.OrderedDict()
self._SelectionTriggerInfo__keys = list()
for selectionText, nextQuestIDs in selectionMap.items():
if nextQuestIDs:
if isinstance(nextQuestIDs, collections.Sequence):
self._SelectionTriggerInfo__selectionMap[selectionText] = self.Results(nextQuestIDs)
self._SelectionTriggerInfo__keys.append(selectionText)
[USER=1333341624]Property[/USER]
def Keys(self) -> 'collections.Sequence':
return self._SelectionTriggerInfo__keys
def getResults(self, key: 'str') -> 'None or .Results':
return self._SelectionTriggerInfo__selectionMap.get(key)
_SelectionTriggerInfo__selectionMap = None
_SelectionTriggerInfo__keys = None
class GuardianChangedTriggerInfo:
FromClassID = thing.accessor.ri(None, doc='None or thing.system.uuid')
ToClassID = thing.accessor.ri(None, doc='None or thing.system.uuid')
UsageType = thing.accessor.ri(None, doc='TriggerUsageTypes')
def __init__(self, fromClassID: 'None or thing.system.uuid', classID: 'None or thing.system.uuid', usageType: 'TriggerUsageTypes'):
if fromClassID is not None:
self.FromClassID = natuum.util.convertToUUID(fromClassID)
else:
self.FromClassID = None
if classID is not None:
self.ToClassID = natuum.util.convertToUUID(classID)
else:
self.ToClassID = None
self.UsageType = usageType
class PropertyTriggerInfo:
Condition = thing.accessor.ri(None, doc='PropertyCondition')
def __init__(self, condition: 'collections.Mapping'):
self.Condition = PropertyCondition(condition)
class TriggerInfo:
def __init__(self, triggerType: 'TriggerTypes', triggerName: 'str', auxArg: 'var'):
self._TriggerInfo__triggerType = triggerType
self._TriggerInfo__triggerName = triggerName
self._TriggerInfo__auxArg = auxArg
[USER=1333341624]Property[/USER]
def TriggerType(self) -> 'TriggerTypes':
return self._TriggerInfo__triggerType
[USER=1333341624]Property[/USER]
def TriggerName(self) -> 'str':
return self._TriggerInfo__triggerName
[USER=1333341624]Property[/USER]
def AuxArg(self) -> 'var':
return self._TriggerInfo__auxArg
_TriggerInfo__triggerType = None
_TriggerInfo__triggerName = None
_TriggerInfo__auxArg = None
class TriggerInfoList:
def __init__(self, infoList: 'collections.Sequence', isInstant: 'bool'):
if not infoList:
raise ValueError('invalid info list : {}'.format(infoList))
for subList in infoList:
if not subList:
raise ValueError('invalid sub info list : {}'.format(subList))
self._TriggerInfoList__infoList = infoList
self._TriggerInfoList__isInstant = isInstant
[USER=1333341624]Property[/USER]
def IsInstant(self):
return self._TriggerInfoList__isInstant
def iterate(self) -> 'generator of list of TriggerInfo':
for subList in self._TriggerInfoList__infoList:
yield subList
def getSubListByIndex(self, index: 'int') -> 'None or list of TriggerInfo':
try:
return self._TriggerInfoList__infoList[index]
except IndexError:
return
def iterateWithIndex(self) -> 'generator of ( int, list of TriggerInfo )':
for index, subList in enumerate(self._TriggerInfoList__infoList):
yield (
index, subList)
_TriggerInfoList__infoList = None
_TriggerInfoList__isInstant = None
class InverseTriggerInfoList(TriggerInfoList):
ModifierTargets = thing.accessor.ri(None, doc='None or dict')
def __init__(self, infoMap):
super().__init__(list(infoMap.values()), False)
self.ModifierTargets = self._InverseTriggerInfoList__getModifierTargets()
self._InverseTriggerInfoList__infoMap = infoMap
def getSubListByIndex(self, index: 'int') -> 'None or list of TriggerInfo':
return self._InverseTriggerInfoList__infoMap.get(index)
def iterateWithIndex(self) -> 'generator of ( int, list of TriggerInfo )':
for index, subList in self._InverseTriggerInfoList__infoMap.items():
yield (
index, subList)
@staticmethod
def create(triggerList: 'TriggerInfoList') -> 'None or InverseTriggerInfoList':
revertible = dict()
for index, subList in triggerList.iterateWithIndex():
subRevertible = list()
for info in subList:
if TriggerTypes.isRevertible(info.TriggerType):
subRevertible.append(info)
if subRevertible:
revertible[index] = subRevertible
if revertible:
return InverseTriggerInfoList(revertible)
def __getModifierTargets(self) -> 'None or collections.Mapping':
targetMap = dict()
for subList in self.iterate():
for info in subList:
if not TriggerTypes.isRevertible(info.TriggerType):
continue
else:
if info.TriggerType not in targetMap:
targetMap[info.TriggerType] = set()
subSet = targetMap[info.TriggerType]
if TriggerTypes.ITEM_COUNT == info.TriggerType:
subSet.add((info.AuxArg.TargetClassID,
info.AuxArg.PropertyCondition))
else:
if TriggerTypes.RANDOM_ITEM_COUNT == info.TriggerType:
subSet.add(info)
else:
if TriggerTypes.PROPERTY == info.TriggerType:
subSet.add(info.AuxArg.Condition.PropertyName)
else:
if TriggerTypes.GUARDIAN_PROPERTY == info.TriggerType:
subSet.add(info.AuxArg.Condition.PropertyName)
else:
if TriggerTypes.DECK == info.TriggerType:
continue
if TriggerTypes.HAND == info.TriggerType:
continue
if targetMap:
return targetMap
_InverseTriggerInfoList__infoMap = None
class CutsceneInfo:
ScriptData = thing.accessor.ri(None, doc='natuum.cutscene.ScriptData')
RoleNameMap = thing.accessor.ri(None, doc='dict')
CaptionPositionMap = thing.accessor.ri(None, doc='dict')
IsStaged = thing.accessor.ri(None, doc='bool')
StageFlag = thing.accessor.ri(None, doc='None or int')
def __init__(self, scriptData: 'natuum.cutscene.ScriptData', roleNameMap: 'collections.Mapping', captionPositionMap: 'collections.Mapping', isStaged: 'int or bool'):
self.ScriptData = scriptData
self.RoleNameMap = roleNameMap
self.CaptionPositionMap = captionPositionMap
self.IsStaged = bool(isStaged)
self.StageFlag = natuum.cutscene.QuestStagedFlag if isStaged else None
class RelationshipTarget(natuum.relationship.RelationshipTarget):
STR_QUEST_GIVER = 'QUEST_GIVER'
[USER=1333341624]Property[/USER]
def TargetQuestGiver(self):
return self.Target == self.STR_QUEST_GIVER
class RelationshipCondition:
RelationshipTarget = thing.accessor.ri(None, doc='RelationshipTarget')
Value = thing.accessor.ri(None, doc='Value')
CountType = thing.accessor.ri(None, doc='CountTypes')
def __init__(self, info: 'collections.Mapping'):
self.RelationshipTarget = RelationshipTarget(info['target'])
self.CountType = CountTypes.convert(info['countType'])
value = info['value']
if not isinstance(value, int):
msg = 'invalid relationship value : {}'.format(value)
raise ValueError(msg)
self.Value = value
def check(self, level: 'int') -> 'bool':
if CountTypes.LessThan == self.CountType:
return level < self.Value
return level >= self.Value
class RelationshipConditionList:
class IGetter(metaclass=abc.ABCMeta):
[USER=2000283886]abc[/USER].abstractmethod
def getRelationship(self, target: 'str', group: 'natuum.relationship.RelationshipGroup') -> 'int':
pass
def __init__(self, value: 'collections.Sequence'):
self._RelationshipConditionList__giverRelationship = list()
self._RelationshipConditionList__targetedRelationship = list()
for info in value:
cond = RelationshipCondition(info)
if cond.RelationshipTarget.TargetQuestGiver:
self._RelationshipConditionList__giverRelationship.append(cond)
else:
self._RelationshipConditionList__targetedRelationship.append(cond)
def checkGiver(self, getter: '.IGetter', giverUID: 'str') -> 'bool':
for cond in self._RelationshipConditionList__giverRelationship:
relTarget = cond.RelationshipTarget
level = getter.getRelationship(giverUID, relTarget.Group)
if not cond.check(level):
return False
return True
def checkTargeted(self, getter: '.IGetter') -> 'bool':
for cond in self._RelationshipConditionList__targetedRelationship:
relTarget = cond.RelationshipTarget
level = getter.getRelationship(relTarget.Target, relTarget.Group)
if not cond.check(level):
return False
return True
_RelationshipConditionList__giverRelationship = None
_RelationshipConditionList__targetedRelationship = None
class DineEventTriggerInfo:
FoodClassID = thing.accessor.ri(None, doc='thing.system.uuid')
def __init__(self, info: 'collections.Mapping'):
classID = natuum.util.convertToUUID(info['foodClassID'])
try:
natuum.gameClass.getClassByID(classID)
except Exception:
raise ValueError('invalid classID : {}'.format(classID))
if not natuum.gameClass.isSameOrDescendant(classID, '{CL1096(Dish)}'):
raise ValueError('{} is not a descendant of CL1096'.format(classID))
self.FoodClassID = classID
class Result:
class Base:
pass
class Action(Base):
def __init__(self, actionID: 'str', roleMap: 'None or collections.Mapping'):
self._Action__actionID = actionID
self._Action__roleMap = roleMap
[USER=1333341624]Property[/USER]
def ActionID(self) -> 'str':
return self._Action__actionID
[USER=1333341624]Property[/USER]
def RoleMap(self) -> 'None or collections.Mapping':
return self._Action__roleMap
_Action__actionID = None
_Action__roleMap = None
class ItemBase(Base):
def __init__(self, itemClassID: 'str', itemCount: 'int'):
if not isinstance(itemClassID, str):
msg = 'invalid item classID : {}'.format(itemClassID)
raise ValueError(msg)
self._ItemBase__itemClassID = itemClassID
if not isinstance(itemCount, int):
msg = 'invalid item count : {}'.format(itemCount)
raise ValueError(msg)
self._ItemBase__itemCount = itemCount
[USER=1333341624]Property[/USER]
def ItemClassID(self) -> 'str':
return self._ItemBase__itemClassID
[USER=1333341624]Property[/USER]
def ItemCount(self) -> 'int':
return self._ItemBase__itemCount
_ItemBase__itemClassID = None
_ItemBase__itemCount = None
class CreateItem(ItemBase):
def __init__(self, bind, *args):
(super().__init__)(*args)
self._CreateItem__bind = bind
[USER=1333341624]Property[/USER]
def Bind(self) -> 'bool':
return self._CreateItem__bind
_CreateItem__bind = None
class DeleteItem(ItemBase):
pass
class Teleport(Base):
NamedLocation = collections.namedtuple('NamedLocation', ('Name', 'RelativePosition'))
def __init__(self, target: 'str', position: 'collections.Mapping or thing.system.vec'):
self._Teleport__target = target
if isinstance(position, thing.system.vec):
self._Teleport__position = position
elif isinstance(position, collections.Mapping):
name = position.get('beaconName')
relativePos = position.get('relativePosition')
if isinstance(relativePos, (type(None), thing.system.vec)):
if isinstance(name, str):
self._Teleport__position = self.NamedLocation(name, relativePos)
if self._Teleport__position is None:
raise ValueError('invalid position : {}'.format(position))
[USER=1333341624]Property[/USER]
def Target(self) -> 'str':
return self._Teleport__target
[USER=1333341624]Property[/USER]
def Position(self) -> 'thing.system.vec or .NamedLocation':
return self._Teleport__position
_Teleport__target = None
_Teleport__position = None
class KiranaCarve(Base):
class Types(enum.IntEnum):
ClassID = 0
QuestRole = 1
ActorTable = 2
[USER=437263]clas[/USER]smethod
def convert(cls, value: 'str') -> 'Result.KiranaCarve.Types':
if value == cls.ClassID.name:
return cls.ClassID
if value == cls.QuestRole.name:
return cls.QuestRole
if value == cls.ActorTable.name:
return cls.ActorTable
msg = 'invalid kirana carve target type : {}'
raise ValueError(msg.format(value))
def __init__(self, targetType: 'str', targetPerson: 'str', targetItemClassID: 'str', questRolesToBeRemoved: 'None or collections.Sequence', targetItemBans: 'None or collections.Sequence', addToHand: 'bool'):
if not isinstance(targetPerson, str):
msg = 'invalid kirana carve target : {}'
raise ValueError(msg.format(targetPerson))
self._KiranaCarve__targetType = Result.KiranaCarve.Types.convert(targetType)
if Result.KiranaCarve.Types.ClassID == self._KiranaCarve__targetType:
try:
natuum.gameClass.getClassByID(targetPerson)
except Exception:
raise ValueError('invalid classID : {}'.format(targetPerson))
self._KiranaCarve__targetPerson = targetPerson
self._KiranaCarve__targetItemClassID = targetItemClassID
self._KiranaCarve__questRolesToBeRemoved = questRolesToBeRemoved
self._KiranaCarve__targetItemBans = targetItemBans
self._KiranaCarve__addToHand = addToHand
[USER=1333341624]Property[/USER]
def TargetType(self) -> 'Result.KiranaCarve.Types':
return self._KiranaCarve__targetType
[USER=1333341624]Property[/USER]
def TargetPerson(self) -> 'str':
return self._KiranaCarve__targetPerson
[USER=1333341624]Property[/USER]
def TargetItemClassID(self) -> 'str':
return self._KiranaCarve__targetItemClassID
[USER=1333341624]Property[/USER]
def QuestRolesToBeRemoved(self) -> 'None or collections.Sequence':
return self._KiranaCarve__questRolesToBeRemoved
[USER=1333341624]Property[/USER]
def TargetItemBans(self) -> 'None or collections.Sequence':
return self._KiranaCarve__targetItemBans
[USER=1333341624]Property[/USER]
def AddToHand(self) -> 'bool':
return self._KiranaCarve__addToHand
_KiranaCarve__targetType = None
_KiranaCarve__targetPerson = None
_KiranaCarve__targetItemClassID = None
_KiranaCarve__questRolesToBeRemoved = None
_KiranaCarve__targetItemBans = None
_KiranaCarve__addToHand = None
class Experience(Base):
def __init__(self, value: 'int'):
self._Experience__value = value
[USER=1333341624]Property[/USER]
def Value(self) -> 'int':
return self._Experience__value
_Experience__value = None
class Relationship(Base):
def __init__(self, target: 'collections.Mapping', value: 'int'):
self._Relationship__target = RelationshipTarget(target)
self._Relationship__value = value
[USER=1333341624]Property[/USER]
def RelationshipTarget(self) -> 'RelationshipTarget':
return self._Relationship__target
[USER=1333341624]Property[/USER]
def Value(self) -> 'int':
return self._Relationship__value
_Relationship__target = None
_Relationship__value = None
class Town(Base):
def __init__(self, townID: 'str'):
self._Town__townID = townID
[USER=1333341624]Property[/USER]
def TownID(self) -> 'str':
return self._Town__townID
_Town__townID = None
class ResultList:
PropertyNames = thing.accessor.ri(None, doc='list')
Entry = collections.namedtuple('Entry', ('PropertyConditions', 'Results'))
def __init__(self, raw: 'collections.Sequence'):
self.PropertyNames = list()
self._ResultList__resultList = list()
propertyNameSet = set()
for subMap in raw:
propertyConditions = subMap.get('propertyConditions')
if propertyConditions is not None:
propertyConditions = PropertyConditionList(propertyConditions)
propertyNameSet.update(propertyConditions.PropertyNames)
else:
resultMap = dict()
for resultType, rawResults in subMap['results'].items():
if resultType == 'town':
resultMap[Result.Town] = {
Result.Town(rawResults)}
else:
if resultType == 'experience':
resultMap[Result.Experience] = {
Result.Experience(rawResults)}
else:
for sub in rawResults:
if resultType == 'action':
resultClass = Result.Action
result = resultClass(sub['actionID'], sub.get('roleMap'))
elif resultType == 'createItem':
resultClass = Result.CreateItem
result = resultClass(sub.get('bind', False), sub['classID'], sub['count'])
elif resultType == 'deleteItem':
resultClass = Result.DeleteItem
result = resultClass(sub['classID'], sub['count'])
elif resultType == 'teleport':
resultClass = Result.Teleport
result = resultClass(sub['target'], sub['position'])
elif resultType == 'relationship':
resultClass = Result.Relationship
result = resultClass(sub['target'], sub['value'])
elif resultType == 'kiranaCarve':
resultClass = Result.KiranaCarve
result = resultClass(sub['targetType'], sub['targetPerson'], sub['targetItemClassID'], sub.get('questRolesToBeRemoved'), sub.get('targetItemBans'), sub.get('addToHand', False))
else:
raise ValueError('invalid result type : {}'.format(resultType))
if resultClass not in resultMap:
resultMap[resultClass] = set()
else:
resultMap[resultClass].add(result)
self._ResultList__resultList.append(self.Entry(propertyConditions, resultMap))
self.PropertyNames.extend(propertyNameSet)
def filterResults(self, propertyMap: 'None or collections.Mapping') -> 'collections.Mapping':
filtered = dict()
for entry in self._ResultList__resultList:
conditions = entry.PropertyConditions
if conditions is not None:
if propertyMap:
if not conditions.check(propertyMap):
continue
for resultClass, subList in entry.Results.items():
if resultClass not in filtered:
filtered[resultClass] = list()
else:
filtered[resultClass].extend(subList)
return filtered
_ResultList__resultList = None
class PatternQuestStates(enum.IntEnum):
ACTIVE = 0
FINISHED = 2
CANCELED = 1
@staticmethod
def serialize(questID: 'str', state: 'PatternQuestStates', timeStamp: 'int') -> '( str, int, str )':
return (
questID, int(state), str(timeStamp))
@staticmethod
def deserialize(value: 'collections.Sequence') -> '( str, PatternQuestStates, int )':
questID, stateInt, timeStampStr = value
return (
questID, PatternQuestStates(stateInt), int(timeStampStr))
@staticmethod
def canStartPatternQuest(serialized: 'None or collections.Sequence', questID: 'None or str', current: 'int') -> 'bool or str':
if not isinstance(serialized, collections.Sequence):
return True
prevQuestID, prevState, prevTimeStamp = PatternQuestStates.deserialize(serialized)
if PatternQuestStates.ACTIVE == prevState:
return False
if PatternQuestStates.FINISHED == prevState:
return prevTimeStamp < current
if PatternQuestStates.CANCELED == prevState:
if prevTimeStamp < current:
return True
if questID is None:
return prevQuestID
return questID == prevQuestID
msg = 'invalid pattern quest state : {}'.format(prevState)
raise NotImplementedError(msg)
@staticmethod
def hasPatternQuest(stateMap: 'collections.Mapping', questID: 'str', state: 'PatternQuestStates') -> 'bool':
for serialized in stateMap.values():
otherQuestID, otherState, *_ = PatternQuestStates.deserialize(serialized)
if otherQuestID == questID:
if otherState == state:
return True
return False
class PatternQuestInfo:
ResetDelay = thing.accessor.ri(None, doc='int')
def __init__(self, resetDelay: 'int'):
self.ResetDelay = resetDelay
class PropertyCondition(natuum.cutscene.IPropertyCondition):
def __init__(self, value: 'collections.Mapping'):
self._PropertyCondition__propertyName = value['propertyName']
self._PropertyCondition__comp = ComparisonTypes(value['comparisonMethod'])
self._PropertyCondition__targetValueRaw = value['targetValue']
try:
self._PropertyCondition__targetValueUUID = natuum.util.convertToUUID(self._PropertyCondition__targetValueRaw)
except ValueError:
self._PropertyCondition__targetValueUUID = None
[USER=1333341624]Property[/USER]
[USER=186209]Thing[/USER].overrides(natuum.cutscene.IPropertyCondition)
def PropertyName(self) -> 'str':
return self._PropertyCondition__propertyName
[USER=186209]Thing[/USER].overrides(natuum.cutscene.IPropertyCondition)
def check(self, value: 'var') -> 'bool':
if self._PropertyCondition__targetValueUUID is not None:
if self._PropertyCondition__check(value, self._PropertyCondition__targetValueUUID):
return True
return self._PropertyCondition__check(value, self._PropertyCondition__targetValueRaw)
def propertyConditionToString(self) -> 'str':
return self.PropertyName + str(self._PropertyCondition__comp) + str(self._PropertyCondition__targetValueRaw)
def __check(self, value: 'var', targetValue: 'var') -> 'bool':
try:
return ComparisonTypes.compare(self._PropertyCondition__comp, value, targetValue)
except Exception:
return False
_PropertyCondition__propertyName = None
_PropertyCondition__comp = None
_PropertyCondition__targetValueRaw = None
_PropertyCondition__targetValueUUID = None
class PropertyConditionList:
PropertyNames = thing.accessor.ri(None, doc='list')
def __init__(self, value: 'collections.Sequence'):
self._PropertyConditionList__conditionList = list()
self.PropertyNames = list()
for subMap in value:
condition = PropertyCondition(subMap)
self._PropertyConditionList__conditionList.append(condition)
self.PropertyNames.append(condition.PropertyName)
def check(self, propertyMap: 'collections.Mapping') -> 'bool':
if not propertyMap:
return False
for key, value in propertyMap.items():
if value == natuum.util.UUID_PROPERTY_NOT_FOUND:
return False
for condition in self._PropertyConditionList__conditionList:
if condition.PropertyName not in propertyMap:
return False
if not condition.check(propertyMap[condition.PropertyName]):
return False
return True
def _checkProperty(self, name: 'str', value: 'var') -> 'bool':
for condition in self._PropertyConditionList__conditionList:
if condition.PropertyName == name:
if not condition.check(value):
return False
return True
_PropertyConditionList__conditionList = None
class PrerequisiteInfo:
def __init__(self, questID: 'str', raw: 'None or collections.Mapping'):
self._PrerequisiteInfo__questID = questID
if raw is None:
self._PrerequisiteInfo__activeQuestIDs = None
self._PrerequisiteInfo__inactiveQuestIDs = None
self._PrerequisiteInfo__finishedQuestIDs = None
self._PrerequisiteInfo__notInFinishedQuestIDs = None
self._PrerequisiteInfo__notInDeck = None
self._PrerequisiteInfo__gInfo = None
self._PrerequisiteInfo__rInfo = None
self._PrerequisiteInfo__guardianInfo = None
self._PrerequisiteInfo__kiranaInfoMap = None
self._PrerequisiteInfo__kiranaInfoNameMap = None
self._PrerequisiteInfo__relationship = None
self._PrerequisiteInfo__rPropNames = None
self._PrerequisiteInfo__townID = None
self._PrerequisiteInfo__unregTownIDs = None
else:
value = raw.get('activeQuestIDs')
self._PrerequisiteInfo__activeQuestIDs = set(value) if value else None
value = raw.get('inactiveQuestIDs')
self._PrerequisiteInfo__inactiveQuestIDs = set(value) if value else None
value = raw.get('finishedQuestIDs')
self._PrerequisiteInfo__finishedQuestIDs = set(value) if value else None
value = raw.get('notInFinishedQuestIDs')
self._PrerequisiteInfo__notInFinishedQuestIDs = set(value) if value else None
value = raw.get('notInDeck')
if isinstance(value, collections.Sequence):
self._PrerequisiteInfo__notInDeck = set()
for classIDStr in value:
self._PrerequisiteInfo__notInDeck.add(natuum.util.convertToUUID(classIDStr))
else:
self._PrerequisiteInfo__notInDeck = None
value = raw.get('giverProperties')
if value:
self._PrerequisiteInfo__gInfo = PropertyConditionList(value)
rPropNames = set()
value = raw.get('receiverProperties')
if value:
self._PrerequisiteInfo__rInfo = PropertyConditionList(value)
rPropNames.update(self._PrerequisiteInfo__rInfo.PropertyNames)
value = raw.get('guardianProperties')
if value:
self._PrerequisiteInfo__guardianInfo = PropertyConditionList(value)
value = raw.get('kiranaProperties')
if value:
self._PrerequisiteInfo__kiranaInfoMap = dict()
self._PrerequisiteInfo__kiranaInfoNameMap = dict()
for rawClassID, condition in value.items():
classID = natuum.util.convertToUUID(rawClassID)
conditionList = PropertyConditionList(condition)
self._PrerequisiteInfo__kiranaInfoMap[classID] = conditionList
self._PrerequisiteInfo__kiranaInfoNameMap[classID] = conditionList.PropertyNames
value = raw.get('relationship')
if value:
self._PrerequisiteInfo__relationship = RelationshipConditionList(value)
value = raw.get('registeredTown')
if value:
self._PrerequisiteInfo__townID = value
rPropNames.add(natuum.gameClass.PROP_REGISTERED_TOWN)
value = raw.get('unregisteredTowns')
if value:
self._PrerequisiteInfo__unregTownIDs = value
rPropNames.add(natuum.gameClass.PROP_REGISTERED_TOWN)
self._PrerequisiteInfo__rPropNames = list(rPropNames) if rPropNames else None
[USER=1333341624]Property[/USER]
def GiverPropertyNames(self) -> 'None or collections.Sequence':
if self._PrerequisiteInfo__gInfo is None:
return
return self._PrerequisiteInfo__gInfo.PropertyNames
[USER=1333341624]Property[/USER]
def ReceiverPropertyNames(self) -> 'None or collections.Sequence':
return self._PrerequisiteInfo__rPropNames
[USER=1333341624]Property[/USER]
def GuardianPropertyNames(self) -> 'None or collections.Sequence':
if self._PrerequisiteInfo__guardianInfo is None:
return
return self._PrerequisiteInfo__guardianInfo.PropertyNames
[USER=1333341624]Property[/USER]
def KiranaPropertyNameMap(self) -> 'None or collections.Mapping':
if self._PrerequisiteInfo__kiranaInfoNameMap is None:
return
return self._PrerequisiteInfo__kiranaInfoNameMap
[USER=1333341624]Property[/USER]
def Relationship(self) -> 'None or RelationshipConditionList':
return self._PrerequisiteInfo__relationship
def canStart(self, questStates: 'collections.Mapping', finished: 'collections.Sequence', contracted: 'collections.Sequence') -> 'bool':
if self._PrerequisiteInfo__questID in questStates or (self._PrerequisiteInfo__questID in finished):
return False
if self._PrerequisiteInfo__activeQuestIDs is not None:
if not self._PrerequisiteInfo__activeQuestIDs.issubset(questStates):
return False
if self._PrerequisiteInfo__inactiveQuestIDs is not None:
if self._PrerequisiteInfo__inactiveQuestIDs.intersection(questStates):
return False
if self._PrerequisiteInfo__finishedQuestIDs is not None:
if not self._PrerequisiteInfo__finishedQuestIDs.issubset(finished):
return False
if self._PrerequisiteInfo__notInFinishedQuestIDs is not None:
if self._PrerequisiteInfo__notInFinishedQuestIDs.intersection(finished):
return False
if self._PrerequisiteInfo__notInDeck is not None:
if self._PrerequisiteInfo__notInDeck.intersection(contracted):
return False
return True
def checkGiverProperties(self, propMap: 'None or collections.Mapping') -> 'bool':
if self._PrerequisiteInfo__gInfo is None:
return True
return propMap and self._PrerequisiteInfo__gInfo.check(propMap)
def checkReceiverProperties(self, propMap: 'None or collections.Mapping') -> 'bool':
if self._PrerequisiteInfo__townID or self._PrerequisiteInfo__unregTownIDs:
townID = propMap.get(natuum.gameClass.PROP_REGISTERED_TOWN)
if not self._PrerequisiteInfo__checkTownID(townID):
return False
if self._PrerequisiteInfo__rInfo is None:
return True
return propMap and self._PrerequisiteInfo__rInfo.check(propMap)
def checkGuardianProperties(self, propMap: 'None or collections.Mapping') -> 'bool':
if self._PrerequisiteInfo__guardianInfo is None:
return True
return propMap and self._PrerequisiteInfo__guardianInfo.check(propMap)
def checkKiranaProperties(self, classID: 'thing.system.uuid', propMap: 'None or collections.Mapping') -> 'bool':
info = self._PrerequisiteInfo__kiranaInfoMap.get(classID)
if info is None:
return True
return propMap and info.check(propMap)
def checkGiverRelationship(self, getter: '.IGetter', giverUID: 'str') -> 'bool':
if not self._PrerequisiteInfo__relationship:
return True
return self._PrerequisiteInfo__relationship.checkGiver(getter, giverUID)
def checkTargetedRelationship(self, getter: '.IGetter') -> 'bool':
if not self._PrerequisiteInfo__relationship:
return True
return self._PrerequisiteInfo__relationship.checkTargeted(getter)
def checkReceiverTownID(self, townID: 'str') -> 'bool':
if not self._PrerequisiteInfo__checkTownID(townID):
return False
if not self._PrerequisiteInfo__rInfo:
return True
return self._PrerequisiteInfo__rInfo._checkProperty(natuum.gameClass.PROP_REGISTERED_TOWN, townID)
def __checkTownID(self, townID: 'str') -> 'bool':
if self._PrerequisiteInfo__townID:
if self._PrerequisiteInfo__townID != townID:
return False
if self._PrerequisiteInfo__unregTownIDs:
if townID in self._PrerequisiteInfo__unregTownIDs:
return False
return True
_PrerequisiteInfo__questID = None
_PrerequisiteInfo__activeQuestIDs = None
_PrerequisiteInfo__inactiveQuestIDs = None
_PrerequisiteInfo__finishedQuestIDs = None
_PrerequisiteInfo__notInFinishedQuestIDs = None
_PrerequisiteInfo__notInDeck = None
_PrerequisiteInfo__gInfo = None
_PrerequisiteInfo__rInfo = None
_PrerequisiteInfo__guardianInfo = None
_PrerequisiteInfo__kiranaInfoMap = None
_PrerequisiteInfo__kiranaInfoNameMap = None
_PrerequisiteInfo__relationship = None
_PrerequisiteInfo__rPropNames = None
_PrerequisiteInfo__townID = None
_PrerequisiteInfo__unregTownIDs = None
class QuestData:
QuestID = thing.accessor.ri(None, doc='str')
QuestName = thing.accessor.ri(None, doc='str')
QuestType = thing.accessor.ri(None, doc='QuestType')
UIInfo = thing.accessor.ri(None, doc='UIInfo')
GuideKeyword = thing.accessor.ri(None, doc='None or str')
Prerequisites = thing.accessor.ri(None, doc='PrerequisiteInfo')
ReportTargetType = thing.accessor.ri(None, doc='ReportTargetType')
ManuallyCancellable = thing.accessor.ri(None, doc='bool')
Repeatable = thing.accessor.ri(None, doc='bool')
StartableByTownUI = thing.accessor.ri(None, doc='bool')
PatternQuestSettings = thing.accessor.ri(None, doc='PatternQuestInfo')
Rewards = thing.accessor.ri(None, doc='None or ResultList')
AcceptResults = thing.accessor.ri(None, doc='None or ResultList')
ForwardResults = thing.accessor.ri(None, doc='None or ResultList')
EndResults = thing.accessor.ri(None, doc='None or ResultList')
CancelResults = thing.accessor.ri(None, doc='None or ResultList')
StartTriggers = thing.accessor.ri(None, doc='set')
TokenItem = thing.accessor.ri(None, doc='str')
NextQuestIDs = thing.accessor.ri(None, doc='list')
TurnInItems = thing.accessor.ri(None, doc='bool')
ReceiverNotReadyCutscene = thing.accessor.ri(None, doc='CutsceneInfo')
InitialCutscene = thing.accessor.ri(None, doc='CutsceneInfo')
AcceptCutscene = thing.accessor.ri(None, doc='CutsceneInfo')
RejectCutscene = thing.accessor.ri(None, doc='CutsceneInfo')
CompleteCutscene = thing.accessor.ri(None, doc='CutsceneInfo')
EndCutscene = thing.accessor.ri(None, doc='CutsceneInfo')
ProgressCutscene = thing.accessor.ri(None, doc='CutsceneInfo')
CancelCutscene = thing.accessor.ri(None, doc='CutsceneInfo')
RewardCutscene = thing.accessor.ri(None, doc='CutsceneInfo')
AcceptSelections = thing.accessor.ri(None, doc='TriggerInfo')
ForwardTrigger = thing.accessor.ri(None, doc='TriggerInfoList')
BackwardTrigger = thing.accessor.ri(None, doc='InverseTriggerInfoList')
EndTrigger = thing.accessor.ri(None, doc='TriggerInfoList')
ProgressTrigger = thing.accessor.ri(None, doc='TriggerInfoList')
CancelTrigger = thing.accessor.ri(None, doc='TriggerInfoList')
FailTrigger = thing.accessor.ri(None, doc='TriggerInfoList')
RetryTrigger = thing.accessor.ri(None, doc='TriggerInfoList')
HasStagedInitSequence = thing.accessor.ri(None, doc='bool')
HasStagedEndCutscene = thing.accessor.ri(None, doc='bool')
HasNonInstantMainTrigger = thing.accessor.ri(None, doc='bool')
MIN_TRIGGER_INDEX = 0
TRIGGER_NAME_PATTERN = re.compile('(.*?)([\\d]+)')
TRIGGER_NAME_PREFIX_START = 'start/'
TRIGGER_NAME_PREFIX_FORWARD = 'forward/'
TRIGGER_NAME_PREFIX_END = 'end/'
TRIGGER_NAME_PREFIX_PROGRESS = 'progress/'
TRIGGER_NAME_PREFIX_CANCEL = 'cancel/'
TRIGGER_NAME_PREFIX_FAIL = 'fail/'
TRIGGER_NAME_PREFIX_RETRY = 'retry/'
TRIGGER_NAME_PREFIX_SELECTION = 'selection/'
TRIGGER_NAME_SORT_KEY_MAP = {TRIGGER_NAME_PREFIX_START: 1,
TRIGGER_NAME_PREFIX_FORWARD: MIN_TRIGGER_INDEX,
TRIGGER_NAME_PREFIX_END: MIN_TRIGGER_INDEX,
TRIGGER_NAME_PREFIX_PROGRESS: math.inf,
TRIGGER_NAME_PREFIX_CANCEL: math.inf,
TRIGGER_NAME_PREFIX_FAIL: math.inf,
TRIGGER_NAME_PREFIX_RETRY: math.inf,
TRIGGER_NAME_PREFIX_SELECTION: math.inf}
def __init__(self, questID: 'str', questName: 'str', manuallyCancellable: 'bool', repeatable: 'bool', startableByTownUI: 'bool', questType: 'QuestType', uiInfo: 'natuum.quest.UIInfo', guideKeyword: 'None or str', reportTargetT
ype: 'ReportTargetType', patternQuestSettings: 'None or collections.Mapping', prerequisites: 'None or collections.Mapping', defaultRoleNameMap: 'collections.Mapping', defaultCaptionPositionMap: 'collections.Mapping', rewards: 'Non
e or collections.Sequence', acceptResults: 'None or collections.Sequence', forwardResults: 'None or collections.Sequence', endResults: 'None or collections.Sequence', cancelResults: 'None or collections.Sequence', tokenItem: 'None
or str', nextQuestIDs: 'None or collections.Sequence', acceptSelectionText: 'None or str', rejectSelectionText: 'None or str', startTriggers: 'None or collections.Sequence', forwardTrigger: 'None or collections.Mapping', endTrigg
er: 'None or collections.Mapping', progressTrigger: 'None or collections.Mapping', cancelTrigger: 'None or collections.Mapping', failTrigger: 'None or collections.Mapping', retryTrigger: 'None or collections.Mapping', enableBackwa
rdTrigger: 'bool', turnInItems: 'bool', hasStagedInitSequence: 'bool', receiverNotReadyCutscene: 'None or collections.Mapping', initialCutscene: 'None or collections.Mapping', acceptCutscene: 'None or collections.Mapping', rejectC
utscene: 'None or collections.Mapping', completeCutscene: 'None or collections.Mapping', endCutscene: 'None or collections.Mapping', progressCutscene: 'None or collections.Mapping', cancelCutscene: 'None or collections.Mapping', r
ewardCutscene: 'None or collections.Mapping'):
if not isinstance(questID, str):
raise ValueError('invalid quest ID : {}'.format(questID))
if not isinstance(questName, str):
raise ValueError('invalid quest name : {}'.format(questName))
if not all((isinstance(key, str) and isinstance(value, str) for key, value in defaultRoleNameMap.items())):
raise ValueError('invalid defaultRoleNameMap : {}'.format(defaultRoleNameMap))
self.QuestID = questID
self.QuestName = questName
self.ManuallyCancellable = manuallyCancellable
self.Repeatable = repeatable
self.StartableByTownUI = startableByTownUI
self.QuestType = questType
self.UIInfo = uiInfo
self.GuideKeyword = guideKeyword
self.ReportTargetType = reportTargetType
self.Prerequisites = PrerequisiteInfo(questID, prerequisites)
self.TurnInItems = turnInItems
if patternQuestSettings is not None:
self.PatternQuestSettings = PatternQuestInfo(patternQuestSettings['resetDelay'])
self._QuestData__defaultRoleNameMap = defaultRoleNameMap
self._QuestData__defaultCaptionPositionMap = self._QuestData__resolveCaptionPositionMap(defaultCaptionPositionMap)
self.Rewards = ResultList(rewards) if rewards else None
self.AcceptResults = ResultList(acceptResults) if acceptResults else None
self.ForwardResults = ResultList(forwardResults) if forwardResults else None
self.EndResults = ResultList(endResults) if endResults else None
self.CancelResults = ResultList(cancelResults) if cancelResults else None
self.TokenItem = tokenItem
self.NextQuestIDs = nextQuestIDs
if acceptSelectionText is not None or (rejectSelectionText is not None):
triggerName = QuestData.createTriggerName(self.TRIGGER_NAME_PREFIX_SELECTION, self.MIN_TRIGGER_INDEX)
self.AcceptSelections = TriggerInfo(TriggerTypes.SELECT_STATE, triggerName, AcceptTriggerInfo(acceptSelectionText, rejectSelectionText))
self.StartTriggers = set()
if startTriggers is not None:
index = self.MIN_TRIGGER_INDEX
for rawTrigger in startTriggers:
triggerName = QuestData.createTriggerName(self.TRIGGER_NAME_PREFIX_START, index)
self.StartTriggers.add(self._QuestData__resolveTrigger(rawTrigger, triggerName, TriggerUsageTypes.START))
index += 1
if forwardTrigger is not None:
self.ForwardTrigger = self._QuestData__resolveTriggerInfoList(forwardTrigger, self.TRIGGER_NAME_PREFIX_FORWARD)
if endTrigger is not None:
self.EndTrigger = self._QuestData__resolveTriggerInfoList(endTrigger, self.TRIGGER_NAME_PREFIX_END)
if progressTrigger is not None:
self.ProgressTrigger = self._QuestData__resolveTriggerInfoList(progressTrigger, self.TRIGGER_NAME_PREFIX_PROGRESS)
if cancelTrigger is not None:
self.CancelTrigger = self._QuestData__resolveTriggerInfoList(cancelTrigger, self.TRIGGER_NAME_PREFIX_CANCEL)
if failTrigger is not None:
self.FailTrigger = self._QuestData__resolveTriggerInfoList(failTrigger, self.TRIGGER_NAME_PREFIX_FAIL)
if retryTrigger is not None:
self.RetryTrigger = self._QuestData__resolveTriggerInfoList(retryTrigger, self.TRIGGER_NAME_PREFIX_RETRY)
if self.ForwardTrigger is not None and enableBackwardTrigger:
self.BackwardTrigger = InverseTriggerInfoList.create(self.ForwardTrigger)
else:
self.BackwardTrigger = None
if receiverNotReadyCutscene is not None:
self.ReceiverNotReadyCutscene = (self._QuestData__resolveCutscene)(*receiverNotReadyCutscene)
if initialCutscene is not None:
self.InitialCutscene = (self._QuestData__resolveCutscene)(*initialCutscene[:-1], *(hasStagedInitSequence,))
if acceptCutscene is not None:
self.AcceptCutscene = (self._QuestData__resolveCutscene)(*acceptCutscene[:-1], *(hasStagedInitSequence,))
if rejectCutscene is not None:
self.RejectCutscene = (self._QuestData__resolveCutscene)(*rejectCutscene[:-1], *(hasStagedInitSequence,))
if completeCutscene is not None:
self.CompleteCutscene = (self._QuestData__resolveCutscene)(*completeCutscene)
if endCutscene is not None:
self.EndCutscene = (self._QuestData__resolveCutscene)(*endCutscene)
if progressCutscene is not None:
self.ProgressCutscene = (self._QuestData__resolveCutscene)(*progressCutscene)
if cancelCutscene is not None:
self.CancelCutscene = (self._QuestData__resolveCutscene)(*cancelCutscene)
if rewardCutscene is not None:
self.RewardCutscene = (self._QuestData__resolveCutscene)(*rewardCutscene)
if self.AcceptSelections is not None or self.InitialCutscene is not None or self.AcceptCutscene is not None or self.RejectCutscene is not None:
self.HasStagedInitSequence = hasStagedInitSequence
else:
self.HasStagedInitSequence = False
if self.EndCutscene is not None:
self.HasStagedEndCutscene = self.EndCutscene.IsStaged
else:
self.HasStagedEndCutscene = False
hasNonInstantMainTrigger = False
if self.ForwardTrigger is not None:
hasNonInstantMainTrigger |= not self.ForwardTrigger.IsInstant
if self.EndTrigger is not None:
hasNonInstantMainTrigger |= not self.EndTrigger.IsInstant
self.HasNonInstantMainTrigger = hasNonInstantMainTrigger
@staticmethod
def createTriggerName(prefix: 'str', index: 'int') -> 'str':
return prefix + str(index)
@staticmethod
def getTriggerNameSortKey(triggerName: 'str') -> 'collections.Sequence':
match = QuestData.TRIGGER_NAME_PATTERN.fullmatch(triggerName)
if match is None:
return (QuestData.MIN_TRIGGER_INDEX, QuestData.MIN_TRIGGER_INDEX)
try:
index = int(match.group(2))
except Exception:
index = QuestData.MIN_TRIGGER_INDEX
return (
QuestData.TRIGGER_NAME_SORT_KEY_MAP.get(match.group(1), QuestData.MIN_TRIGGER_INDEX), index)
@staticmethod
def isStartTrigger(triggerName: 'str') -> 'bool':
return triggerName.startswith(QuestData.TRIGGER_NAME_PREFIX_START)
def iterateTriggerInfos(self) -> 'generator of TriggerInfo':
def iterate(trigger: 'None or natuum.quest.TriggerInfoList') -> 'generator of TriggerInfo':
if isinstance(trigger, natuum.quest.TriggerInfoList):
for subList in trigger.iterate():
for triggerInfo in subList:
yield triggerInfo
for triggerInfo in iterate(self.ForwardTrigger):
yield triggerInfo
for triggerInfo in iterate(self.EndTrigger):
yield triggerInfo
for triggerInfo in iterate(self.ProgressTrigger):
yield triggerInfo
for triggerInfo in iterate(self.CancelTrigger):
yield triggerInfo
for triggerInfo in iterate(self.FailTrigger):
yield triggerInfo
for triggerInfo in iterate(self.RetryTrigger):
yield triggerInfo
if self.StartTriggers:
for triggerInfo in self.StartTriggers:
yield triggerInfo
if self.AcceptSelections:
yield self.AcceptSelections
def __resolveTriggerInfoList(self, raw: 'collections.Sequence', prefix: 'str') -> 'None or TriggerInfoList':
index = self.MIN_TRIGGER_INDEX
orList = list()
isInstant = False
for subRaw in raw:
if not subRaw:
continue
else:
isInstantAndList = True
andList = list()
for sub in subRaw:
triggerName = QuestData.createTriggerName(prefix, index)
index += 1
trigger = self._QuestData__resolveTrigger(sub, triggerName, TriggerUsageTypes.UPDATE)
isInstantAndList &= TriggerTypes.isInstant(trigger.TriggerType)
andList.append(trigger)
isInstant |= isInstantAndList
orList.append(andList)
if orList:
return TriggerInfoList(orList, isInstant)
def __resolveTrigger(self, raw: 'collections.Mapping', triggerName: 'str', usageType: 'TriggerUsageTypes') -> 'TriggerInfo':
if not len(raw) == 1:
raise ValueError('invalid trigger data : {}'.format(raw))
for rawTriggerType, rawAuxArg in raw.items():
triggerType = TriggerTypes._convert(rawTriggerType)
if TriggerTypes.CONVERSATION == triggerType:
listenerQuestRole = rawAuxArg['listenerQuestRole']
selectionText = rawAuxArg['selectionText']
confirmText = rawAuxArg.get('confirmText')
auxArg = ConversationTriggerInfo(listenerQuestRole, selectionText, confirmText, self.QuestID, usageType)
elif TriggerTypes.GUARDIAN_CHANGED == triggerType:
fromClassID = rawAuxArg['fromClassID']
toClassID = rawAuxArg['toClassID']
auxArg = GuardianChangedTriggerInfo(fromClassID, toClassID, usageType)
elif TriggerTypes.PROPERTY == triggerType:
auxArg = PropertyTriggerInfo(rawAuxArg['condition'])
elif TriggerTypes.GUARDIAN_PROPERTY == triggerType:
auxArg = PropertyTriggerInfo(rawAuxArg['condition'])
elif TriggerTypes.RELATIONSHIP == triggerType:
auxArg = RelationshipCondition(rawAuxArg)
elif TriggerTypes.DINE == triggerType:
auxArg = DineEventTriggerInfo(rawAuxArg)
elif TriggerTypes.CONTRACT == triggerType:
auxArg = ContractInfo(rawAuxArg['classID'])
elif TriggerTypes.DECK == triggerType:
auxArg = DeckInfo(rawAuxArg['classIDs'])
elif TriggerTypes.HAND == triggerType:
auxArg = HandInfo(rawAuxArg)
elif TriggerTypes.ACTION == triggerType:
auxArg = ActionInfo(rawAuxArg['actionID'], rawAuxArg['hostRole'])
elif TriggerTypes.KILL_COUNT == triggerType:
targetClassID = rawAuxArg['classID']
targetCount = rawAuxArg['count']
questRole = rawAuxArg.get('questRole')
uiName = rawAuxArg.get('uiName')
auxArg = CountTriggerInfo(targetClassID, targetCount, questRole=questRole,
uiName=uiName)
elif TriggerTypes.ITEM_COUNT == triggerType:
targetClassID = rawAuxArg['classID']
targetCount = rawAuxArg['count']
countType = rawAuxArg['countType']
propertyCondition = rawAuxArg.get('propertyCondition')
if propertyCondition is not None:
propertyCondition = PropertyCondition(propertyCondition)
uiName = rawAuxArg.get('uiName')
auxArg = CountTriggerInfo(targetClassID, targetCount, countType=countType,
propertyCondition=propertyCondition,
uiName=uiName)
elif TriggerTypes.RANDOM_KILL_COUNT == triggerType:
candidates = rawAuxArg['classIDCandidates']
maxCount = rawAuxArg['countRange']['max']
minCount = rawAuxArg['countRange']['min']
auxArg = RandomCountTriggerInfo(candidates, maxCount, minCount)
elif TriggerTypes.RANDOM_ITEM_COUNT == triggerType:
candidates = rawAuxArg['classIDCandidates']
maxCount = rawAuxArg['countRange']['max']
minCount = rawAuxArg['countRange']['min']
countType = rawAuxArg['countType']
auxArg = RandomCountTriggerInfo(candidates, maxCount, minCount, countType)
elif TriggerTypes.PROXIMITY_ENTER == triggerType:
try:
auxArg = EnterTriggerInfo(rawAuxArg['target'], rawAuxArg['targetType'], rawAuxArg['range'])
except ValueError as e:
try:
raise ValueError('{} - {}'.format(self.QuestID, e))
finally:
e = None
del e
elif TriggerTypes.TIMER == triggerType:
auxArg = rawAuxArg['delay']
elif TriggerTypes.SELECTION == triggerType:
selectionMap = dict()
for selectionText, subMap in rawAuxArg.items():
selectionMap[selectionText] = subMap.get('nextQuestIDs')
auxArg = SelectionTriggerInfo(selectionMap)
else:
raise ValueError('invalid trigger type : {}'.format(rawTriggerType))
return TriggerInfo(triggerType, triggerName, auxArg)
def __resolveCutscene(self, scriptData: 'natuum.cutscene.ScriptData', roleNameMap: 'None or collections.Mapping', captionPositionMap: 'None or collections.Mapping', isStaged: 'bool') -> 'CutsceneInfo':
if roleNameMap is None:
roleNameMap = self._QuestData__defaultRoleNameMap
if captionPositionMap is None:
captionPositionMap = self._QuestData__defaultCaptionPositionMap
else:
captionPositionMap = self._QuestData__resolveCaptionPositionMap(captionPositionMap)
return CutsceneInfo(scriptData, roleNameMap, captionPositionMap, isStaged)
def __resolveCaptionPositionMap(self, valueMap: 'None or collections.Mapping') -> 'collections.Mapping':
converted = dict()
if valueMap is not None:
for key, value in valueMap.items():
converted[key] = natuum.cutscene._CaptionPositionTypes.fromString(value)
return converted
_QuestData__defaultRoleNameMap = None
_QuestData__defaultCaptionPositionMap = None