/// <summary> /// 打撃のスイングデータを取得する /// </summary> /// <param name="actualBallData">投球データ(選球結果含む)</param> /// <returns></returns> public BattingSwingData GetSwingData(PitchingBallData actualBallData) { List<BattingMission> missions = new List<BattingMission>(); if (GameData.RequestedBattingMissions.Count > 0) { // ユーザ指定の作戦があれば実行 missions.AddRange(GameData.RequestedBattingMissions); } else { // 「おまかせ」か高速試合モードか敵のAIの場合は、智力依存で作戦を登録する AddMissions(missions); } // どちらかの作戦しか実行できないものも削除しない(優先度高い方が採用される) // 基本作戦の「ストライクを振る」「クサイ所はカット」の作戦を追加 missions.Add(new BattingMission(BattingMissionKind.SwingStrike)); missions.Add(new BattingMission(BattingMissionKind.CutScantBall)); // スイングデータの作成開始 BattingSwingData swingData = new BattingSwingData(); // 優先順位の低い順にソートした作戦を元にスイングデータを作成する // (優先度の高い作戦により、後から上書きする) var sortedMissions = missions.OrderBy(m => m.Priority); foreach (BattingMission mission in sortedMissions) { swingData = mission.GetSwingData(actualBallData, GameData, swingData); } return swingData; }
/// <summary> /// スイングのデータを取得する /// </summary> /// <param name="ballData"></param> /// <param name="gameData"></param> /// <param name="swingData"></param> /// <returns></returns> public BattingSwingData GetSwingData(PitchingBallData ballData, GameData gameData, BattingSwingData swingData) { // 優先度の低い作戦順に処理して上書きすることを前提とするため // IsSwingとSwingKindの両方を必ずしも設定しない switch (BattingMissionKind) { #region スイングの種別選定 case BattingMissionKind.SwingStrike: // ストライクを振る swingData.IsSwing = PitchingCourseHelper.IsStrike(ballData.PitchingCourseByBattingEye); swingData.BattingSwingKind = BattingSwingKind.Normal; break; case BattingMissionKind.RollBallByAbility: // 得意なら転がせ // パワーが一定以下で、ミートがパワーより大幅に高い場合は転がす if (gameData.CurrentBatter.Power < Constants.MinValueForEfficientSwing && gameData.CurrentBatter.Meet - gameData.CurrentBatter.Power >= Constants.DeltaForRollBollByAbility) { swingData.BattingSwingKind = BattingSwingKind.RollBoll; } // 内野前進かバントシフトの場合、転がせ有利のため // パワーが一定未満か[ミート > パワー]なら実行 if ((gameData.InFielderDrawIn || gameData.BuntShift) && (gameData.CurrentBatter.Power < Constants.MinValueForEfficientSwing || gameData.CurrentBatter.Meet > gameData.CurrentBatter.Power)) { swingData.BattingSwingKind = BattingSwingKind.RollBoll; } break; case BattingMissionKind.FullSwingByAbility: // 得意なら長打狙い // 以下の条件を満たす場合に実行 // ・パワーが一定以上 // ・パワーがミート以上 // ・8回までで二死ランナーなしか二死一塁(長打狙いだと打率が大幅に下がるため) if (gameData.CurrentBatter.Power >= Constants.PowerForFullSwingByAbility && gameData.CurrentBatter.Power >= gameData.CurrentBatter.Meet && gameData.CurrentInning <= 8 && gameData.OutCount == 2 && gameData.AllRunnerMembers.Count <= 1 && gameData.SecondRunnerMember == null && gameData.ThirdRunnerMember == null) { swingData.BattingSwingKind = BattingSwingKind.FullSwing; } break; case BattingMissionKind.RightBatting: // 右打ち // 右打者であり、無死二塁で、ミート>パワーの場合、右打ちする // (一死二塁は、右打ちしない方が良い場合もあるため、自動では右打ちしない) if (!gameData.CurrentBatter.Player.IsLeftBox && gameData.OutCount == 0 && gameData.AllRunnerMembers.Count == 1 && gameData.SecondRunnerMember != null && gameData.CurrentBatter.Meet > gameData.CurrentBatter.Power) { swingData.BattingSwingKind = BattingSwingKind.RightBatting; } break; case BattingMissionKind.FullSwingByTwoStrike: // 2ストライクまで長打狙い if (gameData.StrikeCount < 2) { swingData.BattingSwingKind = BattingSwingKind.FullSwing; } break; case BattingMissionKind.NormalSwingByUser: // 通常スイング(ユーザ操作) swingData.BattingSwingKind = BattingSwingKind.Normal; break; case BattingMissionKind.FullSwingByUser: // 長打狙い(ユーザ操作) swingData.BattingSwingKind = BattingSwingKind.FullSwing; break; case BattingMissionKind.RollBallByUser: // 転がせ(ユーザ操作) swingData.BattingSwingKind = BattingSwingKind.RollBoll; // 智力が一定以上の場合、ユーザ戦術が「転がせ」であっても // 右打者であり、無死二塁の場合、右打ちする // (一死二塁は、右打ちしない方が良い場合もあるため、自動では右打ちしない) if (gameData.CurrentBatter.Wisdom >= Constants.WisdomForRightBatting && !gameData.CurrentBatter.Player.IsLeftBox && gameData.OutCount == 0 && gameData.AllRunnerMembers.Count == 1 && gameData.SecondRunnerMember != null) { swingData.BattingSwingKind = BattingSwingKind.RightBatting; } break; #endregion #region 狙い球の選定 case BattingMissionKind.TargetSweetCourseByTwoStrike: // 甘い球狙い(2ストライクまで) if (PitchingCourseHelper.IsStrike(ballData.PitchingCourseByBattingEye) && gameData.StrikeCount < 2) { if (ballData.PitchingCourseByBattingEye == PitchingCourse.Center || ballData.PitchingCourseByBattingEye == PitchingCourse.Sweet) { // ストライクでど真ん中か甘いコースのみスイング swingData.IsSwing = true; swingData.TargetBollSwingLevel = 1; } else { // 狙い球でなければスイングしない swingData.IsSwing = false; } } break; case BattingMissionKind.TargetStrongBall: // 得意球種狙い if (PitchingCourseHelper.IsStrike(ballData.PitchingCourseByBattingEye)) { // 球種能力差が大きい場合に適用 // 能力オール8で球種能力差が4でOPSが大きく上がるため、その差以上で適用 // (得意球種を投げる確率はPitchingMission.GetRequestBallメソッド参照) double fastValue = gameData.CurrentPitcherAbirity.Fastball; double breakingValue = gameData.CurrentPitcherAbirity.Breakingball; double delta = fastValue - breakingValue; if (Math.Abs(delta) >= 4) { // 狙う球種の設定 bool targetFastBall = fastValue > breakingValue; // 相手バッテリーが「得意な球種を多用」作戦の実行ができない智力の場合 // 逆に弱い方の球種を狙う(50%で弱い球種が来るならその方が狙い目) if (gameData.CurrentBatteryWisdom < Constants.WisdomForStrongBoll) { targetFastBall = !targetFastBall; } if (targetFastBall == ballData.IsFastBall) { // ストライクで狙う球種が一致した時のみスイング swingData.IsSwing = true; swingData.TargetBollSwingLevel = 1; } else { if (gameData.StrikeCount < 2) { // 狙う球種が一致しなければスイングしない swingData.IsSwing = false; } else { // 追い込まれていればファウル狙い swingData.IsSwing = true; swingData.BattingSwingKind = BattingSwingKind.Cut; } } } } break; case BattingMissionKind.TargetSweetAndFastOrBreakingBall: // 甘い直球か甘い変化球狙い // 智力が打力より圧倒的に高い場合に適用する(カットするごとにカットのための智力は減衰) if (gameData.CurrentBatter.Wisdom - gameData.FoulCountForSwing * Constants.DownCutAbilityByThrowBall - gameData.CurrentBatter.MeetAndPower >= Constants.TargetFourBollMeetAndPowerDelta) { if (PitchingCourseHelper.IsStrike(ballData.PitchingCourseByBattingEye)) { // 相手投手の能力が高い方の球種に絞って対応する bool targeFastBalll = gameData.CurrentPitcher.Fastball > gameData.CurrentPitcher.Breakingball; // 相手バッテリーが「得意な球種を多用」作戦の実行ができない智力の場合 // 逆に弱い方の球種を狙う(50%で弱い球種が来るならその方が狙い目) if (gameData.CurrentBatteryWisdom < Constants.WisdomForStrongBoll) { targeFastBalll = !targeFastBalll; } if ((ballData.PitchingCourseByBattingEye == PitchingCourse.Center || ballData.PitchingCourseByBattingEye == PitchingCourse.Sweet) && ballData.IsFastBall == targeFastBalll) { // ど真ん中か甘いコースで球種が一致時のみスイング swingData.IsSwing = true; swingData.TargetBollSwingLevel = 2; } else { if (gameData.StrikeCount < 2) { // 条件に合わなければスイングしない swingData.IsSwing = false; } else { // 追い込まれていればファウル狙い swingData.IsSwing = true; swingData.BattingSwingKind = BattingSwingKind.Cut; } } } } break; case BattingMissionKind.TargetFastBallByUser: // 直球狙い(ユーザ操作) if (PitchingCourseHelper.IsStrike(ballData.PitchingCourseByBattingEye)) { if (ballData.IsFastBall) { // ストライクで直球のときのみスイング swingData.IsSwing = true; swingData.TargetBollSwingLevel = 1; } else { if (gameData.StrikeCount< 2) { // 変化球はスイングしない swingData.IsSwing = false; } else { // 変化球でも追い込まれていればファウル狙い swingData.IsSwing = true; swingData.BattingSwingKind = BattingSwingKind.Cut; } } } break; case BattingMissionKind.TargetBreakingBallByUser: // 変化球狙い(ユーザ操作) if (PitchingCourseHelper.IsStrike(ballData.PitchingCourseByBattingEye)) { if (!ballData.IsFastBall) { // ストライクで変化球のときのみスイング swingData.IsSwing = true; swingData.TargetBollSwingLevel = 1; } else { if (gameData.StrikeCount < 2) { // 直球はスイングしない swingData.IsSwing = false; } else { // 直球でも追い込まれていればファウル狙い swingData.IsSwing = true; swingData.BattingSwingKind = BattingSwingKind.Cut; } } } break; case BattingMissionKind.TargetSweetCourseByUser: // 甘い球狙い(ユーザ操作) if (PitchingCourseHelper.IsStrike(ballData.PitchingCourseByBattingEye)) { if (ballData.PitchingCourseByBattingEye == PitchingCourse.Center || ballData.PitchingCourseByBattingEye == PitchingCourse.Sweet) { // ストライクでど真ん中か甘いコースのみスイング swingData.IsSwing = true; swingData.TargetBollSwingLevel = 1; } else { if (gameData.StrikeCount < 2) { // 甘いコースでなければスイングしない swingData.IsSwing = false; } else { // 甘いコースでなくても、追い込まれていればファウル狙い swingData.IsSwing = true; swingData.BattingSwingKind = BattingSwingKind.Cut; } } } break; case BattingMissionKind.TargetSweetFastBallByUser: // 甘い直球狙い(ユーザ操作) if (PitchingCourseHelper.IsStrike(ballData.PitchingCourseByBattingEye)) { if ((ballData.PitchingCourseByBattingEye == PitchingCourse.Center || ballData.PitchingCourseByBattingEye == PitchingCourse.Sweet) && ballData.IsFastBall) { // ど真ん中か甘いコースで直球のみスイング swingData.IsSwing = true; swingData.TargetBollSwingLevel = 2; } else { if (gameData.StrikeCount < 2) { // 条件に合わなければスイングしない swingData.IsSwing = false; } else { // 追い込まれていればファウル狙い swingData.IsSwing = true; swingData.BattingSwingKind = BattingSwingKind.Cut; } } } break; case BattingMissionKind.TargetSweetBreakingBallByUser: // 甘い変化球狙い(ユーザ操作) if (PitchingCourseHelper.IsStrike(ballData.PitchingCourseByBattingEye)) { if ((ballData.PitchingCourseByBattingEye == PitchingCourse.Center || ballData.PitchingCourseByBattingEye == PitchingCourse.Sweet) && !ballData.IsFastBall) { // ど真ん中か甘いコースで変化球のみスイング swingData.IsSwing = true; swingData.TargetBollSwingLevel = 2; } else { if (gameData.StrikeCount < 2) { // 条件に合わなければスイングしない swingData.IsSwing = false; } else { // 追い込まれていればファウル狙い swingData.IsSwing = true; swingData.BattingSwingKind = BattingSwingKind.Cut; } } } break; #endregion #region クサイ所はカット case BattingMissionKind.CutScantBall: // クサイ所はカット if (gameData.StrikeCount == 2 && (ballData.PitchingCourse == PitchingCourse.Severe || ballData.PitchingCourse == PitchingCourse.Corner) && ballData.PitchingCourseByBattingEye == PitchingCourse.ScantBall) { // 2ストライクで「厳しいボール」を誤判断で「わずかにボール」と判定した場合に // 一定確率でカットする(智力依存) GameMemberAbility memberAbility = gameData.CurrentBatterAbirity; double percent = GameMemberAbility.GetHigherValueOverBaseline(memberAbility.Wisdom, Constants.MaxCutScantBollPercent); if (RandomCreater.GetPercentValue() < percent) { swingData.IsSwing = true; swingData.BattingSwingKind = BattingSwingKind.Cut; } } break; #endregion #region 送りバント case BattingMissionKind.Bunt: // 送りバント if (gameData.UnableBunt) { break; } // 二塁→三塁へのバントは一死では効果が低いため実施しない // (一死二塁と一死一二塁はバントしない) if (gameData.OutCount == 1 && gameData.SecondRunnerMember != null) { break; } // 智力が打力以上であり // 次の打者の打者能力が現在の打者より著しく劣っていない if (gameData.CurrentBatter.Wisdom >= gameData.CurrentBatter.MeetAndPower && gameData.NextBatter.BatterAbility > gameData.CurrentBatter.BatterAbility - Constants.BatterAbilityDeltaForBunt) { // 試合出場中メンバのうち、打者能力上位3名はバントしない var goodBatters = gameData.CurrentAttackTeam.CurrentPlayingMembers.OrderByDescending(m => m.BatterAbility).Take(3); if (goodBatters.SingleOrDefault(m => m == gameData.CurrentBatter) == null) { swingData.BattingSwingKind = BattingSwingKind.Bunt; if (PitchingCourseHelper.IsStrike(ballData.PitchingCourseByBattingEye)) { swingData.IsSwing = true; } // ストライクと判断しない場合にスイングをやめる処理は行わない // クサイ球をカットするためにスイングする状態になっているのをそのまま活用してストライクを見逃さない } } break; #endregion #region 出塁狙い case BattingMissionKind.TargetFourBall: // 四球狙い // 智力が打力より圧倒的に高い場合に適用する if (gameData.CurrentBatter.Wisdom - gameData.CurrentBatter.MeetAndPower >= Constants.TargetFourBollMeetAndPowerDelta) { if (PitchingCourseHelper.IsStrike(ballData.PitchingCourseByBattingEye)) { if (gameData.StrikeCount < 2) { // 2ストライクまではスイングしない swingData.IsSwing = false; } else { // 追い込まれていればファウル狙い swingData.IsSwing = true; swingData.BattingSwingKind = BattingSwingKind.Cut; } } } break; case BattingMissionKind.SafetyBunt: // セーフティバント if (gameData.UnableSafetyBunt || gameData.CurrentBatter.SafetyBuntAbility <= gameData.CurrentBatter.MeetAndPower || (gameData.AllRunnerMembers.Count > 0 && gameData.AllRunnerMembers.Min(r => r.RunnerAbility) < Constants.RunAbilityForBuntRunner)) { // 以下の場合は実行しない // ・セーフティバント実施すべきでない状況(走力足りない、2ストライク、バントシフト) // ・「セーフティバント能力」が「ミート+パワー」以下 // ・ランナーがいる場合に走塁能力が一定以上ないランナーがいる // (足が遅すぎるとランナーがアウトになるため) break; } // セーフティバント能力が、三塁手の守備力を大幅に超えている場合に実行 // (前進守備しているかどうかで必要な差分は異なる) { double delta = (gameData.CurrentBatter.SafetyBuntAbility) - (gameData.GetDefenseMember(DefensePosition.Third).Defense); if ((gameData.InFielderDrawIn == false && delta >= Constants.DeltaForSafetyBunt) || (gameData.InFielderDrawIn == true && delta >= Constants.DeltaForBuntShift)) { swingData.BattingSwingKind = BattingSwingKind.SafetyBunt; if (PitchingCourseHelper.IsStrike(ballData.PitchingCourseByBattingEye)) { swingData.IsSwing = true; } } } break; #endregion #region 盗塁・スクイズ case BattingMissionKind.Steal: // 盗塁 if (RunnerTactics.TrySteal(gameData)) { swingData.IsSteal = true; if (gameData.StrikeCount < 2) { // 追い込まれていない場合 // (見逃す場合でもアニメーションで使用するため、スイングの種類はバントにする) swingData.IsSwing = false; swingData.BattingSwingKind = BattingSwingKind.Bunt; } else { // 追い込まれている場合 // スイング有無とスイング種別は変更しない // (カット狙いや長打狙いをやめて転がせにするとデメリットがあるため) } } break; case BattingMissionKind.Squeeze: // スクイズ if (gameData.UnableSqueeze) { break; } // 打者の智力がミート・パワーの平均より低い場合は実施しない(打った方が期待できるため) if (gameData.CurrentBatter.Wisdom < gameData.CurrentBatter.MeetAndPower) { break; } // 試合出場中メンバのうち、打者能力上位2名はスクイズしない var highBatters = gameData.CurrentAttackTeam.CurrentPlayingMembers.OrderByDescending(m => m.BatterAbility).Take(2); if (highBatters.Any(m => m == gameData.CurrentBatter)) { break; } int squeezeValue = 0; if (gameData.StrikeCount + gameData.BallCount == 0) { // 初球 squeezeValue = 10; } else if (gameData.StrikeCount == 1 && gameData.BallCount == 0) { // 2球目(ストライク先行) squeezeValue = 10; } else if (gameData.StrikeCount == 0 && gameData.BallCount == 1) { // 2球目(ボール先行) squeezeValue = 20; } else if (gameData.StrikeCount == 1 && gameData.BallCount == 1) { // 3球目の並行カウント squeezeValue = 20; } else if (gameData.StrikeCount == 2) { // 追い込まれるとスクイズなし squeezeValue = 0; } else if (gameData.StrikeCount == 0 && gameData.BallCount >= 2) { // ストライク一球もなければ逆に敬遠の可能性があるため控える squeezeValue = 10; } else if (gameData.StrikeCount == 1 && gameData.BallCount >= 2) { // ストライク一球あってボール先行であれば敬遠のリスクも低くスクイズのチャンス squeezeValue = 20; } // 指定した確率でスクイズを実行するか判定 if (RandomCreater.GetPercentValue() < squeezeValue) { swingData.AllRunnerStart = true; swingData.IsSwing = true; swingData.BattingSwingKind = BattingSwingKind.Squeeze; } break; #endregion #region ユーザ操作による最優先作戦 // スクイズ作戦を自動で実行している可能性があるため、全ランナースタートフラグをリセットする // 盗塁も自動で実行している可能性があるが、それはそれで効果的なのでフラグリセットしない case BattingMissionKind.SafetyBuntByUser: // セーフティバント(ユーザ操作) // (見逃す場合でもアニメーションで使用するため、スイングの種類はバントにする) swingData.AllRunnerStart = false; swingData.BattingSwingKind = BattingSwingKind.SafetyBunt; if (PitchingCourseHelper.IsStrike(ballData.PitchingCourseByBattingEye)) { swingData.IsSwing = true; } // ストライクと判断しない場合にスイングをやめる処理は行わない // クサイ球をカットするためにスイングする状態になっているのをそのまま活用してストライクを見逃さない break; case BattingMissionKind.BuntByUser: // 送りバント(ユーザ操作) // (見逃す場合でもアニメーションで使用するため、スイングの種類はバントにする) swingData.AllRunnerStart = false; swingData.BattingSwingKind = BattingSwingKind.Bunt; if (PitchingCourseHelper.IsStrike(ballData.PitchingCourseByBattingEye)) { swingData.IsSwing = true; } // ストライクと判断しない場合にスイングをやめる処理は行わない // クサイ球をカットするためにスイングする状態になっているのをそのまま活用してストライクを見逃さない break; case BattingMissionKind.SqueezeByUser: // スクイズ(ユーザ操作) { swingData.AllRunnerStart = true; swingData.IsSwing = true; swingData.BattingSwingKind = BattingSwingKind.Squeeze; } break; case BattingMissionKind.StealByUser: // 盗塁(ユーザ操作) // (見逃す場合でもアニメーションで使用するため、スイングの種類はバントにする) swingData.AllRunnerStart = false; swingData.IsSwing = false; swingData.BattingSwingKind = BattingSwingKind.Bunt; swingData.IsSteal = true; break; case BattingMissionKind.EndRunByUser: // エンドラン(ユーザ操作) swingData.AllRunnerStart = true; swingData.IsSwing = true; swingData.BattingSwingKind = BattingSwingKind.EndRun; break; case BattingMissionKind.WaitByUser: // 待て(ユーザ操作) swingData.AllRunnerStart = false; swingData.IsSwing = false; break; #endregion default: throw new Exception("Invalid BattingMissinKind"); } return swingData; }