/// <summary> /// マウスが移動したときに呼び出されるハンドラ /// </summary> /// <param name="p"></param> public void OnMouseMove(Point p) { // ダイアログを表示している場合、そこにマウスがhoverすると // 素材がhover状態の表示になるので、その変化を反映しなくてはならない。 var state = ViewModel.viewState; // 成り・不成のダイアログを出している if (state.state == GameScreenViewStateEnum.PromoteDialog) { // 与えられたpointがどこに属するか判定する。 // まず逆affine変換して、viewの(DrawSpriteなどで使っている)座標系にする var pt = InverseAffine(p); var zero = state.promote_dialog_location; // ここを原点とする pt = new Point(pt.X - zero.X, pt.Y - zero.Y); var selection = SPRITE.IsHoverPromoteDialog(pt); // 前回までと違う場所が選択されたのでselectionの値を更新して、画面の描画をしなおす。 if (state.promote_dialog_selection != selection) { state.promote_dialog_selection = selection; ViewModel.dirty = true; } } }
/// <summary> /// [UI Thread] 対局終了 /// </summary> /// <param name="args"></param> private void GameEndEventHandler(PropertyChangedEventArgs args) { animatorManager.ClearAnimator(); // 他のanimatorをすべて削除 // エフェクト無効なら、やらない。 var enable = TheApp.app.Config.EnableGameGreetingEffect != 0; if (!enable) { return; } // 対局結果から、「勝ち」「負け」「引き分け」のいずれかを表示する。 var result = (MoveGameResult)args.value; var win_lose_draw = result == MoveGameResult.WIN || result == MoveGameResult.LOSE || result == MoveGameResult.DRAW; var sprite = win_lose_draw ? SPRITE.GameResult(result): result == MoveGameResult.UNKNOWN ? SPRITE.GameEnd(): result == MoveGameResult.INTERRUPT ? SPRITE.GameInterrupt(): null; var pos = win_lose_draw ? game_result_pos : game_start_pos; // 1秒後以降に表示する。(すぐに表示されるとちょっと邪魔) animatorManager.AddAnimator(new Animator( elapsed => { DrawSprite(pos, sprite); }, false, 1000, 3500 )); }
/// <summary> /// マウスが移動したときに呼び出されるハンドラ /// </summary> /// <param name="p"></param> public void OnMouseMove(Point p) { // ダイアログを表示している場合、そこにマウスがhoverすると // 素材がhover状態の表示になるので、その変化を反映しなくてはならない。 var state = viewState; var pt = InverseAffine(p); var reverse = gameServer.BoardReverse; // 成り・不成のダイアログを出している if (state.state == GameScreenControlViewStateEnum.PromoteDialog) { // 与えられたpointがどこに属するか判定する。 // まず逆affine変換して、viewの(DrawSpriteなどで使っている)座標系にする bool flip; var zero = CalcPromoteDialogLocation(state, reverse, out flip); // ここを原点とする pt = !flip ? new Point(pt.X - zero.X, pt.Y - zero.Y) : new Point(zero.X - pt.X, zero.Y - pt.Y); var selection = SPRITE.IsHoverPromoteDialog(pt); // 前回までと違う場所が選択されたのでselectionの値を更新して、画面の描画をしなおす。 if (state.promote_dialog_selection != selection) { state.promote_dialog_selection = selection; Dirty = true; } } else { // 成り・不成のダイアログを出していないので、この座標を保存しておく。 MouseClientLocation = pt; MouseClientLocationReverse = reverse; // この保存されたときにreverseであったかも併せて保存しておく。 if (state.state == GameScreenControlViewStateEnum.PiecePickedUp && TheApp.app.Config.PickedMoveDisplayStyle == 1) { // 駒をマウスカーソルに追随させるモードである // マウスカーソルが移動しているので、このとき再描画が必要になる。 Dirty = true; } } }
/// <summary> /// 描画時に呼び出される。 /// 対局盤面を描画する。 /// </summary> public void OnDraw(Graphics g_) { // 初期化が終わっていない。この描画は出来ない。 if (gameServer == null) { return; } graphics = g_; /// 以降、このクラスのDrawSprite(),DrawString()は正常にaffine変換されて描画されるものとする。 // ここでdirtyをfalseにしておく。 // 描画中に他のスレッドからの依頼によりDirty=trueになったら、再度描画がなされなくてはならないため。 dirty = false; // ここではDrawSprite()とDrawString()だけで描画を完結させてあるので複数Viewへの対応は(描画だけなら)容易。 var app = TheApp.app; var config = app.Config; // 描画する局面 // これに基づいて描画しないと駄目(途中でgameServer.Positionが差し替わる必要があるので、参照を掴んでおく必要がある) var pos = gameServer.Position; // MainDialogViewModel。掴んでいる駒などのViewの状態 if (pos == null) { return; // 初期化まだ終わってない。 } var state = viewState; var picked_from = state.picked_from; // 持ち上げている駒のスプライトと座標(最後に描画するために積んでおく) SpriteEx picked_sprite = null; // 盤面を反転させて描画するかどうか // このメソッドが呼び出されている最中にこの値が変化しうるので、このメソッド内では、ここで取得したものを一貫して使う必要がある。 var reverse = gameServer.BoardReverse; // -- 盤面の描画 { // 盤面編集中 → 駒箱を描画する必要がある。 var inTheBoardEdit = gameServer.InTheBoardEdit; // 座標系はストレートに指定しておけばaffine変換されて適切に描画される。 DrawSprite(new Point(0, 0), SPRITE.Board(PieceTableVersion, inTheBoardEdit)); } // -- 駒の描画 { // -- 駒を持ち上げた時の描画 var DrawPickedSprite = (Action <Sprite, Point>)((sprite, dest) => { // 移動元の升に適用されるエフェクトを描画する。 DrawSprite(dest, SPRITE.PieceMove(PieceMoveEffect.PickedFrom)); switch (config.PickedMoveDisplayStyle) { case 0: // 少し持ち上げた感じで描画する。 picked_sprite = new SpriteEx(sprite, dest + new Size(-5, -20)); break; case 1: // マウスカーソルに追随させるモードであるから、マウスカーソルの位置に.. picked_sprite = new SpriteEx(sprite, MouseClientLocation + new Size(-piece_img_size.Width / 2, -piece_img_size.Height / 2)); break; } }); // -- 盤上の駒 // 最終手(初期盤面などでは存在せず、lastMove == Move.NONEであることに注意) var lastMove = pos.State().lastMove; // 最終手の移動元の升 var lastMoveFrom = (lastMove != ShogiCore.Move.NONE && !lastMove.IsDrop()) ? lastMove.From() : Square.NB; // 最終手の移動先の升 var lastMoveTo = (lastMove != ShogiCore.Move.NONE) ? lastMove.To() : Square.NB; for (Square sq = Square.ZERO; sq < Square.NB; ++sq) { var pc = pos.PieceOn(sq); var dest = PieceLocation((SquareHand)sq, reverse); // ダイアログが出ている時や、駒を掴んでいるときは最終手のエフェクトがあると紛らわしいので消す。 if (state.state == GameScreenControlViewStateEnum.Normal) { // これが最終手の移動元の升であるなら、エフェクトを描画する。 if (sq == lastMoveFrom) { var piece_to = pos.PieceOn(lastMoveTo); DrawSprite(dest, SPRITE.PieceMove(PieceMoveEffect.From, piece_to)); } // これが最終手の移動先の升であるなら、エフェクトを描画する。 if (sq == lastMoveTo) { DrawSprite(dest, SPRITE.PieceMove(PieceMoveEffect.To)); } } // 盤面反転モードなら、駒を先後入れ替えて描画する。 var sprite = SPRITE.Piece(reverse ? pc.Inverse() : pc); // いま持ち上げている駒であるなら、少し持ち上げている感じで描画する if (picked_from != SquareHand.NB) { // ただし、一番手前に描画したいので、この駒は一番最後に描画する。 // (なので今回の描画はskipする) if (sq == (Square)picked_from) { DrawPickedSprite(sprite, dest); continue; // 持ち上げているので通常の駒の描画をskipする。 } else { // 駒を持ち上げてはいる時の移動先の候補の升のエフェクト // 移動先の候補の升か? var movable = viewState.picked_piece_legalmovesto.IsSet(sq); if (movable && config.PickedMoveToColorType >= 4) // 移動先の候補の升を明るく { var picked_pc = pos.PieceOn(picked_from); DrawSprite(dest, SPRITE.PieceMove(PieceMoveEffect.PickedTo, picked_pc)); } else if (!movable && config.PickedMoveToColorType < 4) { // 移動先の候補以外の升を暗く DrawSprite(dest, SPRITE.PieceMove(PieceMoveEffect.PickedTo)); } } } // 駒の通常の描画 DrawSprite(dest, sprite); } // -- 手駒の描画 foreach (var c in All.Colors()) { Hand h = pos.Hand(c); // 枚数によって位置が自動調整されるの、わりと見づらいので嫌。 // 駒種によって位置固定で良いと思う。 //同種の駒が3枚以上になったときに、その駒は1枚だけを表示して、 //数字を右肩表示しようと考えていたのですが、例えば、金が2枚、 //歩が3枚あるときに、歩だけが数字表示になるのはどうもおかしい気が //するのです。2枚以上は全部数字表示のほうが良いだろう。 foreach (var pc in hand_piece_list) { int count = h.Count(pc); if (count != 0) { // この駒の描画されるべき位置を求めるためにSquareHand型に変換する。 var piece = Util.ToHandPiece(c, pc); var dest = PieceLocation(piece, reverse); // 物理画面で後手側の駒台への描画であるか(駒を180度回転さて描画しないといけない) var is_white_in_display = (c == SColor.WHITE) ^ reverse; var sprite = SPRITE.Piece(is_white_in_display ? pc.Inverse() : pc); // この駒を掴んでいるならすごしずれたところに描画する。 // ただし、掴んでいるので描画を一番最後に回す if (picked_from == piece) { // 掴んでいる表現 DrawPickedSprite(sprite, dest); } else { // 駒の通常の描画 DrawSprite(dest, sprite); } // 駒の枚数を表す数字の描画(枚数が2枚以上のとき) if (count >= 2) { DrawSprite(dest + hand_number_offset, SPRITE.HandNumber(count)); } } } } // -- 駒箱の駒の描画(盤面編集時のみ) if (gameServer.InTheBoardEdit) { // 通常の駒台 0 , 細長い駒台なら1 var v = PieceTableVersion; // 表示倍率 var ratio = v == 0 ? 1.0f : 0.6f; // 駒箱の駒 var h = pos.Hand(SColor.NB); foreach (var pt in piece_box_list) { int count = pos.PieceBoxCount(pt); if (count > 0) { var dest = PieceLocation(Util.ToPieceBoxPiece(pt), reverse); var sprite = SPRITE.Piece(pt); var piece = Util.ToPieceBoxPiece(pt); // この駒、選択されているならば if (picked_from == piece) { // 移動元の升に適用されるエフェクトを描画する。 DrawSprite(dest, SPRITE.PieceMove(PieceMoveEffect.PickedFrom), ratio); picked_sprite = new SpriteEx(sprite, dest + new Size(-5, -20), ratio); } else { // 駒の描画 DrawSprite(dest, sprite, ratio); } // 数字の描画(枚数が2枚以上のとき) if (count >= 2) { DrawSprite(dest + hand_number_offset2[v], SPRITE.HandBoxNumber(count), ratio); } } } } } // -- 盤の段・筋を表す数字の表示 { var version = config.BoardNumberImageVersion; DrawSprite(board_number_pos[0], SPRITE.BoardNumberFile(reverse)); DrawSprite(board_number_pos[1], SPRITE.BoardNumberRank(reverse)); } // 手番側が先手なら0、後手なら1。ただし、盤面反転しているなら、その逆。 int side = pos.sideToMove == SColor.BLACK ? 0 : 1; side = reverse ? (side ^ 1) : side; if (Setting.NamePlateVisible) { // -- 対局者氏名 { switch (PieceTableVersion) { // 通常状態の駒台表示 case 0: // 手番側を赤文字で表現するなら、その処理 var playerColors = new[] { Brushes.Black, Brushes.Black }; if (config.TurnDisplay == 2) { playerColors[side] = Brushes.IndianRed; } DrawString(name_plate_name[0], gameServer.ShortDisplayNameWithTurn(reverse ? SColor.WHITE : SColor.BLACK), 26, new DrawStringOption(playerColors[0], 0)); DrawString(name_plate_name[1], gameServer.ShortDisplayNameWithTurn(reverse ? SColor.BLACK : SColor.WHITE), 26, new DrawStringOption(playerColors[1], 0)); break; // 細長い状態の駒台表示 case 1: DrawSprite(turn_slim_pos, SPRITE.NamePlateSlim(pos.sideToMove, reverse)); DrawString(name_plate_slim_name[0], gameServer.ShortDisplayNameWithTurn(reverse ? SColor.WHITE : SColor.BLACK), 26, new DrawStringOption(Brushes.White, 2)); DrawString(name_plate_slim_name[1], gameServer.ShortDisplayNameWithTurn(reverse ? SColor.BLACK : SColor.WHITE), 26, new DrawStringOption(Brushes.White, 0)); break; } } // -- 持ち時間等 { switch (PieceTableVersion) { // 通常状態の駒台表示 case 0: foreach (var c in All.IntColors()) { var isWhite = c == 1; // 対局時間設定 var timeSettingString = gameServer.TimeSettingString(reverse ^ isWhite ? SColor.WHITE : SColor.BLACK); // 2行に渡っているなら残り時間の表示位置を調整してやる。 var offset = (timeSettingString == null || !timeSettingString.Contains("\r")) ? Size.Empty : new Size(0, 10); DrawString(time_setting_pos[c], timeSettingString, 18); // 残り時間 DrawString(time_setting_pos2[c] + offset, gameServer.RestTimeString(reverse ^ isWhite ? SColor.WHITE : SColor.BLACK), 28, new DrawStringOption(Brushes.Black, 1)); } break; // 細長い状態の駒台表示 case 1: // 対局時間設定(表示する場所がなさげ) //DrawString(time_setting_slim_pos[0], gameServer.TimeSettingString(reverse ? SColor.WHITE : SColor.BLACK), 18 , new DrawStringOption(Brushes.Black, 2)); //DrawString(time_setting_slim_pos[1], gameServer.TimeSettingString(reverse ? SColor.BLACK : SColor.WHITE), 18 , new DrawStringOption(Brushes.Black, 0)); // 残り時間 DrawString(time_setting_slim_pos2[0], gameServer.RestTimeString(reverse ? SColor.WHITE : SColor.BLACK), 24, new DrawStringOption(Brushes.Black, 1)); DrawString(time_setting_slim_pos2[1], gameServer.RestTimeString(reverse ? SColor.BLACK : SColor.WHITE), 24, new DrawStringOption(Brushes.Black, 1)); break; } } // -- 手番の表示 { switch (PieceTableVersion) { case 0: if (config.TurnDisplay == 1) { DrawSprite(turn_normal_pos[side], SPRITE.TurnNormal()); } break; case 1: DrawSprite(turn_slim_pos, SPRITE.TurnSlim(pos.sideToMove, reverse)); break; } } } // -- 持ち上げている駒があるなら、一番最後に描画する。 { if (picked_sprite != null) { DrawSprite(picked_sprite); } } // -- 成り、不成の選択ダイアログを出している最中であるなら if (state.state == GameScreenControlViewStateEnum.PromoteDialog) { DrawSprite(state.promote_dialog_location, SPRITE.PromoteDialog(state.promote_dialog_selection, state.moved_piece_type)); } // -- エンジン初期化中のダイアログ if (gameServer.EngineInitializing) { DrawSprite(engine_init_pos, SPRITE.EngineInit()); } // -- animator (この上位に位置する)の描画 animatorManager.OnDraw(); // -- 連続対局中のメッセージ { var cont = gameServer == null ? null : gameServer.continuousGame.GetGamePlayingString(); if (cont != null) { // 赤文字でセンタリングして表示 //DrawString(continuos_game_pos, cont, 22, new DrawStringOption(Brushes.Red, Brushes.Black /* 影つき */ , 1)); // → 人によっては、これは、やや見づらいらしい。 // 青文字でセンタリングして表示 DrawString(continuos_game_pos, cont, 22, new DrawStringOption(Brushes.Blue, Brushes.White /* 影つき */, 1)); } } // リソースリークを調べる(デバッグ時) #if false GdiResourceWatcher.DisplayMemory(); #endif }
/// <summary> /// 描画時に呼び出される。 /// 対局盤面を描画する。 /// </summary> public void OnDraw(Graphics g_) { graphics = g_; /// 以降、このクラスのDrawSprite(),DrawString()は正常にaffine変換されて描画されるものとする。 // ここではDrawSprite()とDrawString()だけで描画を完結させてあるので複数Viewへの対応は(描画だけなら)容易。 var app = TheApp.app; var config = app.config; var vm = ViewModel; // 対局を監視しているLocalGameServer var gameServer = vm.ViewModel.gameServer; // 描画する局面 var pos = gameServer.Position; // MainDialogViewModel // 掴んでいる駒などのViewの状態 var state = vm.viewState; var picked_from = state.picked_from; // 持ち上げている駒のスプライトと座標(最後に描画するために積んでおく) Sprite picked_sprite = null; Point picked_sprite_location = new Point(0, 0); // 盤面を反転させて描画するかどうか var reverse = gameServer.BoardReverse; // -- 盤面の描画 { // 座標系はストレートに指定しておけばaffine変換されて適切に描画される。 DrawSprite(new Point(0, 0), SPRITE.Board()); } // -- 駒の描画 { // -- 盤上の駒 // 最終手(初期盤面などでは存在せず、lastMove == Move.NONEであることに注意) var lastMove = pos.State().lastMove; // 最終手の移動元の升 var lastMoveFrom = (lastMove != ShogiCore.Move.NONE && !lastMove.IsDrop()) ? lastMove.From() : Square.NB; // 最終手の移動先の升 var lastMoveTo = (lastMove != ShogiCore.Move.NONE) ? lastMove.To() : Square.NB; for (Square sq = Square.ZERO; sq < Square.NB; ++sq) { var pc = pos.PieceOn(sq); var dest = PieceLocation((SquareHand)sq); // ダイアログが出ている時や、駒を掴んでいるときは最終手のエフェクトがあると紛らわしいので消す。 if (state.state == GameScreenViewStateEnum.Normal) { // これが最終手の移動元の升であるなら、エフェクトを描画する。 if (sq == lastMoveFrom) { var piece_to = pos.PieceOn(lastMoveTo); DrawSprite(dest, SPRITE.PieceMove(PieceMoveEffect.From, piece_to)); } // これが最終手の移動先の升であるなら、エフェクトを描画する。 if (sq == lastMoveTo) { DrawSprite(dest, SPRITE.PieceMove(PieceMoveEffect.To)); } } var sprite = SPRITE.Piece(reverse ? pc.Inverse() : pc); // いま持ち上げている駒であるなら、少し持ち上げている感じで描画する if (picked_from != SquareHand.NB) { // ただし、一番手前に描画したいので、この駒は一番最後に描画する。 // (なので今回の描画はskipする) if (sq == (Square)picked_from) { // 移動元の升に適用されるエフェクトを描画する。 DrawSprite(dest, SPRITE.PieceMove(PieceMoveEffect.PickedFrom)); picked_sprite_location = dest + new Size(-5, -20); picked_sprite = sprite; continue; } else { // 駒を持ち上げてはいる時の移動先の候補の升のエフェクト // 移動先の候補の升か? var movable = vm.viewState.picked_piece_legalmovesto.IsSet(sq); if (movable && config.PickedMoveToColorType >= 4) // 移動先の候補の升を明るく { var picked_pc = pos.PieceOn(picked_from); DrawSprite(dest, SPRITE.PieceMove(PieceMoveEffect.PickedTo, picked_pc)); } else if (!movable && config.PickedMoveToColorType < 4) { // 移動先の候補以外の升を暗く DrawSprite(dest, SPRITE.PieceMove(PieceMoveEffect.PickedTo)); } } } // 盤面反転モードなら、駒を先後入れ替えて描画する。 DrawSprite(dest, sprite); } // -- 手駒の描画 foreach (var c in All.Colors()) { Hand h = pos.Hand(c); // 枚数によって位置が自動調整されるの、わりと見づらいので嫌。 // 駒種によって位置固定で良いと思う。 //同種の駒が3枚以上になったときに、その駒は1枚だけを表示して、 //数字を右肩表示しようと考えていたのですが、例えば、金が2枚、 //歩が3枚あるときに、歩だけが数字表示になるのはどうもおかしい気が //するのです。2枚以上は全部数字表示のほうが良いだろう。 foreach (var pc in hand_piece_list) { int count = h.Count(pc); if (count != 0) { // この駒の描画されるべき位置を求めるためにSquareHand型に変換する。 var piece = Util.ToSquareHand(c, pc); var dest = PieceLocation(piece); // 物理画面で後手側の駒台への描画であるか(駒を180度回転さて描画しないといけない) var is_white_in_display = (c == SColor.WHITE) ^ reverse; var sprite = SPRITE.Piece(is_white_in_display ? pc.Inverse() : pc); // この駒を掴んでいるならすごしずれたところに描画する。 // ただし、掴んでいるので描画を一番最後に回す if (picked_from == piece) { // 移動元の升に適用されるエフェクトを描画する。 DrawSprite(dest, SPRITE.PieceMove(PieceMoveEffect.PickedFrom)); picked_sprite_location = dest + new Size(-5, -20); picked_sprite = sprite; } else { // 駒の描画 DrawSprite(dest, sprite); } // 数字の描画(枚数が2枚以上のとき) if (count >= 2) { DrawSprite(dest + hand_number_offset, SPRITE.HandNumber(count)); } } } } // -- 駒箱の駒の描画(盤面編集時のみ) { // 駒箱の駒 var h = pos.Hand(SColor.NB); foreach (var pt in piece_box_list) { int count = pos.PieceBoxCount(pt); if (count > 0) { var dest = PieceLocation((int)(pt - 1) + SquareHand.PieceBox); DrawSprite(dest, SPRITE.Piece(pt)); // 数字の描画(枚数が2枚以上のとき) if (count >= 2) { DrawSprite(dest + hand_number_offset2, SPRITE.HandNumber(count)); } } } } } // -- 盤の段・筋を表す数字の表示 { var version = config.BoardNumberImageVersion; DrawSprite(board_number_pos[0], SPRITE.BoardNumberFile(reverse)); DrawSprite(board_number_pos[1], SPRITE.BoardNumberRank(reverse)); } // -- 対局者氏名 { switch (config.KomadaiImageVersion) { // 通常状態の駒台表示 case 1: DrawString(name_plate_name[0], gameServer.ShortDisplayName(reverse ? SColor.WHITE : SColor.BLACK), 28); DrawString(name_plate_name[1], gameServer.ShortDisplayName(reverse ? SColor.BLACK : SColor.WHITE), 28); break; // 細長い状態の駒台表示 case 2: DrawSprite(turn_slim_pos, SPRITE.NamePlateSlim(pos.sideToMove, reverse)); DrawString(name_plate_slim_name[0], gameServer.ShortDisplayName(reverse ? SColor.WHITE : SColor.BLACK), 28, new DrawStringOption(Brushes.White, 2)); DrawString(name_plate_slim_name[1], gameServer.ShortDisplayName(reverse ? SColor.BLACK : SColor.WHITE), 28, new DrawStringOption(Brushes.White, 0)); break; } } // -- 持ち時間等 { switch (config.KomadaiImageVersion) { // 通常状態の駒台表示 case 1: // 対局時間設定 DrawString(time_setting_pos[0], gameServer.TimeSettingString(reverse ? SColor.WHITE : SColor.BLACK), 18); DrawString(time_setting_pos[1], gameServer.TimeSettingString(reverse ? SColor.BLACK : SColor.WHITE), 18); // 残り時間 DrawString(time_setting_pos2[0], gameServer.RestTimeString(reverse ? SColor.WHITE : SColor.BLACK), 28, new DrawStringOption(Brushes.Black, 1)); DrawString(time_setting_pos2[1], gameServer.RestTimeString(reverse ? SColor.BLACK : SColor.WHITE), 28, new DrawStringOption(Brushes.Black, 1)); break; // 細長い状態の駒台表示 case 2: // 対局時間設定(表示する場所がなさげ) //DrawString(time_setting_slim_pos[0], gameServer.TimeSettingString(reverse ? SColor.WHITE : SColor.BLACK), 18 , new DrawStringOption(Brushes.Black, 2)); //DrawString(time_setting_slim_pos[1], gameServer.TimeSettingString(reverse ? SColor.BLACK : SColor.WHITE), 18 , new DrawStringOption(Brushes.Black, 0)); // 残り時間 DrawString(time_setting_slim_pos2[0], gameServer.RestTimeString(reverse ? SColor.WHITE : SColor.BLACK), 24, new DrawStringOption(Brushes.Black, 1)); DrawString(time_setting_slim_pos2[1], gameServer.RestTimeString(reverse ? SColor.BLACK : SColor.WHITE), 24, new DrawStringOption(Brushes.Black, 1)); break; } } // -- 手番の表示 { // 手番側が先手なら0、後手なら1。ただし、盤面反転しているなら、その逆。 int side = pos.sideToMove == SColor.BLACK ? 0 : 1; side = reverse ? (side ^ 1) : side; switch (config.KomadaiImageVersion) { case 1: DrawSprite(turn_normal_pos[side], SPRITE.TurnNormal()); break; case 2: DrawSprite(turn_slim_pos, SPRITE.TurnSlim(pos.sideToMove, reverse)); break; } } // -- 持ち上げている駒があるなら、一番最後に描画する。 { if (picked_sprite != null) { DrawSprite(picked_sprite_location, picked_sprite); } } // -- 成り、不成の選択ダイアログを出している最中であるなら if (state.state == GameScreenViewStateEnum.PromoteDialog) { DrawSprite(state.promote_dialog_location, SPRITE.PromoteDialog(state.promote_dialog_selection, state.moved_piece_type)); } // -- エンジン初期化中のダイアログ if (vm.ViewModel.gameServer.EngineInitializing) { DrawSprite(engine_init_pos, SPRITE.EngineInit()); } // 描画が完了したのでDirtyフラグを戻しておく。 vm.dirty = false; }
/// <summary> /// [UI Thread] 対局開始 /// </summary> /// <param name="args"></param> private void GameStartEventHandler(PropertyChangedEventArgs args) { animatorManager.ClearAnimator(); // 他のanimatorをすべて削除 var continuousGame = args.value as ContinuousGame; // 振り駒画像を表示するのかのフラグ var enable_piece_toss = continuousGame.EnablePieceToss; // true; // 振り駒を表示しているので「対局開始」の表示が後ろ倒しになる時間。[ms] var piece_toss_time = 0; // 振り駒エフェクト無効なら、やらない。 var enable_piece_toss_effect = TheApp.app.Config.EnablePieceTossEffect != 0; if (enable_piece_toss && enable_piece_toss_effect) { // 振り駒イメージ画像 animatorManager.AddAnimator(new Animator( elapsed => { DrawSprite(game_piece_toss_pos, SPRITE.GamePieceToss()); }, false, 0, 500 )); // 駒を5枚描画 animatorManager.AddAnimator(new Animator( elapsed => { // ptpc[x] == trueならx枚目として歩、falseなら「と」を描画。 var ptpc = continuousGame.PieceTossPieceColor; foreach (var i in All.Int(5)) { var sprite = SPRITE.Piece(ptpc[i] ? Piece.PAWN : Piece.PRO_PAWN); var dest = new Point( board_img_size.Width / 2 + (i - 2) * 110 - piece_img_size.Width / 2, board_img_size.Height / 2 - piece_img_size.Height * 3 / 4); DrawSprite(dest, sprite); } }, false, 500, 2500 )); piece_toss_time = 1000; } // 開始エフェクト無効なら、やらない。 var enable_greeting = TheApp.app.Config.EnableGameGreetingEffect != 0; if (!enable_greeting) { return; } // これだけでアニメーションできる。便利すぎ。 animatorManager.AddAnimator(new Animator( elapsed => { // 中断局など特定局面からの開始でかつ連続対局ではないならこれは再開局扱い。 var restart = gameServer.GameSetting.BoardSetting.BoardTypeCurrent && !gameServer.continuousGame.IsContinuousGameSet(); // 「対局開始」もしくは「対局再開」 var sprite = restart ? SPRITE.GameRestart() : SPRITE.GameStart(); DrawSprite(game_start_pos, sprite); var handicapped = continuousGame.Handicapped; var black = handicapped ? SPRITE.GameShitate() : SPRITE.GameBlack(); var white = handicapped ? SPRITE.GameUwate() : SPRITE.GameWhite(); if (gameServer.BoardReverse) { Utility.Swap(ref black, ref white); } // 「先手」/「下手」 DrawSprite(game_black_pos, black); // 「後手」/「上手」 DrawSprite(game_white_pos, white); }, false, piece_toss_time, 2000 )); }