//----------------------------------------------------------------- /// <summary> /// 세션 로드 /// </summary> /// <param name="filepath"></param> /// <returns></returns> public static FSNSession Load(string filepath) { var newsession = new FSNSession(); var rawdata = FSNUtils.LoadTextData(filepath); JSONObject json = JSONObject.Create(rawdata); if (json != null) { // Script 관련 newsession.ScriptName = json[c_field_scriptName].str; newsession.ScriptHashKey = json[c_field_scriptHash].str; newsession.SnapshotIndex = (int)json[c_field_snapshotIndex].n; // 플래그 테이블 var flagtable = json[c_field_flagTable]; foreach (var key in flagtable.keys) { newsession.SetFlagValue(key, flagtable[key].b, true); } // 값 테이블 var valuetable = json[c_field_valueTable]; foreach (var key in valuetable.keys) { newsession.SetNumberValue(key, valuetable[key].n, true); } } else { Debug.LogErrorFormat("[FSNSession] error loading session file : {0}", filepath); } return(newsession); }
/// <summary> /// 세션 저장 /// </summary> /// <param name="session"></param> /// <param name="filepath"></param> public static void Save(FSNSession session, string filepath, string saveTitle = "") { { var json = new JSONObject(JSONObject.Type.OBJECT); // Script 관련 json.AddField(c_field_scriptName, session.ScriptName); json.AddField(c_field_scriptHash, session.ScriptHashKey); json.AddField(c_field_snapshotIndex, session.SnapshotIndex); // 세이브 정보 json.AddField(c_field_saveDateTime, FSNUtils.GenerateCurrentDateAndTimeString()); json.AddField(c_field_saveTitle, saveTitle); // 플래그 테이블 var flagtable = new JSONObject(JSONObject.Type.OBJECT); json.AddField(c_field_flagTable, flagtable); foreach (var pair in session.m_flagTable) { flagtable.AddField(pair.Key, pair.Value); } // 값 테이블 var valuetable = new JSONObject(JSONObject.Type.OBJECT); json.AddField(c_field_valueTable, valuetable); foreach (var pair in session.m_valueTable) { valuetable.AddField(pair.Key, pair.Value); } FSNUtils.SaveTextData(filepath, json.Print()); } }
/// <summary> /// 매칭되는 세이브 파일을 세팅하고 파일 정보를 읽어서 표시 /// </summary> /// <param name="path"></param> public void SetSaveFile(string path) { m_savefilePath = path; m_saveinfo = FSNSession.GetSaveFileInfo(path); var title = !string.IsNullOrEmpty(m_saveinfo.title)? m_saveinfo.title : "(제목 없음)"; var text = string.Format("{0}\n{1}", title, m_saveinfo.saveDateTime); m_text.text = text; }
/// <summary> /// 세션 (세이브 파일) 로드 /// </summary> /// <param name="filepath"></param> public void LoadSession(string filepath) { foreach (var module in m_moduleRefDict.Values) // 로딩 직전의 이벤트 호출 { module.OnBeforeLoadSession(); } var session = FSNSession.Load(filepath); m_seqEngine.LoadFromSession(session); }
//--------------------------------------------------------------------------------------- /// <summary> /// 헤더 정보 먼저 처리 /// </summary> /// <param name="scriptData"></param> /// <param name="session"></param> static void ProcessHeaders(string scriptData, FSNScriptSequence sequence, FSNSession session) { var strstream = new System.IO.StringReader(scriptData); string line = null; int linenumber = 0; // 줄 번호 while ((line = strstream.ReadLine()) != null) // 줄 단위로 읽는다. { linenumber++; FSNDebug.currentProcessingScriptLine = linenumber; // 디버깅 정보 설정 if (line.Length > 0 && line.Substring(0, 1) == c_token_PreProcessor) // 프리프로세서 기호에만 반응한다 { var commandAndParam = line.Substring(1).Split(c_whiteSpaceArray, 2); // 명령어 파라미터 구분 var command = commandAndParam[0]; var paramStr = commandAndParam.Length > 1? commandAndParam[1] : ""; // 아직까지는 header 커맨드밖에 없으므로 간단하게 if로만 체크한다. 더 늘어나면 리팩토링이 필요해질듯... if (command == "헤더" || command == "header") { sequence.Header.FromAsset(paramStr.Trim()); } else { Debug.LogErrorFormat("[FSNSequence] line {0} : unknown preprocessor command {1}", linenumber, command); } } } // 읽어들인 헤더 정보를 바탕으로 플래그/변수 기본값들 세팅하기 (선언되어있지 않은 경우에만) foreach (var pair in sequence.Header.FlagDeclarations) // 플래그 선언 { if (!session.FlagIsDeclared(pair.Key)) // 아직 선언되지 않은 경우만 세팅 { bool value = string.IsNullOrEmpty(pair.Value)? false : FSNUtils.StringToValue <bool>(pair.Value); // 초기값까지 선언한 경우 값 해독, 아니면 기본값 false session.SetFlagValue(pair.Key, value, true); } } foreach (var pair in sequence.Header.ValueDeclarations) // 값 선언 { if (!session.ValueIsDeclared(pair.Key)) // 아직 선언되지 않은 경우만 세팅 { float value = string.IsNullOrEmpty(pair.Value)? 0 : FSNUtils.StringToValue <float>(pair.Value); // 초기값까지 선언한 경우 값 해독, 아니면 기본값 false session.SetNumberValue(pair.Key, value, true); } } }
/// <summary> /// Asset에 포함된 텍스트 파일에서 스크립트를 읽는다 /// </summary> /// <returns></returns> public static FSNScriptSequence FromAsset(string assetPath, FSNSession session) { FSNDebug.currentProcessingScript = assetPath; // 디버깅 정보 세팅 var textfile = Resources.Load <TextAsset>(assetPath); if (textfile == null) { Debug.LogErrorFormat("[FSNSequence] Cannot open script asset : {0}", assetPath); } var sequence = FromString(textfile.text, session); sequence.OriginalScriptPath = assetPath; // 경로를 기록해둔다 return(sequence); }
/// <summary> /// Session에 기록된 값을 통해 스크립트 상태 로딩 /// </summary> /// <param name="session"></param> /// <returns>정상적으로 로드했다면 true, 만약 스크립트 버전이 달라서 첫 번재 snapshot부터 보여줘야하는 경우 false</returns> public bool LoadFromSession(FSNSession session) { bool fullSuccess = false; CurrentSession = session; // 로딩시의 세션 정보를 사용하도록 지정 FSNEngine.Instance.RunScript(session.ScriptName, FSNEngine.ExecuteType.LoadFromSession); // 스크립트 로드 if (m_snapshotSeq.ScriptHashKey == session.ScriptHashKey) // 저장 당시의 hashkey가 일치한다면, 저장 당시의 snapshot index로 점프 { m_snapshotTraveler.JumpToIndex(session.SnapshotIndex); m_snapshotTraveler.ExecuteSnapshotFunctions(); // 함수 실행이 자동으로 되지 않으므로, 수동으로 호출 fullSuccess = true; // 실제로 트랜지션 var curshot = m_snapshotTraveler.Current; float transTime = 0f; // 트랜지션 시간 foreach (var module in m_layerModules.Values) // 현재 로드된 모든 LayerModule 에 맞는 레이어를 찾아 각각 처리한다 { int layerID = module.LayerID; var newLayer = curshot.GetLayer(layerID) ?? FSNSnapshot.Layer.Empty; // 로딩 직후의 상황이므로 이전 화면과는 어떤 연관성도 없는 것으로 간주, 빈 레이어도 강제로 트랜지션을 걸어준다. float curtt = module.StartTransition(newLayer, curshot.InGameSetting, 0, false); // 트랜지션 if (transTime < curtt) // 제일 긴 트랜지션 시간 추적 { transTime = curtt; } } m_lastSwipeWasBackward = false; // swipe 방향성, 정방향으로 취급하기 m_swipeAvailableTime = Time.time + transTime; // 현재 시간 + 트랜지션에 걸리는 시간 뒤에 swipe가 가능해짐 } else { Debug.LogWarningFormat("[FSNSequenceEngine] Script version differs ({0}), so started from the begining", session.ScriptName); } return(fullSuccess); }
public void PopulateList() { SortedList <long, FSNSaveListUIItem> sortList = new SortedList <long, FSNSaveListUIItem>(); var fileList = FSNSession.GetSaveFileList(); // 파일 목록 읽어오기 int count = fileList.Length; for (int i = 0; i < count; i++) // 리스트 항목 생성 { var newitemgo = Instantiate <GameObject>(m_itemOriginal.gameObject); // 게임 오브젝트 복제 newitemgo.SetActive(true); newitemgo.transform.SetParent(m_gridTransform, false); var newitem = newitemgo.GetComponent <FSNSaveListUIItem>(); // 컴포넌트 찾기 newitem.SetSaveFile(fileList[i]); // 세이브 파일 지정하기 m_allItems.Add(newitem); sortList.Add(newitem.DateTime.Ticks, newitem); // 소팅 리스트에 집어넣기 newitem.GetComponent <Toggle>().onValueChanged.AddListener(OnItemValueChanged); // 이벤트 추가 } int sortlistcount = sortList.Count; foreach (var item in sortList.Values) // 역순으로 아이템 순서 맞추기 { item.transform.SetSiblingIndex(sortlistcount--); } if (m_saveMode) // 저장 모드일 경우, 새 세이브 슬롯도 추가 { m_newSaveItem.gameObject.SetActive(true); // 활성화하기 m_newSaveItem.transform.SetAsFirstSibling(); // 순서 맞추기 (제일 위로) var toggleComp = m_newSaveItem.GetComponent <Toggle>(); // 이벤트 추가 var onChanged = toggleComp.onValueChanged; onChanged.RemoveAllListeners(); onChanged.AddListener(OnItemValueChanged); m_allItems.Add(m_newSaveItem); // 똑같이 아이템 리스트에 추가 } }
//--------------------------------------------------------------------------------------- /// <summary> /// 헤더 정보 먼저 처리 /// </summary> /// <param name="scriptData"></param> /// <param name="session"></param> static void ProcessHeaders(string scriptData, FSNScriptSequence sequence, FSNSession session) { var strstream = new System.IO.StringReader(scriptData); string line = null; int linenumber = 0; // 줄 번호 while ((line = strstream.ReadLine()) != null) // 줄 단위로 읽는다. { linenumber++; FSNDebug.currentProcessingScriptLine = linenumber; // 디버깅 정보 설정 if (line.Length > 0 && line.Substring(0,1) == c_token_PreProcessor) // 프리프로세서 기호에만 반응한다 { var commandAndParam = line.Substring(1).Split(c_whiteSpaceArray, 2); // 명령어 파라미터 구분 var command = commandAndParam[0]; var paramStr = commandAndParam.Length > 1? commandAndParam[1] : ""; // 아직까지는 header 커맨드밖에 없으므로 간단하게 if로만 체크한다. 더 늘어나면 리팩토링이 필요해질듯... if (command == "헤더" || command == "header") { sequence.Header.FromAsset(paramStr.Trim()); } else { Debug.LogErrorFormat("[FSNSequence] line {0} : unknown preprocessor command {1}", linenumber, command); } } } // 읽어들인 헤더 정보를 바탕으로 플래그/변수 기본값들 세팅하기 (선언되어있지 않은 경우에만) foreach (var pair in sequence.Header.FlagDeclarations) // 플래그 선언 { if (!session.FlagIsDeclared(pair.Key)) // 아직 선언되지 않은 경우만 세팅 { bool value = string.IsNullOrEmpty(pair.Value)? false : FSNUtils.StringToValue<bool>(pair.Value); // 초기값까지 선언한 경우 값 해독, 아니면 기본값 false session.SetFlagValue(pair.Key, value, true); } } foreach (var pair in sequence.Header.ValueDeclarations) // 값 선언 { if (!session.ValueIsDeclared(pair.Key)) // 아직 선언되지 않은 경우만 세팅 { float value = string.IsNullOrEmpty(pair.Value)? 0 : FSNUtils.StringToValue<float>(pair.Value); // 초기값까지 선언한 경우 값 해독, 아니면 기본값 false session.SetNumberValue(pair.Key, value, true); } } }
/// <summary> /// 문자열으로 스크립트 파싱 /// </summary> /// <param name="scriptData"></param> /// <returns></returns> public static FSNScriptSequence FromString(string scriptData, FSNSession session) { // 디버깅 세션 세팅 FSNDebug.currentRuntimeStage = FSNDebug.RuntimeStage.Compile; var sequence = new FSNScriptSequence(); sequence.OriginalScriptPath = "(string)"; sequence.ScriptHashKey = GenerateHashKeyFromScript(scriptData); // 해시키 생성해두기 (세이브 파일과 스크립트 파일 버전 체크용) // ===== FIRST PASS : 헤더 파일 먼저 해석 ================================== ProcessHeaders(scriptData, sequence, session); // ===== SECOND PASS : 나머지 스크립트 요소들 해석 ========================= var strstream = new System.IO.StringReader(scriptData); // 스크립트 해석 상태값들 CommandGenerateProtocol protocol = new CommandGenerateProtocol(); // flags Segments.Period periodSeg = null; // Period 세그먼트. 먼저 만들어놓고 있다가 적당한 때에 삽입한다. (스크립트와 실제 세그먼트 순서가 다르기 때문) bool textMultilineMode = false; // 텍스트 여러줄 처리중인지 (//) string multilineText = ""; // 멀티라인 모드에서, 텍스트 처리중일 때 // // ** 스크립트 로드 후 첫번째 스냅샷에서 다시 이전으로 돌아가는 것은 불가능하므로, 맨 처음에 oneway 컨트롤 세그먼트를 추가해준다 var onewayAtFirstSeg = new Segments.Control(); onewayAtFirstSeg.controlType = Segments.Control.ControlType.Oneway; var onewaySegInfo = new FSNScriptSequence.Parser.GeneratedSegmentInfo() { newSeg = onewayAtFirstSeg }; protocol.PushSegment(onewaySegInfo); // string line = null; int linenumber = 0; // 줄 번호 while ((line = strstream.ReadLine()) != null) // 줄 단위로 읽는다. { line = sequence.Header.Macros.Replace(line); // 정적 매크로 치환 linenumber++; FSNDebug.currentProcessingScriptLine = linenumber; // 디버깅 정보 설정 if (!textMultilineMode && line.Length == 0) // * 빈 줄은 스루. 단 여러줄 텍스트 모드일 경우 빈 줄에서는 여러줄 모드를 끝내게 한다. continue; if (line.EndsWith(c_token_LineConcat)) // * 여러줄 텍스트로 지정된 경우, 자동으로 여러줄 모드로. 해당 라인 붙이기 { textMultilineMode = true; if (multilineText.Length > 0) // 이미 쌓여있는 텍스트가 있다면 공백 추가 multilineText += "\n"; multilineText += line.Substring(0, line.Length - c_token_LineConcat.Length); } else { var pretoken = line.Length > 0? line.Substring(0, 1) : ""; switch (pretoken) // 앞쪽 토큰으로 명령 구분 { case c_token_Comment: // * 주석 // 스루. 뭐 왜 뭐 주석인데 뭐 break; case c_token_PreProcessor: // * 전처리 구문 // 프리프로세서는 첫번째 패스에서 처리함. break; case c_token_Command: // * 명령 { var commandAndParam = line.Substring(1).Split(c_whiteSpaceArray, 2); // 명령어 파라미터 구분 var command = commandAndParam[0]; var paramStr = commandAndParam.Length > 1? commandAndParam[1] : ""; CommandSegmentGenerateFunc genfunc = null; s_aliasToSegFunction.TryGetValue(command, out genfunc); if(genfunc == null) // 등록된 명령어인지 체크 { Debug.LogError("Unknown command : " + command); } else { protocol.parameters = ParseParameters(paramStr); genfunc(protocol); } } break; case c_token_HardLabel: // * hard label { var labelSeg = new Segments.Label(); labelSeg.labelName = line.Substring(1); labelSeg.labelType = Segments.Label.LabelType.Hard; var segInfo = new GeneratedSegmentInfo(); segInfo.newSeg = labelSeg; segInfo.usePrevPeriod = true; // Label 전에 period로 다 출력해야함 segInfo.selfPeriod = false; protocol.PushSegment(segInfo); } break; case c_token_SoftLabel: // * soft label { var labelSeg = new Segments.Label(); labelSeg.labelName = line.Substring(1); labelSeg.labelType = Segments.Label.LabelType.Soft; var segInfo = new GeneratedSegmentInfo(); segInfo.newSeg = labelSeg; segInfo.usePrevPeriod = true; // Label 전에 period로 다 출력해야함 segInfo.selfPeriod = false; protocol.PushSegment(segInfo); } break; case c_token_Period: // * period if (line.Length == 1) // . 한글자일 때 - 일반 period { if(periodSeg != null) // * 이미 period 명령어가 대기중일 때, 기존 명령어를 먼저 처리한다 { sequence.m_segments.Add(periodSeg); } periodSeg = new Segments.Period(); } else if(line.Length == 2 && line[1].ToString() == c_token_Period) // .. 으로 두 글자일 때 - 연결 period, 만약 period가 이전에 등장했다면 연결으로 변경 { Segments.Period lastPeriodseg; if(periodSeg != null) // 처리 안된 period가 있을 경우, 이것을 chaining으로 변경해준다 { lastPeriodseg = periodSeg; } else if((lastPeriodseg = sequence.m_segments[sequence.m_segments.Count - 1] as Segments.Period) .type == Segment.Type.Period) // 아닐 경우, 가장 마지막으로 추가된 세그먼트가 period를 chaining으로 변경한다 { // } else { // 그도 아닐 경우 새로 period 생성 periodSeg = new Segments.Period(); lastPeriodseg = periodSeg; } lastPeriodseg.isChaining = true; // 선택한 period에 chaining속성 부여 } else { Debug.LogError("invalid command... is it a period command?"); } break; case c_token_ForceText: default: // * 아무 토큰도 없음 : 텍스트 { if (line.Length > 0 && line[0].ToString() == c_token_ForceText) // 만약 강제 텍스트 토큰 (~) 이 붙어있었다면, 해당 토큰 제거 line = line.Substring(1); multilineText += multilineText.Length > 0? "\n" + line : line; var textSeg = new Segments.Text(); textSeg.text = multilineText; textSeg.textType = Segments.Text.TextType.Normal; multilineText = ""; // 멀티라인 텍스트 보관되어있던 것을 초기화 textMultilineMode = false; var segInfo = new GeneratedSegmentInfo(); segInfo.newSeg = textSeg; segInfo.usePrevPeriod = true; // 출력 명령어임 segInfo.selfPeriod = true; // 스스로 period를 포함함 protocol.PushSegment(segInfo); } break; } GeneratedSegmentInfo newSegInfo = null; while ((newSegInfo = protocol.PullSegment()) != null) // 새로 생성된 시퀀스 모두 처리 { if (newSegInfo.usePrevPeriod && periodSeg != null) // * 선행 period를 먼저 처리해야하는 상황 { periodSeg.scriptLineNumber = linenumber; // 줄번호 기록? sequence.m_segments.Add(periodSeg); periodSeg = null; } newSegInfo.newSeg.scriptLineNumber = linenumber; // 줄번호 기록 sequence.m_segments.Add(newSegInfo.newSeg); // 시퀀스 추가 if (newSegInfo.newSeg.type == Segment.Type.Label) // 라벨일 경우 등록 sequence.RegisterLabelSegment(); if(newSegInfo.selfPeriod) // * 방금 추가된 세그먼트가 period를 포함하는 개념이라면, period 대기시켜놓기 { periodSeg = new Segments.Period(); } } } } if(periodSeg != null) // 끝날 때까지 처리되지 않은 period가 있다면 여기서 추가해준다 { periodSeg.scriptLineNumber = linenumber; // 줄번호 기록? sequence.m_segments.Add(periodSeg); periodSeg = null; } // 디버깅 세션 세팅 FSNDebug.currentRuntimeStage = FSNDebug.RuntimeStage.Runtime; return sequence; }
/// <summary> /// Asset에 포함된 텍스트 파일에서 스크립트를 읽는다 /// </summary> /// <returns></returns> public static FSNScriptSequence FromAsset(string assetPath, FSNSession session) { FSNDebug.currentProcessingScript = assetPath; // 디버깅 정보 세팅 var textfile = Resources.Load<TextAsset>(assetPath); if (textfile == null) { Debug.LogErrorFormat("[FSNSequence] Cannot open script asset : {0}", assetPath); } var sequence = FromString(textfile.text, session); sequence.OriginalScriptPath = assetPath; // 경로를 기록해둔다 return sequence; }
/// <summary> /// 문자열으로 스크립트 파싱 /// </summary> /// <param name="scriptData"></param> /// <returns></returns> public static FSNScriptSequence FromString(string scriptData, FSNSession session) { // 디버깅 세션 세팅 FSNDebug.currentRuntimeStage = FSNDebug.RuntimeStage.Compile; var sequence = new FSNScriptSequence(); sequence.OriginalScriptPath = "(string)"; sequence.ScriptHashKey = GenerateHashKeyFromScript(scriptData); // 해시키 생성해두기 (세이브 파일과 스크립트 파일 버전 체크용) // ===== FIRST PASS : 헤더 파일 먼저 해석 ================================== ProcessHeaders(scriptData, sequence, session); // ===== SECOND PASS : 나머지 스크립트 요소들 해석 ========================= var strstream = new System.IO.StringReader(scriptData); // 스크립트 해석 상태값들 CommandGenerateProtocol protocol = new CommandGenerateProtocol(); // flags Segments.Period periodSeg = null; // Period 세그먼트. 먼저 만들어놓고 있다가 적당한 때에 삽입한다. (스크립트와 실제 세그먼트 순서가 다르기 때문) bool textMultilineMode = false; // 텍스트 여러줄 처리중인지 (//) string multilineText = ""; // 멀티라인 모드에서, 텍스트 처리중일 때 // // ** 스크립트 로드 후 첫번째 스냅샷에서 다시 이전으로 돌아가는 것은 불가능하므로, 맨 처음에 oneway 컨트롤 세그먼트를 추가해준다 var onewayAtFirstSeg = new Segments.Control(); onewayAtFirstSeg.controlType = Segments.Control.ControlType.Oneway; var onewaySegInfo = new FSNScriptSequence.Parser.GeneratedSegmentInfo() { newSeg = onewayAtFirstSeg }; protocol.PushSegment(onewaySegInfo); // string line = null; int linenumber = 0; // 줄 번호 while ((line = strstream.ReadLine()) != null) // 줄 단위로 읽는다. { line = sequence.Header.Macros.Replace(line); // 정적 매크로 치환 linenumber++; FSNDebug.currentProcessingScriptLine = linenumber; // 디버깅 정보 설정 if (!textMultilineMode && line.Length == 0) // * 빈 줄은 스루. 단 여러줄 텍스트 모드일 경우 빈 줄에서는 여러줄 모드를 끝내게 한다. { continue; } if (line.EndsWith(c_token_LineConcat)) // * 여러줄 텍스트로 지정된 경우, 자동으로 여러줄 모드로. 해당 라인 붙이기 { textMultilineMode = true; if (multilineText.Length > 0) // 이미 쌓여있는 텍스트가 있다면 공백 추가 { multilineText += "\n"; } multilineText += line.Substring(0, line.Length - c_token_LineConcat.Length); } else { var pretoken = line.Length > 0? line.Substring(0, 1) : ""; switch (pretoken) // 앞쪽 토큰으로 명령 구분 { case c_token_Comment: // * 주석 // 스루. 뭐 왜 뭐 주석인데 뭐 break; case c_token_PreProcessor: // * 전처리 구문 // 프리프로세서는 첫번째 패스에서 처리함. break; case c_token_Command: // * 명령 { var commandAndParam = line.Substring(1).Split(c_whiteSpaceArray, 2); // 명령어 파라미터 구분 var command = commandAndParam[0]; var paramStr = commandAndParam.Length > 1? commandAndParam[1] : ""; CommandSegmentGenerateFunc genfunc = null; s_aliasToSegFunction.TryGetValue(command, out genfunc); if (genfunc == null) // 등록된 명령어인지 체크 { Debug.LogError("Unknown command : " + command); } else { protocol.parameters = ParseParameters(paramStr); genfunc(protocol); } } break; case c_token_HardLabel: // * hard label { var labelSeg = new Segments.Label(); labelSeg.labelName = line.Substring(1); labelSeg.labelType = Segments.Label.LabelType.Hard; var segInfo = new GeneratedSegmentInfo(); segInfo.newSeg = labelSeg; segInfo.usePrevPeriod = true; // Label 전에 period로 다 출력해야함 segInfo.selfPeriod = false; protocol.PushSegment(segInfo); } break; case c_token_SoftLabel: // * soft label { var labelSeg = new Segments.Label(); labelSeg.labelName = line.Substring(1); labelSeg.labelType = Segments.Label.LabelType.Soft; var segInfo = new GeneratedSegmentInfo(); segInfo.newSeg = labelSeg; segInfo.usePrevPeriod = true; // Label 전에 period로 다 출력해야함 segInfo.selfPeriod = false; protocol.PushSegment(segInfo); } break; case c_token_Period: // * period if (line.Length == 1) // . 한글자일 때 - 일반 period { if (periodSeg != null) // * 이미 period 명령어가 대기중일 때, 기존 명령어를 먼저 처리한다 { sequence.m_segments.Add(periodSeg); } periodSeg = new Segments.Period(); } else if (line.Length == 2 && line[1].ToString() == c_token_Period) // .. 으로 두 글자일 때 - 연결 period, 만약 period가 이전에 등장했다면 연결으로 변경 { Segments.Period lastPeriodseg; if (periodSeg != null) // 처리 안된 period가 있을 경우, 이것을 chaining으로 변경해준다 { lastPeriodseg = periodSeg; } else if ((lastPeriodseg = sequence.m_segments[sequence.m_segments.Count - 1] as Segments.Period) .type == Segment.Type.Period) // 아닐 경우, 가장 마지막으로 추가된 세그먼트가 period를 chaining으로 변경한다 { // } else { // 그도 아닐 경우 새로 period 생성 periodSeg = new Segments.Period(); lastPeriodseg = periodSeg; } lastPeriodseg.isChaining = true; // 선택한 period에 chaining속성 부여 } else { Debug.LogError("invalid command... is it a period command?"); } break; case c_token_ForceText: default: // * 아무 토큰도 없음 : 텍스트 { if (line.Length > 0 && line[0].ToString() == c_token_ForceText) // 만약 강제 텍스트 토큰 (~) 이 붙어있었다면, 해당 토큰 제거 { line = line.Substring(1); } multilineText += multilineText.Length > 0? "\n" + line : line; var textSeg = new Segments.Text(); textSeg.text = multilineText; textSeg.textType = Segments.Text.TextType.Normal; multilineText = ""; // 멀티라인 텍스트 보관되어있던 것을 초기화 textMultilineMode = false; var segInfo = new GeneratedSegmentInfo(); segInfo.newSeg = textSeg; segInfo.usePrevPeriod = true; // 출력 명령어임 segInfo.selfPeriod = true; // 스스로 period를 포함함 protocol.PushSegment(segInfo); } break; } GeneratedSegmentInfo newSegInfo = null; while ((newSegInfo = protocol.PullSegment()) != null) // 새로 생성된 시퀀스 모두 처리 { if (newSegInfo.usePrevPeriod && periodSeg != null) // * 선행 period를 먼저 처리해야하는 상황 { periodSeg.scriptLineNumber = linenumber; // 줄번호 기록? sequence.m_segments.Add(periodSeg); periodSeg = null; } newSegInfo.newSeg.scriptLineNumber = linenumber; // 줄번호 기록 sequence.m_segments.Add(newSegInfo.newSeg); // 시퀀스 추가 if (newSegInfo.newSeg.type == Segment.Type.Label) // 라벨일 경우 등록 { sequence.RegisterLabelSegment(); } if (newSegInfo.selfPeriod) // * 방금 추가된 세그먼트가 period를 포함하는 개념이라면, period 대기시켜놓기 { periodSeg = new Segments.Period(); } } } } if (periodSeg != null) // 끝날 때까지 처리되지 않은 period가 있다면 여기서 추가해준다 { periodSeg.scriptLineNumber = linenumber; // 줄번호 기록? sequence.m_segments.Add(periodSeg); periodSeg = null; } // 디버깅 세션 세팅 FSNDebug.currentRuntimeStage = FSNDebug.RuntimeStage.Runtime; return(sequence); }
/// <summary> /// 새 게임시 Session 오브젝트를 준비한다. /// </summary> public void PrepareNewSession() { CurrentSession = new FSNSession(); }
/// <summary> /// 세션 저장 /// </summary> /// <param name="session"></param> /// <param name="filepath"></param> public static void Save(FSNSession session, string filepath, string saveTitle = "") { { var json = new JSONObject(JSONObject.Type.OBJECT); // Script 관련 json.AddField(c_field_scriptName, session.ScriptName); json.AddField(c_field_scriptHash, session.ScriptHashKey); json.AddField(c_field_snapshotIndex, session.SnapshotIndex); // 세이브 정보 json.AddField(c_field_saveDateTime, FSNUtils.GenerateCurrentDateAndTimeString()); json.AddField(c_field_saveTitle, saveTitle); // 플래그 테이블 var flagtable = new JSONObject(JSONObject.Type.OBJECT); json.AddField(c_field_flagTable, flagtable); foreach(var pair in session.m_flagTable) { flagtable.AddField(pair.Key, pair.Value); } // 값 테이블 var valuetable = new JSONObject(JSONObject.Type.OBJECT); json.AddField(c_field_valueTable, valuetable); foreach(var pair in session.m_valueTable) { valuetable.AddField(pair.Key, pair.Value); } FSNUtils.SaveTextData(filepath, json.Print()); } }
//----------------------------------------------------------------- /// <summary> /// 세션 로드 /// </summary> /// <param name="filepath"></param> /// <returns></returns> public static FSNSession Load(string filepath) { var newsession = new FSNSession(); var rawdata = FSNUtils.LoadTextData(filepath); JSONObject json = JSONObject.Create(rawdata); if (json != null) { // Script 관련 newsession.ScriptName = json[c_field_scriptName].str; newsession.ScriptHashKey = json[c_field_scriptHash].str; newsession.SnapshotIndex = (int)json[c_field_snapshotIndex].n; // 플래그 테이블 var flagtable = json[c_field_flagTable]; foreach(var key in flagtable.keys) { newsession.SetFlagValue(key, flagtable[key].b, true); } // 값 테이블 var valuetable = json[c_field_valueTable]; foreach(var key in valuetable.keys) { newsession.SetNumberValue(key, valuetable[key].n, true); } } else { Debug.LogErrorFormat("[FSNSession] error loading session file : {0}", filepath); } return newsession; }
/// <summary> /// 세션 (세이브 파일) 저장 /// </summary> /// <param name="filepath"></param> public void SaveSession(string filepath, string memo = "") { m_seqEngine.SaveToCurrentSession(); FSNSession.Save(m_seqEngine.CurrentSession, filepath, memo); }