// コマンドを実行し、実行後のEventDataLine#IDを返す public int Exec() { SRC.LogDebug(Name.ToString(), args.Select(x => x.strArg).ToArray()); try { return(ExecInternal()); } catch (EventErrorException ex) { Event.DisplayEventErrorMessage(ex?.EventData.ID ?? EventData.ID, ex.Message); return(-1); } catch (NotImplementedException ex) { SRC.Log.LogWarning("NotImplemented: " + EventData.Data); return(EventData.NextID); } catch (Exception ex) { SRC.Log.LogError(ex.Message, ex); if (Strings.LCase(GeneralLib.ListIndex(EventData.Data, 1)) == "talk") { Event.DisplayEventErrorMessage(EventData.ID, "Talkコマンド実行中に不正な処理が行われました。" + "MIDIがソフトウェアシンセサイザで演奏されているか、" + "フォントキャッシュが壊れている可能性があります。" + "詳しくはSRC公式ホームページの「よくある質問集」をご覧下さい。"); } else if (Strings.LCase(GeneralLib.ListIndex(EventData.Data, 1)) == "autotalk") { Event.DisplayEventErrorMessage(EventData.ID, "AutoTalkコマンド実行中に不正な処理が行われました。" + "MIDIがソフトウェアシンセサイザで演奏されているか、" + "フォントキャッシュが壊れている可能性があります。" + "詳しくはSRC公式ホームページの「よくある質問集」をご覧下さい。"); } else { Event.DisplayEventErrorMessage(EventData.ID, "イベントデータが不正です"); } return(-1); } }
public string SearchFile(string name) { var path = ImageFilders() .Select(x => SRC.FileSystem.PathCombine(x, name)) .FirstOrDefault(x => SRC.FileSystem.FileExists(x)); SRC.LogDebug($"{name} -> {path}"); return(path); }
// イベントの実行 public void HandleEvent(params string[] Args) { SRC.LogDebug(">", Args); // 画面入力をロック var prev_is_gui_locked = GUI.IsGUILocked; if (!GUI.IsGUILocked) { GUI.LockGUI(); } // 現在選択されているユニット&ターゲットをイベント用に設定 // (SearchLabel()実行時の式計算用にあらかじめ設定しておく) SelectedUnitForEvent = Commands.SelectedUnit; // 引数に指定されたユニットを優先 if (Args.Length > 1) { if (SRC.PList.IsDefined(Args[1])) { var p = SRC.PList.Item(Args[1]); if (p.Unit is object) { SelectedUnitForEvent = p.Unit; } } } SelectedTargetForEvent = Commands.SelectedTarget; // イベントキューを作成 // XXX キューをいつ処理しきるのか? //event_que_idx = Information.UBound(EventQue); switch (Args[0]) { case "プロローグ": { EventQue.Enqueue("プロローグ"); SRC.Stage = "プロローグ"; break; } case "エピローグ": { EventQue.Enqueue("エピローグ"); SRC.Stage = "エピローグ"; break; } case "破壊": { EventQue.Enqueue("破壊 " + Args[1]); var p = SRC.PList.Item(Args[1]); var uparty = p.Party; if (p.Unit != null) { var u = p.Unit; // 格納されていたユニットも破壊しておく while (u.CountUnitOnBoard() > 0) { var onBoardUnit = u.UnitOnBoards.First(); onBoardUnit.UnloadUnit(onBoardUnit.ID); onBoardUnit.Status = "破壊"; onBoardUnit.HP = 0; EventQue.Enqueue("マップ攻撃破壊 " + onBoardUnit.MainPilot().ID); } uparty = u.Party0; } // 全滅の判定 var flag = false; foreach (Unit currentU in SRC.UList.Items) { if ((currentU.Party0 ?? "") == (uparty ?? "") && currentU.Status == "出撃" && !currentU.IsConditionSatisfied("憑依")) { flag = true; break; } } if (!flag) { EventQue.Enqueue("全滅 " + uparty); } break; } case "マップ攻撃破壊": { EventQue.Enqueue("マップ攻撃破壊 " + Args[1]); var p = SRC.PList.Item(Args[1]); var uparty = p.Party; if (p.Unit != null) { var u = p.Unit; // 格納されていたユニットも破壊しておく while (u.CountUnitOnBoard() > 0) { var onBoardUnit = u.UnitOnBoards.First(); onBoardUnit.UnloadUnit(onBoardUnit.ID); onBoardUnit.Status = "破壊"; onBoardUnit.HP = 0; EventQue.Enqueue("マップ攻撃破壊 " + onBoardUnit.MainPilot().ID); } uparty = u.Party0; } // XXX 全滅の処理どこでやってんの? break; } case "ターン": EventQue.Enqueue(string.Join(" ", "ターン ", "全", Args[2])); EventQue.Enqueue(string.Join(" ", "ターン ", Args[1], Args[2])); break; case "損傷率": EventQue.Enqueue(string.Join(" ", "損傷率", Args[1], Args[2])); break; case "攻撃": EventQue.Enqueue(string.Join(" ", "攻撃", Args[1], Args[2])); break; case "攻撃後": EventQue.Enqueue(string.Join(" ", "攻撃後", Args[1], Args[2])); break; case "会話": EventQue.Enqueue(string.Join(" ", "会話", Args[1], Args[2])); break; case "接触": EventQue.Enqueue(string.Join(" ", "接触", Args[1], Args[2])); break; case "進入": EventQue.Enqueue(string.Join(" ", "進入", Args[1], Args[2], Args[3])); EventQue.Enqueue(string.Join(" ", "進入", Args[1], Map.Terrain(Conversions.ToInteger(Args[2]), Conversions.ToInteger(Args[3])).Name)); if (Conversions.ToInteger(Args[2]) == 1) { EventQue.Enqueue(string.Join(" ", "脱出", Args[1], "W")); } else if (Conversions.ToInteger(Args[2]) == Map.MapWidth) { EventQue.Enqueue(string.Join(" ", "脱出", Args[1], "E")); } else if (Conversions.ToInteger(Args[3]) == 1) { EventQue.Enqueue(string.Join(" ", "脱出", Args[1], "N")); } else if (Conversions.ToInteger(Args[3]) == Map.MapHeight) { EventQue.Enqueue(string.Join(" ", "脱出", Args[1], "S")); } break; case "収納": EventQue.Enqueue(string.Join(" ", "収納", Args[1])); break; case "使用": EventQue.Enqueue(string.Join(" ", "使用", Args[1], Args[2])); break; case "使用後": EventQue.Enqueue(string.Join(" ", "使用後", Args[1], Args[2])); break; case "行動終了": EventQue.Enqueue(string.Join(" ", "行動終了", Args[1])); break; case "ユニットコマンド": var cmd1 = string.Join(" ", "ユニットコマンド", Args[1], Args[2]); if (IsEventDefined(cmd1)) { EventQue.Enqueue(cmd1); } else { var cmd2 = string.Join(" ", "ユニットコマンド", Args[1], SRC.PList.Item(Args[2]).Unit.Name); EventQue.Enqueue(cmd2); } break; default: EventQue.Enqueue(string.Join(" ", Args)); break; } if (CallDepth > MaxCallDepth) { GUI.ErrorMessage("サブルーチンの呼び出し階層が" + SrcFormatter.Format(MaxCallDepth) + "を超えているため、イベントの処理が出来ません"); CallDepth = MaxCallDepth; return; } // 現在の状態を保存 ArgIndexStack[CallDepth] = ArgIndex; VarIndexStack[CallDepth] = VarIndex; ForIndexStack[CallDepth] = ForIndex; SaveBasePoint(); // 呼び出し階層数をインクリメント var prev_call_depth = CallDepth; CallDepth = (CallDepth + 1); // 各イベントを発生させる // XXX キューのスキップすんの? //i = event_que_idx; SRC.IsCanceled = false; while (EventQue.Count > 0) { var eventItem = EventQue.Dequeue(); SRC.LogDebug("EventQue.Dequeue", eventItem); // 前のイベントで他のユニットが出現している可能性があるので // 本当に全滅したのか判定 if (GeneralLib.LIndex(eventItem, 1) == "全滅") { var uparty = GeneralLib.LIndex(eventItem, 2); if (SRC.UList.Items.Any(u => u.Party0 == uparty && u.Status == "出撃" && !u.IsConditionSatisfied("憑依"))) { continue; } } CurrentLabel = -1; var ret = -1; var main_event_done = false; while (true) { // 現在選択されているユニット&ターゲットをイベント用に設定 // SearchLabel()で入れ替えられる可能性があるので、毎回設定し直す必要あり SelectedUnitForEvent = Commands.SelectedUnit; // 引数に指定されたユニットを優先 if (Args.Length > 1) { if (SRC.PList.IsDefined(Args[1])) { var p = SRC.PList.Item(Args[1]); if (p.Unit is object) { SelectedUnitForEvent = p.Unit; } } } SelectedTargetForEvent = Commands.SelectedTarget; // 実行するイベントラベルを探す do { if (Information.IsNumeric(eventItem)) { // 数値の場合はラベルの示す位置が指定されたとみなす // (マップコマンド、ユニットコマンド) if (CurrentLabel < 0) { ret = Conversions.ToInteger(eventItem); } else { ret = -1; } } else { // 数値以外はラベルを探す ret = SearchLabel(eventItem, CurrentLabel + 1); } // ラベルが見つからなければ終わり if (ret < 0) { break; } CurrentLabel = ret; if (!EventData[ret].IsAlwaysEventLabel) { // 常時イベントではないイベントは1度しか実行しない if (main_event_done) { ret = -1; } else { main_event_done = true; } } }while (ret < 0); if (ret < 0) { break; } // 戦闘後のイベント実行前にはいくつかの後始末が必要 if (!EventData[ret].IsAlwaysEventLabel) { if (Args[0] == "破壊" || Args[0] == "損傷率" || Args[0] == "攻撃後" || Args[0] == "全滅") { // 画面をクリア if (GUI.MainFormVisible) { Status.ClearUnitStatus(); GUI.RedrawScreen(); } // メッセージウィンドウを閉じる if (GUI.MessageFormVisible) { GUI.CloseMessageForm(); } } } // ラベルの行は実行しても無駄なので ret = ret + 1; GUI.DoEvents(); // イベントの各コマンドを実行 do { CurrentLineNum = ret; if (CurrentLineNum >= EventCmd.Count) { break; } ret = EventCmd[CurrentLineNum].Exec(); }while (ret > 0); // ステージが終了 or キャンセル? if (SRC.IsScenarioFinished || SRC.IsCanceled) { break; } } // ステージが終了 or キャンセル? if (SRC.IsScenarioFinished || SRC.IsCanceled) { break; } } ; if (CallDepth >= 0) { // 呼び出し階層数を元に戻す // (サブルーチン内でExitが呼ばれることがあるので単純に-1出来ない) CallDepth = prev_call_depth; // イベント実行前の状態に復帰 ArgIndex = ArgIndexStack[CallDepth]; VarIndex = VarIndexStack[CallDepth]; ForIndex = ForIndexStack[CallDepth]; } else { ArgIndex = 0; VarIndex = 0; ForIndex = 0; } //// イベントキューを元に戻す //Array.Resize(EventQue, GeneralLib.MinLng(event_que_idx - 1, Information.UBound(EventQue)) + 1); // フォント設定をデフォルトに戻す // XXX 位置はリセットしない? GUI.ResetDrawString(); GUI.PermanentStringMode = false; GUI.KeepStringMode = false; // オブジェクト色をデフォルトに戻す // XXX これはEventsが持ってていいのか? ObjColor = Color.White; ObjFillColor = Color.White; ObjFillStyle = FillStyle.VbFSTransparent; ObjDrawWidth = 1; ObjDrawOption = ""; // 描画の基準座標位置を元に戻す RestoreBasePoint(); // 画面入力のロックを解除 if (!prev_is_gui_locked) { GUI.UnlockGUI(); } }
// ラベルの検索 public int SearchLabel(string lname, int start = -1) { // ラベルの各要素をあらかじめ解析 // XXX Indexがずれていて辛い。 string[] litem; int llen = GeneralLib.ListSplit(lname, out litem); // XXX 死にたい litem = (new string[] { "" }).Concat(litem).ToArray(); // ラベルの種類を判定 LabelType ltype; var lnum = new string[5]; var is_unit = new bool[5]; var is_num = new bool[5]; var is_condition = new bool[5]; var revrersible = false; switch (litem[1] ?? "") { case "プロローグ": { ltype = LabelType.PrologueEventLabel; break; } case "スタート": { ltype = LabelType.StartEventLabel; break; } case "エピローグ": { ltype = LabelType.EpilogueEventLabel; break; } case "ターン": ltype = LabelType.TurnEventLabel; if (Information.IsNumeric(litem[2])) { is_num[2] = true; } lnum[2] = GeneralLib.StrToLng(litem[2]).ToString(); break; case "損傷率": ltype = LabelType.DamageEventLabel; is_unit[2] = true; is_num[3] = true; lnum[3] = GeneralLib.StrToLng(litem[3]).ToString(); break; case "破壊": case "マップ攻撃破壊": ltype = LabelType.DestructionEventLabel; is_unit[2] = true; break; case "全滅": ltype = LabelType.TotalDestructionEventLabel; break; case "攻撃": ltype = LabelType.AttackEventLabel; revrersible = true; is_unit[2] = true; is_unit[3] = true; break; case "攻撃後": ltype = LabelType.AfterAttackEventLabel; revrersible = true; is_unit[2] = true; is_unit[3] = true; break; case "会話": ltype = LabelType.TalkEventLabel; is_unit[2] = true; is_unit[3] = true; break; case "接触": ltype = LabelType.ContactEventLabel; revrersible = true; is_unit[2] = true; is_unit[3] = true; break; case "進入": ltype = LabelType.EnterEventLabel; is_unit[2] = true; if (llen == 4) { is_num[3] = true; is_num[4] = true; lnum[3] = GeneralLib.StrToLng(litem[3]).ToString(); lnum[4] = GeneralLib.StrToLng(litem[4]).ToString(); } break; case "脱出": ltype = LabelType.EscapeEventLabel; is_unit[2] = true; break; case "収納": ltype = LabelType.LandEventLabel; is_unit[2] = true; break; case "使用": ltype = LabelType.UseEventLabel; is_unit[2] = true; break; case "使用後": ltype = LabelType.AfterUseEventLabel; is_unit[2] = true; break; case "変形": ltype = LabelType.TransformEventLabel; is_unit[2] = true; break; case "合体": ltype = LabelType.CombineEventLabel; is_unit[2] = true; break; case "分離": ltype = LabelType.SplitEventLabel; is_unit[2] = true; break; case "行動終了": ltype = LabelType.FinishEventLabel; is_unit[2] = true; break; case "レベルアップ": ltype = LabelType.LevelUpEventLabel; is_unit[2] = true; break; case "勝利条件": ltype = LabelType.RequirementEventLabel; break; case "再開": ltype = LabelType.ResumeEventLabel; break; case "マップコマンド": ltype = LabelType.MapCommandEventLabel; is_condition[3] = true; break; case "ユニットコマンド": ltype = LabelType.UnitCommandEventLabel; is_condition[4] = true; break; case "特殊効果": ltype = LabelType.EffectEventLabel; break; default: ltype = LabelType.NormalLabel; break; } // 各ラベルについて一致しているかチェック foreach (LabelData lab in colEventLabelList.List) { // ラベルの種類が一致している? if (ltype != lab.Name) { continue; } // ClearEventされていない? if (!lab.Enable) { continue; } // 検索開始行より後ろ? if (lab.EventDataId < start) { continue; } // パラメータ数が一致している? if (llen != lab.CountPara()) { if (ltype != LabelType.MapCommandEventLabel && ltype != LabelType.UnitCommandEventLabel) { continue; } } // 各パラメータが一致している? var reversed = false; var isMatch = IsMatch(ltype, lab, litem, lnum, is_unit, is_num, is_condition, reversed); if (!isMatch && revrersible) { // 対象と相手を入れ替えたイベントラベルが存在するか判定 var lname2 = litem[1] + " " + GeneralLib.ListIndex(lab.Data, 3) + " " + GeneralLib.ListIndex(lab.Data, 2); if (lab.AsterNum > 0) { lname2 = "*" + lname2; } if (FindLabel(lname2) == 0) { // 対象と相手を入れ替えて判定し直す reversed = true; isMatch = IsMatch(ltype, lab, litem, lnum, is_unit, is_num, is_condition, reversed); } } if (!isMatch) { continue; } // ここまでたどり付けばラベルは一致している SRC.LogDebug("Found", lab.Name.ToString(), lab.Data); // 対象と相手を入れ替えて一致した場合はグローバル変数も入れ替え if (reversed) { var tmp_u = SelectedUnitForEvent; SelectedUnitForEvent = SelectedTargetForEvent; SelectedTargetForEvent = tmp_u; } return(lab.EventDataId); } return(-1); }